Files
FFCardGame/scripts/game/abilities/ConditionChecker.gd
2026-02-02 16:28:53 -05:00

511 lines
15 KiB
GDScript

class_name ConditionChecker
extends RefCounted
## Centralized condition evaluation for all ability types
## Handles conditions like "If you control X", "If you have received Y damage", etc.
## Main evaluation entry point
## Returns true if condition is met, false otherwise
func evaluate(condition: Dictionary, context: Dictionary) -> bool:
if condition.is_empty():
return true # Empty condition = unconditional
var condition_type = condition.get("type", "")
match condition_type:
"CONTROL_CARD":
return _check_control_card(condition, context)
"CONTROL_COUNT":
return _check_control_count(condition, context)
"DAMAGE_RECEIVED":
return _check_damage_received(condition, context)
"BREAK_ZONE_COUNT":
return _check_break_zone_count(condition, context)
"CARD_IN_ZONE":
return _check_card_in_zone(condition, context)
"FORWARD_STATE":
return _check_forward_state(condition, context)
"COST_COMPARISON":
return _check_cost_comparison(condition, context)
"POWER_COMPARISON":
return _check_power_comparison(condition, context)
"ELEMENT_MATCH":
return _check_element_match(condition, context)
"CARD_TYPE_MATCH":
return _check_card_type_match(condition, context)
"JOB_MATCH":
return _check_job_match(condition, context)
"CATEGORY_MATCH":
return _check_category_match(condition, context)
"AND":
return _check_and(condition, context)
"OR":
return _check_or(condition, context)
"NOT":
return _check_not(condition, context)
_:
push_warning("ConditionChecker: Unknown condition type '%s'" % condition_type)
return false
# =============================================================================
# CONTROL CONDITIONS
# =============================================================================
func _check_control_card(condition: Dictionary, context: Dictionary) -> bool:
var card_name = condition.get("card_name", "")
var player = context.get("player_id", 0)
var game_state = context.get("game_state")
if not game_state:
return false
# Check all field cards for the player
var field_cards = _get_field_cards(game_state, player)
for card in field_cards:
if card and card.card_data and card.card_data.name == card_name:
return true
return false
func _check_control_count(condition: Dictionary, context: Dictionary) -> bool:
var card_type = condition.get("card_type", "")
var element = condition.get("element", "")
var job = condition.get("job", "")
var category = condition.get("category", "")
var comparison = condition.get("comparison", "GTE")
var value = condition.get("value", 1)
var player = context.get("player_id", 0)
var game_state = context.get("game_state")
if not game_state:
return false
var count = 0
var field_cards = _get_field_cards(game_state, player)
for card in field_cards:
if not card or not card.card_data:
continue
var matches = true
# Check card type filter
if card_type != "" and not _matches_card_type(card, card_type):
matches = false
# Check element filter
if element != "" and not _matches_element(card, element):
matches = false
# Check job filter
if job != "" and not _matches_job(card, job):
matches = false
# Check category filter
if category != "" and not _matches_category(card, category):
matches = false
if matches:
count += 1
return _compare(count, comparison, value)
# =============================================================================
# DAMAGE CONDITIONS
# =============================================================================
func _check_damage_received(condition: Dictionary, context: Dictionary) -> bool:
var comparison = condition.get("comparison", "GTE")
var value = condition.get("value", 1)
var player = context.get("player_id", 0)
var game_state = context.get("game_state")
if not game_state:
return false
var damage = _get_player_damage(game_state, player)
return _compare(damage, comparison, value)
# =============================================================================
# ZONE CONDITIONS
# =============================================================================
func _check_break_zone_count(condition: Dictionary, context: Dictionary) -> bool:
var card_name = condition.get("card_name", "")
var card_names: Array = condition.get("card_names", [])
if card_name != "" and card_name not in card_names:
card_names.append(card_name)
var comparison = condition.get("comparison", "GTE")
var value = condition.get("value", 1)
var player = context.get("player_id", 0)
var game_state = context.get("game_state")
if not game_state:
return false
var count = 0
var break_zone = _get_break_zone(game_state, player)
for card in break_zone:
if not card or not card.card_data:
continue
# If no specific names, count all
if card_names.is_empty():
count += 1
elif card.card_data.name in card_names:
count += 1
return _compare(count, comparison, value)
func _check_card_in_zone(condition: Dictionary, context: Dictionary) -> bool:
var zone = condition.get("zone", "") # "HAND", "DECK", "BREAK_ZONE", "REMOVED"
var card_name = condition.get("card_name", "")
var card_type = condition.get("card_type", "")
var player = context.get("player_id", 0)
var game_state = context.get("game_state")
if not game_state:
return false
var zone_cards: Array = []
match zone:
"HAND":
zone_cards = _get_hand(game_state, player)
"DECK":
zone_cards = _get_deck(game_state, player)
"BREAK_ZONE":
zone_cards = _get_break_zone(game_state, player)
"REMOVED":
zone_cards = _get_removed_zone(game_state, player)
"FIELD":
zone_cards = _get_field_cards(game_state, player)
for card in zone_cards:
if not card or not card.card_data:
continue
var matches = true
if card_name != "" and card.card_data.name != card_name:
matches = false
if card_type != "" and not _matches_card_type(card, card_type):
matches = false
if matches:
return true
return false
# =============================================================================
# CARD STATE CONDITIONS
# =============================================================================
func _check_forward_state(condition: Dictionary, context: Dictionary) -> bool:
var state = condition.get("state", "") # "DULL", "ACTIVE", "DAMAGED"
var check_self = condition.get("check_self", false)
var target = context.get("target_card") if not check_self else context.get("source_card")
if not target:
return false
match state:
"DULL":
return target.is_dull if target.has_method("get") or "is_dull" in target else false
"ACTIVE":
return not target.is_dull if "is_dull" in target else false
"DAMAGED":
if "current_power" in target and target.card_data:
return target.current_power < target.card_data.power
"FROZEN":
return target.is_frozen if "is_frozen" in target else false
return false
func _check_cost_comparison(condition: Dictionary, context: Dictionary) -> bool:
var comparison = condition.get("comparison", "LTE")
var value = condition.get("value", 0)
var compare_to = condition.get("compare_to", "") # "SELF_COST", "VALUE", or empty for value
var target = context.get("target_card")
var source = context.get("source_card")
if not target or not target.card_data:
return false
var target_cost = target.card_data.cost
var compare_value = value
if compare_to == "SELF_COST" and source and source.card_data:
compare_value = source.card_data.cost
return _compare(target_cost, comparison, compare_value)
func _check_power_comparison(condition: Dictionary, context: Dictionary) -> bool:
var comparison = condition.get("comparison", "LTE")
var value = condition.get("value", 0)
var compare_to = condition.get("compare_to", "") # "SELF_POWER", "VALUE"
var target = context.get("target_card")
var source = context.get("source_card")
if not target:
return false
var target_power = target.current_power if "current_power" in target else 0
var compare_value = value
if compare_to == "SELF_POWER" and source:
compare_value = source.current_power if "current_power" in source else 0
return _compare(target_power, comparison, compare_value)
# =============================================================================
# CARD ATTRIBUTE CONDITIONS
# =============================================================================
func _check_element_match(condition: Dictionary, context: Dictionary) -> bool:
var element = condition.get("element", "")
var check_self = condition.get("check_self", false)
var target = context.get("target_card") if not check_self else context.get("source_card")
if not target or not target.card_data:
return false
return _matches_element(target, element)
func _check_card_type_match(condition: Dictionary, context: Dictionary) -> bool:
var card_type = condition.get("card_type", "")
var check_self = condition.get("check_self", false)
var target = context.get("target_card") if not check_self else context.get("source_card")
if not target:
return false
return _matches_card_type(target, card_type)
func _check_job_match(condition: Dictionary, context: Dictionary) -> bool:
var job = condition.get("job", "")
var check_self = condition.get("check_self", false)
var target = context.get("target_card") if not check_self else context.get("source_card")
if not target or not target.card_data:
return false
return _matches_job(target, job)
func _check_category_match(condition: Dictionary, context: Dictionary) -> bool:
var category = condition.get("category", "")
var check_self = condition.get("check_self", false)
var target = context.get("target_card") if not check_self else context.get("source_card")
if not target or not target.card_data:
return false
return _matches_category(target, category)
# =============================================================================
# LOGICAL OPERATORS
# =============================================================================
func _check_and(condition: Dictionary, context: Dictionary) -> bool:
var conditions: Array = condition.get("conditions", [])
for sub_condition in conditions:
if not evaluate(sub_condition, context):
return false
return true
func _check_or(condition: Dictionary, context: Dictionary) -> bool:
var conditions: Array = condition.get("conditions", [])
for sub_condition in conditions:
if evaluate(sub_condition, context):
return true
return false
func _check_not(condition: Dictionary, context: Dictionary) -> bool:
var inner: Dictionary = condition.get("condition", {})
return not evaluate(inner, context)
# =============================================================================
# HELPER FUNCTIONS
# =============================================================================
func _compare(actual: int, comparison: String, expected: int) -> bool:
match comparison:
"EQ":
return actual == expected
"NEQ":
return actual != expected
"GT":
return actual > expected
"GTE":
return actual >= expected
"LT":
return actual < expected
"LTE":
return actual <= expected
return false
func _matches_card_type(card, card_type: String) -> bool:
if not card or not card.card_data:
return false
var type_upper = card_type.to_upper()
var card_type_value = card.card_data.type
# Handle string or enum type
if card_type_value is String:
return card_type_value.to_upper() == type_upper
# Handle Enums.CardType enum
match type_upper:
"FORWARD":
return card_type_value == Enums.CardType.FORWARD
"BACKUP":
return card_type_value == Enums.CardType.BACKUP
"SUMMON":
return card_type_value == Enums.CardType.SUMMON
"MONSTER":
return card_type_value == Enums.CardType.MONSTER
return false
func _matches_element(card, element: String) -> bool:
if not card or not card.card_data:
return false
var element_upper = element.to_upper()
var card_element = card.card_data.element
if card_element is String:
return card_element.to_upper() == element_upper
# Handle Enums.Element enum
match element_upper:
"FIRE":
return card_element == Enums.Element.FIRE
"ICE":
return card_element == Enums.Element.ICE
"WIND":
return card_element == Enums.Element.WIND
"EARTH":
return card_element == Enums.Element.EARTH
"LIGHTNING":
return card_element == Enums.Element.LIGHTNING
"WATER":
return card_element == Enums.Element.WATER
"LIGHT":
return card_element == Enums.Element.LIGHT
"DARK":
return card_element == Enums.Element.DARK
return false
func _matches_job(card, job: String) -> bool:
if not card or not card.card_data:
return false
var card_job = card.card_data.get("job", "")
if card_job is String:
return card_job.to_lower() == job.to_lower()
return false
func _matches_category(card, category: String) -> bool:
if not card or not card.card_data:
return false
var card_categories = card.card_data.get("categories", [])
if card_categories is Array:
for cat in card_categories:
if cat is String and cat.to_lower() == category.to_lower():
return true
return false
# =============================================================================
# GAME STATE ACCESSORS
# These abstract away the game state interface for flexibility
# =============================================================================
func _get_field_cards(game_state, player: int) -> Array:
if game_state.has_method("get_field_cards"):
return game_state.get_field_cards(player)
elif game_state.has_method("get_player_field"):
return game_state.get_player_field(player)
elif "players" in game_state and player < game_state.players.size():
var p = game_state.players[player]
if "field" in p:
return p.field
return []
func _get_player_damage(game_state, player: int) -> int:
if game_state.has_method("get_player_damage"):
return game_state.get_player_damage(player)
elif "players" in game_state and player < game_state.players.size():
var p = game_state.players[player]
if "damage" in p:
return p.damage
return 0
func _get_break_zone(game_state, player: int) -> Array:
if game_state.has_method("get_break_zone"):
return game_state.get_break_zone(player)
elif "players" in game_state and player < game_state.players.size():
var p = game_state.players[player]
if "break_zone" in p:
return p.break_zone
return []
func _get_hand(game_state, player: int) -> Array:
if game_state.has_method("get_hand"):
return game_state.get_hand(player)
elif "players" in game_state and player < game_state.players.size():
var p = game_state.players[player]
if "hand" in p:
return p.hand
return []
func _get_deck(game_state, player: int) -> Array:
if game_state.has_method("get_deck"):
return game_state.get_deck(player)
elif "players" in game_state and player < game_state.players.size():
var p = game_state.players[player]
if "deck" in p:
return p.deck
return []
func _get_removed_zone(game_state, player: int) -> Array:
if game_state.has_method("get_removed_zone"):
return game_state.get_removed_zone(player)
elif "players" in game_state and player < game_state.players.size():
var p = game_state.players[player]
if "removed_zone" in p:
return p.removed_zone
return []