//! The interpreter: turns one effect, as data, into engine actions. //! //! This is the only place that knows what an effect *means*. A card's text is a //! small tree (clauses holding effects holding targets); the engine walks it. The //! effect vocabulary is closed: a card can only do what this match can do, which is //! what makes a data card safe to load. use super::card::{Effect, Target}; use super::queue::{Action, Ctx}; use super::world::EntityId; /// The actions one effect performs, in order, given where and for whom it runs. pub fn actions(effect: &Effect, ctx: &Ctx) -> Vec { let owner = ctx.owner; match effect { Effect::DealDamage { target, amount } => vec![Action::Damage { targets: targets(*target, ctx), amount: *amount, }], Effect::Draw { count } => (0..*count) .map(|_| Action::Draw { player: owner }) .collect(), Effect::Summon { card, count } => (0..*count) .map(|_| Action::Summon { card: card.clone(), player: owner, at: summon_position(ctx), }) .collect(), Effect::GainArmor { amount } => vec![Action::GainArmor { player: owner, amount: *amount, }], Effect::GainAttack { target, amount } => targets(*target, ctx) .into_iter() .map(|id| Action::GainAttack { id, amount: *amount, }) .collect(), Effect::Destroy { target } => vec![Action::Destroy { targets: targets(*target, ctx), }], Effect::Assemble { filter } => vec![Action::Assemble { player: owner, filter: filter.clone(), }], Effect::BuffHand { attack, health } => ctx .world .minions_in_hand(owner) .into_iter() .map(|id| Action::Buff { id, attack: *attack, health: *health, }) .collect(), Effect::SpawnText { .. } => panic!("spawnText needs a grid: it is a Part III effect"), } } /// The entities a target names, from where the effect runs. fn targets(target: Target, ctx: &Ctx) -> Vec { let world = ctx.world; let others = |ids: Vec| -> Vec { ids.into_iter() .filter(|&id| Some(id) != ctx.source) .collect() }; match target { Target::This => vec![ctx.minion()], Target::AllMinions => world.minions_in_play(), Target::AllOtherMinions => others(world.minions_in_play()), Target::AllOtherCharacters => { let mut ids = vec![world.hero(0), world.hero(1)]; ids.extend(world.minions_in_play()); others(ids) } Target::EnemyHero => vec![ctx.enemy_hero()], Target::Chosen => ctx.target.into_iter().collect(), } } /// Where a summon goes: to the right of a source still on the board; where a dead /// source stood (its Death Event's position); at the right end for a spell. fn summon_position(ctx: &Ctx) -> Option { match ctx.source.and_then(|s| ctx.world.board_index(s)) { Some(index) => Some(index + 1), None => ctx.position, } }