use crate::card::{Card, CounterType};
use crate::ids::{CardId, PlayerId};
use crate::spellability::SpellAbility;
use forge_foundation::ZoneType;
use std::collections::BTreeMap;
#[derive(Debug, Clone)]
pub struct CardSnapshot {
pub id: CardId,
pub controller: PlayerId,
pub owner: PlayerId,
pub power: i32,
pub toughness: i32,
pub counters: BTreeMap<CounterType, i32>,
pub tapped: bool,
pub zone: ZoneType,
pub card_name: String,
}
impl CardSnapshot {
pub fn from_card(card: &Card) -> Self {
Self {
id: card.id,
controller: card.controller,
owner: card.owner,
power: card.power(),
toughness: card.toughness(),
counters: card.counters.clone(),
tapped: card.tapped,
zone: card.zone,
card_name: card.card_name.clone(),
}
}
}
impl crate::game::GameState {
pub fn copy_last_state(&mut self) {
for card in self.cards.iter() {
if card.zone == ZoneType::Battlefield {
if let Some(existing) = self
.last_state_battlefield
.iter_mut()
.find(|s| s.id == card.id)
{
*existing = CardSnapshot::from_card(card);
} else {
self.last_state_battlefield
.push(CardSnapshot::from_card(card));
}
}
}
}
pub fn get_lki_snapshot(&self, card_id: CardId) -> Option<&CardSnapshot> {
self.last_state_battlefield.iter().find(|s| s.id == card_id)
}
pub fn update_lki_snapshot(&mut self, card_id: CardId) {
let card = &self.cards[card_id.index()];
if card.zone != ZoneType::Battlefield {
return;
}
let snapshot = CardSnapshot::from_card(card);
if let Some(existing) = self
.last_state_battlefield
.iter_mut()
.find(|s| s.id == card_id)
{
*existing = snapshot;
} else {
self.last_state_battlefield.push(snapshot);
}
}
}
pub fn resolve_lki_power(game: &crate::game::GameState, trigger_src: CardId) -> i32 {
if let Some(lki) = game.card(trigger_src).lki_power {
return lki;
}
if let Some(snapshot) = game.get_lki_snapshot(trigger_src) {
return snapshot.power;
}
0
}
pub fn resolve_lki_toughness(game: &crate::game::GameState, trigger_src: CardId) -> i32 {
if let Some(lki) = game.card(trigger_src).lki_toughness {
return lki;
}
if let Some(snapshot) = game.get_lki_snapshot(trigger_src) {
return snapshot.toughness;
}
0
}
pub fn resolve_lki_counter_count(
game: &crate::game::GameState,
trigger_src: CardId,
counter_type: &crate::card::CounterType,
) -> i32 {
let card = game.card(trigger_src);
if let Some(&count) = card.lki_counters.as_ref().and_then(|c| c.get(counter_type)) {
return count;
}
if let Some(snapshot) = game.get_lki_snapshot(trigger_src) {
return snapshot.counters.get(counter_type).copied().unwrap_or(0);
}
0
}
fn trigger_card_object(sa: &SpellAbility, key: &str) -> Option<CardId> {
crate::ability::ability_key::from_string(key)
.and_then(|ability_key| sa.get_triggering_card(ability_key))
}
fn trigger_int_object(sa: &SpellAbility, key: &str) -> Option<i32> {
crate::ability::ability_key::from_string(key)
.and_then(|ability_key| sa.get_triggering_value(ability_key))
.and_then(|value| value.trim().parse::<i32>().ok())
}
pub fn resolve_triggered_card_lki_svar(
game: &crate::game::GameState,
sa: &SpellAbility,
svar_expr: &str,
) -> Option<i32> {
let property = svar_expr.strip_prefix("TriggeredCard$")?;
resolve_triggered_card_lki_property(game, sa, property)
}
pub fn resolve_triggered_card_lki_property(
game: &crate::game::GameState,
sa: &SpellAbility,
property: &str,
) -> Option<i32> {
if property == "CardPower" {
if let Some(power) = trigger_int_object(sa, "TriggeredCardPower") {
return Some(power);
}
return trigger_card_object(sa, "Card")
.or(sa.trigger_source)
.map(|trigger_src| resolve_lki_power(game, trigger_src));
}
if property == "CardToughness" {
if let Some(toughness) = trigger_int_object(sa, "TriggeredCardToughness") {
return Some(toughness);
}
return trigger_card_object(sa, "Card")
.or(sa.trigger_source)
.map(|trigger_src| resolve_lki_toughness(game, trigger_src));
}
if let Some(counter_name) = property.strip_prefix("CardCounters.") {
let counter_type = crate::ability::effects::parse_counter_type(counter_name);
return trigger_card_object(sa, "Card")
.or(sa.trigger_source)
.map(|trigger_src| resolve_lki_counter_count(game, trigger_src, &counter_type));
}
if let Some(filter) = property.strip_prefix("Valid ") {
let card_id = trigger_card_object(sa, "Card").or(sa.trigger_source)?;
let source_id = sa.source?;
let selector = crate::parsing::cached_compiled_selector(filter);
let matched = crate::card::valid_filter::matches_valid_card_selector_with_context(
&selector,
game.card(card_id),
crate::card::valid_filter::MatchContext::from_source(game.card(source_id))
.with_game(game)
.with_spell_ability(sa),
);
return Some(i32::from(matched));
}
None
}