Skip to main content

manabrew_engine/keyword/
keyword_instance.rs

1//! Keyword instance base data and the Keyword enum.
2//!
3//! Ported from Java's `KeywordInstance.java` and `Keyword.java` in `forge/game/keyword/`.
4
5use std::collections::HashMap;
6use std::fmt;
7
8/// Base data shared by all keyword instances.
9/// Mirrors Java's `KeywordInstance` abstract class fields.
10#[derive(Debug, Clone)]
11pub struct KeywordInstanceData {
12    /// The keyword enum variant.
13    pub keyword: Keyword,
14    /// The original keyword string as parsed from the card.
15    pub original: String,
16    /// Whether this keyword is intrinsic (printed on the card).
17    pub intrinsic: bool,
18    /// Unique index for this keyword instance.
19    pub idx: i64,
20}
21
22impl KeywordInstanceData {
23    /// Create new keyword instance data.
24    pub fn new(keyword: Keyword, original: String) -> Self {
25        Self {
26            keyword,
27            original,
28            intrinsic: false,
29            idx: -1,
30        }
31    }
32}
33
34/// A keyword instance with its associated traits (triggers, replacement effects,
35/// static abilities, spell abilities).
36/// Mirrors Java's `KeywordInstance<T>` abstract class.
37#[derive(Debug, Clone)]
38pub struct KeywordInstance {
39    /// The underlying keyword data (keyword enum, original string, intrinsic flag, idx).
40    pub data: KeywordInstanceData,
41    /// Trigger definitions associated with this keyword.
42    pub triggers: Vec<String>,
43    /// Replacement effect definitions associated with this keyword.
44    pub replacements: Vec<String>,
45    /// Spell ability definitions associated with this keyword.
46    pub spell_abilities: Vec<String>,
47    /// Static ability definitions associated with this keyword.
48    pub static_abilities: Vec<String>,
49    /// SVars associated with this keyword instance.
50    pub svars: HashMap<String, String>,
51}
52
53impl KeywordInstance {
54    /// Create a new keyword instance from base data.
55    pub fn new(data: KeywordInstanceData) -> Self {
56        Self {
57            data,
58            triggers: Vec::new(),
59            replacements: Vec::new(),
60            spell_abilities: Vec::new(),
61            static_abilities: Vec::new(),
62            svars: HashMap::new(),
63        }
64    }
65
66    /// Initialize trait lists from the keyword. Clears existing traits and
67    /// re-parses them from the keyword definition.
68    /// Mirrors Java's `KeywordInstance.createTraits(Card, boolean)`.
69    pub fn create_traits(&mut self) {
70        self.triggers.clear();
71        self.replacements.clear();
72        self.spell_abilities.clear();
73        self.static_abilities.clear();
74    }
75
76    /// Add a trigger definition string.
77    /// Mirrors Java's `KeywordInstance.addTrigger(Trigger)`.
78    pub fn add_trigger(&mut self, trigger: String) {
79        self.triggers.push(trigger);
80    }
81
82    /// Add a replacement effect definition string.
83    /// Mirrors Java's `KeywordInstance.addReplacement(ReplacementEffect)`.
84    pub fn add_replacement(&mut self, replacement: String) {
85        self.replacements.push(replacement);
86    }
87
88    /// Add a spell ability definition string.
89    /// Mirrors Java's `KeywordInstance.addSpellAbility(SpellAbility)`.
90    pub fn add_spell_ability(&mut self, ability: String) {
91        self.spell_abilities.push(ability);
92    }
93
94    /// Add a static ability definition string.
95    /// Mirrors Java's `KeywordInstance.addStaticAbility(StaticAbility)`.
96    pub fn add_static_ability(&mut self, static_ab: String) {
97        self.static_abilities.push(static_ab);
98    }
99
100    /// Returns true if any traits (triggers, replacements, spell abilities,
101    /// or static abilities) are defined.
102    /// Mirrors Java's `KeywordInstance.hasTraits()`.
103    pub fn has_traits(&self) -> bool {
104        !self.triggers.is_empty()
105            || !self.replacements.is_empty()
106            || !self.spell_abilities.is_empty()
107            || !self.static_abilities.is_empty()
108    }
109
110    /// Apply this keyword's spell abilities to a card by parsing each spell
111    /// ability string and adding it to the card's abilities list.
112    /// Mirrors Java's `KeywordInstance.applySpellAbility(List)`.
113    pub fn apply_spell_ability(&self, card: &mut crate::card::Card) {
114        for sa in &self.spell_abilities {
115            card.abilities.push(sa.clone());
116        }
117    }
118
119    /// Apply this keyword's triggers to a card by parsing each trigger string
120    /// and adding it to the card's triggers list.
121    /// Mirrors Java's `KeywordInstance.applyTrigger(List)`.
122    pub fn apply_trigger(&self, card: &mut crate::card::Card) {
123        let mut next_id = card.triggers.len() as u32;
124        for trig_str in &self.triggers {
125            if let Some(trigger) = crate::trigger::trigger::parse_trigger(trig_str, &mut next_id) {
126                card.add_trigger(trigger);
127            }
128        }
129    }
130
131    /// Apply this keyword's replacement effects to a card by parsing each
132    /// replacement effect string and adding it to the card's replacement effects list.
133    /// Mirrors Java's `KeywordInstance.applyReplacementEffect(List)`.
134    pub fn apply_replacement_effect(&self, card: &mut crate::card::Card) {
135        for repl_str in &self.replacements {
136            if let Some(repl) =
137                crate::replacement::replacement_effect::parse_replacement_effect(repl_str)
138            {
139                card.add_replacement_effect(repl);
140            }
141        }
142    }
143
144    /// Apply this keyword's static abilities to a card by parsing each static
145    /// ability string and adding it to the card's static abilities list.
146    /// Mirrors Java's `KeywordInstance.applyStaticAbility(List)`.
147    pub fn apply_static_ability(&self, card: &mut crate::card::Card) {
148        for sa_str in &self.static_abilities {
149            if let Some(sa) = crate::staticability::static_ability::parse_static_ability(sa_str) {
150                card.static_abilities.push(sa);
151            }
152        }
153    }
154
155    /// Create a deep copy of this keyword instance.
156    /// Mirrors Java's `KeywordInstance.copy(Card, boolean)`.
157    pub fn copy(&self) -> Self {
158        self.clone()
159    }
160
161    /// Check if this keyword instance is redundant with another (same keyword text).
162    /// Mirrors Java's `KeywordInstance.redundant(Collection)`.
163    pub fn redundant(&self, other: &Self) -> bool {
164        if !self.data.keyword.is_multiple_redundant() {
165            return false;
166        }
167        self.data.original == other.data.original
168    }
169
170    /// Check if this keyword instance has an SVar with the given name.
171    /// Mirrors Java's `KeywordInstance.hasSVar(String)`.
172    pub fn has_s_var(&self, name: &str) -> bool {
173        self.svars.contains_key(name)
174    }
175
176    /// Remove an SVar from this keyword instance by name.
177    /// Mirrors Java's `KeywordInstance.removeSVar(String)`.
178    pub fn remove_s_var(&mut self, name: &str) {
179        self.svars.remove(name);
180    }
181}
182
183/// The Keyword enum with all keyword variants.
184/// Mirrors Java's `Keyword` enum with 213 entries.
185#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
186pub enum Keyword {
187    Undefined,
188    Absorb,
189    Adapt,
190    Affinity,
191    Afflict,
192    Afterlife,
193    Aftermath,
194    Amplify,
195    Annihilator,
196    Ascend,
197    Assist,
198    AuraSwap,
199    Awaken,
200    Backup,
201    Banding,
202    BandsWith,
203    Bargain,
204    BattleCry,
205    Bestow,
206    Blitz,
207    Bloodthirst,
208    Bushido,
209    Buyback,
210    Cascade,
211    Casualty,
212    Champion,
213    Changeling,
214    ChooseABackground,
215    Cipher,
216    Companion,
217    Compleated,
218    Conspire,
219    Convoke,
220    Craft,
221    Crew,
222    CumulativeUpkeep,
223    Cycling,
224    Dash,
225    Daybound,
226    Deathtouch,
227    Decayed,
228    Defender,
229    Delve,
230    Demonstrate,
231    Dethrone,
232    Devour,
233    Devoid,
234    Disguise,
235    Disturb,
236    DoctorsCompanion,
237    DoubleAgenda,
238    DoubleStrike,
239    DoubleTeam,
240    Dredge,
241    Echo,
242    Embalm,
243    Emerge,
244    Enchant,
245    Encore,
246    Enlist,
247    Entwine,
248    Epic,
249    Equip,
250    Escape,
251    Escalate,
252    Eternalize,
253    Evoke,
254    Evolve,
255    Exalted,
256    Exploit,
257    Extort,
258    Fabricate,
259    Fading,
260    Fear,
261    Firebending,
262    FirstStrike,
263    Flanking,
264    Flash,
265    Flashback,
266    Flying,
267    ForMirrodin,
268    Foretell,
269    Fortify,
270    Freerunning,
271    Frenzy,
272    Fuse,
273    Gift,
274    Graft,
275    Gravestorm,
276    Harmonize,
277    Haste,
278    Haunt,
279    Hexproof,
280    Hideaway,
281    HiddenAgenda,
282    Horsemanship,
283    Impending,
284    Improvise,
285    Indestructible,
286    Infect,
287    Ingest,
288    Intimidate,
289    Kicker,
290    JobSelect,
291    JumpStart,
292    Landwalk,
293    LevelUp,
294    Lifelink,
295    LivingMetal,
296    LivingWeapon,
297    Madness,
298    Mayhem,
299    Melee,
300    Mentor,
301    Menace,
302    Megamorph,
303    Miracle,
304    Mobilize,
305    Monstrosity,
306    Modular,
307    MoreThanMeetsTheEye,
308    Morph,
309    Multikicker,
310    Mutate,
311    Myriad,
312    Nightbound,
313    Ninjutsu,
314    Outlast,
315    Offering,
316    Offspring,
317    Overload,
318    Partner,
319    PartnerWith,
320    Persist,
321    Phasing,
322    Plot,
323    Poisonous,
324    Protection,
325    Prototype,
326    Provoke,
327    Prowess,
328    Prowl,
329    Rampage,
330    Ravenous,
331    Reach,
332    ReadAhead,
333    Rebound,
334    Recover,
335    Reconfigure,
336    Reflect,
337    Reinforce,
338    Renown,
339    Replicate,
340    Retrace,
341    Riot,
342    Ripple,
343    Saddle,
344    Scavenge,
345    Shadow,
346    Shroud,
347    Skulk,
348    Sneak,
349    Soulbond,
350    Soulshift,
351    SpaceSculptor,
352    Specialize,
353    Spectacle,
354    Splice,
355    SplitSecond,
356    Spree,
357    Squad,
358    StartYourEngines,
359    StartingIntensity,
360    Station,
361    Storm,
362    Strive,
363    Sunburst,
364    Surge,
365    Suspend,
366    Tiered,
367    Toxic,
368    Training,
369    Trample,
370    Transfigure,
371    Transmute,
372    Tribute,
373    TypeCycling,
374    UmbraArmor,
375    Undaunted,
376    Undying,
377    Unearth,
378    Unleash,
379    Vanishing,
380    Vigilance,
381    Ward,
382    Warp,
383    WebSlinging,
384    Wither,
385    MayFlashCost,
386    MayFlashSac,
387}
388
389impl Keyword {
390    /// Get the display name for this keyword.
391    pub fn display_name(&self) -> &'static str {
392        match self {
393            Keyword::Undefined => "",
394            Keyword::Absorb => "Absorb",
395            Keyword::Adapt => "Adapt",
396            Keyword::Affinity => "Affinity",
397            Keyword::Afflict => "Afflict",
398            Keyword::Afterlife => "Afterlife",
399            Keyword::Aftermath => "Aftermath",
400            Keyword::Amplify => "Amplify",
401            Keyword::Annihilator => "Annihilator",
402            Keyword::Ascend => "Ascend",
403            Keyword::Assist => "Assist",
404            Keyword::AuraSwap => "Aura swap",
405            Keyword::Awaken => "Awaken",
406            Keyword::Backup => "Backup",
407            Keyword::Banding => "Banding",
408            Keyword::BandsWith => "Bands with other",
409            Keyword::Bargain => "Bargain",
410            Keyword::BattleCry => "Battle cry",
411            Keyword::Bestow => "Bestow",
412            Keyword::Blitz => "Blitz",
413            Keyword::Bloodthirst => "Bloodthirst",
414            Keyword::Bushido => "Bushido",
415            Keyword::Buyback => "Buyback",
416            Keyword::Cascade => "Cascade",
417            Keyword::Casualty => "Casualty",
418            Keyword::Champion => "Champion",
419            Keyword::Changeling => "Changeling",
420            Keyword::ChooseABackground => "Choose a Background",
421            Keyword::Cipher => "Cipher",
422            Keyword::Companion => "Companion",
423            Keyword::Compleated => "Compleated",
424            Keyword::Conspire => "Conspire",
425            Keyword::Convoke => "Convoke",
426            Keyword::Craft => "Craft",
427            Keyword::Crew => "Crew",
428            Keyword::CumulativeUpkeep => "Cumulative upkeep",
429            Keyword::Cycling => "Cycling",
430            Keyword::Dash => "Dash",
431            Keyword::Daybound => "Daybound",
432            Keyword::Deathtouch => "Deathtouch",
433            Keyword::Decayed => "Decayed",
434            Keyword::Defender => "Defender",
435            Keyword::Delve => "Delve",
436            Keyword::Demonstrate => "Demonstrate",
437            Keyword::Dethrone => "Dethrone",
438            Keyword::Devour => "Devour",
439            Keyword::Devoid => "Devoid",
440            Keyword::Disguise => "Disguise",
441            Keyword::Disturb => "Disturb",
442            Keyword::DoctorsCompanion => "Doctor's companion",
443            Keyword::DoubleAgenda => "Double agenda",
444            Keyword::DoubleStrike => "Double Strike",
445            Keyword::DoubleTeam => "Double team",
446            Keyword::Dredge => "Dredge",
447            Keyword::Echo => "Echo",
448            Keyword::Embalm => "Embalm",
449            Keyword::Emerge => "Emerge",
450            Keyword::Enchant => "Enchant",
451            Keyword::Encore => "Encore",
452            Keyword::Enlist => "Enlist",
453            Keyword::Entwine => "Entwine",
454            Keyword::Epic => "Epic",
455            Keyword::Equip => "Equip",
456            Keyword::Escape => "Escape",
457            Keyword::Escalate => "Escalate",
458            Keyword::Eternalize => "Eternalize",
459            Keyword::Evoke => "Evoke",
460            Keyword::Evolve => "Evolve",
461            Keyword::Exalted => "Exalted",
462            Keyword::Exploit => "Exploit",
463            Keyword::Extort => "Extort",
464            Keyword::Fabricate => "Fabricate",
465            Keyword::Fading => "Fading",
466            Keyword::Fear => "Fear",
467            Keyword::Firebending => "Firebending",
468            Keyword::FirstStrike => "First Strike",
469            Keyword::Flanking => "Flanking",
470            Keyword::Flash => "Flash",
471            Keyword::Flashback => "Flashback",
472            Keyword::Flying => "Flying",
473            Keyword::ForMirrodin => "For Mirrodin",
474            Keyword::Foretell => "Foretell",
475            Keyword::Fortify => "Fortify",
476            Keyword::Freerunning => "Freerunning",
477            Keyword::Frenzy => "Frenzy",
478            Keyword::Fuse => "Fuse",
479            Keyword::Gift => "Gift",
480            Keyword::Graft => "Graft",
481            Keyword::Gravestorm => "Gravestorm",
482            Keyword::Harmonize => "Harmonize",
483            Keyword::Haste => "Haste",
484            Keyword::Haunt => "Haunt",
485            Keyword::Hexproof => "Hexproof",
486            Keyword::Hideaway => "Hideaway",
487            Keyword::HiddenAgenda => "Hidden agenda",
488            Keyword::Horsemanship => "Horsemanship",
489            Keyword::Impending => "Impending",
490            Keyword::Improvise => "Improvise",
491            Keyword::Indestructible => "Indestructible",
492            Keyword::Infect => "Infect",
493            Keyword::Ingest => "Ingest",
494            Keyword::Intimidate => "Intimidate",
495            Keyword::Kicker => "Kicker",
496            Keyword::JobSelect => "Job select",
497            Keyword::JumpStart => "Jump-start",
498            Keyword::Landwalk => "Landwalk",
499            Keyword::LevelUp => "Level up",
500            Keyword::Lifelink => "Lifelink",
501            Keyword::LivingMetal => "Living metal",
502            Keyword::LivingWeapon => "Living Weapon",
503            Keyword::Madness => "Madness",
504            Keyword::Mayhem => "Mayhem",
505            Keyword::Melee => "Melee",
506            Keyword::Mentor => "Mentor",
507            Keyword::Menace => "Menace",
508            Keyword::Megamorph => "Megamorph",
509            Keyword::Miracle => "Miracle",
510            Keyword::Mobilize => "Mobilize",
511            Keyword::Monstrosity => "Monstrosity",
512            Keyword::Modular => "Modular",
513            Keyword::MoreThanMeetsTheEye => "More Than Meets the Eye",
514            Keyword::Morph => "Morph",
515            Keyword::Multikicker => "Multikicker",
516            Keyword::Mutate => "Mutate",
517            Keyword::Myriad => "Myriad",
518            Keyword::Nightbound => "Nightbound",
519            Keyword::Ninjutsu => "Ninjutsu",
520            Keyword::Outlast => "Outlast",
521            Keyword::Offering => "Offering",
522            Keyword::Offspring => "Offspring",
523            Keyword::Overload => "Overload",
524            Keyword::Partner => "Partner",
525            Keyword::PartnerWith => "Partner with",
526            Keyword::Persist => "Persist",
527            Keyword::Phasing => "Phasing",
528            Keyword::Plot => "Plot",
529            Keyword::Poisonous => "Poisonous",
530            Keyword::Protection => "Protection",
531            Keyword::Prototype => "Prototype",
532            Keyword::Provoke => "Provoke",
533            Keyword::Prowess => "Prowess",
534            Keyword::Prowl => "Prowl",
535            Keyword::Rampage => "Rampage",
536            Keyword::Ravenous => "Ravenous",
537            Keyword::Reach => "Reach",
538            Keyword::ReadAhead => "Read ahead",
539            Keyword::Rebound => "Rebound",
540            Keyword::Recover => "Recover",
541            Keyword::Reconfigure => "Reconfigure",
542            Keyword::Reflect => "Reflect",
543            Keyword::Reinforce => "Reinforce",
544            Keyword::Renown => "Renown",
545            Keyword::Replicate => "Replicate",
546            Keyword::Retrace => "Retrace",
547            Keyword::Riot => "Riot",
548            Keyword::Ripple => "Ripple",
549            Keyword::Saddle => "Saddle",
550            Keyword::Scavenge => "Scavenge",
551            Keyword::Shadow => "Shadow",
552            Keyword::Shroud => "Shroud",
553            Keyword::Skulk => "Skulk",
554            Keyword::Sneak => "Sneak",
555            Keyword::Soulbond => "Soulbond",
556            Keyword::Soulshift => "Soulshift",
557            Keyword::SpaceSculptor => "Space sculptor",
558            Keyword::Specialize => "Specialize",
559            Keyword::Spectacle => "Spectacle",
560            Keyword::Splice => "Splice",
561            Keyword::SplitSecond => "Split second",
562            Keyword::Spree => "Spree",
563            Keyword::Squad => "Squad",
564            Keyword::StartYourEngines => "Start your engines",
565            Keyword::StartingIntensity => "Starting intensity",
566            Keyword::Station => "Station",
567            Keyword::Storm => "Storm",
568            Keyword::Strive => "Strive",
569            Keyword::Sunburst => "Sunburst",
570            Keyword::Surge => "Surge",
571            Keyword::Suspend => "Suspend",
572            Keyword::Tiered => "Tiered",
573            Keyword::Toxic => "Toxic",
574            Keyword::Training => "Training",
575            Keyword::Trample => "Trample",
576            Keyword::Transfigure => "Transfigure",
577            Keyword::Transmute => "Transmute",
578            Keyword::Tribute => "Tribute",
579            Keyword::TypeCycling => "TypeCycling",
580            Keyword::UmbraArmor => "Umbra armor",
581            Keyword::Undaunted => "Undaunted",
582            Keyword::Undying => "Undying",
583            Keyword::Unearth => "Unearth",
584            Keyword::Unleash => "Unleash",
585            Keyword::Vanishing => "Vanishing",
586            Keyword::Vigilance => "Vigilance",
587            Keyword::Ward => "Ward",
588            Keyword::Warp => "Warp",
589            Keyword::WebSlinging => "Web-slinging",
590            Keyword::Wither => "Wither",
591            Keyword::MayFlashCost => "MayFlashCost",
592            Keyword::MayFlashSac => "MayFlashSac",
593        }
594    }
595
596    /// Whether multiple instances of this keyword are redundant.
597    pub fn is_multiple_redundant(&self) -> bool {
598        matches!(
599            self,
600            Keyword::Ascend
601                | Keyword::Assist
602                | Keyword::Banding
603                | Keyword::Changeling
604                | Keyword::Cipher
605                | Keyword::Companion
606                | Keyword::Compleated
607                | Keyword::Convoke
608                | Keyword::Daybound
609                | Keyword::Deathtouch
610                | Keyword::Decayed
611                | Keyword::Defender
612                | Keyword::Delve
613                | Keyword::Devoid
614                | Keyword::DoubleStrike
615                | Keyword::Entwine
616                | Keyword::Epic
617                | Keyword::Fear
618                | Keyword::FirstStrike
619                | Keyword::Flash
620                | Keyword::Flying
621                | Keyword::Fuse
622                | Keyword::Gift
623                | Keyword::Haste
624                | Keyword::Hexproof
625                | Keyword::HiddenAgenda
626                | Keyword::Horsemanship
627                | Keyword::Improvise
628                | Keyword::Indestructible
629                | Keyword::Infect
630                | Keyword::Intimidate
631                | Keyword::Landwalk
632                | Keyword::Lifelink
633                | Keyword::LivingMetal
634                | Keyword::LivingWeapon
635                | Keyword::Mutate
636                | Keyword::Nightbound
637                | Keyword::Partner
638                | Keyword::Phasing
639                | Keyword::Protection
640                | Keyword::Reach
641                | Keyword::ReadAhead
642                | Keyword::Rebound
643                | Keyword::Shadow
644                | Keyword::Shroud
645                | Keyword::Skulk
646                | Keyword::Soulbond
647                | Keyword::SpaceSculptor
648                | Keyword::SplitSecond
649                | Keyword::Spree
650                | Keyword::StartYourEngines
651                | Keyword::StartingIntensity
652                | Keyword::Tiered
653                | Keyword::Trample
654                | Keyword::UmbraArmor
655                | Keyword::Vigilance
656                | Keyword::Wither
657        )
658    }
659
660    /// Look up a keyword by its display name (case-insensitive).
661    pub fn smart_value_of(value: &str) -> Keyword {
662        // Check all variants for a case-insensitive match on display name.
663        for kw in Self::all_variants() {
664            if kw.display_name().eq_ignore_ascii_case(value) {
665                return kw;
666            }
667        }
668        Keyword::Undefined
669    }
670
671    /// Return an iterator over all keyword variants (excluding Undefined).
672    pub fn all_keywords() -> impl Iterator<Item = Keyword> {
673        Self::all_variants()
674            .into_iter()
675            .filter(|k| *k != Keyword::Undefined)
676    }
677
678    /// Return all enum variants as a slice-like vec.
679    fn all_variants() -> Vec<Keyword> {
680        vec![
681            Keyword::Undefined,
682            Keyword::Absorb,
683            Keyword::Adapt,
684            Keyword::Affinity,
685            Keyword::Afflict,
686            Keyword::Afterlife,
687            Keyword::Aftermath,
688            Keyword::Amplify,
689            Keyword::Annihilator,
690            Keyword::Ascend,
691            Keyword::Assist,
692            Keyword::AuraSwap,
693            Keyword::Awaken,
694            Keyword::Backup,
695            Keyword::Banding,
696            Keyword::BandsWith,
697            Keyword::Bargain,
698            Keyword::BattleCry,
699            Keyword::Bestow,
700            Keyword::Blitz,
701            Keyword::Bloodthirst,
702            Keyword::Bushido,
703            Keyword::Buyback,
704            Keyword::Cascade,
705            Keyword::Casualty,
706            Keyword::Champion,
707            Keyword::Changeling,
708            Keyword::ChooseABackground,
709            Keyword::Cipher,
710            Keyword::Companion,
711            Keyword::Compleated,
712            Keyword::Conspire,
713            Keyword::Convoke,
714            Keyword::Craft,
715            Keyword::Crew,
716            Keyword::CumulativeUpkeep,
717            Keyword::Cycling,
718            Keyword::Dash,
719            Keyword::Daybound,
720            Keyword::Deathtouch,
721            Keyword::Decayed,
722            Keyword::Defender,
723            Keyword::Delve,
724            Keyword::Demonstrate,
725            Keyword::Dethrone,
726            Keyword::Devour,
727            Keyword::Devoid,
728            Keyword::Disguise,
729            Keyword::Disturb,
730            Keyword::DoctorsCompanion,
731            Keyword::DoubleAgenda,
732            Keyword::DoubleStrike,
733            Keyword::DoubleTeam,
734            Keyword::Dredge,
735            Keyword::Echo,
736            Keyword::Embalm,
737            Keyword::Emerge,
738            Keyword::Enchant,
739            Keyword::Encore,
740            Keyword::Enlist,
741            Keyword::Entwine,
742            Keyword::Epic,
743            Keyword::Equip,
744            Keyword::Escape,
745            Keyword::Escalate,
746            Keyword::Eternalize,
747            Keyword::Evoke,
748            Keyword::Evolve,
749            Keyword::Exalted,
750            Keyword::Exploit,
751            Keyword::Extort,
752            Keyword::Fabricate,
753            Keyword::Fading,
754            Keyword::Fear,
755            Keyword::Firebending,
756            Keyword::FirstStrike,
757            Keyword::Flanking,
758            Keyword::Flash,
759            Keyword::Flashback,
760            Keyword::Flying,
761            Keyword::ForMirrodin,
762            Keyword::Foretell,
763            Keyword::Fortify,
764            Keyword::Freerunning,
765            Keyword::Frenzy,
766            Keyword::Fuse,
767            Keyword::Gift,
768            Keyword::Graft,
769            Keyword::Gravestorm,
770            Keyword::Harmonize,
771            Keyword::Haste,
772            Keyword::Haunt,
773            Keyword::Hexproof,
774            Keyword::Hideaway,
775            Keyword::HiddenAgenda,
776            Keyword::Horsemanship,
777            Keyword::Impending,
778            Keyword::Improvise,
779            Keyword::Indestructible,
780            Keyword::Infect,
781            Keyword::Ingest,
782            Keyword::Intimidate,
783            Keyword::Kicker,
784            Keyword::JobSelect,
785            Keyword::JumpStart,
786            Keyword::Landwalk,
787            Keyword::LevelUp,
788            Keyword::Lifelink,
789            Keyword::LivingMetal,
790            Keyword::LivingWeapon,
791            Keyword::Madness,
792            Keyword::Mayhem,
793            Keyword::Melee,
794            Keyword::Mentor,
795            Keyword::Menace,
796            Keyword::Megamorph,
797            Keyword::Miracle,
798            Keyword::Mobilize,
799            Keyword::Monstrosity,
800            Keyword::Modular,
801            Keyword::MoreThanMeetsTheEye,
802            Keyword::Morph,
803            Keyword::Multikicker,
804            Keyword::Mutate,
805            Keyword::Myriad,
806            Keyword::Nightbound,
807            Keyword::Ninjutsu,
808            Keyword::Outlast,
809            Keyword::Offering,
810            Keyword::Offspring,
811            Keyword::Overload,
812            Keyword::Partner,
813            Keyword::PartnerWith,
814            Keyword::Persist,
815            Keyword::Phasing,
816            Keyword::Plot,
817            Keyword::Poisonous,
818            Keyword::Protection,
819            Keyword::Prototype,
820            Keyword::Provoke,
821            Keyword::Prowess,
822            Keyword::Prowl,
823            Keyword::Rampage,
824            Keyword::Ravenous,
825            Keyword::Reach,
826            Keyword::ReadAhead,
827            Keyword::Rebound,
828            Keyword::Recover,
829            Keyword::Reconfigure,
830            Keyword::Reflect,
831            Keyword::Reinforce,
832            Keyword::Renown,
833            Keyword::Replicate,
834            Keyword::Retrace,
835            Keyword::Riot,
836            Keyword::Ripple,
837            Keyword::Saddle,
838            Keyword::Scavenge,
839            Keyword::Shadow,
840            Keyword::Shroud,
841            Keyword::Skulk,
842            Keyword::Sneak,
843            Keyword::Soulbond,
844            Keyword::Soulshift,
845            Keyword::SpaceSculptor,
846            Keyword::Specialize,
847            Keyword::Spectacle,
848            Keyword::Splice,
849            Keyword::SplitSecond,
850            Keyword::Spree,
851            Keyword::Squad,
852            Keyword::StartYourEngines,
853            Keyword::StartingIntensity,
854            Keyword::Station,
855            Keyword::Storm,
856            Keyword::Strive,
857            Keyword::Sunburst,
858            Keyword::Surge,
859            Keyword::Suspend,
860            Keyword::Tiered,
861            Keyword::Toxic,
862            Keyword::Training,
863            Keyword::Trample,
864            Keyword::Transfigure,
865            Keyword::Transmute,
866            Keyword::Tribute,
867            Keyword::TypeCycling,
868            Keyword::UmbraArmor,
869            Keyword::Undaunted,
870            Keyword::Undying,
871            Keyword::Unearth,
872            Keyword::Unleash,
873            Keyword::Vanishing,
874            Keyword::Vigilance,
875            Keyword::Ward,
876            Keyword::Warp,
877            Keyword::WebSlinging,
878            Keyword::Wither,
879            Keyword::MayFlashCost,
880            Keyword::MayFlashSac,
881        ]
882    }
883}
884
885impl fmt::Display for Keyword {
886    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
887        write!(f, "{}", self.display_name())
888    }
889}