manabrew_engine/staticability/
static_ability_attack_restrict.rs1use forge_foundation::ZoneType;
2
3use crate::card::Card;
4use crate::ids::PlayerId;
5use crate::staticability::StaticMode;
6
7pub fn global_attack_restrict(cards: &[Card]) -> Option<i32> {
8 let mut max: Option<i32> = None;
9 for source in cards.iter().filter(|c| c.zone == ZoneType::Battlefield) {
10 for st_ab in source
11 .static_abilities
12 .iter()
13 .filter(|sa| sa.check_mode(&StaticMode::AttackRestrict))
14 {
15 if st_ab.ir.valid_defender_text.is_some() {
16 continue;
17 }
18 let m = st_ab
19 .ir
20 .max_attackers
21 .as_deref()
22 .map(|s| eval_amount(source, s))
23 .unwrap_or(1);
24 max = Some(max.map_or(m, |x| x.min(m)));
25 }
26 }
27 max
28}
29
30pub fn attack_restrict_num_for_defender(cards: &[Card], defender: PlayerId) -> Option<i32> {
31 let mut max: Option<i32> = None;
32 for source in cards.iter().filter(|c| c.zone == ZoneType::Battlefield) {
33 for st_ab in source
34 .static_abilities
35 .iter()
36 .filter(|sa| sa.check_mode(&StaticMode::AttackRestrict))
37 {
38 let Some(valid_defender) = st_ab.ir.valid_defender_text.as_deref() else {
39 continue;
40 };
41 if !matches_valid_defender(valid_defender, defender, source.controller) {
42 continue;
43 }
44 let m = st_ab
45 .ir
46 .max_attackers
47 .as_deref()
48 .map(|s| eval_amount(source, s))
49 .unwrap_or(1);
50 max = Some(max.map_or(m, |x| x.min(m)));
51 }
52 }
53 max
54}
55
56fn eval_amount(source: &Card, expr: &str) -> i32 {
57 if let Ok(n) = expr.parse::<i32>() {
58 return n;
59 }
60 if let Some(svar) = source.svars.get(expr) {
61 if let Ok(n) = svar.parse::<i32>() {
62 return n;
63 }
64 let mut sa =
65 crate::spellability::SpellAbility::new_empty(Some(source.id), source.controller);
66 sa.kicked = false;
67 return crate::ability::effects::evaluate_svar(svar, &sa);
68 }
69 1
70}
71
72fn matches_valid_defender(valid: &str, defender: PlayerId, source_controller: PlayerId) -> bool {
73 if valid.eq_ignore_ascii_case("Player") {
74 return true;
75 }
76 if valid.eq_ignore_ascii_case("You") || valid.eq_ignore_ascii_case("YouCtrl") {
77 return defender == source_controller;
78 }
79 if valid.eq_ignore_ascii_case("Opponent") || valid.eq_ignore_ascii_case("OppCtrl") {
80 return defender != source_controller;
81 }
82 true
83}