Files
FFCardGame/scripts/game/CPPool.gd
2026-01-26 16:14:57 -05:00

232 lines
6.4 KiB
GDScript

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) + "]"