feature updates
This commit is contained in:
681
scripts/game/abilities/FieldEffectManager.gd
Normal file
681
scripts/game/abilities/FieldEffectManager.gd
Normal file
@@ -0,0 +1,681 @@
|
||||
class_name FieldEffectManager
|
||||
extends RefCounted
|
||||
|
||||
## FieldEffectManager - Manages continuous FIELD abilities
|
||||
## Tracks active field effects and calculates their impact on the game state
|
||||
|
||||
# Active field abilities by source card instance_id
|
||||
var _active_abilities: Dictionary = {} # instance_id -> Array of abilities
|
||||
|
||||
|
||||
## Register field abilities when a card enters the field
|
||||
func register_field_abilities(card: CardInstance, abilities: Array) -> void:
|
||||
var field_abilities: Array = []
|
||||
|
||||
for ability in abilities:
|
||||
var parsed = ability.get("parsed", {})
|
||||
if parsed.get("type") == "FIELD":
|
||||
field_abilities.append({
|
||||
"ability": ability,
|
||||
"source": card
|
||||
})
|
||||
|
||||
if not field_abilities.is_empty():
|
||||
_active_abilities[card.instance_id] = field_abilities
|
||||
|
||||
|
||||
## Unregister field abilities when a card leaves the field
|
||||
func unregister_field_abilities(card: CardInstance) -> void:
|
||||
_active_abilities.erase(card.instance_id)
|
||||
|
||||
|
||||
## Get total power modifier for a card from all active field effects
|
||||
func get_power_modifiers(card: CardInstance, game_state) -> int:
|
||||
var total_modifier: int = 0
|
||||
|
||||
for instance_id in _active_abilities:
|
||||
var abilities = _active_abilities[instance_id]
|
||||
for ability_data in abilities:
|
||||
var ability = ability_data.ability
|
||||
var source = ability_data.source
|
||||
var parsed = ability.get("parsed", {})
|
||||
|
||||
for effect in parsed.get("effects", []):
|
||||
if effect.get("type") == "POWER_MOD":
|
||||
if _card_matches_effect_target(card, effect, source, game_state):
|
||||
total_modifier += effect.get("amount", 0)
|
||||
|
||||
return total_modifier
|
||||
|
||||
|
||||
## Check if a card has a keyword granted by field effects
|
||||
func has_keyword(card: CardInstance, keyword: String, game_state) -> bool:
|
||||
var keyword_upper = keyword.to_upper()
|
||||
|
||||
for instance_id in _active_abilities:
|
||||
var abilities = _active_abilities[instance_id]
|
||||
for ability_data in abilities:
|
||||
var ability = ability_data.ability
|
||||
var source = ability_data.source
|
||||
var parsed = ability.get("parsed", {})
|
||||
|
||||
for effect in parsed.get("effects", []):
|
||||
if effect.get("type") == "KEYWORD":
|
||||
var granted_keyword = str(effect.get("keyword", "")).to_upper()
|
||||
if granted_keyword == keyword_upper:
|
||||
if _card_matches_effect_target(card, effect, source, game_state):
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
## Get all keywords granted to a card by field effects
|
||||
func get_granted_keywords(card: CardInstance, game_state) -> Array:
|
||||
var keywords: Array = []
|
||||
|
||||
for instance_id in _active_abilities:
|
||||
var abilities = _active_abilities[instance_id]
|
||||
for ability_data in abilities:
|
||||
var ability = ability_data.ability
|
||||
var source = ability_data.source
|
||||
var parsed = ability.get("parsed", {})
|
||||
|
||||
for effect in parsed.get("effects", []):
|
||||
if effect.get("type") == "KEYWORD":
|
||||
if _card_matches_effect_target(card, effect, source, game_state):
|
||||
var keyword = effect.get("keyword", "")
|
||||
if keyword and keyword not in keywords:
|
||||
keywords.append(keyword)
|
||||
|
||||
return keywords
|
||||
|
||||
|
||||
## Check if a card has protection from something via field effects
|
||||
func has_protection(card: CardInstance, protection_type: String, game_state) -> bool:
|
||||
var protection_upper = protection_type.to_upper()
|
||||
|
||||
for instance_id in _active_abilities:
|
||||
var abilities = _active_abilities[instance_id]
|
||||
for ability_data in abilities:
|
||||
var ability = ability_data.ability
|
||||
var source = ability_data.source
|
||||
var parsed = ability.get("parsed", {})
|
||||
|
||||
for effect in parsed.get("effects", []):
|
||||
if effect.get("type") == "PROTECTION":
|
||||
var from = str(effect.get("from", "")).to_upper()
|
||||
if from == protection_upper or from == "ALL":
|
||||
if _card_matches_effect_target(card, effect, source, game_state):
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
## Check if a card is affected by a damage modifier
|
||||
func get_damage_modifier(card: CardInstance, game_state) -> int:
|
||||
var total_modifier: int = 0
|
||||
|
||||
for instance_id in _active_abilities:
|
||||
var abilities = _active_abilities[instance_id]
|
||||
for ability_data in abilities:
|
||||
var ability = ability_data.ability
|
||||
var source = ability_data.source
|
||||
var parsed = ability.get("parsed", {})
|
||||
|
||||
for effect in parsed.get("effects", []):
|
||||
if effect.get("type") == "DAMAGE_MODIFIER":
|
||||
if _card_matches_effect_target(card, effect, source, game_state):
|
||||
total_modifier += effect.get("amount", 0)
|
||||
|
||||
return total_modifier
|
||||
|
||||
|
||||
## Check if a card matches an effect's target specification
|
||||
func _card_matches_effect_target(
|
||||
card: CardInstance,
|
||||
effect: Dictionary,
|
||||
source: CardInstance,
|
||||
game_state
|
||||
) -> bool:
|
||||
var target = effect.get("target", {})
|
||||
if target.is_empty():
|
||||
# No target specified, assume applies to source only
|
||||
return card == source
|
||||
|
||||
var target_type = str(target.get("type", "")).to_upper()
|
||||
|
||||
# Check owner
|
||||
var owner = str(target.get("owner", "ANY")).to_upper()
|
||||
match owner:
|
||||
"CONTROLLER":
|
||||
if card.controller_index != source.controller_index:
|
||||
return false
|
||||
"OPPONENT":
|
||||
if card.controller_index == source.controller_index:
|
||||
return false
|
||||
# "ANY" matches all
|
||||
|
||||
# Check if applies to self
|
||||
if target_type == "SELF":
|
||||
return card == source
|
||||
|
||||
# Check if applies to all matching
|
||||
if target_type == "ALL":
|
||||
return _matches_filter(card, target.get("filter", {}), source)
|
||||
|
||||
# Default check filter
|
||||
return _matches_filter(card, target.get("filter", {}), source)
|
||||
|
||||
|
||||
## Check if a card matches a filter (duplicated from TargetSelector for independence)
|
||||
func _matches_filter(
|
||||
card: CardInstance,
|
||||
filter: Dictionary,
|
||||
source: CardInstance
|
||||
) -> bool:
|
||||
if filter.is_empty():
|
||||
return true
|
||||
|
||||
# Card type filter
|
||||
if filter.has("card_type"):
|
||||
var type_str = str(filter.card_type).to_upper()
|
||||
match type_str:
|
||||
"FORWARD":
|
||||
if not card.is_forward():
|
||||
return false
|
||||
"BACKUP":
|
||||
if not card.is_backup():
|
||||
return false
|
||||
"SUMMON":
|
||||
if not card.is_summon():
|
||||
return false
|
||||
"CHARACTER":
|
||||
if not (card.is_forward() or card.is_backup()):
|
||||
return false
|
||||
|
||||
# Element filter
|
||||
if filter.has("element"):
|
||||
var element_str = str(filter.element).to_upper()
|
||||
var element = Enums.element_from_string(element_str)
|
||||
if element not in card.get_elements():
|
||||
return false
|
||||
|
||||
# Cost filters
|
||||
if filter.has("cost_min") and card.card_data.cost < int(filter.cost_min):
|
||||
return false
|
||||
if filter.has("cost_max") and card.card_data.cost > int(filter.cost_max):
|
||||
return false
|
||||
if filter.has("cost") and card.card_data.cost != int(filter.cost):
|
||||
return false
|
||||
|
||||
# Power filters
|
||||
if filter.has("power_min") and card.get_power() < int(filter.power_min):
|
||||
return false
|
||||
if filter.has("power_max") and card.get_power() > int(filter.power_max):
|
||||
return false
|
||||
|
||||
# Name filter
|
||||
if filter.has("name") and card.card_data.name != filter.name:
|
||||
return false
|
||||
|
||||
# Category filter
|
||||
if filter.has("category") and card.card_data.category != filter.category:
|
||||
return false
|
||||
|
||||
# Job filter
|
||||
if filter.has("job") and card.card_data.job != filter.job:
|
||||
return false
|
||||
|
||||
# Exclude self
|
||||
if filter.get("exclude_self", false) and card == source:
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
|
||||
## Get count of active field abilities
|
||||
func get_active_ability_count() -> int:
|
||||
var count = 0
|
||||
for instance_id in _active_abilities:
|
||||
count += _active_abilities[instance_id].size()
|
||||
return count
|
||||
|
||||
|
||||
## Clear all active abilities (for game reset)
|
||||
func clear_all() -> void:
|
||||
_active_abilities.clear()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# BLOCK IMMUNITY CHECKS
|
||||
# =============================================================================
|
||||
|
||||
## Check if a card has block immunity (can't be blocked by certain cards)
|
||||
func has_block_immunity(card: CardInstance, potential_blocker: CardInstance, game_state) -> bool:
|
||||
for instance_id in _active_abilities:
|
||||
var abilities = _active_abilities[instance_id]
|
||||
for ability_data in abilities:
|
||||
var source = ability_data.source
|
||||
if source != card:
|
||||
continue
|
||||
|
||||
var ability = ability_data.ability
|
||||
var parsed = ability.get("parsed", {})
|
||||
|
||||
for effect in parsed.get("effects", []):
|
||||
if effect.get("type") == "BLOCK_IMMUNITY":
|
||||
var condition = effect.get("condition", {})
|
||||
if _blocker_matches_immunity_condition(potential_blocker, condition, card):
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
## Check if blocker matches the immunity condition
|
||||
func _blocker_matches_immunity_condition(
|
||||
blocker: CardInstance,
|
||||
condition: Dictionary,
|
||||
attacker: CardInstance
|
||||
) -> bool:
|
||||
if condition.is_empty():
|
||||
return true # Unconditional block immunity
|
||||
|
||||
var comparison = condition.get("comparison", "")
|
||||
var attribute = condition.get("attribute", "")
|
||||
var value = condition.get("value", 0)
|
||||
var compare_to = condition.get("compare_to", "")
|
||||
|
||||
var blocker_value = 0
|
||||
match attribute:
|
||||
"cost":
|
||||
blocker_value = blocker.card_data.cost if blocker.card_data else 0
|
||||
"power":
|
||||
blocker_value = blocker.get_power()
|
||||
|
||||
var compare_value = value
|
||||
if compare_to == "SELF_POWER":
|
||||
compare_value = attacker.get_power()
|
||||
|
||||
match comparison:
|
||||
"GTE":
|
||||
return blocker_value >= compare_value
|
||||
"GT":
|
||||
return blocker_value > compare_value
|
||||
"LTE":
|
||||
return blocker_value <= compare_value
|
||||
"LT":
|
||||
return blocker_value < compare_value
|
||||
"EQ":
|
||||
return blocker_value == compare_value
|
||||
|
||||
return false
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# ATTACK RESTRICTION CHECKS
|
||||
# =============================================================================
|
||||
|
||||
## Check if a card has attack restrictions
|
||||
func has_attack_restriction(card: CardInstance, game_state) -> bool:
|
||||
for instance_id in _active_abilities:
|
||||
var abilities = _active_abilities[instance_id]
|
||||
for ability_data in abilities:
|
||||
var ability = ability_data.ability
|
||||
var source = ability_data.source
|
||||
var parsed = ability.get("parsed", {})
|
||||
|
||||
for effect in parsed.get("effects", []):
|
||||
if effect.get("type") == "RESTRICTION":
|
||||
var restriction = effect.get("restriction", "")
|
||||
if restriction in ["CANNOT_ATTACK", "CANNOT_ATTACK_OR_BLOCK"]:
|
||||
if _card_matches_effect_target(card, effect, source, game_state):
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
## Check if a card has block restrictions
|
||||
func has_block_restriction(card: CardInstance, game_state) -> bool:
|
||||
for instance_id in _active_abilities:
|
||||
var abilities = _active_abilities[instance_id]
|
||||
for ability_data in abilities:
|
||||
var ability = ability_data.ability
|
||||
var source = ability_data.source
|
||||
var parsed = ability.get("parsed", {})
|
||||
|
||||
for effect in parsed.get("effects", []):
|
||||
if effect.get("type") == "RESTRICTION":
|
||||
var restriction = effect.get("restriction", "")
|
||||
if restriction in ["CANNOT_BLOCK", "CANNOT_ATTACK_OR_BLOCK"]:
|
||||
if _card_matches_effect_target(card, effect, source, game_state):
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# TAUNT CHECKS (Must be targeted if possible)
|
||||
# =============================================================================
|
||||
|
||||
## Get cards that must be targeted by opponent's abilities if possible
|
||||
func get_taunt_targets(player_index: int, game_state) -> Array:
|
||||
var taunt_cards: Array = []
|
||||
|
||||
for instance_id in _active_abilities:
|
||||
var abilities = _active_abilities[instance_id]
|
||||
for ability_data in abilities:
|
||||
var ability = ability_data.ability
|
||||
var source = ability_data.source
|
||||
var parsed = ability.get("parsed", {})
|
||||
|
||||
for effect in parsed.get("effects", []):
|
||||
if effect.get("type") == "TAUNT":
|
||||
var target = effect.get("target", {})
|
||||
if target.get("type") == "SELF":
|
||||
if source.controller_index == player_index:
|
||||
taunt_cards.append(source)
|
||||
|
||||
return taunt_cards
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# COST MODIFICATION
|
||||
# =============================================================================
|
||||
|
||||
## Get cost modifier for playing a card
|
||||
func get_cost_modifier(
|
||||
card_to_play: CardInstance,
|
||||
playing_player: int,
|
||||
game_state
|
||||
) -> int:
|
||||
var total_modifier = 0
|
||||
|
||||
for instance_id in _active_abilities:
|
||||
var abilities = _active_abilities[instance_id]
|
||||
for ability_data in abilities:
|
||||
var ability = ability_data.ability
|
||||
var source = ability_data.source
|
||||
var parsed = ability.get("parsed", {})
|
||||
|
||||
for effect in parsed.get("effects", []):
|
||||
var effect_type = effect.get("type", "")
|
||||
|
||||
if effect_type == "COST_REDUCTION":
|
||||
if _cost_effect_applies(effect, card_to_play, playing_player, source, game_state):
|
||||
total_modifier -= effect.get("amount", 0)
|
||||
|
||||
elif effect_type == "COST_REDUCTION_SCALING":
|
||||
if _cost_effect_applies(effect, card_to_play, playing_player, source, game_state):
|
||||
var reduction = _calculate_scaling_cost_reduction(effect, source, game_state)
|
||||
total_modifier -= reduction
|
||||
|
||||
elif effect_type == "COST_INCREASE":
|
||||
if _cost_effect_applies(effect, card_to_play, playing_player, source, game_state):
|
||||
total_modifier += effect.get("amount", 0)
|
||||
|
||||
return total_modifier
|
||||
|
||||
|
||||
## Check if a cost modification effect applies to a card being played
|
||||
func _cost_effect_applies(
|
||||
effect: Dictionary,
|
||||
card: CardInstance,
|
||||
player: int,
|
||||
source: CardInstance,
|
||||
game_state
|
||||
) -> bool:
|
||||
var for_player = effect.get("for_player", "CONTROLLER")
|
||||
|
||||
# Check if effect applies to this player
|
||||
match for_player:
|
||||
"CONTROLLER":
|
||||
if player != source.controller_index:
|
||||
return false
|
||||
"OPPONENT":
|
||||
if player == source.controller_index:
|
||||
return false
|
||||
|
||||
# Check card filter
|
||||
var card_filter = effect.get("card_filter", "")
|
||||
if card_filter and not _card_matches_name_filter(card, card_filter):
|
||||
return false
|
||||
|
||||
# Check condition
|
||||
var condition = effect.get("condition", {})
|
||||
if not condition.is_empty():
|
||||
if not _cost_condition_met(condition, source, game_state):
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
|
||||
## Check if a card name matches a filter
|
||||
func _card_matches_name_filter(card: CardInstance, filter_text: String) -> bool:
|
||||
if not card or not card.card_data:
|
||||
return false
|
||||
|
||||
var filter_lower = filter_text.to_lower()
|
||||
var card_name = card.card_data.name.to_lower()
|
||||
|
||||
# Direct name match
|
||||
if card_name in filter_lower or filter_lower in card_name:
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
## Check if a cost condition is met
|
||||
func _cost_condition_met(condition: Dictionary, source: CardInstance, game_state) -> bool:
|
||||
if condition.has("control_card_name"):
|
||||
var name_to_find = condition.control_card_name.to_lower()
|
||||
var player = game_state.get_player(source.controller_index)
|
||||
if player:
|
||||
for card in player.field_forwards.get_cards():
|
||||
if name_to_find in card.card_data.name.to_lower():
|
||||
return true
|
||||
for card in player.field_backups.get_cards():
|
||||
if name_to_find in card.card_data.name.to_lower():
|
||||
return true
|
||||
return false
|
||||
|
||||
if condition.has("control_category"):
|
||||
var category = condition.control_category.to_lower()
|
||||
var player = game_state.get_player(source.controller_index)
|
||||
if player:
|
||||
for card in player.field_forwards.get_cards():
|
||||
if category in card.card_data.category.to_lower():
|
||||
return true
|
||||
for card in player.field_backups.get_cards():
|
||||
if category in card.card_data.category.to_lower():
|
||||
return true
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# SCALING COST REDUCTION
|
||||
# =============================================================================
|
||||
|
||||
## Calculate cost reduction for a COST_REDUCTION_SCALING effect
|
||||
func _calculate_scaling_cost_reduction(
|
||||
effect: Dictionary,
|
||||
source: CardInstance,
|
||||
game_state
|
||||
) -> int:
|
||||
var reduction_per = effect.get("reduction_per", 1)
|
||||
var scale_by = str(effect.get("scale_by", "")).to_upper()
|
||||
var scale_filter = effect.get("scale_filter", {})
|
||||
|
||||
# Get scale value using similar logic to EffectResolver
|
||||
var scale_value = _get_scale_value(scale_by, source, game_state, scale_filter)
|
||||
|
||||
return scale_value * reduction_per
|
||||
|
||||
|
||||
## Get scale value based on scale_by type (with optional filter)
|
||||
## Mirrors the logic in EffectResolver for consistency
|
||||
func _get_scale_value(
|
||||
scale_by: String,
|
||||
source: CardInstance,
|
||||
game_state,
|
||||
scale_filter: Dictionary = {}
|
||||
) -> int:
|
||||
if not source or not game_state:
|
||||
return 0
|
||||
|
||||
var player_index = source.controller_index
|
||||
var player = game_state.get_player(player_index)
|
||||
if not player:
|
||||
return 0
|
||||
|
||||
# Determine owner from filter (default to CONTROLLER)
|
||||
var owner = scale_filter.get("owner", "CONTROLLER").to_upper() if scale_filter else "CONTROLLER"
|
||||
|
||||
# Get cards based on scale_by and owner
|
||||
var cards_to_count: Array = []
|
||||
|
||||
match scale_by:
|
||||
"DAMAGE_RECEIVED":
|
||||
# Special case - not card-based
|
||||
return _get_damage_for_owner(owner, player_index, game_state)
|
||||
"FORWARDS_CONTROLLED", "FORWARDS":
|
||||
cards_to_count = _get_forwards_for_owner(owner, player_index, game_state)
|
||||
"BACKUPS_CONTROLLED", "BACKUPS":
|
||||
cards_to_count = _get_backups_for_owner(owner, player_index, game_state)
|
||||
"FIELD_CARDS_CONTROLLED", "FIELD_CARDS":
|
||||
cards_to_count = _get_field_cards_for_owner(owner, player_index, game_state)
|
||||
"CARDS_IN_HAND":
|
||||
cards_to_count = _get_hand_for_owner(owner, player_index, game_state)
|
||||
"CARDS_IN_BREAK_ZONE":
|
||||
cards_to_count = _get_break_zone_for_owner(owner, player_index, game_state)
|
||||
"OPPONENT_FORWARDS":
|
||||
cards_to_count = _get_forwards_for_owner("OPPONENT", player_index, game_state)
|
||||
"OPPONENT_BACKUPS":
|
||||
cards_to_count = _get_backups_for_owner("OPPONENT", player_index, game_state)
|
||||
_:
|
||||
push_warning("FieldEffectManager: Unknown scale_by type: " + scale_by)
|
||||
return 0
|
||||
|
||||
# If no filter, just return count
|
||||
if not scale_filter or scale_filter.is_empty() or (scale_filter.size() == 1 and scale_filter.has("owner")):
|
||||
return cards_to_count.size()
|
||||
|
||||
# Apply filter and count matching cards using CardFilter utility
|
||||
return CardFilter.count_matching(cards_to_count, scale_filter)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# OWNER-BASED ACCESS HELPERS FOR SCALING
|
||||
# =============================================================================
|
||||
|
||||
func _get_forwards_for_owner(owner: String, player_index: int, game_state) -> Array:
|
||||
match owner.to_upper():
|
||||
"CONTROLLER":
|
||||
var player = game_state.get_player(player_index)
|
||||
return player.field_forwards.get_cards() if player and player.field_forwards else []
|
||||
"OPPONENT":
|
||||
var opponent = game_state.get_player(1 - player_index)
|
||||
return opponent.field_forwards.get_cards() if opponent and opponent.field_forwards else []
|
||||
_:
|
||||
var all_cards = []
|
||||
for p in game_state.players:
|
||||
if p and p.field_forwards:
|
||||
all_cards.append_array(p.field_forwards.get_cards())
|
||||
return all_cards
|
||||
|
||||
|
||||
func _get_backups_for_owner(owner: String, player_index: int, game_state) -> Array:
|
||||
match owner.to_upper():
|
||||
"CONTROLLER":
|
||||
var player = game_state.get_player(player_index)
|
||||
return player.field_backups.get_cards() if player and player.field_backups else []
|
||||
"OPPONENT":
|
||||
var opponent = game_state.get_player(1 - player_index)
|
||||
return opponent.field_backups.get_cards() if opponent and opponent.field_backups else []
|
||||
_:
|
||||
var all_cards = []
|
||||
for p in game_state.players:
|
||||
if p and p.field_backups:
|
||||
all_cards.append_array(p.field_backups.get_cards())
|
||||
return all_cards
|
||||
|
||||
|
||||
func _get_field_cards_for_owner(owner: String, player_index: int, game_state) -> Array:
|
||||
var cards = []
|
||||
cards.append_array(_get_forwards_for_owner(owner, player_index, game_state))
|
||||
cards.append_array(_get_backups_for_owner(owner, player_index, game_state))
|
||||
return cards
|
||||
|
||||
|
||||
func _get_hand_for_owner(owner: String, player_index: int, game_state) -> Array:
|
||||
match owner.to_upper():
|
||||
"CONTROLLER":
|
||||
var player = game_state.get_player(player_index)
|
||||
return player.hand.get_cards() if player and player.hand else []
|
||||
"OPPONENT":
|
||||
var opponent = game_state.get_player(1 - player_index)
|
||||
return opponent.hand.get_cards() if opponent and opponent.hand else []
|
||||
_:
|
||||
var all_cards = []
|
||||
for p in game_state.players:
|
||||
if p and p.hand:
|
||||
all_cards.append_array(p.hand.get_cards())
|
||||
return all_cards
|
||||
|
||||
|
||||
func _get_break_zone_for_owner(owner: String, player_index: int, game_state) -> Array:
|
||||
match owner.to_upper():
|
||||
"CONTROLLER":
|
||||
var player = game_state.get_player(player_index)
|
||||
return player.break_zone.get_cards() if player and player.break_zone else []
|
||||
"OPPONENT":
|
||||
var opponent = game_state.get_player(1 - player_index)
|
||||
return opponent.break_zone.get_cards() if opponent and opponent.break_zone else []
|
||||
_:
|
||||
var all_cards = []
|
||||
for p in game_state.players:
|
||||
if p and p.break_zone:
|
||||
all_cards.append_array(p.break_zone.get_cards())
|
||||
return all_cards
|
||||
|
||||
|
||||
func _get_damage_for_owner(owner: String, player_index: int, game_state) -> int:
|
||||
match owner.to_upper():
|
||||
"CONTROLLER":
|
||||
var player = game_state.get_player(player_index)
|
||||
return player.damage if player and "damage" in player else 0
|
||||
"OPPONENT":
|
||||
var opponent = game_state.get_player(1 - player_index)
|
||||
return opponent.damage if opponent and "damage" in opponent else 0
|
||||
_:
|
||||
var total = 0
|
||||
for p in game_state.players:
|
||||
if p and "damage" in p:
|
||||
total += p.damage
|
||||
return total
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# MULTI-ATTACK CHECKS
|
||||
# =============================================================================
|
||||
|
||||
## Get maximum attacks allowed for a card this turn
|
||||
func get_max_attacks(card: CardInstance, game_state) -> int:
|
||||
var max_attacks = 1 # Default is 1 attack per turn
|
||||
|
||||
for instance_id in _active_abilities:
|
||||
var abilities = _active_abilities[instance_id]
|
||||
for ability_data in abilities:
|
||||
var ability = ability_data.ability
|
||||
var source = ability_data.source
|
||||
var parsed = ability.get("parsed", {})
|
||||
|
||||
for effect in parsed.get("effects", []):
|
||||
if effect.get("type") == "MULTI_ATTACK":
|
||||
if _card_matches_effect_target(card, effect, source, game_state):
|
||||
var attack_count = effect.get("attack_count", 1)
|
||||
if attack_count > max_attacks:
|
||||
max_attacks = attack_count
|
||||
|
||||
return max_attacks
|
||||
Reference in New Issue
Block a user