screeps/constants/
types.rs

1//! `*Type` constants.
2use std::{borrow::Cow, fmt};
3
4use enum_iterator::Sequence;
5use num_derive::FromPrimitive;
6use num_traits::FromPrimitive;
7use serde::{
8    de::{Error as _, Unexpected},
9    Deserialize, Serialize,
10};
11use serde_repr::{Deserialize_repr, Serialize_repr};
12use wasm_bindgen::prelude::*;
13
14use super::{macros::named_enum_serialize_deserialize, InvalidConstantString};
15use crate::{JsCollectionFromValue, JsCollectionIntoValue};
16
17/// Translates `STRUCTURE_*` constants.
18#[wasm_bindgen]
19#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Sequence)]
20pub enum StructureType {
21    Spawn = "spawn",
22    Extension = "extension",
23    Road = "road",
24    Wall = "constructedWall",
25    Rampart = "rampart",
26    KeeperLair = "keeperLair",
27    Portal = "portal",
28    Controller = "controller",
29    Link = "link",
30    Storage = "storage",
31    Tower = "tower",
32    Observer = "observer",
33    PowerBank = "powerBank",
34    PowerSpawn = "powerSpawn",
35    Extractor = "extractor",
36    Lab = "lab",
37    Terminal = "terminal",
38    Container = "container",
39    Nuker = "nuker",
40    Factory = "factory",
41    InvaderCore = "invaderCore",
42}
43
44named_enum_serialize_deserialize!(StructureType);
45
46impl StructureType {
47    /// Translates the `CONSTRUCTION_COST` constant.
48    #[inline]
49    pub const fn construction_cost(self) -> Option<u32> {
50        use self::StructureType::*;
51
52        let cost = match self {
53            Spawn => 15_000,
54            Extension => 3_000,
55            Road => 300,
56            Wall => 1,
57            Rampart => 1,
58            Link => 5_000,
59            Storage => 30_000,
60            Tower => 5_000,
61            Observer => 8_000,
62            PowerSpawn => 100_000,
63            Extractor => 5_000,
64            Lab => 50_000,
65            Terminal => 100_000,
66            Container => 5_000,
67            Nuker => 100_000,
68            Factory => 100_000,
69            _ => return None,
70        };
71        Some(cost)
72    }
73
74    /// Translates the `CONTROLLER_STRUCTURES` constant
75    #[inline]
76    pub const fn controller_structures(self, current_rcl: u32) -> u32 {
77        use self::StructureType::*;
78
79        match self {
80            Spawn => match current_rcl {
81                0 => 0,
82                1..=6 => 1,
83                7 => 2,
84                _ => 3,
85            },
86            Extension => match current_rcl {
87                0 | 1 => 0,
88                2 => 5,
89                3 => 10,
90                4 => 20,
91                5 => 30,
92                6 => 40,
93                7 => 50,
94                _ => 60,
95            },
96            Road => 2500,
97            Wall => match current_rcl {
98                0 | 1 => 0,
99                _ => 2500,
100            },
101            Rampart => match current_rcl {
102                0 | 1 => 0,
103                _ => 2500,
104            },
105            Link => match current_rcl {
106                0..=4 => 0,
107                5 => 2,
108                6 => 3,
109                7 => 4,
110                _ => 6,
111            },
112            Storage => match current_rcl {
113                0..=3 => 0,
114                _ => 1,
115            },
116            Tower => match current_rcl {
117                0..=2 => 0,
118                3 | 4 => 1,
119                5 | 6 => 2,
120                7 => 3,
121                _ => 6,
122            },
123            Observer => match current_rcl {
124                0..=7 => 0,
125                _ => 1,
126            },
127            PowerSpawn => match current_rcl {
128                0..=7 => 0,
129                _ => 1,
130            },
131            Extractor => match current_rcl {
132                0..=5 => 0,
133                _ => 1,
134            },
135            Lab => match current_rcl {
136                0..=5 => 0,
137                6 => 3,
138                7 => 6,
139                _ => 10,
140            },
141            Terminal => match current_rcl {
142                0..=5 => 0,
143                _ => 1,
144            },
145            Container => 5,
146            Nuker => match current_rcl {
147                0..=7 => 0,
148                _ => 1,
149            },
150            Factory => match current_rcl {
151                0..=6 => 0,
152                _ => 1,
153            },
154            _ => 0,
155        }
156    }
157
158    /// Translates the `*_HITS` constants, initial hits for structures
159    #[inline]
160    pub const fn initial_hits(self) -> Option<u32> {
161        use self::StructureType::*;
162        use super::numbers::*;
163
164        let hits = match self {
165            Spawn => SPAWN_HITS,
166            Extension => EXTENSION_HITS,
167            Road => ROAD_HITS,
168            Wall => WALL_HITS,
169            Rampart => RAMPART_HITS,
170            Link => LINK_HITS,
171            Storage => STORAGE_HITS,
172            Tower => TOWER_HITS,
173            Observer => OBSERVER_HITS,
174            PowerBank => POWER_BANK_HITS,
175            PowerSpawn => POWER_SPAWN_HITS,
176            Extractor => EXTRACTOR_HITS,
177            Lab => LAB_HITS,
178            Terminal => TERMINAL_HITS,
179            Container => CONTAINER_HITS,
180            Nuker => NUKER_HITS,
181            Factory => FACTORY_HITS,
182            InvaderCore => INVADER_CORE_HITS,
183            _ => return None,
184        };
185        Some(hits)
186    }
187
188    /// Translates the `OBSTACLE_OBJECT_TYPES` constant, returning true for
189    /// structure types that are obstacles
190    #[inline]
191    pub const fn is_obstacle(self) -> bool {
192        use self::StructureType::*;
193
194        match self {
195            Spawn => true,
196            Extension => true,
197            Road => false,
198            Wall => true,
199            Rampart => false,
200            KeeperLair => false,
201            Portal => false,
202            Controller => true,
203            Link => true,
204            Storage => true,
205            Tower => true,
206            Observer => true,
207            PowerBank => true,
208            PowerSpawn => true,
209            Extractor => false,
210            Lab => true,
211            Terminal => true,
212            Container => false,
213            Nuker => true,
214            Factory => true,
215            InvaderCore => true,
216            _ => false,
217        }
218    }
219}
220
221/// Translates `SUBSCRIPTION_TOKEN` and `INTERSHARD_RESOURCES` constants.
222#[wasm_bindgen]
223#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Sequence)]
224pub enum IntershardResourceType {
225    // no longer used, not implemented
226    // SubscriptionToken = "token",
227    CpuUnlock = "cpuUnlock",
228    Pixel = "pixel",
229    AccessKey = "accessKey",
230}
231
232named_enum_serialize_deserialize!(IntershardResourceType);
233
234impl JsCollectionIntoValue for IntershardResourceType {
235    fn into_value(self) -> JsValue {
236        self.into()
237    }
238}
239
240impl JsCollectionFromValue for IntershardResourceType {
241    fn from_value(v: JsValue) -> IntershardResourceType {
242        IntershardResourceType::from_js_value(&v).expect("valid intershard resource type string")
243    }
244}
245
246/// Translates the values of the `RESOURCES_ALL` constant, representing all
247/// possible in-game (non-intershard) resources.
248#[wasm_bindgen]
249#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Sequence)]
250pub enum ResourceType {
251    Energy = "energy",
252    Power = "power",
253    Hydrogen = "H",
254    Oxygen = "O",
255    Utrium = "U",
256    Lemergium = "L",
257    Keanium = "K",
258    Zynthium = "Z",
259    Catalyst = "X",
260    Ghodium = "G",
261    Silicon = "silicon",
262    Metal = "metal",
263    Biomass = "biomass",
264    Mist = "mist",
265    Hydroxide = "OH",
266    ZynthiumKeanite = "ZK",
267    UtriumLemergite = "UL",
268    UtriumHydride = "UH",
269    UtriumOxide = "UO",
270    KeaniumHydride = "KH",
271    KeaniumOxide = "KO",
272    LemergiumHydride = "LH",
273    LemergiumOxide = "LO",
274    ZynthiumHydride = "ZH",
275    ZynthiumOxide = "ZO",
276    GhodiumHydride = "GH",
277    GhodiumOxide = "GO",
278    UtriumAcid = "UH2O",
279    UtriumAlkalide = "UHO2",
280    KeaniumAcid = "KH2O",
281    KeaniumAlkalide = "KHO2",
282    LemergiumAcid = "LH2O",
283    LemergiumAlkalide = "LHO2",
284    ZynthiumAcid = "ZH2O",
285    ZynthiumAlkalide = "ZHO2",
286    GhodiumAcid = "GH2O",
287    GhodiumAlkalide = "GHO2",
288    CatalyzedUtriumAcid = "XUH2O",
289    CatalyzedUtriumAlkalide = "XUHO2",
290    CatalyzedKeaniumAcid = "XKH2O",
291    CatalyzedKeaniumAlkalide = "XKHO2",
292    CatalyzedLemergiumAcid = "XLH2O",
293    CatalyzedLemergiumAlkalide = "XLHO2",
294    CatalyzedZynthiumAcid = "XZH2O",
295    CatalyzedZynthiumAlkalide = "XZHO2",
296    CatalyzedGhodiumAcid = "XGH2O",
297    CatalyzedGhodiumAlkalide = "XGHO2",
298    Ops = "ops",
299    UtriumBar = "utrium_bar",
300    LemergiumBar = "lemergium_bar",
301    ZynthiumBar = "zynthium_bar",
302    KeaniumBar = "keanium_bar",
303    GhodiumMelt = "ghodium_melt",
304    Oxidant = "oxidant",
305    Reductant = "reductant",
306    Purifier = "purifier",
307    Battery = "battery",
308    Composite = "composite",
309    Crystal = "crystal",
310    Liquid = "liquid",
311    Wire = "wire",
312    Switch = "switch",
313    Transistor = "transistor",
314    Microchip = "microchip",
315    Circuit = "circuit",
316    Device = "device",
317    Cell = "cell",
318    Phlegm = "phlegm",
319    Tissue = "tissue",
320    Muscle = "muscle",
321    Organoid = "organoid",
322    Organism = "organism",
323    Alloy = "alloy",
324    Tube = "tube",
325    Fixtures = "fixtures",
326    Frame = "frame",
327    Hydraulics = "hydraulics",
328    Machine = "machine",
329    Condensate = "condensate",
330    Concentrate = "concentrate",
331    Extract = "extract",
332    Spirit = "spirit",
333    Emanation = "emanation",
334    Essence = "essence",
335    #[cfg(feature = "seasonal-season-1")]
336    Score = "score",
337    #[cfg(feature = "seasonal-season-2")]
338    SymbolAleph = "symbol_aleph",
339    #[cfg(feature = "seasonal-season-2")]
340    SymbolBeth = "symbol_beth",
341    #[cfg(feature = "seasonal-season-2")]
342    SymbolGimmel = "symbol_gimmel",
343    #[cfg(feature = "seasonal-season-2")]
344    SymbolDaleth = "symbol_daleth",
345    #[cfg(feature = "seasonal-season-2")]
346    SymbolHe = "symbol_he",
347    #[cfg(feature = "seasonal-season-2")]
348    SymbolWaw = "symbol_waw",
349    #[cfg(feature = "seasonal-season-2")]
350    SymbolZayin = "symbol_zayin",
351    #[cfg(feature = "seasonal-season-2")]
352    SymbolHeth = "symbol_heth",
353    #[cfg(feature = "seasonal-season-2")]
354    SymbolTeth = "symbol_teth",
355    #[cfg(feature = "seasonal-season-2")]
356    SymbolYodh = "symbol_yodh",
357    #[cfg(feature = "seasonal-season-2")]
358    SymbolKaph = "symbol_kaph",
359    #[cfg(feature = "seasonal-season-2")]
360    SymbolLamedh = "symbol_lamedh",
361    #[cfg(feature = "seasonal-season-2")]
362    SymbolMem = "symbol_mem",
363    #[cfg(feature = "seasonal-season-2")]
364    SymbolNun = "symbol_nun",
365    #[cfg(feature = "seasonal-season-2")]
366    SymbolSamekh = "symbol_samekh",
367    #[cfg(feature = "seasonal-season-2")]
368    SymbolAyin = "symbol_ayin",
369    #[cfg(feature = "seasonal-season-2")]
370    SymbolPe = "symbol_pe",
371    #[cfg(feature = "seasonal-season-2")]
372    SymbolTsade = "symbol_tsade",
373    #[cfg(feature = "seasonal-season-2")]
374    SymbolQoph = "symbol_qoph",
375    #[cfg(feature = "seasonal-season-2")]
376    SymbolRes = "symbol_res",
377    // sin/sim mismatch is intended here - see official mod:
378    // https://github.com/screeps/mod-season2/blob/3dfaa8f6214b2610dbe2a700c6287a10e7960ae8/src/resources.js#L23
379    #[cfg(feature = "seasonal-season-2")]
380    SymbolSin = "symbol_sim",
381    #[cfg(feature = "seasonal-season-2")]
382    SymbolTaw = "symbol_taw",
383    #[cfg(feature = "seasonal-season-5")]
384    Thorium = "T",
385}
386
387named_enum_serialize_deserialize!(ResourceType);
388
389impl ResourceType {
390    /// Translates the `BOOSTS` constant.
391    #[inline]
392    pub const fn boost(self) -> Option<Boost> {
393        use ResourceType::*;
394        let boost = match self {
395            // these comments copied directly from JavaScript 'constants.js' file.
396            // UH: {
397            //     attack: 2
398            // },
399            UtriumHydride => Boost::Attack(2),
400            // UH2O: {
401            //     attack: 3
402            // },
403            UtriumAcid => Boost::Attack(3),
404            // XUH2O: {
405            //     attack: 4
406            // }
407            CatalyzedUtriumAcid => Boost::Attack(4),
408            // UO: {
409            //     harvest: 3
410            // },
411            UtriumOxide => Boost::Harvest(3),
412            // UHO2: {
413            //     harvest: 5
414            // },
415            UtriumAlkalide => Boost::Harvest(5),
416            // XUHO2: {
417            //     harvest: 7
418            // },
419            CatalyzedUtriumAlkalide => Boost::Harvest(7),
420            // KH: {
421            //     capacity: 2
422            // },
423            KeaniumHydride => Boost::Carry(2),
424            // KH2O: {
425            //     capacity: 3
426            // },
427            KeaniumAcid => Boost::Carry(3),
428            // XKH2O: {
429            //     capacity: 4
430            // }
431            CatalyzedKeaniumAcid => Boost::Carry(4),
432            // KO: {
433            //     rangedAttack: 2,
434            //     rangedMassAttack: 2
435            // },
436            KeaniumOxide => Boost::RangedAttack(2),
437            // KHO2: {
438            //     rangedAttack: 3,
439            //     rangedMassAttack: 3
440            // },
441            KeaniumAlkalide => Boost::RangedAttack(3),
442            // XKHO2: {
443            //     rangedAttack: 4,
444            //     rangedMassAttack: 4
445            // }
446            CatalyzedKeaniumAlkalide => Boost::RangedAttack(4),
447            // LH: {
448            //     build: 1.5,
449            //     repair: 1.5
450            // },
451            LemergiumHydride => Boost::BuildAndRepair(1.5),
452            // LH2O: {
453            //     build: 1.8,
454            //     repair: 1.8
455            // },
456            LemergiumAcid => Boost::BuildAndRepair(1.8),
457            // XLH2O: {
458            //     build: 2,
459            //     repair: 2
460            // },
461            CatalyzedLemergiumAcid => Boost::BuildAndRepair(2.0),
462            // LO: {
463            //     heal: 2,
464            //     rangedHeal: 2
465            // },
466            LemergiumOxide => Boost::Heal(2),
467            // LHO2: {
468            //     heal: 3,
469            //     rangedHeal: 3
470            // },
471            LemergiumAlkalide => Boost::Heal(3),
472            // XLHO2: {
473            //     heal: 4,
474            //     rangedHeal: 4
475            // }
476            CatalyzedLemergiumAlkalide => Boost::Heal(4),
477            // ZH: {
478            //     dismantle: 2
479            // },
480            ZynthiumHydride => Boost::Dismantle(2),
481            // ZH2O: {
482            //     dismantle: 3
483            // },
484            ZynthiumAcid => Boost::Dismantle(3),
485            // XZH2O: {
486            //     dismantle: 4
487            // },
488            CatalyzedZynthiumAcid => Boost::Dismantle(4),
489            // ZO: {
490            //     fatigue: 2
491            // },
492            ZynthiumOxide => Boost::Move(2),
493            // ZHO2: {
494            //     fatigue: 3
495            // },
496            ZynthiumAlkalide => Boost::Move(3),
497            // XZHO2: {
498            //     fatigue: 4
499            // }
500            CatalyzedZynthiumAlkalide => Boost::Move(4),
501            // GH: {
502            //     upgradeController: 1.5
503            // },
504            GhodiumHydride => Boost::UpgradeController(1.5),
505            // GH2O: {
506            //     upgradeController: 1.8
507            // },
508            GhodiumAcid => Boost::UpgradeController(1.8),
509            // XGH2O: {
510            //     upgradeController: 2
511            // }
512            CatalyzedGhodiumAcid => Boost::UpgradeController(2.0),
513            // GO: {
514            //     damage: .7
515            // },
516            GhodiumOxide => Boost::Tough(0.7),
517            // GHO2: {
518            //     damage: .5
519            // },
520            GhodiumAlkalide => Boost::Tough(0.5),
521            // XGHO2: {
522            //     damage: .3
523            // }
524            CatalyzedGhodiumAlkalide => Boost::Tough(0.3),
525            // non-boost resources
526            _ => return None,
527        };
528        Some(boost)
529    }
530}
531
532/// A collection of all resource types. This is a direct translation of the
533/// `RESOURCES_ALL` constant in game using the rust `ResourceType` enum.
534///
535/// Feature-specific resources are included in this list when the appropriate
536/// feature is enabled.
537pub const RESOURCES_ALL: &[ResourceType] = &[
538    ResourceType::Power,
539    ResourceType::Energy,
540    ResourceType::Hydrogen,
541    ResourceType::Oxygen,
542    ResourceType::Utrium,
543    ResourceType::Lemergium,
544    ResourceType::Keanium,
545    ResourceType::Zynthium,
546    ResourceType::Catalyst,
547    ResourceType::Ghodium,
548    ResourceType::Silicon,
549    ResourceType::Metal,
550    ResourceType::Biomass,
551    ResourceType::Mist,
552    ResourceType::Hydroxide,
553    ResourceType::ZynthiumKeanite,
554    ResourceType::UtriumLemergite,
555    ResourceType::UtriumHydride,
556    ResourceType::UtriumOxide,
557    ResourceType::KeaniumHydride,
558    ResourceType::KeaniumOxide,
559    ResourceType::LemergiumHydride,
560    ResourceType::LemergiumOxide,
561    ResourceType::ZynthiumHydride,
562    ResourceType::ZynthiumOxide,
563    ResourceType::GhodiumHydride,
564    ResourceType::GhodiumOxide,
565    ResourceType::UtriumAcid,
566    ResourceType::UtriumAlkalide,
567    ResourceType::KeaniumAcid,
568    ResourceType::KeaniumAlkalide,
569    ResourceType::LemergiumAcid,
570    ResourceType::LemergiumAlkalide,
571    ResourceType::ZynthiumAcid,
572    ResourceType::ZynthiumAlkalide,
573    ResourceType::GhodiumAcid,
574    ResourceType::GhodiumAlkalide,
575    ResourceType::CatalyzedUtriumAcid,
576    ResourceType::CatalyzedUtriumAlkalide,
577    ResourceType::CatalyzedKeaniumAcid,
578    ResourceType::CatalyzedKeaniumAlkalide,
579    ResourceType::CatalyzedLemergiumAcid,
580    ResourceType::CatalyzedLemergiumAlkalide,
581    ResourceType::CatalyzedZynthiumAcid,
582    ResourceType::CatalyzedZynthiumAlkalide,
583    ResourceType::CatalyzedGhodiumAcid,
584    ResourceType::CatalyzedGhodiumAlkalide,
585    ResourceType::Ops,
586    ResourceType::UtriumBar,
587    ResourceType::LemergiumBar,
588    ResourceType::ZynthiumBar,
589    ResourceType::KeaniumBar,
590    ResourceType::GhodiumMelt,
591    ResourceType::Oxidant,
592    ResourceType::Reductant,
593    ResourceType::Purifier,
594    ResourceType::Battery,
595    ResourceType::Composite,
596    ResourceType::Crystal,
597    ResourceType::Liquid,
598    ResourceType::Wire,
599    ResourceType::Switch,
600    ResourceType::Transistor,
601    ResourceType::Microchip,
602    ResourceType::Circuit,
603    ResourceType::Device,
604    ResourceType::Cell,
605    ResourceType::Phlegm,
606    ResourceType::Tissue,
607    ResourceType::Muscle,
608    ResourceType::Organoid,
609    ResourceType::Organism,
610    ResourceType::Alloy,
611    ResourceType::Tube,
612    ResourceType::Fixtures,
613    ResourceType::Frame,
614    ResourceType::Hydraulics,
615    ResourceType::Machine,
616    ResourceType::Condensate,
617    ResourceType::Concentrate,
618    ResourceType::Extract,
619    ResourceType::Spirit,
620    ResourceType::Emanation,
621    ResourceType::Essence,
622    #[cfg(feature = "seasonal-season-1")]
623    ResourceType::Score,
624    #[cfg(feature = "seasonal-season-2")]
625    ResourceType::SymbolAleph,
626    #[cfg(feature = "seasonal-season-2")]
627    ResourceType::SymbolBeth,
628    #[cfg(feature = "seasonal-season-2")]
629    ResourceType::SymbolGimmel,
630    #[cfg(feature = "seasonal-season-2")]
631    ResourceType::SymbolDaleth,
632    #[cfg(feature = "seasonal-season-2")]
633    ResourceType::SymbolHe,
634    #[cfg(feature = "seasonal-season-2")]
635    ResourceType::SymbolWaw,
636    #[cfg(feature = "seasonal-season-2")]
637    ResourceType::SymbolZayin,
638    #[cfg(feature = "seasonal-season-2")]
639    ResourceType::SymbolHeth,
640    #[cfg(feature = "seasonal-season-2")]
641    ResourceType::SymbolTeth,
642    #[cfg(feature = "seasonal-season-2")]
643    ResourceType::SymbolYodh,
644    #[cfg(feature = "seasonal-season-2")]
645    ResourceType::SymbolKaph,
646    #[cfg(feature = "seasonal-season-2")]
647    ResourceType::SymbolLamedh,
648    #[cfg(feature = "seasonal-season-2")]
649    ResourceType::SymbolMem,
650    #[cfg(feature = "seasonal-season-2")]
651    ResourceType::SymbolNun,
652    #[cfg(feature = "seasonal-season-2")]
653    ResourceType::SymbolSamekh,
654    #[cfg(feature = "seasonal-season-2")]
655    ResourceType::SymbolAyin,
656    #[cfg(feature = "seasonal-season-2")]
657    ResourceType::SymbolPe,
658    #[cfg(feature = "seasonal-season-2")]
659    ResourceType::SymbolTsade,
660    #[cfg(feature = "seasonal-season-2")]
661    ResourceType::SymbolQoph,
662    #[cfg(feature = "seasonal-season-2")]
663    ResourceType::SymbolRes,
664    #[cfg(feature = "seasonal-season-2")]
665    ResourceType::SymbolSin,
666    #[cfg(feature = "seasonal-season-2")]
667    ResourceType::SymbolTaw,
668    #[cfg(feature = "seasonal-season-5")]
669    ResourceType::Thorium,
670];
671
672/// Returned values from [`ResourceType::boost`] representing the effect of
673/// boosting a creep with the given resource.
674#[derive(Copy, Clone, Debug)]
675pub enum Boost {
676    Harvest(u32),
677    BuildAndRepair(f32),
678    Dismantle(u32),
679    UpgradeController(f32),
680    Attack(u32),
681    RangedAttack(u32),
682    Heal(u32),
683    Carry(u32),
684    Move(u32),
685    Tough(f32),
686}
687
688/// Translates all resource types that can be used on the market.
689#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Sequence)]
690#[serde(untagged)]
691pub enum MarketResourceType {
692    Resource(ResourceType),
693    IntershardResource(IntershardResourceType),
694}
695
696impl wasm_bindgen::convert::FromWasmAbi for MarketResourceType {
697    type Abi = <wasm_bindgen::JsValue as wasm_bindgen::convert::FromWasmAbi>::Abi;
698
699    #[inline]
700    unsafe fn from_abi(js: Self::Abi) -> Self {
701        let s = <wasm_bindgen::JsValue as wasm_bindgen::convert::FromWasmAbi>::from_abi(js);
702        // first try deserialize as ResourceType
703        match ResourceType::from_js_value(&s) {
704            Some(r) => Self::Resource(r),
705            None => {
706                // try with IntershardResourceType
707                match IntershardResourceType::from_js_value(&s) {
708                    Some(r) => Self::IntershardResource(r),
709                    None => unreachable!("should have come from IntoWasmAbi"),
710                }
711            }
712        }
713    }
714}
715
716impl wasm_bindgen::convert::IntoWasmAbi for MarketResourceType {
717    type Abi = <wasm_bindgen::JsValue as wasm_bindgen::convert::IntoWasmAbi>::Abi;
718
719    #[inline]
720    fn into_abi(self) -> Self::Abi {
721        match self {
722            MarketResourceType::Resource(r) => {
723                <wasm_bindgen::JsValue as wasm_bindgen::convert::IntoWasmAbi>::into_abi(r.into())
724            }
725            MarketResourceType::IntershardResource(r) => {
726                <wasm_bindgen::JsValue as wasm_bindgen::convert::IntoWasmAbi>::into_abi(r.into())
727            }
728        }
729    }
730}
731
732impl wasm_bindgen::describe::WasmDescribe for MarketResourceType {
733    fn describe() {
734        <wasm_bindgen::JsValue as wasm_bindgen::describe::WasmDescribe>::describe()
735    }
736}
737
738/// Translates the `POWER_CLASS` constants, which are classes of power creeps
739#[wasm_bindgen]
740#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Sequence)]
741pub enum PowerCreepClass {
742    Operator = "operator",
743}
744
745named_enum_serialize_deserialize!(PowerCreepClass);
746
747/// Translates the `PWR_*` constants, which are types of powers used by power
748/// creeps
749#[wasm_bindgen]
750#[derive(
751    Debug,
752    PartialEq,
753    Eq,
754    Clone,
755    Copy,
756    Hash,
757    FromPrimitive,
758    Deserialize_repr,
759    Serialize_repr,
760    Sequence,
761)]
762#[repr(u32)]
763pub enum PowerType {
764    GenerateOps = 1,
765    OperateSpawn = 2,
766    OperateTower = 3,
767    OperateStorage = 4,
768    OperateLab = 5,
769    OperateExtension = 6,
770    OperateObserver = 7,
771    OperateTerminal = 8,
772    DisruptSpawn = 9,
773    DisruptTower = 10,
774    Shield = 12,
775    RegenSource = 13,
776    RegenMineral = 14,
777    DisruptTerminal = 15,
778    OperatePower = 16,
779    Fortify = 17,
780    OperateController = 18,
781    OperateFactory = 19,
782}
783
784impl JsCollectionFromValue for PowerType {
785    fn from_value(val: JsValue) -> Self {
786        let power_type_id = if let Some(val) = val.as_string() {
787            val.parse::<u32>().expect("expected parseable u32 string")
788        } else {
789            val.as_f64().expect("expected number value") as u32
790        };
791
792        Self::from_u32(power_type_id).expect("unknown power type")
793    }
794}
795
796impl JsCollectionIntoValue for PowerType {
797    fn into_value(self) -> JsValue {
798        JsValue::from_f64(self as u32 as f64)
799    }
800}
801
802/// Translates the `EFFECT_*` constants, which are natural effect types
803#[wasm_bindgen]
804#[derive(
805    Copy,
806    Clone,
807    Debug,
808    PartialEq,
809    Eq,
810    Hash,
811    FromPrimitive,
812    Serialize_repr,
813    Deserialize_repr,
814    Sequence,
815)]
816#[repr(u32)]
817pub enum NaturalEffectType {
818    Invulnerability = 1001,
819    CollapseTimer = 1002,
820}
821
822/// Translates effect types on room objects, which can include both `PWR_*` and
823/// `EFFECT_*` constants.
824#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Sequence)]
825pub enum EffectType {
826    PowerEffect(PowerType),
827    NaturalEffect(NaturalEffectType),
828}
829
830impl wasm_bindgen::convert::IntoWasmAbi for EffectType {
831    type Abi = u32;
832
833    #[inline]
834    fn into_abi(self) -> Self::Abi {
835        match self {
836            EffectType::PowerEffect(e) => (e as u32).into_abi(),
837            EffectType::NaturalEffect(e) => (e as u32).into_abi(),
838        }
839    }
840}
841
842impl wasm_bindgen::convert::FromWasmAbi for EffectType {
843    type Abi = u32;
844
845    #[inline]
846    unsafe fn from_abi(js: u32) -> Self {
847        match PowerType::from_u32(js) {
848            Some(pt) => Self::PowerEffect(pt),
849            None => {
850                Self::NaturalEffect(NaturalEffectType::from_u32(js).expect("unknown effect id!"))
851            }
852        }
853    }
854}
855
856impl wasm_bindgen::describe::WasmDescribe for EffectType {
857    fn describe() {
858        wasm_bindgen::describe::inform(wasm_bindgen::describe::U32)
859    }
860}
861
862#[cfg(test)]
863mod test {
864    use super::*;
865
866    #[test]
867    fn resources_rust_to_serde_json_from_serde_json_roundtrip() {
868        for resource in enum_iterator::all::<ResourceType>() {
869            if resource != ResourceType::__Invalid {
870                let serialized = serde_json::to_string(&resource).unwrap();
871                let parsed: ResourceType = serde_json::from_str(&serialized).unwrap();
872                assert_eq!(resource, parsed);
873            }
874        }
875    }
876
877    #[test]
878    fn resources_rust_to_display_from_str_roundtrip() {
879        for resource in enum_iterator::all::<ResourceType>() {
880            if resource != ResourceType::__Invalid {
881                let string = format!("{}", resource);
882                let parsed = ResourceType::from_str(&string).unwrap();
883                assert_eq!(resource, parsed);
884            }
885        }
886    }
887
888    #[test]
889    fn resources_rust_vec_to_serde_json_from_serde_json_roundtrip() {
890        let mut resources = vec![];
891        for resource in enum_iterator::all::<ResourceType>() {
892            if resource != ResourceType::__Invalid {
893                resources.push(resource);
894            }
895        }
896        let serialized = serde_json::to_string(&resources).unwrap();
897        let resources_reparsed: Vec<ResourceType> = serde_json::from_str(&serialized).unwrap();
898        assert_eq!(resources, resources_reparsed);
899    }
900
901    #[test]
902    fn resources_rust_vec_to_serde_json_from_serde_json_roundtrip_via_values() {
903        let mut resources = vec![];
904        for resource in enum_iterator::all::<ResourceType>() {
905            if resource != ResourceType::__Invalid {
906                resources.push(resource);
907            }
908        }
909        let serialized = serde_json::to_string(&resources).unwrap();
910        let resources_reparsed_values: Vec<serde_json::Value> =
911            serde_json::from_str(&serialized).unwrap();
912        let resources_reparsed_native: Vec<ResourceType> = resources_reparsed_values
913            .iter()
914            .map(|v| serde_json::from_value(v.clone()).unwrap())
915            .collect();
916        assert_eq!(resources, resources_reparsed_native);
917    }
918
919    #[test]
920    fn intershard_resources_rust_to_serde_json_from_serde_json_roundtrip() {
921        for resource in enum_iterator::all::<IntershardResourceType>() {
922            if resource != IntershardResourceType::__Invalid {
923                let serialized = serde_json::to_string(&resource).unwrap();
924                let parsed: IntershardResourceType = serde_json::from_str(&serialized).unwrap();
925                assert_eq!(resource, parsed);
926            }
927        }
928    }
929
930    #[test]
931    fn intershard_resources_rust_to_display_from_str_roundtrip() {
932        for resource in enum_iterator::all::<IntershardResourceType>() {
933            if resource != IntershardResourceType::__Invalid {
934                let string = format!("{}", resource);
935                let parsed = IntershardResourceType::from_str(&string).unwrap();
936                assert_eq!(resource, parsed);
937            }
938        }
939    }
940
941    #[test]
942    fn intershard_resources_rust_vec_to_serde_json_from_serde_json_roundtrip() {
943        let mut resources = vec![];
944        for resource in enum_iterator::all::<IntershardResourceType>() {
945            if resource != IntershardResourceType::__Invalid {
946                resources.push(resource);
947            }
948        }
949        let serialized = serde_json::to_string(&resources).unwrap();
950        let resources_reparsed: Vec<IntershardResourceType> =
951            serde_json::from_str(&serialized).unwrap();
952        assert_eq!(resources, resources_reparsed);
953    }
954
955    #[test]
956    fn intershard_resources_rust_vec_to_serde_json_from_serde_json_roundtrip_via_values() {
957        let mut resources = vec![];
958        for resource in enum_iterator::all::<IntershardResourceType>() {
959            if resource != IntershardResourceType::__Invalid {
960                resources.push(resource);
961            }
962        }
963        let serialized = serde_json::to_string(&resources).unwrap();
964        let resources_reparsed_values: Vec<serde_json::Value> =
965            serde_json::from_str(&serialized).unwrap();
966        let resources_reparsed_native: Vec<IntershardResourceType> = resources_reparsed_values
967            .iter()
968            .map(|v| serde_json::from_value(v.clone()).unwrap())
969            .collect();
970        assert_eq!(resources, resources_reparsed_native);
971    }
972
973    #[test]
974    fn market_resources_rust_to_serde_json_from_serde_json_roundtrip() {
975        for resource in enum_iterator::all::<MarketResourceType>() {
976            if resource != MarketResourceType::Resource(ResourceType::__Invalid)
977                && resource
978                    != MarketResourceType::IntershardResource(IntershardResourceType::__Invalid)
979            {
980                let serialized = serde_json::to_string(&resource).unwrap();
981                let parsed: MarketResourceType = serde_json::from_str(&serialized).unwrap();
982                assert_eq!(resource, parsed);
983            }
984        }
985    }
986
987    #[test]
988    fn market_resources_rust_vec_to_serde_json_from_serde_json_roundtrip() {
989        let mut resources = vec![];
990        for resource in enum_iterator::all::<MarketResourceType>() {
991            if resource != MarketResourceType::Resource(ResourceType::__Invalid)
992                && resource
993                    != MarketResourceType::IntershardResource(IntershardResourceType::__Invalid)
994            {
995                resources.push(resource);
996            }
997        }
998        let serialized = serde_json::to_string(&resources).unwrap();
999        let resources_reparsed: Vec<MarketResourceType> =
1000            serde_json::from_str(&serialized).unwrap();
1001        assert_eq!(resources, resources_reparsed);
1002    }
1003
1004    #[test]
1005    fn market_resources_rust_vec_to_serde_json_from_serde_json_roundtrip_via_values() {
1006        let mut resources = vec![];
1007        for resource in enum_iterator::all::<MarketResourceType>() {
1008            if resource != MarketResourceType::Resource(ResourceType::__Invalid)
1009                && resource
1010                    != MarketResourceType::IntershardResource(IntershardResourceType::__Invalid)
1011            {
1012                resources.push(resource);
1013            }
1014        }
1015        let serialized = serde_json::to_string(&resources).unwrap();
1016        let resources_reparsed_values: Vec<serde_json::Value> =
1017            serde_json::from_str(&serialized).unwrap();
1018        let resources_reparsed_native: Vec<MarketResourceType> = resources_reparsed_values
1019            .iter()
1020            .map(|v| serde_json::from_value(v.clone()).unwrap())
1021            .collect();
1022        assert_eq!(resources, resources_reparsed_native);
1023    }
1024}