1pub mod ability;
2pub mod ability_activated;
3pub mod ability_mana_part;
4pub mod ability_static;
5pub mod ability_sub;
6pub mod alternative_cost;
7pub mod land_ability;
8pub mod optional_cost;
9pub mod optional_cost_value;
10pub mod params;
11pub mod runtime_types;
12pub mod spell;
13pub mod spell_ability_condition;
14pub mod spell_ability_predicates;
15pub mod spell_ability_restriction;
16pub mod spell_ability_stack_instance;
17pub mod spell_ability_variables;
18pub mod spell_permanent;
19pub mod target_choices;
20pub mod target_restrictions;
21pub mod trait_spell_ability;
22pub mod valid_sa;
23
24use std::collections::HashMap;
25use std::sync::atomic::{AtomicU32, Ordering};
26
27use serde::{Deserialize, Serialize};
28
29use crate::ability::ability_factory::AbilityRecordType;
30use crate::ability::ability_ir::SpellAbilityIr;
31use crate::ability::api_type::ApiType;
32use crate::ability::AbilityKey;
33use crate::agent::PlayerAgent;
34use crate::card::card_damage_map::CardDamageMap;
35use crate::card::card_zone_table::CardZoneTable;
36use crate::card_trait_base::CardTraitIrOwner;
37use crate::cost::{parse_cost, Cost};
38use crate::event::AbilityValue;
39use crate::game::GameState;
40use crate::ids::{CardId, PlayerId};
41use crate::mana::ManaPool;
42use crate::parsing::{keys, Params, ParsedParams};
43
44pub use ability_mana_part::AbilityManaPart;
45pub use alternative_cost::{AlternativeCost, MORPH_GENERIC_COST, MORPH_PT};
46pub use optional_cost::OptionalCost;
47pub use optional_cost_value::OptionalCostValue;
48pub use runtime_types::{
49 AbilityDuration, ReplaceDyingCondition, SpellAbilityMode, TriggerCondition,
50};
51pub use spell_ability_condition::SpellAbilityCondition;
52pub use spell_ability_predicates::{has_sub_ability_api, is_api, is_valid};
53pub use spell_ability_restriction::SpellAbilityRestriction;
54pub use spell_ability_variables::SpellAbilityVariables;
55pub use target_choices::TargetChoices;
56pub use target_restrictions::{TargetKind, TargetRestrictions};
57pub use valid_sa::matches_valid_sa;
58
59static NEXT_SPELL_ABILITY_ID: AtomicU32 = AtomicU32::new(1);
60
61fn next_spell_ability_id() -> u32 {
62 NEXT_SPELL_ABILITY_ID.fetch_add(1, Ordering::Relaxed)
63}
64
65pub trait TriggerKeyInput {
66 fn into_ability_key(self) -> Option<AbilityKey>;
67}
68
69impl TriggerKeyInput for AbilityKey {
70 fn into_ability_key(self) -> Option<AbilityKey> {
71 Some(self)
72 }
73}
74
75impl TriggerKeyInput for &str {
76 fn into_ability_key(self) -> Option<AbilityKey> {
77 crate::ability::ability_key::from_string(self)
78 }
79}
80
81impl TriggerKeyInput for String {
82 fn into_ability_key(self) -> Option<AbilityKey> {
83 crate::ability::ability_key::from_string(&self)
84 }
85}
86
87impl TriggerKeyInput for &String {
88 fn into_ability_key(self) -> Option<AbilityKey> {
89 crate::ability::ability_key::from_string(self)
90 }
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct SpellAbility {
100 #[serde(default)]
101 pub id: u32,
102 pub api: Option<ApiType>,
105 pub source: Option<CardId>,
107 #[serde(default)]
110 pub original_host: Option<CardId>,
111 pub activating_player: PlayerId,
113 pub targeting_player: Option<PlayerId>,
116 pub ability_text: String,
118 #[serde(default)]
120 pub record_type: AbilityRecordType,
121 #[serde(skip)]
125 pub ir: SpellAbilityIr,
126 pub target_restrictions: Option<TargetRestrictions>,
130 pub target_chosen: TargetChoices,
133 pub pay_costs: Option<Cost>,
136 pub sub_ability: Option<Box<SpellAbility>>,
139 #[serde(default)]
141 pub wrapped_ability: Option<Box<SpellAbility>>,
142 pub is_spell: bool,
144 pub is_trigger: bool,
146 pub is_activated: bool,
148 #[serde(default)]
150 pub intrinsic: bool,
151 pub trigger_source: Option<CardId>,
153 #[serde(default)]
156 pub trigger_source_zone_timestamp: Option<u64>,
157 #[serde(default)]
160 pub source_zone_timestamp: Option<u64>,
161 pub source_trigger_id: Option<u32>,
163 pub trigger_index: Option<usize>,
165 pub alt_cost: Option<AlternativeCost>,
167 #[serde(default)]
171 pub alt_cost_index: u8,
172 #[serde(default)]
180 pub evoke_keyword_count: u8,
181 pub kicked: bool,
183 pub buyback_paid: bool,
185 pub overloaded: bool,
187 pub is_copy: bool,
189 #[serde(default)]
191 pub paid_life_amount: i32,
192 pub kick_count: u32,
194 pub replicate_count: u32,
196 pub optional_generic_cost_paid: bool,
198 pub trigger_remembered_amount: i32,
201 pub x_mana_cost_paid: u32,
204 pub discarded_cost_cards: Vec<crate::ids::CardId>,
207 #[serde(default)]
210 pub optional_costs: Vec<OptionalCost>,
211 #[serde(default)]
214 pub paid_hash: HashMap<String, Vec<String>>,
215 #[serde(default)]
217 pub paying_mana: Vec<u16>,
218 #[serde(default)]
220 pub paid_abilities: Vec<SpellAbility>,
221 pub mana_part: Option<AbilityManaPart>,
224 #[serde(default)]
226 pub express_mana_choice: Option<u16>,
227 #[serde(default)]
230 pub convoke_tapped: Vec<CardId>,
231 #[serde(default)]
234 pub spliced_cards: Vec<CardId>,
235 #[serde(default)]
238 pub announce_vars: HashMap<String, i32>,
239 pub sacrificed_as_emerge: Option<CardId>,
242 pub sacrificed_as_offering: Option<CardId>,
245 #[serde(default)]
248 pub description: String,
249 #[serde(default)]
252 pub stack_description: String,
253 #[serde(default)]
256 pub is_mana_ability: bool,
257 #[serde(default)]
260 pub is_land_ability: bool,
261 #[serde(default)]
263 pub cast_face_down: bool,
264 #[serde(default)]
266 pub trigger_objects: HashMap<AbilityKey, AbilityValue>,
267 #[serde(default)]
269 pub trigger_spell_abilities: HashMap<AbilityKey, SpellAbility>,
270 #[serde(default)]
272 pub additional_ability_lists: HashMap<String, Vec<SpellAbility>>,
273 #[serde(default)]
275 pub replacing_objects: HashMap<AbilityKey, AbilityValue>,
276 #[serde(default)]
278 pub trigger_remembered: Vec<AbilityValue>,
279 #[serde(default)]
281 pub restriction: SpellAbilityRestriction,
282 #[serde(default)]
284 pub condition: SpellAbilityCondition,
285 #[serde(default)]
287 pub rollback_effects: Vec<String>,
288 #[serde(default)]
290 pub optional_keyword_amounts: HashMap<String, i32>,
291 #[serde(default)]
293 pub pips_to_reduce: Vec<String>,
294 #[serde(default)]
296 pub may_choose_new_targets: bool,
297 #[serde(default)]
299 pub last_state: HashMap<String, String>,
300 #[serde(skip)]
302 pub change_zone_table: Option<CardZoneTable>,
303 #[serde(skip)]
305 pub damage_map: Option<CardDamageMap>,
306 #[serde(skip)]
308 pub prevent_map: Option<CardDamageMap>,
309}
310
311impl std::fmt::Display for SpellAbility {
314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315 let mut node = Some(self);
316 let mut first = true;
317 while let Some(current) = node {
318 if !first {
319 write!(f, " ")?;
320 }
321 first = false;
322 write!(f, "{}", current.description)?;
323 node = current.sub_ability.as_deref();
324 }
325 Ok(())
326 }
327}
328
329impl CardTraitIrOwner for SpellAbility {
330 type Ir = SpellAbilityIr;
331
332 fn ir(&self) -> &Self::Ir {
333 &self.ir
334 }
335
336 fn card_trait_requirements(&self) -> &crate::card::valid_filter::CardTraitRequirementsIr {
337 &self.ir.card_trait_requirements
338 }
339}
340
341impl SpellAbility {
342 pub fn uses_targeting(&self) -> bool {
345 self.target_restrictions.is_some()
346 }
347
348 pub fn param_is_true(&self, key: &str) -> bool {
351 match key {
352 keys::OPTIONAL => self.ir.optional,
353 keys::MANDATORY => self.ir.mandatory,
354 keys::TAPPED => self.ir.tapped,
355 keys::HIDDEN => self.ir.hidden,
356 keys::IMPRINT => self.ir.imprint,
357 keys::CHOOSE_FROM_DEFINED_CARDS => self.ir.choose_from_defined_cards,
358 keys::FACE_DOWN => self.ir.face_down,
359 keys::EXILE_FACE_DOWN => self.ir.exile_face_down,
360 keys::TRANSFORMED => self.ir.transformed,
361 keys::AT_RANDOM => self.ir.at_random,
362 keys::REMEMBER_ALTERED => self.ir.remember_altered,
363 keys::REMEMBER_AMASS => self.ir.remember_amass,
364 keys::REMEMBER => self.ir.remember_flag,
365 keys::REMOVE_FROM_COMBAT => self.ir.remove_from_combat,
366 keys::RANDOM_TARGET => self.ir.random_target,
367 keys::REMEMBER_CHOSEN => self.ir.remember_chosen,
368 keys::REMEMBER_CLASHER => self.ir.remember_clasher,
369 keys::REMEMBER_CLOAKED => self.ir.remember_cloaked,
370 keys::REMEMBER_DISCOVERED => self.ir.remember_discovered,
371 keys::REMEMBER_DRAFTED => self.ir.remember_drafted,
372 keys::REMEMBER_EXCHANGED => self.ir.remember_exchanged,
373 keys::REMEMBER_INVESTIGATING_PLAYERS => self.ir.remember_investigating_players,
374 keys::REMEMBER_MADE => self.ir.remember_made,
375 keys::IMPRINT_MADE => self.ir.imprint_made,
376 keys::RANDOM_CHOSEN => self.ir.random_chosen,
377 keys::SNEAK => self.ir.sneak,
378 keys::MEGA => self.ir.mega,
379 keys::STORE_VOTE_NUM => self.ir.store_vote_num,
380 keys::REMEMBER_VOTED_OBJECTS => self.ir.remember_voted_objects,
381 "ToVisitYourAttractions" => self.ir.to_visit_your_attractions,
382 "RememberHighestPlayer" => self.ir.remember_highest_player,
383 "UseHighestRoll" => self.ir.use_highest_roll,
384 "UseDifferenceBetweenRolls" => self.ir.use_difference_between_rolls,
385 "StoreResults" => self.ir.store_results,
386 "EvenOddResults" => self.ir.even_odd_results,
387 "DifferentResults" => self.ir.different_results,
388 "MaxRollsResults" => self.ir.max_rolls_results,
389 "NoteDoubles" => self.ir.note_doubles,
390 "SubsForEach" => self.ir.subs_for_each,
391 "RerollResults" => self.ir.reroll_results,
392 keys::NINJUTSU => self.ir.ninjutsu,
393 keys::UNEARTH => self.ir.unearth,
394 keys::ATTACKING => self.ir.attacking,
395 keys::OVERWRITE_COLORS => self.ir.overwrite_colors,
396 keys::FORETOLD => self.ir.foretold,
397 keys::FORETOLD_COST => self.ir.foretold_cost,
398 keys::IMPRINT_LAST => self.ir.imprint_last,
399 keys::RANDOM_ORDER => self.ir.random_order,
400 keys::SHUFFLE_CHANGED_PILE => self.ir.shuffle_changed_pile,
401 keys::WARP => self.ir.warp,
402 keys::CAN_REPEAT_MODES => self.ir.can_repeat_modes,
403 keys::ENTWINE => self.ir.entwine,
404 keys::REMOVE_CREATURE_TYPES => self.ir.animate_remove_creature_types,
405 keys::REMOVE_ALL_ABILITIES => self.ir.animate_remove_all_abilities,
406 keys::REMEMBER_REMOVED_CARDS => self.ir.remember_removed_cards,
407 keys::TOKEN_TAPPED => self.ir.token_tapped,
408 keys::REMEMBER_TOKENS => self.ir.remember_tokens,
409 keys::REMEMBER_ORIGINAL_TOKENS => self.ir.remember_original_tokens,
410 keys::IMPRINT_TOKENS => self.ir.imprint_tokens,
411 keys::REMEMBER_SOURCE => self.ir.remember_source,
412 keys::CLEANUP_FOR_EACH => self.ir.cleanup_for_each,
413 "Morph" => self.ir.morph,
414 "MorphUp" => self.ir.morph_up,
415 "Megamorph" => self.ir.megamorph,
416 "RememberAbandoned" => self.ir.remember_abandoned,
417 _ => false,
418 }
419 }
420
421 pub fn param_value(&self, key: &str) -> Option<&str> {
422 match key {
423 keys::MODE => self.ir.mode_text.as_deref(),
424 keys::VALID_CARDS => self.ir.valid_cards_text.as_deref(),
425 keys::VALID_CARD => self.ir.valid_card_text.as_deref(),
426 keys::VALID_PLAYERS => self.ir.valid_players_text.as_deref(),
427 keys::VALID_PLAYER => self.ir.valid_player_text.as_deref(),
428 keys::VALID_TGTS => self.ir.valid_tgts_text.as_deref(),
429 keys::VALID_TARGET => self.ir.valid_target_text.as_deref(),
430 keys::DEFINED => self.ir.defined_text.as_deref(),
431 keys::DEFINED_PLAYER => self.ir.defined_player_text.as_deref(),
432 keys::CONTROLLER => self.ir.controller_text.as_deref(),
433 keys::ORIGIN => self.ir.origin_text.as_deref(),
434 keys::DESTINATION => self.ir.destination_text.as_deref(),
435 keys::CHOICES => self.ir.choices.as_deref(),
436 keys::FOR_EACH => self.ir.for_each_text.as_deref(),
437 keys::TRIGGERS => self.ir.triggers.as_deref(),
438 keys::COUNTER_TYPE => self.ir.counter_type_text.as_deref(),
439 keys::TOKEN_SCRIPT => self.ir.token_script.as_deref(),
440 keys::TOKEN_OWNER => self.ir.token_owner.as_deref(),
441 keys::TOKEN_NAME => self.ir.token_name_text.as_deref(),
442 keys::TOKEN_TYPES => self.ir.token_types_text.as_deref(),
443 keys::TOKEN_COLORS => self.ir.token_colors_text.as_deref(),
444 keys::TOKEN_KEYWORDS => self.ir.token_keywords_text.as_deref(),
445 keys::TOKEN_ATTACKING => self.ir.token_attacking_text.as_deref(),
446 keys::TOKEN_BLOCKING => self.ir.token_blocking_text.as_deref(),
447 keys::TOKEN_REMEMBERED => self.ir.token_remembered.as_deref(),
448 keys::ADD_TRIGGERS_FROM => self.ir.add_triggers_from_text.as_deref(),
449 keys::AT_EOT => self.ir.at_eot.as_deref(),
450 keys::AT_EOT_TRIG => self.ir.at_eot_trig_text.as_deref(),
451 keys::ATTACHED_TO => self.ir.attached_to.as_deref(),
452 keys::ATTACH_AFTER => self.ir.attach_after_text.as_deref(),
453 keys::WITH_COUNTERS_TYPE => self.ir.with_counters_type_text.as_deref(),
454 keys::WITH_COUNTERS_AMOUNT => self.ir.with_counters_amount_text.as_deref(),
455 keys::PUMP_KEYWORDS => self.ir.pump_keywords.as_deref(),
456 keys::PUMP_DURATION => self.ir.pump_duration_text.as_deref(),
457 "Keyword" => self.ir.keyword_text.as_deref(),
458 keys::CHOOSER => self.ir.chooser.as_deref(),
459 keys::NAME => self.ir.name_text.as_deref(),
460 keys::NAMES => self.ir.names_text.as_deref(),
461 keys::CHOOSE_FROM_LIST => self.ir.choose_from_list_text.as_deref(),
462 keys::GAIN_CONTROL => self.ir.gain_control_text.as_deref(),
463 keys::SPELLBOOK => self.ir.spellbook_text.as_deref(),
464 keys::VOTE_MESSAGE => self.ir.vote_message_text.as_deref(),
465 keys::DEFINED_MAGNET => self.ir.defined_magnet_text.as_deref(),
466 "PhaseInOrOut" => self.ir.phase_in_or_out_text.as_deref(),
467 "ExtraPhase" => self.ir.extra_phase_text.as_deref(),
468 "CardState" => self.ir.card_state_name.as_deref(),
469 _ => None,
470 }
471 }
472
473 pub fn get_targets(&self) -> &TargetChoices {
475 &self.target_chosen
476 }
477
478 pub fn get_targets_mut(&mut self) -> &mut TargetChoices {
480 &mut self.target_chosen
481 }
482
483 pub fn get_sub_ability(&self) -> Option<&SpellAbility> {
485 self.sub_ability.as_deref()
486 }
487
488 pub fn get_sub_ability_mut(&mut self) -> Option<&mut SpellAbility> {
490 self.sub_ability.as_deref_mut()
491 }
492
493 pub fn is_wrapper(&self) -> bool {
495 self.wrapped_ability.is_some()
496 }
497
498 pub fn get_wrapped_ability(&self) -> &SpellAbility {
500 self.wrapped_ability
501 .as_deref()
502 .expect("SpellAbility.get_wrapped_ability called on non-wrapper")
503 }
504
505 pub fn get_wrapped_ability_mut(&mut self) -> &mut SpellAbility {
506 self.wrapped_ability
507 .as_deref_mut()
508 .expect("SpellAbility.get_wrapped_ability_mut called on non-wrapper")
509 }
510
511 pub fn set_wrapped_ability(&mut self, wrapped: SpellAbility) {
512 self.wrapped_ability = Some(Box::new(wrapped));
513 }
514
515 pub fn clear_targets(&mut self) {
517 self.target_chosen = TargetChoices::default();
518 }
519
520 pub fn setup_targets(
526 &mut self,
527 game: &GameState,
528 agents: &mut [Box<dyn PlayerAgent>],
529 mana_pools: &[ManaPool],
530 ) -> bool {
531 if self.uses_targeting() {
533 self.clear_targets();
534 self.targeting_player = choose_targeting_player(self, game, agents);
535 let player = self.targeting_player.unwrap_or(self.activating_player);
536 if !agents[player.index()].choose_targets_for(self, game, mana_pools) {
537 return false;
538 }
539 }
540
541 let mut current = self.sub_ability.as_deref_mut();
543 while let Some(sa) = current {
544 if sa.uses_targeting() {
545 sa.clear_targets();
546 sa.targeting_player = choose_targeting_player(sa, game, agents);
547 let player = sa.targeting_player.unwrap_or(sa.activating_player);
548 if !agents[player.index()].choose_targets_for(sa, game, mana_pools) {
549 return false;
550 }
551 }
552 current = sa.sub_ability.as_deref_mut();
553 }
554
555 if !crate::staticability::static_ability_must_target::meets_must_target_restriction(
556 game, self,
557 ) {
558 return false;
559 }
560
561 true
562 }
563
564 pub fn new_simple(source: Option<CardId>, player: PlayerId, ability_text: &str) -> Self {
566 let _perf_scope = crate::perf::ParamsLookupScopeGuard::enter(
567 crate::perf::ParamsLookupScope::AbilityBuild,
568 );
569 let parsed = ParsedParams::parse(ability_text);
570 let params = Params::from_parsed(&parsed);
571 let api = parsed
572 .get(keys::SP)
573 .or_else(|| parsed.get(keys::DB))
574 .or_else(|| parsed.get(keys::AB))
575 .and_then(ApiType::smart_value_of);
576 let record_type = crate::ability::ability_factory::AbilityRecordType::from_parsed(&parsed)
577 .unwrap_or_default();
578 let target_restrictions = if parsed.has(keys::VALID_TGTS) {
579 TargetRestrictions::new_from_parsed(&parsed, ¶ms)
580 } else {
581 None
582 };
583 let cost = parsed.get(keys::COST).map(parse_cost);
584 let mut ir = crate::ability::ability_ir::SpellAbilityIr::from_parsed(api, &parsed);
585 ir.compile_numeric_params_from_runtime(¶ms);
586
587 SpellAbility {
588 id: next_spell_ability_id(),
589 api,
590 source,
591 original_host: None,
592 activating_player: player,
593 targeting_player: None,
594 ability_text: ability_text.to_string(),
595 record_type,
596 ir,
597 target_restrictions,
598 target_chosen: TargetChoices::default(),
599 pay_costs: cost,
600 sub_ability: None,
601 wrapped_ability: None,
602 is_spell: false,
603 is_trigger: false,
604 is_activated: false,
605 intrinsic: false,
606 trigger_source: None,
607 trigger_source_zone_timestamp: None,
608 source_zone_timestamp: None,
609 source_trigger_id: None,
610 trigger_index: None,
611 alt_cost: None,
612 alt_cost_index: 0,
613 evoke_keyword_count: 0,
614 kicked: false,
615 buyback_paid: false,
616 overloaded: false,
617 is_copy: false,
618 paid_life_amount: 0,
619 kick_count: 0,
620 replicate_count: 0,
621 optional_generic_cost_paid: false,
622 trigger_remembered_amount: 0,
623 x_mana_cost_paid: 0,
624 discarded_cost_cards: Vec::new(),
625 optional_costs: Vec::new(),
626 paid_hash: HashMap::new(),
627 paying_mana: Vec::new(),
628 paid_abilities: Vec::new(),
629 mana_part: None,
630 express_mana_choice: None,
631 convoke_tapped: Vec::new(),
632 spliced_cards: Vec::new(),
633 announce_vars: HashMap::new(),
634 sacrificed_as_emerge: None,
635 sacrificed_as_offering: None,
636 description: String::new(),
637 stack_description: String::new(),
638 is_mana_ability: false,
639 is_land_ability: false,
640 cast_face_down: false,
641 trigger_objects: HashMap::new(),
642 trigger_spell_abilities: HashMap::new(),
643 additional_ability_lists: HashMap::new(),
644 replacing_objects: HashMap::new(),
645 trigger_remembered: Vec::new(),
646 restriction: SpellAbilityRestriction::default(),
647 condition: SpellAbilityCondition::default(),
648 rollback_effects: Vec::new(),
649 optional_keyword_amounts: HashMap::new(),
650 pips_to_reduce: Vec::new(),
651 may_choose_new_targets: false,
652 last_state: HashMap::new(),
653 change_zone_table: None,
654 damage_map: None,
655 prevent_map: None,
656 }
657 }
658
659 pub fn new_empty(source: Option<CardId>, player: PlayerId) -> Self {
662 Self::new_simple(source, player, "")
663 }
664
665 pub fn new_land(source: Option<CardId>, player: PlayerId) -> Self {
667 let mut sa = Self::new_empty(source, player);
668 sa.is_land_ability = true;
669 sa
670 }
671
672 pub fn find_sub_ability_by_type(&self, api: ApiType) -> Option<&SpellAbility> {
677 let mut current = self.sub_ability.as_deref();
678 while let Some(sub) = current {
679 if sub.api == Some(api) {
680 return Some(sub);
681 }
682 current = sub.sub_ability.as_deref();
683 }
684 None
685 }
686
687 pub fn can_this_produce(&self) -> bool {
692 match &self.mana_part {
693 Some(mp) => mp.can_this_produce(),
694 None => false,
695 }
696 }
697
698 pub fn can_produce(&self, color: &str) -> bool {
701 match &self.mana_part {
702 Some(mp) => mp.can_produce(color),
703 None => false,
704 }
705 }
706
707 pub fn amount_of_mana_generated(&self) -> i32 {
710 match &self.mana_part {
711 Some(mp) => mp.amount_of_mana_generated(),
712 None => 0,
713 }
714 }
715
716 pub fn total_amount_of_mana_generated(&self) -> i32 {
719 match &self.mana_part {
720 Some(mp) => mp.total_amount_of_mana_generated(),
721 None => 0,
722 }
723 }
724
725 pub fn allows_paying_with_shard(&self) -> bool {
730 self.ir.allows_paying_with_shard
731 }
732
733 pub fn cant_be_copied(&self) -> bool {
736 self.ir.cant_be_copied_ability
737 }
738
739 pub fn can_play(&self, game: &GameState) -> bool {
742 if let Some(card_id) = self.source {
743 if !self
744 .restriction
745 .can_play_with_sa(game, card_id, self.activating_player, Some(self))
746 {
747 return false;
748 }
749
750 let card = game.card(card_id);
751 if let Some(limit_expr) = self.restriction.variables.limit_to_check() {
752 let limit = crate::svar::resolve_numeric_value(game, self, limit_expr, 0);
753 if card.get_ability_activated_this_turn(Some(self)) as i32 >= limit {
754 return false;
755 }
756 }
757 if let Some(limit_expr) = self.restriction.variables.game_limit_to_check() {
758 let limit = crate::svar::resolve_numeric_value(game, self, limit_expr, 0);
759 if card.get_ability_activated_this_game(Some(self)) as i32 >= limit {
760 return false;
761 }
762 }
763
764 true
765 } else {
766 true
767 }
768 }
769
770 pub fn can_play_with_optional_cost(&self) -> bool {
773 !self.optional_costs.is_empty()
774 }
775
776 pub fn prompt_if_only_possible_ability(&self) -> bool {
779 self.ir.prompt_if_only_possible_ability
780 }
781
782 pub fn add_optional_cost(&mut self, cost: OptionalCost) {
785 if !self.optional_costs.contains(&cost) {
786 self.optional_costs.push(cost);
787 }
788 }
789
790 pub fn cost_has_x(&self) -> bool {
793 self.ir.cost_has_x
794 }
795
796 pub fn cost_has_mana_x(&self) -> bool {
799 self.ir.cost_has_x
800 }
801
802 pub fn met_conditions(&self, game: &GameState) -> bool {
805 self.condition.are_met(game, self)
806 }
807
808 pub fn clear_mana_paid(&mut self) {
811 self.x_mana_cost_paid = 0;
812 }
813
814 pub fn apply_paying_mana_effects(&mut self) {
817 }
820
821 pub fn run(&self) {
824 }
827
828 pub fn add_cost_to_hash_list(&mut self, key: &str, value: &str) {
833 self.paid_hash
834 .entry(key.to_string())
835 .or_default()
836 .push(value.to_string());
837 }
838
839 pub fn reset_paid_hash(&mut self) {
842 self.paid_hash.clear();
843 }
844
845 pub fn has_triggering_object<K: TriggerKeyInput>(&self, key: K) -> bool {
850 key.into_ability_key()
851 .map(|parsed| self.trigger_objects.contains_key(&parsed))
852 .unwrap_or(false)
853 }
854
855 pub fn get_triggering_value(&self, key: AbilityKey) -> Option<&AbilityValue> {
857 self.trigger_objects.get(&key)
858 }
859
860 pub fn get_triggering_card(&self, key: AbilityKey) -> Option<CardId> {
862 match self.get_triggering_value(key) {
863 Some(AbilityValue::Card(card)) => Some(*card),
864 Some(AbilityValue::Cards(cards)) => cards.first().copied(),
865 Some(AbilityValue::GameEntities(entities)) => {
866 entities.iter().find_map(|entity| match entity {
867 crate::agent::GameEntity::Card(card) => Some(*card),
868 crate::agent::GameEntity::Player(_) => None,
869 })
870 }
871 _ => None,
872 }
873 }
874
875 pub fn get_triggering_player(&self, key: AbilityKey) -> Option<PlayerId> {
877 match self.get_triggering_value(key) {
878 Some(AbilityValue::Player(player)) => Some(*player),
879 Some(AbilityValue::Players(players)) => players.first().copied(),
880 Some(AbilityValue::GameEntities(entities)) => {
881 entities.iter().find_map(|entity| match entity {
882 crate::agent::GameEntity::Player(player) => Some(*player),
883 crate::agent::GameEntity::Card(_) => None,
884 })
885 }
886 _ => None,
887 }
888 }
889
890 pub fn get_triggering_cards(&self, key: AbilityKey) -> Vec<CardId> {
892 match self.get_triggering_value(key) {
893 Some(AbilityValue::Card(card)) => vec![*card],
894 Some(AbilityValue::Cards(cards)) => cards.clone(),
895 Some(AbilityValue::GameEntities(entities)) => entities
896 .iter()
897 .filter_map(|entity| match entity {
898 crate::agent::GameEntity::Card(card) => Some(*card),
899 crate::agent::GameEntity::Player(_) => None,
900 })
901 .collect(),
902 _ => Vec::new(),
903 }
904 }
905
906 pub fn get_triggering_players(&self, key: AbilityKey) -> Vec<PlayerId> {
908 match self.get_triggering_value(key) {
909 Some(AbilityValue::Player(player)) => vec![*player],
910 Some(AbilityValue::Players(players)) => players.clone(),
911 Some(AbilityValue::GameEntities(entities)) => entities
912 .iter()
913 .filter_map(|entity| match entity {
914 crate::agent::GameEntity::Player(player) => Some(*player),
915 crate::agent::GameEntity::Card(_) => None,
916 })
917 .collect(),
918 _ => Vec::new(),
919 }
920 }
921
922 pub fn get_triggering_object<K: TriggerKeyInput>(&self, key: K) -> Option<&str> {
925 key.into_ability_key()
926 .and_then(|parsed| self.get_triggering_value(parsed))
927 .and_then(|value| match value {
928 AbilityValue::String(raw) => Some(raw.as_str()),
929 _ => None,
930 })
931 }
932
933 pub fn reset_triggering_objects(&mut self) {
936 self.trigger_objects.clear();
937 }
938
939 pub fn reset_once_resolved(&mut self) {
942 self.clear_targets();
943 self.reset_triggering_objects();
944 self.reset_paid_hash();
945 self.x_mana_cost_paid = 0;
946 self.kick_count = 0;
947 self.replicate_count = 0;
948 self.optional_generic_cost_paid = false;
949 self.discarded_cost_cards.clear();
950 self.optional_costs.clear();
951 self.convoke_tapped.clear();
952 self.spliced_cards.clear();
953 self.announce_vars.clear();
954 self.sacrificed_as_emerge = None;
955 self.sacrificed_as_offering = None;
956 }
957
958 pub fn yield_key(&self) -> String {
963 let api_str = self.api.map(|a| format!("{a:?}")).unwrap_or_default();
964 let source_str = self.source.map(|s| format!("{}", s.0)).unwrap_or_default();
965 format!("{api_str}_{source_str}")
966 }
967
968 pub fn rebuilt_description(&self) -> String {
971 if !self.description.is_empty() {
972 return self.description.clone();
973 }
974 if let Some(desc) = self.ir.sp_desc_text.as_deref() {
975 return desc.to_string();
976 }
977 self.ability_text.clone()
978 }
979
980 pub fn to_unsuppressed_string(&self) -> String {
983 self.rebuilt_description()
984 }
985
986 pub fn has_additional_ability<K: TriggerKeyInput>(&self, key: K) -> bool {
991 key.into_ability_key()
992 .map(|parsed| self.trigger_spell_abilities.contains_key(&parsed))
993 .unwrap_or(false)
994 }
995
996 pub fn get_additional_ability<K: TriggerKeyInput>(&self, key: K) -> Option<&SpellAbility> {
999 key.into_ability_key()
1000 .and_then(|parsed| self.trigger_spell_abilities.get(&parsed))
1001 }
1002
1003 pub fn set_additional_ability<K: TriggerKeyInput>(&mut self, key: K, ability: SpellAbility) {
1006 if let Some(parsed) = key.into_ability_key() {
1007 self.trigger_spell_abilities.insert(parsed, ability);
1008 }
1009 }
1010
1011 pub fn append_sub_ability(&mut self, sub: SpellAbility) {
1014 if self.sub_ability.is_none() {
1015 self.sub_ability = Some(Box::new(sub));
1016 } else {
1017 let mut current = self.sub_ability.as_deref_mut();
1019 while let Some(sa) = current {
1020 if sa.sub_ability.is_none() {
1021 sa.sub_ability = Some(Box::new(sub));
1022 return;
1023 }
1024 current = sa.sub_ability.as_deref_mut();
1025 }
1026 }
1027 }
1028
1029 pub fn copy(&self) -> Self {
1034 crate::perf::increment(crate::perf::Metric::SpellAbilityClones, 1);
1035 self.clone()
1036 }
1037
1038 pub fn copy_for_player(&self, activ: PlayerId) -> Self {
1039 crate::perf::increment(crate::perf::Metric::SpellAbilityClones, 1);
1040 let mut clone = self.clone();
1041 clone.activating_player = activ;
1042 clone
1043 }
1044
1045 pub fn copy_with_host_lki(&self, host: crate::card::Card, lki: bool) -> Self {
1046 self.copy_with_host_activating_lki_keep_text_changes(
1047 host,
1048 self.activating_player,
1049 lki,
1050 false,
1051 )
1052 }
1053
1054 pub fn copy_with_host_lki_keep_text_changes(
1055 &self,
1056 host: crate::card::Card,
1057 lki: bool,
1058 keep_text_changes: bool,
1059 ) -> Self {
1060 self.copy_with_host_activating_lki_keep_text_changes(
1061 host,
1062 self.activating_player,
1063 lki,
1064 keep_text_changes,
1065 )
1066 }
1067
1068 pub fn copy_with_host_activating_lki(
1069 &self,
1070 host: crate::card::Card,
1071 activ: PlayerId,
1072 lki: bool,
1073 ) -> Self {
1074 self.copy_with_host_activating_lki_keep_text_changes(host, activ, lki, false)
1075 }
1076
1077 pub fn copy_with_host_activating_lki_keep_text_changes(
1078 &self,
1079 host: crate::card::Card,
1080 activ: PlayerId,
1081 lki: bool,
1082 keep_text_changes: bool,
1083 ) -> Self {
1084 crate::perf::increment(crate::perf::Metric::SpellAbilityClones, 1);
1085 let mut clone = self.clone();
1086 clone.id = if lki {
1087 self.id
1088 } else {
1089 next_spell_ability_id()
1090 };
1091
1092 clone.source = Some(host.id);
1093 clone.may_choose_new_targets = false;
1094 clone.trigger_objects = self.trigger_objects.clone();
1095 if !lki {
1096 clone.replacing_objects = HashMap::new();
1097 }
1098
1099 clone.pay_costs = self.pay_costs.clone();
1100 if self.mana_part.is_some() {
1101 clone.mana_part = self.mana_part.clone();
1102 }
1103
1104 clone.optional_keyword_amounts = self.optional_keyword_amounts.clone();
1105 clone.damage_map = self.damage_map.clone();
1106 clone.prevent_map = self.prevent_map.clone();
1107 clone.change_zone_table = self.change_zone_table.clone();
1108 clone.paying_mana = self.paying_mana.clone();
1109 clone.paid_abilities = Vec::new();
1110 clone.paid_hash = self.paid_hash.clone();
1111
1112 if self.uses_targeting() {
1113 clone.target_chosen = self.target_chosen.clone();
1114 }
1115
1116 clone.trigger_spell_abilities = HashMap::new();
1117 clone.additional_ability_lists = HashMap::new();
1118
1119 if let Some(sub_ability) = &self.sub_ability {
1120 clone.sub_ability = Some(Box::new(
1121 sub_ability.copy_with_host_activating_lki_keep_text_changes(
1122 host.clone(),
1123 activ,
1124 lki,
1125 keep_text_changes,
1126 ),
1127 ));
1128 }
1129
1130 for (name, ability) in &self.trigger_spell_abilities {
1131 clone.trigger_spell_abilities.insert(
1132 *name,
1133 ability.copy_with_host_activating_lki_keep_text_changes(
1134 host.clone(),
1135 activ,
1136 lki,
1137 keep_text_changes,
1138 ),
1139 );
1140 }
1141
1142 for (name, abilities) in &self.additional_ability_lists {
1143 clone.additional_ability_lists.insert(
1144 name.clone(),
1145 abilities
1146 .iter()
1147 .map(|ability| {
1148 ability.copy_with_host_activating_lki_keep_text_changes(
1149 host.clone(),
1150 activ,
1151 lki,
1152 keep_text_changes,
1153 )
1154 })
1155 .collect(),
1156 );
1157 }
1158
1159 clone.restriction = self.restriction.clone();
1160 clone.condition = self.condition.clone();
1161 clone.activating_player = activ;
1162
1163 let _ = keep_text_changes;
1164 clone
1165 }
1166
1167 pub fn copy_with_no_mana_cost(&self) -> Self {
1170 crate::perf::increment(crate::perf::Metric::SpellAbilityClones, 1);
1171 let mut copied = self.clone();
1172 copied.pay_costs = None;
1173 copied
1174 }
1175
1176 pub fn copy_with_defined_cost(&self, cost: &str) -> Self {
1179 crate::perf::increment(crate::perf::Metric::SpellAbilityClones, 1);
1180 let mut copied = self.clone();
1181 copied.pay_costs = Some(parse_cost(cost));
1182 copied
1183 }
1184
1185 pub fn copy_with_mana_cost_replaced(&self, old: &str, new: &str) -> Self {
1188 crate::perf::increment(crate::perf::Metric::SpellAbilityClones, 1);
1189 let mut copied = self.clone();
1190 if let Some(ref cost) = self.pay_costs {
1191 let cost_str = format!("{cost:?}");
1192 let replaced = cost_str.replace(old, new);
1193 copied.pay_costs = Some(parse_cost(&replaced));
1194 }
1195 copied
1196 }
1197
1198 pub fn can_target(&self, card: CardId, game: &GameState) -> bool {
1203 if let Some(ref tr) = self.target_restrictions {
1204 tr.has_candidates(game, self.activating_player, self.source)
1205 && self
1206 .ir
1207 .targets_with_defined_controller_text
1208 .as_deref()
1209 .map(|defined| {
1210 crate::ability::ability_utils::resolve_defined_players_with_sa(
1211 defined,
1212 self,
1213 self.activating_player,
1214 game,
1215 )
1216 })
1217 .map(|players| {
1218 players.is_empty() || players.contains(&game.card(card).controller)
1219 })
1220 .unwrap_or(true)
1221 && target_restrictions::can_be_targeted_by_sa(
1222 game,
1223 card,
1224 self.activating_player,
1225 self,
1226 )
1227 } else {
1228 false
1229 }
1230 }
1231
1232 pub fn reset_targets(&mut self) {
1235 self.clear_targets();
1236 }
1237
1238 pub fn add_divided_allocation(&mut self, card: CardId, amount: i32) {
1241 self.target_chosen.add_divided_allocation(card, amount);
1242 }
1243
1244 pub fn reset_first_target(&mut self) {
1247 self.target_chosen = TargetChoices::default();
1248 }
1249
1250 pub fn can_add_more_target(&self, game: &GameState) -> bool {
1253 if let Some(ref tr) = self.target_restrictions {
1254 let max = tr.get_max_targets(game, self);
1255 let current = self.target_chosen.all_target_cards().len() as i32
1256 + self.target_chosen.all_target_players().len() as i32;
1257 current < max
1258 } else {
1259 false
1260 }
1261 }
1262
1263 pub fn find_targeted_cards(&self) -> Vec<CardId> {
1266 let mut cards = Vec::new();
1267 cards.extend(self.target_chosen.all_target_cards());
1268 let mut current = self.sub_ability.as_deref();
1269 while let Some(sub) = current {
1270 cards.extend(sub.target_chosen.all_target_cards());
1271 current = sub.sub_ability.as_deref();
1272 }
1273 cards
1274 }
1275
1276 pub fn find_targeted_players(&self) -> Vec<PlayerId> {
1279 let mut players = Vec::new();
1280 players.extend(self.target_chosen.all_target_players());
1281 let mut current = self.sub_ability.as_deref();
1282 while let Some(sub) = current {
1283 for player in sub.target_chosen.all_target_players() {
1284 if !players.contains(&player) {
1285 players.push(player);
1286 }
1287 }
1288 current = sub.sub_ability.as_deref();
1289 }
1290 players
1291 }
1292
1293 pub fn can_target_spell_ability(&self) -> bool {
1296 matches!(
1297 self.target_restrictions.as_ref().map(|tr| &tr.target_kind),
1298 Some(TargetKind::Spell)
1299 )
1300 }
1301
1302 pub fn setup_new_targets(
1305 &mut self,
1306 game: &GameState,
1307 agents: &mut [Box<dyn PlayerAgent>],
1308 mana_pools: &[ManaPool],
1309 ) -> bool {
1310 self.clear_targets();
1311 self.setup_targets(game, agents, mana_pools)
1312 }
1313
1314 pub fn clear_pips_to_reduce(&mut self) {
1319 self.pips_to_reduce.clear();
1320 }
1321
1322 pub fn add_tapped_for_convoke(&mut self, card: CardId) {
1325 self.convoke_tapped.push(card);
1326 }
1327
1328 pub fn clear_tapped_for_convoke(&mut self) {
1331 self.convoke_tapped.clear();
1332 }
1333
1334 pub fn reset_sacrificed_as_emerge(&mut self) {
1337 self.sacrificed_as_emerge = None;
1338 }
1339
1340 pub fn reset_sacrificed_as_offering(&mut self) {
1343 self.sacrificed_as_offering = None;
1344 }
1345
1346 pub fn add_spliced_cards(&mut self, cards: Vec<CardId>) {
1351 self.spliced_cards.extend(cards);
1352 }
1353
1354 pub fn known_determine_defined(&self) -> bool {
1359 match self.defined() {
1360 Some(defined) => matches!(
1361 defined,
1362 "Self"
1363 | "You"
1364 | "Targeted"
1365 | "TargetedPlayer"
1366 | "Remembered"
1367 | "ParentTarget"
1368 | "SourceController"
1369 | "Imprinted"
1370 ),
1371 None => true,
1372 }
1373 }
1374
1375 pub fn undo(&mut self) -> bool {
1380 self.clear_tapped_for_convoke();
1381 self.reset_sacrificed_as_emerge();
1382 self.reset_sacrificed_as_offering();
1383 self.reset_paid_hash();
1384 self.clear_mana_paid();
1385 true
1386 }
1387
1388 pub fn add_announce_var(&mut self, key: &str, value: i32) {
1393 self.announce_vars.insert(key.to_string(), value);
1394 }
1395
1396 pub fn can_be_targeted_by(&self, _sa: &SpellAbility) -> bool {
1401 if self.is_spell {
1405 return !self.cant_be_copied();
1406 }
1407 true
1408 }
1409
1410 pub fn has_property(&self, property: &str) -> bool {
1415 match property {
1416 "Spell" => self.is_spell,
1417 "Trigger" => self.is_trigger,
1418 "Activated" => self.is_activated,
1419 "ManaAbility" => self.is_mana_ability,
1420 "Optional" => self.ir.optional,
1421 "Mandatory" => self.ir.mandatory,
1422 "Tapped" => self.ir.tapped,
1423 "Hidden" => self.ir.hidden,
1424 "FaceDown" => self.ir.face_down,
1425 "ExileFaceDown" => self.ir.exile_face_down,
1426 "Transformed" => self.ir.transformed,
1427 "AtRandom" => self.ir.at_random,
1428 "Imprint" => self.ir.imprint,
1429 "Morph" => self.ir.morph,
1430 "MorphUp" => self.ir.morph_up,
1431 "Megamorph" => self.ir.megamorph,
1432 "PwAbility" => self.ir.pw_ability,
1433 "Flash" => self.ir.flash,
1434 "SplitSecond" => self.ir.split_second,
1435 _ => false,
1436 }
1437 }
1438
1439 pub fn is_last_chapter(&self, game: &crate::game::GameState) -> bool {
1440 let Some(source) = self.trigger_source.or(self.source) else {
1441 return false;
1442 };
1443 let Some(trigger_id) = self.source_trigger_id else {
1444 return false;
1445 };
1446 let card = game.card(source);
1447 card.triggers
1448 .iter()
1449 .find(|trigger| trigger.id == trigger_id)
1450 .is_some_and(|trigger| trigger.is_last_chapter(card))
1451 }
1452
1453 pub fn tracks_mana_spent(&self) -> bool {
1456 self.ir.track_mana_spent
1457 }
1458
1459 pub fn apply_text_change(&mut self, original: &str, replacement: &str) {
1464 if original == replacement {
1465 return;
1466 }
1467 self.description = self.description.replace(original, replacement);
1468 self.stack_description = self.stack_description.replace(original, replacement);
1469 if let Some(ref mut tr) = self.target_restrictions {
1470 tr.apply_target_text_changes(&[(original, replacement)]);
1471 }
1472
1473 if let Some(sub_ability) = self.sub_ability.as_deref_mut() {
1474 sub_ability.apply_text_change(original, replacement);
1475 }
1476
1477 for ability in self.trigger_spell_abilities.values_mut() {
1478 ability.apply_text_change(original, replacement);
1479 }
1480 }
1481
1482 pub fn apply_text_change_intrinsic(&mut self, original: &str, replacement: &str) {
1485 self.apply_text_change(original, replacement);
1486 }
1487
1488 pub fn apply_text_changes(&mut self, pairs: &[(String, String)]) {
1490 for (original, replacement) in pairs {
1491 self.apply_text_change(original, replacement);
1492 }
1493 }
1494
1495 pub fn apply_text_changes_intrinsic(
1497 &mut self,
1498 color_map: &HashMap<String, String>,
1499 type_map: &HashMap<String, String>,
1500 ) {
1501 for (original, replacement) in color_map.iter().chain(type_map.iter()) {
1502 self.apply_text_change_intrinsic(original, replacement);
1503 }
1504 }
1505
1506 pub fn set_host_card(&mut self, card: crate::card::Card) {
1508 self.set_host_card_id(card.id);
1509 }
1510
1511 pub fn set_host_card_id(&mut self, card_id: CardId) {
1512 self.source = Some(card_id);
1513 if self.original_host.is_none() {
1514 self.original_host = Some(card_id);
1515 }
1516
1517 if let Some(sub_ability) = self.sub_ability.as_deref_mut() {
1518 sub_ability.set_host_card_id(card_id);
1519 }
1520
1521 for ability in self.trigger_spell_abilities.values_mut() {
1522 ability.set_host_card_id(card_id);
1523 }
1524 }
1525
1526 pub fn set_keyword(&mut self, keyword: crate::keyword::keyword_interface::KeywordInterface) {
1528 if let Some(sub_ability) = self.sub_ability.as_deref_mut() {
1529 sub_ability.set_keyword(keyword.clone());
1530 }
1531
1532 for ability in self.trigger_spell_abilities.values_mut() {
1533 ability.set_keyword(keyword.clone());
1534 }
1535 }
1536
1537 #[allow(clippy::only_used_in_recursion)]
1539 pub fn set_card_state(&mut self, state: &crate::card::card_state::CardState) {
1540 if let Some(sub_ability) = self.sub_ability.as_deref_mut() {
1541 sub_ability.set_card_state(state);
1542 }
1543
1544 for ability in self.trigger_spell_abilities.values_mut() {
1545 ability.set_card_state(state);
1546 }
1547 }
1548
1549 pub fn set_intrinsic(&mut self, intrinsic: bool) {
1551 self.intrinsic = intrinsic;
1552
1553 if let Some(sub_ability) = self.sub_ability.as_deref_mut() {
1554 if sub_ability.is_intrinsic() != intrinsic {
1555 sub_ability.set_intrinsic(intrinsic);
1556 }
1557 }
1558
1559 for ability in self.trigger_spell_abilities.values_mut() {
1560 if ability.is_intrinsic() != intrinsic {
1561 ability.set_intrinsic(intrinsic);
1562 }
1563 }
1564 }
1565
1566 pub fn is_intrinsic(&self) -> bool {
1567 self.intrinsic
1568 }
1569
1570 pub fn get_amount_life_paid(&self) -> i32 {
1572 self.paid_life_amount
1573 }
1574
1575 pub fn set_amount_life_paid(&mut self, value: i32) {
1577 self.paid_life_amount = value;
1578 }
1579
1580 pub fn calculate_score_for_mana_ability(&self) -> i32 {
1585 if !self.is_mana_ability {
1586 return 0;
1587 }
1588 let base = self.total_amount_of_mana_generated();
1589 let restriction_penalty = if self.restriction.variables.sorcery_speed() {
1591 -1
1592 } else {
1593 0
1594 };
1595 base + restriction_penalty
1596 }
1597
1598 pub fn can_cast_timing(&self, game: &GameState) -> bool {
1603 let can_cast_sorcery = game.turn.phase.is_main()
1604 && game.stack.is_empty()
1605 && game.turn.active_player == self.activating_player;
1606
1607 if !self.is_spell && !self.is_activated {
1609 return true;
1610 }
1611
1612 if can_cast_sorcery || self.with_flash(game) {
1613 return true;
1614 }
1615
1616 if self.is_spell {
1618 return false;
1619 }
1620
1621 if self.is_activated {
1624 return !self.ir.pw_ability && !self.restriction.variables.sorcery_speed();
1625 }
1626
1627 true
1628 }
1629
1630 pub fn with_flash(&self, game: &GameState) -> bool {
1633 if self.restriction.variables.instant_speed() {
1634 return true;
1635 }
1636 if self.ir.flash {
1637 return true;
1638 }
1639 if let Some(card_id) = self.source {
1640 let card = game.card(card_id);
1641 if ((self.is_spell || self.is_land_ability) && card.type_line.is_instant())
1642 || card.has_keyword("Flash")
1643 {
1644 return true;
1645 }
1646 return crate::staticability::static_ability_cast_with_flash::any_with_flash_for_card(
1647 &game.cards,
1648 card,
1649 self.activating_player,
1650 );
1651 }
1652 false
1653 }
1654
1655 pub fn check_restrictions(&self, game: &GameState) -> bool {
1658 self.can_play(game)
1659 }
1660
1661 pub fn add_rollback_effect(&mut self, effect: String) {
1666 self.rollback_effects.push(effect);
1667 }
1668
1669 pub fn rollback(&mut self) -> bool {
1672 let had_effects = !self.rollback_effects.is_empty();
1673 self.rollback_effects.clear();
1674 had_effects
1675 }
1676
1677 pub fn has_optional_keyword_amount(&self, keyword: &str) -> bool {
1682 self.optional_keyword_amounts.contains_key(keyword)
1683 }
1684
1685 pub fn clear_optional_keyword_amount(&mut self) {
1688 self.optional_keyword_amounts.clear();
1689 }
1690
1691 pub fn clear_last_state(&mut self) {
1694 self.last_state.clear();
1695 }
1696
1697 pub fn set_triggering_object<K: TriggerKeyInput, V: Into<AbilityValue>>(
1702 &mut self,
1703 key: K,
1704 value: V,
1705 ) {
1706 if let Some(parsed) = key.into_ability_key() {
1707 self.trigger_objects.insert(parsed, value.into());
1708 }
1709 }
1710
1711 pub fn set_triggering_value<V: Into<AbilityValue>>(&mut self, key: AbilityKey, value: V) {
1713 self.trigger_objects.insert(key, value.into());
1714 }
1715
1716 pub fn set_triggering_spell_ability<K: TriggerKeyInput>(
1719 &mut self,
1720 key: K,
1721 value: SpellAbility,
1722 ) {
1723 if let Some(parsed) = key.into_ability_key() {
1724 self.trigger_spell_abilities.insert(parsed, value);
1725 }
1726 }
1727
1728 pub fn get_triggering_spell_ability<K: TriggerKeyInput>(
1730 &self,
1731 key: K,
1732 ) -> Option<&SpellAbility> {
1733 key.into_ability_key()
1734 .and_then(|parsed| self.trigger_spell_abilities.get(&parsed))
1735 }
1736
1737 pub fn update_triggering_object<K: TriggerKeyInput, V: Into<AbilityValue>>(
1740 &mut self,
1741 key: K,
1742 value: V,
1743 ) {
1744 self.set_triggering_object(key, value);
1745 }
1746
1747 pub fn update_target(&mut self, old: CardId, new: CardId) {
1752 self.target_chosen.replace_target_card(old, new);
1753 }
1754
1755 pub fn targets_single_target(&self) -> bool {
1758 if let Some(ref tr) = self.target_restrictions {
1759 tr.max_targets == "1"
1760 } else {
1761 false
1762 }
1763 }
1764
1765 pub fn gets_var_operand(&self) -> Option<&str> {
1772 self.condition
1773 .variables
1774 .gets_var_operand()
1775 .or_else(|| self.restriction.variables.gets_var_operand())
1776 }
1777
1778 pub fn gets_var_operand2(&self) -> Option<&str> {
1781 self.condition
1782 .variables
1783 .gets_var_operand2()
1784 .or_else(|| self.restriction.variables.gets_var_operand2())
1785 }
1786
1787 pub fn sets_var_operand(&mut self, value: &str) {
1790 self.condition.variables.sets_var_operand(value);
1791 self.restriction.variables.sets_var_operand(value);
1792 }
1793
1794 pub fn sets_var_operand2(&mut self, value: &str) {
1797 self.condition.variables.sets_var_operand2(value);
1798 self.restriction.variables.sets_var_operand2(value);
1799 }
1800
1801 pub fn gets_var_to_check(&self) -> Option<&str> {
1804 self.condition
1805 .variables
1806 .gets_var_to_check()
1807 .or_else(|| self.restriction.variables.gets_var_to_check())
1808 }
1809
1810 pub fn gets_var_to_check2(&self) -> Option<&str> {
1813 self.condition
1814 .variables
1815 .gets_var_to_check2()
1816 .or_else(|| self.restriction.variables.gets_var_to_check2())
1817 }
1818
1819 pub fn sets_var_to_check(&mut self, value: &str) {
1822 self.condition.variables.sets_var_to_check(value);
1823 self.restriction.variables.sets_var_to_check(value);
1824 }
1825
1826 pub fn sets_var_to_check2(&mut self, value: &str) {
1829 self.condition.variables.sets_var_to_check2(value);
1830 self.restriction.variables.sets_var_to_check2(value);
1831 }
1832
1833 pub fn gets_var_operator(&self) -> Option<&str> {
1836 self.condition
1837 .variables
1838 .gets_var_operator()
1839 .or_else(|| self.restriction.variables.gets_var_operator())
1840 }
1841
1842 pub fn gets_var_operator2(&self) -> Option<&str> {
1845 self.condition
1846 .variables
1847 .gets_var_operator2()
1848 .or_else(|| self.restriction.variables.gets_var_operator2())
1849 }
1850
1851 pub fn sets_var_operator(&mut self, value: &str) {
1854 self.condition.variables.sets_var_operator(value);
1855 self.restriction.variables.sets_var_operator(value);
1856 }
1857
1858 pub fn sets_var_operator2(&mut self, value: &str) {
1861 self.condition.variables.sets_var_operator2(value);
1862 self.restriction.variables.sets_var_operator2(value);
1863 }
1864}
1865
1866pub use crate::ability::ability_factory::build_spell_ability;
1869pub use crate::ability::ability_factory::build_spell_ability_for_card_cast;
1870pub use crate::ability::ability_factory::build_spell_ability_from_host_card;
1871
1872pub fn has_split_second_on_stack(game: &GameState) -> bool {
1877 for entry in game.stack.iter() {
1878 if entry.spell_ability.ir.split_second {
1879 return true;
1880 }
1881 if let Some(card_id) = entry.spell_ability.source {
1882 let card = game.card(card_id);
1883 if card.has_keyword("Split second") {
1884 return true;
1885 }
1886 }
1887 }
1888 false
1889}
1890
1891pub fn choose_targets_by_kind(
1892 agent: &mut dyn PlayerAgent,
1893 sa: &mut SpellAbility,
1894 game: &GameState,
1895 mana_pools: &[ManaPool],
1896) -> bool {
1897 use crate::card::card_util;
1898
1899 let tr = match &sa.target_restrictions {
1900 Some(tr) => tr,
1901 None => return true,
1902 };
1903
1904 let player = sa.targeting_player.unwrap_or(sa.activating_player);
1905
1906 let min_targets = tr.get_min_targets(game, sa);
1907 let max_targets = tr.get_max_targets(game, sa);
1908 if max_targets <= 0 {
1909 return true;
1910 }
1911
1912 if !matches!(tr.target_kind, TargetKind::CardInZone { .. })
1913 && !tr.has_candidates(game, player, sa.source)
1914 {
1915 return min_targets <= 0;
1916 }
1917
1918 sa.target_chosen.target_card = None;
1919 sa.target_chosen.target_card_zone_timestamp = None;
1920 sa.target_chosen.divided_map.clear();
1921
1922 match &tr.target_kind {
1923 TargetKind::None => {}
1924 TargetKind::Player => {
1925 agent.snapshot_state(game, mana_pools);
1926 let is_opponent_only = tr
1927 .valid_tgts
1928 .iter()
1929 .any(|v| v.eq_ignore_ascii_case("Opponent"));
1930 let valid_players: Vec<PlayerId> = game
1931 .alive_players()
1932 .into_iter()
1933 .filter(|&pid| !is_opponent_only || pid != player)
1934 .collect();
1935 if max_targets > 1 {
1936 let mut chosen = Vec::new();
1937 while (chosen.len() as i32) < max_targets {
1938 let Some(pid) = agent.choose_target_player(player, &valid_players, Some(&*sa))
1939 else {
1940 break;
1941 };
1942 if !chosen.contains(&pid) {
1943 chosen.push(pid);
1944 }
1945 if chosen.len() == valid_players.len() {
1946 break;
1947 }
1948 }
1949 sa.target_chosen.target_player = chosen.first().copied();
1950 sa.target_chosen.additional_target_players = chosen.into_iter().skip(1).collect();
1951 } else {
1952 sa.target_chosen.target_player =
1953 agent.choose_target_player(player, &valid_players, Some(&*sa));
1954 }
1955 }
1956 TargetKind::Any => {
1957 let valid_players: Vec<PlayerId> =
1958 if target_restrictions::any_target_allows_players(&tr.valid_tgts) {
1959 game.alive_players().into_iter().collect()
1960 } else {
1961 Vec::new()
1962 };
1963 let valid_cards: Vec<CardId> = card_util::get_valid_cards_to_target(game, sa);
1964 agent.snapshot_state(game, mana_pools);
1965 match agent.choose_target_any(player, &valid_players, &valid_cards, Some(&*sa)) {
1966 crate::agent::TargetChoice::Player(pid) => {
1967 sa.target_chosen.target_player = Some(pid)
1968 }
1969 crate::agent::TargetChoice::Card(cid) => {
1970 sa.target_chosen.target_card = Some(cid);
1971 sa.target_chosen.target_card_zone_timestamp =
1972 Some(game.card(cid).zone_timestamp);
1973 }
1974 crate::agent::TargetChoice::None => {}
1975 }
1976 }
1977 TargetKind::Creature(_) => {
1978 let valid: Vec<CardId> = card_util::get_valid_cards_to_target(game, sa)
1979 .into_iter()
1980 .filter(|&cid| target_allowed_by_defined_controller(game, sa, cid))
1981 .collect();
1982 agent.snapshot_state(game, mana_pools);
1983 if max_targets > 1 {
1984 let chosen = agent.choose_cards_for_effect(
1985 player,
1986 &valid,
1987 min_targets.max(0) as usize,
1988 max_targets as usize,
1989 );
1990 if let Some(&first) = chosen.first() {
1991 sa.target_chosen.target_card = Some(first);
1992 sa.target_chosen.target_card_zone_timestamp =
1993 Some(game.card(first).zone_timestamp);
1994 for &extra in chosen.iter().skip(1) {
1995 sa.target_chosen.divided_map.insert(extra, 0);
1996 }
1997 }
1998 } else {
1999 sa.target_chosen.target_card = agent.choose_target_card(player, &valid, Some(&*sa));
2000 if let Some(cid) = sa.target_chosen.target_card {
2001 sa.target_chosen.target_card_zone_timestamp =
2002 Some(game.card(cid).zone_timestamp);
2003 }
2004 }
2005 }
2006 TargetKind::Permanent(_) => {
2007 let valid: Vec<CardId> = card_util::get_valid_cards_to_target(game, sa)
2008 .into_iter()
2009 .filter(|&cid| target_allowed_by_defined_controller(game, sa, cid))
2010 .collect();
2011 agent.snapshot_state(game, mana_pools);
2012 if max_targets > 1 {
2013 let chosen = agent.choose_cards_for_effect(
2014 player,
2015 &valid,
2016 min_targets.max(0) as usize,
2017 max_targets as usize,
2018 );
2019 if let Some(&first) = chosen.first() {
2020 sa.target_chosen.target_card = Some(first);
2021 sa.target_chosen.target_card_zone_timestamp =
2022 Some(game.card(first).zone_timestamp);
2023 for &extra in chosen.iter().skip(1) {
2024 sa.target_chosen.divided_map.insert(extra, 0);
2025 }
2026 }
2027 } else {
2028 sa.target_chosen.target_card = agent.choose_target_card(player, &valid, Some(&*sa));
2029 if let Some(cid) = sa.target_chosen.target_card {
2030 sa.target_chosen.target_card_zone_timestamp =
2031 Some(game.card(cid).zone_timestamp);
2032 }
2033 }
2034 }
2035 TargetKind::CardInZone { zone, .. } => {
2036 let valid: Vec<CardId> = card_util::get_valid_cards_to_target(game, sa)
2037 .into_iter()
2038 .filter(|&cid| target_allowed_by_defined_controller(game, sa, cid))
2039 .collect();
2040 if valid.is_empty() {
2041 return min_targets <= 0;
2042 }
2043 agent.snapshot_state(game, mana_pools);
2044 if max_targets > 1 {
2045 let chosen = agent.choose_cards_for_effect(
2046 player,
2047 &valid,
2048 min_targets.max(0) as usize,
2049 max_targets as usize,
2050 );
2051 if let Some(&first) = chosen.first() {
2052 sa.target_chosen.target_card = Some(first);
2053 sa.target_chosen.target_card_zone_timestamp =
2054 Some(game.card(first).zone_timestamp);
2055 for &extra in chosen.iter().skip(1) {
2056 sa.target_chosen.divided_map.insert(extra, 0);
2057 }
2058 }
2059 } else {
2060 sa.target_chosen.target_card =
2061 agent.choose_target_card_from_zone(player, *zone, &valid, Some(&*sa));
2062 if let Some(cid) = sa.target_chosen.target_card {
2063 sa.target_chosen.target_card_zone_timestamp =
2064 Some(game.card(cid).zone_timestamp);
2065 }
2066 }
2067 }
2068 TargetKind::Spell => {
2069 let valid = target_restrictions::get_all_candidates_spells(game);
2070 let valid = if let Some(ref restrictions) = sa.target_restrictions {
2071 target_restrictions::filter_spells_for_target_restrictions(
2072 game,
2073 &valid,
2074 restrictions,
2075 )
2076 } else {
2077 valid
2078 };
2079 agent.snapshot_state(game, mana_pools);
2080 sa.target_chosen.target_stack_entry =
2081 agent.choose_target_spell(player, &valid, sa.source);
2082 }
2083 }
2084
2085 let chosen_targets = sa.target_chosen.all_target_cards().len() as i32
2086 + sa.target_chosen.all_target_players().len() as i32
2087 + i32::from(sa.target_chosen.target_stack_entry.is_some());
2088 chosen_targets >= min_targets
2089}
2090
2091fn target_allowed_by_defined_controller(
2092 game: &GameState,
2093 sa: &SpellAbility,
2094 card_id: CardId,
2095) -> bool {
2096 let Some(defined) = sa.ir.targets_with_defined_controller_text.as_deref() else {
2097 return true;
2098 };
2099 let players = crate::ability::ability_utils::resolve_defined_players_with_sa(
2100 defined,
2101 sa,
2102 sa.activating_player,
2103 game,
2104 );
2105 players.is_empty() || players.contains(&game.card(card_id).controller)
2106}
2107
2108fn choose_targeting_player(
2109 sa: &SpellAbility,
2110 game: &GameState,
2111 agents: &mut [Box<dyn PlayerAgent>],
2112) -> Option<PlayerId> {
2113 if let Some(defined) = sa.ir.targeting_player_text.as_deref() {
2114 let candidates = crate::ability::ability_utils::resolve_defined_players_with_sa(
2115 defined,
2116 sa,
2117 sa.activating_player,
2118 game,
2119 );
2120 if candidates.is_empty() {
2121 return None;
2122 }
2123 return agents[sa.activating_player.index()].choose_target_player(
2124 sa.activating_player,
2125 &candidates,
2126 None,
2127 );
2128 }
2129 Some(sa.activating_player)
2130}
2131
2132pub use crate::zone::magic_stack::{MagicStack, StackEntry};