RapidGameFramework
Reusable Godot managers for data-driven small games
Loading...
Searching...
No Matches
combat_card_factory.gd
Go to the documentation of this file.
1extends RefCounted
2
3
6
7const DataCacheManager = preload("res://scripts/systems/dataCacheManager/data_cache_manager.gd")
8
9var cards := {}
10var manifestations := {}
11var modifiers := {}
12var elements := {}
13var compounds := {}
14var composition_rules := {}
15var data_cache_manager = DataCacheManager.new()
16
17
18
19func load_catalog(file_path:String) -> void:
20 cards.clear()
21 manifestations.clear()
22 modifiers.clear()
23 composition_rules.clear()
24 load_catalog_data(data_cache_manager.load_json(file_path))
25
26
27
28func load_catalog_data(data) -> void:
29 cards.clear()
30 manifestations.clear()
31 modifiers.clear()
32 composition_rules.clear()
33 if not data is Dictionary:
34 return
35 composition_rules = data.get("composition", {}).duplicate(true)
36 for manifestation in data.get("manifestations", []):
37 if manifestation is Dictionary:
38 var id = str(manifestation.get("id", ""))
39 if id != "":
40 manifestations[id] = manifestation.duplicate(true)
41 for modifier in data.get("modifiers", []):
42 if modifier is Dictionary:
43 var id = str(modifier.get("id", ""))
44 if id != "":
45 modifiers[id] = modifier.duplicate(true)
46 for card in data.get("cards", []):
47 if card is Dictionary:
48 var id = str(card.get("id", ""))
49 if id != "":
50 cards[id] = card.duplicate(true)
51
52
53
54func load_definitions(file_path:String) -> void:
55 elements.clear()
56 compounds.clear()
57 load_definition_data(data_cache_manager.load_json(file_path))
58
59
60
61func get_cache_stats() -> Dictionary:
62 return data_cache_manager.get_cache_stats()
63
64
65
66func load_definition_data(data) -> void:
67 elements.clear()
68 compounds.clear()
69 if not data is Dictionary:
70 return
71 for element in data.get("elements", []):
72 if element is Dictionary:
73 var id = str(element.get("id", ""))
74 if id != "":
75 elements[id] = element.duplicate(true)
76 for compound in data.get("compounds", []):
77 if compound is Dictionary:
78 var id = str(compound.get("id", ""))
79 if id != "":
80 compounds[id] = compound.duplicate(true)
81
82
83
84func get_card(card_id:String) -> Dictionary:
85 return cards.get(card_id, {}).duplicate(true)
86
87
88
89func get_compound_definition(compound_id:String) -> Dictionary:
90 return _compound_definition(compound_id)
91
92
93
94func register_card(card:Dictionary) -> bool:
95 var id = str(card.get("id", ""))
96 if id == "":
97 return false
98 cards[id] = card.duplicate(true)
99 return true
100
101
102
103func unregister_card(card_id:String) -> bool:
104 if not cards.has(card_id):
105 return false
106 cards.erase(card_id)
107 return true
108
109
110
111func get_cards_for_unlocks(unlocked_ids:Array, fallback_ids:Array = []) -> Array:
112 var rows := []
113 var seen := {}
114 for id in fallback_ids:
115 var key = str(id)
116 if cards.has(key) and not seen.has(key):
117 rows.append(cards[key].duplicate(true))
118 seen[key] = true
119 for id in unlocked_ids:
120 var key = str(id)
121 if cards.has(key) and not seen.has(key):
122 rows.append(cards[key].duplicate(true))
123 seen[key] = true
124 return rows
125
126
127
128func get_cards_for_reaction(reaction:Dictionary) -> Array:
129 return get_cards_for_unlocks(reaction.get("unlocks_cards", []))
130
131
132
133func get_missing_unlocks(unlocked_ids:Array) -> Array:
134 var missing := []
135 for id in unlocked_ids:
136 var key = str(id)
137 if key != "" and not cards.has(key):
138 missing.append(key)
139 missing.sort()
140 return missing
141
142
143
144func compose_card(compound, manifestation_id:String, modifier_ids:Array = [], options:Dictionary = {}) -> Dictionary:
145 var compound_def = _compound_definition(compound)
146 var compound_id = str(compound_def.get("id", ""))
147 if compound_id == "" or not manifestations.has(manifestation_id):
148 return {}
149 var manifestation = manifestations[manifestation_id]
150 var clean_modifiers := []
151 for id in modifier_ids:
152 var key = str(id)
153 if modifiers.has(key) and not clean_modifiers.has(key):
154 clean_modifiers.append(key)
155 var tier = int(compound_def.get("tier", 0))
156 var energy = int(composition_rules.get("base_energy", 1)) + int(manifestation.get("energy_delta", 0))
157 var damage = int(composition_rules.get("base_damage", 6)) + tier * int(composition_rules.get("damage_per_tier", 4)) + int(manifestation.get("damage_bonus", 0))
158 var card_range = int(manifestation.get("range", composition_rules.get("default_range", 4)))
159 var aoe_radius = int(manifestation.get("aoe_radius", composition_rules.get("default_aoe_radius", 1)))
160 var duration = float(manifestation.get("duration", 0.24))
161 for id in clean_modifiers:
162 var modifier = modifiers[id]
163 energy += int(modifier.get("energy_delta", 0))
164 damage += int(modifier.get("damage_delta", 0))
165 card_range += int(modifier.get("range_delta", 0))
166 aoe_radius += int(modifier.get("aoe_delta", 0))
167 duration += float(modifier.get("duration_delta", 0.0))
168 energy = max(0, energy)
169 damage = max(1, damage)
170 card_range = max(0, card_range)
171 aoe_radius = max(0, aoe_radius)
172 duration = max(0.05, duration)
173 var modifier_names := []
174 for id in clean_modifiers:
175 modifier_names.append(str(modifiers[id].get("name", id)))
176 var compound_name = str(compound_def.get("name", compound_id.capitalize()))
177 var manifestation_name = str(manifestation.get("name", manifestation_id.capitalize()))
178 var card_name = "%s %s" % [compound_name, manifestation_name]
179 if not modifier_names.is_empty():
180 card_name = "%s %s" % [" ".join(modifier_names), card_name]
181 var card_id = str(options.get("id", _composed_card_id(compound_id, manifestation_id, clean_modifiers)))
182 var targeting = {
183 "type": str(manifestation.get("targeting_type", manifestation_id)),
184 "requires_direction": bool(manifestation.get("requires_direction", true)),
185 "range": card_range
186 }
187 if aoe_radius > 0:
188 targeting["aoe_radius"] = aoe_radius
189 return {
190 "id": card_id,
191 "name": card_name,
192 "compound": compound_id,
193 "manifestation": manifestation_id,
194 "modifiers": clean_modifiers,
195 "targeting": targeting,
196 "visual": {
197 "color": str(compound_def.get("color", composition_rules.get("default_color", "#f5d36a"))),
198 "shape": str(manifestation.get("shape", targeting["type"])),
199 "duration": duration
200 },
201 "energy": energy,
202 "damage": damage,
203 "description": _composition_description(compound_def, manifestation, clean_modifiers),
204 "source_recipe": {
205 "compound": compound_id,
206 "manifestation": manifestation_id,
207 "modifiers": clean_modifiers
208 },
209 "composed": true
210 }
211
212
213func preview_composition(compound, manifestation_id:String, modifier_ids:Array = []) -> Dictionary:
214 var card = compose_card(compound, manifestation_id, modifier_ids)
215 if not card.is_empty():
216 card["preview"] = true
217 return card
218
219
220func get_composition_issues(compound_ids:Array = []) -> Array:
221 var issues := []
222 for id in compound_ids:
223 var key = str(id)
224 if key != "" and _compound_definition(key).is_empty():
225 issues.append({"type": "missing_compound", "id": key})
226 for id in manifestations.keys():
227 var manifestation = manifestations[id]
228 if not manifestation.has("targeting_type"):
229 issues.append({"type": "missing_targeting_type", "id": id})
230 for id in modifiers.keys():
231 var modifier = modifiers[id]
232 if not modifier.has("description"):
233 issues.append({"type": "missing_modifier_description", "id": id})
234 return issues
235
236
237func get_state() -> Dictionary:
238 return {
239 "cards": cards.duplicate(true),
240 "manifestations": manifestations.duplicate(true),
241 "modifiers": modifiers.duplicate(true),
242 "elements": elements.duplicate(true),
243 "compounds": compounds.duplicate(true),
244 "composition_rules": composition_rules.duplicate(true)
245 }
246
247
248func apply_state(state:Dictionary) -> void:
249 cards = state.get("cards", {}).duplicate(true)
250 manifestations = state.get("manifestations", {}).duplicate(true)
251 modifiers = state.get("modifiers", {}).duplicate(true)
252 elements = state.get("elements", {}).duplicate(true)
253 compounds = state.get("compounds", {}).duplicate(true)
254 composition_rules = state.get("composition_rules", {}).duplicate(true)
255
256
257func _compound_definition(compound) -> Dictionary:
258 if compound is Dictionary:
259 return compound.duplicate(true)
260 var id = str(compound)
261 if compounds.has(id):
262 return compounds[id].duplicate(true)
263 if elements.has(id):
264 return elements[id].duplicate(true)
265 return {}
266
267
268func _composed_card_id(compound_id:String, manifestation_id:String, modifier_ids:Array) -> String:
269 var parts := ["crafted", compound_id, manifestation_id]
270 var sorted_modifiers = modifier_ids.duplicate()
271 sorted_modifiers.sort()
272 for id in sorted_modifiers:
273 parts.append(str(id))
274 return "_".join(parts)
275
276
277func _composition_description(compound:Dictionary, manifestation:Dictionary, modifier_ids:Array) -> String:
278 var text = "%s expressed as %s." % [
279 str(compound.get("name", compound.get("id", "Compound"))),
280 str(manifestation.get("name", manifestation.get("id", "Form"))).to_lower()
281 ]
282 if not modifier_ids.is_empty():
283 var names := []
284 for id in modifier_ids:
285 names.append(str(modifiers[id].get("name", id)))
286 text += " Modified by %s." % ", ".join(names)
287 return text