Merge pull request #95 from Nitwel/save-system
Implement Store based save system
This commit is contained in:
commit
018a0f3b5f
|
@ -1,9 +1,12 @@
|
||||||
extends Node3D
|
extends Entity
|
||||||
|
|
||||||
|
const Entity = preload("../entity.gd")
|
||||||
|
|
||||||
var entity_id = "button.plug_printer_2"
|
|
||||||
@onready var button = $Button
|
@onready var button = $Button
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
|
super()
|
||||||
|
|
||||||
var stateInfo = await HomeApi.get_state(entity_id)
|
var stateInfo = await HomeApi.get_state(entity_id)
|
||||||
|
|
||||||
if stateInfo == null:
|
if stateInfo == null:
|
||||||
|
@ -29,9 +32,3 @@ func set_state(state):
|
||||||
else:
|
else:
|
||||||
button.icon = false
|
button.icon = false
|
||||||
button.label = name
|
button.label = name
|
||||||
|
|
||||||
func _save():
|
|
||||||
return {
|
|
||||||
"transform": transform,
|
|
||||||
"entity_id": entity_id
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
extends StaticBody3D
|
extends Entity
|
||||||
|
|
||||||
|
const Entity = preload("../entity.gd")
|
||||||
|
|
||||||
@export var entity_id = "camera.bedroomspeaker"
|
|
||||||
@export var view_width = 0.15
|
@export var view_width = 0.15
|
||||||
|
|
||||||
@onready var view = $View
|
@onready var view = $View
|
||||||
|
@ -10,6 +11,8 @@ extends StaticBody3D
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
# Called when the node enters the scene tree for the first time.
|
||||||
func _ready():
|
func _ready():
|
||||||
|
super()
|
||||||
|
|
||||||
var stateInfo = await HomeApi.get_state(entity_id)
|
var stateInfo = await HomeApi.get_state(entity_id)
|
||||||
|
|
||||||
set_state(stateInfo)
|
set_state(stateInfo)
|
||||||
|
@ -57,9 +60,3 @@ func load_image(url: String):
|
||||||
view.texture = texture
|
view.texture = texture
|
||||||
view.pixel_size = pixel_size
|
view.pixel_size = pixel_size
|
||||||
mesh.visible = false
|
mesh.visible = false
|
||||||
|
|
||||||
func _save():
|
|
||||||
return {
|
|
||||||
"transform": transform,
|
|
||||||
"entity_id": entity_id
|
|
||||||
}
|
|
||||||
|
|
11
content/entities/entity.gd
Normal file
11
content/entities/entity.gd
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
extends StaticBody3D
|
||||||
|
|
||||||
|
var entity_id: String
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
var movable = get_node("Movable")
|
||||||
|
|
||||||
|
if movable:
|
||||||
|
movable.on_moved.connect(func():
|
||||||
|
House.body.save_all_entities()
|
||||||
|
)
|
|
@ -1,6 +1,7 @@
|
||||||
extends StaticBody3D
|
extends Entity
|
||||||
|
|
||||||
|
const Entity = preload("../entity.gd")
|
||||||
|
|
||||||
@export var entity_id = "switch.plug_printer_2"
|
|
||||||
@export var color_off = Color(0.23, 0.23, 0.23)
|
@export var color_off = Color(0.23, 0.23, 0.23)
|
||||||
@export var color_on = Color(1.0, 0.85, 0.0)
|
@export var color_on = Color(1.0, 0.85, 0.0)
|
||||||
|
|
||||||
|
@ -12,6 +13,8 @@ var brightness = 0 # 0-255
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
# Called when the node enters the scene tree for the first time.
|
||||||
func _ready():
|
func _ready():
|
||||||
|
super()
|
||||||
|
|
||||||
var stateInfo = await HomeApi.get_state(entity_id)
|
var stateInfo = await HomeApi.get_state(entity_id)
|
||||||
set_state(stateInfo["state"] == "on")
|
set_state(stateInfo["state"] == "on")
|
||||||
|
|
||||||
|
@ -56,9 +59,3 @@ func _on_click(event):
|
||||||
|
|
||||||
HomeApi.set_state(entity_id, "on" if !state else "off", attributes)
|
HomeApi.set_state(entity_id, "on" if !state else "off", attributes)
|
||||||
set_state(!state, brightness)
|
set_state(!state, brightness)
|
||||||
|
|
||||||
func _save():
|
|
||||||
return {
|
|
||||||
"transform": transform,
|
|
||||||
"entity_id": entity_id
|
|
||||||
}
|
|
|
@ -1,6 +1,7 @@
|
||||||
extends StaticBody3D
|
extends Entity
|
||||||
|
|
||||||
|
const Entity = preload("../entity.gd")
|
||||||
|
|
||||||
@export var entity_id = "media_player.bedroomspeaker"
|
|
||||||
@export var image_width = 0.15
|
@export var image_width = 0.15
|
||||||
|
|
||||||
@onready var previous = $Previous
|
@onready var previous = $Previous
|
||||||
|
@ -18,6 +19,8 @@ var volume = 50
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
# Called when the node enters the scene tree for the first time.
|
||||||
func _ready():
|
func _ready():
|
||||||
|
super()
|
||||||
|
|
||||||
var stateInfo = await HomeApi.get_state(entity_id)
|
var stateInfo = await HomeApi.get_state(entity_id)
|
||||||
set_state(stateInfo)
|
set_state(stateInfo)
|
||||||
|
|
||||||
|
@ -86,9 +89,3 @@ func load_image(url: String):
|
||||||
var texture = ImageTexture.create_from_image(image)
|
var texture = ImageTexture.create_from_image(image)
|
||||||
logo.texture = texture
|
logo.texture = texture
|
||||||
logo.pixel_size = pixel_size
|
logo.pixel_size = pixel_size
|
||||||
|
|
||||||
func _save():
|
|
||||||
return {
|
|
||||||
"transform": transform,
|
|
||||||
"entity_id": entity_id
|
|
||||||
}
|
|
|
@ -1,10 +1,13 @@
|
||||||
extends StaticBody3D
|
extends Entity
|
||||||
|
|
||||||
|
const Entity = preload("../entity.gd")
|
||||||
|
|
||||||
@export var entity_id = "sensor.sun_next_dawn"
|
|
||||||
@onready var label: Label3D = $Label
|
@onready var label: Label3D = $Label
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
# Called when the node enters the scene tree for the first time.
|
||||||
func _ready():
|
func _ready():
|
||||||
|
super()
|
||||||
|
|
||||||
var stateInfo = await HomeApi.get_state(entity_id)
|
var stateInfo = await HomeApi.get_state(entity_id)
|
||||||
set_text(stateInfo)
|
set_text(stateInfo)
|
||||||
|
|
||||||
|
@ -22,9 +25,3 @@ func set_text(stateInfo):
|
||||||
text += " " + stateInfo["attributes"]["unit_of_measurement"]
|
text += " " + stateInfo["attributes"]["unit_of_measurement"]
|
||||||
|
|
||||||
label.text = text
|
label.text = text
|
||||||
|
|
||||||
func _save():
|
|
||||||
return {
|
|
||||||
"transform": transform,
|
|
||||||
"entity_id": entity_id
|
|
||||||
}
|
|
|
@ -1,10 +1,13 @@
|
||||||
extends StaticBody3D
|
extends Entity
|
||||||
|
|
||||||
|
const Entity = preload("../entity.gd")
|
||||||
|
|
||||||
@export var entity_id = "switch.plug_printer_2"
|
|
||||||
@onready var sprite: AnimatedSprite3D = $Icon
|
@onready var sprite: AnimatedSprite3D = $Icon
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
# Called when the node enters the scene tree for the first time.
|
||||||
func _ready():
|
func _ready():
|
||||||
|
super()
|
||||||
|
|
||||||
var stateInfo = await HomeApi.get_state(entity_id)
|
var stateInfo = await HomeApi.get_state(entity_id)
|
||||||
if stateInfo == null:
|
if stateInfo == null:
|
||||||
return
|
return
|
||||||
|
@ -31,9 +34,3 @@ func _on_click(event):
|
||||||
|
|
||||||
func _on_request_completed():
|
func _on_request_completed():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
func _save():
|
|
||||||
return {
|
|
||||||
"transform": transform,
|
|
||||||
"entity_id": entity_id
|
|
||||||
}
|
|
|
@ -3,6 +3,7 @@ extends Function
|
||||||
class_name Movable
|
class_name Movable
|
||||||
|
|
||||||
signal on_move(position: Vector3, rotation: Vector3)
|
signal on_move(position: Vector3, rotation: Vector3)
|
||||||
|
signal on_moved()
|
||||||
|
|
||||||
@export var restricted: bool = false
|
@export var restricted: bool = false
|
||||||
@export var restrict_movement: Callable
|
@export var restrict_movement: Callable
|
||||||
|
@ -33,6 +34,7 @@ func _on_grab_move(_event: EventPointer):
|
||||||
|
|
||||||
func _on_grab_up(event: EventPointer):
|
func _on_grab_up(event: EventPointer):
|
||||||
event.initiator.node.remove_child(hit_node)
|
event.initiator.node.remove_child(hit_node)
|
||||||
|
on_moved.emit()
|
||||||
|
|
||||||
func _get_configuration_warnings() -> PackedStringArray:
|
func _get_configuration_warnings() -> PackedStringArray:
|
||||||
var warnings := PackedStringArray()
|
var warnings := PackedStringArray()
|
||||||
|
|
|
@ -44,3 +44,12 @@ func get_new_transform(old_transform: Transform3D):
|
||||||
marker.scale = Vector3(1, 1, 1)
|
marker.scale = Vector3(1, 1, 1)
|
||||||
return marker.global_transform
|
return marker.global_transform
|
||||||
|
|
||||||
|
func update_align_reference():
|
||||||
|
corner1.global_position = Store.house.align_position1
|
||||||
|
corner2.global_position = Store.house.align_position2
|
||||||
|
|
||||||
|
edge.align_to_corners(corner1.global_position, corner2.global_position)
|
||||||
|
|
||||||
|
func update_store():
|
||||||
|
Store.house.align_position1 = corner1.global_position
|
||||||
|
Store.house.align_position2 = corner2.global_position
|
|
@ -16,21 +16,47 @@ var mini_view: bool = false:
|
||||||
|
|
||||||
var target_size: float = 1.0
|
var target_size: float = 1.0
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
Store.house.on_loaded.connect(func():
|
||||||
|
update_house()
|
||||||
|
)
|
||||||
|
|
||||||
func _physics_process(delta):
|
func _physics_process(delta):
|
||||||
levels.scale.x = lerp(levels.scale.x, target_size, 10.0 * delta)
|
levels.scale.x = lerp(levels.scale.x, target_size, 10.0 * delta)
|
||||||
levels.scale.y = lerp(levels.scale.y, target_size, 10.0 * delta)
|
levels.scale.y = lerp(levels.scale.y, target_size, 10.0 * delta)
|
||||||
levels.scale.z = lerp(levels.scale.z, target_size, 10.0 * delta)
|
levels.scale.z = lerp(levels.scale.z, target_size, 10.0 * delta)
|
||||||
|
|
||||||
|
func update_house():
|
||||||
|
for old_room in get_rooms(0):
|
||||||
|
old_room.queue_free()
|
||||||
|
await old_room.tree_exited
|
||||||
|
|
||||||
|
align_reference.update_align_reference()
|
||||||
|
|
||||||
|
for index in range(Store.house.rooms.size()):
|
||||||
|
var new_room = Store.house.rooms[index]
|
||||||
|
create_room(new_room.name, 0)
|
||||||
|
|
||||||
|
for entity in Store.house.entities:
|
||||||
|
var entity_instance = create_entity_in(entity.id, entity.room)
|
||||||
|
|
||||||
|
if entity_instance == null:
|
||||||
|
continue
|
||||||
|
|
||||||
|
entity_instance.global_rotation = entity.rotation
|
||||||
|
|
||||||
func create_room(room_name: String, level: int) -> RoomType:
|
func create_room(room_name: String, level: int) -> RoomType:
|
||||||
if editing_room != null:
|
var existing_room = Store.house.get_room(room_name)
|
||||||
editing_room.editable = false
|
|
||||||
editing_room = null
|
if existing_room == null:
|
||||||
|
Store.house.rooms.append({
|
||||||
|
"name": room_name,
|
||||||
|
"height": 2.0,
|
||||||
|
"corners": [],
|
||||||
|
})
|
||||||
|
|
||||||
var room = Room.instantiate()
|
var room = Room.instantiate()
|
||||||
room.name = room_name
|
room.name = room_name
|
||||||
room.editable = true
|
|
||||||
editing_room = room
|
|
||||||
|
|
||||||
get_level(level).add_child(room)
|
get_level(level).add_child(room)
|
||||||
|
|
||||||
|
@ -70,22 +96,21 @@ func delete_room(room_name):
|
||||||
|
|
||||||
room.get_parent().remove_child(room)
|
room.get_parent().remove_child(room)
|
||||||
room.queue_free()
|
room.queue_free()
|
||||||
|
await room.tree_exited
|
||||||
|
|
||||||
func is_editiong(room_name):
|
func is_editiong(room_name):
|
||||||
return editing_room != null && editing_room.name == room_name
|
return editing_room != null && editing_room.name == room_name
|
||||||
|
|
||||||
func find_room(room_name):
|
func find_room(room_name):
|
||||||
for level in levels.get_children():
|
for room in get_rooms(0):
|
||||||
for room in level.get_children():
|
if room.name == room_name:
|
||||||
if room.name == room_name:
|
return room
|
||||||
return room
|
|
||||||
return null
|
return null
|
||||||
|
|
||||||
func find_room_at(entity_position: Vector3):
|
func find_room_at(entity_position: Vector3):
|
||||||
for level in levels.get_children():
|
for room in get_rooms(0):
|
||||||
for room in level.get_children():
|
if room.has_point(entity_position):
|
||||||
if room.has_point(entity_position):
|
return room
|
||||||
return room
|
|
||||||
return null
|
return null
|
||||||
|
|
||||||
func get_level(level: int):
|
func get_level(level: int):
|
||||||
|
@ -120,16 +145,38 @@ func create_entity(entity_id: String, entity_position: Vector3):
|
||||||
var room = find_room_at(entity_position)
|
var room = find_room_at(entity_position)
|
||||||
|
|
||||||
if room == null:
|
if room == null:
|
||||||
return
|
return null
|
||||||
|
|
||||||
var entity = EntityFactory.create_entity(entity_id)
|
var entity = EntityFactory.create_entity(entity_id)
|
||||||
|
|
||||||
if entity == null:
|
if entity == null:
|
||||||
return
|
return null
|
||||||
|
|
||||||
room.add_child(entity)
|
room.get_node("Entities").add_child(entity)
|
||||||
entity.global_position = entity_position
|
entity.global_position = entity_position
|
||||||
|
|
||||||
|
save_all_entities()
|
||||||
|
|
||||||
|
return entity
|
||||||
|
|
||||||
|
func create_entity_in(entity_id: String, room_name: String):
|
||||||
|
var room = find_room(room_name)
|
||||||
|
|
||||||
|
if room == null:
|
||||||
|
return null
|
||||||
|
|
||||||
|
var entity = EntityFactory.create_entity(entity_id)
|
||||||
|
|
||||||
|
if entity == null:
|
||||||
|
return null
|
||||||
|
|
||||||
|
room.get_node("Entities").add_child(entity)
|
||||||
|
entity.global_position = room.get_aabb().position + room.get_aabb().size / 2.0
|
||||||
|
|
||||||
|
save_all_entities()
|
||||||
|
|
||||||
|
return entity
|
||||||
|
|
||||||
func update_mini_view():
|
func update_mini_view():
|
||||||
collision_shape.disabled = !mini_view
|
collision_shape.disabled = !mini_view
|
||||||
|
|
||||||
|
@ -166,3 +213,22 @@ func save_reference():
|
||||||
|
|
||||||
align_reference.disabled = true
|
align_reference.disabled = true
|
||||||
align_reference.update_initial_positions()
|
align_reference.update_initial_positions()
|
||||||
|
|
||||||
|
align_reference.update_store()
|
||||||
|
Store.house.save_local()
|
||||||
|
|
||||||
|
func save_all_entities():
|
||||||
|
Store.house.entities.clear()
|
||||||
|
|
||||||
|
for room in get_rooms(0):
|
||||||
|
for entity in room.get_node("Entities").get_children():
|
||||||
|
var entity_data = {
|
||||||
|
"id": entity.entity_id,
|
||||||
|
"position": entity.global_position,
|
||||||
|
"rotation": entity.global_rotation,
|
||||||
|
"room": String(room.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
Store.house.entities.append(entity_data)
|
||||||
|
|
||||||
|
Store.house.save_local()
|
||||||
|
|
|
@ -20,34 +20,103 @@ var editable: bool = false:
|
||||||
else:
|
else:
|
||||||
state_machine.change_to("View")
|
state_machine.change_to("View")
|
||||||
|
|
||||||
func get_corner(index: int) -> MeshInstance3D:
|
|
||||||
return wall_corners.get_child(index % wall_corners.get_child_count())
|
|
||||||
|
|
||||||
func get_edge(index: int) -> MeshInstance3D:
|
|
||||||
return wall_edges.get_child(index % wall_edges.get_child_count())
|
|
||||||
|
|
||||||
func has_point(point: Vector3) -> bool:
|
func has_point(point: Vector3) -> bool:
|
||||||
return get_aabb().has_point(point)
|
return get_aabb().has_point(point)
|
||||||
|
|
||||||
func remove_corner(index: int):
|
|
||||||
get_corner(index).queue_free()
|
|
||||||
get_edge(index).queue_free()
|
|
||||||
|
|
||||||
func get_aabb():
|
func get_aabb():
|
||||||
if wall_corners.get_child_count() == 0:
|
var room_store = Store.house.get_room(name)
|
||||||
|
|
||||||
|
var corners = room_store.corners
|
||||||
|
|
||||||
|
if corners.size() == 0:
|
||||||
return AABB()
|
return AABB()
|
||||||
|
|
||||||
var min_pos = wall_corners.get_child(0).position
|
var min_pos = Vector3(corners[0].x, 0, corners[0].y)
|
||||||
var max_pos = wall_corners.get_child(0).position
|
var max_pos = min_pos
|
||||||
|
|
||||||
for corner in wall_corners.get_children():
|
for corner in corners:
|
||||||
min_pos.x = min(min_pos.x, corner.position.x)
|
min_pos.x = min(min_pos.x, corner.x)
|
||||||
min_pos.z = min(min_pos.z, corner.position.z)
|
min_pos.z = min(min_pos.z, corner.y)
|
||||||
|
|
||||||
max_pos.x = max(max_pos.x, corner.position.x)
|
max_pos.x = max(max_pos.x, corner.x)
|
||||||
max_pos.z = max(max_pos.z, corner.position.z)
|
max_pos.z = max(max_pos.z, corner.y)
|
||||||
|
|
||||||
min_pos.y = room_floor.position.y
|
min_pos.y = 0
|
||||||
max_pos.y = room_ceiling.position.y
|
max_pos.y = room_store.height
|
||||||
|
|
||||||
return AABB(to_global(min_pos), to_global(max_pos) - to_global(min_pos))
|
return AABB(to_global(min_pos), to_global(max_pos) - to_global(min_pos))
|
||||||
|
|
||||||
|
static func generate_wall_mesh(room_store: Dictionary):
|
||||||
|
if room_store.corners.size() < 2:
|
||||||
|
return null
|
||||||
|
|
||||||
|
var st = SurfaceTool.new()
|
||||||
|
var wall_up = Vector3.UP * room_store.height
|
||||||
|
|
||||||
|
st.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
|
||||||
|
|
||||||
|
for corner in room_store.corners:
|
||||||
|
var corner3D = Vector3(corner.x, 0, corner.y)
|
||||||
|
|
||||||
|
st.add_vertex(corner3D)
|
||||||
|
st.add_vertex(corner3D + wall_up)
|
||||||
|
|
||||||
|
var first_corner = Vector3(room_store.corners[0].x, 0, room_store.corners[0].y)
|
||||||
|
|
||||||
|
st.add_vertex(first_corner)
|
||||||
|
st.add_vertex(first_corner + wall_up)
|
||||||
|
|
||||||
|
st.index()
|
||||||
|
st.generate_normals()
|
||||||
|
st.generate_tangents()
|
||||||
|
var mesh = st.commit()
|
||||||
|
|
||||||
|
return mesh
|
||||||
|
|
||||||
|
static func generate_ceiling_mesh(room_store: Dictionary):
|
||||||
|
|
||||||
|
var points: PackedVector2Array = PackedVector2Array()
|
||||||
|
var edges: PackedInt32Array = PackedInt32Array()
|
||||||
|
var triangles: PackedInt32Array
|
||||||
|
|
||||||
|
var corners = room_store.corners
|
||||||
|
|
||||||
|
if corners.size() < 3:
|
||||||
|
return null
|
||||||
|
|
||||||
|
for i in range(corners.size()):
|
||||||
|
var corner = corners[i]
|
||||||
|
points.append(Vector2(corner.x, corner.y))
|
||||||
|
edges.append(i)
|
||||||
|
edges.append((i + 1) % corners.size())
|
||||||
|
|
||||||
|
var cdt: ConstrainedTriangulation = ConstrainedTriangulation.new()
|
||||||
|
|
||||||
|
cdt.init(true, true, 0.1)
|
||||||
|
|
||||||
|
cdt.insert_vertices(points)
|
||||||
|
cdt.insert_edges(edges)
|
||||||
|
|
||||||
|
cdt.erase_outer_triangles()
|
||||||
|
|
||||||
|
points = cdt.get_all_vertices()
|
||||||
|
triangles = cdt.get_all_triangles()
|
||||||
|
|
||||||
|
var st = SurfaceTool.new()
|
||||||
|
|
||||||
|
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
||||||
|
|
||||||
|
for i in range(points.size()):
|
||||||
|
st.add_vertex(Vector3(points[i].x, 0, points[i].y))
|
||||||
|
|
||||||
|
for i in range(triangles.size()):
|
||||||
|
st.add_index(triangles[i])
|
||||||
|
|
||||||
|
st.index()
|
||||||
|
st.generate_normals()
|
||||||
|
st.generate_tangents()
|
||||||
|
|
||||||
|
var mesh = st.commit()
|
||||||
|
|
||||||
|
return mesh
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=11 format=3 uid="uid://bswgmclohuqui"]
|
[gd_scene load_steps=10 format=3 uid="uid://bswgmclohuqui"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://content/system/house/room/room.gd" id="1_fccq0"]
|
[ext_resource type="Script" path="res://content/system/house/room/room.gd" id="1_fccq0"]
|
||||||
[ext_resource type="Script" path="res://content/functions/clickable.gd" id="1_ugebq"]
|
[ext_resource type="Script" path="res://content/functions/clickable.gd" id="1_ugebq"]
|
||||||
|
@ -10,8 +10,6 @@
|
||||||
|
|
||||||
[sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_08sv0"]
|
[sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_08sv0"]
|
||||||
|
|
||||||
[sub_resource type="ArrayMesh" id="ArrayMesh_7dibq"]
|
|
||||||
|
|
||||||
[sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_ap613"]
|
[sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_ap613"]
|
||||||
plane = Plane(0, -1, 0, 0)
|
plane = Plane(0, -1, 0, 0)
|
||||||
|
|
||||||
|
@ -30,7 +28,6 @@ script = ExtResource("1_ugebq")
|
||||||
|
|
||||||
[node name="WallMesh" type="MeshInstance3D" parent="."]
|
[node name="WallMesh" type="MeshInstance3D" parent="."]
|
||||||
material_override = ExtResource("3_al1ev")
|
material_override = ExtResource("3_al1ev")
|
||||||
mesh = SubResource("ArrayMesh_7dibq")
|
|
||||||
|
|
||||||
[node name="CeilingMesh" type="MeshInstance3D" parent="."]
|
[node name="CeilingMesh" type="MeshInstance3D" parent="."]
|
||||||
material_override = ExtResource("3_al1ev")
|
material_override = ExtResource("3_al1ev")
|
||||||
|
@ -65,3 +62,5 @@ script = ExtResource("7_ap14h")
|
||||||
|
|
||||||
[node name="Mini" type="Node" parent="StateMachine"]
|
[node name="Mini" type="Node" parent="StateMachine"]
|
||||||
script = ExtResource("6_g4qca")
|
script = ExtResource("6_g4qca")
|
||||||
|
|
||||||
|
[node name="Entities" type="Node3D" parent="."]
|
||||||
|
|
|
@ -11,13 +11,21 @@ var height_corner: StaticBody3D = null
|
||||||
var height_edge: StaticBody3D = null
|
var height_edge: StaticBody3D = null
|
||||||
|
|
||||||
func _on_enter():
|
func _on_enter():
|
||||||
room.wall_corners.visible = true
|
var room_store = Store.house.get_room(room.name)
|
||||||
room.wall_edges.visible = true
|
|
||||||
|
|
||||||
if floor_corner != null:
|
if room_store == null:
|
||||||
floor_corner.visible = true
|
return
|
||||||
height_corner.visible = true
|
|
||||||
height_edge.visible = true
|
var corners = room_store.corners
|
||||||
|
|
||||||
|
if corners.size() > 0:
|
||||||
|
add_floor_corner(Vector3(corners[0].x, 0, corners[0].y))
|
||||||
|
add_height_corner(Vector3(corners[0].x, 0, corners[0].y))
|
||||||
|
room.room_ceiling.position.y = room_store.height
|
||||||
|
height_edge.align_to_corners(floor_corner.global_position, height_corner.global_position)
|
||||||
|
|
||||||
|
for i in range(1, corners.size()):
|
||||||
|
add_corner(Vector3(corners[i].x, 0, corners[i].y))
|
||||||
|
|
||||||
room.room_ceiling.get_node("CollisionShape3D").disabled = (floor_corner == null && height_corner == null)
|
room.room_ceiling.get_node("CollisionShape3D").disabled = (floor_corner == null && height_corner == null)
|
||||||
room.room_floor.get_node("CollisionShape3D").disabled = false
|
room.room_floor.get_node("CollisionShape3D").disabled = false
|
||||||
|
@ -32,13 +40,21 @@ func _on_enter():
|
||||||
room.room_floor.get_node("Clickable").on_click.connect(_on_click_floor)
|
room.room_floor.get_node("Clickable").on_click.connect(_on_click_floor)
|
||||||
|
|
||||||
func _on_leave():
|
func _on_leave():
|
||||||
room.wall_corners.visible = false
|
update_store()
|
||||||
room.wall_edges.visible = false
|
|
||||||
|
for child in room.wall_corners.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
await child.tree_exited
|
||||||
|
|
||||||
|
for child in room.wall_edges.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
await child.tree_exited
|
||||||
|
|
||||||
if floor_corner != null:
|
if floor_corner != null:
|
||||||
floor_corner.visible = false
|
floor_corner.queue_free()
|
||||||
height_corner.visible = false
|
await floor_corner.tree_exited
|
||||||
height_edge.visible = false
|
height_edge.queue_free()
|
||||||
|
await height_edge.tree_exited
|
||||||
|
|
||||||
room.room_ceiling.get_node("CollisionShape3D").disabled = true
|
room.room_ceiling.get_node("CollisionShape3D").disabled = true
|
||||||
room.room_floor.get_node("CollisionShape3D").disabled = true
|
room.room_floor.get_node("CollisionShape3D").disabled = true
|
||||||
|
@ -46,6 +62,16 @@ func _on_leave():
|
||||||
room.room_ceiling.get_node("Clickable").on_click.disconnect(_on_click_ceiling)
|
room.room_ceiling.get_node("Clickable").on_click.disconnect(_on_click_ceiling)
|
||||||
room.room_floor.get_node("Clickable").on_click.disconnect(_on_click_floor)
|
room.room_floor.get_node("Clickable").on_click.disconnect(_on_click_floor)
|
||||||
|
|
||||||
|
func get_corner(index: int) -> MeshInstance3D:
|
||||||
|
return room.wall_corners.get_child(index % room.wall_corners.get_child_count())
|
||||||
|
|
||||||
|
func get_edge(index: int) -> MeshInstance3D:
|
||||||
|
return room.wall_edges.get_child(index % room.wall_edges.get_child_count())
|
||||||
|
|
||||||
|
func remove_corner(index: int):
|
||||||
|
get_corner(index).queue_free()
|
||||||
|
get_edge(index).queue_free()
|
||||||
|
|
||||||
func _on_click_floor(event):
|
func _on_click_floor(event):
|
||||||
if floor_corner != null && height_corner != null:
|
if floor_corner != null && height_corner != null:
|
||||||
return
|
return
|
||||||
|
@ -68,7 +94,7 @@ func add_floor_corner(position: Vector3):
|
||||||
floor_corner.position = position
|
floor_corner.position = position
|
||||||
|
|
||||||
height_edge = wall_edge_scene.instantiate()
|
height_edge = wall_edge_scene.instantiate()
|
||||||
height_edge.align_to_corners(position, position + Vector3.UP * room.room_ceiling.global_position.y)
|
height_edge.align_to_corners(position, position + Vector3.UP * room.room_ceiling.position.y)
|
||||||
|
|
||||||
floor_corner.get_node("Clickable").on_grab_down.connect(func(event):
|
floor_corner.get_node("Clickable").on_grab_down.connect(func(event):
|
||||||
if !is_active() || moving != null:
|
if !is_active() || moving != null:
|
||||||
|
@ -92,14 +118,14 @@ func add_floor_corner(position: Vector3):
|
||||||
|
|
||||||
height_edge.align_to_corners(new_position, new_position + Vector3.UP * room.room_ceiling.global_position.y)
|
height_edge.align_to_corners(new_position, new_position + Vector3.UP * room.room_ceiling.global_position.y)
|
||||||
|
|
||||||
room.get_corner(moving_index).position.x = new_position.x
|
get_corner(moving_index).position.x = new_position.x
|
||||||
room.get_corner(moving_index).position.z = new_position.z
|
get_corner(moving_index).position.z = new_position.z
|
||||||
|
|
||||||
if room.wall_edges.get_child_count() == 0:
|
if room.wall_edges.get_child_count() == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
room.get_edge(moving_index).align_to_corners(new_position, room.get_corner(moving_index + 1).position)
|
get_edge(moving_index).align_to_corners(new_position, get_corner(moving_index + 1).position)
|
||||||
room.get_edge(moving_index - 1).align_to_corners(room.get_corner(moving_index - 1).position, new_position)
|
get_edge(moving_index - 1).align_to_corners(get_corner(moving_index - 1).position, new_position)
|
||||||
)
|
)
|
||||||
|
|
||||||
floor_corner.get_node("Clickable").on_grab_up.connect(func(_event):
|
floor_corner.get_node("Clickable").on_grab_up.connect(func(_event):
|
||||||
|
@ -174,13 +200,13 @@ func add_corner(position: Vector3, index: int = -1):
|
||||||
|
|
||||||
new_position = event.ray.global_position + direction
|
new_position = event.ray.global_position + direction
|
||||||
|
|
||||||
room.get_corner(moving_index).global_position = new_position
|
get_corner(moving_index).global_position = new_position
|
||||||
|
|
||||||
if room.wall_edges.get_child_count() == 0:
|
if room.wall_edges.get_child_count() == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
room.get_edge(moving_index).align_to_corners(room.get_corner(moving_index - 1).position, room.get_corner(moving_index + 1).position)
|
get_edge(moving_index).align_to_corners(get_corner(moving_index - 1).position, get_corner(moving_index + 1).position)
|
||||||
room.get_edge(moving_index - 1).transform = room.get_edge(moving_index).transform
|
get_edge(moving_index - 1).transform = get_edge(moving_index).transform
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -194,14 +220,14 @@ func add_corner(position: Vector3, index: int = -1):
|
||||||
if room.wall_edges.get_child_count() == 0:
|
if room.wall_edges.get_child_count() == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
room.get_edge(moving_index).align_to_corners(new_position, room.get_corner(moving_index + 1).position)
|
get_edge(moving_index).align_to_corners(new_position, get_corner(moving_index + 1).position)
|
||||||
room.get_edge(moving_index - 1).align_to_corners(room.get_corner(moving_index - 1).position, new_position)
|
get_edge(moving_index - 1).align_to_corners(get_corner(moving_index - 1).position, new_position)
|
||||||
)
|
)
|
||||||
|
|
||||||
corner.get_node("Clickable").on_grab_up.connect(func(_event):
|
corner.get_node("Clickable").on_grab_up.connect(func(_event):
|
||||||
if deleting:
|
if deleting:
|
||||||
var moving_index = moving.get_index()
|
var moving_index = moving.get_index()
|
||||||
room.remove_corner(moving_index)
|
remove_corner(moving_index)
|
||||||
|
|
||||||
moving = null
|
moving = null
|
||||||
deleting = false
|
deleting = false
|
||||||
|
@ -213,13 +239,13 @@ func add_corner(position: Vector3, index: int = -1):
|
||||||
var num_corners = room.wall_corners.get_child_count()
|
var num_corners = room.wall_corners.get_child_count()
|
||||||
|
|
||||||
if num_corners > 1:
|
if num_corners > 1:
|
||||||
add_edge(position, room.get_corner(index + 1).position, index)
|
add_edge(position, get_corner(index + 1).position, index)
|
||||||
|
|
||||||
if num_corners > 2:
|
if num_corners > 2:
|
||||||
if num_corners != room.wall_edges.get_child_count():
|
if num_corners != room.wall_edges.get_child_count():
|
||||||
add_edge(room.get_corner(-2).position, room.get_corner(-1).position, -2)
|
add_edge(get_corner(-2).position, get_corner(-1).position, -2)
|
||||||
else:
|
else:
|
||||||
room.get_edge(index - 1).align_to_corners(room.get_corner(index - 1).position, position)
|
get_edge(index - 1).align_to_corners(get_corner(index - 1).position, position)
|
||||||
|
|
||||||
|
|
||||||
func add_edge(from_pos: Vector3, to_pos: Vector3, index: int = -1):
|
func add_edge(from_pos: Vector3, to_pos: Vector3, index: int = -1):
|
||||||
|
@ -236,3 +262,19 @@ func add_edge(from_pos: Vector3, to_pos: Vector3, index: int = -1):
|
||||||
room.wall_edges.add_child(edge)
|
room.wall_edges.add_child(edge)
|
||||||
room.wall_edges.move_child(edge, index)
|
room.wall_edges.move_child(edge, index)
|
||||||
return edge
|
return edge
|
||||||
|
|
||||||
|
func update_store():
|
||||||
|
var store_room = Store.house.get_room(room.name)
|
||||||
|
|
||||||
|
var corners = []
|
||||||
|
|
||||||
|
for corner in room.wall_corners.get_children():
|
||||||
|
corners.append(Vector2(corner.position.x, corner.position.z))
|
||||||
|
|
||||||
|
store_room.corners = corners
|
||||||
|
store_room.height = room.room_ceiling.position.y
|
||||||
|
|
||||||
|
if corners.size() < 3:
|
||||||
|
House.body.delete_room(room.name)
|
||||||
|
|
||||||
|
Store.house.save_local()
|
||||||
|
|
|
@ -2,20 +2,17 @@ extends RoomState
|
||||||
|
|
||||||
const RoomState = preload("./room_state.gd")
|
const RoomState = preload("./room_state.gd")
|
||||||
|
|
||||||
var room_height = 3
|
|
||||||
var corner_count = 0
|
|
||||||
|
|
||||||
func _on_enter():
|
func _on_enter():
|
||||||
corner_count = room.wall_corners.get_child_count()
|
var room_store = Store.house.get_room(room.name)
|
||||||
|
|
||||||
if corner_count < 3:
|
if room_store == null || room_store.corners.size() < 3:
|
||||||
return
|
return
|
||||||
|
|
||||||
room_height = room.room_ceiling.position.y
|
|
||||||
|
|
||||||
room.wall_mesh.visible = true
|
room.wall_mesh.visible = true
|
||||||
room.ceiling_mesh.visible = true
|
room.ceiling_mesh.visible = true
|
||||||
room.wall_mesh.mesh = generate_mesh()
|
|
||||||
|
room.wall_mesh.mesh = Room.generate_wall_mesh(room_store)
|
||||||
|
|
||||||
if room.wall_mesh.mesh == null:
|
if room.wall_mesh.mesh == null:
|
||||||
return
|
return
|
||||||
|
@ -26,7 +23,7 @@ func _on_enter():
|
||||||
ceiling_shape.disabled = false
|
ceiling_shape.disabled = false
|
||||||
floor_shape.disabled = false
|
floor_shape.disabled = false
|
||||||
|
|
||||||
room.ceiling_mesh.mesh = generate_ceiling_mesh()
|
room.ceiling_mesh.mesh = Room.generate_ceiling_mesh(room_store)
|
||||||
ceiling_shape.shape = room.ceiling_mesh.mesh.create_trimesh_shape()
|
ceiling_shape.shape = room.ceiling_mesh.mesh.create_trimesh_shape()
|
||||||
floor_shape.shape = room.ceiling_mesh.mesh.create_trimesh_shape()
|
floor_shape.shape = room.ceiling_mesh.mesh.create_trimesh_shape()
|
||||||
ceiling_shape.shape.backface_collision = true
|
ceiling_shape.shape.backface_collision = true
|
||||||
|
@ -52,87 +49,28 @@ func _on_leave():
|
||||||
|
|
||||||
for collision in room.wall_collisions.get_children():
|
for collision in room.wall_collisions.get_children():
|
||||||
collision.queue_free()
|
collision.queue_free()
|
||||||
|
await collision.tree_exited
|
||||||
func generate_mesh():
|
|
||||||
var st = SurfaceTool.new()
|
|
||||||
var wall_up = Vector3.UP * room_height
|
|
||||||
|
|
||||||
st.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
|
|
||||||
|
|
||||||
for i in range(corner_count):
|
|
||||||
var corner = room.get_corner(i)
|
|
||||||
|
|
||||||
st.add_vertex(corner.position)
|
|
||||||
st.add_vertex(corner.position + wall_up)
|
|
||||||
|
|
||||||
var first_corner = room.get_corner(0)
|
|
||||||
|
|
||||||
st.add_vertex(first_corner.position)
|
|
||||||
st.add_vertex(first_corner.position + wall_up)
|
|
||||||
|
|
||||||
st.index()
|
|
||||||
st.generate_normals()
|
|
||||||
st.generate_tangents()
|
|
||||||
var mesh = st.commit()
|
|
||||||
|
|
||||||
return mesh
|
|
||||||
|
|
||||||
func generate_ceiling_mesh():
|
|
||||||
var points: PackedVector2Array = PackedVector2Array()
|
|
||||||
var edges: PackedInt32Array = PackedInt32Array()
|
|
||||||
var triangles: PackedInt32Array
|
|
||||||
|
|
||||||
for i in range(corner_count):
|
|
||||||
var corner = room.get_corner(i)
|
|
||||||
points.append(Vector2(corner.position.x, corner.position.z))
|
|
||||||
edges.append(i)
|
|
||||||
edges.append((i + 1) % corner_count)
|
|
||||||
|
|
||||||
var cdt: ConstrainedTriangulation = ConstrainedTriangulation.new()
|
|
||||||
|
|
||||||
cdt.init(true, true, 0.1)
|
|
||||||
|
|
||||||
cdt.insert_vertices(points)
|
|
||||||
cdt.insert_edges(edges)
|
|
||||||
|
|
||||||
cdt.erase_outer_triangles()
|
|
||||||
|
|
||||||
points = cdt.get_all_vertices()
|
|
||||||
triangles = cdt.get_all_triangles()
|
|
||||||
|
|
||||||
var st = SurfaceTool.new()
|
|
||||||
|
|
||||||
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
|
||||||
|
|
||||||
for i in range(points.size()):
|
|
||||||
st.add_vertex(Vector3(points[i].x, 0, points[i].y))
|
|
||||||
|
|
||||||
for i in range(triangles.size()):
|
|
||||||
st.add_index(triangles[i])
|
|
||||||
|
|
||||||
st.index()
|
|
||||||
st.generate_normals()
|
|
||||||
st.generate_tangents()
|
|
||||||
|
|
||||||
var mesh = st.commit()
|
|
||||||
|
|
||||||
return mesh
|
|
||||||
|
|
||||||
func generate_collision():
|
func generate_collision():
|
||||||
|
var room_store = Store.house.get_room(room.name)
|
||||||
|
|
||||||
var collision_shapes: Array[CollisionShape3D] = []
|
var collision_shapes: Array[CollisionShape3D] = []
|
||||||
|
|
||||||
for i in range(corner_count):
|
var corners = room_store.corners
|
||||||
var corner = room.get_corner(i)
|
|
||||||
var next_corner = room.get_corner(i + 1)
|
for i in range(corners.size()):
|
||||||
|
var corner = Vector3(corners[i].x, 0, corners[i].y)
|
||||||
|
var next_corner_index = (i + 1) % corners.size()
|
||||||
|
var next_corner = Vector3(corners[next_corner_index].x, 0, corners[next_corner_index].y)
|
||||||
|
|
||||||
var shape = BoxShape3D.new()
|
var shape = BoxShape3D.new()
|
||||||
shape.size = Vector3((next_corner.position - corner.position).length(), room_height, 0.04)
|
shape.size = Vector3((next_corner - corner).length(), room_store.height, 0.04)
|
||||||
|
|
||||||
var transform = Transform3D()
|
var transform = Transform3D()
|
||||||
var back_vector = (corner.position - next_corner.position).cross(Vector3.UP).normalized() * shape.size.z / 2
|
var back_vector = (corner - next_corner).cross(Vector3.UP).normalized() * shape.size.z / 2
|
||||||
|
|
||||||
transform.basis = Basis((next_corner.position - corner.position).normalized(), Vector3.UP, back_vector.normalized())
|
transform.basis = Basis((next_corner - corner).normalized(), Vector3.UP, back_vector.normalized())
|
||||||
transform.origin = corner.position + (next_corner.position - corner.position) / 2 + back_vector + Vector3.UP * shape.size.y / 2
|
transform.origin = corner + (next_corner - corner) / 2 + back_vector + Vector3.UP * shape.size.y / 2
|
||||||
|
|
||||||
var collision_shape = CollisionShape3D.new()
|
var collision_shape = CollisionShape3D.new()
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,10 @@ func _on_entity_click(entity_name):
|
||||||
|
|
||||||
AudioPlayer.play_effect("spawn")
|
AudioPlayer.play_effect("spawn")
|
||||||
|
|
||||||
House.body.create_entity(entity_name, global_position)
|
var entity = House.body.create_entity(entity_name, global_position)
|
||||||
|
|
||||||
|
if entity == null:
|
||||||
|
EventSystem.notify("Entity is not in Room", EventNotify.Type.INFO)
|
||||||
|
|
||||||
func clear_menu():
|
func clear_menu():
|
||||||
for child in devices_node.get_children():
|
for child in devices_node.get_children():
|
||||||
|
|
|
@ -41,6 +41,9 @@ var edit_room = false:
|
||||||
room_button.label = "edit"
|
room_button.label = "edit"
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
|
Store.house.on_loaded.connect(func():
|
||||||
|
_generate_room_map()
|
||||||
|
)
|
||||||
|
|
||||||
room_button.on_button_down.connect(func():
|
room_button.on_button_down.connect(func():
|
||||||
if selected_room == null:
|
if selected_room == null:
|
||||||
|
@ -50,6 +53,7 @@ func _ready():
|
||||||
return
|
return
|
||||||
|
|
||||||
House.body.create_room(room_name, 0)
|
House.body.create_room(room_name, 0)
|
||||||
|
House.body.edit_room(room_name)
|
||||||
selected_room = room_name
|
selected_room = room_name
|
||||||
edit_room = true
|
edit_room = true
|
||||||
else:
|
else:
|
||||||
|
@ -84,27 +88,36 @@ func _on_click(event: EventPointer):
|
||||||
selected_room = room_name
|
selected_room = room_name
|
||||||
|
|
||||||
func _generate_room_map():
|
func _generate_room_map():
|
||||||
var rooms = House.body.get_rooms(0)
|
var rooms = Store.house.rooms
|
||||||
|
|
||||||
var target_size = Vector3(0.3, 1, 0.24)
|
var target_size = Vector2(0.3, 0.24)
|
||||||
|
|
||||||
for old_room in rooms_map.get_children():
|
for old_room in rooms_map.get_children():
|
||||||
old_room.queue_free()
|
old_room.queue_free()
|
||||||
await old_room.tree_exited
|
await old_room.tree_exited
|
||||||
|
|
||||||
var aabb = House.body.get_level_aabb(0)
|
if rooms.size() == 0:
|
||||||
var current_min = aabb.position
|
return
|
||||||
var current_max = aabb.position + aabb.size
|
|
||||||
|
var current_min = Vector2(rooms[0].corners[0].x, rooms[0].corners[0].y)
|
||||||
|
var current_max = current_min
|
||||||
|
|
||||||
for room in rooms:
|
for room in rooms:
|
||||||
var mesh = room.ceiling_mesh.mesh
|
for corner in room.corners:
|
||||||
|
current_min.x = min(current_min.x, corner.x)
|
||||||
|
current_min.y = min(current_min.y, corner.y)
|
||||||
|
current_max.x = max(current_max.x, corner.x)
|
||||||
|
current_max.y = max(current_max.y, corner.y)
|
||||||
|
|
||||||
|
for room in rooms:
|
||||||
|
var mesh = RoomType.generate_ceiling_mesh(room)
|
||||||
|
|
||||||
if mesh == null:
|
if mesh == null:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var body = StaticBody3D.new()
|
var body = StaticBody3D.new()
|
||||||
body.name = room.name
|
body.name = room.name
|
||||||
|
|
||||||
|
|
||||||
var mesh_instance = MeshInstance3D.new()
|
var mesh_instance = MeshInstance3D.new()
|
||||||
mesh_instance.name = "MeshInstance3D"
|
mesh_instance.name = "MeshInstance3D"
|
||||||
mesh_instance.mesh = mesh
|
mesh_instance.mesh = mesh
|
||||||
|
@ -122,9 +135,9 @@ func _generate_room_map():
|
||||||
|
|
||||||
var current_size = current_max - current_min
|
var current_size = current_max - current_min
|
||||||
var target_scale = target_size / current_size
|
var target_scale = target_size / current_size
|
||||||
var scale_value = min(target_scale.x, target_scale.z)
|
var scale_value = min(target_scale.x, target_scale.y)
|
||||||
|
|
||||||
rooms_map.position.x = -current_min.x * scale_value
|
rooms_map.position.x = -current_min.x * scale_value
|
||||||
rooms_map.position.z = -current_min.z * scale_value
|
rooms_map.position.z = -current_min.y * scale_value
|
||||||
|
|
||||||
rooms_map.scale = Vector3(scale_value, scale_value, scale_value)
|
rooms_map.scale = Vector3(scale_value, scale_value, scale_value)
|
||||||
|
|
|
@ -27,39 +27,37 @@ func _ready():
|
||||||
)
|
)
|
||||||
|
|
||||||
credits.on_click.connect(func(_event):
|
credits.on_click.connect(func(_event):
|
||||||
print("_active_controller")
|
|
||||||
var credits_instance = credits_scene.instantiate()
|
var credits_instance = credits_scene.instantiate()
|
||||||
get_tree().root.add_child(credits_instance)
|
get_tree().root.add_child(credits_instance)
|
||||||
var label = $Content/Credits/Label
|
var label = $Content/Credits/Label
|
||||||
credits_instance.global_position = + label.to_global(label.position + Vector3(0.1, 0, -0.15))
|
credits_instance.global_position = + label.to_global(label.position + Vector3(0.1, 0, -0.15))
|
||||||
)
|
)
|
||||||
|
|
||||||
var config = ConfigData.load_config()
|
Store.settings.on_loaded.connect(func():
|
||||||
|
input_url.text = Store.settings.url
|
||||||
if config.has("url"):
|
input_token.text = Store.settings.token
|
||||||
input_url.text = config["url"]
|
)
|
||||||
if config.has("token"):
|
|
||||||
input_token.text = config["token"]
|
|
||||||
|
|
||||||
button_connect.on_button_down.connect(func():
|
button_connect.on_button_down.connect(func():
|
||||||
var url = input_url.text + "/api/websocket"
|
var url = input_url.text
|
||||||
var token = input_token.text
|
var token = input_token.text
|
||||||
|
|
||||||
HomeApi.start_adapter("hass_ws", url, token)
|
HomeApi.start_adapter("hass_ws", url, token)
|
||||||
|
|
||||||
ConfigData.save_config({
|
Store.settings.url = url
|
||||||
"api_type": "hass_ws",
|
Store.settings.token = token
|
||||||
"url": input_url.text,
|
|
||||||
"token": input_token.text
|
Store.settings.save_local()
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
save.on_button_down.connect(func():
|
save.on_button_down.connect(func():
|
||||||
SaveSystem.save()
|
House.body.save_all_entities()
|
||||||
|
Store.house.save_local()
|
||||||
)
|
)
|
||||||
|
|
||||||
clear_save.on_button_down.connect(func():
|
clear_save.on_button_down.connect(func():
|
||||||
SaveSystem.clear()
|
Store.house.clear()
|
||||||
|
House.body.update_house()
|
||||||
)
|
)
|
||||||
|
|
||||||
HomeApi.on_connect.connect(func():
|
HomeApi.on_connect.connect(func():
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
extends Node
|
|
||||||
|
|
||||||
const VariantSerializer = preload("res://lib/utils/variant_serializer.gd")
|
|
||||||
|
|
||||||
var file_url: String = "user://config.cfg"
|
|
||||||
|
|
||||||
func save_config(data: Dictionary):
|
|
||||||
var file := FileAccess.open(file_url, FileAccess.WRITE)
|
|
||||||
|
|
||||||
if file == null:
|
|
||||||
return
|
|
||||||
|
|
||||||
var json_data := JSON.stringify(VariantSerializer.stringify_value(data))
|
|
||||||
file.store_string(json_data)
|
|
||||||
|
|
||||||
func load_config():
|
|
||||||
var file := FileAccess.open(file_url, FileAccess.READ)
|
|
||||||
|
|
||||||
if file == null:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
var json_data := file.get_as_text()
|
|
||||||
var data = VariantSerializer.parse_value(JSON.parse_string(json_data))
|
|
||||||
|
|
||||||
return data
|
|
|
@ -24,14 +24,10 @@ var api: Node
|
||||||
func _ready():
|
func _ready():
|
||||||
print("HomeApi ready")
|
print("HomeApi ready")
|
||||||
|
|
||||||
var config = ConfigData.load_config()
|
var success = Store.settings.load_local()
|
||||||
|
|
||||||
if config.has("api_type") && config.has("url") && config.has("token"):
|
if success:
|
||||||
var type = config["api_type"]
|
start_adapter(Store.settings.type.to_lower(), Store.settings.url, Store.settings.token)
|
||||||
var url = config["url"] + "/api/websocket"
|
|
||||||
var token = config["token"]
|
|
||||||
|
|
||||||
start_adapter(type, url, token)
|
|
||||||
|
|
||||||
|
|
||||||
func start_adapter(type: String, url: String, token: String):
|
func start_adapter(type: String, url: String, token: String):
|
||||||
|
@ -47,7 +43,7 @@ func start_adapter(type: String, url: String, token: String):
|
||||||
add_child(api)
|
add_child(api)
|
||||||
|
|
||||||
api.on_connect.connect(func():
|
api.on_connect.connect(func():
|
||||||
SaveSystem.load()
|
Store.house.load_local()
|
||||||
on_connect.emit()
|
on_connect.emit()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -96,5 +92,5 @@ func watch_state(entity: String, callback: Callable):
|
||||||
|
|
||||||
func _notification(what):
|
func _notification(what):
|
||||||
if what == NOTIFICATION_WM_CLOSE_REQUEST || what == NOTIFICATION_WM_GO_BACK_REQUEST:
|
if what == NOTIFICATION_WM_CLOSE_REQUEST || what == NOTIFICATION_WM_GO_BACK_REQUEST:
|
||||||
# SaveSystem.save()
|
# Store.house.save_local()
|
||||||
pass
|
pass
|
9
lib/globals/main_store.gd
Normal file
9
lib/globals/main_store.gd
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
const SettingsStore = preload("res://lib/stores/settings.gd")
|
||||||
|
const HouseStore = preload("res://lib/stores/house.gd")
|
||||||
|
const DevicesStore = preload("res://lib/stores/devices.gd")
|
||||||
|
|
||||||
|
var settings = SettingsStore.new()
|
||||||
|
var house = HouseStore.new()
|
||||||
|
var devices = DevicesStore.new()
|
|
@ -1,140 +0,0 @@
|
||||||
extends Node
|
|
||||||
|
|
||||||
const VariantSerializer = preload("res://lib/utils/variant_serializer.gd")
|
|
||||||
|
|
||||||
signal loaded()
|
|
||||||
signal unloaded()
|
|
||||||
|
|
||||||
var is_loaded := false
|
|
||||||
var export_config = ConfigFile.new()
|
|
||||||
var export_config_path = "res://export_presets.cfg"
|
|
||||||
|
|
||||||
func clear():
|
|
||||||
await _clear_save_tree(get_tree().root.get_node("Main"))
|
|
||||||
unloaded.emit()
|
|
||||||
is_loaded = false
|
|
||||||
|
|
||||||
func save():
|
|
||||||
if HomeApi.has_connected() == false:
|
|
||||||
return
|
|
||||||
|
|
||||||
var filename = HomeApi.api.url.split("//")[1].replace("/api/websocket", "").replace(".", "_").replace(":", "_")
|
|
||||||
|
|
||||||
var save_file = FileAccess.open("user://%s.save" % filename, FileAccess.WRITE)
|
|
||||||
|
|
||||||
if save_file == null:
|
|
||||||
return
|
|
||||||
|
|
||||||
var save_tree = _generate_save_tree(get_tree().root.get_node("Main"))
|
|
||||||
|
|
||||||
var json_text = JSON.stringify({
|
|
||||||
"version": get_version(),
|
|
||||||
"tree": save_tree
|
|
||||||
})
|
|
||||||
save_file.store_line(json_text)
|
|
||||||
|
|
||||||
func load():
|
|
||||||
await clear()
|
|
||||||
|
|
||||||
if HomeApi.has_connected() == false:
|
|
||||||
return
|
|
||||||
|
|
||||||
var filename = HomeApi.api.url.split("//")[1].replace("/api/websocket", "").replace(".", "_").replace(":", "_")
|
|
||||||
|
|
||||||
var save_file = FileAccess.open("user://%s.save" % filename, FileAccess.READ)
|
|
||||||
|
|
||||||
if save_file == null:
|
|
||||||
return
|
|
||||||
|
|
||||||
var json_text = save_file.get_line()
|
|
||||||
var save_data = JSON.parse_string(json_text)
|
|
||||||
|
|
||||||
if save_data == null:
|
|
||||||
return
|
|
||||||
|
|
||||||
if save_data.has("version") == false:
|
|
||||||
save()
|
|
||||||
return
|
|
||||||
|
|
||||||
var save_tree = save_data["tree"]
|
|
||||||
|
|
||||||
if save_tree == null:
|
|
||||||
return
|
|
||||||
|
|
||||||
if save_tree is Array:
|
|
||||||
for tree in save_tree:
|
|
||||||
_build_save_tree(tree)
|
|
||||||
else:
|
|
||||||
_build_save_tree(save_tree)
|
|
||||||
|
|
||||||
loaded.emit()
|
|
||||||
is_loaded = true
|
|
||||||
|
|
||||||
func get_version():
|
|
||||||
var config_error = export_config.load(export_config_path)
|
|
||||||
|
|
||||||
if config_error != OK:
|
|
||||||
return null
|
|
||||||
|
|
||||||
var version = export_config.get_value("preset.1.options", "version/name")
|
|
||||||
|
|
||||||
if version == null:
|
|
||||||
return null
|
|
||||||
|
|
||||||
return version
|
|
||||||
|
|
||||||
func _clear_save_tree(node: Node):
|
|
||||||
for child in node.get_children():
|
|
||||||
await _clear_save_tree(child)
|
|
||||||
|
|
||||||
if node.has_method("_save"):
|
|
||||||
node.queue_free()
|
|
||||||
await node.tree_exited
|
|
||||||
|
|
||||||
func _generate_save_tree(node: Node):
|
|
||||||
var children = []
|
|
||||||
|
|
||||||
if node.has_method("_save") == false:
|
|
||||||
for child in node.get_children():
|
|
||||||
var data = _generate_save_tree(child)
|
|
||||||
|
|
||||||
if data is Array:
|
|
||||||
for child_data in data:
|
|
||||||
children.append(child_data)
|
|
||||||
else:
|
|
||||||
children.append(data)
|
|
||||||
return children
|
|
||||||
|
|
||||||
|
|
||||||
var save_tree = {
|
|
||||||
"data": VariantSerializer.stringify_value(node.call("_save")),
|
|
||||||
"parent": node.get_parent().get_path(),
|
|
||||||
"filename": node.get_scene_file_path()
|
|
||||||
}
|
|
||||||
|
|
||||||
for child in node.get_children():
|
|
||||||
var child_data = _generate_save_tree(child)
|
|
||||||
|
|
||||||
if child_data is Array:
|
|
||||||
for data in child_data:
|
|
||||||
children.append(data)
|
|
||||||
else:
|
|
||||||
children.append(child_data)
|
|
||||||
|
|
||||||
save_tree["children"] = children
|
|
||||||
|
|
||||||
return save_tree
|
|
||||||
|
|
||||||
func _build_save_tree(tree: Dictionary):
|
|
||||||
var new_object = load(tree["filename"]).instantiate()
|
|
||||||
|
|
||||||
if new_object.has_method("_load"):
|
|
||||||
new_object.call("_load", VariantSerializer.parse_value(tree["data"]))
|
|
||||||
else:
|
|
||||||
for key in tree["data"].keys():
|
|
||||||
new_object.set(key, VariantSerializer.parse_value(tree["data"][key]))
|
|
||||||
|
|
||||||
get_node(tree["parent"]).add_child(new_object)
|
|
||||||
|
|
||||||
for child in tree["children"]:
|
|
||||||
_build_save_tree(child)
|
|
|
@ -34,13 +34,13 @@ func connect_ws():
|
||||||
if url == "" || token == "":
|
if url == "" || token == "":
|
||||||
return
|
return
|
||||||
|
|
||||||
print("Connecting to %s" % self.url)
|
print("Connecting to %s" % url + "/api/websocket")
|
||||||
socket.connect_to_url(self.url)
|
socket.connect_to_url(url + "/api/websocket")
|
||||||
set_process(true)
|
set_process(true)
|
||||||
|
|
||||||
# https://github.com/godotengine/godot/issues/84423
|
# https://github.com/godotengine/godot/issues/84423
|
||||||
# Otherwise the WebSocketPeer will crash when receiving large packets
|
# Otherwise the WebSocketPeer will crash when receiving large packets
|
||||||
socket.set_inbound_buffer_size(pow(2, 22)) # ~4MB buffer
|
socket.set_inbound_buffer_size(pow(2, 23)) # ~8MB buffer
|
||||||
|
|
||||||
func _process(delta):
|
func _process(delta):
|
||||||
socket.poll()
|
socket.poll()
|
||||||
|
|
5
lib/stores/devices.gd
Normal file
5
lib/stores/devices.gd
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
extends StoreClass
|
||||||
|
const StoreClass = preload("./store.gd")
|
||||||
|
|
||||||
|
func clear():
|
||||||
|
pass
|
30
lib/stores/house.gd
Normal file
30
lib/stores/house.gd
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
extends StoreClass
|
||||||
|
|
||||||
|
const StoreClass = preload("./store.gd")
|
||||||
|
|
||||||
|
# Type Room
|
||||||
|
# name: String
|
||||||
|
# corners: Vec2[]
|
||||||
|
# height: float
|
||||||
|
var rooms = []
|
||||||
|
# Type Entity
|
||||||
|
# id: String
|
||||||
|
# position: Vec3
|
||||||
|
# rotation: Vec3
|
||||||
|
# room: String
|
||||||
|
var entities = []
|
||||||
|
var align_position1: Vector3
|
||||||
|
var align_position2: Vector3
|
||||||
|
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
_save_path = "user://house.json"
|
||||||
|
|
||||||
|
func clear():
|
||||||
|
pass
|
||||||
|
|
||||||
|
func get_room(name):
|
||||||
|
for room in rooms:
|
||||||
|
if room.name == name:
|
||||||
|
return room
|
||||||
|
return null
|
16
lib/stores/settings.gd
Normal file
16
lib/stores/settings.gd
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
extends StoreClass
|
||||||
|
|
||||||
|
const StoreClass = preload("./store.gd")
|
||||||
|
|
||||||
|
|
||||||
|
var type: String = "HASS_WS"
|
||||||
|
var url: String = ""
|
||||||
|
var token: String = ""
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
_save_path = "user://settings.json"
|
||||||
|
|
||||||
|
func clear():
|
||||||
|
type = "HASS_WS"
|
||||||
|
url = ""
|
||||||
|
token = ""
|
84
lib/stores/store.gd
Normal file
84
lib/stores/store.gd
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
const VariantSerializer = preload("res://lib/utils/variant_serializer.gd")
|
||||||
|
|
||||||
|
signal on_loaded
|
||||||
|
signal on_saved
|
||||||
|
|
||||||
|
var _loaded = false
|
||||||
|
var _save_path = null
|
||||||
|
|
||||||
|
func is_loaded():
|
||||||
|
return _loaded
|
||||||
|
|
||||||
|
func clear():
|
||||||
|
pass
|
||||||
|
|
||||||
|
func create_dict():
|
||||||
|
var data: Dictionary = {}
|
||||||
|
|
||||||
|
for prop_info in get_property_list():
|
||||||
|
if prop_info.name.begins_with("_") || prop_info.hint_string != "":
|
||||||
|
continue
|
||||||
|
|
||||||
|
var prop = get(prop_info.name)
|
||||||
|
|
||||||
|
if prop is Store:
|
||||||
|
data[prop_info.name] = prop.create_dict()
|
||||||
|
else:
|
||||||
|
data[prop_info.name] = VariantSerializer.stringify_value(prop)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
func use_dict(dict: Dictionary):
|
||||||
|
for prop_info in get_property_list():
|
||||||
|
if prop_info.name.begins_with("_") || prop_info.hint_string != "":
|
||||||
|
continue
|
||||||
|
|
||||||
|
var prop = get(prop_info.name)
|
||||||
|
var prop_value = dict[prop_info.name]
|
||||||
|
|
||||||
|
if prop is Store:
|
||||||
|
prop.use_dict(prop_value)
|
||||||
|
else:
|
||||||
|
set(prop_info.name, prop_value)
|
||||||
|
|
||||||
|
func save_local(path = _save_path):
|
||||||
|
if path == null:
|
||||||
|
return false
|
||||||
|
|
||||||
|
var data = create_dict()
|
||||||
|
|
||||||
|
var save_file = FileAccess.open(path, FileAccess.WRITE)
|
||||||
|
|
||||||
|
if save_file == null:
|
||||||
|
return false
|
||||||
|
|
||||||
|
var json_text = JSON.stringify(data)
|
||||||
|
save_file.store_line(json_text)
|
||||||
|
|
||||||
|
on_saved.emit()
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
func load_local(path = _save_path):
|
||||||
|
if path == null:
|
||||||
|
return false
|
||||||
|
|
||||||
|
var save_file = FileAccess.open(path, FileAccess.READ)
|
||||||
|
|
||||||
|
if save_file == null:
|
||||||
|
return false
|
||||||
|
|
||||||
|
var json_text = save_file.get_as_text()
|
||||||
|
var save_data = VariantSerializer.parse_value(JSON.parse_string(json_text))
|
||||||
|
|
||||||
|
if save_data == null:
|
||||||
|
return false
|
||||||
|
|
||||||
|
use_dict(save_data)
|
||||||
|
|
||||||
|
_loaded = true
|
||||||
|
on_loaded.emit()
|
||||||
|
|
||||||
|
return true
|
|
@ -30,3 +30,9 @@ func change_to(new_state: String) -> void:
|
||||||
add_child(current_state)
|
add_child(current_state)
|
||||||
current_state._on_enter()
|
current_state._on_enter()
|
||||||
changed.emit(new_state, old_state)
|
changed.emit(new_state, old_state)
|
||||||
|
|
||||||
|
func get_state(state_name: String) -> Node:
|
||||||
|
if states.has(state_name) == false:
|
||||||
|
return null
|
||||||
|
|
||||||
|
return states[state_name]
|
|
@ -11,7 +11,7 @@ static func stringify_value(value):
|
||||||
return value.map(func(item):
|
return value.map(func(item):
|
||||||
return stringify_value(item)
|
return stringify_value(item)
|
||||||
)
|
)
|
||||||
TYPE_BOOL, TYPE_INT, TYPE_FLOAT, TYPE_STRING:
|
TYPE_BOOL, TYPE_INT, TYPE_FLOAT, TYPE_STRING, TYPE_NIL:
|
||||||
return value
|
return value
|
||||||
TYPE_VECTOR2:
|
TYPE_VECTOR2:
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -18,12 +18,11 @@ config/icon="res://assets/logo.png"
|
||||||
[autoload]
|
[autoload]
|
||||||
|
|
||||||
XRToolsUserSettings="*res://addons/godot-xr-tools/user_settings/user_settings.gd"
|
XRToolsUserSettings="*res://addons/godot-xr-tools/user_settings/user_settings.gd"
|
||||||
ConfigData="*res://lib/globals/config_data.gd"
|
|
||||||
Request="*res://lib/globals/request.gd"
|
Request="*res://lib/globals/request.gd"
|
||||||
HomeApi="*res://lib/globals/home_api.gd"
|
HomeApi="*res://lib/globals/home_api.gd"
|
||||||
AudioPlayer="*res://lib/globals/audio_player.gd"
|
AudioPlayer="*res://lib/globals/audio_player.gd"
|
||||||
EventSystem="*res://lib/globals/event_system.gd"
|
EventSystem="*res://lib/globals/event_system.gd"
|
||||||
SaveSystem="*res://lib/globals/save_system.gd"
|
Store="*res://lib/globals/main_store.gd"
|
||||||
House="*res://lib/globals/house_body.gd"
|
House="*res://lib/globals/house_body.gd"
|
||||||
|
|
||||||
[editor_plugins]
|
[editor_plugins]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user