manabrew_engine/staticability/static_ability_num_loyalty_act.rs
1use crate::card::{valid_filter, Card};
2use crate::spellability::SpellAbility;
3use crate::staticability::StaticMode;
4
5/// Apply the limit increase check for a single static ability.
6///
7/// Mirrors Java's `StaticAbilityNumLoyaltyAct.applyLimitIncrease()`.
8pub fn apply_limit_increase(
9 st_ab: &crate::staticability::StaticAbility,
10 card: &Card,
11 source: &Card,
12) -> bool {
13 if !valid_filter::matches_valid_card_selector_opt(st_ab.ir.valid_card.as_ref(), card, source) {
14 return false;
15 }
16 st_ab.ir.twice
17}
18
19/// Check if a planeswalker can activate loyalty abilities twice per turn.
20///
21/// Mirrors Java's `StaticAbilityNumLoyaltyAct.limitIncrease()`.
22pub fn limit_increase(cards: &[Card], card: &Card) -> bool {
23 for source in cards.iter().filter(|c| c.zone.is_static_ability_source()) {
24 for st_ab in source
25 .static_abilities
26 .iter()
27 .filter(|sa| sa.check_mode(&StaticMode::NumLoyaltyAct) && sa.zones_check(source.zone))
28 {
29 if apply_limit_increase(st_ab, card, source) {
30 return true;
31 }
32 }
33 }
34 false
35}
36
37/// Count additional loyalty activations from static abilities.
38///
39/// Mirrors Java's `StaticAbilityNumLoyaltyAct.additionalActivations()`.
40/// The `sa` parameter is the SpellAbility being activated (used for OnlySourceAbs check).
41pub fn additional_activations(cards: &[Card], card: &Card, sa: Option<&SpellAbility>) -> i32 {
42 let mut addl = 0;
43 for source in cards.iter().filter(|c| c.zone.is_static_ability_source()) {
44 for st_ab in source
45 .static_abilities
46 .iter()
47 .filter(|s| s.check_mode(&StaticMode::NumLoyaltyAct) && s.zones_check(source.zone))
48 {
49 if !valid_filter::matches_valid_card_selector_opt(
50 st_ab.ir.valid_card.as_ref(),
51 card,
52 source,
53 ) {
54 continue;
55 }
56 if let Some(additional) = st_ab.ir.additional_text.as_deref() {
57 // OnlySourceAbs$ — if present, only count if the SA being activated
58 // is the original ability from the effect source card.
59 if st_ab.ir.only_source_abs {
60 // Java: stAb.getHostCard().getEffectSourceAbility().getRootAbility()
61 // .getOriginalAbility().equals(sa)
62 // In Rust, we approximate by checking if the SA's source card matches
63 // the static host's effect_source.
64 if let Some(spell_ability) = sa {
65 let sa_source = spell_ability.source;
66 let effect_source = source.effect_source;
67 // If the SA's source doesn't match the effect source, skip.
68 if sa_source != effect_source {
69 continue;
70 }
71 } else {
72 // No SA provided but OnlySourceAbs required — skip.
73 continue;
74 }
75 }
76
77 // Java: AbilityUtils.calculateAmount(card, additional, stAb)
78 // TODO: support expression-based calculateAmount when ported.
79 addl += additional.parse::<i32>().unwrap_or(0);
80 }
81 }
82 }
83 addl
84}