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