7const DataCacheManager = preload(
"res://scripts/systems/dataCacheManager/data_cache_manager.gd")
10var data_cache_manager = DataCacheManager.new()
14func load_adventure(file_path:String) -> bool:
15 return load_adventure_data(data_cache_manager.load_json(file_path))
19func get_cache_stats() -> Dictionary:
20 return data_cache_manager.get_cache_stats()
24func load_adventure_data(data) -> bool:
25 if not data
is Dictionary:
27 return register_adventure(data)
31func load_adventures(file_paths:Array) -> int:
33 for file_path
in file_paths:
34 if load_adventure(str(file_path)):
40func load_adventures_data(adventure_rows:Array) -> int:
42 for adventure_data
in adventure_rows:
43 if load_adventure_data(adventure_data):
49func register_adventure(data:Dictionary) -> bool:
50 var id = str(data.get(
"id",
""))
51 if id ==
"" or not (data.get(
"levels", [])
is Array)
or data.get(
"levels", []).is_empty():
53 adventures[id] = data.duplicate(true)
57func has_adventure(id:String) -> bool:
58 return adventures.has(id)
61func get_adventure(id:String) -> Dictionary:
62 return adventures.get(id, {}).duplicate(true)
65func list_adventures() -> Array:
67 for id
in adventures.keys():
68 var adventure = adventures[id]
71 "name": str(adventure.get(
"name", id)),
72 "description": str(adventure.get(
"description",
"")),
73 "level_count": adventure.get(
"levels", []).size(),
74 "estimated_minutes": int(adventure.get(
"estimated_minutes", 0)),
75 "rewards": adventure.get(
"rewards", {}).duplicate(true)
77 rows.sort_custom(func(a, b):
return str(a.get(
"name",
"")) < str(b.get(
"name",
"")))
81func get_level(adventure_id:String, level_index:int) -> Dictionary:
82 var levels = adventures.get(adventure_id, {}).get(
"levels", [])
83 if level_index < 0
or level_index >= levels.size():
85 return levels[level_index].duplicate(true)
89func validate_adventure(adventure_id:String) -> Array:
91 var adventure = adventures.get(adventure_id, {})
92 if adventure.is_empty():
93 return [
"Missing adventure: %s" % adventure_id]
94 if str(adventure.get(
"id",
"")) ==
"":
95 issues.append(
"Adventure is missing id.")
96 var levels = adventure.get(
"levels", [])
97 if not (levels
is Array)
or levels.is_empty():
98 issues.append(
"Adventure has no levels.")
99 for index
in range(levels.size()):
100 var level = levels[index]
101 if not level
is Dictionary:
102 issues.append(
"Level %d is not a dictionary." % index)
104 if str(level.get(
"id",
"")) ==
"":
105 issues.append(
"Level %d is missing id." % index)
106 if str(level.get(
"name",
"")) ==
"":
107 issues.append(
"Level %d is missing name." % index)
108 if not level.has(
"layout")
and not level.has(
"procedural_template"):
109 issues.append(
"Level %d needs layout or procedural_template." % index)
110 if not (level.get(
"objectives", [])
is Array)
or level.get(
"objectives", []).is_empty():
111 issues.append(
"Level %d has no objectives." % index)
112 if not (level.get(
"exits", [])
is Array)
or level.get(
"exits", []).is_empty():
113 issues.append(
"Level %d has no exits." % index)
114 if not level.has(
"player_spawn"):
115 issues.append(
"Level %d has no player_spawn." % index)
120func create_run_state(adventure_id:String) -> Dictionary:
121 if not adventures.has(adventure_id):
124 "adventure_id": adventure_id,
126 "completed_objectives": {},
127 "completed_levels": [],
128 "collected_rewards": {},
133func get_current_level(run_state:Dictionary) -> Dictionary:
134 return get_level(str(run_state.get(
"adventure_id",
"")), int(run_state.get(
"level_index", 0)))
137func complete_objective(run_state:Dictionary, objective_id:String) -> Dictionary:
138 var next_state = run_state.duplicate(true)
139 var level = get_current_level(next_state)
140 if level.is_empty()
or objective_id ==
"":
142 var level_id = str(level.get(
"id",
""))
143 var completed = next_state.get(
"completed_objectives", {}).duplicate(true)
144 var level_completed = completed.get(level_id, [])
145 if not level_completed.has(objective_id):
146 level_completed.append(objective_id)
147 completed[level_id] = level_completed
148 next_state[
"completed_objectives"] = completed
152func is_current_level_complete(run_state:Dictionary) -> bool:
153 var level = get_current_level(run_state)
157 for objective
in level.get(
"objectives", []):
158 if objective
is Dictionary
and bool(objective.get(
"required", true)):
159 required.append(str(objective.get(
"id",
"")))
160 var completed = run_state.get(
"completed_objectives", {}).get(str(level.get(
"id",
"")), [])
161 for objective_id
in required:
162 if objective_id !=
"" and not completed.has(objective_id):
167func get_progress_summary(run_state:Dictionary) -> Dictionary:
168 var adventure_id = str(run_state.get(
"adventure_id",
""))
169 var adventure = get_adventure(adventure_id)
170 var levels = adventure.get(
"levels", [])
171 var level_index = clamp(int(run_state.get(
"level_index", 0)), 0, max(0, levels.size() - 1))
172 var level = get_level(adventure_id, level_index)
173 var completed = run_state.get(
"completed_objectives", {}).get(str(level.get(
"id",
"")), [])
174 var required_total := 0
175 var optional_total := 0
176 var required_done := 0
177 var optional_done := 0
178 for objective
in level.get(
"objectives", []):
179 if not objective
is Dictionary:
181 var objective_id = str(objective.get(
"id",
""))
182 if bool(objective.get(
"required", true)):
184 if completed.has(objective_id):
188 if completed.has(objective_id):
191 "adventure_id": adventure_id,
192 "adventure_name": str(adventure.get(
"name", adventure_id)),
193 "level_index": level_index,
194 "level_count": levels.size(),
195 "level_id": str(level.get(
"id",
"")),
196 "level_name": str(level.get(
"name",
"")),
197 "required_done": required_done,
198 "required_total": required_total,
199 "optional_done": optional_done,
200 "optional_total": optional_total,
201 "finished": bool(run_state.get(
"finished", false))
206func get_pacing_summary(adventure_id:String) -> Dictionary:
207 var adventure = get_adventure(adventure_id)
208 var levels = adventure.get(
"levels", [])
209 var estimated_minutes = int(adventure.get(
"estimated_minutes", 0))
210 var required_objectives := 0
211 var optional_objectives := 0
212 var encounter_count := 0
213 var material_count := 0
215 if not level
is Dictionary:
217 for objective
in level.get(
"objectives", []):
218 if not objective
is Dictionary:
220 if bool(objective.get(
"required", true)):
221 required_objectives += 1
223 optional_objectives += 1
224 var explicit_encounters = 0
225 if level.get(
"enemies", [])
is Array:
226 explicit_encounters += level.get(
"enemies", []).size()
227 if level.get(
"bosses", [])
is Array:
228 explicit_encounters += level.get(
"bosses", []).size()
229 var procedural_budget = int(level.get(
"procedural_template", {}).get(
"enemy_budget", 0))
if level.get(
"procedural_template", {})
is Dictionary
else 0
230 encounter_count += max(explicit_encounters, procedural_budget)
231 if level.get(
"procedural_template", {})
is Dictionary:
232 material_count += int(level.get(
"procedural_template", {}).get(
"material_nodes", 0))
233 for object
in level.get(
"objects", []):
234 if object
is Dictionary
and str(object.get(
"kind",
"")) ==
"resource":
235 material_count += int(object.get(
"amount", 1))
237 "adventure_id": adventure_id,
238 "estimated_minutes": estimated_minutes,
239 "level_count": levels.size(),
240 "required_objectives": required_objectives,
241 "optional_objectives": optional_objectives,
242 "encounter_count": encounter_count,
243 "material_count": material_count,
244 "mvp_length_ready": estimated_minutes >= 8
and estimated_minutes <= 12,
245 "has_required_objectives": required_objectives > 0,
246 "has_completion_encounter": encounter_count > 0
250func advance_level(run_state:Dictionary) -> Dictionary:
251 var next_state = run_state.duplicate(true)
252 if not is_current_level_complete(next_state):
254 var adventure = get_adventure(str(next_state.get(
"adventure_id",
"")))
255 var current_index = int(next_state.get(
"level_index", 0))
256 var completed_levels = next_state.get(
"completed_levels", []).duplicate()
257 var current_level = get_level(str(next_state.get(
"adventure_id",
"")), current_index)
258 var level_id = str(current_level.get(
"id",
""))
259 if level_id !=
"" and not completed_levels.has(level_id):
260 completed_levels.append(level_id)
261 next_state[
"completed_levels"] = completed_levels
262 if current_index + 1 >= adventure.get(
"levels", []).size():
263 next_state[
"finished"] = true
265 next_state[
"level_index"] = current_index + 1