class_name CPPool extends RefCounted ## CPPool - Tracks Crystal Points generated during a turn # CP stored by element var _cp: Dictionary = {} func _init() -> void: clear() ## Clear all CP (called at end of each action) func clear() -> void: for element in Enums.Element.values(): _cp[element] = 0 ## Add CP of a specific element (floor at 0 to prevent negative) func add_cp(element: Enums.Element, amount: int) -> void: _cp[element] = max(0, _cp.get(element, 0) + amount) ## Get CP of a specific element func get_cp(element: Enums.Element) -> int: return _cp.get(element, 0) ## Get total CP available func get_total_cp() -> int: var total = 0 for element in _cp: total += _cp[element] return total ## Get total CP of non-Light/Dark elements func get_non_light_dark_cp() -> int: var total = 0 for element in _cp: if not Enums.is_light_or_dark(element): total += _cp[element] return total ## Check if we can afford a card cost func can_afford_card(card_data: CardDatabase.CardData) -> bool: if not card_data: return false var cost = card_data.cost var elements = card_data.elements # Check if this is a Light/Dark card var is_light_dark = false for element in elements: if Enums.is_light_or_dark(element): is_light_dark = true break # For Light/Dark cards: need at least 1 CP of any non-Light/Dark element # (Light/Dark cards can be paid with any combination of CP, but you need # at least 1 CP from a regular element - can't pay entirely with L/D CP) if is_light_dark: if get_non_light_dark_cp() < 1: return false return get_total_cp() >= cost # For multi-element cards, need at least 1 CP of each element if elements.size() > 1: for element in elements: if get_cp(element) < 1: return false # Total must be at least the cost return get_total_cp() >= cost # For single element cards, need at least 1 CP of that element var primary_element = elements[0] if elements.size() > 0 else Enums.Element.FIRE if get_cp(primary_element) < 1: return false return get_total_cp() >= cost ## Spend CP to pay a card cost ## Returns dictionary of CP spent by element, or empty dict if cannot afford func spend_for_card(card_data: CardDatabase.CardData) -> Dictionary: if not can_afford_card(card_data): return {} var cost = card_data.cost var elements = card_data.elements var remaining = cost var spent_cp: Dictionary = {} # Track what we spent var max_iterations = cost + 10 # Safety limit to prevent infinite loops var iterations = 0 # Check if this is a Light/Dark card var is_light_dark = false for element in elements: if Enums.is_light_or_dark(element): is_light_dark = true break # For Light/Dark cards: spend 1 non-Light/Dark CP first (required) if is_light_dark: for element in _cp: if not Enums.is_light_or_dark(element) and _cp[element] > 0: _cp[element] -= 1 spent_cp[element] = spent_cp.get(element, 0) + 1 remaining -= 1 break # For multi-element, spend 1 of each required element first elif elements.size() > 1: for element in elements: _cp[element] -= 1 spent_cp[element] = spent_cp.get(element, 0) + 1 remaining -= 1 # For single element (non-Light/Dark), spend at least 1 of that element elif elements.size() == 1: var element = elements[0] if not Enums.is_light_or_dark(element): _cp[element] -= 1 spent_cp[element] = spent_cp.get(element, 0) + 1 remaining -= 1 # Spend remaining from any element (prefer non-Light/Dark first) while remaining > 0 and iterations < max_iterations: iterations += 1 var did_spend = false # First try non-Light/Dark elements for element in _cp: if not Enums.is_light_or_dark(element) and _cp[element] > 0: _cp[element] -= 1 spent_cp[element] = spent_cp.get(element, 0) + 1 remaining -= 1 did_spend = true break # Then try Light/Dark elements if needed if not did_spend: for element in _cp: if _cp[element] > 0: _cp[element] -= 1 spent_cp[element] = spent_cp.get(element, 0) + 1 remaining -= 1 did_spend = true break if not did_spend: push_error("Failed to spend remaining CP") return {} if remaining > 0: push_error("CP spending exceeded maximum iterations") return {} return spent_cp ## Check if we can afford an ability cost func can_afford_ability(cost: CardDatabase.CostData) -> bool: if not cost: return true # No cost means free # Check element-specific requirements if cost.fire > 0 and get_cp(Enums.Element.FIRE) < cost.fire: return false if cost.ice > 0 and get_cp(Enums.Element.ICE) < cost.ice: return false if cost.wind > 0 and get_cp(Enums.Element.WIND) < cost.wind: return false if cost.lightning > 0 and get_cp(Enums.Element.LIGHTNING) < cost.lightning: return false if cost.water > 0 and get_cp(Enums.Element.WATER) < cost.water: return false if cost.earth > 0 and get_cp(Enums.Element.EARTH) < cost.earth: return false if cost.light > 0 and get_cp(Enums.Element.LIGHT) < cost.light: return false if cost.dark > 0 and get_cp(Enums.Element.DARK) < cost.dark: return false # Check total return get_total_cp() >= cost.get_total_cp() ## Spend CP to pay an ability cost func spend_for_ability(cost: CardDatabase.CostData) -> bool: if not can_afford_ability(cost): return false # Spend element-specific CP (use add_cp with negative to ensure floor at 0) if cost.fire > 0: add_cp(Enums.Element.FIRE, -cost.fire) if cost.ice > 0: add_cp(Enums.Element.ICE, -cost.ice) if cost.wind > 0: add_cp(Enums.Element.WIND, -cost.wind) if cost.lightning > 0: add_cp(Enums.Element.LIGHTNING, -cost.lightning) if cost.water > 0: add_cp(Enums.Element.WATER, -cost.water) if cost.earth > 0: add_cp(Enums.Element.EARTH, -cost.earth) if cost.light > 0: add_cp(Enums.Element.LIGHT, -cost.light) if cost.dark > 0: add_cp(Enums.Element.DARK, -cost.dark) # Spend generic from any element var generic_remaining = cost.generic while generic_remaining > 0: for element in _cp: if _cp[element] > 0: add_cp(element, -1) generic_remaining -= 1 break return true ## Get a display-friendly dictionary of current CP func get_display_data() -> Dictionary: var data = {} for element in _cp: if _cp[element] > 0: data[Enums.element_to_string(element)] = _cp[element] return data func _to_string() -> String: var parts = [] for element in _cp: if _cp[element] > 0: parts.append("%s: %d" % [Enums.element_to_string(element), _cp[element]]) if parts.size() == 0: return "[CPPool: empty]" return "[CPPool: " + ", ".join(parts) + "]"