RapidGameFramework
Reusable Godot managers for data-driven small games
Loading...
Searching...
No Matches
grid_world_manager.gd
Go to the documentation of this file.
1extends RefCounted
2
3
6
7var world_config := {}
8var tile_size := 40.0
9var blocked_cells_cache:Array = []
10var material_nodes_by_cell:Dictionary = {}
11var spell_interactions_by_cell:Dictionary = {}
12var cache_hits:int = 0
13var cache_misses:int = 0
14
15
16
17func configure(config:Dictionary, next_tile_size := 40.0) -> void:
18 world_config = config.duplicate(true)
19 tile_size = max(1.0, float(next_tile_size))
20 _rebuild_runtime_indexes()
21
22
23func get_grid_size() -> Vector2i:
24 return _cell_from_value(world_config.get("grid_size", [12, 12]))
25
26
27func get_bounds() -> Rect2:
28 return Rect2(Vector2.ZERO, Vector2(get_grid_size()) * tile_size)
29
30
31func get_blocked_cells() -> Array:
32 cache_hits += 1
33 return blocked_cells_cache.duplicate()
34
35
36func get_movement_blocked_cells(occupied_cells:Array) -> Array:
37 var cells = get_blocked_cells()
38 for cell in occupied_cells:
39 cells.append(_cell_from_value(cell))
40 return cells
41
42
43func cells_from_obstacle(obstacle:Dictionary) -> Array:
44 var cells := []
45 if obstacle.has("cells"):
46 for cell in obstacle.get("cells", []):
47 cells.append(_cell_from_value(cell))
48 elif obstacle.has("rect"):
49 cells = rect_to_cells(_rect_from_value(obstacle.get("rect", [])))
50 return cells
51
52
53func rect_to_cells(rect:Rect2) -> Array:
54 var min_cell = Vector2i(int(floor(rect.position.x / tile_size)), int(floor(rect.position.y / tile_size)))
55 var max_cell = Vector2i(
56 int(floor((rect.position.x + max(0.0, rect.size.x - 0.01)) / tile_size)),
57 int(floor((rect.position.y + max(0.0, rect.size.y - 0.01)) / tile_size))
58 )
59 var cells := []
60 for y in range(min_cell.y, max_cell.y + 1):
61 for x in range(min_cell.x, max_cell.x + 1):
62 cells.append(Vector2i(x, y))
63 return cells
64
65
66
67func collect_material_at(cell:Vector2i, collected_nodes:Dictionary) -> Dictionary:
68 var nodes:Array = material_nodes_by_cell.get(_cell_key(cell), []) if material_nodes_by_cell.get(_cell_key(cell), []) is Array else []
69 if nodes.is_empty():
70 cache_misses += 1
71 else:
72 cache_hits += 1
73 for node in nodes:
74 if not node is Dictionary:
75 continue
76 var node_id = str(node.get("id", ""))
77 if node_id == "" or collected_nodes.has(node_id):
78 continue
79 if _cell_from_value(node.get("cell", [0, 0])) != cell:
80 continue
81 return {
82 "collected": true,
83 "node_id": node_id,
84 "element": str(node.get("element", "")),
85 "amount": int(node.get("amount", 1))
86 }
87 return {"collected": false}
88
89
90
91func resolve_spell_interactions(card:Dictionary, affected_cells:Array, activated_interactions:Dictionary) -> Array:
92 var activated := []
93 for affected_cell in affected_cells:
94 var cell:Vector2i = _cell_from_value(affected_cell)
95 var interactions:Array = spell_interactions_by_cell.get(_cell_key(cell), []) if spell_interactions_by_cell.get(_cell_key(cell), []) is Array else []
96 if interactions.is_empty():
97 cache_misses += 1
98 else:
99 cache_hits += 1
100 for interaction in interactions:
101 if not interaction is Dictionary:
102 continue
103 var id = str(interaction.get("id", ""))
104 if id == "" or activated_interactions.has(id):
105 continue
106 if not _card_matches_interaction(card, interaction):
107 continue
108 var entry = interaction.duplicate(true)
109 entry["activated"] = true
110 activated.append(entry)
111 return activated
112
113
114func get_cache_stats() -> Dictionary:
115 return {
116 "blocked_cells": blocked_cells_cache.size(),
117 "material_cells": material_nodes_by_cell.size(),
118 "interaction_cells": spell_interactions_by_cell.size(),
119 "cache_hits": cache_hits,
120 "cache_misses": cache_misses
121 }
122
123
124func clear_caches() -> void:
125 blocked_cells_cache.clear()
126 material_nodes_by_cell.clear()
127 spell_interactions_by_cell.clear()
128 cache_hits = 0
129 cache_misses = 0
130
131
132func _rect_from_value(value) -> Rect2:
133 if value is Rect2:
134 return value
135 if value is Array and value.size() >= 4:
136 return Rect2(Vector2(float(value[0]), float(value[1])), Vector2(float(value[2]), float(value[3])))
137 return Rect2()
138
139
140func _cell_from_value(value) -> Vector2i:
141 if value is Vector2i:
142 return value
143 if value is Vector2:
144 return Vector2i(int(value.x), int(value.y))
145 if value is Array and value.size() >= 2:
146 return Vector2i(int(value[0]), int(value[1]))
147 if value is Dictionary and value.has("cell"):
148 return _cell_from_value(value.get("cell"))
149 return Vector2i.ZERO
150
151
152func _rebuild_runtime_indexes() -> void:
153 clear_caches()
154 for obstacle in world_config.get("obstacles", []):
155 if not obstacle is Dictionary:
156 continue
157 for cell in cells_from_obstacle(obstacle):
158 blocked_cells_cache.append(_cell_from_value(cell))
159 for node in world_config.get("material_nodes", []):
160 if not node is Dictionary:
161 continue
162 var node_cell:Vector2i = _cell_from_value(node.get("cell", [0, 0]))
163 var material_key:String = _cell_key(node_cell)
164 if not material_nodes_by_cell.has(material_key):
165 material_nodes_by_cell[material_key] = []
166 material_nodes_by_cell[material_key].append(node.duplicate(true))
167 for interaction in world_config.get("spell_interactions", []):
168 if not interaction is Dictionary:
169 continue
170 var interaction_cell:Vector2i = _cell_from_value(interaction.get("cell", [0, 0]))
171 var interaction_key:String = _cell_key(interaction_cell)
172 if not spell_interactions_by_cell.has(interaction_key):
173 spell_interactions_by_cell[interaction_key] = []
174 spell_interactions_by_cell[interaction_key].append(interaction.duplicate(true))
175
176
177func _cell_key(cell:Vector2i) -> String:
178 return "%d,%d" % [cell.x, cell.y]
179
180
181func _card_matches_interaction(card:Dictionary, interaction:Dictionary) -> bool:
182 var requires = interaction.get("requires", {})
183 if not requires is Dictionary or requires.is_empty():
184 return true
185 for key in requires.keys():
186 var expected = requires[key]
187 if key == "compound" and str(card.get("compound", "")) != str(expected):
188 return false
189 if key == "manifestation" and str(card.get("manifestation", "")) != str(expected):
190 return false
191 if key == "card_id" and str(card.get("id", "")) != str(expected):
192 return false
193 if key == "modifier" and not card.get("modifiers", []).has(str(expected)):
194 return false
195 return true
196
197