mindustry logic execution, map- and schematic- parsing and rendering
fix bad segment + parallax + sandbox item rendering
41 files changed, 333 insertions, 294 deletions
@@ -1,6 +1,6 @@ [package] name = "mindus" -version = "1.0.1" +version = "1.0.2" edition = "2021" description = "A library for working with mindustry data formats (eg schematics) (fork of plandustry)" authors = [ diff --git a/assets/blocks/defense/force-projector-top.png b/assets/blocks/defense/force-projector-top.png Binary files differdeleted file mode 100644 index 540cc9b..0000000 --- a/assets/blocks/defense/force-projector-top.png +++ /dev/null diff --git a/assets/blocks/defense/mend-projector-top.png b/assets/blocks/defense/mend-projector-top.png Binary files differdeleted file mode 100644 index 24c80c0..0000000 --- a/assets/blocks/defense/mend-projector-top.png +++ /dev/null diff --git a/assets/blocks/defense/mender-top.png b/assets/blocks/defense/mender-top.png Binary files differdeleted file mode 100644 index c1feb99..0000000 --- a/assets/blocks/defense/mender-top.png +++ /dev/null diff --git a/assets/blocks/defense/overdrive-dome-top.png b/assets/blocks/defense/overdrive-dome-top.png Binary files differdeleted file mode 100644 index 4daa23b..0000000 --- a/assets/blocks/defense/overdrive-dome-top.png +++ /dev/null diff --git a/assets/blocks/defense/overdrive-projector-top.png b/assets/blocks/defense/overdrive-projector-top.png Binary files differdeleted file mode 100644 index 5b51c8d..0000000 --- a/assets/blocks/defense/overdrive-projector-top.png +++ /dev/null diff --git a/assets/blocks/defense/parallax.png b/assets/blocks/defense/parallax.png Binary files differdeleted file mode 100644 index 2b6109e..0000000 --- a/assets/blocks/defense/parallax.png +++ /dev/null diff --git a/assets/blocks/defense/segment.png b/assets/blocks/defense/segment.png Binary files differdeleted file mode 100644 index 57da490..0000000 --- a/assets/blocks/defense/segment.png +++ /dev/null diff --git a/assets/blocks/sandbox/item-source.png b/assets/blocks/distribution/item-source.png Binary files differindex 21c254c..21c254c 100644 --- a/assets/blocks/sandbox/item-source.png +++ b/assets/blocks/distribution/item-source.png diff --git a/assets/blocks/sandbox/item-void.png b/assets/blocks/distribution/item-void.png Binary files differindex eb3e38a..eb3e38a 100644 --- a/assets/blocks/sandbox/item-void.png +++ b/assets/blocks/distribution/item-void.png diff --git a/assets/blocks/sandbox/liquid-source.png b/assets/blocks/liquid/liquid-source.png Binary files differindex 92693f9..92693f9 100644 --- a/assets/blocks/sandbox/liquid-source.png +++ b/assets/blocks/liquid/liquid-source.png diff --git a/assets/blocks/sandbox/liquid-void.png b/assets/blocks/liquid/liquid-void.png Binary files differindex 0aa9e75..0aa9e75 100644 --- a/assets/blocks/sandbox/liquid-void.png +++ b/assets/blocks/liquid/liquid-void.png diff --git a/assets/blocks/power/differential-generator-top.png b/assets/blocks/power/differential-generator-top.png Binary files differdeleted file mode 100644 index eb504bc..0000000 --- a/assets/blocks/power/differential-generator-top.png +++ /dev/null diff --git a/assets/blocks/power/thorium-reactor-top.png b/assets/blocks/power/thorium-reactor-top.png Binary files differdeleted file mode 100644 index a22ce50..0000000 --- a/assets/blocks/power/thorium-reactor-top.png +++ /dev/null diff --git a/assets/blocks/sandbox/heat-source.png b/assets/blocks/production/heat-source.png Binary files differindex e281804..e281804 100644 --- a/assets/blocks/sandbox/heat-source.png +++ b/assets/blocks/production/heat-source.png diff --git a/assets/blocks/production/kiln-top.png b/assets/blocks/production/kiln-top.png Binary files differdeleted file mode 100644 index 54b786e..0000000 --- a/assets/blocks/production/kiln-top.png +++ /dev/null diff --git a/assets/blocks/production/plastanium-compressor-top.png b/assets/blocks/production/plastanium-compressor-top.png Binary files differdeleted file mode 100644 index 5289200..0000000 --- a/assets/blocks/production/plastanium-compressor-top.png +++ /dev/null diff --git a/assets/blocks/production/silicon-crucible-top.png b/assets/blocks/production/silicon-crucible-top.png Binary files differdeleted file mode 100644 index 36cc10b..0000000 --- a/assets/blocks/production/silicon-crucible-top.png +++ /dev/null diff --git a/assets/blocks/sandbox/heat-source-heat.png b/assets/blocks/sandbox/heat-source-heat.png Binary files differdeleted file mode 100644 index 92ae12d..0000000 --- a/assets/blocks/sandbox/heat-source-heat.png +++ /dev/null diff --git a/assets/blocks/sandbox/source-bottom.png b/assets/blocks/sandbox/source-bottom.png Binary files differdeleted file mode 100644 index a2dd7b5..0000000 --- a/assets/blocks/sandbox/source-bottom.png +++ /dev/null diff --git a/assets/blocks/turrets/parallax.png b/assets/blocks/turrets/parallax.png Binary files differnew file mode 100644 index 0000000..881149a --- /dev/null +++ b/assets/blocks/turrets/parallax.png diff --git a/assets/blocks/turrets/segment.png b/assets/blocks/turrets/segment.png Binary files differnew file mode 100644 index 0000000..6b752af --- /dev/null +++ b/assets/blocks/turrets/segment.png @@ -50,6 +50,7 @@ where } fn main() -> Result<(), ZipError> { + let _ = std::fs::remove_dir_all("target/out"); let walkdir = WalkDir::new("assets"); let it = walkdir.into_iter(); println!("cargo:rerun-if-changed=assets/"); diff --git a/src/block/base.rs b/src/block/base.rs deleted file mode 100644 index fde5c5a..0000000 --- a/src/block/base.rs +++ /dev/null @@ -1,104 +0,0 @@ -//! all the uncategorized blocks (they need to be categorized for proper rendering tho) -use std::any::Any; - -use crate::block::simple::{cost, state_impl, BuildCost, SimpleBlock}; -use crate::block::{ - impl_block, make_register, BlockLogic, DataConvertError, DeserializeError, SerializeError, -}; -use crate::data::dynamic::{DynData, DynType}; -use crate::data::GridPos; -use crate::item::storage::Storage; - -make_register! { - "mender" => SimpleBlock::new(1, true, cost!(Copper: 25, Lead: 30)); - "mend-projector" => SimpleBlock::new(2, true, cost!(Copper: 50, Lead: 100, Titanium: 25, Silicon: 40)); - "overdrive-projector" => SimpleBlock::new(2, true, cost!(Lead: 100, Titanium: 75, Silicon: 75, Plastanium: 30)); - "overdrive-dome" => SimpleBlock::new(3, true, cost!(Lead: 200, Titanium: 130, Silicon: 130, Plastanium: 80, SurgeAlloy: 120)); - "force-projector" => SimpleBlock::new(3, true, cost!(Lead: 100, Titanium: 75, Silicon: 125)); - "shock-mine" => SimpleBlock::new(1, true, cost!(Lead: 25, Silicon: 12)); - "illuminator" => LampBlock::new(1, true, cost!(Lead: 8, Graphite: 12, Silicon: 8)); - "launch-pad" => SimpleBlock::new(3, true, cost!(Copper: 350, Lead: 200, Titanium: 150, Silicon: 140)); - "radar" => SimpleBlock::new(1, true, cost!(Silicon: 60, Graphite: 50, Beryllium: 10)); - "build-tower" => SimpleBlock::new(3, true, cost!(Silicon: 150, Oxide: 40, Thorium: 60)); - "regen-projector" => SimpleBlock::new(3, true, cost!(Silicon: 80, Tungsten: 60, Oxide: 40, Beryllium: 80)); - // barrier projector - // editor only - "shockwave-tower" => SimpleBlock::new(3, true, cost!(SurgeAlloy: 50, Silicon: 150, Oxide: 30, Tungsten: 100)); - "shield-projector" => SimpleBlock::new(3, true, &[]); - "large-shield-projector" => SimpleBlock::new(4, true, &[]); -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct RGBA(u8, u8, u8, u8); - -impl From<u32> for RGBA { - fn from(value: u32) -> Self { - Self( - (value >> 24) as u8, - (value >> 16) as u8, - (value >> 8) as u8, - value as u8, - ) - } -} - -impl From<RGBA> for u32 { - fn from(value: RGBA) -> Self { - (u32::from(value.0) << 24) - | (u32::from(value.1) << 16) - | (u32::from(value.2) << 8) - | u32::from(value.3) - } -} - -pub struct LampBlock { - size: u8, - symmetric: bool, - build_cost: BuildCost, -} - -impl LampBlock { - #[must_use] - pub const fn new(size: u8, symmetric: bool, build_cost: BuildCost) -> Self { - assert!(size != 0, "invalid size"); - Self { - size, - symmetric, - build_cost, - } - } - - state_impl!(pub RGBA); -} - -impl BlockLogic for LampBlock { - impl_block!(); - - fn data_from_i32(&self, config: i32, _: GridPos) -> Result<DynData, DataConvertError> { - Ok(DynData::Int(config)) - } - - fn deserialize_state(&self, data: DynData) -> Result<Option<Box<dyn Any>>, DeserializeError> { - match data { - DynData::Int(rgba) => Ok(Some(Self::create_state(RGBA::from(rgba as u32)))), - _ => Err(DeserializeError::InvalidType { - have: data.get_type(), - expect: DynType::Int, - }), - } - } - - fn clone_state(&self, state: &dyn Any) -> Box<dyn Any> { - let state = Self::get_state(state); - Box::new(Self::create_state(*state)) - } - - fn mirror_state(&self, _: &mut dyn Any, _: bool, _: bool) {} - - fn rotate_state(&self, _: &mut dyn Any, _: bool) {} - - fn serialize_state(&self, state: &dyn Any) -> Result<DynData, SerializeError> { - let state = Self::get_state(state); - Ok(DynData::Int(u32::from(*state) as i32)) - } -} diff --git a/src/block/campaign.rs b/src/block/campaign.rs new file mode 100644 index 0000000..9c87a15 --- /dev/null +++ b/src/block/campaign.rs @@ -0,0 +1,8 @@ +//! campaign blocks +use crate::block::make_register; +use crate::block::simple::{cost, SimpleBlock}; + +make_register! { + "launch-pad" => SimpleBlock::new(3, true, cost!(Copper: 350, Lead: 200, Titanium: 150, Silicon: 140)); + "interplanetary-accelerator" => SimpleBlock::new(7, true, cost!(Copper: 16000, Silicon: 11000, Thorium: 13000, Titanium: 12000, SurgeAlloy: 6000, PhaseFabric: 5000)); +} diff --git a/src/block/defense.rs b/src/block/defense.rs index 2cf3bbc..e197c37 100644 --- a/src/block/defense.rs +++ b/src/block/defense.rs @@ -1,93 +1,19 @@ -//! walls -use std::any::Any; - -use crate::block::simple::{cost, state_impl, BuildCost, SimpleBlock}; -use crate::block::{ - impl_block, make_register, BlockLogic, DataConvertError, DeserializeError, SerializeError, -}; -use crate::data::dynamic::{DynData, DynType}; -use crate::data::GridPos; -use crate::item::storage::Storage; - +//! defense +use crate::block::make_register; +use crate::block::simple::{cost, SimpleBlock}; make_register! { - "copper-wall" => SimpleBlock::new(1, true, cost!(Copper: 6)); - "copper-wall-large" => SimpleBlock::new(2, true, cost!(Copper: 6 * 4)); - "titanium-wall" => SimpleBlock::new(1, true, cost!(Titanium: 6)); - "titanium-wall-large" => SimpleBlock::new(2, true, cost!(Titanium: 6 * 4)); - "plastanium-wall" => SimpleBlock::new(1, true, cost!(Metaglass: 2, Plastanium: 5)); - "plastanium-wall-large" => SimpleBlock::new(2, true, cost!(Metaglass: 2 * 4, Plastanium: 5 * 4)); - "thorium-wall" => SimpleBlock::new(1, true, cost!(Thorium: 6)); - "thorium-wall-large" => SimpleBlock::new(2, true, cost!(Thorium: 6 * 4)); - "phase-wall" => SimpleBlock::new(1, true, cost!(PhaseFabric: 6)); - "phase-wall-large" => SimpleBlock::new(2, true, cost!(PhaseFabric: 6 * 4)); - "surge-wall" => SimpleBlock::new(1, true, cost!(SurgeAlloy: 6)); - "surge-wall-large" => SimpleBlock::new(2, true, cost!(SurgeAlloy: 6 * 4)); - "door" => DoorBlock::new(1, true, cost!(Titanium: 6, Silicon: 4)); - "door-large" => DoorBlock::new(2, true, cost!(Titanium: 6 * 4, Silicon: 4 * 4)); - "tungsten-wall" => SimpleBlock::new(1, true, cost!(Tungsten: 6)); - "large-tungsten-wall" => SimpleBlock::new(2, true, cost!(Tungsten: 6 * 4)); - "blast-door" => DoorBlock::new(2, true, cost!(Tungsten: 24, Silicon: 24)); - "reinforced-surge-wall" => SimpleBlock::new(1, true, cost!(SurgeAlloy: 6, Tungsten: 2)); - "reinforced-surge-wall-large" => SimpleBlock::new(2, true, cost!(SurgeAlloy: 6 * 4, Tungsten: 2 * 4)); - "carbide-wall" => SimpleBlock::new(1, true, cost!(Thorium: 6, Carbide: 6)); - "carbide-wall-large" => SimpleBlock::new(2, true, cost!(Thorium: 6 * 4, Carbide: 6 * 4)); - "shielded-wall" => SimpleBlock::new(2, true, cost!(PhaseFabric: 20, SurgeAlloy: 12, Beryllium: 12)); - // sandbox only - "scrap-wall" => SimpleBlock::new(1, true, cost!(Scrap: 6)); - "scrap-wall-large" => SimpleBlock::new(2, true, cost!(Scrap: 24)); - "scrap-wall-huge" => SimpleBlock::new(3, true, cost!(Scrap: 54)); - "scrap-wall-gigantic" => SimpleBlock::new(4, true, cost!(Scrap: 96)); - "thruster" => SimpleBlock::new(4, false, cost!(Scrap: 96)); -} - -pub struct DoorBlock { - size: u8, - symmetric: bool, - build_cost: BuildCost, -} - -impl DoorBlock { - #[must_use] - pub const fn new(size: u8, symmetric: bool, build_cost: BuildCost) -> Self { - assert!(size != 0, "invalid size"); - Self { - size, - symmetric, - build_cost, - } - } - - state_impl!(pub bool); -} - -impl BlockLogic for DoorBlock { - impl_block!(); - - fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> { - Ok(DynData::Boolean(false)) - } - - fn deserialize_state(&self, data: DynData) -> Result<Option<Box<dyn Any>>, DeserializeError> { - match data { - DynData::Boolean(opened) => Ok(Some(Self::create_state(opened))), - _ => Err(DeserializeError::InvalidType { - have: data.get_type(), - expect: DynType::Boolean, - }), - } - } - - fn clone_state(&self, state: &dyn Any) -> Box<dyn Any> { - let state = Self::get_state(state); - Box::new(Self::create_state(*state)) - } - - fn mirror_state(&self, _: &mut dyn Any, _: bool, _: bool) {} - - fn rotate_state(&self, _: &mut dyn Any, _: bool) {} - - fn serialize_state(&self, state: &dyn Any) -> Result<DynData, SerializeError> { - let state = Self::get_state(state); - Ok(DynData::Boolean(*state)) - } + "mender" => SimpleBlock::new(1, true, cost!(Copper: 25, Lead: 30)); + "mend-projector" => SimpleBlock::new(2, true, cost!(Copper: 50, Lead: 100, Titanium: 25, Silicon: 40)); + "overdrive-projector" => SimpleBlock::new(2, true, cost!(Lead: 100, Titanium: 75, Silicon: 75, Plastanium: 30)); + "overdrive-dome" => SimpleBlock::new(3, true, cost!(Lead: 200, Titanium: 130, Silicon: 130, Plastanium: 80, SurgeAlloy: 120)); + "force-projector" => SimpleBlock::new(3, true, cost!(Lead: 100, Titanium: 75, Silicon: 125)); + "regen-projector" => SimpleBlock::new(3, true, cost!(Silicon: 80, Tungsten: 60, Oxide: 40, Beryllium: 80)); + "shock-mine" => SimpleBlock::new(1, true, cost!(Lead: 25, Silicon: 12)); + "radar" => SimpleBlock::new(1, true, cost!(Silicon: 60, Graphite: 50, Beryllium: 10)); + "build-tower" => SimpleBlock::new(3, true, cost!(Silicon: 150, Oxide: 40, Thorium: 60)); + // barrier projector + // editor only + "shockwave-tower" => SimpleBlock::new(3, true, cost!(SurgeAlloy: 50, Silicon: 150, Oxide: 30, Tungsten: 100)); + "shield-projector" => SimpleBlock::new(3, true, &[]); + "large-shield-projector" => SimpleBlock::new(4, true, &[]); } diff --git a/src/block/distribution.rs b/src/block/distribution.rs index 9076a12..1114115 100644 --- a/src/block/distribution.rs +++ b/src/block/distribution.rs @@ -3,7 +3,7 @@ use std::any::Any; use std::error::Error; use std::fmt; -use image::{Rgba, RgbaImage}; +use image::RgbaImage; use crate::block::simple::{cost, state_impl, BuildCost, SimpleBlock}; use crate::block::{ @@ -110,22 +110,17 @@ impl BlockLogic for ItemBlock { } fn draw(&self, category: &str, name: &str, state: Option<&dyn Any>) -> Option<RgbaImage> { + if !matches!( + name, + "unloader" | "item-source" | "sorter" | "inverted-sorter" + ) { + return None; + } let mut p = load(category, name).unwrap(); if let Some(state) = state { if let Some(s) = Self::get_state(state) { - let item_c = s.color(); - let [tr, tg, tb] = [ - item_c[0] as f32 / 255.0, - item_c[1] as f32 / 255.0, - item_c[2] as f32 / 255.0, - ]; let mut top = load(category, "center").unwrap(); - for Rgba([r, g, b, _]) in top.pixels_mut() { - *r = (*r as f32 * tr) as u8; - *g = (*g as f32 * tg) as u8; - *b = (*b as f32 * tb) as u8; - } - + crate::utils::image::tint(&mut top, s.color()); image::imageops::overlay(&mut p, &top, 0, 0); return Some(p); } diff --git a/src/block/liquid.rs b/src/block/liquid.rs index 2400988..758ff0d 100644 --- a/src/block/liquid.rs +++ b/src/block/liquid.rs @@ -10,6 +10,7 @@ use crate::block::{ }; use crate::content; use crate::data::dynamic::{DynData, DynType}; +use crate::data::renderer::load; use crate::data::GridPos; use crate::fluid; use crate::item::storage::Storage; @@ -102,6 +103,26 @@ impl BlockLogic for FluidBlock { Some(fluid) => Ok(DynData::Content(content::Type::Fluid, (*fluid).into())), } } + + fn draw( + &self, + category: &str, + name: &str, + state: Option<&dyn Any>, + ) -> Option<image::RgbaImage> { + let mut p = load(category, name).unwrap(); + if let Some(state) = state { + if let Some(s) = Self::get_state(state) { + let mut top = load("distribution", "center").unwrap(); + crate::utils::image::tint(&mut top, s.color()); + image::imageops::overlay(&mut p, &top, 0, 0); + return Some(p); + } + } + let mut null = load("distribution", "cross-full").unwrap(); + image::imageops::overlay(&mut null, &p, 0, 0); + Some(null) + } } #[derive(Clone, Copy, Debug, Eq, PartialEq)] diff --git a/src/block/logic.rs b/src/block/logic.rs index 027302b..a56b564 100644 --- a/src/block/logic.rs +++ b/src/block/logic.rs @@ -20,6 +20,7 @@ use crate::item::storage::Storage; make_register! { // todo reinforced proc + "reinforced-message" => MessageLogic::new(1, true, cost!(Graphite: 10, Beryllium: 5)); "message" => MessageLogic::new(1, true, cost!(Copper: 5, Graphite: 5)); "switch" => SwitchLogic::new(1, true, cost!(Copper: 5, Graphite: 5)); "micro-processor" => ProcessorLogic::new(1, true, cost!(Copper: 90, Lead: 50, Silicon: 50)); diff --git a/src/block/mod.rs b/src/block/mod.rs index 70fd498..47501cc 100644 --- a/src/block/mod.rs +++ b/src/block/mod.rs @@ -1,6 +1,7 @@ //! deal with blocks. //! //! categorized as mindustry categorizes them in its assets folder, for easy drawing +//! with the exception of sandbox. use image::RgbaImage; use std::any::Any; use std::borrow::Cow; @@ -13,7 +14,7 @@ use crate::data::GridPos; use crate::item::storage::Storage as ItemStorage; use crate::registry::RegistryEntry; -pub mod base; +pub mod campaign; pub mod content; pub mod defense; pub mod distribution; @@ -26,6 +27,7 @@ pub mod production; pub mod simple; pub mod storage; pub mod turrets; +pub mod walls; pub trait BlockLogic { /// mindustry blocks are the same width and height @@ -80,11 +82,11 @@ pub(crate) use impl_block; #[derive(Debug)] pub enum DataConvertError { - Custom(Box<dyn Error>), + Custom(Box<dyn Error + Sync + Send>), } impl DataConvertError { - pub fn forward<T, E: Error + 'static>(result: Result<T, E>) -> Result<T, Self> { + pub fn forward<T, E: Error + Sync + Send + 'static>(result: Result<T, E>) -> Result<T, Self> { match result { Ok(v) => Ok(v), Err(e) => Err(Self::Custom(Box::new(e))), @@ -111,11 +113,11 @@ impl Error for DataConvertError { #[derive(Debug)] pub enum DeserializeError { InvalidType { have: DynType, expect: DynType }, - Custom(Box<dyn Error>), + Custom(Box<dyn Error + Sync + Send>), } impl DeserializeError { - pub fn forward<T, E: Error + 'static>(result: Result<T, E>) -> Result<T, Self> { + pub fn forward<T, E: Error + Sync + Send + 'static>(result: Result<T, E>) -> Result<T, Self> { match result { Ok(v) => Ok(v), Err(e) => Err(Self::Custom(Box::new(e))), @@ -145,11 +147,11 @@ impl Error for DeserializeError { #[derive(Debug)] pub enum SerializeError { - Custom(Box<dyn Error>), + Custom(Box<dyn Error + Sync + Send>), } impl SerializeError { - pub fn forward<T, E: Error + 'static>(result: Result<T, E>) -> Result<T, Self> { + pub fn forward<T, E: Error + Sync + Send + 'static>(result: Result<T, E>) -> Result<T, Self> { match result { Ok(v) => Ok(v), Err(e) => Err(Self::Custom(Box::new(e))), @@ -449,6 +451,7 @@ fn register(reg: &mut BlockRegistry<'_>) { defense::register(reg); production::register(reg); payload::register(reg); - base::register(reg); + campaign::register(reg); logic::register(reg); + walls::register(reg); } diff --git a/src/block/power.rs b/src/block/power.rs index 9e0e69b..397cb62 100644 --- a/src/block/power.rs +++ b/src/block/power.rs @@ -12,6 +12,8 @@ use crate::data::GridPos; use crate::item::storage::Storage; make_register! { + // illuminator == power ????? + "illuminator" => LampBlock::new(1, true, cost!(Lead: 8, Graphite: 12, Silicon: 8)); "power-node" => ConnectorBlock::new(1, true, cost!(Copper: 1, Lead: 3), 10); "power-node-large" => ConnectorBlock::new(2, true, cost!(Lead: 10, Titanium: 5, Silicon: 3), 15); "surge-tower" => ConnectorBlock::new(2, true, cost!(Lead: 10, Titanium: 7, Silicon: 15, SurgeAlloy: 15), 2); @@ -143,3 +145,78 @@ impl fmt::Display for ConnectorDeserializeError { } impl Error for ConnectorDeserializeError {} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct RGBA(u8, u8, u8, u8); + +impl From<u32> for RGBA { + fn from(value: u32) -> Self { + Self( + (value >> 24) as u8, + (value >> 16) as u8, + (value >> 8) as u8, + value as u8, + ) + } +} + +impl From<RGBA> for u32 { + fn from(value: RGBA) -> Self { + (u32::from(value.0) << 24) + | (u32::from(value.1) << 16) + | (u32::from(value.2) << 8) + | u32::from(value.3) + } +} + +pub struct LampBlock { + size: u8, + symmetric: bool, + build_cost: BuildCost, +} + +impl LampBlock { + #[must_use] + pub const fn new(size: u8, symmetric: bool, build_cost: BuildCost) -> Self { + assert!(size != 0, "invalid size"); + Self { + size, + symmetric, + build_cost, + } + } + + state_impl!(pub RGBA); +} + +impl BlockLogic for LampBlock { + impl_block!(); + + fn data_from_i32(&self, config: i32, _: GridPos) -> Result<DynData, DataConvertError> { + Ok(DynData::Int(config)) + } + + fn deserialize_state(&self, data: DynData) -> Result<Option<Box<dyn Any>>, DeserializeError> { + match data { + DynData::Int(rgba) => Ok(Some(Self::create_state(RGBA::from(rgba as u32)))), + _ => Err(DeserializeError::InvalidType { + have: data.get_type(), + expect: DynType::Int, + }), + } + } + + fn clone_state(&self, state: &dyn Any) -> Box<dyn Any> { + let state = Self::get_state(state); + Box::new(Self::create_state(*state)) + } + + fn mirror_state(&self, _: &mut dyn Any, _: bool, _: bool) {} + + fn rotate_state(&self, _: &mut dyn Any, _: bool) {} + + fn serialize_state(&self, state: &dyn Any) -> Result<DynData, SerializeError> { + let state = Self::get_state(state); + Ok(DynData::Int(u32::from(*state) as i32)) + } +} diff --git a/src/block/production.rs b/src/block/production.rs index 65dc9f9..ab91605 100644 --- a/src/block/production.rs +++ b/src/block/production.rs @@ -23,7 +23,7 @@ make_register! { "incinerator" => SimpleBlock::new(1, true, cost!(Lead: 15, Graphite: 5)); "silicon-arc-furnace" => SimpleBlock::new(3, true, cost!(Beryllium: 70, Graphite: 80)); "electrolyzer" => SimpleBlock::new(3, true, cost!(Silicon: 50, Graphite: 40, Beryllium: 130, Tungsten: 80)); - "atmospheric-condenser" => SimpleBlock::new(3, true, cost!(Oxide: 60, Beryllium: 180, Silicon: 150)); + "atmospheric-concentrator" => SimpleBlock::new(3, true, cost!(Oxide: 60, Beryllium: 180, Silicon: 150)); "oxidation-chamber" => SimpleBlock::new(3, true, cost!(Tungsten: 120, Graphite: 80, Silicon: 100, Beryllium: 120)); "electric-heater" => SimpleBlock::new(2, false, cost!(Tungsten: 30, Oxide: 30)); "slag-heater" => SimpleBlock::new(3, false, cost!(Tungsten: 50, Oxide: 20, Beryllium: 20)); diff --git a/src/block/storage.rs b/src/block/storage.rs index 0e34e34..db93403 100644 --- a/src/block/storage.rs +++ b/src/block/storage.rs @@ -13,6 +13,6 @@ make_register! { "container" => SimpleBlock::new(2, true, cost!(Titanium: 100)); "vault" => SimpleBlock::new(3, true, cost!(Titanium: 250, Thorium: 125)); "unloader" => ItemBlock::new(1, true, cost!(Titanium: 25, Silicon: 30)); - "reinforced-container" => SimpleBlock::new(2, true, cost!(Tungsten: 30, Graphite: 40)); - "reinforced-vault" => SimpleBlock::new(3, true, cost!(Tungsten: 125, Thorium: 70, Beryllium: 100)); + "reinforced-container" => SimpleBlock::new(2, true, cost!(Tungsten: 30, Graphite: 40)); + "reinforced-vault" => SimpleBlock::new(3, true, cost!(Tungsten: 125, Thorium: 70, Beryllium: 100)); } diff --git a/src/block/walls.rs b/src/block/walls.rs new file mode 100644 index 0000000..2cf3bbc --- /dev/null +++ b/src/block/walls.rs @@ -0,0 +1,93 @@ +//! walls +use std::any::Any; + +use crate::block::simple::{cost, state_impl, BuildCost, SimpleBlock}; +use crate::block::{ + impl_block, make_register, BlockLogic, DataConvertError, DeserializeError, SerializeError, +}; +use crate::data::dynamic::{DynData, DynType}; +use crate::data::GridPos; +use crate::item::storage::Storage; + +make_register! { + "copper-wall" => SimpleBlock::new(1, true, cost!(Copper: 6)); + "copper-wall-large" => SimpleBlock::new(2, true, cost!(Copper: 6 * 4)); + "titanium-wall" => SimpleBlock::new(1, true, cost!(Titanium: 6)); + "titanium-wall-large" => SimpleBlock::new(2, true, cost!(Titanium: 6 * 4)); + "plastanium-wall" => SimpleBlock::new(1, true, cost!(Metaglass: 2, Plastanium: 5)); + "plastanium-wall-large" => SimpleBlock::new(2, true, cost!(Metaglass: 2 * 4, Plastanium: 5 * 4)); + "thorium-wall" => SimpleBlock::new(1, true, cost!(Thorium: 6)); + "thorium-wall-large" => SimpleBlock::new(2, true, cost!(Thorium: 6 * 4)); + "phase-wall" => SimpleBlock::new(1, true, cost!(PhaseFabric: 6)); + "phase-wall-large" => SimpleBlock::new(2, true, cost!(PhaseFabric: 6 * 4)); + "surge-wall" => SimpleBlock::new(1, true, cost!(SurgeAlloy: 6)); + "surge-wall-large" => SimpleBlock::new(2, true, cost!(SurgeAlloy: 6 * 4)); + "door" => DoorBlock::new(1, true, cost!(Titanium: 6, Silicon: 4)); + "door-large" => DoorBlock::new(2, true, cost!(Titanium: 6 * 4, Silicon: 4 * 4)); + "tungsten-wall" => SimpleBlock::new(1, true, cost!(Tungsten: 6)); + "large-tungsten-wall" => SimpleBlock::new(2, true, cost!(Tungsten: 6 * 4)); + "blast-door" => DoorBlock::new(2, true, cost!(Tungsten: 24, Silicon: 24)); + "reinforced-surge-wall" => SimpleBlock::new(1, true, cost!(SurgeAlloy: 6, Tungsten: 2)); + "reinforced-surge-wall-large" => SimpleBlock::new(2, true, cost!(SurgeAlloy: 6 * 4, Tungsten: 2 * 4)); + "carbide-wall" => SimpleBlock::new(1, true, cost!(Thorium: 6, Carbide: 6)); + "carbide-wall-large" => SimpleBlock::new(2, true, cost!(Thorium: 6 * 4, Carbide: 6 * 4)); + "shielded-wall" => SimpleBlock::new(2, true, cost!(PhaseFabric: 20, SurgeAlloy: 12, Beryllium: 12)); + // sandbox only + "scrap-wall" => SimpleBlock::new(1, true, cost!(Scrap: 6)); + "scrap-wall-large" => SimpleBlock::new(2, true, cost!(Scrap: 24)); + "scrap-wall-huge" => SimpleBlock::new(3, true, cost!(Scrap: 54)); + "scrap-wall-gigantic" => SimpleBlock::new(4, true, cost!(Scrap: 96)); + "thruster" => SimpleBlock::new(4, false, cost!(Scrap: 96)); +} + +pub struct DoorBlock { + size: u8, + symmetric: bool, + build_cost: BuildCost, +} + +impl DoorBlock { + #[must_use] + pub const fn new(size: u8, symmetric: bool, build_cost: BuildCost) -> Self { + assert!(size != 0, "invalid size"); + Self { + size, + symmetric, + build_cost, + } + } + + state_impl!(pub bool); +} + +impl BlockLogic for DoorBlock { + impl_block!(); + + fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> { + Ok(DynData::Boolean(false)) + } + + fn deserialize_state(&self, data: DynData) -> Result<Option<Box<dyn Any>>, DeserializeError> { + match data { + DynData::Boolean(opened) => Ok(Some(Self::create_state(opened))), + _ => Err(DeserializeError::InvalidType { + have: data.get_type(), + expect: DynType::Boolean, + }), + } + } + + fn clone_state(&self, state: &dyn Any) -> Box<dyn Any> { + let state = Self::get_state(state); + Box::new(Self::create_state(*state)) + } + + fn mirror_state(&self, _: &mut dyn Any, _: bool, _: bool) {} + + fn rotate_state(&self, _: &mut dyn Any, _: bool) {} + + fn serialize_state(&self, state: &dyn Any) -> Result<DynData, SerializeError> { + let state = Self::get_state(state); + Ok(DynData::Boolean(*state)) + } +} diff --git a/src/content.rs b/src/content.rs index 51025fb..5576458 100644 --- a/src/content.rs +++ b/src/content.rs @@ -86,6 +86,27 @@ macro_rules! content_enum { } pub(crate) use content_enum; +macro_rules! color_content_enum { + ($vis:vis enum $tname:ident / $ctype:ident for u16 | $error:ident {$($val:literal: $col:literal),* $(,)?}) => + { + paste::paste! { + $crate::content::content_enum!($vis enum $tname / $ctype for u16 | $error { + $($val),*, + }); + + impl Type { + pub fn color(&self) -> image::Rgb<u8> { + match &self { + $(Self::[<$val:camel>] => { + image::Rgb(color_hex::color_from_hex!($col)) + },)* + } + } + } + }} +} +pub(crate) use color_content_enum; + numeric_enum! { pub enum Type for u8 | TryFromU8Error { diff --git a/src/data/renderer.rs b/src/data/renderer.rs index fdd169d..1f0e4d8 100644 --- a/src/data/renderer.rs +++ b/src/data/renderer.rs @@ -10,9 +10,13 @@ use zip::ZipArchive; use super::schematic::Schematic; pub(crate) fn load(category: &str, name: &str) -> Option<RgbaImage> { - let mut p = Path::new("target/out/blocks").join(category).join(name); + let mut p = Path::new("blocks").join(category).join(name); p.set_extension("png"); - let f = std::fs::File::open(p).ok()?; + load_raw(p) +} + +pub(crate) fn load_raw(f: impl AsRef<Path>) -> Option<RgbaImage> { + let f = std::fs::File::open(Path::new("target/out").join(f)).ok()?; let r = PngDecoder::new(BufReader::new(f)).unwrap(); Some(DynamicImage::from_decoder(r).unwrap().into_rgba8()) } @@ -27,8 +31,8 @@ fn load_zip() { } } -const SUFFIXES: &[&str; 8] = &[ - "bottom", "mid", "", "-base", "-left", "-right", "-top", "-over", +const SUFFIXES: &[&str; 10] = &[ + "-bottom", "-mid", "", "-base", "-left", "-right", "-rotator", "-weave", "-top", "-over", ]; pub(crate) fn read<S>(category: &str, name: &str, size: S) -> RgbaImage where @@ -46,26 +50,23 @@ where /// renderer for creating images of schematics pub struct Renderer {} impl<'l> Renderer { - /// creates a picture of a schematic. Bridges and nodes are not drawn, and there is no background. + /// creates a picture of a schematic. Bridges and node connections are not drawn, and there is no background. /// conveyors, conduits, and ducts currently do not render. /// ``` /// use mindus::*; - /// let s = Schematic::new(2, 3); - /// s.put(0, 0, blocks::distribution::DISTRIBUTOR); - /// s.put(0, 3, blocks::distrubution::ROUTER); - /// s.put(1, 3, blocks::defense::COPPER_WALL); + /// let mut s = Schematic::new(2, 3); + /// s.put(0, 0, &block::distribution::DISTRIBUTOR); + /// s.put(0, 3, &block::distribution::ROUTER); + /// s.put(1, 3, &block::walls::COPPER_WALL); /// let output /*: RgbaImage */ = Renderer::render(&s); /// ``` pub fn render(s: &'l Schematic<'_>) -> RgbaImage { load_zip(); let mut canvas = RgbaImage::new((s.width * 32).into(), (s.height * 32).into()); for tile in s.block_iter() { - let mut x = tile.pos.0 as i64; - let mut y = tile.pos.1 as i64; - if tile.block.get_size() != 1 && tile.block.get_size() % 2 != 0 { - x -= 1; - y -= 1; - } + let sub = ((tile.block.get_size() - 1) / 2) as u16; + let x = (tile.pos.0 - sub) as i64; + let y = (tile.pos.1 - sub) as i64; overlay(&mut canvas, &tile.image(), x * 32, y * 32); } canvas diff --git a/src/fluid/mod.rs b/src/fluid/mod.rs index f4d9395..45254b9 100644 --- a/src/fluid/mod.rs +++ b/src/fluid/mod.rs @@ -1,18 +1,18 @@ -use crate::content::content_enum; +use crate::content::color_content_enum; -content_enum! { +color_content_enum! { pub enum Type / Fluid for u16 | TryFromU16Error { - "water", - "slag", - "oil", - "cryofluid", - "neoplasm", - "arkycite", - "gallium", - "ozone", - "hydrogen", - "nitrogen", - "cyanogen", + "water": "596ab8", + "slag": "ffa166", + "oil": "313131", + "cryofluid": "6ecdec", + "neoplasm": "c33e2b", + "arkycite": "84a94b", + "gallium": "9a9dbf", + "ozone": "fc81dd", + "hydrogen": "9eabf7", + "nitrogen": "efe3ff", + "cyanogen": "89e8b6", } } diff --git a/src/item/mod.rs b/src/item/mod.rs index 6a4f8fc..863d3b0 100644 --- a/src/item/mod.rs +++ b/src/item/mod.rs @@ -1,49 +1,29 @@ //! the different kinds of items -use image::Rgb; - pub mod storage; - -macro_rules! item_enum { - ($($val:literal: $col:literal),* $(,)?) => - { - paste::paste! { - $crate::content::content_enum!(pub enum Type / Item for u16 | TryFromU16Error { - $($val),*, - }); - - impl Type { - pub fn color(&self) -> Rgb<u8> { - match &self { - $(Self::[<$val:camel>] => { - Rgb(color_hex::color_from_hex!($col)) - },)* - } - } - } - }} -} - -item_enum! { - "copper": "d99d73", - "lead": "8c7fa9", - "metaglass": "ebeef5", - "graphite": "b2c6d2", - "sand": "f7cba4", - "coal": "272727", - "titanium": "8da1e3", - "thorium": "f9a3c7", - "scrap": "777777", - "silicon": "53565c", - "plastanium": "cbd97f", - "phase-fabric": "f4ba6e", - "surge-alloy": "f3e979", - "spore-pod": "7457ce", - "blast-compound": "ff795e", - "pyratite": "ffaa5f", - "beryllium": "3a8f64", - "tungsten": "768a9a", - "oxide": "e4ffd6", - "carbide": "89769a", - "fissile-matter": "5e988d", - "dormant-cyst": "df824d", +use crate::content::color_content_enum; +color_content_enum! { + pub enum Type / Item for u16 | TryFromU16Error { + "copper": "d99d73", + "lead": "8c7fa9", + "metaglass": "ebeef5", + "graphite": "b2c6d2", + "sand": "f7cba4", + "coal": "272727", + "titanium": "8da1e3", + "thorium": "f9a3c7", + "scrap": "777777", + "silicon": "53565c", + "plastanium": "cbd97f", + "phase-fabric": "f4ba6e", + "surge-alloy": "f3e979", + "spore-pod": "7457ce", + "blast-compound": "ff795e", + "pyratite": "ffaa5f", + "beryllium": "3a8f64", + "tungsten": "768a9a", + "oxide": "e4ffd6", + "carbide": "89769a", + "fissile-matter": "5e988d", + "dormant-cyst": "df824d", + } } @@ -10,6 +10,7 @@ mod modifier; mod registry; mod team; mod unit; +mod utils; pub use block::build_registry; pub use data::dynamic::DynData; pub use data::renderer::Renderer; diff --git a/src/utils/image.rs b/src/utils/image.rs new file mode 100644 index 0000000..50a4cf7 --- /dev/null +++ b/src/utils/image.rs @@ -0,0 +1,14 @@ +use image::{Rgb, Rgba, RgbaImage}; + +pub fn tint(image: &mut RgbaImage, color: Rgb<u8>) { + let [tr, tg, tb] = [ + color[0] as f32 / 255.0, + color[1] as f32 / 255.0, + color[2] as f32 / 255.0, + ]; + for Rgba([r, g, b, _]) in image.pixels_mut() { + *r = (*r as f32 * tr) as u8; + *g = (*g as f32 * tg) as u8; + *b = (*b as f32 * tb) as u8; + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..14995d4 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1 @@ +pub mod image; |