Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a790e9d47e | |||
| 0130d89553 | |||
| 99da16474a | |||
| 949bc9f4ff | |||
| 1bb038d8b7 | |||
| 2f5f465799 | |||
| a1dec4b25c | |||
| 2534a72265 | |||
| 931aa8b350 | |||
| d0bb1ac5f0 | |||
| e76a7b1f83 | |||
| ea31a0363b | |||
| 04ea9c0be2 | |||
| a7da10c17c | |||
| 2c3fa47ac5 | |||
| a476687fbf |
123
actors/base_actor/base_actor.gd
Normal file
123
actors/base_actor/base_actor.gd
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
extends Node3D
|
||||||
|
|
||||||
|
class_name Actor
|
||||||
|
|
||||||
|
enum ACTOR_STATE {INACTIVE, ACTIVE, IDLE, PAUSED}
|
||||||
|
|
||||||
|
var actor_state = null: set = state_change
|
||||||
|
|
||||||
|
const red = preload("res://tiles/base_tile/red.tres")
|
||||||
|
const blue = preload("res://tiles/base_tile/lightblue.tres")
|
||||||
|
|
||||||
|
var current_place_path: Array
|
||||||
|
var current_room_path: Array
|
||||||
|
|
||||||
|
var temp_speed = 4
|
||||||
|
var time = 0
|
||||||
|
|
||||||
|
var current_tile = null: set = change_current_tile
|
||||||
|
var current_room = null
|
||||||
|
|
||||||
|
var lookup_actor_to_task = []
|
||||||
|
var current_task: Dictionary : set = task_change
|
||||||
|
|
||||||
|
@onready var task_creator: TaskCreator = $TaskCreator
|
||||||
|
|
||||||
|
signal current_tile_request
|
||||||
|
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
match actor_state:
|
||||||
|
ACTOR_STATE.ACTIVE:
|
||||||
|
get_current_tile()
|
||||||
|
|
||||||
|
if current_room_path:
|
||||||
|
|
||||||
|
if current_tile.grid_pos == current_task.location:
|
||||||
|
temp_complete_task()
|
||||||
|
|
||||||
|
else:
|
||||||
|
if self.position.distance_to(current_room_path[0]) > .1:
|
||||||
|
position += position.direction_to(Vector3(current_room_path[0])) * 6 * delta
|
||||||
|
else:
|
||||||
|
current_room_path.pop_front()
|
||||||
|
|
||||||
|
ACTOR_STATE.IDLE:
|
||||||
|
|
||||||
|
if not lookup_actor_to_task.is_empty():
|
||||||
|
current_task = lookup_actor_to_task[0]
|
||||||
|
else:
|
||||||
|
actor_state = ACTOR_STATE.PAUSED
|
||||||
|
|
||||||
|
ACTOR_STATE.PAUSED:
|
||||||
|
pass
|
||||||
|
|
||||||
|
func state_change(state):
|
||||||
|
actor_state = state
|
||||||
|
match state:
|
||||||
|
ACTOR_STATE.ACTIVE:
|
||||||
|
$SelfMesh.set_material_override(red)
|
||||||
|
ACTOR_STATE.IDLE:
|
||||||
|
$SelfMesh.set_material_override(null)
|
||||||
|
ACTOR_STATE.PAUSED:
|
||||||
|
$SelfMesh.set_material_override(blue)
|
||||||
|
await get_tree().create_timer(1).timeout
|
||||||
|
temp_create_task()
|
||||||
|
actor_state = ACTOR_STATE.IDLE
|
||||||
|
|
||||||
|
func task_change(task):
|
||||||
|
current_task = task
|
||||||
|
if current_task:
|
||||||
|
actor_state = ACTOR_STATE.ACTIVE
|
||||||
|
get_room_path()
|
||||||
|
|
||||||
|
func get_current_tile():
|
||||||
|
#Requests the tile that this actor is currently on
|
||||||
|
|
||||||
|
emit_signal("current_tile_request", self)
|
||||||
|
|
||||||
|
func change_current_tile(new_tile):
|
||||||
|
#General function for when this actor enters a new tile
|
||||||
|
|
||||||
|
if current_tile != new_tile:
|
||||||
|
current_tile = new_tile
|
||||||
|
get_room()
|
||||||
|
|
||||||
|
func get_room():
|
||||||
|
#Gets the room that the current tile is in
|
||||||
|
#TODO: can't handle tiles being in more than one room
|
||||||
|
|
||||||
|
if current_tile.lookup_tile_to_room:
|
||||||
|
current_room = current_tile.lookup_tile_to_room
|
||||||
|
else:
|
||||||
|
current_room = null
|
||||||
|
|
||||||
|
func temp_create_task():
|
||||||
|
var temp_task_target = Vector3i(randi_range(1, 31), 0, randi_range(1, 31))
|
||||||
|
task_creator.create_task(
|
||||||
|
self,
|
||||||
|
self,
|
||||||
|
temp_task_target,
|
||||||
|
)
|
||||||
|
|
||||||
|
func temp_complete_task():
|
||||||
|
if lookup_actor_to_task:
|
||||||
|
var completed_task = lookup_actor_to_task.pop_front()
|
||||||
|
current_task = {}
|
||||||
|
task_creator.complete_task(self, completed_task)
|
||||||
|
actor_state = ACTOR_STATE.IDLE
|
||||||
|
|
||||||
|
func get_room_path():
|
||||||
|
get_current_tile()
|
||||||
|
var start = current_tile.grid_pos
|
||||||
|
var end = current_task.location
|
||||||
|
|
||||||
|
if destination_in_current_room(end):
|
||||||
|
current_room_path = current_room.get_room_path(start, end)
|
||||||
|
else:
|
||||||
|
temp_complete_task()
|
||||||
|
|
||||||
|
func destination_in_current_room(destination):
|
||||||
|
for i in current_room.room_to_tile:
|
||||||
|
if i.grid_pos == destination:
|
||||||
|
return true
|
||||||
|
return false
|
||||||
1
actors/base_actor/base_actor.gd.uid
Normal file
1
actors/base_actor/base_actor.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dilighbu5t233
|
||||||
14
actors/base_actor/base_actor.tscn
Normal file
14
actors/base_actor/base_actor.tscn
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[gd_scene load_steps=4 format=3 uid="uid://jcmf542by4e6"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://dilighbu5t233" path="res://actors/base_actor/base_actor.gd" id="1_fkyrj"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://btpcj4sv1qgck" path="res://task_creator.tscn" id="2_onjxy"]
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleMesh" id="CapsuleMesh_wghh8"]
|
||||||
|
|
||||||
|
[node name="BaseActor" type="Node3D"]
|
||||||
|
script = ExtResource("1_fkyrj")
|
||||||
|
|
||||||
|
[node name="TaskCreator" parent="." instance=ExtResource("2_onjxy")]
|
||||||
|
|
||||||
|
[node name="SelfMesh" type="MeshInstance3D" parent="."]
|
||||||
|
mesh = SubResource("CapsuleMesh_wghh8")
|
||||||
21
addons/godot_object_serializer/LICENSE
Normal file
21
addons/godot_object_serializer/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Charles Crete
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
63
addons/godot_object_serializer/binary_serializer.gd
Normal file
63
addons/godot_object_serializer/binary_serializer.gd
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
## Serializer to be used with Godot's built-in binary serialization ([method @GlobalScope.var_to_bytes] and [method @GlobalScope.bytes_to_var]).
|
||||||
|
## This serializes objects but leaves built-in Godot types as-is.
|
||||||
|
class_name BinarySerializer
|
||||||
|
|
||||||
|
|
||||||
|
## Serialize [param data] to value which can be passed to [method @GlobalScope.var_to_bytes].
|
||||||
|
static func serialize_var(value: Variant) -> Variant:
|
||||||
|
match typeof(value):
|
||||||
|
TYPE_OBJECT:
|
||||||
|
var name: StringName = value.get_script().get_global_name()
|
||||||
|
var object_entry := ObjectSerializer._get_entry(name, value.get_script())
|
||||||
|
if !object_entry:
|
||||||
|
assert(
|
||||||
|
false,
|
||||||
|
(
|
||||||
|
"Could not find type (%s) in registry\n%s"
|
||||||
|
% [name if name else "no name", value.get_script().source_code]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return object_entry.serialize(value, serialize_var)
|
||||||
|
TYPE_ARRAY:
|
||||||
|
return value.map(serialize_var)
|
||||||
|
TYPE_DICTIONARY:
|
||||||
|
var result := {}
|
||||||
|
for i: Variant in value:
|
||||||
|
result[i] = serialize_var(value[i])
|
||||||
|
return result
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
## Serialize [param data] into bytes with [method BinaryObjectSerializer.serialize_var] and [method @GlobalScope.var_to_bytes].
|
||||||
|
static func serialize_bytes(value: Variant) -> PackedByteArray:
|
||||||
|
return var_to_bytes(serialize_var(value))
|
||||||
|
|
||||||
|
|
||||||
|
## Deserialize [param data] from [method @GlobalScope.bytes_to_var] to value.
|
||||||
|
static func deserialize_var(value: Variant) -> Variant:
|
||||||
|
match typeof(value):
|
||||||
|
TYPE_DICTIONARY:
|
||||||
|
if value.has(ObjectSerializer.type_field):
|
||||||
|
var type: String = value.get(ObjectSerializer.type_field)
|
||||||
|
if type.begins_with(ObjectSerializer.object_type_prefix):
|
||||||
|
var entry := ObjectSerializer._get_entry(type)
|
||||||
|
if !entry:
|
||||||
|
assert(false, "Could not find type (%s) in registry" % type)
|
||||||
|
|
||||||
|
return entry.deserialize(value, deserialize_var)
|
||||||
|
|
||||||
|
var result := {}
|
||||||
|
for i: Variant in value:
|
||||||
|
result[i] = deserialize_var(value[i])
|
||||||
|
return result
|
||||||
|
TYPE_ARRAY:
|
||||||
|
return value.map(deserialize_var)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
## Deserialize bytes [param data] to value with [method @GlobalScope.bytes_to_var] and [method BinaryObjectSerializer.deserialize_var].
|
||||||
|
static func deserialize_bytes(value: PackedByteArray) -> Variant:
|
||||||
|
return deserialize_var(bytes_to_var(value))
|
||||||
1
addons/godot_object_serializer/binary_serializer.gd.uid
Normal file
1
addons/godot_object_serializer/binary_serializer.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://82dofbvl1g6n
|
||||||
101
addons/godot_object_serializer/dictionary_serializer.gd
Normal file
101
addons/godot_object_serializer/dictionary_serializer.gd
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
## Serializer to be used with JSON serialization ([method JSON.stringify] and [method JSON.parse_string]).
|
||||||
|
## This serializes objects and built-in Godot types.
|
||||||
|
class_name DictionarySerializer
|
||||||
|
|
||||||
|
# Types that can natively be represented in JSON
|
||||||
|
const _JSON_SERIALIZABLE_TYPES = [
|
||||||
|
TYPE_NIL, TYPE_BOOL, TYPE_INT, TYPE_FLOAT, TYPE_STRING, TYPE_STRING_NAME
|
||||||
|
]
|
||||||
|
|
||||||
|
## Controls if [PackedByteArray] should be serialized as base64 (instead of array of bytes as uint8).
|
||||||
|
## It's highly recommended to leave this enabled as it will result to smaller serialized payloads and should be faster.
|
||||||
|
## This can be changed, but must be configured before any serialization or deserialization.
|
||||||
|
static var bytes_as_base64 := true
|
||||||
|
## The type of the object for [PackedByteArray] when [member bytes_as_base64] is enabled.
|
||||||
|
## This should be set to something unlikely to clash with built-in type names or [member ObjectSerializer.object_type_prefix].
|
||||||
|
## This can be changed, but must be configured before any serialization or deserialization.
|
||||||
|
static var bytes_to_base64_type := "PackedByteArray_Base64"
|
||||||
|
|
||||||
|
|
||||||
|
## Serialize [param data] into value which can be passed to [method JSON.stringify].
|
||||||
|
static func serialize_var(value: Variant) -> Variant:
|
||||||
|
match typeof(value):
|
||||||
|
TYPE_OBJECT:
|
||||||
|
var name: StringName = value.get_script().get_global_name()
|
||||||
|
var object_entry := ObjectSerializer._get_entry(name, value.get_script())
|
||||||
|
if !object_entry:
|
||||||
|
assert(
|
||||||
|
false,
|
||||||
|
(
|
||||||
|
"Could not find type (%s) in registry\n%s"
|
||||||
|
% [name if name else "no name", value.get_script().source_code]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return object_entry.serialize(value, serialize_var)
|
||||||
|
|
||||||
|
TYPE_ARRAY:
|
||||||
|
return value.map(serialize_var)
|
||||||
|
|
||||||
|
TYPE_DICTIONARY:
|
||||||
|
var result := {}
|
||||||
|
for i: Variant in value:
|
||||||
|
result[i] = serialize_var(value[i])
|
||||||
|
return result
|
||||||
|
|
||||||
|
TYPE_PACKED_BYTE_ARRAY:
|
||||||
|
if bytes_as_base64:
|
||||||
|
return {
|
||||||
|
ObjectSerializer.type_field: bytes_to_base64_type,
|
||||||
|
ObjectSerializer.args_field:
|
||||||
|
Marshalls.raw_to_base64(value) if !value.is_empty() else ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if _JSON_SERIALIZABLE_TYPES.has(typeof(value)):
|
||||||
|
return value
|
||||||
|
|
||||||
|
return {
|
||||||
|
ObjectSerializer.type_field: type_string(typeof(value)),
|
||||||
|
ObjectSerializer.args_field: JSON.from_native(value)["args"]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
## Serialize [param data] into JSON string with [method DictionaryObjectSerializer.serialize_var] and [method JSON.stringify]. Supports same arguments as [method JSON.stringify]
|
||||||
|
static func serialize_json(
|
||||||
|
value: Variant, indent := "", sort_keys := true, full_precision := false
|
||||||
|
) -> String:
|
||||||
|
return JSON.stringify(serialize_var(value), indent, sort_keys, full_precision)
|
||||||
|
|
||||||
|
|
||||||
|
## Deserialize [data] from [JSON.parse_string] into value.
|
||||||
|
static func deserialize_var(value: Variant) -> Variant:
|
||||||
|
match typeof(value):
|
||||||
|
TYPE_DICTIONARY:
|
||||||
|
if value.has(ObjectSerializer.type_field):
|
||||||
|
var type: String = value.get(ObjectSerializer.type_field)
|
||||||
|
if bytes_as_base64 and type == bytes_to_base64_type:
|
||||||
|
return Marshalls.base64_to_raw(value[ObjectSerializer.args_field])
|
||||||
|
|
||||||
|
if type.begins_with(ObjectSerializer.object_type_prefix):
|
||||||
|
var entry := ObjectSerializer._get_entry(type)
|
||||||
|
if !entry:
|
||||||
|
assert(false, "Could not find type (%s) in registry" % type)
|
||||||
|
|
||||||
|
return entry.deserialize(value, deserialize_var, true)
|
||||||
|
|
||||||
|
return JSON.to_native({"type": type, "args": value[ObjectSerializer.args_field]})
|
||||||
|
|
||||||
|
var result := {}
|
||||||
|
for i: Variant in value:
|
||||||
|
result[i] = deserialize_var(value[i])
|
||||||
|
return result
|
||||||
|
|
||||||
|
TYPE_ARRAY:
|
||||||
|
return value.map(deserialize_var)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
## Deserialize JSON string [param data] to value with [method JSON.parse_string] and [method DictionaryObjectSerializer.deserialize_var].
|
||||||
|
static func deserialize_json(value: String) -> Variant:
|
||||||
|
return deserialize_var(JSON.parse_string(value))
|
||||||
@ -0,0 +1 @@
|
|||||||
|
uid://cejorwceoqbqe
|
||||||
131
addons/godot_object_serializer/example
Normal file
131
addons/godot_object_serializer/example
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
|
||||||
|
# Example data class. Can extend any type, include Resource
|
||||||
|
class Data:
|
||||||
|
# Supports all primitive types (String, int, float, bool, null), including @export variables
|
||||||
|
@export var string: String
|
||||||
|
# Supports all extended built-in types (Vector2/3/4/i, Rect2/i, Transform2D/3D, Color, Packed*Array, etc)
|
||||||
|
var vector: Vector3
|
||||||
|
# Supports enum
|
||||||
|
var enum_state: State
|
||||||
|
# Supports arrays, including Array[Variant]
|
||||||
|
var array: Array[int]
|
||||||
|
# Supports dictionaries, including Dictionary[Variant, Variant]
|
||||||
|
var dictionary: Dictionary[String, Vector2]
|
||||||
|
# Supports efficient byte array serialization to base64
|
||||||
|
var packed_byte_array: PackedByteArray
|
||||||
|
# Supports nested data, either as a field or in array/dictionary
|
||||||
|
var nested: DataResource
|
||||||
|
|
||||||
|
|
||||||
|
class DataResource:
|
||||||
|
extends Resource
|
||||||
|
var name: String
|
||||||
|
|
||||||
|
|
||||||
|
enum State { OPENED, CLOSED }
|
||||||
|
|
||||||
|
|
||||||
|
# _static_init is used to register scripts without having to instanciate the script.
|
||||||
|
# It's recommended to either place all registrations in a single script, or have each script register itself.
|
||||||
|
static func _static_init() -> void:
|
||||||
|
# Required: Register possible object scripts
|
||||||
|
ObjectSerializer.register_script("Data", Data)
|
||||||
|
ObjectSerializer.register_script("DataResource", DataResource)
|
||||||
|
|
||||||
|
|
||||||
|
# Setup testing data
|
||||||
|
var data := Data.new()
|
||||||
|
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
data.string = "Lorem ipsum"
|
||||||
|
data.vector = Vector3(1, 2, 3)
|
||||||
|
data.enum_state = State.CLOSED
|
||||||
|
data.array = [1, 2]
|
||||||
|
data.dictionary = {"position": Vector2(1, 2)}
|
||||||
|
data.packed_byte_array = PackedByteArray([1, 2, 3, 4, 5, 6, 7, 8])
|
||||||
|
var data_resource := DataResource.new()
|
||||||
|
data_resource.name = "dolor sit amet"
|
||||||
|
data.nested = data_resource
|
||||||
|
|
||||||
|
json_serialization()
|
||||||
|
binary_serialization()
|
||||||
|
|
||||||
|
|
||||||
|
func json_serialization() -> void:
|
||||||
|
# Serialize to JSON
|
||||||
|
# Alternative: DictionarySerializer.serialize_json(data)
|
||||||
|
var serialized: Variant = DictionarySerializer.serialize_var(data)
|
||||||
|
var json := JSON.stringify(serialized, "\t")
|
||||||
|
print(json)
|
||||||
|
""" Output:
|
||||||
|
{
|
||||||
|
"._type": "Object_Data",
|
||||||
|
"string": "Lorem ipsum",
|
||||||
|
"vector": {
|
||||||
|
"._type": "Vector3",
|
||||||
|
"._": [
|
||||||
|
1.0,
|
||||||
|
2.0,
|
||||||
|
3.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"enum_state": 1,
|
||||||
|
"array": [
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"dictionary": {
|
||||||
|
"position": {
|
||||||
|
"._type": "Vector2",
|
||||||
|
"._": [
|
||||||
|
1.0,
|
||||||
|
2.0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"packed_byte_array": {
|
||||||
|
"._type": "PackedByteArray_Base64",
|
||||||
|
"._": "AQIDBAUGBwg="
|
||||||
|
},
|
||||||
|
"nested": {
|
||||||
|
"._type": "Object_DataResource",
|
||||||
|
"name": "dolor sit amet"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Verify after JSON deserialization
|
||||||
|
# Alternative: DictionarySerializer.deserialize_json(json)
|
||||||
|
var parsed_json = JSON.parse_string(json)
|
||||||
|
var deserialized: Data = DictionarySerializer.deserialize_var(parsed_json)
|
||||||
|
_assert_data(deserialized)
|
||||||
|
|
||||||
|
|
||||||
|
func binary_serialization() -> void:
|
||||||
|
# Serialize to bytes
|
||||||
|
# Alternative: BinarySerializer.serialize_bytes(data)
|
||||||
|
var serialized: Variant = BinarySerializer.serialize_var(data)
|
||||||
|
var bytes := var_to_bytes(serialized)
|
||||||
|
print(bytes)
|
||||||
|
# Output: List of bytes
|
||||||
|
|
||||||
|
# Verify after bytes deserialization.
|
||||||
|
# Alternative: BinarySerializer.deserialize_bytes(bytes)
|
||||||
|
var parsed_bytes = bytes_to_var(bytes)
|
||||||
|
var deserialized: Data = BinarySerializer.deserialize_var(parsed_bytes)
|
||||||
|
_assert_data(deserialized)
|
||||||
|
|
||||||
|
|
||||||
|
func _assert_data(deserialized: Data) -> void:
|
||||||
|
assert(data.string == deserialized.string, "string is different")
|
||||||
|
assert(data.vector == deserialized.vector, "vector is different")
|
||||||
|
assert(data.enum_state == deserialized.enum_state, "enum_state is different")
|
||||||
|
assert(data.array == deserialized.array, "array is different")
|
||||||
|
assert(data.dictionary == deserialized.dictionary, "dictionary is different")
|
||||||
|
assert(
|
||||||
|
data.packed_byte_array == deserialized.packed_byte_array, "packed_byte_array is different"
|
||||||
|
)
|
||||||
|
assert(data.nested.name == deserialized.nested.name, "nested.name is different")
|
||||||
1
addons/godot_object_serializer/example.uid
Normal file
1
addons/godot_object_serializer/example.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://cnmoj6e2jq737
|
||||||
185
addons/godot_object_serializer/object_serializer.gd
Normal file
185
addons/godot_object_serializer/object_serializer.gd
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
## Main godot-object-serializer class. Stores the script registry.
|
||||||
|
## [url]https://github.com/Cretezy/godot-object-serializer[/url]
|
||||||
|
class_name ObjectSerializer
|
||||||
|
|
||||||
|
## The field containing the type in serialized object values. Not recommended to change.
|
||||||
|
##
|
||||||
|
## This should be set to something unlikely to clash with keys in objects/dictionaries.
|
||||||
|
##
|
||||||
|
## This can be changed, but must be configured before any serialization or deserialization.
|
||||||
|
static var type_field := "._type"
|
||||||
|
|
||||||
|
## The field containing the constructor arguments in serialized object values. Not recommended to change.
|
||||||
|
##
|
||||||
|
## This should be set to something unlikely to clash with keys in objects.
|
||||||
|
##
|
||||||
|
## This can be changed, but must be configured before any serialization or deserialization.
|
||||||
|
static var args_field := "._"
|
||||||
|
|
||||||
|
## The prefix for object types stored in [member type_field]. Not recommended to change.
|
||||||
|
##
|
||||||
|
## This should be set to something unlikely to clash with built-in type names.
|
||||||
|
##
|
||||||
|
## This can be changed, but must be configured before any serialization or deserialization.
|
||||||
|
static var object_type_prefix := "Object_"
|
||||||
|
|
||||||
|
## By default, variables with [@GlobalScope.PROPERTY_USAGE_SCRIPT_VARIABLE] are serialized (all variables have this by default).
|
||||||
|
## When [member require_export_storage] is true, variables will also require [@GlobalScope.PROPERTY_USAGE_STORAGE] to be serialized.
|
||||||
|
## This can be set on variables using [annotation @GDScript.@export_storage]. Example: [code]@export_storage var name: String[/code]
|
||||||
|
static var require_export_storage := false
|
||||||
|
|
||||||
|
## Registry of object types
|
||||||
|
static var _script_registry: Dictionary[String, _ScriptRegistryEntry]
|
||||||
|
|
||||||
|
|
||||||
|
## Registers a script (an object type) to be serialized/deserialized. All custom types (including nested types) must be registered [b]before[/b] using this library.
|
||||||
|
## [param name] can be empty if script uses [code]class_name[/code] (e.g [code]ObjectSerializer.register_script("", Data)[/code]), but it's generally better to set the name.
|
||||||
|
static func register_script(name: StringName, script: Script) -> void:
|
||||||
|
var script_name := _get_script_name(script, name)
|
||||||
|
assert(script_name, "Script must have name\n" + script.source_code)
|
||||||
|
var entry := _ScriptRegistryEntry.new()
|
||||||
|
entry.script_type = script
|
||||||
|
entry.type = object_type_prefix + script_name
|
||||||
|
_script_registry[entry.type] = entry
|
||||||
|
|
||||||
|
|
||||||
|
## Registers multiple scripts (object types) to be serialized/deserialized from a dictionary.
|
||||||
|
## See [method ObjectSerializer.register_script]
|
||||||
|
static func register_scripts(scripts: Dictionary[String, Script]) -> void:
|
||||||
|
for name in scripts:
|
||||||
|
register_script(name, scripts[name])
|
||||||
|
|
||||||
|
|
||||||
|
static func _get_script_name(script: Script, name: StringName = "") -> StringName:
|
||||||
|
if name:
|
||||||
|
return name
|
||||||
|
if script.resource_name:
|
||||||
|
return script.resource_name
|
||||||
|
if script.get_global_name():
|
||||||
|
return script.get_global_name()
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
static func _get_entry(name: StringName = "", script: Script = null) -> _ScriptRegistryEntry:
|
||||||
|
if name:
|
||||||
|
var entry: _ScriptRegistryEntry = _script_registry.get(name)
|
||||||
|
if entry:
|
||||||
|
return entry
|
||||||
|
|
||||||
|
if script:
|
||||||
|
for i: String in _script_registry:
|
||||||
|
var entry: _ScriptRegistryEntry = _script_registry.get(i)
|
||||||
|
if entry:
|
||||||
|
if script == entry.script_type:
|
||||||
|
return entry
|
||||||
|
|
||||||
|
return null
|
||||||
|
|
||||||
|
|
||||||
|
class _ScriptRegistryEntry:
|
||||||
|
var type: String
|
||||||
|
var script_type: Script
|
||||||
|
|
||||||
|
func serialize(value: Variant, next: Callable) -> Variant:
|
||||||
|
if value.has_method("_serialize"):
|
||||||
|
var result: Dictionary = value._serialize(next)
|
||||||
|
result[ObjectSerializer.type_field] = type
|
||||||
|
return result
|
||||||
|
|
||||||
|
var result := {ObjectSerializer.type_field: type}
|
||||||
|
|
||||||
|
var excluded_properties: Array[String] = []
|
||||||
|
if value.has_method("_get_excluded_properties"):
|
||||||
|
excluded_properties = value._get_excluded_properties()
|
||||||
|
|
||||||
|
var partial: Dictionary = {}
|
||||||
|
if value.has_method("_serialize_partial"):
|
||||||
|
partial = value._serialize_partial(next)
|
||||||
|
|
||||||
|
for property: Dictionary in value.get_property_list():
|
||||||
|
if (
|
||||||
|
property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE
|
||||||
|
and (
|
||||||
|
!ObjectSerializer.require_export_storage
|
||||||
|
or property.usage & PROPERTY_USAGE_STORAGE
|
||||||
|
)
|
||||||
|
and !excluded_properties.has(property.name)
|
||||||
|
and !partial.has(property.name)
|
||||||
|
):
|
||||||
|
result[property.name] = next.call(value.get(property.name))
|
||||||
|
|
||||||
|
for key in partial:
|
||||||
|
result[key] = partial[key]
|
||||||
|
|
||||||
|
if value.has_method("_get_constructor_args"):
|
||||||
|
var args: Array = value._get_constructor_args()
|
||||||
|
result[ObjectSerializer.args_field] = args
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
## When [param json_keys] is enabled, attempt to convert int/float/bool string keys into values
|
||||||
|
func deserialize(value: Variant, next: Callable, json_keys := false) -> Variant:
|
||||||
|
if script_type.has_method("_deserialize"):
|
||||||
|
return script_type._deserialize(value, next)
|
||||||
|
|
||||||
|
var instance: Variant
|
||||||
|
if value.has(ObjectSerializer.args_field):
|
||||||
|
instance = script_type.new.callv(value[ObjectSerializer.args_field])
|
||||||
|
else:
|
||||||
|
instance = script_type.new()
|
||||||
|
|
||||||
|
var excluded_properties: Array[String] = []
|
||||||
|
if instance.has_method("_get_excluded_properties"):
|
||||||
|
excluded_properties = instance._get_excluded_properties()
|
||||||
|
|
||||||
|
var partial: Dictionary = {}
|
||||||
|
if instance.has_method("_deserialize_partial"):
|
||||||
|
partial = instance._deserialize_partial(value, next)
|
||||||
|
|
||||||
|
for key: String in value:
|
||||||
|
if (
|
||||||
|
key == ObjectSerializer.type_field
|
||||||
|
or key == ObjectSerializer.args_field
|
||||||
|
or excluded_properties.has(key)
|
||||||
|
or partial.has(key)
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
var key_value: Variant = next.call(value[key])
|
||||||
|
match typeof(key_value):
|
||||||
|
TYPE_DICTIONARY:
|
||||||
|
if json_keys and instance[key].is_typed_key():
|
||||||
|
match instance[key].get_typed_key_builtin():
|
||||||
|
TYPE_STRING:
|
||||||
|
instance[key].assign(key_value)
|
||||||
|
TYPE_BOOL:
|
||||||
|
var dict: Dictionary[bool, Variant] = {}
|
||||||
|
for i in key_value:
|
||||||
|
dict[i == "true"] = key_value[i]
|
||||||
|
instance[key].assign(dict)
|
||||||
|
TYPE_INT:
|
||||||
|
var dict: Dictionary[int, Variant] = {}
|
||||||
|
for i in key_value:
|
||||||
|
dict[int(i)] = key_value[i]
|
||||||
|
instance[key].assign(dict)
|
||||||
|
TYPE_FLOAT:
|
||||||
|
var dict: Dictionary[float, Variant] = {}
|
||||||
|
for i in key_value:
|
||||||
|
dict[float(i)] = key_value[i]
|
||||||
|
instance[key].assign(dict)
|
||||||
|
_:
|
||||||
|
assert(
|
||||||
|
false,
|
||||||
|
"Trying to deserialize from JSON to a dictionary with non-primitive (String/int/float/bool) keys"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
instance[key].assign(key_value)
|
||||||
|
TYPE_ARRAY:
|
||||||
|
instance[key].assign(key_value)
|
||||||
|
_:
|
||||||
|
instance[key] = key_value
|
||||||
|
|
||||||
|
for key in partial:
|
||||||
|
instance[key] = partial[key]
|
||||||
|
|
||||||
|
return instance
|
||||||
1
addons/godot_object_serializer/object_serializer.gd.uid
Normal file
1
addons/godot_object_serializer/object_serializer.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dn0k1lmeff72p
|
||||||
7
addons/godot_object_serializer/plugin.cfg
Normal file
7
addons/godot_object_serializer/plugin.cfg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[plugin]
|
||||||
|
|
||||||
|
name="Godot Object Serializer"
|
||||||
|
description="Safely serialize/deserialize objects and built-in types in Godot"
|
||||||
|
author="Cretezy"
|
||||||
|
version="0.3.0"
|
||||||
|
script=""
|
||||||
1
game.gd.uid
Normal file
1
game.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://b2ytri51qn3r0
|
||||||
79
game.tscn
Normal file
79
game.tscn
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
[gd_scene load_steps=6 format=3 uid="uid://x8xo5b1b3q41"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://b2ytri51qn3r0" path="res://game.gd" id="1_feb5d"]
|
||||||
|
[ext_resource type="Script" uid="uid://bi7t6jxlfl0ln" path="res://interface/build_toggle.gd" id="2_7jktm"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://0d1d5e1u2fys" path="res://places/place_manager.tscn" id="2_fc0e3"]
|
||||||
|
[ext_resource type="Script" uid="uid://cgfibq8ku7tey" path="res://interface/game_cam.gd" id="3_7jktm"]
|
||||||
|
[ext_resource type="Script" uid="uid://bdf7ll8y2htic" path="res://interface/save_load.gd" id="5_7jktm"]
|
||||||
|
|
||||||
|
[node name="Game" type="Node"]
|
||||||
|
script = ExtResource("1_feb5d")
|
||||||
|
|
||||||
|
[node name="InterfaceLayer" type="CanvasLayer" parent="."]
|
||||||
|
|
||||||
|
[node name="BuildToggle" type="CheckButton" parent="InterfaceLayer"]
|
||||||
|
offset_left = 7.0
|
||||||
|
offset_top = 603.0
|
||||||
|
offset_right = 119.0
|
||||||
|
offset_bottom = 634.0
|
||||||
|
text = "Build A Room"
|
||||||
|
script = ExtResource("2_7jktm")
|
||||||
|
|
||||||
|
[node name="BuildButton" type="Button" parent="InterfaceLayer"]
|
||||||
|
offset_left = 172.0
|
||||||
|
offset_top = 604.0
|
||||||
|
offset_right = 243.0
|
||||||
|
offset_bottom = 635.0
|
||||||
|
text = "Confirm"
|
||||||
|
|
||||||
|
[node name="SaveButton" type="Button" parent="InterfaceLayer"]
|
||||||
|
offset_left = 995.0
|
||||||
|
offset_top = 609.0
|
||||||
|
offset_right = 1141.0
|
||||||
|
offset_bottom = 640.0
|
||||||
|
text = "Save Current Map
|
||||||
|
"
|
||||||
|
|
||||||
|
[node name="LoadButton" type="Button" parent="InterfaceLayer"]
|
||||||
|
offset_left = 996.0
|
||||||
|
offset_top = 573.0
|
||||||
|
offset_right = 1142.0
|
||||||
|
offset_bottom = 604.0
|
||||||
|
text = "Load Map"
|
||||||
|
|
||||||
|
[node name="GridButton" type="Button" parent="InterfaceLayer"]
|
||||||
|
offset_left = 18.0
|
||||||
|
offset_top = 568.0
|
||||||
|
offset_right = 89.0
|
||||||
|
offset_bottom = 599.0
|
||||||
|
text = "Init Grid"
|
||||||
|
|
||||||
|
[node name="DoorButton" type="Button" parent="InterfaceLayer"]
|
||||||
|
offset_left = 255.0
|
||||||
|
offset_top = 603.0
|
||||||
|
offset_right = 347.0
|
||||||
|
offset_bottom = 634.0
|
||||||
|
text = "Place Door"
|
||||||
|
|
||||||
|
[node name="WorkerButton" type="Button" parent="InterfaceLayer"]
|
||||||
|
offset_right = 8.0
|
||||||
|
offset_bottom = 8.0
|
||||||
|
text = "Place Worker"
|
||||||
|
|
||||||
|
[node name="GameCam" type="Camera3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 0.182236, 0.983255, 0, -0.983255, 0.182236, 24.5127, 23.2849, 20.9667)
|
||||||
|
size = 31.34
|
||||||
|
script = ExtResource("3_7jktm")
|
||||||
|
|
||||||
|
[node name="PlaceManager" parent="." instance=ExtResource("2_fc0e3")]
|
||||||
|
|
||||||
|
[node name="SaveLoad" type="Node" parent="."]
|
||||||
|
script = ExtResource("5_7jktm")
|
||||||
|
|
||||||
|
[connection signal="toggled" from="InterfaceLayer/BuildToggle" to="PlaceManager" method="_on_build_toggle"]
|
||||||
|
[connection signal="pressed" from="InterfaceLayer/BuildButton" to="PlaceManager" method="_on_confirm_button_pressed"]
|
||||||
|
[connection signal="pressed" from="InterfaceLayer/SaveButton" to="SaveLoad" method="_on_save_button_pressed"]
|
||||||
|
[connection signal="pressed" from="InterfaceLayer/LoadButton" to="SaveLoad" method="_on_load_button_pressed"]
|
||||||
|
[connection signal="pressed" from="InterfaceLayer/GridButton" to="PlaceManager" method="_on_init_grid_button_pressed"]
|
||||||
|
[connection signal="pressed" from="InterfaceLayer/DoorButton" to="PlaceManager" method="_on_door_button_pressed"]
|
||||||
|
[connection signal="pressed" from="InterfaceLayer/WorkerButton" to="PlaceManager" method="_on_worker_button_pressed"]
|
||||||
5
interface/build_toggle.gd
Normal file
5
interface/build_toggle.gd
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
extends CheckButton
|
||||||
|
|
||||||
|
|
||||||
|
func _on_room_built() -> void:
|
||||||
|
button_pressed = false
|
||||||
1
interface/build_toggle.gd.uid
Normal file
1
interface/build_toggle.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://bi7t6jxlfl0ln
|
||||||
26
interface/game_cam.gd
Normal file
26
interface/game_cam.gd
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
extends Camera3D
|
||||||
|
|
||||||
|
@export var cam_max_pan_speed = 0.5
|
||||||
|
@export var cam_max_zoom_speed = 2
|
||||||
|
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
move_camera()
|
||||||
|
|
||||||
|
func move_camera():
|
||||||
|
if Input.is_action_pressed("main_cam_up"):
|
||||||
|
position.z = position.z - cam_max_pan_speed
|
||||||
|
|
||||||
|
if Input.is_action_pressed("main_cam_right"):
|
||||||
|
position.x = position.x + cam_max_pan_speed
|
||||||
|
|
||||||
|
if Input.is_action_pressed("main_cam_down"):
|
||||||
|
position.z = position.z + cam_max_pan_speed
|
||||||
|
|
||||||
|
if Input.is_action_pressed("main_cam_left"):
|
||||||
|
position.x = position.x - cam_max_pan_speed
|
||||||
|
|
||||||
|
if Input.is_action_just_released("main_cam_in"):
|
||||||
|
fov = fov - cam_max_zoom_speed
|
||||||
|
|
||||||
|
if Input.is_action_just_released("main_cam_out"):
|
||||||
|
fov = fov + cam_max_zoom_speed
|
||||||
1
interface/game_cam.gd.uid
Normal file
1
interface/game_cam.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://cgfibq8ku7tey
|
||||||
181
interface/save_load.gd
Normal file
181
interface/save_load.gd
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
extends Node
|
||||||
|
|
||||||
|
class SaveData:
|
||||||
|
var id: int
|
||||||
|
var type: String
|
||||||
|
var parent: String
|
||||||
|
var scene_file_path: String
|
||||||
|
|
||||||
|
var position: Vector3
|
||||||
|
var rotation_degrees
|
||||||
|
|
||||||
|
#Connections
|
||||||
|
var place_id: int
|
||||||
|
var room_ids: Array
|
||||||
|
var tile_ids: Array
|
||||||
|
|
||||||
|
var door_to_tile: Dictionary
|
||||||
|
|
||||||
|
#Tile data
|
||||||
|
var grid_pos: Vector3i
|
||||||
|
var face_dict: Array
|
||||||
|
var construction_mode: int
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
ObjectSerializer.register_scripts({
|
||||||
|
"SaveData": SaveData,
|
||||||
|
"Tile": Tile,
|
||||||
|
"Room": Room,
|
||||||
|
"Wall": Wall,
|
||||||
|
"Door": Door,
|
||||||
|
})
|
||||||
|
|
||||||
|
func _on_save_button_pressed() -> void:
|
||||||
|
var save_nodes = get_tree().get_nodes_in_group("SaveObjects")
|
||||||
|
var save_file = FileAccess.open("user://savegame.save", FileAccess.WRITE)
|
||||||
|
for node in save_nodes:
|
||||||
|
|
||||||
|
if not node.has_method("save"):
|
||||||
|
print("no method")
|
||||||
|
continue
|
||||||
|
|
||||||
|
var node_data = node.save()
|
||||||
|
var save_data = SaveData.new()
|
||||||
|
for i in node_data:
|
||||||
|
save_data.set(i, node_data[i])
|
||||||
|
|
||||||
|
var serialized_save_data = BinarySerializer.serialize_var(save_data)
|
||||||
|
|
||||||
|
var byte_data = var_to_bytes(serialized_save_data)
|
||||||
|
|
||||||
|
save_file.store_var(byte_data)
|
||||||
|
|
||||||
|
pass # Replace with function body.
|
||||||
|
|
||||||
|
func _on_load_button_pressed():
|
||||||
|
|
||||||
|
if not FileAccess.file_exists("user://savegame.save"):
|
||||||
|
print("no save to load")
|
||||||
|
return
|
||||||
|
|
||||||
|
var save_nodes = get_tree().get_nodes_in_group("SaveObjects")
|
||||||
|
for i in save_nodes:
|
||||||
|
if i:
|
||||||
|
i.free()
|
||||||
|
|
||||||
|
var save_file = FileAccess.open("user://savegame.save", FileAccess.READ)
|
||||||
|
|
||||||
|
var place_id_dict: Dictionary = {}
|
||||||
|
var room_id_dict: Dictionary = {}
|
||||||
|
var tile_id_dict: Dictionary = {}
|
||||||
|
|
||||||
|
var load_place_to_room: Dictionary = {}
|
||||||
|
var load_place_to_tile: Dictionary = {}
|
||||||
|
var load_room_to_tile: Dictionary = {}
|
||||||
|
var load_room_to_door: Dictionary = {}
|
||||||
|
var load_tile_to_door: Array = []
|
||||||
|
|
||||||
|
while save_file.get_position() < save_file.get_length():
|
||||||
|
|
||||||
|
var next_load_bytes = save_file.get_var()
|
||||||
|
var next_load = bytes_to_var(next_load_bytes)
|
||||||
|
var load_object = load(next_load["scene_file_path"]).instantiate()
|
||||||
|
var id = next_load["id"]
|
||||||
|
|
||||||
|
match next_load["type"]:
|
||||||
|
"Place":
|
||||||
|
place_id_dict[id] = load_object.get_instance_id()
|
||||||
|
load_place_to_room[load_object] = []
|
||||||
|
load_place_to_tile[load_object] = {}
|
||||||
|
|
||||||
|
for j in next_load.keys():
|
||||||
|
load_object.set(j, next_load[j])
|
||||||
|
get_node(next_load["parent"]).add_child(load_object)
|
||||||
|
|
||||||
|
"Room":
|
||||||
|
room_id_dict[id] = load_object.get_instance_id()
|
||||||
|
load_room_to_tile[load_object] = []
|
||||||
|
load_room_to_door[load_object] = []
|
||||||
|
|
||||||
|
for j in next_load.keys():
|
||||||
|
if j == "place_id":
|
||||||
|
var new_place_id = place_id_dict[next_load[j]]
|
||||||
|
var place = instance_from_id(new_place_id)
|
||||||
|
load_place_to_room[place].append(load_object)
|
||||||
|
load_object.set(j, new_place_id)
|
||||||
|
else:
|
||||||
|
load_object.set(j, next_load[j])
|
||||||
|
instance_from_id(load_object["place_id"]).get_node("RoomContainer").add_child(load_object)
|
||||||
|
|
||||||
|
"Tile":
|
||||||
|
tile_id_dict[id] = load_object.get_instance_id()
|
||||||
|
|
||||||
|
for j in next_load.keys():
|
||||||
|
if j == "place_id":
|
||||||
|
var new_place_id = place_id_dict[next_load[j]]
|
||||||
|
var place = instance_from_id(new_place_id)
|
||||||
|
load_place_to_tile[place].merge({next_load["grid_pos"]: load_object})
|
||||||
|
load_object.set(j, new_place_id)
|
||||||
|
elif j == "main_room":
|
||||||
|
var new_room_id = room_id_dict[j]
|
||||||
|
var room = instance_from_id(new_room_id)
|
||||||
|
load_room_to_tile[room].append(load_object)
|
||||||
|
load_object["lookup_tile_to_room"] = room
|
||||||
|
elif j == "face_dict":
|
||||||
|
if next_load[j]:
|
||||||
|
var temp_new_dict: Dictionary
|
||||||
|
for k in next_load[j]:
|
||||||
|
temp_new_dict[k] = null
|
||||||
|
load_object[j] = temp_new_dict.duplicate()
|
||||||
|
|
||||||
|
else:
|
||||||
|
load_object.set(j, next_load[j])
|
||||||
|
instance_from_id(load_object["place_id"]).get_node("TileContainer").add_child(load_object)
|
||||||
|
|
||||||
|
"Door":
|
||||||
|
for j in next_load.keys():
|
||||||
|
if j == "place_id":
|
||||||
|
var new_place_id = place_id_dict[next_load[j]]
|
||||||
|
load_object.set(j, new_place_id)
|
||||||
|
elif j == "room_ids":
|
||||||
|
for k in next_load["room_ids"]:
|
||||||
|
var new_room_id = room_id_dict[k]
|
||||||
|
var room = instance_from_id(new_room_id)
|
||||||
|
load_room_to_door[room].append(load_object)
|
||||||
|
load_object["lookup_door_to_room"].append(room)
|
||||||
|
elif j == "door_to_tile":
|
||||||
|
var tile_dict = next_load["door_to_tile"]
|
||||||
|
for k in tile_dict.keys():
|
||||||
|
var new_tile_id = tile_id_dict[tile_dict[k]]
|
||||||
|
var tile = instance_from_id(new_tile_id)
|
||||||
|
|
||||||
|
load_tile_to_door.append({tile: {k: load_object}})
|
||||||
|
|
||||||
|
load_object["lookup_door_to_tile"].merge({k: tile})
|
||||||
|
else:
|
||||||
|
load_object.set(j, next_load[j])
|
||||||
|
instance_from_id(load_object["place_id"]).get_node("ObjectContainer").add_child(load_object)
|
||||||
|
|
||||||
|
_:
|
||||||
|
for j in next_load.keys():
|
||||||
|
load_object.set(j, next_load[j])
|
||||||
|
get_node(next_load["parent"]).add_child(load_object)
|
||||||
|
|
||||||
|
for i in load_place_to_room.keys():
|
||||||
|
i.lookup_place_to_room = load_place_to_room[i]
|
||||||
|
|
||||||
|
for i in load_place_to_tile.keys():
|
||||||
|
i.lookup_place_to_tile.merge(load_place_to_tile[i])
|
||||||
|
|
||||||
|
for i in load_room_to_tile.keys():
|
||||||
|
i.room_to_tile = load_room_to_tile[i]
|
||||||
|
|
||||||
|
for i in load_room_to_door.keys():
|
||||||
|
i.room_to_door = load_room_to_door[i]
|
||||||
|
|
||||||
|
for i in load_tile_to_door:
|
||||||
|
for j in i.keys():
|
||||||
|
for k in i[j].keys():
|
||||||
|
var one = i[j]
|
||||||
|
var two = one[k]
|
||||||
|
j.face_dict[k] = two
|
||||||
1
interface/save_load.gd.uid
Normal file
1
interface/save_load.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://bdf7ll8y2htic
|
||||||
5
objects/base_object.gd
Normal file
5
objects/base_object.gd
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
extends Node3D
|
||||||
|
|
||||||
|
class_name GameObject
|
||||||
|
|
||||||
|
enum PLACEMENTS {DOOR}
|
||||||
1
objects/base_object.gd.uid
Normal file
1
objects/base_object.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://ca63k51v37jyg
|
||||||
401
places/base_place/base_place.gd
Normal file
401
places/base_place/base_place.gd
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
extends Node3D
|
||||||
|
class_name Place
|
||||||
|
|
||||||
|
var tile_grid_size: int = 32
|
||||||
|
|
||||||
|
var load_tile = preload("res://tiles/base_tile/base_tile.tscn")
|
||||||
|
var load_room = preload("res://places/base_place/base_room.tscn")
|
||||||
|
var load_door = preload("res://tiles/base_tile/base_door.tscn")
|
||||||
|
var load_actor = preload("res://actors/base_actor/base_actor.tscn")
|
||||||
|
|
||||||
|
@onready var task_list: Node = $TaskList
|
||||||
|
|
||||||
|
#Contains all tiles in a workplace.
|
||||||
|
var lookup_place_to_tile: Dictionary = {}
|
||||||
|
#Contains all rooms in a workplace.
|
||||||
|
var lookup_place_to_room: Array = []
|
||||||
|
|
||||||
|
#Contains the tiles in the current selection drag
|
||||||
|
var selection_drag_dict: Dictionary = {}
|
||||||
|
#Contains all the tiles that have been selected in the current build action
|
||||||
|
var selection_dict: Dictionary = {}
|
||||||
|
|
||||||
|
#Tracks the previous amount of tiles counted in a selection drac
|
||||||
|
var tile_count_x_hist: int = 0
|
||||||
|
var tile_count_z_hist: int = 0
|
||||||
|
|
||||||
|
#Tracks if confirming a build is allowed
|
||||||
|
var build_confirm_allowed: bool = true
|
||||||
|
|
||||||
|
#The object that's currently being built
|
||||||
|
var current_object: Object = null
|
||||||
|
#Where that object can be placed
|
||||||
|
var current_placement: Array = []
|
||||||
|
|
||||||
|
#Tracks parts of the room currently being built
|
||||||
|
var current_room: Object = null
|
||||||
|
|
||||||
|
var map = PlaceAStar3D.new()
|
||||||
|
|
||||||
|
func save():
|
||||||
|
var save_data = {
|
||||||
|
#Basics
|
||||||
|
"id": self.get_instance_id(),
|
||||||
|
"type": "Place",
|
||||||
|
"scene_file_path": scene_file_path,
|
||||||
|
"parent": get_parent().get_path(),
|
||||||
|
#Connections
|
||||||
|
#Data
|
||||||
|
}
|
||||||
|
return save_data
|
||||||
|
|
||||||
|
func init_grid():
|
||||||
|
#TEMP: Sets up a simple 2D grid of blank tiles.
|
||||||
|
|
||||||
|
for x in range(tile_grid_size):
|
||||||
|
for z in range (tile_grid_size):
|
||||||
|
var pos = Vector3i(x, 0, z)
|
||||||
|
var tile: Object = load_tile.instantiate()
|
||||||
|
|
||||||
|
tile.set_position(pos)
|
||||||
|
tile.grid_pos = pos
|
||||||
|
tile.name = str("Tile", tile.get_instance_id())
|
||||||
|
tile.place_id = self.get_instance_id()
|
||||||
|
tile.selection_mode = Tile.SEL_MODE.NONE
|
||||||
|
tile.construction_mode = Tile.CON_MODE.CLOSED
|
||||||
|
|
||||||
|
$TileContainer.add_child(tile)
|
||||||
|
|
||||||
|
func draw_tile_click(click_pos):
|
||||||
|
#starts a selection drag
|
||||||
|
|
||||||
|
var build_start_pos: Vector3i = click_pos.floor()
|
||||||
|
|
||||||
|
if lookup_place_to_tile.has(build_start_pos):
|
||||||
|
if not lookup_place_to_tile[build_start_pos].construction_mode == Tile.CON_MODE.BUILT:
|
||||||
|
select_tile(build_start_pos)
|
||||||
|
|
||||||
|
func init_select_drag(float_build_start_pos, float_build_mouse_pos):
|
||||||
|
#Creats an array of dragged tiles between mouse start and current position
|
||||||
|
|
||||||
|
var select_drag_array: Array = []
|
||||||
|
|
||||||
|
var build_start_pos: Vector3i = float_build_start_pos.floor()
|
||||||
|
var build_mouse_pos: Vector3i = float_build_mouse_pos.floor()
|
||||||
|
|
||||||
|
var tile_count_x: int = build_mouse_pos.x - build_start_pos.x
|
||||||
|
var tile_count_z: int = build_mouse_pos.z - build_start_pos.z
|
||||||
|
|
||||||
|
if not tile_count_x_hist == tile_count_x or not tile_count_z_hist == tile_count_z:
|
||||||
|
|
||||||
|
tile_count_x_hist = tile_count_x
|
||||||
|
tile_count_z_hist = tile_count_z
|
||||||
|
|
||||||
|
for x in range(min(0, tile_count_x), max(0, tile_count_x) + 1):
|
||||||
|
for z in range(min(0, tile_count_z), max(0, tile_count_z) + 1):
|
||||||
|
var select_drag_pos: Vector3i = build_start_pos + Vector3i(x, 0, z)
|
||||||
|
if lookup_place_to_tile.has(select_drag_pos):
|
||||||
|
if not lookup_place_to_tile[select_drag_pos].construction_mode == Tile.CON_MODE.BUILT:
|
||||||
|
select_drag_array.append(select_drag_pos)
|
||||||
|
|
||||||
|
if select_drag_array:
|
||||||
|
draw_select_drag(select_drag_array)
|
||||||
|
|
||||||
|
func draw_select_drag(array):
|
||||||
|
#Clears previous drag, then calls tile selection on all currently dragged tiles
|
||||||
|
|
||||||
|
for i in lookup_place_to_tile:
|
||||||
|
if not selection_dict.has(i):
|
||||||
|
var tile: Object = lookup_place_to_tile[i]
|
||||||
|
tile.selection_mode = Tile.SEL_MODE.NONE
|
||||||
|
|
||||||
|
selection_drag_dict.clear()
|
||||||
|
|
||||||
|
for i in array:
|
||||||
|
select_tile(i)
|
||||||
|
|
||||||
|
func select_tile(pos):
|
||||||
|
#Tells tiles to be selected
|
||||||
|
|
||||||
|
var tile: Object = lookup_place_to_tile[pos]
|
||||||
|
|
||||||
|
selection_drag_dict[pos] = tile
|
||||||
|
|
||||||
|
if build_confirm_allowed:
|
||||||
|
tile.selection_mode = Tile.SEL_MODE.BUILD
|
||||||
|
else:
|
||||||
|
tile.selection_mode = Tile.SEL_MODE.INVALID
|
||||||
|
|
||||||
|
func verify_room():
|
||||||
|
#Verifies that a given selection is fully contiguous
|
||||||
|
|
||||||
|
var verify_array: Array = selection_dict.keys()
|
||||||
|
var verify_queue_array: Array = [verify_array[0]]
|
||||||
|
var verify_checked_array: Array = []
|
||||||
|
|
||||||
|
while verify_array:
|
||||||
|
if not verify_queue_array:
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
var verify_pos: Vector3i = verify_queue_array.pop_back()
|
||||||
|
|
||||||
|
var verify_neighbor_array: Array = lookup_place_to_tile[verify_pos].direction_vector_dict.values()
|
||||||
|
|
||||||
|
for n in verify_neighbor_array:
|
||||||
|
|
||||||
|
if selection_dict.has(verify_pos + n):
|
||||||
|
if not verify_checked_array.has(verify_pos + n):
|
||||||
|
if not verify_queue_array.has(verify_pos + n):
|
||||||
|
verify_queue_array.append(verify_pos + n)
|
||||||
|
|
||||||
|
verify_checked_array.append(verify_pos)
|
||||||
|
verify_array.erase(verify_pos)
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
func end_select_drag():
|
||||||
|
#Adds dragged tiles to the current selection on mouse-up
|
||||||
|
if not selection_drag_dict:
|
||||||
|
return
|
||||||
|
|
||||||
|
tile_count_x_hist = 0
|
||||||
|
tile_count_z_hist = 0
|
||||||
|
|
||||||
|
selection_dict.merge(selection_drag_dict)
|
||||||
|
|
||||||
|
if verify_room():
|
||||||
|
build_confirm_allowed = true
|
||||||
|
for i in selection_dict:
|
||||||
|
lookup_place_to_tile[i].selection_mode = Tile.SEL_MODE.BUILD
|
||||||
|
else:
|
||||||
|
build_confirm_allowed = false
|
||||||
|
for i in selection_dict:
|
||||||
|
lookup_place_to_tile[i].selection_mode = Tile.SEL_MODE.INVALID
|
||||||
|
|
||||||
|
selection_drag_dict.clear()
|
||||||
|
|
||||||
|
func build_selection():
|
||||||
|
#When the build button is clicked, changes the selected tiles to match the button's request
|
||||||
|
|
||||||
|
if not build_confirm_allowed:
|
||||||
|
return
|
||||||
|
|
||||||
|
if selection_dict:
|
||||||
|
|
||||||
|
var room: Object = create_room(selection_dict)
|
||||||
|
|
||||||
|
for i in selection_dict:
|
||||||
|
var tile: Object = lookup_place_to_tile[i]
|
||||||
|
tile.selection_mode = Tile.SEL_MODE.NONE
|
||||||
|
tile.construction_mode = Tile.CON_MODE.BUILT
|
||||||
|
|
||||||
|
selection_dict.clear()
|
||||||
|
|
||||||
|
return room
|
||||||
|
|
||||||
|
func clear_selection():
|
||||||
|
#When the clear button is clicked, it clears the selected tiles without doing anything.
|
||||||
|
|
||||||
|
for i in selection_dict:
|
||||||
|
var tile: Object = selection_dict[i]
|
||||||
|
tile.selection_mode = Tile.SEL_MODE.NONE
|
||||||
|
build_confirm_allowed = true
|
||||||
|
|
||||||
|
selection_dict.clear()
|
||||||
|
|
||||||
|
func create_room(selection):
|
||||||
|
#Creates a room from the selected tiles.
|
||||||
|
|
||||||
|
var room: Object = load_room.instantiate()
|
||||||
|
|
||||||
|
room.position = (selection.keys().min())
|
||||||
|
|
||||||
|
room.room_to_tile = selection.values()
|
||||||
|
room.place_id = self.get_instance_id()
|
||||||
|
|
||||||
|
room.name = str("Room", room.get_instance_id())
|
||||||
|
|
||||||
|
$RoomContainer.add_child(room)
|
||||||
|
|
||||||
|
for i in room.room_to_tile:
|
||||||
|
i.lookup_tile_to_room = room
|
||||||
|
|
||||||
|
lookup_place_to_room.append(room)
|
||||||
|
|
||||||
|
return room
|
||||||
|
|
||||||
|
func init_object(object):
|
||||||
|
#Instantiates the object to be placed
|
||||||
|
|
||||||
|
match object:
|
||||||
|
"door":
|
||||||
|
var door: Object = load_door.instantiate()
|
||||||
|
|
||||||
|
init_object_locations(door)
|
||||||
|
|
||||||
|
door.place_id = self.get_instance_id()
|
||||||
|
door.visible = false
|
||||||
|
|
||||||
|
$ObjectContainer.add_child(door)
|
||||||
|
|
||||||
|
current_object = door
|
||||||
|
|
||||||
|
"actor":
|
||||||
|
var actor: Actor = load_actor.instantiate()
|
||||||
|
|
||||||
|
current_object = actor
|
||||||
|
|
||||||
|
actor.visible = false
|
||||||
|
|
||||||
|
$ActorContainer.add_child(actor)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func init_object_locations(object):
|
||||||
|
#Gets the list of locations that the object can be placed
|
||||||
|
|
||||||
|
match object.placement:
|
||||||
|
GameObject.PLACEMENTS.DOOR:
|
||||||
|
for i in lookup_place_to_tile.keys():
|
||||||
|
for j in lookup_place_to_tile[i].face_dict.keys():
|
||||||
|
if lookup_place_to_tile[i].face_dict[j] is not Door:
|
||||||
|
current_placement.append((Vector3(i) + Vector3(0.5, 0, 0.5)) + Vector3(j) / 2)
|
||||||
|
|
||||||
|
func hover_object(mouse_pos):
|
||||||
|
#Hovers the object at the closest acceptable placement location to the mouse
|
||||||
|
|
||||||
|
current_object.visible = true
|
||||||
|
|
||||||
|
if current_placement:
|
||||||
|
var closest: Vector3
|
||||||
|
var closest_distance: float = INF
|
||||||
|
|
||||||
|
for i in current_placement:
|
||||||
|
var distance: float = mouse_pos.distance_to(i)
|
||||||
|
if closest_distance > distance:
|
||||||
|
closest_distance = distance
|
||||||
|
closest = i
|
||||||
|
|
||||||
|
current_object.position = closest
|
||||||
|
current_object.rotation_degrees = Vector3(0, 180 * fmod(closest.x, 1), 0)
|
||||||
|
|
||||||
|
else:
|
||||||
|
current_object.position = mouse_pos
|
||||||
|
|
||||||
|
func confirm_object():
|
||||||
|
#Places the object at the hovered location
|
||||||
|
#TODO: this is very sloppy
|
||||||
|
|
||||||
|
var check = current_object.get_class()
|
||||||
|
if current_object is Door:
|
||||||
|
|
||||||
|
var tile_1: Object
|
||||||
|
var tile_2: Object
|
||||||
|
|
||||||
|
if fmod(current_object.position.x, 1) == 0:
|
||||||
|
tile_1 = lookup_place_to_tile[Vector3i(current_object.position.x, 0, current_object.position.z)]
|
||||||
|
tile_2 = lookup_place_to_tile[Vector3i(current_object.position.x - 1, 0, current_object.position.z)]
|
||||||
|
else:
|
||||||
|
tile_1 = lookup_place_to_tile[Vector3i(current_object.position.x, 0, current_object.position.z)]
|
||||||
|
tile_2 = lookup_place_to_tile[Vector3i(current_object.position.x, 0, current_object.position.z - 1)]
|
||||||
|
|
||||||
|
var tile_1_new_dict = tile_1.face_dict.duplicate()
|
||||||
|
var tile_2_new_dict = tile_2.face_dict.duplicate()
|
||||||
|
|
||||||
|
var tile_1_door_face: Vector3i = tile_2.position - tile_1.position
|
||||||
|
|
||||||
|
tile_1_new_dict[tile_1_door_face] = current_object
|
||||||
|
|
||||||
|
tile_1.update_faces(tile_1_new_dict)
|
||||||
|
|
||||||
|
tile_1.lookup_tile_to_room.room_to_door.append(current_object)
|
||||||
|
|
||||||
|
tile_2.lookup_connected_rooms.append(tile_1.lookup_tile_to_room)
|
||||||
|
|
||||||
|
tile_2.lookup_tile_to_room.room_to_tile.append(tile_1)
|
||||||
|
|
||||||
|
var tile_2_door_face: Vector3i = tile_1.position - tile_2.position
|
||||||
|
|
||||||
|
tile_2_new_dict[tile_2_door_face] = current_object
|
||||||
|
|
||||||
|
tile_2.update_faces(tile_2_new_dict)
|
||||||
|
|
||||||
|
tile_2.lookup_tile_to_room.room_to_door.append(current_object)
|
||||||
|
|
||||||
|
tile_1.lookup_connected_rooms.append(tile_2.lookup_tile_to_room)
|
||||||
|
|
||||||
|
tile_1.lookup_tile_to_room.room_to_tile.append(tile_2)
|
||||||
|
|
||||||
|
current_object.lookup_door_to_room.append(tile_1.lookup_tile_to_room)
|
||||||
|
current_object.lookup_door_to_room.append(tile_2.lookup_tile_to_room)
|
||||||
|
current_object.lookup_door_to_tile[tile_1] = tile_1_door_face
|
||||||
|
current_object.lookup_door_to_tile[tile_2] = tile_2_door_face
|
||||||
|
|
||||||
|
var id = map.get_available_point_id()
|
||||||
|
map.add_point(id, current_object.position)
|
||||||
|
current_object.map_point = id
|
||||||
|
prints(map.get_point_ids())
|
||||||
|
for i in tile_1.lookup_tile_to_room.room_to_door:
|
||||||
|
if i.map_point != id:
|
||||||
|
map.connect_points(i.map_point, id)
|
||||||
|
for i in tile_2.lookup_tile_to_room.room_to_door:
|
||||||
|
if i.map_point != id:
|
||||||
|
map.connect_points(i.map_point, id)
|
||||||
|
prints(map.get_point_connections(id))
|
||||||
|
|
||||||
|
elif current_object is Actor:
|
||||||
|
current_object.actor_state = Actor.ACTOR_STATE.PAUSED
|
||||||
|
|
||||||
|
current_room = null
|
||||||
|
current_placement = []
|
||||||
|
current_object = null
|
||||||
|
|
||||||
|
func give_neighbors(tile, grid_pos, directions):
|
||||||
|
#Responds to a tile's request for it's neighbors
|
||||||
|
|
||||||
|
var neighbor_dict = {}
|
||||||
|
for i in directions.keys():
|
||||||
|
if lookup_place_to_tile.has(directions[i] + grid_pos):
|
||||||
|
neighbor_dict[directions[i]] = lookup_place_to_tile[directions[i] + grid_pos]
|
||||||
|
tile.neighbor_dict = neighbor_dict
|
||||||
|
|
||||||
|
func _on_tile_container_child_entered_tree(node: Node) -> void:
|
||||||
|
#Connects signals from created tiles
|
||||||
|
|
||||||
|
lookup_place_to_tile[node.grid_pos] = node
|
||||||
|
node.connect("neighbor_request", give_neighbors)
|
||||||
|
|
||||||
|
func _on_actor_container_child_entered_tree(node: Node) -> void:
|
||||||
|
#Connects signals from created actors
|
||||||
|
|
||||||
|
node.connect("current_tile_request", give_tile)
|
||||||
|
if node.has_node("TaskCreator"):
|
||||||
|
var tasker = node.get_node("TaskCreator")
|
||||||
|
tasker.connect("record_task", record_task)
|
||||||
|
tasker.connect("finish_task", finish_task)
|
||||||
|
|
||||||
|
func give_tile(actor):
|
||||||
|
#Responds to an actor's request for its current tile
|
||||||
|
|
||||||
|
#var closest: Vector3
|
||||||
|
#var closest_distance: float = INF
|
||||||
|
#
|
||||||
|
#for i in lookup_place_to_tile:
|
||||||
|
#var distance: float = actor.position.distance_to(i)
|
||||||
|
#if closest_distance > distance:
|
||||||
|
#closest_distance = distance
|
||||||
|
#closest = i
|
||||||
|
|
||||||
|
actor.current_tile = lookup_place_to_tile[Vector3i(floor(actor.position.x), 0, floor(actor.position.z))]
|
||||||
|
|
||||||
|
func record_task(task):
|
||||||
|
#Records created tasks in the task list.
|
||||||
|
|
||||||
|
task_list.record_task(task)
|
||||||
|
|
||||||
|
func finish_task(source, task):
|
||||||
|
#Marks tasks as completed in the task list.
|
||||||
|
|
||||||
|
task_list.finish_task(source, task)
|
||||||
1
places/base_place/base_place.gd.uid
Normal file
1
places/base_place/base_place.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://n4vvqmlmq5fp
|
||||||
24
places/base_place/base_place.tscn
Normal file
24
places/base_place/base_place.tscn
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://bujuwgn3y42ek"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://n4vvqmlmq5fp" path="res://places/base_place/base_place.gd" id="1_uq3a8"]
|
||||||
|
[ext_resource type="Script" uid="uid://3tnkke3ugl3t" path="res://places/base_place/task_list.gd" id="2_tkpnh"]
|
||||||
|
|
||||||
|
[node name="BasePlace" type="Node3D" groups=["SaveObjects"]]
|
||||||
|
script = ExtResource("1_uq3a8")
|
||||||
|
|
||||||
|
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 0.345446, 0.938439, 0, -0.938439, 0.345446, 41.393, 18.7409, 34.4224)
|
||||||
|
|
||||||
|
[node name="RoomContainer" type="Node" parent="."]
|
||||||
|
|
||||||
|
[node name="TileContainer" type="Node" parent="."]
|
||||||
|
|
||||||
|
[node name="ObjectContainer" type="Node" parent="."]
|
||||||
|
|
||||||
|
[node name="ActorContainer" type="Node" parent="."]
|
||||||
|
|
||||||
|
[node name="TaskList" type="Node" parent="."]
|
||||||
|
script = ExtResource("2_tkpnh")
|
||||||
|
|
||||||
|
[connection signal="child_entered_tree" from="TileContainer" to="." method="_on_tile_container_child_entered_tree"]
|
||||||
|
[connection signal="child_entered_tree" from="ActorContainer" to="." method="_on_actor_container_child_entered_tree"]
|
||||||
60
places/base_place/base_room.gd
Normal file
60
places/base_place/base_room.gd
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
extends Node
|
||||||
|
class_name Room
|
||||||
|
|
||||||
|
var room_to_tile = []: set = setup_path_grid
|
||||||
|
|
||||||
|
var path_grid = AStar3D.new()
|
||||||
|
|
||||||
|
var room_to_door = []
|
||||||
|
|
||||||
|
var place_id: int = 0
|
||||||
|
|
||||||
|
func save():
|
||||||
|
var save_data = {
|
||||||
|
#Basics
|
||||||
|
"id": self.get_instance_id(),
|
||||||
|
"type": "Room",
|
||||||
|
"scene_file_path": scene_file_path,
|
||||||
|
"parent": get_parent().get_instance_id(),
|
||||||
|
#Connections
|
||||||
|
"place_id": place_id,
|
||||||
|
#Data
|
||||||
|
}
|
||||||
|
return save_data
|
||||||
|
|
||||||
|
func init_room(new_dict):
|
||||||
|
#When a room is created, run through these steps
|
||||||
|
|
||||||
|
setup_path_grid(new_dict)
|
||||||
|
for i in new_dict.values():
|
||||||
|
i.room = self
|
||||||
|
|
||||||
|
func setup_path_grid(new_array):
|
||||||
|
#Sets up the A* grid of tiles that are in this room.
|
||||||
|
#TODO: Better grid changing
|
||||||
|
|
||||||
|
path_grid.clear()
|
||||||
|
room_to_tile = new_array
|
||||||
|
var room_to_tile_positions: Array
|
||||||
|
|
||||||
|
for i in room_to_tile:
|
||||||
|
var id = path_grid.get_available_point_id()
|
||||||
|
var point_pos = Vector3(i.grid_pos) + Vector3(0.5, 0, 0.5)
|
||||||
|
path_grid.add_point(id, point_pos)
|
||||||
|
room_to_tile_positions.append(point_pos)
|
||||||
|
|
||||||
|
for n in Tile.direction_vector_dict.values():
|
||||||
|
var neighbor_pos = point_pos + Vector3(n)
|
||||||
|
if room_to_tile_positions.has(neighbor_pos):
|
||||||
|
var closest = path_grid.get_closest_point(neighbor_pos)
|
||||||
|
if (neighbor_pos) == Vector3(path_grid.get_point_position(closest)):
|
||||||
|
path_grid.connect_points(id, closest)
|
||||||
|
|
||||||
|
func get_room_path(start, end):
|
||||||
|
for i in room_to_tile:
|
||||||
|
if i.grid_pos == end:
|
||||||
|
var start_point = path_grid.get_closest_point(Vector3(start) + Vector3(0.5, 0, 0.5))
|
||||||
|
var end_point = path_grid.get_closest_point(Vector3(end) + Vector3(0.5, 0, 0.5))
|
||||||
|
var path = path_grid.get_point_path(start_point, end_point)
|
||||||
|
return path
|
||||||
|
return false
|
||||||
1
places/base_place/base_room.gd.uid
Normal file
1
places/base_place/base_room.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://c2neixe7kpvma
|
||||||
6
places/base_place/base_room.tscn
Normal file
6
places/base_place/base_room.tscn
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://f0b4ptwari8y"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://c2neixe7kpvma" path="res://places/base_place/base_room.gd" id="1_3l4mp"]
|
||||||
|
|
||||||
|
[node name="BaseRoom" type="Node3D" groups=["SaveObjects"]]
|
||||||
|
script = ExtResource("1_3l4mp")
|
||||||
2
places/base_place/place_map.gd
Normal file
2
places/base_place/place_map.gd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
class_name PlaceAStar3D
|
||||||
|
extends AStar3D
|
||||||
1
places/base_place/place_map.gd.uid
Normal file
1
places/base_place/place_map.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dk4yug4ofcoc3
|
||||||
33
places/base_place/task_list.gd
Normal file
33
places/base_place/task_list.gd
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
extends Node
|
||||||
|
|
||||||
|
class_name TaskList
|
||||||
|
|
||||||
|
var unassigned_tasks = []
|
||||||
|
|
||||||
|
var assigned_task_dict: Dictionary = {}
|
||||||
|
|
||||||
|
func record_task(task):
|
||||||
|
#Records a provided task in the list
|
||||||
|
|
||||||
|
if task.assignment:
|
||||||
|
var assignment: Actor = task["assignment"]
|
||||||
|
#var assignment_task_list: Array = []
|
||||||
|
assign_task(task, assignment)
|
||||||
|
if not assigned_task_dict.has(assignment):
|
||||||
|
assigned_task_dict[assignment] = []
|
||||||
|
assigned_task_dict[assignment].append(task)
|
||||||
|
|
||||||
|
func finish_task(source, task):
|
||||||
|
#Removes a completed task from the list
|
||||||
|
|
||||||
|
var source_task_array = assigned_task_dict[source]
|
||||||
|
var finished_task_index = source_task_array.find(task)
|
||||||
|
source_task_array.pop_at(finished_task_index)
|
||||||
|
|
||||||
|
if not assigned_task_dict[source]:
|
||||||
|
assigned_task_dict.erase(source)
|
||||||
|
|
||||||
|
func assign_task(task, assignment: Actor):
|
||||||
|
#Alerts an actor that it has a new task.
|
||||||
|
|
||||||
|
assignment.lookup_actor_to_task.append(task)
|
||||||
1
places/base_place/task_list.gd.uid
Normal file
1
places/base_place/task_list.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://3tnkke3ugl3t
|
||||||
100
places/place_manager.gd
Normal file
100
places/place_manager.gd
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
extends Node3D
|
||||||
|
|
||||||
|
var load_place = preload("res://places/base_place/base_place.tscn")
|
||||||
|
var place = null
|
||||||
|
|
||||||
|
enum ROOM_BUILD_STATE {NONE, SELECT, DRAG, CONFIRM, PLACE}
|
||||||
|
|
||||||
|
#Tracks the current build state.
|
||||||
|
var room_build_state = 0
|
||||||
|
|
||||||
|
var current_room = null
|
||||||
|
|
||||||
|
var current_object = null
|
||||||
|
|
||||||
|
#Tracks the position that was first clicked to start a build drag
|
||||||
|
var build_drag_start_pos = null
|
||||||
|
|
||||||
|
signal room_built
|
||||||
|
|
||||||
|
func _on_child_entered_tree(node: Node) -> void:
|
||||||
|
if node is Place:
|
||||||
|
place = node
|
||||||
|
|
||||||
|
func _on_build_toggle(toggled_on):
|
||||||
|
#Enables construction of a room
|
||||||
|
|
||||||
|
if toggled_on:
|
||||||
|
room_build_state = ROOM_BUILD_STATE.SELECT
|
||||||
|
else:
|
||||||
|
place.clear_selection()
|
||||||
|
room_build_state = ROOM_BUILD_STATE.NONE
|
||||||
|
|
||||||
|
func _on_confirm_button_pressed() -> void:
|
||||||
|
#Confirms the room that is currently being constructed
|
||||||
|
|
||||||
|
if place.selection_dict:
|
||||||
|
if room_build_state == ROOM_BUILD_STATE.SELECT:
|
||||||
|
if place.build_confirm_allowed:
|
||||||
|
current_room = place.build_selection()
|
||||||
|
room_build_state = ROOM_BUILD_STATE.NONE
|
||||||
|
|
||||||
|
else:
|
||||||
|
prints("Building not allowed.")
|
||||||
|
else:
|
||||||
|
prints("Nothing selected.")
|
||||||
|
|
||||||
|
func _on_init_grid_button_pressed() -> void:
|
||||||
|
#TEMP: Responds to the grid initialization button
|
||||||
|
|
||||||
|
if place:
|
||||||
|
place.queue_free()
|
||||||
|
var new_place = load_place.instantiate()
|
||||||
|
add_child(new_place)
|
||||||
|
place.init_grid()
|
||||||
|
|
||||||
|
func _on_door_button_pressed() -> void:
|
||||||
|
#Sets up door creation
|
||||||
|
|
||||||
|
match room_build_state:
|
||||||
|
ROOM_BUILD_STATE.NONE:
|
||||||
|
current_object = "door"
|
||||||
|
place.current_room = current_room
|
||||||
|
place.init_object(current_object)
|
||||||
|
room_build_state = ROOM_BUILD_STATE.PLACE
|
||||||
|
_:
|
||||||
|
return
|
||||||
|
|
||||||
|
func _on_worker_button_pressed() -> void:
|
||||||
|
#Sets up worker creation
|
||||||
|
|
||||||
|
match room_build_state:
|
||||||
|
ROOM_BUILD_STATE.NONE:
|
||||||
|
current_object = "actor"
|
||||||
|
place.init_object(current_object)
|
||||||
|
room_build_state = ROOM_BUILD_STATE.PLACE
|
||||||
|
|
||||||
|
func _on_area_3d_input_event(_camera, _event, event_position, _normal, _shade_id):
|
||||||
|
#Checks input events from the mouse plane
|
||||||
|
|
||||||
|
match room_build_state:
|
||||||
|
ROOM_BUILD_STATE.SELECT:
|
||||||
|
if Input.is_action_pressed("select"):
|
||||||
|
room_build_state = ROOM_BUILD_STATE.DRAG
|
||||||
|
build_drag_start_pos = event_position
|
||||||
|
place.draw_tile_click(build_drag_start_pos)
|
||||||
|
|
||||||
|
ROOM_BUILD_STATE.DRAG:
|
||||||
|
if Input.is_action_pressed("select"):
|
||||||
|
place.init_select_drag(build_drag_start_pos, event_position)
|
||||||
|
else:
|
||||||
|
room_build_state = ROOM_BUILD_STATE.SELECT
|
||||||
|
place.end_select_drag()
|
||||||
|
|
||||||
|
ROOM_BUILD_STATE.PLACE:
|
||||||
|
if Input.is_action_pressed("select"):
|
||||||
|
room_build_state = ROOM_BUILD_STATE.NONE
|
||||||
|
place.confirm_object()
|
||||||
|
emit_signal("room_built")
|
||||||
|
else:
|
||||||
|
place.hover_object(event_position)
|
||||||
1
places/place_manager.gd.uid
Normal file
1
places/place_manager.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://d24sr2dat4lj0
|
||||||
18
places/place_manager.tscn
Normal file
18
places/place_manager.tscn
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://0d1d5e1u2fys"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://d24sr2dat4lj0" path="res://places/place_manager.gd" id="1_rcbs8"]
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape3D" id="BoxShape3D_rcbs8"]
|
||||||
|
size = Vector3(256, 0, 256)
|
||||||
|
|
||||||
|
[node name="PlaceManager" type="Node3D"]
|
||||||
|
script = ExtResource("1_rcbs8")
|
||||||
|
|
||||||
|
[node name="FloorPlane" type="Area3D" parent="."]
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="FloorPlane"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 128, 0, 128)
|
||||||
|
shape = SubResource("BoxShape3D_rcbs8")
|
||||||
|
|
||||||
|
[connection signal="child_entered_tree" from="." to="." method="_on_child_entered_tree"]
|
||||||
|
[connection signal="input_event" from="FloorPlane" to="." method="_on_area_3d_input_event"]
|
||||||
18
places/place_manager.tscn182623054522.tmp
Normal file
18
places/place_manager.tscn182623054522.tmp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://0d1d5e1u2fys"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://d24sr2dat4lj0" path="res://places/place_manager.gd" id="1_rcbs8"]
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape3D" id="BoxShape3D_rcbs8"]
|
||||||
|
size = Vector3(256, 0, 256)
|
||||||
|
|
||||||
|
[node name="PlaceManager" type="Node3D"]
|
||||||
|
script = ExtResource("1_rcbs8")
|
||||||
|
|
||||||
|
[node name="FloorPlane" type="Area3D" parent="."]
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="FloorPlane"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 128, 0, 128)
|
||||||
|
shape = SubResource("BoxShape3D_rcbs8")
|
||||||
|
|
||||||
|
[connection signal="child_entered_tree" from="." to="." method="_on_child_entered_tree"]
|
||||||
|
[connection signal="input_event" from="FloorPlane" to="." method="_on_area_3d_input_event"]
|
||||||
@ -11,5 +11,59 @@ config_version=5
|
|||||||
[application]
|
[application]
|
||||||
|
|
||||||
config/name="Project Villain"
|
config/name="Project Villain"
|
||||||
|
run/main_scene="uid://x8xo5b1b3q41"
|
||||||
config/features=PackedStringArray("4.4", "Forward Plus")
|
config/features=PackedStringArray("4.4", "Forward Plus")
|
||||||
config/icon="res://icon.svg"
|
config/icon="res://icon.svg"
|
||||||
|
|
||||||
|
[global_group]
|
||||||
|
|
||||||
|
SaveObjects="Anything that will need to be saved when the game is."
|
||||||
|
|
||||||
|
[input]
|
||||||
|
|
||||||
|
select={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||||
|
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":67,"key_label":0,"unicode":99,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
main_cam_up={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":79,"key_label":0,"unicode":111,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
main_cam_right={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
main_cam_down={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
main_cam_left={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":73,"key_label":0,"unicode":105,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
main_cam_in={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":4,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
main_cam_out={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":5,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
[physics]
|
||||||
|
|
||||||
|
3d/default_gravity=0.0
|
||||||
|
3d/default_gravity_vector=Vector3(0, 0, 0)
|
||||||
|
|
||||||
|
[rendering]
|
||||||
|
|
||||||
|
renderer/rendering_method="gl_compatibility"
|
||||||
|
renderer/rendering_method.mobile="gl_compatibility"
|
||||||
|
|||||||
37
task_creator.gd
Normal file
37
task_creator.gd
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
extends Node
|
||||||
|
|
||||||
|
class_name TaskCreator
|
||||||
|
|
||||||
|
signal record_task
|
||||||
|
signal finish_task
|
||||||
|
|
||||||
|
var task_list = null
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
pass
|
||||||
|
|
||||||
|
func create_task(
|
||||||
|
source: Object,
|
||||||
|
assignment = null,
|
||||||
|
location = null,
|
||||||
|
interaction = null,
|
||||||
|
urgency = null,
|
||||||
|
):
|
||||||
|
#Creates a task with the information provided and sends it to the list.
|
||||||
|
|
||||||
|
var task: Dictionary = {
|
||||||
|
"source": source,
|
||||||
|
"assignment": assignment,
|
||||||
|
"location": location,
|
||||||
|
"interaction": interaction,
|
||||||
|
"urgency": urgency,
|
||||||
|
}
|
||||||
|
emit_signal("record_task", task)
|
||||||
|
|
||||||
|
func complete_task(
|
||||||
|
source,
|
||||||
|
task
|
||||||
|
):
|
||||||
|
#Requests removal of a completed task
|
||||||
|
|
||||||
|
emit_signal("finish_task", source, task)
|
||||||
1
task_creator.gd.uid
Normal file
1
task_creator.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://crchqrpts6q1i
|
||||||
6
task_creator.tscn
Normal file
6
task_creator.tscn
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://btpcj4sv1qgck"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://crchqrpts6q1i" path="res://task_creator.gd" id="1_atcl4"]
|
||||||
|
|
||||||
|
[node name="TaskCreator" type="Node"]
|
||||||
|
script = ExtResource("1_atcl4")
|
||||||
47
tiles/base_tile/base_door.gd
Normal file
47
tiles/base_tile/base_door.gd
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
extends GameObject
|
||||||
|
|
||||||
|
class_name Door
|
||||||
|
|
||||||
|
var place_id: int
|
||||||
|
|
||||||
|
var lookup_door_to_room = []
|
||||||
|
|
||||||
|
var lookup_door_to_tile = {}
|
||||||
|
|
||||||
|
var direction = null
|
||||||
|
|
||||||
|
var placement = GameObject.PLACEMENTS.DOOR
|
||||||
|
|
||||||
|
var map_point: int
|
||||||
|
|
||||||
|
func save():
|
||||||
|
|
||||||
|
var room_ids: Array
|
||||||
|
var door_to_tile: Dictionary
|
||||||
|
var door_to_tile_direction: Dictionary = {}
|
||||||
|
|
||||||
|
for i in lookup_door_to_room:
|
||||||
|
room_ids.append(i.get_instance_id())
|
||||||
|
|
||||||
|
for i in lookup_door_to_tile.keys():
|
||||||
|
var id = i.get_instance_id()
|
||||||
|
door_to_tile[lookup_door_to_tile[i]] = id
|
||||||
|
door_to_tile_direction[id] = i.face_dict.find_key(self)
|
||||||
|
|
||||||
|
var save_data = {
|
||||||
|
#Basics
|
||||||
|
"id": self.get_instance_id(),
|
||||||
|
"type": "Door",
|
||||||
|
"scene_file_path": scene_file_path,
|
||||||
|
"parent": get_parent().get_path(),
|
||||||
|
#Connections
|
||||||
|
"place_id": place_id,
|
||||||
|
"room_ids": room_ids,
|
||||||
|
"door_to_tile": door_to_tile,
|
||||||
|
"door_to_tile_direction": door_to_tile_direction,
|
||||||
|
#Data
|
||||||
|
"position": position,
|
||||||
|
"rotation_degrees": rotation_degrees
|
||||||
|
}
|
||||||
|
|
||||||
|
return save_data
|
||||||
1
tiles/base_tile/base_door.gd.uid
Normal file
1
tiles/base_tile/base_door.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dptw3xl225fxk
|
||||||
16
tiles/base_tile/base_door.tscn
Normal file
16
tiles/base_tile/base_door.tscn
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[gd_scene load_steps=4 format=3 uid="uid://b6qehpvvnv5r3"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://dptw3xl225fxk" path="res://tiles/base_tile/base_door.gd" id="1_8olbq"]
|
||||||
|
|
||||||
|
[sub_resource type="BoxMesh" id="BoxMesh_8olbq"]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_8olbq"]
|
||||||
|
albedo_color = Color(0.625824, 0.328179, 0.426969, 1)
|
||||||
|
|
||||||
|
[node name="BaseDoor" type="Node3D" groups=["SaveObjects"]]
|
||||||
|
script = ExtResource("1_8olbq")
|
||||||
|
|
||||||
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||||
|
transform = Transform3D(-4.37114e-08, 0, 0.4, 0, 0.5, 0, -1, 0, -1.74846e-08, 0, 0.25, 0)
|
||||||
|
mesh = SubResource("BoxMesh_8olbq")
|
||||||
|
surface_material_override/0 = SubResource("StandardMaterial3D_8olbq")
|
||||||
4
tiles/base_tile/base_floor.tres
Normal file
4
tiles/base_tile/base_floor.tres
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[gd_resource type="PlaneMesh" format=3 uid="uid://bis4hdushjnjm"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
size = Vector2(1, 1)
|
||||||
161
tiles/base_tile/base_tile.gd
Normal file
161
tiles/base_tile/base_tile.gd
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
extends Node3D
|
||||||
|
class_name Tile
|
||||||
|
|
||||||
|
var place_id: int = 0
|
||||||
|
|
||||||
|
#This tile's current construction and selection modes.
|
||||||
|
@export var construction_mode = 0: set = update_construction
|
||||||
|
@export var selection_mode = 0: set = update_selection
|
||||||
|
|
||||||
|
const orange = preload("res://tiles/base_tile/orange.tres")
|
||||||
|
const gray = preload("res://tiles/base_tile/gray.tres")
|
||||||
|
const red = preload("res://tiles/base_tile/red.tres")
|
||||||
|
const blue = preload("res://tiles/base_tile/blue.tres")
|
||||||
|
const lightblue = preload("res://tiles/base_tile/lightblue.tres")
|
||||||
|
|
||||||
|
#Lists of possible states for various modes.
|
||||||
|
enum SEL_MODE {NONE, ROOM, BUILD, INVALID}
|
||||||
|
|
||||||
|
enum CON_MODE {NONE, NON_INTERACTABLE, CLOSED, OPEN, BUILT, REINFORCED, CONSTRUCTION}
|
||||||
|
|
||||||
|
#List of vectors for each direction a wall can be in
|
||||||
|
const direction_vector_dict = {
|
||||||
|
"North": Vector3i(0, 0, -1),
|
||||||
|
"East": Vector3i(1, 0, 0),
|
||||||
|
"South": Vector3i(0, 0, 1),
|
||||||
|
"West": Vector3i(-1, 0, 0),
|
||||||
|
}
|
||||||
|
#TODO: replace this with better math on the above dict
|
||||||
|
const wall_position_dict = {
|
||||||
|
"North": Vector3i(0, 0, 0),
|
||||||
|
"East": Vector3i(1, 0, 0),
|
||||||
|
"South": Vector3i(1, 0, 1),
|
||||||
|
"West": Vector3i(0, 0, 1),
|
||||||
|
}
|
||||||
|
|
||||||
|
const load_wall = preload("res://tiles/base_tile/base_wall.tscn")
|
||||||
|
|
||||||
|
var grid_pos: Vector3i
|
||||||
|
|
||||||
|
var face_dict = {}: set = update_faces
|
||||||
|
|
||||||
|
var lookup_tile_to_room: Object
|
||||||
|
var lookup_connected_rooms: Array = []
|
||||||
|
|
||||||
|
var neighbor_dict = {}
|
||||||
|
|
||||||
|
signal neighbor_request
|
||||||
|
|
||||||
|
func save():
|
||||||
|
|
||||||
|
#var room_ids: Array
|
||||||
|
#for i in lookup_tile_to_room:
|
||||||
|
#room_ids.append(i.get_instance_id())
|
||||||
|
|
||||||
|
var save_data = {
|
||||||
|
#Basics
|
||||||
|
"id": self.get_instance_id(),
|
||||||
|
"type": "Tile",
|
||||||
|
"scene_file_path": scene_file_path,
|
||||||
|
"parent": get_parent().get_path(),
|
||||||
|
#Connections
|
||||||
|
"place_id": place_id,
|
||||||
|
"main_room": lookup_tile_to_room,
|
||||||
|
"connected_rooms": lookup_connected_rooms,
|
||||||
|
#Data
|
||||||
|
"position": position,
|
||||||
|
"grid_pos": grid_pos,
|
||||||
|
"face_dict": face_dict.keys(),
|
||||||
|
"construction_mode": construction_mode,
|
||||||
|
}
|
||||||
|
return save_data
|
||||||
|
|
||||||
|
func update_faces(new_dict):
|
||||||
|
#Changes the walls when relevant
|
||||||
|
|
||||||
|
for i in new_dict.keys():
|
||||||
|
if face_dict.has(i):
|
||||||
|
if face_dict[i] == new_dict[i]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
face_dict[i].queue_free()
|
||||||
|
face_dict[i] = null
|
||||||
|
|
||||||
|
if new_dict[i] is Door:
|
||||||
|
face_dict[i] = new_dict[i]
|
||||||
|
continue
|
||||||
|
var direction: String = direction_vector_dict.find_key(i)
|
||||||
|
var wall = load_wall.instantiate()
|
||||||
|
wall.position = wall_position_dict[direction]
|
||||||
|
wall.name = str(direction, "Wall")
|
||||||
|
match direction:
|
||||||
|
"East":
|
||||||
|
wall.rotation_degrees = Vector3(0, -90, 0)
|
||||||
|
"South":
|
||||||
|
wall.rotation_degrees = Vector3(0, -180, 0)
|
||||||
|
"West":
|
||||||
|
wall.rotation_degrees = Vector3(0, -270, 0)
|
||||||
|
$Walls.add_child(wall)
|
||||||
|
face_dict[i] = wall
|
||||||
|
|
||||||
|
func update_neighbors():
|
||||||
|
#Requests the current neighboring tiles
|
||||||
|
|
||||||
|
neighbor_dict = {}
|
||||||
|
emit_signal("neighbor_request", self, grid_pos, direction_vector_dict)
|
||||||
|
pass
|
||||||
|
|
||||||
|
func update_construction(mode):
|
||||||
|
#Changes the tile based on its construction mode
|
||||||
|
|
||||||
|
construction_mode = mode
|
||||||
|
|
||||||
|
match mode:
|
||||||
|
CON_MODE.NON_INTERACTABLE:
|
||||||
|
face_dict = {}
|
||||||
|
|
||||||
|
CON_MODE.CLOSED:
|
||||||
|
face_dict = {}
|
||||||
|
$Floor/FloorMesh.set_material_override(gray)
|
||||||
|
|
||||||
|
CON_MODE.OPEN:
|
||||||
|
face_dict = {}
|
||||||
|
|
||||||
|
CON_MODE.BUILT:
|
||||||
|
$Floor/FloorMesh.set_material_override(null)
|
||||||
|
|
||||||
|
if not face_dict:
|
||||||
|
update_neighbors()
|
||||||
|
|
||||||
|
var temp_face_dict = {}
|
||||||
|
|
||||||
|
for i in neighbor_dict.keys():
|
||||||
|
if not neighbor_dict[i].lookup_tile_to_room == lookup_tile_to_room:
|
||||||
|
temp_face_dict[i] = null
|
||||||
|
|
||||||
|
face_dict = temp_face_dict.duplicate()
|
||||||
|
|
||||||
|
CON_MODE.REINFORCED:
|
||||||
|
face_dict = {}
|
||||||
|
|
||||||
|
CON_MODE.CONSTRUCTION:
|
||||||
|
face_dict = {}
|
||||||
|
|
||||||
|
func update_selection(mode):
|
||||||
|
#Changes the tile based on whether it has been selected
|
||||||
|
|
||||||
|
selection_mode = mode
|
||||||
|
|
||||||
|
match mode:
|
||||||
|
SEL_MODE.NONE:
|
||||||
|
$Floor/FloorMesh.set_material_overlay(null)
|
||||||
|
|
||||||
|
SEL_MODE.ROOM:
|
||||||
|
for i in $Walls.get_children():
|
||||||
|
i.Mesh.set_material_override(orange)
|
||||||
|
|
||||||
|
SEL_MODE.BUILD:
|
||||||
|
$Floor/FloorMesh.set_material_overlay(lightblue)
|
||||||
|
|
||||||
|
SEL_MODE.INVALID:
|
||||||
|
$Floor/FloorMesh.set_material_overlay(orange)
|
||||||
1
tiles/base_tile/base_tile.gd.uid
Normal file
1
tiles/base_tile/base_tile.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://jqjcr7dxjnbt
|
||||||
21
tiles/base_tile/base_tile.tscn
Normal file
21
tiles/base_tile/base_tile.tscn
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[gd_scene load_steps=3 format=3 uid="uid://gs6yynwvvot2"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://jqjcr7dxjnbt" path="res://tiles/base_tile/base_tile.gd" id="1_yoisu"]
|
||||||
|
|
||||||
|
[sub_resource type="PlaneMesh" id="PlaneMesh_6tbiv"]
|
||||||
|
size = Vector2(1, 1)
|
||||||
|
|
||||||
|
[node name="BaseTile" type="Node3D" groups=["SaveObjects"]]
|
||||||
|
script = ExtResource("1_yoisu")
|
||||||
|
|
||||||
|
[node name="Floor" type="Node3D" parent="."]
|
||||||
|
|
||||||
|
[node name="FloorMesh" type="MeshInstance3D" parent="Floor"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 0, 0.5)
|
||||||
|
mesh = SubResource("PlaneMesh_6tbiv")
|
||||||
|
skeleton = NodePath("../..")
|
||||||
|
|
||||||
|
[node name="Walls" type="Node3D" parent="."]
|
||||||
|
|
||||||
|
[node name="Label3D" type="Label3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 0.0668138, 0.997765, 0, -0.997765, 0.0668138, 0.341829, 0.0223614, 0.130147)
|
||||||
3
tiles/base_tile/base_wall.gd
Normal file
3
tiles/base_tile/base_wall.gd
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
extends Node3D
|
||||||
|
|
||||||
|
class_name Wall
|
||||||
1
tiles/base_tile/base_wall.gd.uid
Normal file
1
tiles/base_tile/base_wall.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://cx6bmej4pcjlt
|
||||||
16
tiles/base_tile/base_wall.tscn
Normal file
16
tiles/base_tile/base_wall.tscn
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[gd_scene load_steps=4 format=3 uid="uid://diovc4myisuow"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://cx6bmej4pcjlt" path="res://tiles/base_tile/base_wall.gd" id="1_lirtl"]
|
||||||
|
|
||||||
|
[sub_resource type="BoxMesh" id="BoxMesh_xdfmn"]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_lirtl"]
|
||||||
|
albedo_color = Color(0.0574887, 2.24891e-05, 0.240434, 1)
|
||||||
|
|
||||||
|
[node name="BaseWall" type="Node3D"]
|
||||||
|
script = ExtResource("1_lirtl")
|
||||||
|
|
||||||
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 0.1, 0.5, 0.5, 0.05)
|
||||||
|
mesh = SubResource("BoxMesh_xdfmn")
|
||||||
|
surface_material_override/0 = SubResource("StandardMaterial3D_lirtl")
|
||||||
4
tiles/base_tile/blue.tres
Normal file
4
tiles/base_tile/blue.tres
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[gd_resource type="StandardMaterial3D" format=3 uid="uid://cunwv438ndyai"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
albedo_color = Color(0.0629363, 0.460903, 0.811309, 1)
|
||||||
4
tiles/base_tile/gray.tres
Normal file
4
tiles/base_tile/gray.tres
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[gd_resource type="StandardMaterial3D" format=3 uid="uid://cm5lg0k4vihh3"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
albedo_color = Color(0.143918, 0.143918, 0.143918, 1)
|
||||||
4
tiles/base_tile/lightblue.tres
Normal file
4
tiles/base_tile/lightblue.tres
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[gd_resource type="StandardMaterial3D" format=3 uid="uid://5s3s8phwidfy"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
albedo_color = Color(0.638985, 0.806077, 0.995958, 1)
|
||||||
4
tiles/base_tile/orange.tres
Normal file
4
tiles/base_tile/orange.tres
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[gd_resource type="StandardMaterial3D" format=3 uid="uid://dbfgv7cy7t7vo"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
albedo_color = Color(1, 0.522709, 0.205897, 1)
|
||||||
4
tiles/base_tile/red.tres
Normal file
4
tiles/base_tile/red.tres
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[gd_resource type="StandardMaterial3D" format=3 uid="uid://nkwjhj76xffi"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
albedo_color = Color(0.996113, 0, 0.229677, 1)
|
||||||
Loading…
Reference in New Issue
Block a user