20var card_instances := []
22var categorized_stacks := {}
23var next_legacy_uid := 1
27func add_item(item_id:String, amount:int = 1) -> void:
28 add_to_stack(item_stacks, item_id, amount)
32func remove_item(item_id:String, amount:int = 1) -> bool:
33 if amount <= 0
or get_item_count(item_id) < amount:
35 var remaining = get_item_count(item_id) - amount
37 item_stacks.erase(item_id)
39 item_stacks[item_id] = remaining
44func get_item_count(item_id:String) -> int:
45 return int(item_stacks.get(item_id, 0))
49func get_items() -> Dictionary:
50 return item_stacks.duplicate(true)
55func add_to_stack(stacks:Dictionary, item_id:String, amount:int) -> void:
56 if item_id ==
"" or amount == 0:
58 var next_amount =
int(stacks.get(item_id, 0)) + amount
62 stacks[item_id] = next_amount
66func positive_stack_ids(stacks:Dictionary) -> Array:
68 for id
in stacks.keys():
69 if int(stacks.get(id, 0)) > 0:
77func stack_rows(stacks:Dictionary, formatter:Callable =
Callable()) -> Array:
79 for id
in positive_stack_ids(stacks):
81 if formatter.is_valid():
82 label = str(formatter.call(id))
86 "count":
int(stacks.get(id, 0))
92func stack_lines(stacks:Dictionary, formatter:Callable =
Callable(), empty_text:String =
"None") -> Array:
94 for row
in stack_rows(stacks, formatter):
95 lines.append(
"%s: %d" % [str(row.get(
"label",
"")),
int(row.get(
"count", 0))])
98 lines.append(empty_text)
103func add_to_category(category:String, item_id:String, amount:int = 1) -> void:
104 if category ==
"" or item_id ==
"" or amount <= 0:
106 if not categorized_stacks.has(category):
107 categorized_stacks[category] = {}
108 var stacks = categorized_stacks[category]
109 stacks[item_id] =
int(stacks.get(item_id, 0)) + amount
110 categorized_stacks[category] = stacks
114func remove_from_category(category:String, item_id:String, amount:int = 1) -> bool:
115 if category ==
"" or item_id ==
"" or amount <= 0
or get_category_count(category, item_id) < amount:
117 var stacks = categorized_stacks.get(category, {})
118 var remaining =
int(stacks.get(item_id, 0)) - amount
120 stacks.erase(item_id)
122 stacks[item_id] = remaining
123 categorized_stacks[category] = stacks
128func get_category_count(category:String, item_id:String) -> int:
129 return int(categorized_stacks.get(category, {}).get(item_id, 0))
133func set_category(category:String, stacks:Dictionary) -> void:
136 categorized_stacks[category] = _normalized_stacks(stacks)
140func get_category(category:String) -> Dictionary:
141 return categorized_stacks.get(category, {}).duplicate(true)
145func get_categories() -> Dictionary:
146 return categorized_stacks.duplicate(true)
150func apply_category_delta(category:String, delta:Dictionary) -> Dictionary:
151 for id
in delta.keys():
152 var amount =
int(delta.get(id, 0))
154 add_to_category(category, str(id), amount)
156 remove_from_category(category, str(id), abs(amount))
157 return get_category(category)
162func add_card(card_id) -> void:
163 card_ids.append(str(card_id))
164 card_instances.append({
165 "uid": _make_legacy_uid(),
166 "base_id": str(card_id),
174func add_cards(ids:Array) -> void:
181func add_card_instance(instance:Dictionary) -> void:
182 var record = instance.duplicate(true)
183 if str(record.get(
"uid",
"")) ==
"":
184 record[
"uid"] = _make_legacy_uid()
185 if not record.has(
"locked"):
186 record[
"locked"] = false
187 card_instances.append(record)
188 card_ids.append(str(record.get(
"base_id", record.get(
"id",
""))))
193func remove_card(card_id_or_uid) -> bool:
194 var id = str(card_id_or_uid)
195 var index = _find_instance_index(id)
198 if bool(card_instances[index].get(
"locked", false)):
200 var base_id = str(card_instances[index].get(
"base_id",
""))
201 card_instances.remove_at(index)
202 var base_index = card_ids.find(base_id)
204 card_ids.remove_at(base_index)
209func count_card(card_id) -> int:
212 if id == str(card_id):
218func get_card_ids() -> Array:
219 return card_ids.duplicate()
223func get_unique_card_ids() -> Array:
226 if not unique.has(id):
232func get_instances() -> Array:
233 return card_instances.duplicate(true)
237func get_instance(uid:String) -> Dictionary:
238 var index = _find_instance_index(uid)
241 return card_instances[index].duplicate(true)
245func get_instances_for_base(card_id) -> Array:
247 for instance
in card_instances:
248 if str(instance.get(
"base_id",
"")) == str(card_id):
249 list.append(instance.duplicate(true))
256func upgrade_instance(uid:String, consumed_count:int = 1, max_level:int = 5) -> bool:
257 var index = _find_instance_index(uid)
260 if bool(card_instances[index].get(
"locked", false)):
262 var base_id = str(card_instances[index].get(
"base_id",
""))
263 if _count_unlocked_merge_material(base_id, uid) < consumed_count:
265 var material_index = _find_best_merge_material_index(base_id, uid,
int(card_instances[index].get(
"level", 1)), max_level)
266 if material_index < 0:
268 var material_level =
int(card_instances[material_index].get(
"level", 1))
269 var new_level =
int(card_instances[index].get(
"level", 1)) + material_level
270 if new_level > max_level:
273 for i
in [material_index]:
274 if consumed >= consumed_count:
278 if str(card_instances[i].get(
"base_id",
"")) == base_id
and not bool(card_instances[i].get(
"locked", false)):
279 card_instances.remove_at(i)
280 var base_index = card_ids.find(base_id)
282 card_ids.remove_at(base_index)
284 if consumed < consumed_count:
286 index = _find_instance_index(uid)
289 card_instances[index][
"level"] = new_level
294func set_locked(uid:String, locked:bool) -> bool:
295 var index = _find_instance_index(uid)
298 card_instances[index][
"locked"] = locked
303func is_locked(uid:String) -> bool:
304 var index = _find_instance_index(uid)
307 return bool(card_instances[index].get(
"locked", false))
311func count_unlocked_merge_material(card_id, selected_uid:String =
"") -> int:
312 return _count_unlocked_merge_material(str(card_id), selected_uid)
316func get_best_merge_material(card_id, selected_uid:String =
"", max_level:int = 5) -> Dictionary:
317 var selected = get_instance(selected_uid)
318 var selected_level =
int(selected.get(
"level", 1))
319 var index = _find_best_merge_material_index(str(card_id), selected_uid, selected_level, max_level)
322 return card_instances[index].duplicate(true)
326func get_cards(card_manager) -> Array:
329 var card = card_manager.get_card(id)
330 if not card.is_empty():
336func get_state() -> Dictionary:
338 "items": get_items(),
339 "categories": get_categories(),
340 "card_ids": get_card_ids(),
341 "card_instances": get_instances()
347func apply_state(state:Dictionary) -> void:
348 item_stacks = state.get(
"items", {}).duplicate(true)
349 categorized_stacks.clear()
350 for category
in state.get(
"categories", {}).keys():
351 var raw_stacks = state.get(
"categories", {}).get(category, {})
352 if raw_stacks
is Dictionary:
353 categorized_stacks[str(category)] = _normalized_stacks(raw_stacks)
355 card_instances.clear()
356 if state.has(
"card_instances"):
357 for raw_instance
in state.get(
"card_instances", []):
358 if not raw_instance
is Dictionary:
360 var instance = raw_instance.duplicate(true)
361 if str(instance.get(
"uid",
"")) ==
"":
362 instance[
"uid"] = _make_legacy_uid()
363 instance[
"base_id"] = str(instance.get(
"base_id", instance.get(
"id",
"")))
364 if not instance.has(
"modifier_ids"):
365 instance[
"modifier_ids"] = []
366 if not instance.has(
"level"):
367 instance[
"level"] = 1
368 if not instance.has(
"locked"):
369 instance[
"locked"] = false
370 card_instances.append(instance)
371 card_ids.append(str(instance.get(
"base_id",
"")))
373 for id
in state.get(
"card_ids", []):
375 _sync_legacy_counter()
378func _find_instance_index(id_or_uid:String) -> int:
379 for i
in range(card_instances.size()):
380 if str(card_instances[i].get(
"uid",
"")) == id_or_uid:
382 for i
in range(card_instances.size()):
383 if str(card_instances[i].get(
"base_id",
"")) == id_or_uid:
388func _count_unlocked_merge_material(base_id:String, selected_uid:String =
"") -> int:
390 for instance
in card_instances:
391 if str(instance.get(
"uid",
"")) == selected_uid:
393 if str(instance.get(
"base_id",
"")) == base_id
and not bool(instance.get(
"locked", false)):
398func _find_best_merge_material_index(base_id:String, selected_uid:String, selected_level:int, max_level:int) -> int:
401 for i
in range(card_instances.size()):
402 var instance = card_instances[i]
403 if str(instance.get(
"uid",
"")) == selected_uid:
405 if str(instance.get(
"base_id",
"")) != base_id:
407 if bool(instance.get(
"locked", false)):
409 var level =
int(instance.get(
"level", 1))
410 if selected_level + level > max_level:
412 if level > best_level:
418func _make_legacy_uid() -> String:
419 var uid =
"legacy_%d" % next_legacy_uid
424func _sync_legacy_counter() -> void:
426 for instance
in card_instances:
427 var uid = str(instance.get(
"uid",
""))
428 if uid.begins_with(
"legacy_"):
429 highest = max(highest,
int(uid.trim_prefix(
"legacy_")))
430 next_legacy_uid = highest + 1
433func _normalized_stacks(stacks:Dictionary) -> Dictionary:
435 for id
in stacks.keys():
436 var amount =
int(stacks.get(id, 0))
437 if str(id) !=
"" and amount > 0:
438 normalized[str(id)] = amount
Callable
Return stack rows for table or category-panel renderers.
int
Add amount to a generic item stack.