feature updates
This commit is contained in:
510
scripts/game/abilities/ConditionChecker.gd
Normal file
510
scripts/game/abilities/ConditionChecker.gd
Normal file
@@ -0,0 +1,510 @@
|
||||
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 []
|
||||
Reference in New Issue
Block a user