13func configure(next_entities:Array = []) -> void:
16 for raw_entity
in next_entities:
17 if raw_entity
is Dictionary:
18 add_entity(raw_entity)
22func create_entity(data:Dictionary = {}) -> Dictionary:
23 var id = str(data.get(
"id",
""))
24 var entity = data.duplicate(true)
26 entity[
"kind"] = str(entity.get(
"kind", entity.get(
"type",
"entity")))
27 entity[
"type"] = str(entity.get(
"type", entity.get(
"kind",
"entity")))
28 entity[
"name"] = str(entity.get(
"name", id.capitalize()
if id !=
"" else "Entity"))
29 entity[
"team"] = str(entity.get(
"team",
"neutral"))
30 entity[
"owner"] = str(entity.get(
"owner",
""))
31 entity[
"tags"] = _normalize_string_array(entity.get(
"tags", []))
32 entity[
"state"] = str(entity.get(
"state",
"idle"))
33 entity[
"alive"] = bool(entity.get(
"alive", int(entity.get(
"hp", 1)) > 0))
34 entity[
"hp"] = int(entity.get(
"hp", entity.get(
"health", 1)))
35 entity[
"max_hp"] = int(entity.get(
"max_hp", entity.get(
"max_health", max(1, int(entity.get(
"hp", entity.get(
"health", 1)))))))
36 entity[
"cell"] = _cell_to_array(_cell_from_any(entity.get(
"cell", [0, 0])))
37 entity[
"position"] = _vector_to_array(_vector_from_any(entity.get(
"position", Vector2.ZERO)))
38 entity[
"velocity"] = _vector_to_array(_vector_from_any(entity.get(
"velocity", Vector2.ZERO)))
39 entity[
"facing"] = _cell_to_array(_cell_from_any(entity.get(
"facing", [0, 1])))
40 entity[
"status_effects"] = _normalize_statuses(entity.get(
"status_effects", entity.get(
"statuses", [])))
41 entity[
"metadata"] = entity.get(
"metadata", {}).duplicate(true)
if entity.get(
"metadata", {})
is Dictionary
else {}
46func add_entity(data:Dictionary) -> Dictionary:
47 var entity = create_entity(data)
48 var id = str(entity.get(
"id",
""))
50 id =
"entity_%d" % (entity_order.size() + 1)
52 if not entities.has(id):
53 entity_order.append(id)
55 return entity.duplicate(true)
59func remove_entity(entity_id:String) -> bool:
60 if not entities.has(entity_id):
62 entities.erase(entity_id)
63 entity_order.erase(entity_id)
68func has_entity(entity_id:String) -> bool:
69 return entities.has(entity_id)
73func get_entity(entity_id:String) -> Dictionary:
74 return entities.get(entity_id, {}).duplicate(true)
78func get_entities(filters:Dictionary = {}) -> Array:
80 for id
in entity_order:
81 if not entities.has(id):
83 var entity = entities[id]
84 if _matches_filters(entity, filters):
85 result.append(entity.duplicate(true))
90func update_entity(entity_id:String, patch:Dictionary) -> Dictionary:
91 if not entities.has(entity_id):
93 var entity = entities[entity_id].duplicate(true)
94 for key
in patch.keys():
95 entity[key] = patch[key]
96 entities[entity_id] = create_entity(entity)
97 return entities[entity_id].duplicate(true)
101func set_cell(entity_id:String, cell) -> Dictionary:
102 return update_entity(entity_id, {
"cell": _cell_to_array(_cell_from_any(cell))})
106func set_position(entity_id:String, position) -> Dictionary:
107 return update_entity(entity_id, {
"position": _vector_to_array(_vector_from_any(position))})
111func add_status(entity_id:String, status:Dictionary) -> Dictionary:
112 if not entities.has(entity_id):
114 var entity = entities[entity_id].duplicate(true)
115 var statuses = _normalize_statuses(entity.get(
"status_effects", []))
116 var next_status = status.duplicate(true)
117 next_status[
"id"] = str(next_status.get(
"id",
"status"))
118 var replaced := false
119 for index
in range(statuses.size()):
120 if str(statuses[index].get(
"id",
"")) == str(next_status.get(
"id",
"")):
121 statuses[index] = next_status
125 statuses.append(next_status)
126 entity[
"status_effects"] = statuses
127 entities[entity_id] = create_entity(entity)
128 return entities[entity_id].duplicate(true)
132func tick_statuses(delta:float) -> void:
133 for id
in entity_order:
134 if not entities.has(id):
136 var entity = entities[id].duplicate(true)
137 var active_statuses := []
138 for status
in _normalize_statuses(entity.get(
"status_effects", [])):
139 var next_status = status.duplicate(true)
140 if next_status.has(
"duration"):
141 next_status[
"duration"] = max(0.0, float(next_status.get(
"duration", 0.0)) - delta)
142 if float(next_status.get(
"duration", 0.0)) <= 0.0:
144 active_statuses.append(next_status)
145 entity[
"status_effects"] = active_statuses
146 entities[id] = create_entity(entity)
150func apply_damage(entity_id:String, amount:int, options:Dictionary = {}) -> Dictionary:
151 if not entities.has(entity_id):
153 var entity = entities[entity_id].duplicate(true)
154 var defense = int(entity.get(
"defense", 0))
155 var dealt = max(0, amount - defense)
if bool(options.get(
"use_defense", true))
else max(0, amount)
156 entity[
"hp"] = max(0, int(entity.get(
"hp", 0)) - dealt)
157 entity[
"alive"] = int(entity.get(
"hp", 0)) > 0
158 if not bool(entity.get(
"alive", true)):
159 entity[
"state"] = str(options.get(
"death_state",
"dead"))
160 entities[entity_id] = create_entity(entity)
162 "entity": entities[entity_id].duplicate(true),
164 "defeated":
not bool(entities[entity_id].get(
"alive", true))
169func heal(entity_id:String, amount:int) -> Dictionary:
170 if not entities.has(entity_id):
172 var entity = entities[entity_id].duplicate(true)
173 entity[
"hp"] = min(int(entity.get(
"max_hp", 1)), int(entity.get(
"hp", 0)) + max(0, amount))
174 entity[
"alive"] = int(entity.get(
"hp", 0)) > 0
175 entities[entity_id] = create_entity(entity)
176 return entities[entity_id].duplicate(true)
180func get_entities_at_cell(cell, filters:Dictionary = {}) -> Array:
181 var target = _cell_from_any(cell)
183 for entity
in get_entities(filters):
184 if _cell_from_any(entity.get(
"cell", [0, 0])) == target:
185 result.append(entity)
190func get_occupied_cells(filters:Dictionary = {}) -> Array:
192 for entity
in get_entities(filters):
193 cells.append(_cell_from_any(entity.get(
"cell", [0, 0])))
198func get_state() -> Dictionary:
200 "entities": get_entities(),
201 "order": entity_order.duplicate(true)
206func apply_state(state:Dictionary) -> void:
207 configure(state.get(
"entities", [])
if state.get(
"entities", [])
is Array
else [])
208 var order = state.get(
"order", [])
209 if order
is Array
and not order.is_empty():
210 var normalized_order := []
212 if entities.has(str(id)):
213 normalized_order.append(str(id))
214 for id
in entity_order:
215 if not normalized_order.has(id):
216 normalized_order.append(id)
217 entity_order = normalized_order
220func _matches_filters(entity:Dictionary, filters:Dictionary) -> bool:
221 if filters.has(
"kind")
and str(entity.get(
"kind",
"")) != str(filters.get(
"kind",
"")):
223 if filters.has(
"type")
and str(entity.get(
"type",
"")) != str(filters.get(
"type",
"")):
225 if filters.has(
"team")
and str(entity.get(
"team",
"")) != str(filters.get(
"team",
"")):
227 if filters.has(
"owner")
and str(entity.get(
"owner",
"")) != str(filters.get(
"owner",
"")):
229 if filters.has(
"alive")
and bool(entity.get(
"alive", true)) != bool(filters.get(
"alive", true)):
231 if filters.has(
"tags"):
232 var tags = _normalize_string_array(entity.get(
"tags", []))
233 for tag
in _normalize_string_array(filters.get(
"tags", [])):
234 if not tags.has(tag):
239func _normalize_string_array(value) -> Array:
241 if not value
is Array:
244 result.append(str(item))
248func _normalize_statuses(value) -> Array:
250 if not value
is Array:
253 if status
is Dictionary:
254 var entry = status.duplicate(true)
255 entry[
"id"] = str(entry.get(
"id",
"status"))
260func _cell_from_any(value) -> Vector2i:
261 if value
is Vector2i:
264 return Vector2i(int(value.x), int(value.y))
265 if value
is Array
and value.size() >= 2:
266 return Vector2i(int(value[0]), int(value[1]))
267 if value
is Dictionary
and value.has(
"cell"):
268 return _cell_from_any(value.get(
"cell"))
269 if value
is Dictionary
and value.has(
"x")
and value.has(
"y"):
270 return Vector2i(int(value.get(
"x", 0)), int(value.get(
"y", 0)))
274func _vector_from_any(value) -> Vector2:
277 if value
is Vector2i:
278 return Vector2(float(value.x), float(value.y))
279 if value
is Array
and value.size() >= 2:
280 return Vector2(float(value[0]), float(value[1]))
281 if value
is Dictionary
and value.has(
"position"):
282 return _vector_from_any(value.get(
"position"))
283 if value
is Dictionary
and value.has(
"x")
and value.has(
"y"):
284 return Vector2(float(value.get(
"x", 0.0)), float(value.get(
"y", 0.0)))
288func _cell_to_array(cell:Vector2i) -> Array:
289 return [cell.x, cell.y]
292func _vector_to_array(vector:Vector2) -> Array:
293 return [vector.x, vector.y]