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 _ => None,
866 }
867 }
868
869 pub fn get_triggering_player(&self, key: AbilityKey) -> Option<PlayerId> {
871 match self.get_triggering_value(key) {
872 Some(AbilityValue::Player(player)) => Some(*player),
873 Some(AbilityValue::Players(players)) => players.first().copied(),
874 _ => None,
875 }
876 }
877
878 pub fn get_triggering_cards(&self, key: AbilityKey) -> Vec<CardId> {
880 match self.get_triggering_value(key) {
881 Some(AbilityValue::Card(card)) => vec![*card],
882 Some(AbilityValue::Cards(cards)) => cards.clone(),
883 _ => Vec::new(),
884 }
885 }
886
887 pub fn get_triggering_players(&self, key: AbilityKey) -> Vec<PlayerId> {
889 match self.get_triggering_value(key) {
890 Some(AbilityValue::Player(player)) => vec![*player],
891 Some(AbilityValue::Players(players)) => players.clone(),
892 _ => Vec::new(),
893 }
894 }
895
896 pub fn get_triggering_object<K: TriggerKeyInput>(&self, key: K) -> Option<&str> {
899 key.into_ability_key()
900 .and_then(|parsed| self.get_triggering_value(parsed))
901 .and_then(|value| match value {
902 AbilityValue::String(raw) => Some(raw.as_str()),
903 _ => None,
904 })
905 }
906
907 pub fn reset_triggering_objects(&mut self) {
910 self.trigger_objects.clear();
911 }
912
913 pub fn reset_once_resolved(&mut self) {
916 self.clear_targets();
917 self.reset_triggering_objects();
918 self.reset_paid_hash();
919 self.x_mana_cost_paid = 0;
920 self.kick_count = 0;
921 self.replicate_count = 0;
922 self.optional_generic_cost_paid = false;
923 self.discarded_cost_cards.clear();
924 self.optional_costs.clear();
925 self.convoke_tapped.clear();
926 self.spliced_cards.clear();
927 self.announce_vars.clear();
928 self.sacrificed_as_emerge = None;
929 self.sacrificed_as_offering = None;
930 }
931
932 pub fn yield_key(&self) -> String {
937 let api_str = self.api.map(|a| format!("{:?}", a)).unwrap_or_default();
938 let source_str = self.source.map(|s| format!("{}", s.0)).unwrap_or_default();
939 format!("{}_{}", api_str, source_str)
940 }
941
942 pub fn rebuilt_description(&self) -> String {
945 if !self.description.is_empty() {
946 return self.description.clone();
947 }
948 if let Some(desc) = self.ir.sp_desc_text.as_deref() {
949 return desc.to_string();
950 }
951 self.ability_text.clone()
952 }
953
954 pub fn to_unsuppressed_string(&self) -> String {
957 self.rebuilt_description()
958 }
959
960 pub fn has_additional_ability<K: TriggerKeyInput>(&self, key: K) -> bool {
965 key.into_ability_key()
966 .map(|parsed| self.trigger_spell_abilities.contains_key(&parsed))
967 .unwrap_or(false)
968 }
969
970 pub fn get_additional_ability<K: TriggerKeyInput>(&self, key: K) -> Option<&SpellAbility> {
973 key.into_ability_key()
974 .and_then(|parsed| self.trigger_spell_abilities.get(&parsed))
975 }
976
977 pub fn set_additional_ability<K: TriggerKeyInput>(&mut self, key: K, ability: SpellAbility) {
980 if let Some(parsed) = key.into_ability_key() {
981 self.trigger_spell_abilities.insert(parsed, ability);
982 }
983 }
984
985 pub fn append_sub_ability(&mut self, sub: SpellAbility) {
988 if self.sub_ability.is_none() {
989 self.sub_ability = Some(Box::new(sub));
990 } else {
991 let mut current = self.sub_ability.as_deref_mut();
993 while let Some(sa) = current {
994 if sa.sub_ability.is_none() {
995 sa.sub_ability = Some(Box::new(sub));
996 return;
997 }
998 current = sa.sub_ability.as_deref_mut();
999 }
1000 }
1001 }
1002
1003 pub fn copy(&self) -> Self {
1008 crate::perf::increment(crate::perf::Metric::SpellAbilityClones, 1);
1009 self.clone()
1010 }
1011
1012 pub fn copy_for_player(&self, activ: PlayerId) -> Self {
1013 crate::perf::increment(crate::perf::Metric::SpellAbilityClones, 1);
1014 let mut clone = self.clone();
1015 clone.activating_player = activ;
1016 clone
1017 }
1018
1019 pub fn copy_with_host_lki(&self, host: crate::card::Card, lki: bool) -> Self {
1020 self.copy_with_host_activating_lki_keep_text_changes(
1021 host,
1022 self.activating_player,
1023 lki,
1024 false,
1025 )
1026 }
1027
1028 pub fn copy_with_host_lki_keep_text_changes(
1029 &self,
1030 host: crate::card::Card,
1031 lki: bool,
1032 keep_text_changes: bool,
1033 ) -> Self {
1034 self.copy_with_host_activating_lki_keep_text_changes(
1035 host,
1036 self.activating_player,
1037 lki,
1038 keep_text_changes,
1039 )
1040 }
1041
1042 pub fn copy_with_host_activating_lki(
1043 &self,
1044 host: crate::card::Card,
1045 activ: PlayerId,
1046 lki: bool,
1047 ) -> Self {
1048 self.copy_with_host_activating_lki_keep_text_changes(host, activ, lki, false)
1049 }
1050
1051 pub fn copy_with_host_activating_lki_keep_text_changes(
1052 &self,
1053 host: crate::card::Card,
1054 activ: PlayerId,
1055 lki: bool,
1056 keep_text_changes: bool,
1057 ) -> Self {
1058 crate::perf::increment(crate::perf::Metric::SpellAbilityClones, 1);
1059 let mut clone = self.clone();
1060 clone.id = if lki {
1061 self.id
1062 } else {
1063 next_spell_ability_id()
1064 };
1065
1066 clone.source = Some(host.id);
1067 clone.may_choose_new_targets = false;
1068 clone.trigger_objects = self.trigger_objects.clone();
1069 if !lki {
1070 clone.replacing_objects = HashMap::new();
1071 }
1072
1073 clone.pay_costs = self.pay_costs.clone();
1074 if self.mana_part.is_some() {
1075 clone.mana_part = self.mana_part.clone();
1076 }
1077
1078 clone.optional_keyword_amounts = self.optional_keyword_amounts.clone();
1079 clone.damage_map = self.damage_map.clone();
1080 clone.prevent_map = self.prevent_map.clone();
1081 clone.change_zone_table = self.change_zone_table.clone();
1082 clone.paying_mana = self.paying_mana.clone();
1083 clone.paid_abilities = Vec::new();
1084 clone.paid_hash = self.paid_hash.clone();
1085
1086 if self.uses_targeting() {
1087 clone.target_chosen = self.target_chosen.clone();
1088 }
1089
1090 clone.trigger_spell_abilities = HashMap::new();
1091 clone.additional_ability_lists = HashMap::new();
1092
1093 if let Some(sub_ability) = &self.sub_ability {
1094 clone.sub_ability = Some(Box::new(
1095 sub_ability.copy_with_host_activating_lki_keep_text_changes(
1096 host.clone(),
1097 activ,
1098 lki,
1099 keep_text_changes,
1100 ),
1101 ));
1102 }
1103
1104 for (name, ability) in &self.trigger_spell_abilities {
1105 clone.trigger_spell_abilities.insert(
1106 *name,
1107 ability.copy_with_host_activating_lki_keep_text_changes(
1108 host.clone(),
1109 activ,
1110 lki,
1111 keep_text_changes,
1112 ),
1113 );
1114 }
1115
1116 for (name, abilities) in &self.additional_ability_lists {
1117 clone.additional_ability_lists.insert(
1118 name.clone(),
1119 abilities
1120 .iter()
1121 .map(|ability| {
1122 ability.copy_with_host_activating_lki_keep_text_changes(
1123 host.clone(),
1124 activ,
1125 lki,
1126 keep_text_changes,
1127 )
1128 })
1129 .collect(),
1130 );
1131 }
1132
1133 clone.restriction = self.restriction.clone();
1134 clone.condition = self.condition.clone();
1135 clone.activating_player = activ;
1136
1137 let _ = keep_text_changes;
1138 clone
1139 }
1140
1141 pub fn copy_with_no_mana_cost(&self) -> Self {
1144 crate::perf::increment(crate::perf::Metric::SpellAbilityClones, 1);
1145 let mut copied = self.clone();
1146 copied.pay_costs = None;
1147 copied
1148 }
1149
1150 pub fn copy_with_defined_cost(&self, cost: &str) -> Self {
1153 crate::perf::increment(crate::perf::Metric::SpellAbilityClones, 1);
1154 let mut copied = self.clone();
1155 copied.pay_costs = Some(parse_cost(cost));
1156 copied
1157 }
1158
1159 pub fn copy_with_mana_cost_replaced(&self, old: &str, new: &str) -> Self {
1162 crate::perf::increment(crate::perf::Metric::SpellAbilityClones, 1);
1163 let mut copied = self.clone();
1164 if let Some(ref cost) = self.pay_costs {
1165 let cost_str = format!("{:?}", cost);
1166 let replaced = cost_str.replace(old, new);
1167 copied.pay_costs = Some(parse_cost(&replaced));
1168 }
1169 copied
1170 }
1171
1172 pub fn can_target(&self, card: CardId, game: &GameState) -> bool {
1177 if let Some(ref tr) = self.target_restrictions {
1178 tr.has_candidates(game, self.activating_player, self.source)
1179 && self
1180 .ir
1181 .targets_with_defined_controller_text
1182 .as_deref()
1183 .map(|defined| {
1184 crate::ability::ability_utils::resolve_defined_players_with_sa(
1185 defined,
1186 self,
1187 self.activating_player,
1188 game,
1189 )
1190 })
1191 .map(|players| {
1192 players.is_empty() || players.contains(&game.card(card).controller)
1193 })
1194 .unwrap_or(true)
1195 && target_restrictions::can_be_targeted_by_sa(
1196 game,
1197 card,
1198 self.activating_player,
1199 self,
1200 )
1201 } else {
1202 false
1203 }
1204 }
1205
1206 pub fn reset_targets(&mut self) {
1209 self.clear_targets();
1210 }
1211
1212 pub fn add_divided_allocation(&mut self, card: CardId, amount: i32) {
1215 self.target_chosen.add_divided_allocation(card, amount);
1216 }
1217
1218 pub fn reset_first_target(&mut self) {
1221 self.target_chosen = TargetChoices::default();
1222 }
1223
1224 pub fn can_add_more_target(&self, game: &GameState) -> bool {
1227 if let Some(ref tr) = self.target_restrictions {
1228 let max = tr.get_max_targets(game, self);
1229 let current = self.target_chosen.all_target_cards().len() as i32
1230 + self.target_chosen.all_target_players().len() as i32;
1231 current < max
1232 } else {
1233 false
1234 }
1235 }
1236
1237 pub fn find_targeted_cards(&self) -> Vec<CardId> {
1240 let mut cards = Vec::new();
1241 cards.extend(self.target_chosen.all_target_cards());
1242 let mut current = self.sub_ability.as_deref();
1243 while let Some(sub) = current {
1244 cards.extend(sub.target_chosen.all_target_cards());
1245 current = sub.sub_ability.as_deref();
1246 }
1247 cards
1248 }
1249
1250 pub fn find_targeted_players(&self) -> Vec<PlayerId> {
1253 let mut players = Vec::new();
1254 players.extend(self.target_chosen.all_target_players());
1255 let mut current = self.sub_ability.as_deref();
1256 while let Some(sub) = current {
1257 for player in sub.target_chosen.all_target_players() {
1258 if !players.contains(&player) {
1259 players.push(player);
1260 }
1261 }
1262 current = sub.sub_ability.as_deref();
1263 }
1264 players
1265 }
1266
1267 pub fn can_target_spell_ability(&self) -> bool {
1270 matches!(
1271 self.target_restrictions.as_ref().map(|tr| &tr.target_kind),
1272 Some(TargetKind::Spell)
1273 )
1274 }
1275
1276 pub fn setup_new_targets(
1279 &mut self,
1280 game: &GameState,
1281 agents: &mut [Box<dyn PlayerAgent>],
1282 mana_pools: &[ManaPool],
1283 ) -> bool {
1284 self.clear_targets();
1285 self.setup_targets(game, agents, mana_pools)
1286 }
1287
1288 pub fn clear_pips_to_reduce(&mut self) {
1293 self.pips_to_reduce.clear();
1294 }
1295
1296 pub fn add_tapped_for_convoke(&mut self, card: CardId) {
1299 self.convoke_tapped.push(card);
1300 }
1301
1302 pub fn clear_tapped_for_convoke(&mut self) {
1305 self.convoke_tapped.clear();
1306 }
1307
1308 pub fn reset_sacrificed_as_emerge(&mut self) {
1311 self.sacrificed_as_emerge = None;
1312 }
1313
1314 pub fn reset_sacrificed_as_offering(&mut self) {
1317 self.sacrificed_as_offering = None;
1318 }
1319
1320 pub fn add_spliced_cards(&mut self, cards: Vec<CardId>) {
1325 self.spliced_cards.extend(cards);
1326 }
1327
1328 pub fn known_determine_defined(&self) -> bool {
1333 match self.defined() {
1334 Some(defined) => matches!(
1335 defined,
1336 "Self"
1337 | "You"
1338 | "Targeted"
1339 | "TargetedPlayer"
1340 | "Remembered"
1341 | "ParentTarget"
1342 | "SourceController"
1343 | "Imprinted"
1344 ),
1345 None => true,
1346 }
1347 }
1348
1349 pub fn undo(&mut self) -> bool {
1354 self.clear_tapped_for_convoke();
1355 self.reset_sacrificed_as_emerge();
1356 self.reset_sacrificed_as_offering();
1357 self.reset_paid_hash();
1358 self.clear_mana_paid();
1359 true
1360 }
1361
1362 pub fn add_announce_var(&mut self, key: &str, value: i32) {
1367 self.announce_vars.insert(key.to_string(), value);
1368 }
1369
1370 pub fn can_be_targeted_by(&self, _sa: &SpellAbility) -> bool {
1375 if self.is_spell {
1379 return !self.cant_be_copied();
1380 }
1381 true
1382 }
1383
1384 pub fn has_property(&self, property: &str) -> bool {
1389 match property {
1390 "Spell" => self.is_spell,
1391 "Trigger" => self.is_trigger,
1392 "Activated" => self.is_activated,
1393 "ManaAbility" => self.is_mana_ability,
1394 "Optional" => self.ir.optional,
1395 "Mandatory" => self.ir.mandatory,
1396 "Tapped" => self.ir.tapped,
1397 "Hidden" => self.ir.hidden,
1398 "FaceDown" => self.ir.face_down,
1399 "ExileFaceDown" => self.ir.exile_face_down,
1400 "Transformed" => self.ir.transformed,
1401 "AtRandom" => self.ir.at_random,
1402 "Imprint" => self.ir.imprint,
1403 "Morph" => self.ir.morph,
1404 "MorphUp" => self.ir.morph_up,
1405 "Megamorph" => self.ir.megamorph,
1406 "PwAbility" => self.ir.pw_ability,
1407 "Flash" => self.ir.flash,
1408 "SplitSecond" => self.ir.split_second,
1409 _ => false,
1410 }
1411 }
1412
1413 pub fn tracks_mana_spent(&self) -> bool {
1416 self.ir.track_mana_spent
1417 }
1418
1419 pub fn apply_text_change(&mut self, original: &str, replacement: &str) {
1424 if original == replacement {
1425 return;
1426 }
1427 self.description = self.description.replace(original, replacement);
1428 self.stack_description = self.stack_description.replace(original, replacement);
1429 if let Some(ref mut tr) = self.target_restrictions {
1430 tr.apply_target_text_changes(&[(original, replacement)]);
1431 }
1432
1433 if let Some(sub_ability) = self.sub_ability.as_deref_mut() {
1434 sub_ability.apply_text_change(original, replacement);
1435 }
1436
1437 for ability in self.trigger_spell_abilities.values_mut() {
1438 ability.apply_text_change(original, replacement);
1439 }
1440 }
1441
1442 pub fn apply_text_change_intrinsic(&mut self, original: &str, replacement: &str) {
1445 self.apply_text_change(original, replacement);
1446 }
1447
1448 pub fn apply_text_changes(&mut self, pairs: &[(String, String)]) {
1450 for (original, replacement) in pairs {
1451 self.apply_text_change(original, replacement);
1452 }
1453 }
1454
1455 pub fn apply_text_changes_intrinsic(
1457 &mut self,
1458 color_map: &HashMap<String, String>,
1459 type_map: &HashMap<String, String>,
1460 ) {
1461 for (original, replacement) in color_map.iter().chain(type_map.iter()) {
1462 self.apply_text_change_intrinsic(original, replacement);
1463 }
1464 }
1465
1466 pub fn set_host_card(&mut self, card: crate::card::Card) {
1468 self.set_host_card_id(card.id);
1469 }
1470
1471 pub fn set_host_card_id(&mut self, card_id: CardId) {
1472 self.source = Some(card_id);
1473 if self.original_host.is_none() {
1474 self.original_host = Some(card_id);
1475 }
1476
1477 if let Some(sub_ability) = self.sub_ability.as_deref_mut() {
1478 sub_ability.set_host_card_id(card_id);
1479 }
1480
1481 for ability in self.trigger_spell_abilities.values_mut() {
1482 ability.set_host_card_id(card_id);
1483 }
1484 }
1485
1486 pub fn set_keyword(&mut self, keyword: crate::keyword::keyword_interface::KeywordInterface) {
1488 if let Some(sub_ability) = self.sub_ability.as_deref_mut() {
1489 sub_ability.set_keyword(keyword.clone());
1490 }
1491
1492 for ability in self.trigger_spell_abilities.values_mut() {
1493 ability.set_keyword(keyword.clone());
1494 }
1495 }
1496
1497 #[allow(clippy::only_used_in_recursion)]
1499 pub fn set_card_state(&mut self, state: &crate::card::card_state::CardState) {
1500 if let Some(sub_ability) = self.sub_ability.as_deref_mut() {
1501 sub_ability.set_card_state(state);
1502 }
1503
1504 for ability in self.trigger_spell_abilities.values_mut() {
1505 ability.set_card_state(state);
1506 }
1507 }
1508
1509 pub fn set_intrinsic(&mut self, intrinsic: bool) {
1511 self.intrinsic = intrinsic;
1512
1513 if let Some(sub_ability) = self.sub_ability.as_deref_mut() {
1514 if sub_ability.is_intrinsic() != intrinsic {
1515 sub_ability.set_intrinsic(intrinsic);
1516 }
1517 }
1518
1519 for ability in self.trigger_spell_abilities.values_mut() {
1520 if ability.is_intrinsic() != intrinsic {
1521 ability.set_intrinsic(intrinsic);
1522 }
1523 }
1524 }
1525
1526 pub fn is_intrinsic(&self) -> bool {
1527 self.intrinsic
1528 }
1529
1530 pub fn get_amount_life_paid(&self) -> i32 {
1532 self.paid_life_amount
1533 }
1534
1535 pub fn set_amount_life_paid(&mut self, value: i32) {
1537 self.paid_life_amount = value;
1538 }
1539
1540 pub fn calculate_score_for_mana_ability(&self) -> i32 {
1545 if !self.is_mana_ability {
1546 return 0;
1547 }
1548 let base = self.total_amount_of_mana_generated();
1549 let restriction_penalty = if self.restriction.variables.sorcery_speed() {
1551 -1
1552 } else {
1553 0
1554 };
1555 base + restriction_penalty
1556 }
1557
1558 pub fn can_cast_timing(&self, game: &GameState) -> bool {
1563 let can_cast_sorcery = game.turn.phase.is_main()
1564 && game.stack.is_empty()
1565 && game.turn.active_player == self.activating_player;
1566
1567 if !self.is_spell && !self.is_activated {
1569 return true;
1570 }
1571
1572 if can_cast_sorcery || self.with_flash(game) {
1573 return true;
1574 }
1575
1576 if self.is_spell {
1578 return false;
1579 }
1580
1581 if self.is_activated {
1584 return !self.ir.pw_ability && !self.restriction.variables.sorcery_speed();
1585 }
1586
1587 true
1588 }
1589
1590 pub fn with_flash(&self, game: &GameState) -> bool {
1593 if self.restriction.variables.instant_speed() {
1594 return true;
1595 }
1596 if self.ir.flash {
1597 return true;
1598 }
1599 if let Some(card_id) = self.source {
1600 let card = game.card(card_id);
1601 if ((self.is_spell || self.is_land_ability) && card.type_line.is_instant())
1602 || card.has_keyword("Flash")
1603 {
1604 return true;
1605 }
1606 return crate::staticability::static_ability_cast_with_flash::any_with_flash_for_card(
1607 &game.cards,
1608 card,
1609 self.activating_player,
1610 );
1611 }
1612 false
1613 }
1614
1615 pub fn check_restrictions(&self, game: &GameState) -> bool {
1618 self.can_play(game)
1619 }
1620
1621 pub fn add_rollback_effect(&mut self, effect: String) {
1626 self.rollback_effects.push(effect);
1627 }
1628
1629 pub fn rollback(&mut self) -> bool {
1632 let had_effects = !self.rollback_effects.is_empty();
1633 self.rollback_effects.clear();
1634 had_effects
1635 }
1636
1637 pub fn has_optional_keyword_amount(&self, keyword: &str) -> bool {
1642 self.optional_keyword_amounts.contains_key(keyword)
1643 }
1644
1645 pub fn clear_optional_keyword_amount(&mut self) {
1648 self.optional_keyword_amounts.clear();
1649 }
1650
1651 pub fn clear_last_state(&mut self) {
1654 self.last_state.clear();
1655 }
1656
1657 pub fn set_triggering_object<K: TriggerKeyInput, V: Into<AbilityValue>>(
1662 &mut self,
1663 key: K,
1664 value: V,
1665 ) {
1666 if let Some(parsed) = key.into_ability_key() {
1667 self.trigger_objects.insert(parsed, value.into());
1668 }
1669 }
1670
1671 pub fn set_triggering_value<V: Into<AbilityValue>>(&mut self, key: AbilityKey, value: V) {
1673 self.trigger_objects.insert(key, value.into());
1674 }
1675
1676 pub fn set_triggering_spell_ability<K: TriggerKeyInput>(
1679 &mut self,
1680 key: K,
1681 value: SpellAbility,
1682 ) {
1683 if let Some(parsed) = key.into_ability_key() {
1684 self.trigger_spell_abilities.insert(parsed, value);
1685 }
1686 }
1687
1688 pub fn get_triggering_spell_ability<K: TriggerKeyInput>(
1690 &self,
1691 key: K,
1692 ) -> Option<&SpellAbility> {
1693 key.into_ability_key()
1694 .and_then(|parsed| self.trigger_spell_abilities.get(&parsed))
1695 }
1696
1697 pub fn update_triggering_object<K: TriggerKeyInput, V: Into<AbilityValue>>(
1700 &mut self,
1701 key: K,
1702 value: V,
1703 ) {
1704 self.set_triggering_object(key, value);
1705 }
1706
1707 pub fn update_target(&mut self, old: CardId, new: CardId) {
1712 self.target_chosen.replace_target_card(old, new);
1713 }
1714
1715 pub fn targets_single_target(&self) -> bool {
1718 if let Some(ref tr) = self.target_restrictions {
1719 tr.max_targets == "1"
1720 } else {
1721 false
1722 }
1723 }
1724
1725 pub fn gets_var_operand(&self) -> Option<&str> {
1732 self.condition
1733 .variables
1734 .gets_var_operand()
1735 .or_else(|| self.restriction.variables.gets_var_operand())
1736 }
1737
1738 pub fn gets_var_operand2(&self) -> Option<&str> {
1741 self.condition
1742 .variables
1743 .gets_var_operand2()
1744 .or_else(|| self.restriction.variables.gets_var_operand2())
1745 }
1746
1747 pub fn sets_var_operand(&mut self, value: &str) {
1750 self.condition.variables.sets_var_operand(value);
1751 self.restriction.variables.sets_var_operand(value);
1752 }
1753
1754 pub fn sets_var_operand2(&mut self, value: &str) {
1757 self.condition.variables.sets_var_operand2(value);
1758 self.restriction.variables.sets_var_operand2(value);
1759 }
1760
1761 pub fn gets_var_to_check(&self) -> Option<&str> {
1764 self.condition
1765 .variables
1766 .gets_var_to_check()
1767 .or_else(|| self.restriction.variables.gets_var_to_check())
1768 }
1769
1770 pub fn gets_var_to_check2(&self) -> Option<&str> {
1773 self.condition
1774 .variables
1775 .gets_var_to_check2()
1776 .or_else(|| self.restriction.variables.gets_var_to_check2())
1777 }
1778
1779 pub fn sets_var_to_check(&mut self, value: &str) {
1782 self.condition.variables.sets_var_to_check(value);
1783 self.restriction.variables.sets_var_to_check(value);
1784 }
1785
1786 pub fn sets_var_to_check2(&mut self, value: &str) {
1789 self.condition.variables.sets_var_to_check2(value);
1790 self.restriction.variables.sets_var_to_check2(value);
1791 }
1792
1793 pub fn gets_var_operator(&self) -> Option<&str> {
1796 self.condition
1797 .variables
1798 .gets_var_operator()
1799 .or_else(|| self.restriction.variables.gets_var_operator())
1800 }
1801
1802 pub fn gets_var_operator2(&self) -> Option<&str> {
1805 self.condition
1806 .variables
1807 .gets_var_operator2()
1808 .or_else(|| self.restriction.variables.gets_var_operator2())
1809 }
1810
1811 pub fn sets_var_operator(&mut self, value: &str) {
1814 self.condition.variables.sets_var_operator(value);
1815 self.restriction.variables.sets_var_operator(value);
1816 }
1817
1818 pub fn sets_var_operator2(&mut self, value: &str) {
1821 self.condition.variables.sets_var_operator2(value);
1822 self.restriction.variables.sets_var_operator2(value);
1823 }
1824}
1825
1826pub use crate::ability::ability_factory::build_spell_ability;
1829pub use crate::ability::ability_factory::build_spell_ability_for_card_cast;
1830pub use crate::ability::ability_factory::build_spell_ability_from_host_card;
1831
1832pub fn has_split_second_on_stack(game: &GameState) -> bool {
1837 for entry in game.stack.iter() {
1838 if entry.spell_ability.ir.split_second {
1839 return true;
1840 }
1841 if let Some(card_id) = entry.spell_ability.source {
1842 let card = game.card(card_id);
1843 if card.has_keyword("Split second") {
1844 return true;
1845 }
1846 }
1847 }
1848 false
1849}
1850
1851pub fn choose_targets_by_kind(
1852 agent: &mut dyn PlayerAgent,
1853 sa: &mut SpellAbility,
1854 game: &GameState,
1855 mana_pools: &[ManaPool],
1856) -> bool {
1857 use crate::card::card_util;
1858
1859 let tr = match &sa.target_restrictions {
1860 Some(tr) => tr,
1861 None => return true,
1862 };
1863
1864 let player = sa.targeting_player.unwrap_or(sa.activating_player);
1865
1866 let min_targets = tr.get_min_targets(game, sa);
1867 let max_targets = tr.get_max_targets(game, sa);
1868 if max_targets <= 0 {
1869 return true;
1870 }
1871
1872 if !matches!(tr.target_kind, TargetKind::CardInZone { .. })
1873 && !tr.has_candidates(game, player, sa.source)
1874 {
1875 return min_targets <= 0;
1876 }
1877
1878 sa.target_chosen.target_card = None;
1879 sa.target_chosen.target_card_zone_timestamp = None;
1880 sa.target_chosen.divided_map.clear();
1881
1882 match &tr.target_kind {
1883 TargetKind::None => {}
1884 TargetKind::Player => {
1885 agent.snapshot_state(game, mana_pools);
1886 let is_opponent_only = tr
1887 .valid_tgts
1888 .iter()
1889 .any(|v| v.eq_ignore_ascii_case("Opponent"));
1890 let valid_players: Vec<PlayerId> = game
1891 .alive_players()
1892 .into_iter()
1893 .filter(|&pid| !is_opponent_only || pid != player)
1894 .collect();
1895 if max_targets > 1 {
1896 let mut chosen = Vec::new();
1897 while (chosen.len() as i32) < max_targets {
1898 let Some(pid) = agent.choose_target_player(player, &valid_players, Some(&*sa))
1899 else {
1900 break;
1901 };
1902 if !chosen.contains(&pid) {
1903 chosen.push(pid);
1904 }
1905 if chosen.len() == valid_players.len() {
1906 break;
1907 }
1908 }
1909 sa.target_chosen.target_player = chosen.first().copied();
1910 sa.target_chosen.additional_target_players = chosen.into_iter().skip(1).collect();
1911 } else {
1912 sa.target_chosen.target_player =
1913 agent.choose_target_player(player, &valid_players, Some(&*sa));
1914 }
1915 }
1916 TargetKind::Any => {
1917 let valid_players: Vec<PlayerId> =
1918 if target_restrictions::any_target_allows_players(&tr.valid_tgts) {
1919 game.alive_players().into_iter().collect()
1920 } else {
1921 Vec::new()
1922 };
1923 let valid_cards: Vec<CardId> = card_util::get_valid_cards_to_target(game, sa);
1924 agent.snapshot_state(game, mana_pools);
1925 match agent.choose_target_any(player, &valid_players, &valid_cards, Some(&*sa)) {
1926 crate::agent::TargetChoice::Player(pid) => {
1927 sa.target_chosen.target_player = Some(pid)
1928 }
1929 crate::agent::TargetChoice::Card(cid) => {
1930 sa.target_chosen.target_card = Some(cid);
1931 sa.target_chosen.target_card_zone_timestamp =
1932 Some(game.card(cid).zone_timestamp);
1933 }
1934 crate::agent::TargetChoice::None => {}
1935 }
1936 }
1937 TargetKind::Creature(_) => {
1938 let valid: Vec<CardId> = card_util::get_valid_cards_to_target(game, sa)
1939 .into_iter()
1940 .filter(|&cid| target_allowed_by_defined_controller(game, sa, cid))
1941 .collect();
1942 agent.snapshot_state(game, mana_pools);
1943 if max_targets > 1 {
1944 let chosen = agent.choose_cards_for_effect(
1945 player,
1946 &valid,
1947 min_targets.max(0) as usize,
1948 max_targets as usize,
1949 );
1950 if let Some(&first) = chosen.first() {
1951 sa.target_chosen.target_card = Some(first);
1952 sa.target_chosen.target_card_zone_timestamp =
1953 Some(game.card(first).zone_timestamp);
1954 for &extra in chosen.iter().skip(1) {
1955 sa.target_chosen.divided_map.insert(extra, 0);
1956 }
1957 }
1958 } else {
1959 sa.target_chosen.target_card = agent.choose_target_card(player, &valid, Some(&*sa));
1960 if let Some(cid) = sa.target_chosen.target_card {
1961 sa.target_chosen.target_card_zone_timestamp =
1962 Some(game.card(cid).zone_timestamp);
1963 }
1964 }
1965 }
1966 TargetKind::Permanent(_) => {
1967 let valid: Vec<CardId> = card_util::get_valid_cards_to_target(game, sa)
1968 .into_iter()
1969 .filter(|&cid| target_allowed_by_defined_controller(game, sa, cid))
1970 .collect();
1971 agent.snapshot_state(game, mana_pools);
1972 if max_targets > 1 {
1973 let chosen = agent.choose_cards_for_effect(
1974 player,
1975 &valid,
1976 min_targets.max(0) as usize,
1977 max_targets as usize,
1978 );
1979 if let Some(&first) = chosen.first() {
1980 sa.target_chosen.target_card = Some(first);
1981 sa.target_chosen.target_card_zone_timestamp =
1982 Some(game.card(first).zone_timestamp);
1983 for &extra in chosen.iter().skip(1) {
1984 sa.target_chosen.divided_map.insert(extra, 0);
1985 }
1986 }
1987 } else {
1988 sa.target_chosen.target_card = agent.choose_target_card(player, &valid, Some(&*sa));
1989 if let Some(cid) = sa.target_chosen.target_card {
1990 sa.target_chosen.target_card_zone_timestamp =
1991 Some(game.card(cid).zone_timestamp);
1992 }
1993 }
1994 }
1995 TargetKind::CardInZone { zone, .. } => {
1996 let valid: Vec<CardId> = card_util::get_valid_cards_to_target(game, sa)
1997 .into_iter()
1998 .filter(|&cid| target_allowed_by_defined_controller(game, sa, cid))
1999 .collect();
2000 if valid.is_empty() {
2001 return min_targets <= 0;
2002 }
2003 agent.snapshot_state(game, mana_pools);
2004 if max_targets > 1 {
2005 let chosen = agent.choose_cards_for_effect(
2006 player,
2007 &valid,
2008 min_targets.max(0) as usize,
2009 max_targets as usize,
2010 );
2011 if let Some(&first) = chosen.first() {
2012 sa.target_chosen.target_card = Some(first);
2013 sa.target_chosen.target_card_zone_timestamp =
2014 Some(game.card(first).zone_timestamp);
2015 for &extra in chosen.iter().skip(1) {
2016 sa.target_chosen.divided_map.insert(extra, 0);
2017 }
2018 }
2019 } else {
2020 sa.target_chosen.target_card =
2021 agent.choose_target_card_from_zone(player, *zone, &valid, Some(&*sa));
2022 if let Some(cid) = sa.target_chosen.target_card {
2023 sa.target_chosen.target_card_zone_timestamp =
2024 Some(game.card(cid).zone_timestamp);
2025 }
2026 }
2027 }
2028 TargetKind::Spell => {
2029 let valid = target_restrictions::get_all_candidates_spells(game);
2030 let valid = if let Some(ref restrictions) = sa.target_restrictions {
2031 target_restrictions::filter_spells_for_target_restrictions(
2032 game,
2033 &valid,
2034 restrictions,
2035 )
2036 } else {
2037 valid
2038 };
2039 agent.snapshot_state(game, mana_pools);
2040 sa.target_chosen.target_stack_entry =
2041 agent.choose_target_spell(player, &valid, sa.source);
2042 }
2043 }
2044
2045 let chosen_targets = sa.target_chosen.all_target_cards().len() as i32
2046 + sa.target_chosen.all_target_players().len() as i32
2047 + i32::from(sa.target_chosen.target_stack_entry.is_some());
2048 chosen_targets >= min_targets
2049}
2050
2051fn target_allowed_by_defined_controller(
2052 game: &GameState,
2053 sa: &SpellAbility,
2054 card_id: CardId,
2055) -> bool {
2056 let Some(defined) = sa.ir.targets_with_defined_controller_text.as_deref() else {
2057 return true;
2058 };
2059 let players = crate::ability::ability_utils::resolve_defined_players_with_sa(
2060 defined,
2061 sa,
2062 sa.activating_player,
2063 game,
2064 );
2065 players.is_empty() || players.contains(&game.card(card_id).controller)
2066}
2067
2068fn choose_targeting_player(
2069 sa: &SpellAbility,
2070 game: &GameState,
2071 agents: &mut [Box<dyn PlayerAgent>],
2072) -> Option<PlayerId> {
2073 if let Some(defined) = sa.ir.targeting_player_text.as_deref() {
2074 let candidates = crate::ability::ability_utils::resolve_defined_players_with_sa(
2075 defined,
2076 sa,
2077 sa.activating_player,
2078 game,
2079 );
2080 if candidates.is_empty() {
2081 return None;
2082 }
2083 return agents[sa.activating_player.index()].choose_target_player(
2084 sa.activating_player,
2085 &candidates,
2086 None,
2087 );
2088 }
2089 Some(sa.activating_player)
2090}
2091
2092pub use crate::zone::magic_stack::{MagicStack, StackEntry};