Skip to main content

mindus/block/
mod.rs

1//! deal with blocks.
2//!
3//! different block types are organized into modules
4use bobbin_bits::U4::{self, B0000, B0001, B0010, B0100, B1000};
5use fimg::DynImage;
6use std::error::Error;
7use std::fmt;
8
9use crate::block::all::Type;
10use crate::content::Content;
11use crate::data::dynamic::{DynData, DynType};
12use crate::data::map::{Build, mcg};
13use crate::data::{self, CompressError, renderer::*};
14use crate::data::{DataRead, GridPos, ReadError as DataReadError};
15use crate::item::storage::ItemStorage;
16use crate::utils::Cow;
17
18macro_rules! mods {
19    ($($mod:ident)*) => {
20        $(pub mod $mod;)*
21
22        mod all {
23            $(#[allow(unused_imports)] pub use crate::block::$mod::*;)*
24            pub use super::simple::BasicBlock;
25        }
26    }
27}
28
29mods! {
30    content defense distribution drills liquid logic payload power production turrets walls units
31}
32
33pub mod ratios;
34mod simple;
35use simple::*;
36
37macro_rules! disp {
38    ($($k:ident,)+) => {
39        use all::{$($k,)+};
40        #[enum_dispatch::enum_dispatch]
41        pub(crate) enum BlockLogicEnum {
42            $($k,)+
43        }
44
45        impl ratios::Ratios for BlockLogicEnum {
46            fn io(&self, state: Option<&State>, name: &str) -> ratios::Io {
47                match self {
48                    $(Self::$k(x) => x.io(state, name),)+
49                }
50            }
51        }
52        pub const trait ConstFrom<T>: Sized {
53            fn fro(value: T) -> Self;
54        }
55        $(
56            impl const ConstFrom<$k> for BlockLogicEnum {
57                fn fro(v: $k) -> Self {
58                    BlockLogicEnum::$k(v)
59                }
60            }
61        )+
62
63        /*impl std::fmt::Debug for BlockLogicEnum {
64            fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
65                match self {
66                    $(BlockLogicEnum::$k { .. } => write!(w, stringify!($k)),)+
67                }
68            }
69        }*/
70    }
71}
72
73disp! {
74    BasicBlock,
75    WallBlock,
76    DuctBlock,
77    BridgeBlock,
78    ItemBlock,
79    ProductionBlock,
80    SeparatorBlock,
81    StackConveyor,
82    HeatCrafter,
83    ConnectorBlock,
84    ItemTurret,
85    ConveyorBlock,
86    PayloadRouter,
87    WallDrillBlock,
88    DrillBlock,
89    NuclearGeneratorBlock,
90    GeneratorBlock,
91    ConduitBlock,
92    HeatedBlock,
93    RadarBlock,
94    ShieldBlock,
95    PointDefenseTurret,
96    JunctionBlock,
97    Turret,
98    MemoryBlock,
99    MessageLogic,
100    ConstructorBlock,
101    AssemblerBlock,
102    UnitFactory,
103    SimpleDuctBlock,
104    SurgeRouter,
105    SimplePayloadBlock,
106    PayloadConveyor,
107    ImpactReactorBlock,
108    Neoplasia,
109    DiodeBlock,
110    HeatConduit,
111    ContinousTurret,
112    TractorBeamTurret,
113    TileableDisplay,
114    AssemblerModule,
115    RepairTurret,
116    FluidBlock,
117    CanvasBlock,
118    SwitchLogic,
119    ProcessorLogic,
120    PayloadBlock,
121    LampBlock,
122    UnitCargoLoader,
123    DoorBlock,
124}
125
126pub trait Cast {
127    fn downcast_ref(state: &State) -> Option<&Self>;
128    fn downcast_mut(state: &mut State) -> Option<&mut Self>;
129}
130
131macro_rules! stater {
132    ($($k: ident($v: ty),)+) => {
133        #[derive(Debug, Clone, Hash)]
134        pub enum State {
135            $($k($v),)+
136        }
137
138        $(
139            impl From<$v> for State {
140                fn from(v: $v) -> State { State::$k(v) }
141            }
142
143            impl Cast for $v {
144                fn downcast_ref(state: &State) -> Option<&Self> {
145                    match state {
146                        State::$k(v) => Some(v),
147                        _ => None,
148                    }
149                }
150                fn downcast_mut(state: &mut State) -> Option<&mut Self> {
151                    match state {
152                        State::$k(v) => Some(v),
153                        _ => None,
154                    }
155                }
156            }
157        )+
158    }
159}
160
161stater! {
162    // TODO deoptionize
163    String(String),
164    Item(Option<crate::item::Type>),
165    Fluid(Option<crate::fluid::Type>),
166    Image(Image<Box<[u8]>, 1>),
167    Point(Option<(i32, i32)>),
168    Bool(bool),
169    Processor(logic::ProcessorState),
170    Payload(payload::Payload),
171    Power(Vec<(i16, i16)>),
172    Color(power::Rgba),
173    Command(Option<crate::data::command::UnitCommand>),
174    Unit(Option<crate::unit::Type>),
175}
176
177impl State {
178    pub fn downcast_ref<T: Cast>(&self) -> Option<&T> {
179        T::downcast_ref(self)
180    }
181
182    pub fn downcast_mut<T: Cast>(&mut self) -> Option<&mut T> {
183        T::downcast_mut(self)
184    }
185
186    pub fn new<T: Into<Self>>(from: T) -> Self {
187        from.into()
188    }
189}
190
191#[enum_dispatch::enum_dispatch(BlockLogicEnum)]
192pub trait BlockLogic: ratios::Ratios {
193    /// mindustry blocks are the same width and height
194    fn get_size(&self) -> u8;
195
196    fn is_symmetric(&self) -> bool;
197
198    fn create_build_cost(&self) -> Option<ItemStorage>;
199
200    fn data_from_i32(&self, config: i32, pos: GridPos) -> Result<DynData, DataConvertError>;
201
202    fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError>;
203
204    #[allow(unused_variables)]
205    fn mirror_state(&self, state: &mut State, horizontally: bool, vertically: bool) {}
206
207    #[allow(unused_variables)]
208    fn rotate_state(&self, state: &mut State, clockwise: bool) {}
209
210    fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError>;
211
212    #[allow(unused_variables)]
213    fn draw(
214        &self,
215        name: &str,
216        state: Option<&State>,
217        context: Option<&RenderingContext>,
218        rot: Rotation,
219        scale: Scale,
220    ) -> ImageHolder<4> {
221        unimplemented!("{name}")
222    }
223
224    #[allow(unused_variables)]
225    fn read(&self, build: &mut Build, buff: &mut DataRead) -> Result<(), DataReadError> {
226        Ok(())
227    }
228}
229
230// i wish i could derive
231macro_rules! impl_block {
232    () => {
233        fn get_size(&self) -> u8 {
234            self.size
235        }
236
237        fn is_symmetric(&self) -> bool {
238            self.symmetric
239        }
240
241        fn create_build_cost(&self) -> Option<$crate::item::storage::ItemStorage> {
242            if self.build_cost.is_empty() {
243                None
244            } else {
245                let mut storage = crate::item::storage::Storage::new();
246                for (ty, cnt) in self.build_cost {
247                    storage.add(*ty, *cnt, u32::MAX);
248                }
249                Some(storage)
250            }
251        }
252    };
253}
254pub(crate) use impl_block;
255
256#[derive(Debug, thiserror::Error)]
257pub enum DataConvertError {
258    #[error(transparent)]
259    Custom(#[from] Box<dyn Error + Sync + Send>),
260}
261
262impl DataConvertError {
263    pub fn forward<T, E: Error + Sync + Send + 'static>(result: Result<T, E>) -> Result<T, Self> {
264        match result {
265            Ok(v) => Ok(v),
266            Err(e) => Err(Self::Custom(Box::new(e))),
267        }
268    }
269}
270
271#[derive(Debug, thiserror::Error)]
272pub enum DeserializeError {
273    #[error(transparent)]
274    DecompressError(#[from] data::DecompressError),
275    #[error("expected type {expect:?} but got {have:?}")]
276    InvalidType { have: DynType, expect: DynType },
277    #[error(transparent)]
278    Custom(#[from] Box<dyn Error + Sync + Send>),
279}
280
281impl DeserializeError {
282    pub fn forward<T, E: Error + Sync + Send + 'static>(result: Result<T, E>) -> Result<T, Self> {
283        match result {
284            Ok(v) => Ok(v),
285            Err(e) => Err(Self::Custom(Box::new(e))),
286        }
287    }
288}
289
290#[derive(Debug, thiserror::Error)]
291pub enum SerializeError {
292    #[error(transparent)]
293    Custom(#[from] Box<dyn Error + Sync + Send>),
294    #[error(transparent)]
295    Compress(#[from] CompressError),
296}
297
298impl SerializeError {
299    pub fn forward<T, E: Error + Sync + Send + 'static>(result: Result<T, E>) -> Result<T, Self> {
300        match result {
301            Ok(v) => Ok(v),
302            Err(e) => Err(Self::Custom(Box::new(e))),
303        }
304    }
305}
306
307/// a block. put it in stuff!
308pub struct Block {
309    image_random: Option<&'static [[DynImage<&'static [u8]>; 3]; 8]>,
310    image: Option<&'static [DynImage<&'static [u8]>; 3]>,
311    pub(crate) name: Type,
312    logic: BlockLogicEnum,
313}
314
315impl core::hash::Hash for Block {
316    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
317        self.name.hash(state);
318    }
319}
320
321impl PartialEq for Block {
322    fn eq(&self, rhs: &Block) -> bool {
323        self.name == rhs.name
324    }
325}
326
327impl Block {
328    /// this blocks name
329    /// ```
330    /// assert!(mindus::block::DISTRIBUTOR.name() == "distributor")
331    /// ```
332    #[must_use]
333    #[inline]
334    pub const fn name(&self) -> &'static str {
335        self.name.get_name()
336    }
337
338    pub fn io(&self, state: Option<&State>) -> ratios::Io {
339        <BlockLogicEnum as ratios::Ratios>::io(&self.logic, state, self.name.get_name())
340    }
341
342    /// should you send context to [`image`]?
343    #[must_use]
344    #[inline]
345    pub const fn wants_context(&self) -> bool {
346        use BlockLogicEnum::*;
347        matches!(
348            self.logic,
349            ConveyorBlock(..)
350                | DuctBlock(..)
351                | StackConveyor(..)
352                | ConduitBlock(..)
353                | TileableDisplay(..)
354        )
355    }
356
357    /// draw this block, with this state
358    #[must_use]
359    #[inline]
360    pub fn image(
361        &self,
362        state: Option<&State>,
363        context: Option<&RenderingContext>,
364        rot: Rotation,
365        scale: Scale,
366    ) -> DynImage<Cow> {
367        if let Some(imgs) = &self.image_random {
368            return unsafe { imgs[mcg() as usize][scale as usize].mapped(Cow::Ref) };
369        }
370        if let Some(imgs) = &self.image {
371            return unsafe { imgs[scale as usize].mapped(Cow::Ref) };
372        }
373        DynImage::Rgba(
374            self.logic
375                .draw(self.name.get_name(), state, context, rot, scale),
376        )
377    }
378
379    /// size.
380    #[must_use]
381    #[inline]
382    pub fn get_size(&self) -> u8 {
383        self.logic.get_size()
384    }
385
386    /// does it matter if its rotated
387    #[must_use]
388    #[inline]
389    pub fn is_symmetric(&self) -> bool {
390        self.logic.is_symmetric()
391    }
392
393    /// cost
394    #[must_use]
395    #[inline]
396    pub fn get_build_cost(&self) -> Option<ItemStorage> {
397        self.logic.create_build_cost()
398    }
399
400    pub(crate) fn data_from_i32(
401        &self,
402        config: i32,
403        pos: GridPos,
404    ) -> Result<DynData, DataConvertError> {
405        self.logic.data_from_i32(config, pos)
406    }
407
408    pub(crate) fn deserialize_state(
409        &self,
410        data: DynData,
411    ) -> Result<Option<State>, DeserializeError> {
412        self.logic.deserialize_state(data)
413    }
414
415    pub(crate) fn mirror_state(&self, state: &mut State, horizontally: bool, vertically: bool) {
416        self.logic.mirror_state(state, horizontally, vertically);
417    }
418
419    pub(crate) fn rotate_state(&self, state: &mut State, clockwise: bool) {
420        self.logic.rotate_state(state, clockwise);
421    }
422
423    pub(crate) fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
424        self.logic.serialize_state(state)
425    }
426
427    #[inline]
428    pub(crate) fn read(&self, build: &mut Build, buff: &mut DataRead) -> Result<(), DataReadError> {
429        self.logic.read(build, buff)
430    }
431}
432
433impl fmt::Debug for Block {
434    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435        write!(f, "Block<{:?}>", self.name)
436    }
437}
438
439#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
440/// the possible rotation states of a object
441#[repr(u8)]
442pub enum Rotation {
443    Up,
444    Right,
445    Down,
446    Left,
447}
448
449impl Rotation {
450    #[must_use]
451    /// count rotations
452    pub const fn count(self) -> u8 {
453        self as u8
454    }
455
456    #[must_use]
457    /// mask
458    pub const fn mask(self) -> U4 {
459        match self {
460            Rotation::Up => B1000,
461            Rotation::Right => B0100,
462            Rotation::Down => B0010,
463            Rotation::Left => B0001,
464        }
465    }
466
467    #[must_use]
468    /// character of this rot (Right => >, Up => ^, Left => <, Down => v)
469    pub const fn ch(self) -> char {
470        match self {
471            Rotation::Right => '>',
472            Rotation::Up => '^',
473            Rotation::Left => '<',
474            Rotation::Down => 'v',
475        }
476    }
477
478    #[must_use]
479    /// mirror the directions.
480    pub const fn mirrored(self, horizontally: bool, vertically: bool) -> Self {
481        match self {
482            Self::Right => {
483                if horizontally {
484                    Self::Left
485                } else {
486                    Self::Right
487                }
488            }
489            Self::Up => {
490                if vertically {
491                    Self::Down
492                } else {
493                    Self::Up
494                }
495            }
496            Self::Left => {
497                if horizontally {
498                    Self::Right
499                } else {
500                    Self::Left
501                }
502            }
503            Self::Down => {
504                if vertically {
505                    Self::Up
506                } else {
507                    Self::Down
508                }
509            }
510        }
511    }
512
513    /// mirror in place
514    pub fn mirror(&mut self, horizontally: bool, vertically: bool) {
515        *self = self.mirrored(horizontally, vertically);
516    }
517
518    #[must_use]
519    /// rotate the rotation
520    pub const fn rotated(self, clockwise: bool) -> Self {
521        match self {
522            Self::Right => {
523                if clockwise {
524                    Self::Down
525                } else {
526                    Self::Up
527                }
528            }
529            Self::Up => {
530                if clockwise {
531                    Self::Right
532                } else {
533                    Self::Left
534                }
535            }
536            Self::Left => {
537                if clockwise {
538                    Self::Up
539                } else {
540                    Self::Down
541                }
542            }
543            Self::Down => {
544                if clockwise {
545                    Self::Left
546                } else {
547                    Self::Right
548                }
549            }
550        }
551    }
552
553    /// rotate the rotation in place
554    pub fn rotate(&mut self, clockwise: bool) {
555        *self = self.rotated(clockwise);
556    }
557
558    #[must_use]
559    /// rotate 180
560    pub const fn rotated_180(self) -> Self {
561        match self {
562            Self::Right => Self::Left,
563            Self::Up => Self::Down,
564            Self::Left => Self::Right,
565            Self::Down => Self::Up,
566        }
567    }
568
569    /// rotate 180 in place
570    pub fn rotate_180(&mut self) {
571        *self = self.rotated_180();
572    }
573}
574
575impl From<u8> for Rotation {
576    fn from(val: u8) -> Self {
577        match val & 3 {
578            0 => Self::Right,
579            1 => Self::Up,
580            2 => Self::Left,
581            _ => Self::Down,
582        }
583    }
584}
585
586impl From<Rotation> for u8 {
587    fn from(rot: Rotation) -> Self {
588        match rot {
589            Rotation::Right => 0,
590            Rotation::Up => 1,
591            Rotation::Left => 2,
592            Rotation::Down => 3,
593        }
594    }
595}
596
597macro_rules! make_register {
598	($($field:literal $op:tt $logic:expr;)+) => { paste::paste! {
599		$(
600            make_register!(impl $field $op $logic);
601        )+
602        pub static BLOCK_REGISTRY: phf::Map<&str, &Block> = phf::phf_map! {$(
603            $field => &[<$field:snake:upper>],
604        )+};
605
606        impl content::Type {
607            pub fn to_block(self) -> Option<&'static Block> {
608                // static L: &[&Block] = &[$(&[<$field:snake:upper>],)+];
609                // L.get(self as usize).copied()
610                match self {
611                    $(content::Type::[<$field:camel>] => Some(&[<$field:snake:upper>]),)+
612                    _ => None,
613                }
614            }
615        }
616    }};
617    (impl $field: literal => $logic: expr) => {
618        paste::paste! { pub static [<$field:snake:upper>]: Block = Block {
619            name: crate::block::content::Type::[<$field:camel>], logic: <crate::block::BlockLogicEnum as crate::block::ConstFrom<_>>::fro($logic), image: None, image_random: None,
620        }; }
621    };
622    (impl $field: literal -> $logic: expr) => {
623        paste::paste! { pub static [<$field:snake:upper>]: Block = Block {
624            name: crate::block::content::Type::[<$field:camel>], logic: <crate::block::BlockLogicEnum as crate::block::ConstFrom<_>>::fro($logic), image: Some(&car::map!(crate::data::renderer::load!($field), DynImage::from)), image_random: None,
625        }; }
626    };
627    (impl $field: literal : $size: literal) => {
628        paste::paste! { pub static [<$field:snake:upper>]: Block = Block {
629            name: crate::block::content::Type::[<$field:camel>], logic: BlockLogicEnum::BasicBlock(BasicBlock::new($size, true, &[])), image: Some(&car::map!(crate::data::renderer::load!($field), DynImage::from)), image_random: None,
630        }; }
631    };
632    (impl $field: literal @ $size: literal) => {
633        paste::paste! { pub static [<$field:snake:upper>]: Block = Block {
634            name: crate::block::content::Type::[<$field:camel>], logic: BlockLogicEnum::BasicBlock(BasicBlock::new($size, true, &[])), image_random: Some(&crate::data::renderer::load!(8x $field)), image: None,
635        }; }
636    };
637    // floors
638    (impl $field: literal > $size: literal) => {
639        paste::paste! { pub static [<$field:snake:upper>]: Block = Block {
640            name: crate::block::content::Type::[<$field:camel>], logic: BlockLogicEnum::BasicBlock(BasicBlock::new($size, true, &[])), image: Some(&car::map!(crate::data::renderer::load!("empty4"), DynImage::from)), image_random: None,
641        }; }
642    };
643}
644// pub(self) use make_register;
645make_register! {
646    "metal-wall-1": 1;
647    "metal-wall-2": 1;
648    "metal-wall-3": 1;
649    "colored-floor" > 1;
650    "colored-wall": 1;
651    "darksand" > 1;
652    "sand-floor" > 1;
653    "yellow-stone" > 1;
654    "arkyic-stone" > 1;
655    "carbon-stone" > 1;
656    "dacite" > 1;
657    "dirt" > 1;
658    "arkycite-floor" > 1;
659    "basalt" > 1;
660    "ice" > 1;
661    "molten-slag" > 1;
662    "moss" > 1;
663    "mud" > 1;
664    "magmarock" > 1;
665    "grass" > 1;
666    "ice-snow" > 1;
667    "hotrock" > 1;
668    "char" > 1;
669    "snow" > 1;
670    "salt" > 1;
671    "shale" > 1;
672    "metal-floor" > 1;
673    "metal-floor-2" > 1;
674    "metal-floor-3" > 1;
675    "metal-floor-4" > 1;
676    "metal-floor-5" > 1;
677    "dark-panel-1" > 1;
678    "dark-panel-2" > 1;
679    "dark-panel-3" > 1;
680    "dark-panel-4" > 1;
681    "dark-panel-5" > 1;
682    "dark-panel-6" > 1;
683    "darksand-tainted-water" > 1;
684    "darksand-water" > 1;
685    "deep-tainted-water" > 1;
686    "deep-water" > 1;
687    "sand-water" > 1;
688    "shallow-water" > 1;
689    "space" > 1;
690    "stone" > 1;
691    "arkyic-vent" > 1;
692    "beryllic-stone" > 1;
693    "bluemat" > 1;
694    "carbon-vent" > 1;
695    "core-zone" > 1;
696    "crater-stone" > 1;
697    "crystal-floor" > 1;
698    "crystalline-stone" > 1;
699    "crystalline-vent" > 1;
700    "metal-floor-damaged" > 1;
701    "dense-red-stone" > 1;
702    "ferric-craters" > 1; // ferris section
703    "ferric-stone" > 1;
704    "pooled-cryofluid" > 1;
705    "red-ice" > 1;
706    "red-stone-vent" > 1;
707    "red-stone" > 1;
708    "redmat" > 1;
709    "regolith" > 1;
710    "rhyolite-crater" > 1;
711    "rhyolite" > 1;
712    "rough-rhyolite" > 1;
713    "tainted-water" > 1;
714    "tar" > 1;
715    "yellow-stone-plates" > 1;
716    "yellow-stone-vent" > 1;
717    "spore-moss" > 1;
718    "ore-beryllium" > 1;
719    "ore-copper" > 1;
720    "ore-lead" > 1;
721    "ore-coal" > 1;
722    "ore-scrap" > 1;
723    "ore-thorium" > 1;
724    "ore-titanium" > 1;
725    "ore-tungsten" > 1;
726    "ore-crystal-thorium" > 1;
727    "ore-wall-beryllium" @ 1;
728    "ore-wall-thorium" @ 1;
729    "ore-wall-tungsten" @ 1;
730    "ore-wall-graphite" @ 1;
731    "graphitic-wall" @ 1;
732    "boulder" @ 1;
733    "arkyic-wall" @ 1;
734    "beryllic-stone-wall" @ 1;
735    "carbon-wall" @ 1;
736    "cliff": 1;
737    "crystalline-stone-wall" @ 1;
738    "dacite-wall" @ 1;
739    "dark-metal" @ 1;
740    "dirt-wall" @ 1;
741    "dune-wall" @ 1;
742    "ferric-stone-wall" @ 1;
743    "ice-wall" @ 1;
744    "pebbles" @ 1;
745    "pine": 1;
746    "red-diamond-wall" @ 1;
747    "red-ice-wall" @ 1;
748    "red-stone-wall" @ 1;
749    "regolith-wall" @ 1;
750    "rhyolite-vent"  @ 1;
751    "rhyolite-wall" @ 1;
752    "salt-wall" @ 1;
753    "sand-wall" @ 1;
754    "shale-wall" @ 1;
755    "shrubs" @ 1;
756    "snow-pine": 1;
757    "snow-wall" @ 1;
758    "spawn": 1;
759    "spore-pine": 1;
760    "spore-wall" @ 1;
761    "stone-wall" @ 1;
762    "yellow-stone-wall" @ 1;
763    // props
764    "yellow-stone-boulder" @ 1;
765    "snow-boulder" @ 1;
766    "shale-boulder" @ 1;
767    "arkyic-boulder" @ 1;
768    "basalt-boulder" @ 1;
769    "beryllic-boulder" @ 1;
770    "carbon-boulder" @ 1;
771    "crystalline-boulder" @ 1;
772    "dacite-boulder" @ 1;
773    "ferric-boulder" @ 1;
774    "red-ice-boulder" @ 1;
775    "red-stone-boulder" @ 1;
776    "rhyolite-boulder" @ 1;
777    "sand-boulder" @ 1;
778    "pur-bush": 1;
779    "tendrils" @ 1;
780    // these are tall but uh (TODO layering)
781    "white-tree-dead": 1;
782    "yellowcoral": 1;
783    "white-tree": 1;
784    "redweed" @ 1;
785    "spore-cluster" @ 1;
786    "crystal-blocks" @ 1;
787    "crystal-cluster"  @ 1;
788    "vibrant-crystal-cluster" @ 1;
789    "crystal-orbs" @ 1;
790    // end tall
791    "build1": 1;
792    "build2": 1;
793    "build3": 1;
794    "build4": 1;
795    "build5": 1;
796    "build6": 1;
797    "build7": 1;
798    "build8": 1;
799    "build9": 1;
800    "build10": 1;
801    "build11": 1;
802    "build12": 1;
803    "build13": 1;
804    "build14": 1;
805    "build15": 1;
806    "build16": 1;
807    "conveyor" => ConveyorBlock::new(1, false, cost!(Copper: 1));
808    "titanium-conveyor" => ConveyorBlock::new(1, false, cost!(Copper: 1, Lead: 1, Titanium: 1));
809    "plastanium-conveyor" => StackConveyor::new(1, false, cost!(Graphite: 1, Silicon: 1, Plastanium: 1));
810    "armored-conveyor" => ConveyorBlock::new(1, false, cost!(Metaglass: 1, Thorium: 1, Plastanium: 1));
811    "junction" -> JunctionBlock::new(1, true, cost!(Copper: 2));
812    "bridge-conveyor" -> BridgeBlock::new(1, false, cost!(Copper: 6, Lead: 6), 4, true);
813    "phase-conveyor" -> BridgeBlock::new(1, false, cost!(Lead: 10, Graphite: 10, Silicon: 7, PhaseFabric: 5), 12, true);
814    "sorter" => ItemBlock::new(1, true, cost!(Copper: 2, Lead: 2));
815    "inverted-sorter" => ItemBlock::new(1, true, cost!(Copper: 2, Lead: 2));
816    "unloader" => ItemBlock::new(1, true, cost!(Titanium: 25, Silicon: 30));
817    "router" -> BasicBlock::new(1, true, cost!(Copper: 3));
818    "distributor" -> BasicBlock::new(2, true, cost!(Copper: 4, Lead: 4));
819    "overflow-gate" -> BasicBlock::new(1, true, cost!(Copper: 4, Lead: 2));
820    "underflow-gate" -> BasicBlock::new(1, true, cost!(Copper: 4, Lead: 2));
821    "mass-driver" => BridgeBlock::new(3, true, cost!(Lead: 125, Titanium: 125, Thorium: 50, Silicon: 75), 55, false);
822    "duct" => DuctBlock::new(1, false, cost!(Beryllium: 1));
823    "armored-duct" => DuctBlock::new(1, false, cost!(Beryllium: 2, Tungsten: 1));
824    "duct-router" => ItemBlock::new(1, true, cost!(Beryllium: 10));
825    "overflow-duct" => SimpleDuctBlock::new(1, true, cost!(Graphite: 8, Beryllium: 8));
826    "underflow-duct" => SimpleDuctBlock::new(1, true, cost!(Graphite: 8, Beryllium: 8));
827    "duct-bridge" => BridgeBlock::new(1, true, cost!(Beryllium: 20), 3, true);
828    "duct-unloader" => ItemBlock::new(1, true, cost!(Graphite: 20, Silicon: 20, Tungsten: 10));
829    "surge-conveyor" => StackConveyor::new(1, false, cost!(SurgeAlloy: 1, Tungsten: 1));
830    "surge-router" => SurgeRouter::new(1, false, cost!(SurgeAlloy: 5, Tungsten: 1)); // not symmetric
831    "unit-cargo-loader" -> UnitCargoLoader::new(3, true, cost!(Silicon: 80, SurgeAlloy: 50, Oxide: 20));
832    "unit-cargo-unload-point" => ItemBlock::new(2, true, cost!(Silicon: 60, Tungsten: 60));
833    "cultivator" -> ProductionBlock::new(2, true, cost!(Copper: 25, Lead: 25, Silicon: 10));
834    "graphite-press" -> ProductionBlock::new(2, true, cost!(Copper: 75, Lead: 30));
835    "multi-press" -> ProductionBlock::new(3, true, cost!(Lead: 100, Graphite: 50, Titanium: 100, Silicon: 25));
836    "silicon-smelter" -> ProductionBlock::new(2, true, cost!(Copper: 30, Lead: 25));
837    "silicon-crucible" -> ProductionBlock::new(3, true, cost!(Metaglass: 80, Titanium: 120, Silicon: 60, Plastanium: 35));
838    "kiln" -> ProductionBlock::new(2, true, cost!(Copper: 60, Lead: 30, Graphite: 30));
839    "plastanium-compressor" -> ProductionBlock::new(2, true, cost!(Lead: 115, Graphite: 60, Titanium: 80, Silicon: 80));
840    "phase-weaver" -> ProductionBlock::new(2, true, cost!(Lead: 120, Thorium: 75, Silicon: 130));
841    "surge-smelter" -> ProductionBlock::new(3, true, cost!(Lead: 80, Thorium: 70, Silicon: 80));
842    "cryofluid-mixer" -> ProductionBlock::new(2, true, cost!(Lead: 65, Titanium: 60, Silicon: 40));
843    "pyratite-mixer" -> ProductionBlock::new(2, true, cost!(Copper: 50, Lead: 25));
844    "blast-mixer" -> ProductionBlock::new(2, true, cost!(Lead: 30, Titanium: 20));
845    "melter" -> ProductionBlock::new(1, true, cost!(Copper: 30, Lead: 35, Graphite: 45));
846    "separator" -> SeparatorBlock::new(2, true, cost!(Copper: 30, Titanium: 25));
847    "disassembler" -> SeparatorBlock::new(3, true, cost!(Titanium: 100, Thorium: 80, Silicon: 150, Plastanium: 40));
848    "spore-press" -> ProductionBlock::new(2, true, cost!(Lead: 35, Silicon: 30));
849    "pulverizer" -> ProductionBlock::new(1, true, cost!(Copper: 30, Lead: 25));
850    "coal-centrifuge" -> ProductionBlock::new(2, true, cost!(Lead: 30, Graphite: 40, Titanium: 20));
851    "incinerator" -> BasicBlock::new(1, true, cost!(Lead: 15, Graphite: 5));
852    "silicon-arc-furnace" -> ProductionBlock::new(3, true, cost!(Beryllium: 70, Graphite: 80));
853    "electrolyzer" => ProductionBlock::new(3, true, cost!(Silicon: 50, Graphite: 40, Beryllium: 130, Tungsten: 80));
854    "atmospheric-concentrator" -> ProductionBlock::new(3, true, cost!(Oxide: 60, Beryllium: 180, Silicon: 150));
855    "oxidation-chamber" => HeatCrafter::new(3, true, cost!(Tungsten: 120, Graphite: 80, Silicon: 100, Beryllium: 120));
856    "electric-heater" => HeatCrafter::new(2, false, cost!(Tungsten: 30, Oxide: 30));
857    "slag-heater" => HeatCrafter::new(3, false, cost!(Tungsten: 50, Oxide: 20, Beryllium: 20));
858    "phase-heater" => HeatCrafter::new(2, false, cost!(Oxide: 30, Carbide: 30, Beryllium: 30));
859    "heat-redirector" => HeatConduit::new(3, false, cost!(Tungsten: 10, Graphite: 10));
860    "small-heat-redirector" => HeatConduit::new(2, false, cost!(SurgeAlloy: 8, Graphite: 8));
861    "heat-router" => HeatConduit::new(3, false, cost!(Tungsten: 15, Graphite: 10));
862    "slag-incinerator" -> BasicBlock::new(1, true, cost!(Tungsten: 15));
863    "carbide-crucible" -> ProductionBlock::new(3, true, cost!(Tungsten: 110, Thorium: 150, Oxide: 60));
864    // slag centrifuge
865    "surge-crucible" -> ProductionBlock::new(3, true, cost!(Silicon: 100, Graphite: 80, Tungsten: 80, Oxide: 80));
866    "cyanogen-synthesizer" -> ProductionBlock::new(3, true, cost!(Carbide: 50, Silicon: 80, Beryllium: 90));
867    "phase-synthesizer" -> ProductionBlock::new(3, true, cost!(Carbide: 90, Silicon: 100, Thorium: 100, Tungsten: 200));
868    "heat-reactor" => HeatCrafter::new(3, false, cost!(Oxide: 70, Graphite: 20, Carbide: 10, Thorium: 80));
869    "payload-conveyor" => PayloadConveyor::new(3, false, cost!(Copper: 10, Graphite: 10));
870    "payload-router" => PayloadRouter::new(3, false, cost!(Copper: 10, Graphite: 15));
871    "reinforced-payload-conveyor" => PayloadConveyor::new(3, false, cost!(Tungsten: 10));
872    "reinforced-payload-router" => PayloadRouter::new(3, false, cost!(Tungsten: 15));
873    "payload-mass-driver" -> BridgeBlock::new(3, true, cost!(Tungsten: 120, Silicon: 120, Graphite: 50), 700, false);
874    "large-payload-mass-driver" -> BridgeBlock::new(5, true, cost!(Thorium: 200, Tungsten: 200, Silicon: 200, Graphite: 100, Oxide: 30), 1100, false);
875    "small-deconstructor" => SimplePayloadBlock::new(3, true, cost!(Beryllium: 100, Silicon: 100, Oxide: 40, Graphite: 80));
876    "deconstructor" => SimplePayloadBlock::new(5, true, cost!(Beryllium: 250, Oxide: 100, Silicon: 250, Carbide: 250));
877    "constructor" => PayloadBlock::new(3, true, cost!(Silicon: 100, Beryllium: 150, Tungsten: 80));
878    "large-constructor" => PayloadBlock::new(5, true, cost!(Silicon: 150, Oxide: 150, Tungsten: 200, PhaseFabric: 40));
879    "payload-loader" => SimplePayloadBlock::new(3, false, cost!(Graphite: 50, Silicon: 50, Tungsten: 80));
880    "payload-unloader" => SimplePayloadBlock::new(3, false, cost!(Graphite: 50, Silicon: 50, Tungsten: 30));
881    "copper-wall" -> WallBlock::new(1, true, cost!(Copper: 6));
882    "copper-wall-large" -> WallBlock::new(2, true, cost!(Copper: 6 * 4));
883    "titanium-wall" -> WallBlock::new(1, true, cost!(Titanium: 6));
884    "titanium-wall-large" -> WallBlock::new(2, true, cost!(Titanium: 6 * 4));
885    "plastanium-wall" -> WallBlock::new(1, true, cost!(Metaglass: 2, Plastanium: 5));
886    "plastanium-wall-large" -> WallBlock::new(2, true, cost!(Metaglass: 2 * 4, Plastanium: 5 * 4));
887    "thorium-wall" -> WallBlock::new(1, true, cost!(Thorium: 6));
888    "thorium-wall-large" -> WallBlock::new(2, true, cost!(Thorium: 6 * 4));
889    "phase-wall" -> WallBlock::new(1, true, cost!(PhaseFabric: 6));
890    "phase-wall-large" -> WallBlock::new(2, true, cost!(PhaseFabric: 6 * 4));
891    "surge-wall" -> WallBlock::new(1, true, cost!(SurgeAlloy: 6));
892    "surge-wall-large" -> WallBlock::new(2, true, cost!(SurgeAlloy: 6 * 4));
893    "door" => DoorBlock::new(1, true, cost!(Titanium: 6, Silicon: 4));
894    "door-large" => DoorBlock::new(2, true, cost!(Titanium: 6 * 4, Silicon: 4 * 4));
895    "tungsten-wall" -> WallBlock::new(1, true, cost!(Tungsten: 6));
896    "tungsten-wall-large" -> WallBlock::new(2, true, cost!(Tungsten: 6 * 4));
897    "blast-door" -> DoorBlock::new(2, true, cost!(Tungsten: 24, Silicon: 24));
898    "reinforced-surge-wall" -> WallBlock::new(1, true, cost!(SurgeAlloy: 6, Tungsten: 2));
899    "reinforced-surge-wall-large" -> WallBlock::new(2, true, cost!(SurgeAlloy: 6 * 4, Tungsten: 2 * 4));
900    "carbide-wall" -> WallBlock::new(1, true, cost!(Thorium: 6, Carbide: 6));
901    "carbide-wall-large" -> WallBlock::new(2, true, cost!(Thorium: 6 * 4, Carbide: 6 * 4));
902    "shielded-wall" -> WallBlock::new(2, true, cost!(PhaseFabric: 20, SurgeAlloy: 12, Beryllium: 12));
903    "beryllium-wall" -> WallBlock::new(1, true, cost!(Beryllium: 6));
904    "beryllium-wall-large" -> WallBlock::new(2, true, cost!(Beryllium: 6 * 4));
905    "scrap-wall" -> WallBlock::new(1, true, cost!(Scrap: 6));
906    "scrap-wall-large" -> WallBlock::new(2, true, cost!(Scrap: 24));
907    "scrap-wall-huge" -> WallBlock::new(3, true, cost!(Scrap: 54));
908    "scrap-wall-gigantic" -> WallBlock::new(4, true, cost!(Scrap: 96));
909    "thruster" => WallBlock::new(4, false, cost!(Scrap: 96));
910    "mender" -> HeatedBlock::new(1, true, cost!(Copper: 25, Lead: 30));
911    "mend-projector" -> HeatedBlock::new(2, true, cost!(Copper: 50, Lead: 100, Titanium: 25, Silicon: 40));
912    "overdrive-projector" -> HeatedBlock::new(2, true, cost!(Lead: 100, Titanium: 75, Silicon: 75, Plastanium: 30));
913    "overdrive-dome" -> HeatedBlock::new(3, true, cost!(Lead: 200, Titanium: 130, Silicon: 130, Plastanium: 80, SurgeAlloy: 120));
914    "force-projector" -> BasicBlock::new(3, true, cost!(Lead: 100, Titanium: 75, Silicon: 125));
915    "regen-projector" -> BasicBlock::new(3, true, cost!(Silicon: 80, Tungsten: 60, Oxide: 40, Beryllium: 80));
916    "shock-mine" -> BasicBlock::new(1, true, cost!(Lead: 25, Silicon: 12));
917    "radar" -> RadarBlock::new(1, true, cost!(Silicon: 60, Graphite: 50, Beryllium: 10));
918    "build-tower" -> BasicBlock::new(3, true, cost!(Silicon: 150, Oxide: 40, Thorium: 60));
919    "shockwave-tower" -> BasicBlock::new(3, true, cost!(SurgeAlloy: 50, Silicon: 150, Oxide: 30, Tungsten: 100));
920    "reinforced-pump" -> BasicBlock::new(2, true, cost!(Beryllium: 40, Tungsten: 30, Silicon: 20));
921    "mechanical-pump" -> BasicBlock::new(1, true, cost!(Copper: 15, Metaglass: 10));
922    "rotary-pump" -> BasicBlock::new(2, true, cost!(Copper: 70, Metaglass: 50, Titanium: 35, Silicon: 20));
923    "impulse-pump" -> BasicBlock::new(3, true, cost!(Copper: 80, Metaglass: 90, Titanium: 40, Thorium: 35, Silicon: 30));
924    "conduit" => ConduitBlock::new(1, false, cost!(Metaglass: 1));
925    "pulse-conduit" => ConduitBlock::new(1, false, cost!(Metaglass: 1, Titanium: 2));
926    "plated-conduit" => ConduitBlock::new(1, false, cost!(Metaglass: 1, Thorium: 2, Plastanium: 1));
927    "liquid-router" -> BasicBlock::new(1, true, cost!(Metaglass: 2, Graphite: 4));
928    "liquid-container" -> BasicBlock::new(2, true, cost!(Metaglass: 15, Titanium: 10));
929    "liquid-tank" -> BasicBlock::new(3, true, cost!(Metaglass: 40, Titanium: 30));
930    "liquid-junction" -> BasicBlock::new(1, true, cost!(Metaglass: 8, Graphite: 4));
931    "bridge-conduit" -> BridgeBlock::new(1, true, cost!(Metaglass: 8, Graphite: 4), 4, true);
932    "phase-conduit" -> BridgeBlock::new(1, true, cost!(Metaglass: 20, Titanium: 10, Silicon: 7, PhaseFabric: 5), 12, true);
933    "reinforced-conduit" => ConduitBlock::new(1, false, cost!(Beryllium: 2));
934    "reinforced-liquid-junction" -> BasicBlock::new(1, true, cost!(Graphite: 4, Beryllium: 8));
935    "reinforced-bridge-conduit" => BridgeBlock::new(1, true, cost!(Graphite: 8, Beryllium: 20), 4, true);
936    "reinforced-liquid-router" -> BasicBlock::new(1, true, cost!(Graphite: 8, Beryllium: 4));
937    "reinforced-liquid-container" -> BasicBlock::new(2, true, cost!(Tungsten: 10, Beryllium: 16));
938    "reinforced-liquid-tank" -> BasicBlock::new(3, true, cost!(Tungsten: 40, Beryllium: 50));
939    "ground-factory" => UnitFactory::new(3, false, cost!(Copper: 50, Lead: 120, Silicon: 80), units::GROUND_UNITS);
940    "air-factory" => UnitFactory::new(3, false, cost!(Copper: 60, Lead: 70), units::AIR_UNITS);
941    "naval-factory" => UnitFactory::new(3, false, cost!(Copper: 150, Lead: 130, Metaglass: 120), units::NAVAL_UNITS);
942    "additive-reconstructor" => ConstructorBlock::new(3, false, cost!(Copper: 200, Lead: 120, Silicon: 90));
943    "multiplicative-reconstructor" => ConstructorBlock::new(5, false, cost!(Lead: 650, Titanium: 350, Thorium: 650, Silicon: 450));
944    "exponential-reconstructor" => ConstructorBlock::new(7, false, cost!(Lead: 2000, Titanium: 2000, Thorium: 750, Silicon: 1000, Plastanium: 450, PhaseFabric: 600));
945    "tetrative-reconstructor" => ConstructorBlock::new(9, false, cost!(Lead: 4000, Thorium: 1000, Silicon: 3000, Plastanium: 600, PhaseFabric: 600, SurgeAlloy: 800));
946    "repair-point" -> RepairTurret::new(1, true, cost!(Copper: 30, Lead: 30, Silicon: 20));
947    "repair-turret" -> RepairTurret::new(2, true, cost!(Thorium: 80, Silicon: 90, Plastanium: 60));
948    "tank-fabricator" => UnitFactory::new(3, true, cost!(Silicon: 200, Beryllium: 150), &[crate::unit::Type::Stell]);
949    "ship-fabricator" => UnitFactory::new(3, true, cost!(Silicon: 250, Beryllium: 200), &[crate::unit::Type::Elude]);
950    "mech-fabricator" => UnitFactory::new(3, true, cost!(Silicon: 200, Graphite: 300, Tungsten: 60), &[crate::unit::Type::Merui]);
951    "tank-refabricator" => ConstructorBlock::new(3, true, cost!(Beryllium: 200, Tungsten: 80, Silicon: 100));
952    "mech-refabricator" => ConstructorBlock::new(3, true, cost!(Beryllium: 250, Tungsten: 120, Silicon: 150));
953    "ship-refabricator" => ConstructorBlock::new(3, true, cost!(Beryllium: 200, Tungsten: 100, Silicon: 150, Oxide: 40));
954    "prime-refabricator" => ConstructorBlock::new(5, true, cost!(Thorium: 250, Oxide: 200, Tungsten: 200, Silicon: 400));
955    "tank-assembler" => AssemblerBlock::new(5, true, cost!(Thorium: 500, Oxide: 150, Carbide: 80, Silicon: 500));
956    "ship-assembler" => AssemblerBlock::new(5, true, cost!(Carbide: 100, Oxide: 200, Tungsten: 500, Silicon: 800, Thorium: 400));
957    "mech-assembler" => AssemblerBlock::new(5, true, cost!(Carbide: 200, Thorium: 600, Oxide: 200, Tungsten: 500, Silicon: 900)); // smh collaris
958    "basic-assembler-module" => AssemblerModule::new(5, true, cost!(Carbide: 300, Thorium: 500, Oxide: 200, PhaseFabric: 400)); // the dummy block
959    "unit-repair-tower" -> BasicBlock::new(2, true, cost!(Graphite: 90, Silicon: 90, Tungsten: 80));
960    "launch-pad" -> BasicBlock::new(3, true, cost!(Copper: 350, Lead: 200, Titanium: 150, Silicon: 140));
961    "landing-pad" -> BasicBlock::new(4, true, cost!(Copper: 200, Graphite: 100, Titanium: 100));
962    "advanced-launch-pad" -> BasicBlock::new(4, true, cost!(Copper: 350, Silicon: 250, Lead: 300, Titanium: 200));
963    "interplanetary-accelerator" -> BasicBlock::new(7, true, cost!(Copper: 16000, Silicon: 11000, Thorium: 13000, Titanium: 12000, SurgeAlloy: 6000, PhaseFabric: 5000));
964    "mechanical-drill" -> DrillBlock::new(2, true, cost!(Copper: 12));
965    "pneumatic-drill" -> DrillBlock::new(2, true, cost!(Copper: 18, Graphite: 10));
966    "laser-drill" -> DrillBlock::new(3, true, cost!(Copper: 35, Graphite: 30, Titanium: 20, Silicon: 30));
967    "blast-drill" -> DrillBlock::new(4, true, cost!(Copper: 65, Titanium: 50, Thorium: 75, Silicon: 60));
968    "water-extractor" -> BasicBlock::new(2, true, cost!(Copper: 30, Lead: 30, Metaglass: 30, Graphite: 30));
969    "oil-extractor" -> BasicBlock::new(3, true, cost!(Copper: 150, Lead: 115, Graphite: 175, Thorium: 115, Silicon: 75));
970    "vent-condenser" -> ProductionBlock::new(3, true, cost!(Graphite: 20, Beryllium: 60));
971    "cliff-crusher" => WallDrillBlock::new(2, false, cost!(Beryllium: 100, Graphite: 40));
972    "large-cliff-crusher" => WallDrillBlock::new(3, false, cost!(Silicon: 80, SurgeAlloy: 15, Beryllium: 100, Tungsten: 50));
973    "plasma-bore" => DrillBlock::new(2, false, cost!(Beryllium: 40));
974    "large-plasma-bore" => DrillBlock::new(3, false, cost!(Silicon: 100, Oxide: 25, Beryllium: 100, Tungsten: 70));
975    "impact-drill" -> DrillBlock::new(4, true, cost!(Silicon: 70, Beryllium: 90, Graphite: 60));
976    "eruption-drill" -> DrillBlock::new(5, true, cost!(Silicon: 200, Oxide: 20, Tungsten: 200, Thorium: 120));
977    "reinforced-message" -> MessageLogic::new(1, true, cost!(Graphite: 10, Beryllium: 5));
978    "message" -> MessageLogic::new(1, true, cost!(Copper: 5, Graphite: 5));
979    "switch" => SwitchLogic::new(1, true, cost!(Copper: 5, Graphite: 5));
980    "micro-processor" -> ProcessorLogic::new(1, true, cost!(Copper: 90, Lead: 50, Silicon: 50));
981    "logic-processor" -> ProcessorLogic::new(2, true, cost!(Lead: 320, Graphite: 60, Thorium: 50, Silicon: 80));
982    "hyper-processor" -> ProcessorLogic::new(3, true, cost!(Lead: 450, Thorium: 75, Silicon: 150, SurgeAlloy: 50));
983    "memory-cell" -> MemoryBlock::new(1, true, cost!(Copper: 30, Graphite: 30, Silicon: 30));
984    "memory-bank" -> MemoryBlock::new(2, true, cost!(Copper: 30, Graphite: 80, Silicon: 80, PhaseFabric: 30));
985    "logic-display" -> BasicBlock::new(3, true, cost!(Lead: 100, Metaglass: 50, Silicon: 50));
986    "large-logic-display" -> BasicBlock::new(6, true, cost!(Lead: 200, Metaglass: 100, Silicon: 150, PhaseFabric: 75));
987    "tile-logic-display" => TileableDisplay::new(1, false, cost!(Lead: 8, Silicon: 8, Metaglass: 8, PhaseFabric: 3));
988    "canvas" => CanvasBlock::new(2, true, cost!(Silicon: 30, Beryllium: 10), 12);
989    "illuminator" -> LampBlock::new(1, true, cost!(Lead: 8, Graphite: 12, Silicon: 8));
990    "power-node" -> ConnectorBlock::new(1, true, cost!(Copper: 1, Lead: 3), 10);
991    "power-node-large" -> ConnectorBlock::new(2, true, cost!(Lead: 10, Titanium: 5, Silicon: 3), 15);
992    "surge-tower" -> ConnectorBlock::new(2, true, cost!(Lead: 10, Titanium: 7, Silicon: 15, SurgeAlloy: 15), 2);
993    "diode" => DiodeBlock::new(1, false, cost!(Metaglass: 10, Silicon: 10, Plastanium: 5));
994    "battery" -> BasicBlock::new(1, true, cost!(Copper: 5, Lead: 20));
995    "battery-large" -> BasicBlock::new(3, true, cost!(Lead: 50, Titanium: 20, Silicon: 30));
996    "combustion-generator" -> GeneratorBlock::new(1, true, cost!(Copper: 25, Lead: 15));
997    "thermal-generator" -> GeneratorBlock::new(2, true, cost!(Copper: 40, Lead: 50, Metaglass: 40, Graphite: 35, Silicon: 35));
998    "steam-generator" -> GeneratorBlock::new(2, true, cost!(Copper: 35, Lead: 40, Graphite: 25, Silicon: 30));
999    "differential-generator" -> GeneratorBlock::new(3, true, cost!(Copper: 70, Lead: 100, Metaglass: 50, Titanium: 50, Silicon: 65));
1000    "rtg-generator" -> GeneratorBlock::new(2, true, cost!(Lead: 100, Thorium: 50, Silicon: 75, Plastanium: 75, PhaseFabric: 25));
1001    "solar-panel" -> GeneratorBlock::new(1, true, cost!(Lead: 10, Silicon: 15));
1002    "solar-panel-large" -> GeneratorBlock::new(3, true, cost!(Lead: 80, Silicon: 110, PhaseFabric: 15));
1003    "thorium-reactor" -> NuclearGeneratorBlock::new(3, true, cost!(Lead: 300, Metaglass: 50, Graphite: 150, Thorium: 150, Silicon: 200));
1004    "impact-reactor" -> ImpactReactorBlock::new(4, true, cost!(Lead: 500, Metaglass: 250, Graphite: 400, Thorium: 100, Silicon: 300, SurgeAlloy: 250));
1005    "beam-node" -> ConnectorBlock::new(1, true, cost!(Beryllium: 8), 4);
1006    "beam-tower" -> ConnectorBlock::new(3, true, cost!(Beryllium: 30, Oxide: 10, Silicon: 10), 12);
1007    "turbine-condenser" -> GeneratorBlock::new(3, true, cost!(Beryllium: 60));
1008    "chemical-combustion-chamber" -> GeneratorBlock::new(3, true, cost!(Graphite: 40, Tungsten: 40, Oxide: 40, Silicon: 30));
1009    "pyrolysis-generator" -> GeneratorBlock::new(3, true, cost!(Graphite: 50, Carbide: 50, Oxide: 60, Silicon: 50));
1010    "flux-reactor" -> GeneratorBlock::new(5, true, cost!(Graphite: 300, Carbide: 200, Oxide: 100, Silicon: 600, SurgeAlloy: 300));
1011    "neoplasia-reactor" => Neoplasia::new(5, true, cost!(Tungsten: 1000, Carbide: 300, Oxide: 150, Silicon: 500, PhaseFabric: 300, SurgeAlloy: 200));
1012    "core-shard" -> BasicBlock::new(3, true, cost!(Copper: 1000, Lead: 800));
1013    "core-foundation" -> BasicBlock::new(4, true, cost!(Copper: 3000, Lead: 3000, Silicon: 2000));
1014    "core-nucleus" -> BasicBlock::new(5, true, cost!(Copper: 8000, Lead: 8000, Thorium: 4000, Silicon: 5000));
1015    "core-bastion" -> BasicBlock::new(4, true, cost!(Graphite: 1000, Silicon: 1000, Beryllium: 800));
1016    "core-citadel" -> BasicBlock::new(5, true, cost!(Silicon: 4000, Beryllium: 4000, Tungsten: 3000, Oxide: 1000));
1017    "core-acropolis" -> BasicBlock::new(6, true, cost!(Beryllium: 6000, Silicon: 5000, Tungsten: 5000, Carbide: 3000, Oxide: 3000));
1018    "container" -> BasicBlock::new(2, true, cost!(Titanium: 100));
1019    "vault" -> BasicBlock::new(3, true, cost!(Titanium: 250, Thorium: 125));
1020    "reinforced-container" -> BasicBlock::new(2, true, cost!(Tungsten: 30, Graphite: 40));
1021    "reinforced-vault" -> BasicBlock::new(3, true, cost!(Tungsten: 125, Thorium: 70, Beryllium: 100));
1022    "duo" -> ItemTurret::new(1, true, cost!(Copper: 35));
1023    "scatter" -> ItemTurret::new(2, true, cost!(Copper: 85, Lead: 45));
1024    "scorch" -> ItemTurret::new(1, true, cost!(Copper: 25, Graphite: 22));
1025    "hail" -> ItemTurret::new(1, true, cost!(Copper: 40, Graphite: 17));
1026    "wave" -> Turret::new(2, true, cost!(Copper: 25, Lead: 75, Metaglass: 45));
1027    "tsunami" -> Turret::new(3, true, cost!(Lead: 400, Metaglass: 100, Titanium: 250, Thorium: 100));
1028    "lancer" -> Turret::new(2, true, cost!(Copper: 60, Lead: 70, Titanium: 30, Silicon: 60));
1029    "arc" -> Turret::new(1, true, cost!(Copper: 50, Lead: 50));
1030    "parallax" -> TractorBeamTurret::new(2, true, cost!(Graphite: 30, Titanium: 90, Silicon: 120));
1031    "swarmer" -> ItemTurret::new(2, true, cost!(Graphite: 35, Titanium: 35, Silicon: 30, Plastanium: 45));
1032    "salvo" -> ItemTurret::new(2, true, cost!(Copper: 100, Graphite: 80, Titanium: 50));
1033    "segment" -> PointDefenseTurret::new(2, true, cost!(Titanium: 40, Thorium: 80, Silicon: 130, PhaseFabric: 40));
1034    "fuse" -> ItemTurret::new(3, true, cost!(Copper: 225, Graphite: 225, Thorium: 100));
1035    "ripple" -> ItemTurret::new(3, true, cost!(Copper: 150, Graphite: 135, Titanium: 60));
1036    "cyclone" -> ItemTurret::new(3, true, cost!(Copper: 200, Titanium: 125, Plastanium: 80));
1037    "foreshadow" -> ItemTurret::new(4, true, cost!(Copper: 1000, Metaglass: 600, Silicon: 600, Plastanium: 200, SurgeAlloy: 300));
1038    "spectre" -> ItemTurret::new(4, true, cost!(Copper: 900, Graphite: 300, Thorium: 250, Plastanium: 175, SurgeAlloy: 250));
1039    "meltdown" -> Turret::new(4, true, cost!(Copper: 1200, Lead: 350, Graphite: 300, Silicon: 325, SurgeAlloy: 325));
1040    "breach" -> ItemTurret::new(3, true, cost!(Beryllium: 150, Silicon: 150, Graphite: 250));
1041    "diffuse" -> ItemTurret::new(3, true, cost!(Beryllium: 150, Silicon: 200, Graphite: 200, Tungsten: 50));
1042    "sublimate" -> ContinousTurret::new(3, true, cost!(Tungsten: 150, Silicon: 200, Oxide: 40, Beryllium: 400));
1043    "titan" -> ItemTurret::new(4, true, cost!(Tungsten: 250, Silicon: 300, Thorium: 400));
1044    "disperse" -> ItemTurret::new(4, true, cost!(Thorium: 50, Oxide: 150, Silicon: 200, Beryllium: 350));
1045    "afflict" -> Turret::new(4, true, cost!(SurgeAlloy: 100, Silicon: 200, Graphite: 250, Oxide: 40));
1046    "lustre" -> ContinousTurret::new(4, true, cost!(Silicon: 250, Graphite: 200, Oxide: 50, Carbide: 90));
1047    "scathe" -> ItemTurret::new(4, true, cost!(Oxide: 200, SurgeAlloy: 400, Silicon: 800, Carbide: 500, PhaseFabric: 300));
1048    "malign" -> Turret::new(5, true, cost!(Carbide: 400, Beryllium: 2000, Silicon: 800, Graphite: 800, PhaseFabric: 300));
1049    "smite" -> ItemTurret::new(5, true, cost!(Oxide: 200, SurgeAlloy: 400, Silicon: 800, Carbide: 500, PhaseFabric: 300));
1050    // sandbox only
1051    "beam-link" -> ConnectorBlock::new(3, true, &[], 12);
1052    "power-source" -> ConnectorBlock::new(1, true, &[], 100);
1053    "power-void" -> BasicBlock::new(1, true, &[]);
1054    "world-processor" -> ProcessorLogic::new(1, true, &[]);
1055    "world-message" -> MessageLogic::new(1, true, &[]);
1056    "world-cell" -> MemoryBlock::new(1, true, &[]);
1057    "world-switch" => SwitchLogic::new(1, true, &[]);
1058    "liquid-source" => FluidBlock::new(1, true, &[]);
1059    "liquid-void" -> BasicBlock::new(1, true, &[]);
1060    "shield-projector" -> ShieldBlock::new(3, true, &[]);
1061    "large-shield-projector" -> ShieldBlock::new(4, true, &[]);
1062    "payload-source" => PayloadBlock::new(5, false, &[]);
1063    "payload-void" => SimplePayloadBlock::new(5, true, &[]);
1064    "item-source" => ItemBlock::new(1, true, &[]);
1065    "item-void" -> BasicBlock::new(1, true, &[]);
1066    "heat-source" => HeatCrafter::new(1, false, &[]);
1067}