feature updates

This commit is contained in:
2026-02-02 16:28:53 -05:00
parent bf9aa3fa23
commit 44c06530ac
83 changed files with 282641 additions and 11251 deletions

View File

@@ -25,6 +25,17 @@ var zone_type: Enums.ZoneType = Enums.ZoneType.DECK
# Temporary effects (cleared at end of turn)
var power_modifiers: Array[int] = []
var temporary_abilities: Array = []
var temporary_keywords: Dictionary = {} # keyword -> duration
var restrictions: Dictionary = {} # restriction_type -> duration
var requirements: Dictionary = {} # requirement_type -> duration
var protections: Dictionary = {} # protection_type -> duration
# Counters
var counters: Dictionary = {} # counter_type -> count
# Special states
var is_frozen: bool = false
var base_power_override: int = -1 # -1 means use card_data.power
# Turn tracking
var turns_on_field: int = 0
@@ -43,11 +54,18 @@ func _init(data: CardDatabase.CardData = null, owner: int = 0) -> void:
if data:
current_power = data.power
## Get the card's current power (base + modifiers)
## Get the card's current power (base + modifiers + field effects)
func get_power() -> int:
var total = current_power
for mod in power_modifiers:
total += mod
# Add field effect modifiers from AbilitySystem
var tree = Engine.get_main_loop()
if tree and tree.root and tree.root.has_node("AbilitySystem"):
var ability_system = tree.root.get_node("AbilitySystem")
total += ability_system.get_field_power_modifier(self)
return max(0, total)
## Check if this is a Forward
@@ -76,8 +94,18 @@ func dull() -> void:
## Activate this card
func activate() -> void:
# Frozen cards can't activate during Active Phase (but can be activated by effects)
state = Enums.CardState.ACTIVE
## Attempt to activate during Active Phase (respects frozen)
func activate_during_active_phase() -> bool:
if is_frozen:
is_frozen = false # Frozen wears off but card stays dull
return false
state = Enums.CardState.ACTIVE
return true
## Check if this card can attack
func can_attack() -> bool:
if not is_forward():
@@ -86,19 +114,34 @@ func can_attack() -> bool:
return false
if attacked_this_turn:
return false
if has_restriction("CANT_ATTACK"):
return false
# Must have been on field since start of turn (or have Haste)
if turns_on_field < 1 and not has_haste():
return false
return true
## Check if this card must attack (if able)
func must_attack() -> bool:
return has_requirement("MUST_ATTACK")
## Check if this card can block
func can_block() -> bool:
if not is_forward():
return false
if is_dull():
return false
if has_restriction("CANT_BLOCK"):
return false
return true
## Check if this card must block (if able)
func must_block() -> bool:
return has_requirement("MUST_BLOCK")
## Check if this card can use dull abilities
func can_use_dull_ability() -> bool:
# Must have been on field since start of turn (or have Haste)
@@ -109,14 +152,21 @@ func can_use_dull_ability() -> bool:
return false
return true
## Check if card has Haste (from abilities)
## Check if card has Haste
func has_haste() -> bool:
if not card_data:
return false
# Check explicit has_haste field first
if card_data.has_haste:
return true
# Fallback: search ability text for backwards compatibility
for ability in card_data.abilities:
if ability.type == Enums.AbilityType.FIELD:
if "haste" in ability.effect.to_lower():
return true
# Check field-granted keywords
if _has_field_keyword("HASTE"):
return true
return false
## Check if card has Brave (from abilities)
@@ -127,6 +177,9 @@ func has_brave() -> bool:
if ability.type == Enums.AbilityType.FIELD:
if "brave" in ability.effect.to_lower():
return true
# Check field-granted keywords
if _has_field_keyword("BRAVE"):
return true
return false
## Check if card has First Strike
@@ -137,6 +190,17 @@ func has_first_strike() -> bool:
if ability.type == Enums.AbilityType.FIELD:
if "first strike" in ability.effect.to_lower():
return true
# Check field-granted keywords
if _has_field_keyword("FIRST_STRIKE"):
return true
return false
## Check for field-granted keyword from AbilitySystem
func _has_field_keyword(keyword: String) -> bool:
var tree = Engine.get_main_loop()
if tree and tree.root and tree.root.has_node("AbilitySystem"):
var ability_system = tree.root.get_node("AbilitySystem")
return ability_system.has_field_keyword(self, keyword)
return false
## Get primary element
@@ -191,3 +255,288 @@ func get_display_name() -> String:
func _to_string() -> String:
return "[CardInstance: %s (%s)]" % [get_display_name(), instance_id]
# =============================================================================
# KEYWORD MANAGEMENT
# =============================================================================
## Add a temporary keyword
func add_temporary_keyword(keyword: String, duration: String = "END_OF_TURN") -> void:
temporary_keywords[keyword.to_upper()] = duration
## Check if card has a keyword (from card data, temp, or field effects)
func has_keyword(keyword: String) -> bool:
var kw_upper = keyword.to_upper()
# Check temporary keywords
if temporary_keywords.has(kw_upper):
return true
# Check field-granted keywords
if _has_field_keyword(kw_upper):
return true
# Check card's base keywords
if card_data:
match kw_upper:
"HASTE":
return card_data.has_haste
"BRAVE":
for ability in card_data.abilities:
if ability.type == Enums.AbilityType.FIELD and "brave" in ability.effect.to_lower():
return true
"FIRST_STRIKE":
for ability in card_data.abilities:
if ability.type == Enums.AbilityType.FIELD and "first strike" in ability.effect.to_lower():
return true
return false
## Remove all abilities
func remove_all_abilities() -> void:
temporary_abilities.clear()
temporary_keywords.clear()
## Remove a specific ability
func remove_ability(ability_name: String) -> void:
var name_upper = ability_name.to_upper()
temporary_abilities.erase(name_upper)
temporary_keywords.erase(name_upper)
# =============================================================================
# RESTRICTIONS & REQUIREMENTS
# =============================================================================
## Add a restriction (can't attack, can't block, etc.)
func add_restriction(restriction_type: String, duration: String = "END_OF_TURN") -> void:
restrictions[restriction_type.to_upper()] = duration
## Check if card has a restriction
func has_restriction(restriction_type: String) -> bool:
return restrictions.has(restriction_type.to_upper())
## Add a requirement (must attack, must block, etc.)
func add_requirement(requirement_type: String, duration: String = "END_OF_TURN") -> void:
requirements[requirement_type.to_upper()] = duration
## Check if card has a requirement
func has_requirement(requirement_type: String) -> bool:
return requirements.has(requirement_type.to_upper())
# =============================================================================
# PROTECTION
# =============================================================================
## Add protection from damage/effects
func add_protection(protection_type: String, duration: String = "END_OF_TURN") -> void:
protections[protection_type.to_upper()] = duration
## Check if card has protection from something
func has_protection(protection_type: String) -> bool:
var pt_upper = protection_type.to_upper()
# Check local protections
if protections.has(pt_upper):
return true
# Check for ALL protection
if protections.has("ALL"):
return true
# Check field-granted protection
var tree = Engine.get_main_loop()
if tree and tree.root and tree.root.has_node("AbilitySystem"):
var ability_system = tree.root.get_node("AbilitySystem")
return ability_system.has_field_protection(self, protection_type)
return false
# =============================================================================
# FROZEN STATE
# =============================================================================
## Set frozen state
func set_frozen(frozen: bool) -> void:
is_frozen = frozen
## Check if frozen (doesn't activate during Active Phase)
func is_card_frozen() -> bool:
return is_frozen
# =============================================================================
# COUNTERS
# =============================================================================
## Add counters
func add_counters(counter_type: String, amount: int = 1) -> void:
var ct = counter_type.to_upper()
counters[ct] = counters.get(ct, 0) + amount
## Remove counters
func remove_counters(counter_type: String, amount: int = 1) -> void:
var ct = counter_type.to_upper()
counters[ct] = max(0, counters.get(ct, 0) - amount)
if counters[ct] == 0:
counters.erase(ct)
## Get counter count
func get_counter_count(counter_type: String) -> int:
return counters.get(counter_type.to_upper(), 0)
# =============================================================================
# POWER MANIPULATION
# =============================================================================
## Set base power (for swap/transform effects)
func set_base_power(new_power: int) -> void:
base_power_override = new_power
## Get base power (respecting override)
func get_base_power() -> int:
if base_power_override >= 0:
return base_power_override
return card_data.power if card_data else 0
# =============================================================================
# DAMAGE MANIPULATION
# =============================================================================
## Heal damage
func heal_damage(amount: int) -> void:
damage_received = max(0, damage_received - amount)
## Remove all damage
func remove_all_damage() -> void:
damage_received = 0
# =============================================================================
# COPY & TRANSFORM
# =============================================================================
## Copy abilities from another card
func copy_abilities_from(other: CardInstance) -> void:
if other and other.card_data:
for ability in other.card_data.abilities:
temporary_abilities.append(ability)
## Copy stats from another card
func copy_stats_from(other: CardInstance) -> void:
if other:
base_power_override = other.get_base_power()
## Become a copy of another card
func become_copy_of(other: CardInstance) -> void:
if other:
copy_stats_from(other)
copy_abilities_from(other)
## Transform into something else
func transform(into: Dictionary) -> void:
if into.has("power"):
base_power_override = int(into.power)
if into.has("name"):
# Transform name handling would require additional infrastructure
pass
# =============================================================================
# PROPERTY MODIFICATION
# =============================================================================
# Temporary element/job storage
var _temp_element: String = ""
var _temp_element_duration: String = ""
var _temp_job: String = ""
var _temp_job_duration: String = ""
## Set temporary element
func set_temporary_element(element: String, duration: String = "END_OF_TURN") -> void:
_temp_element = element.to_upper()
_temp_element_duration = duration
## Set temporary job
func set_temporary_job(job: String, duration: String = "END_OF_TURN") -> void:
_temp_job = job
_temp_job_duration = duration
## Get current elements (including temporary)
func get_current_elements() -> Array:
if _temp_element != "":
var element = Enums.element_from_string(_temp_element)
return [element]
return get_elements()
## Get current job (including temporary)
func get_current_job() -> String:
if _temp_job != "":
return _temp_job
return card_data.job if card_data else ""
# =============================================================================
# CLEANUP
# =============================================================================
## Reset temporary effects at end of turn (extended)
func end_turn_cleanup() -> void:
power_modifiers.clear()
temporary_abilities.clear()
damage_received = 0
attacked_this_turn = false
# Clear END_OF_TURN duration effects
_clear_duration_effects("END_OF_TURN")
## Clear effects with specific duration
func _clear_duration_effects(duration: String) -> void:
for key in temporary_keywords.keys():
if temporary_keywords[key] == duration:
temporary_keywords.erase(key)
for key in restrictions.keys():
if restrictions[key] == duration:
restrictions.erase(key)
for key in requirements.keys():
if requirements[key] == duration:
requirements.erase(key)
for key in protections.keys():
if protections[key] == duration:
protections.erase(key)
# Clear temporary element/job
if _temp_element_duration == duration:
_temp_element = ""
_temp_element_duration = ""
if _temp_job_duration == duration:
_temp_job = ""
_temp_job_duration = ""