234 lines
6.2 KiB
GDScript
234 lines
6.2 KiB
GDScript
class_name TriggerMatcher
|
|
extends RefCounted
|
|
|
|
## TriggerMatcher - Matches game events to ability triggers
|
|
## Scans all cards on field for abilities that trigger from the given event
|
|
|
|
## Reference to ConditionChecker for evaluating trigger conditions
|
|
var condition_checker: ConditionChecker = null
|
|
|
|
|
|
## Find all abilities that should trigger for a given event
|
|
func find_triggered_abilities(
|
|
event_type: String,
|
|
event_data: Dictionary,
|
|
game_state,
|
|
all_abilities: Dictionary
|
|
) -> Array:
|
|
var triggered = []
|
|
|
|
# Check abilities on all cards in play
|
|
for player in game_state.players:
|
|
# Check forwards
|
|
for card in player.field_forwards.get_cards():
|
|
var card_abilities = all_abilities.get(card.card_data.id, [])
|
|
triggered.append_array(_check_card_abilities(card, card_abilities, event_type, event_data, game_state))
|
|
|
|
# Check backups
|
|
for card in player.field_backups.get_cards():
|
|
var card_abilities = all_abilities.get(card.card_data.id, [])
|
|
triggered.append_array(_check_card_abilities(card, card_abilities, event_type, event_data, game_state))
|
|
|
|
return triggered
|
|
|
|
|
|
## Check all abilities on a card for triggers
|
|
func _check_card_abilities(
|
|
card: CardInstance,
|
|
abilities: Array,
|
|
event_type: String,
|
|
event_data: Dictionary,
|
|
game_state
|
|
) -> Array:
|
|
var triggered = []
|
|
|
|
for ability in abilities:
|
|
if _matches_trigger(ability, event_type, event_data, card, game_state):
|
|
triggered.append({
|
|
"source": card,
|
|
"ability": ability,
|
|
"event_data": event_data
|
|
})
|
|
|
|
return triggered
|
|
|
|
|
|
## Check if an ability's trigger matches the event
|
|
func _matches_trigger(
|
|
ability: Dictionary,
|
|
event_type: String,
|
|
event_data: Dictionary,
|
|
source_card: CardInstance,
|
|
game_state
|
|
) -> bool:
|
|
var parsed = ability.get("parsed", {})
|
|
if parsed.is_empty():
|
|
return false
|
|
|
|
# Only AUTO abilities have triggers
|
|
if parsed.get("type") != "AUTO":
|
|
return false
|
|
|
|
var trigger = parsed.get("trigger", {})
|
|
if trigger.is_empty():
|
|
return false
|
|
|
|
# Check event type matches
|
|
var trigger_event = trigger.get("event", "")
|
|
if not _event_matches(trigger_event, event_type):
|
|
return false
|
|
|
|
# Check source filter
|
|
var trigger_source = trigger.get("source", "ANY")
|
|
if not _source_matches(trigger_source, event_data, source_card, game_state):
|
|
return false
|
|
|
|
# Check additional trigger filters
|
|
if trigger.has("source_filter"):
|
|
var filter = trigger.source_filter
|
|
var event_card = event_data.get("card")
|
|
if event_card and not _matches_card_filter(event_card, filter):
|
|
return false
|
|
|
|
# Check trigger condition (if present)
|
|
var trigger_condition = trigger.get("condition", {})
|
|
if not trigger_condition.is_empty() and condition_checker:
|
|
var context = {
|
|
"source_card": source_card,
|
|
"target_card": event_data.get("card"),
|
|
"game_state": game_state,
|
|
"player_id": source_card.controller_index if source_card else 0,
|
|
"event_data": event_data
|
|
}
|
|
if not condition_checker.evaluate(trigger_condition, context):
|
|
return false
|
|
|
|
return true
|
|
|
|
|
|
## Check if event type matches trigger event
|
|
func _event_matches(trigger_event: String, actual_event: String) -> bool:
|
|
# Direct match
|
|
if trigger_event == actual_event:
|
|
return true
|
|
|
|
# Handle variations
|
|
match trigger_event:
|
|
"ENTERS_FIELD":
|
|
return actual_event in ["ENTERS_FIELD", "CARD_PLAYED"]
|
|
"LEAVES_FIELD":
|
|
return actual_event in ["LEAVES_FIELD", "FORWARD_BROKEN", "CARD_BROKEN"]
|
|
"DEALS_DAMAGE":
|
|
return actual_event in ["DEALS_DAMAGE", "DEALS_DAMAGE_TO_OPPONENT", "DEALS_DAMAGE_TO_FORWARD"]
|
|
"DEALS_DAMAGE_TO_OPPONENT":
|
|
return actual_event == "DEALS_DAMAGE_TO_OPPONENT"
|
|
"DEALS_DAMAGE_TO_FORWARD":
|
|
return actual_event == "DEALS_DAMAGE_TO_FORWARD"
|
|
"BLOCKS_OR_IS_BLOCKED":
|
|
return actual_event in ["BLOCKS", "IS_BLOCKED"]
|
|
|
|
return false
|
|
|
|
|
|
## Check if source matches trigger requirements
|
|
func _source_matches(
|
|
trigger_source: String,
|
|
event_data: Dictionary,
|
|
source_card: CardInstance,
|
|
game_state
|
|
) -> bool:
|
|
var event_card = event_data.get("card")
|
|
|
|
match trigger_source:
|
|
"SELF":
|
|
# Trigger source must be this card
|
|
return event_card == source_card
|
|
"CONTROLLER":
|
|
# Trigger source must be controlled by same player
|
|
if event_card:
|
|
return event_card.controller_index == source_card.controller_index
|
|
var event_player = event_data.get("player", -1)
|
|
return event_player == source_card.controller_index
|
|
"OPPONENT":
|
|
# Trigger source must be controlled by opponent
|
|
if event_card:
|
|
return event_card.controller_index != source_card.controller_index
|
|
var event_player = event_data.get("player", -1)
|
|
return event_player != source_card.controller_index and event_player >= 0
|
|
"ANY", _:
|
|
# Any source triggers
|
|
return true
|
|
|
|
return true
|
|
|
|
|
|
## Check if a card matches a filter
|
|
func _matches_card_filter(card: CardInstance, filter: Dictionary) -> 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
|
|
|
|
# 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"):
|
|
if card.card_data.cost < filter.cost_min:
|
|
return false
|
|
if filter.has("cost_max"):
|
|
if card.card_data.cost > filter.cost_max:
|
|
return false
|
|
if filter.has("cost"):
|
|
if card.card_data.cost != filter.cost:
|
|
return false
|
|
|
|
# Power filters
|
|
if filter.has("power_min"):
|
|
if card.get_power() < filter.power_min:
|
|
return false
|
|
if filter.has("power_max"):
|
|
if card.get_power() > filter.power_max:
|
|
return false
|
|
|
|
# State filters
|
|
if filter.has("is_dull"):
|
|
if card.is_dull() != filter.is_dull:
|
|
return false
|
|
if filter.has("is_active"):
|
|
if card.is_active() != filter.is_active:
|
|
return false
|
|
|
|
# Name filter
|
|
if filter.has("name"):
|
|
if card.card_data.name != filter.name:
|
|
return false
|
|
|
|
# Category filter
|
|
if filter.has("category"):
|
|
if card.card_data.category != filter.category:
|
|
return false
|
|
|
|
# Job filter
|
|
if filter.has("job"):
|
|
if card.card_data.job != filter.job:
|
|
return false
|
|
|
|
return true
|