Skip to main content

manabrew_engine/ability/
api_type.rs

1//! ApiType — enum of all effect API types.
2//!
3//! Mirrors Java's `ApiType.java`.
4//! Each variant maps to an effect handler in the `effects` module.
5
6use serde::{Deserialize, Serialize};
7
8/// All known API types for spell ability effects.
9/// Mirrors the Java `ApiType` enum with all its variants.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub enum ApiType {
12    Abandon,
13    ActivateAbility,
14    AddOrRemoveCounter,
15    AddPhase,
16    AddTurn,
17    AdvanceCrank,
18    Airbend,
19    AlterAttribute,
20    Amass,
21    Animate,
22    AnimateAll,
23    Attach,
24    Ascend,
25    AssembleContraption,
26    AssignGroup,
27    Balance,
28    BecomeMonarch,
29    BecomesBlocked,
30    BidLife,
31    Blight,
32    Block,
33    Bond,
34    Branch,
35    Camouflage,
36    ChangeCombatants,
37    ChangeSpeed,
38    ChangeTargets,
39    ChangeText,
40    ChangeX,
41    ChangeZone,
42    ChangeZoneAll,
43    ChaosEnsues,
44    Charm,
45    ChooseCard,
46    ChooseCardName,
47    ChooseColor,
48    ChooseDirection,
49    ChooseEvenOdd,
50    ChooseGeneric,
51    ChooseNumber,
52    ChoosePlayer,
53    ChooseSector,
54    ChooseSource,
55    ChooseType,
56    ClaimThePrize,
57    Clash,
58    ClassLevelUp,
59    Cleanup,
60    Cloak,
61    Clone,
62    Connive,
63    CopyPermanent,
64    CopySpellAbility,
65    ControlSpell,
66    ControlPlayer,
67    Counter,
68    CountersMove,
69    CountersNote,
70    DamageAll,
71    DamagePrevent,
72    DealDamage,
73    Detain,
74    DayTime,
75    Debuff,
76    DelayedTrigger,
77    Destroy,
78    DestroyAll,
79    Dig,
80    DigMultiple,
81    DigUntil,
82    Discard,
83    Discover,
84    DrainMana,
85    Draft,
86    Draw,
87    EachDamage,
88    Earthbend,
89    Effect,
90    Encode,
91    EndCombatPhase,
92    EndTurn,
93    Endure,
94    ExchangeLife,
95    ExchangeLifeVariant,
96    ExchangeControl,
97    ExchangeControlVariant,
98    ExchangePower,
99    ExchangeZone,
100    ExchangeTextBox,
101    Explore,
102    Fight,
103    FlipACoin,
104    FlipOntoBattlefield,
105    Fog,
106    GainControl,
107    GainControlVariant,
108    GainLife,
109    GainOwnership,
110    GameDrawn,
111    GenericChoice,
112    Goad,
113    Haunt,
114    Heist,
115    Investigate,
116    Intensify,
117    ImmediateTrigger,
118    Incubate,
119    Learn,
120    LookAt,
121    LoseLife,
122    LosePerpetual,
123    LosesGame,
124    MakeCard,
125    Mana,
126    ManaReflected,
127    Manifest,
128    ManifestDread,
129    Meld,
130    Mill,
131    MoveCounter,
132    MultiplePiles,
133    MultiplyCounter,
134    MustBlock,
135    Mutate,
136    NameCard,
137    OpenAttraction,
138    PeekAndReveal,
139    PermanentCreature,
140    PermanentNoncreature,
141    Phases,
142    Planeswalk,
143    Play,
144    PlayLandVariant,
145    Poison,
146    PreventDamage,
147    Proliferate,
148    Protection,
149    ProtectionAll,
150    Pump,
151    PumpAll,
152    PutCounter,
153    PutCounterAll,
154    Radiation,
155    RearrangeTopOfLibrary,
156    Regenerate,
157    Regeneration,
158    RemoveCounter,
159    RemoveCounterAll,
160    RemoveFromCombat,
161    RemoveFromGame,
162    RemoveFromMatch,
163    ReorderZone,
164    Repeat,
165    RepeatEach,
166    ReplaceCounter,
167    ReplaceEffect,
168    ReplaceMana,
169    ReplaceDamage,
170    ReplaceToken,
171    ReplaceSplitDamage,
172    RestartGame,
173    Reveal,
174    RevealHand,
175    ReverseTurnOrder,
176    RingTemptsYou,
177    RollDice,
178    RollPlanarDice,
179    RunChaos,
180    Sacrifice,
181    SacrificeAll,
182    Scry,
183    Seek,
184    SetInMotion,
185    SetLife,
186    SetState,
187    Shuffle,
188    SkipPhase,
189    SkipTurn,
190    StoreSVar,
191    Subgame,
192    Surveil,
193    SwitchBlock,
194    TakeInitiative,
195    Tap,
196    TapAll,
197    TapOrUntap,
198    TapOrUntapAll,
199    TimeTravel,
200    Token,
201    TwoPiles,
202    Unattach,
203    UnlockDoor,
204    Untap,
205    UntapAll,
206    Venture,
207    VillainousChoice,
208    Vote,
209    WinsGame,
210    ZoneExchange,
211    // Internal API types
212    BlankLine,
213    DamageResolve,
214    ChangeZoneResolve,
215    CompanionChoose,
216    InternalLegendaryRule,
217    InternalIgnoreEffect,
218    InternalRadiation,
219    Plot,
220}
221
222impl ApiType {
223    /// Parse from a string, case-insensitive.
224    /// Mirrors Java's `ApiType.smartValueOf()`.
225    pub fn smart_value_of(value: &str) -> Option<ApiType> {
226        let lower = value.to_ascii_lowercase();
227        ALL_API_TYPES
228            .iter()
229            .find(|(name, _)| name.to_ascii_lowercase() == lower)
230            .map(|(_, api)| *api)
231    }
232
233    /// Return the name of this API type as it appears in scripts.
234    pub fn name(&self) -> &'static str {
235        ALL_API_TYPES
236            .iter()
237            .find(|(_, api)| api == self)
238            .map(|(name, _)| *name)
239            .unwrap_or("Unknown")
240    }
241}
242
243impl std::fmt::Display for ApiType {
244    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245        f.write_str(self.name())
246    }
247}
248
249/// (name, variant) pairs for lookup. Order matches Java enum declaration.
250/// Aliases (alternate script names that map to the same variant) are listed
251/// after the canonical entries.
252const ALL_API_TYPES: &[(&str, ApiType)] = &[
253    ("Abandon", ApiType::Abandon),
254    ("ActivateAbility", ApiType::ActivateAbility),
255    ("AddOrRemoveCounter", ApiType::AddOrRemoveCounter),
256    ("AddPhase", ApiType::AddPhase),
257    ("AddTurn", ApiType::AddTurn),
258    ("AdvanceCrank", ApiType::AdvanceCrank),
259    ("Airbend", ApiType::Airbend),
260    ("AlterAttribute", ApiType::AlterAttribute),
261    ("Amass", ApiType::Amass),
262    ("Animate", ApiType::Animate),
263    ("AnimateAll", ApiType::AnimateAll),
264    ("Attach", ApiType::Attach),
265    ("Ascend", ApiType::Ascend),
266    ("AssembleContraption", ApiType::AssembleContraption),
267    ("AssignGroup", ApiType::AssignGroup),
268    ("Balance", ApiType::Balance),
269    ("BecomeMonarch", ApiType::BecomeMonarch),
270    ("BecomesBlocked", ApiType::BecomesBlocked),
271    ("BidLife", ApiType::BidLife),
272    ("Blight", ApiType::Blight),
273    ("Block", ApiType::Block),
274    ("Bond", ApiType::Bond),
275    ("Branch", ApiType::Branch),
276    ("Camouflage", ApiType::Camouflage),
277    ("ChangeCombatants", ApiType::ChangeCombatants),
278    ("ChangeSpeed", ApiType::ChangeSpeed),
279    ("ChangeTargets", ApiType::ChangeTargets),
280    ("ChangeText", ApiType::ChangeText),
281    ("ChangeX", ApiType::ChangeX),
282    ("ChangeZone", ApiType::ChangeZone),
283    ("ChangeZoneAll", ApiType::ChangeZoneAll),
284    ("ChaosEnsues", ApiType::ChaosEnsues),
285    ("Charm", ApiType::Charm),
286    ("ChooseCard", ApiType::ChooseCard),
287    ("ChooseCardName", ApiType::ChooseCardName),
288    ("ChooseColor", ApiType::ChooseColor),
289    ("ChooseDirection", ApiType::ChooseDirection),
290    ("ChooseEvenOdd", ApiType::ChooseEvenOdd),
291    ("ChooseGeneric", ApiType::ChooseGeneric),
292    ("ChooseNumber", ApiType::ChooseNumber),
293    ("ChoosePlayer", ApiType::ChoosePlayer),
294    ("ChooseSector", ApiType::ChooseSector),
295    ("ChooseSource", ApiType::ChooseSource),
296    ("ChooseType", ApiType::ChooseType),
297    ("ClaimThePrize", ApiType::ClaimThePrize),
298    ("Clash", ApiType::Clash),
299    ("ClassLevelUp", ApiType::ClassLevelUp),
300    ("Cleanup", ApiType::Cleanup),
301    ("Cloak", ApiType::Cloak),
302    ("Clone", ApiType::Clone),
303    ("Connive", ApiType::Connive),
304    ("CopyPermanent", ApiType::CopyPermanent),
305    ("CopySpellAbility", ApiType::CopySpellAbility),
306    ("ControlSpell", ApiType::ControlSpell),
307    ("ControlPlayer", ApiType::ControlPlayer),
308    ("Counter", ApiType::Counter),
309    ("CountersMove", ApiType::CountersMove),
310    ("CountersNote", ApiType::CountersNote),
311    ("DamageAll", ApiType::DamageAll),
312    ("DamagePrevent", ApiType::DamagePrevent),
313    ("DealDamage", ApiType::DealDamage),
314    ("Detain", ApiType::Detain),
315    ("DayTime", ApiType::DayTime),
316    ("Debuff", ApiType::Debuff),
317    ("DelayedTrigger", ApiType::DelayedTrigger),
318    ("Destroy", ApiType::Destroy),
319    ("DestroyAll", ApiType::DestroyAll),
320    ("Dig", ApiType::Dig),
321    ("DigMultiple", ApiType::DigMultiple),
322    ("DigUntil", ApiType::DigUntil),
323    ("Discard", ApiType::Discard),
324    ("Discover", ApiType::Discover),
325    ("DrainMana", ApiType::DrainMana),
326    ("Draft", ApiType::Draft),
327    ("Draw", ApiType::Draw),
328    ("EachDamage", ApiType::EachDamage),
329    ("Earthbend", ApiType::Earthbend),
330    ("Effect", ApiType::Effect),
331    ("Encode", ApiType::Encode),
332    ("EndCombatPhase", ApiType::EndCombatPhase),
333    ("EndTurn", ApiType::EndTurn),
334    ("Endure", ApiType::Endure),
335    ("ExchangeLife", ApiType::ExchangeLife),
336    ("ExchangeLifeVariant", ApiType::ExchangeLifeVariant),
337    ("ExchangeControl", ApiType::ExchangeControl),
338    ("ExchangeControlVariant", ApiType::ExchangeControlVariant),
339    ("ExchangePower", ApiType::ExchangePower),
340    ("ExchangeZone", ApiType::ExchangeZone),
341    ("ExchangeTextBox", ApiType::ExchangeTextBox),
342    ("Explore", ApiType::Explore),
343    ("Fight", ApiType::Fight),
344    ("FlipACoin", ApiType::FlipACoin),
345    ("FlipOntoBattlefield", ApiType::FlipOntoBattlefield),
346    ("Fog", ApiType::Fog),
347    ("GainControl", ApiType::GainControl),
348    ("GainControlVariant", ApiType::GainControlVariant),
349    ("GainLife", ApiType::GainLife),
350    ("GainOwnership", ApiType::GainOwnership),
351    ("GameDrawn", ApiType::GameDrawn),
352    ("GenericChoice", ApiType::GenericChoice),
353    ("Goad", ApiType::Goad),
354    ("Haunt", ApiType::Haunt),
355    ("Heist", ApiType::Heist),
356    ("Investigate", ApiType::Investigate),
357    ("Intensify", ApiType::Intensify),
358    ("ImmediateTrigger", ApiType::ImmediateTrigger),
359    ("Incubate", ApiType::Incubate),
360    ("Learn", ApiType::Learn),
361    ("LookAt", ApiType::LookAt),
362    ("LoseLife", ApiType::LoseLife),
363    ("LosePerpetual", ApiType::LosePerpetual),
364    ("LosesGame", ApiType::LosesGame),
365    ("MakeCard", ApiType::MakeCard),
366    ("Mana", ApiType::Mana),
367    ("ManaReflected", ApiType::ManaReflected),
368    ("Manifest", ApiType::Manifest),
369    ("ManifestDread", ApiType::ManifestDread),
370    ("Meld", ApiType::Meld),
371    ("Mill", ApiType::Mill),
372    ("MoveCounter", ApiType::MoveCounter),
373    ("MultiplePiles", ApiType::MultiplePiles),
374    ("MultiplyCounter", ApiType::MultiplyCounter),
375    ("MustBlock", ApiType::MustBlock),
376    ("Mutate", ApiType::Mutate),
377    ("NameCard", ApiType::NameCard),
378    ("OpenAttraction", ApiType::OpenAttraction),
379    ("PeekAndReveal", ApiType::PeekAndReveal),
380    ("PermanentCreature", ApiType::PermanentCreature),
381    ("PermanentNoncreature", ApiType::PermanentNoncreature),
382    ("Phases", ApiType::Phases),
383    ("Planeswalk", ApiType::Planeswalk),
384    ("Play", ApiType::Play),
385    ("PlayLandVariant", ApiType::PlayLandVariant),
386    ("Plot", ApiType::Plot),
387    ("Poison", ApiType::Poison),
388    ("PreventDamage", ApiType::PreventDamage),
389    ("Proliferate", ApiType::Proliferate),
390    ("Protection", ApiType::Protection),
391    ("ProtectionAll", ApiType::ProtectionAll),
392    ("Pump", ApiType::Pump),
393    ("PumpAll", ApiType::PumpAll),
394    ("PutCounter", ApiType::PutCounter),
395    ("PutCounterAll", ApiType::PutCounterAll),
396    ("Radiation", ApiType::Radiation),
397    ("RearrangeTopOfLibrary", ApiType::RearrangeTopOfLibrary),
398    ("Regenerate", ApiType::Regenerate),
399    ("Regeneration", ApiType::Regeneration),
400    ("RemoveCounter", ApiType::RemoveCounter),
401    ("RemoveCounterAll", ApiType::RemoveCounterAll),
402    ("RemoveFromCombat", ApiType::RemoveFromCombat),
403    ("RemoveFromGame", ApiType::RemoveFromGame),
404    ("RemoveFromMatch", ApiType::RemoveFromMatch),
405    ("ReorderZone", ApiType::ReorderZone),
406    ("Repeat", ApiType::Repeat),
407    ("RepeatEach", ApiType::RepeatEach),
408    ("ReplaceCounter", ApiType::ReplaceCounter),
409    ("ReplaceEffect", ApiType::ReplaceEffect),
410    ("ReplaceMana", ApiType::ReplaceMana),
411    ("ReplaceDamage", ApiType::ReplaceDamage),
412    ("ReplaceToken", ApiType::ReplaceToken),
413    ("ReplaceSplitDamage", ApiType::ReplaceSplitDamage),
414    ("RestartGame", ApiType::RestartGame),
415    ("Reveal", ApiType::Reveal),
416    ("RevealHand", ApiType::RevealHand),
417    ("ReverseTurnOrder", ApiType::ReverseTurnOrder),
418    ("RingTemptsYou", ApiType::RingTemptsYou),
419    ("RollDice", ApiType::RollDice),
420    ("RollPlanarDice", ApiType::RollPlanarDice),
421    ("RunChaos", ApiType::RunChaos),
422    ("Sacrifice", ApiType::Sacrifice),
423    ("SacrificeAll", ApiType::SacrificeAll),
424    ("Scry", ApiType::Scry),
425    ("Seek", ApiType::Seek),
426    ("SetInMotion", ApiType::SetInMotion),
427    ("SetLife", ApiType::SetLife),
428    ("SetState", ApiType::SetState),
429    ("Shuffle", ApiType::Shuffle),
430    ("SkipPhase", ApiType::SkipPhase),
431    ("SkipTurn", ApiType::SkipTurn),
432    ("StoreSVar", ApiType::StoreSVar),
433    ("Subgame", ApiType::Subgame),
434    ("Surveil", ApiType::Surveil),
435    ("SwitchBlock", ApiType::SwitchBlock),
436    ("TakeInitiative", ApiType::TakeInitiative),
437    ("Tap", ApiType::Tap),
438    ("TapAll", ApiType::TapAll),
439    ("TapOrUntap", ApiType::TapOrUntap),
440    ("TapOrUntapAll", ApiType::TapOrUntapAll),
441    ("TimeTravel", ApiType::TimeTravel),
442    ("Token", ApiType::Token),
443    ("TwoPiles", ApiType::TwoPiles),
444    ("Unattach", ApiType::Unattach),
445    ("UnlockDoor", ApiType::UnlockDoor),
446    ("Untap", ApiType::Untap),
447    ("UntapAll", ApiType::UntapAll),
448    ("Venture", ApiType::Venture),
449    ("VillainousChoice", ApiType::VillainousChoice),
450    ("Vote", ApiType::Vote),
451    ("WinsGame", ApiType::WinsGame),
452    ("ZoneExchange", ApiType::ZoneExchange),
453    // Internal
454    ("BlankLine", ApiType::BlankLine),
455    ("DamageResolve", ApiType::DamageResolve),
456    ("ChangeZoneResolve", ApiType::ChangeZoneResolve),
457    ("CompanionChoose", ApiType::CompanionChoose),
458    ("InternalLegendaryRule", ApiType::InternalLegendaryRule),
459    ("InternalIgnoreEffect", ApiType::InternalIgnoreEffect),
460    ("InternalRadiation", ApiType::InternalRadiation),
461    // ── Aliases: alternate script names that map to canonical variants ──
462    ("ControlExchange", ApiType::ExchangeControl),
463    ("ControlGain", ApiType::GainControl),
464    ("ControlGainVariant", ApiType::GainControlVariant),
465    ("LifeSet", ApiType::SetLife),
466    ("LifeExchange", ApiType::ExchangeLife),
467    ("LifeExchangeVariant", ApiType::ExchangeLifeVariant),
468    ("GameWin", ApiType::WinsGame),
469    ("GameLoss", ApiType::LosesGame),
470    ("GameDraw", ApiType::GameDrawn),
471    ("RingTempts", ApiType::RingTemptsYou),
472    ("TaporUntapAll", ApiType::TapOrUntapAll),
473    ("CountersMultiply", ApiType::MultiplyCounter),
474    ("CountersRemoveAll", ApiType::RemoveCounterAll),
475    ("CountersPutOrRemove", ApiType::AddOrRemoveCounter),
476    ("PowerExchange", ApiType::ExchangePower),
477    ("TextBoxExchange", ApiType::ExchangeTextBox),
478    ("Replace", ApiType::ReplaceEffect),
479];
480
481#[cfg(test)]
482mod tests {
483    use super::*;
484
485    #[test]
486    fn smart_value_of_basic() {
487        assert_eq!(
488            ApiType::smart_value_of("DealDamage"),
489            Some(ApiType::DealDamage)
490        );
491        assert_eq!(
492            ApiType::smart_value_of("dealdamage"),
493            Some(ApiType::DealDamage)
494        );
495        assert_eq!(
496            ApiType::smart_value_of("DEALDAMAGE"),
497            Some(ApiType::DealDamage)
498        );
499    }
500
501    #[test]
502    fn smart_value_of_unknown() {
503        assert_eq!(ApiType::smart_value_of("NotAnApi"), None);
504    }
505
506    #[test]
507    fn name_roundtrip() {
508        let api = ApiType::GainLife;
509        assert_eq!(ApiType::smart_value_of(api.name()), Some(api));
510    }
511
512    #[test]
513    fn alias_resolution() {
514        assert_eq!(
515            ApiType::smart_value_of("ControlGain"),
516            Some(ApiType::GainControl)
517        );
518        assert_eq!(ApiType::smart_value_of("LifeSet"), Some(ApiType::SetLife));
519        assert_eq!(ApiType::smart_value_of("GameWin"), Some(ApiType::WinsGame));
520        assert_eq!(
521            ApiType::smart_value_of("RingTempts"),
522            Some(ApiType::RingTemptsYou)
523        );
524        assert_eq!(
525            ApiType::smart_value_of("TaporUntapAll"),
526            Some(ApiType::TapOrUntapAll)
527        );
528        assert_eq!(
529            ApiType::smart_value_of("Replace"),
530            Some(ApiType::ReplaceEffect)
531        );
532    }
533}