mindustry logic execution, map- and schematic- parsing and rendering
deserialize unit factory config
along with a few other fixes
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | src/block/defense.rs | 15 | ||||
| -rw-r--r-- | src/block/distribution.rs | 8 | ||||
| -rw-r--r-- | src/block/logic.rs | 2 | ||||
| -rw-r--r-- | src/block/mod.rs | 6 | ||||
| -rw-r--r-- | src/block/payload.rs | 8 | ||||
| -rw-r--r-- | src/block/power.rs | 8 | ||||
| -rw-r--r-- | src/block/production.rs | 15 | ||||
| -rw-r--r-- | src/block/units.rs | 241 | ||||
| -rw-r--r-- | src/data/command.rs | 5 | ||||
| -rw-r--r-- | src/data/dynamic.rs | 142 | ||||
| -rw-r--r-- | src/data/map.rs | 4 | ||||
| -rw-r--r-- | src/data/mod.rs | 2 | ||||
| -rw-r--r-- | src/data/renderer.rs | 1 | ||||
| -rw-r--r-- | src/data/schematic.rs | 2 |
15 files changed, 282 insertions, 179 deletions
@@ -1,6 +1,6 @@ [package] name = "mindus" -version = "3.0.5" +version = "3.0.6" edition = "2021" description = "A library for working with mindustry data formats (eg schematics and maps) (fork of plandustry)" authors = [ diff --git a/src/block/defense.rs b/src/block/defense.rs index 0dae9aa..b1548bc 100644 --- a/src/block/defense.rs +++ b/src/block/defense.rs @@ -2,6 +2,8 @@ use crate::block::simple::{cost, make_simple, BasicBlock}; use crate::block::*; make_simple!(HeatedBlock => |_, _, _, buff: &mut DataRead| read_heated(buff)); +make_simple!(RadarBlock => |_, _, _, buff: &mut DataRead| buff.skip(4)); +make_simple!(ShieldBlock => |_, _, _, buff: &mut DataRead| read_shield(buff)); make_register! { "mender" -> HeatedBlock::new(1, true, cost!(Copper: 25, Lead: 30)); "mend-projector" -> HeatedBlock::new(2, true, cost!(Copper: 50, Lead: 100, Titanium: 25, Silicon: 40)); @@ -10,14 +12,14 @@ make_register! { "force-projector" -> BasicBlock::new(3, true, cost!(Lead: 100, Titanium: 75, Silicon: 125)); "regen-projector" -> BasicBlock::new(3, true, cost!(Silicon: 80, Tungsten: 60, Oxide: 40, Beryllium: 80)); "shock-mine" -> BasicBlock::new(1, true, cost!(Lead: 25, Silicon: 12)); - "radar" -> BasicBlock::new(1, true, cost!(Silicon: 60, Graphite: 50, Beryllium: 10)); + "radar" -> RadarBlock::new(1, true, cost!(Silicon: 60, Graphite: 50, Beryllium: 10)); "build-tower" -> BasicBlock::new(3, true, cost!(Silicon: 150, Oxide: 40, Thorium: 60)); "shockwave-tower" -> BasicBlock::new(3, true, cost!(SurgeAlloy: 50, Silicon: 150, Oxide: 30, Tungsten: 100)); // barrier projector // editor only "barrier-projector" -> BasicBlock::new(3, true, &[]); - "shield-projector" -> BasicBlock::new(3, true, &[]); - "large-shield-projector" -> BasicBlock::new(4, true, &[]); + "shield-projector" -> ShieldBlock::new(3, true, &[]); + "large-shield-projector" -> ShieldBlock::new(4, true, &[]); } /// format: @@ -26,3 +28,10 @@ make_register! { fn read_heated(buff: &mut DataRead) -> Result<(), DataReadError> { buff.skip(8) } + +/// format: +/// - smoothing: [`f32`] +/// - broken: [`bool`] +fn read_shield(buff: &mut DataRead) -> Result<(), DataReadError> { + buff.skip(5) +} diff --git a/src/block/distribution.rs b/src/block/distribution.rs index 413f903..46e0f2c 100644 --- a/src/block/distribution.rs +++ b/src/block/distribution.rs @@ -214,11 +214,9 @@ impl BlockLogic for ItemBlock { fn rotate_state(&self, _: &mut State, _: bool) {} fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> { - Ok(Self::get_state(state) - .as_ref() - .map_or(DynData::Empty, |&item| { - DynData::Content(content::Type::Item, item.into()) - })) + Ok(Self::get_state(state).map_or(DynData::Empty, |item| { + DynData::Content(content::Type::Item, item.into()) + })) } fn draw( diff --git a/src/block/logic.rs b/src/block/logic.rs index 4179b89..c0cb727 100644 --- a/src/block/logic.rs +++ b/src/block/logic.rs @@ -562,7 +562,7 @@ impl ProcessorLink { } #[must_use] - pub fn get_pos(&self) -> (i16, i16) { + pub const fn get_pos(&self) -> (i16, i16) { (self.x, self.y) } } diff --git a/src/block/mod.rs b/src/block/mod.rs index 55bb35e..822de05 100644 --- a/src/block/mod.rs +++ b/src/block/mod.rs @@ -69,6 +69,7 @@ disp! { BridgeBlock, ItemBlock, ProductionBlock, + SeparatorBlock, StackConveyor, HeatCrafter, ConnectorBlock, @@ -80,6 +81,8 @@ disp! { GeneratorBlock, ConduitBlock, HeatedBlock, + RadarBlock, + ShieldBlock, PointDefenseTurret, JunctionBlock, Turret, @@ -87,6 +90,7 @@ disp! { MessageLogic, ConstructorBlock, AssemblerBlock, + UnitFactory, SimpleDuctBlock, SurgeRouter, SimplePayloadBlock, @@ -266,7 +270,7 @@ impl Block { /// assert!(mindus::block::distribution::DISTRIBUTOR.name() == "distributor") /// ``` #[must_use] - pub fn name(&self) -> &'static str { + pub const fn name(&self) -> &'static str { self.name } diff --git a/src/block/payload.rs b/src/block/payload.rs index 070efe0..54aaa6f 100644 --- a/src/block/payload.rs +++ b/src/block/payload.rs @@ -208,6 +208,14 @@ fn read_payload_conveyor( } /// format: +/// - iterate [`i16`]..0 +/// - [`u8`], [`i16`], [`i32`] +pub(crate) fn read_payload_seq(buff: &mut DataRead) -> Result<(), DataReadError> { + let amount = (-buff.read_i16()?) as usize; + buff.skip(amount * 7) +} + +/// format: /// - vector: ([`f32`], [`f32`]) /// - rotation: [`f32`] /// - become [`read_payload`] diff --git a/src/block/power.rs b/src/block/power.rs index ada60c9..2e47956 100644 --- a/src/block/power.rs +++ b/src/block/power.rs @@ -66,7 +66,7 @@ pub struct ConnectorBlock { size: u8, symmetric: bool, build_cost: BuildCost, - max: u8, + pub max: u8, } impl ConnectorBlock { @@ -84,12 +84,6 @@ impl ConnectorBlock { max, } } - - #[must_use] - pub fn get_max_links(&self) -> u8 { - self.max - } - state_impl!(pub Vec<(i16, i16)>); } diff --git a/src/block/production.rs b/src/block/production.rs index c8d435f..17e758b 100644 --- a/src/block/production.rs +++ b/src/block/production.rs @@ -17,8 +17,8 @@ make_register! { "pyratite-mixer" -> ProductionBlock::new(2, true, cost!(Copper: 50, Lead: 25)); "blast-mixer" -> ProductionBlock::new(2, true, cost!(Lead: 30, Thorium: 20)); "melter" -> ProductionBlock::new(1, true, cost!(Copper: 30, Lead: 35, Graphite: 45)); - "separator" -> ProductionBlock::new(2, true, cost!(Copper: 30, Titanium: 25)); - "disassembler" -> ProductionBlock::new(3, true, cost!(Titanium: 100, Thorium: 80, Silicon: 150, Plastanium: 40)); + "separator" -> SeparatorBlock::new(2, true, cost!(Copper: 30, Titanium: 25)); + "disassembler" -> SeparatorBlock::new(3, true, cost!(Titanium: 100, Thorium: 80, Silicon: 150, Plastanium: 40)); "spore-press" -> ProductionBlock::new(2, true, cost!(Lead: 35, Silicon: 30)); "pulverizer" -> ProductionBlock::new(1, true, cost!(Copper: 30, Lead: 25)); "coal-centrifuge" -> ProductionBlock::new(2, true, cost!(Lead: 30, Graphite: 40, Titanium: 20)); @@ -43,14 +43,19 @@ make_register! { "heat-source" -> HeatCrafter::new(1, false, &[]); } +// format: call [`read_production_block`], seed: [`i32`] +make_simple!(SeparatorBlock => |_, _, _, buff: &mut DataRead| buff.skip(12)); + make_simple!( ProductionBlock => - |_, _, _, buff: &mut DataRead| { + |b: &mut Build<'_>, _, _, buff: &mut DataRead| { // format: // - progress: `f32` // - warmup: `f32` - buff.skip(8)?; - Ok(()) + // (cultivator) + // `f32` + buff.skip(8 + if b.name() == "cultivator" { 4 } else { 0 }) + } ); diff --git a/src/block/units.rs b/src/block/units.rs index 708902d..c3def54 100644 --- a/src/block/units.rs +++ b/src/block/units.rs @@ -1,10 +1,12 @@ //! unit creation related blocks use thiserror::Error; -use super::payload::read_payload_block; +use super::payload::{read_payload_block, read_payload_seq}; use crate::block::simple::*; use crate::block::*; -use crate::data::dynamic::DynType; +use crate::data::command::UnitCommand; +use crate::data::dynamic::{DynType, DynSerializer}; +use crate::data::Serializer; use crate::unit; // fn is_pay(b: &str) -> bool { @@ -33,75 +35,36 @@ use crate::unit; // ) // } -make_simple!(ConstructorBlock, |_, name, _, _, rot: Rotation, s| { - let mut base = load!(from name which is ["additive-reconstructor" | "multiplicative-reconstructor" | "exponential-reconstructor" | "tetrative-reconstructor" | "tank-refabricator" | "mech-refabricator" | "ship-refabricator" | "prime-refabricator" | "tank-assembler" | "ship-assembler" | "mech-assembler"], s); - let times = rot.rotated(false).count(); - if !name.contains("assembler") { - let mut out = load!(s -> match name { - "additive-reconstructor" => "factory-out-3", - "multiplicative-reconstructor" => "factory-out-5", - "tank-refabricator" | "mech-refabricator" | "ship-refabricator" => - "factory-out-3-dark", - "exponential-reconstructor" => "factory-out-7", - "prime-refabricator" | "tank-assembler" | "ship-assembler" | "mech-assembler" => - "factory-out-5-dark", - "tetrative-reconstructor" => "factory-out-9", - }); - base.overlay(out.rotate(times)); +make_simple!(AssemblerBlock, |_, name, _, _, rot: Rotation, s| { + let mut base = + load!(from name which is ["tank-assembler" | "ship-assembler" | "mech-assembler"], s); + base.overlay( + match rot { + Rotation::Up | Rotation::Right => load!(concat side1 => name which is ["tank-assembler" | "ship-assembler" | "mech-assembler"], s), + Rotation::Down | Rotation::Left => load!(concat side2 => name which is ["tank-assembler" | "ship-assembler" | "mech-assembler"], s) + } + .rotate(rot.rotated(false).count()), + ); + base.overlay(load!(concat top => name which is ["tank-assembler" | "ship-assembler" | "mech-assembler"], s).borrow()); + base +}, |_, reg, map, buff| read_assembler(reg, map, buff)); - let mut r#in = load!(s -> match name { - "additive-reconstructor" => "factory-in-3", - "multiplicative-reconstructor" => "factory-in-5", - "tank-refabricator" | "mech-refabricator" | "ship-refabricator" => - "factory-in-3-dark", - "exponential-reconstructor" => "factory-in-7", - "prime-refabricator" | "tank-assembler" | "ship-assembler" | "mech-assembler" => - "factory-in-5-dark", - "tetrative-reconstructor" => "factory-in-9", - }); - base.overlay(r#in.rotate(times)); - } +/// format: +/// - call [`read_payload_block`] +/// - progress: [`f32`] +/// - iterate [`u8`] +/// - read: [`i32`] +/// - call [`read_payload_seq`] +/// - point: ([`f32`], [`f32`]) (maybe [`NaN`](f32::NAN)) +fn read_assembler(reg: &BlockRegistry, map: &EntityMapping, buff: &mut DataRead) -> Result<(), DataReadError> { + read_payload_block(reg, map, buff)?; + buff.skip(4)?; + let n = buff.read_u8()? as usize; + buff.skip(n * 4)?; + read_payload_seq(buff)?; + buff.skip(8) +} - // TODO: the context cross is too small - // for i in 0..4u8 { - // if let Some((b, rot)) = dbg!(ctx.cross[i as usize]) { - // if rot.mirrored(true, true) != ctx.rotation && match rot { - // Rotation::Up => i == 3, - // Rotation::Right => i == 4, - // Rotation::Down => i == 0, - // Rotation::Left => i == 2, - // } && is_pay(b.name()) - // { - // let r = unsafe { std::mem::transmute::<u8, Rotation>(i) } - // .mirrored(true, true) - // .rotated(false); - // let mut input = input.clone(); - // input.rotate(r.count()); - // base.overlay(&input); - // } - // } - // } - - base.overlay(load!(concat top => name which is ["additive-reconstructor" | "multiplicative-reconstructor" | "exponential-reconstructor" | "tetrative-reconstructor" | "tank-refabricator" | "mech-refabricator" | "ship-refabricator" | "prime-refabricator" | "tank-assembler" | "ship-assembler" | "mech-assembler"], s).borrow()); - if matches!(name, "mech-assembler" | "tank-assembler" | "ship-assembler") { - base.overlay( - match rot { - Rotation::Up | Rotation::Right => load!(s -> match name { - "mech-assembler" => "mech-assembler-side1", - "tank-assembler" => "tank-assembler-side1", - "ship-assembler" => "ship-assembler-side1", - }), - _ => load!(s -> match name { - "mech-assembler" => "mech-assembler-side2", - "tank-assembler" => "tank-assembler-side2", - "ship-assembler" => "ship-assembler-side2", - }), - } - .rotate(times), - ); - } - base -}); make_simple!(AssemblerModule, |_, _, _, _, rot: Rotation, scl| { let mut base = load!("basic-assembler-module", scl); base.overlay( @@ -112,7 +75,8 @@ make_simple!(AssemblerModule, |_, _, _, _, rot: Rotation, scl| { .rotate(rot.rotated(false).count()), ); base -}); +}, |_, reg, map, buff| read_payload_block(reg, map, buff)); + make_simple!( RepairTurret => |scl| { let mut bot = load!("block-2", scl); @@ -130,9 +94,9 @@ const AIR_UNITS: &[unit::Type] = &[unit::Type::Flare, unit::Type::Mono]; const NAVAL_UNITS: &[unit::Type] = &[unit::Type::Risso, unit::Type::Retusa]; make_register! { - "ground-factory" => AssemblerBlock::new(3, false, cost!(Copper: 50, Lead: 120, Silicon: 80), GROUND_UNITS); - "air-factory" => AssemblerBlock::new(3, false, cost!(Copper: 60, Lead: 70), AIR_UNITS); - "naval-factory" => AssemblerBlock::new(3, false, cost!(Copper: 150, Lead: 130, Metaglass: 120), NAVAL_UNITS); + "ground-factory" => UnitFactory::new(3, false, cost!(Copper: 50, Lead: 120, Silicon: 80), GROUND_UNITS); + "air-factory" => UnitFactory::new(3, false, cost!(Copper: 60, Lead: 70), AIR_UNITS); + "naval-factory" => UnitFactory::new(3, false, cost!(Copper: 150, Lead: 130, Metaglass: 120), NAVAL_UNITS); "additive-reconstructor" => ConstructorBlock::new(3, false, cost!(Copper: 200, Lead: 120, Silicon: 90)); "multiplicative-reconstructor" => ConstructorBlock::new(5, false, cost!(Lead: 650, Titanium: 350, Thorium: 650, Silicon: 450)); "exponential-reconstructor" => ConstructorBlock::new(7, false, @@ -141,29 +105,142 @@ make_register! { cost!(Lead: 4000, Thorium: 1000, Silicon: 3000, Plastanium: 600, PhaseFabric: 600, SurgeAlloy: 800)); "repair-point" => RepairTurret::new(1, true, cost!(Copper: 30, Lead: 30, Silicon: 20)); "repair-turret" => RepairTurret::new(2, true, cost!(Thorium: 80, Silicon: 90, Plastanium: 60)); - "tank-fabricator" => AssemblerBlock::new(3, true, cost!(Silicon: 200, Beryllium: 150), &[unit::Type::Stell]); - "ship-fabricator" => AssemblerBlock::new(3, true, cost!(Silicon: 250, Beryllium: 200), &[unit::Type::Elude]); - "mech-fabricator" => AssemblerBlock::new(3, true, cost!(Silicon: 200, Graphite: 300, Tungsten: 60), &[unit::Type::Merui]); + "tank-fabricator" => UnitFactory::new(3, true, cost!(Silicon: 200, Beryllium: 150), &[unit::Type::Stell]); + "ship-fabricator" => UnitFactory::new(3, true, cost!(Silicon: 250, Beryllium: 200), &[unit::Type::Elude]); + "mech-fabricator" => UnitFactory::new(3, true, cost!(Silicon: 200, Graphite: 300, Tungsten: 60), &[unit::Type::Merui]); "tank-refabricator" => ConstructorBlock::new(3, true, cost!(Beryllium: 200, Tungsten: 80, Silicon: 100)); "mech-refabricator" => ConstructorBlock::new(3, true, cost!(Beryllium: 250, Tungsten: 120, Silicon: 150)); "ship-refabricator" => ConstructorBlock::new(3, true, cost!(Beryllium: 200, Tungsten: 100, Silicon: 150, Oxide: 40)); "prime-refabricator" => ConstructorBlock::new(5, true, cost!(Thorium: 250, Oxide: 200, Tungsten: 200, Silicon: 400)); - "tank-assembler" => ConstructorBlock::new(5, true, cost!(Thorium: 500, Oxide: 150, Carbide: 80, Silicon: 500)); - "ship-assembler" => ConstructorBlock::new(5, true, cost!(Carbide: 100, Oxide: 200, Tungsten: 500, Silicon: 800, Thorium: 400)); - "mech-assembler" => ConstructorBlock::new(5, true, cost!(Carbide: 200, Thorium: 600, Oxide: 200, Tungsten: 500, Silicon: 900)); // smh collaris + "tank-assembler" => AssemblerBlock::new(5, true, cost!(Thorium: 500, Oxide: 150, Carbide: 80, Silicon: 500)); + "ship-assembler" => AssemblerBlock::new(5, true, cost!(Carbide: 100, Oxide: 200, Tungsten: 500, Silicon: 800, Thorium: 400)); + "mech-assembler" => AssemblerBlock::new(5, true, cost!(Carbide: 200, Thorium: 600, Oxide: 200, Tungsten: 500, Silicon: 900)); // smh collaris "basic-assembler-module" => AssemblerModule::new(5, true, cost!(Carbide: 300, Thorium: 500, Oxide: 200, PhaseFabric: 400)); // the dummy block "unit-repair-tower" -> BasicBlock::new(2, true, cost!(Graphite: 90, Silicon: 90, Tungsten: 80)); } -pub struct AssemblerBlock { +pub struct ConstructorBlock { + size: u8, + symmetric: bool, + build_cost: BuildCost, +} + +impl ConstructorBlock { + #[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 Option<UnitCommand>); +} + +impl BlockLogic for ConstructorBlock { + impl_block!(); + + fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> { + Ok(DynData::Empty) + } + + fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError> { + match data { + DynData::Empty => Ok(Some(Self::create_state(None))), + DynData::UnitCommand(u) => Ok(Some(Self::create_state(Some(u)))), + _ => Err(DeserializeError::InvalidType { have: data.get_type(), expect: DynType::UnitCommand }) + } + } + + fn clone_state(&self, state: &State) -> State { + Box::new(*Self::get_state(state)) + } + + fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> { + Ok(Self::get_state(state).map_or(DynData::Empty, DynData::UnitCommand)) + } + + fn draw( + &self, + name: &str, + _: Option<&State>, + _: Option<&RenderingContext>, + rot: Rotation, + s: Scale, + ) -> ImageHolder { + let mut base = load!(from name which is ["additive-reconstructor" | "multiplicative-reconstructor" | "exponential-reconstructor" | "tetrative-reconstructor" | "tank-refabricator" | "mech-refabricator" | "ship-refabricator" | "prime-refabricator"], s); + let times = rot.rotated(false).count(); + let mut out = load!(s -> match name { + "additive-reconstructor" => "factory-out-3", + "multiplicative-reconstructor" => "factory-out-5", + "tank-refabricator" | "mech-refabricator" | "ship-refabricator" => + "factory-out-3-dark", + "exponential-reconstructor" => "factory-out-7", + "prime-refabricator" => "factory-out-5-dark", + "tetrative-reconstructor" => "factory-out-9", + }); + base.overlay(out.rotate(times)); + + let mut r#in = load!(s -> match name { + "additive-reconstructor" => "factory-in-3", + "multiplicative-reconstructor" => "factory-in-5", + "tank-refabricator" | "mech-refabricator" | "ship-refabricator" => + "factory-in-3-dark", + "exponential-reconstructor" => "factory-in-7", + "prime-refabricator" => "factory-in-5-dark", + "tetrative-reconstructor" => "factory-in-9", + }); + base.overlay(r#in.rotate(times)); + + // TODO: the context cross is too small + // for i in 0..4u8 { + // if let Some((b, rot)) = dbg!(ctx.cross[i as usize]) { + // if rot.mirrored(true, true) != ctx.rotation && match rot { + // Rotation::Up => i == 3, + // Rotation::Right => i == 4, + // Rotation::Down => i == 0, + // Rotation::Left => i == 2, + // } && is_pay(b.name()) + // { + // let r = unsafe { std::mem::transmute::<u8, Rotation>(i) } + // .mirrored(true, true) + // .rotated(false); + // let mut input = input.clone(); + // input.rotate(r.count()); + // base.overlay(&input); + // } + // } + // } + + base.overlay(load!(concat top => name which is ["additive-reconstructor" | "multiplicative-reconstructor" | "exponential-reconstructor" | "tetrative-reconstructor" | "tank-refabricator" | "mech-refabricator" | "ship-refabricator" | "prime-refabricator"], s).borrow()); + base + } + + /// format: + /// - call [`read_payload_block`] + /// - progress: [`f32`] + /// - point: ([`f32`], [`f32`]) (maybe [`NaN`](f32::NAN)) + /// - command: [`DynData::UnitCommand`] + fn read(&self,b: &mut Build,reg: &BlockRegistry,map: &EntityMapping,buff: &mut DataRead,) -> Result<(),DataReadError> { + read_payload_block(reg,map, buff)?; + buff.skip(12)?; + // TODO handlerr + b.state = self.deserialize_state(DynSerializer.deserialize(buff).unwrap()).unwrap(); + Ok(()) + } +} + +pub struct UnitFactory { size: u8, symmetric: bool, build_cost: BuildCost, valid: &'static [unit::Type], } -impl AssemblerBlock { +impl UnitFactory { #[must_use] pub const fn new( size: u8, @@ -185,7 +262,7 @@ impl AssemblerBlock { state_impl!(pub Option<unit::Type>); } -impl BlockLogic for AssemblerBlock { +impl BlockLogic for UnitFactory { impl_block!(); fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> { diff --git a/src/data/command.rs b/src/data/command.rs index ba146ff..7d5298c 100644 --- a/src/data/command.rs +++ b/src/data/command.rs @@ -1,8 +1,7 @@ use crate::content::numeric_enum; numeric_enum! { - pub enum UnitCommand for u8 | TryFromU8Error - { - Attack, Rally, Idle + pub enum UnitCommand for u8 | TryFromU8Error { + Move, Repair, Rebuild, Assist, Mine, Boost, } } diff --git a/src/data/dynamic.rs b/src/data/dynamic.rs index 877a612..344144b 100644 --- a/src/data/dynamic.rs +++ b/src/data/dynamic.rs @@ -7,19 +7,39 @@ use crate::data::{self, DataRead, DataWrite, GridPos, Serializer}; use crate::logic::LogicField; use crate::team::Team; -#[derive(Clone, Debug, PartialEq)] -/// holds different kinds of data -pub enum DynData { - Empty, +macro_rules! datamaker { + ( + $($k: ident($v: ty),)+ + ) => { paste::paste! { + #[derive(Clone, Debug, PartialEq)] + /// holds different kinds of data + pub enum DynData { + Empty, + Content(content::Type, u16), + Point2(i32, i32), + Vec2(f32, f32), + TechNode(content::Type, u16), + $($k($v),)+ + } + + $( + impl From<$v> for DynData { + #[doc = concat!(" to [`DynData::", stringify!($k), "`]")] + fn from(f: $v) -> Self { + Self::$k(f) + } + } + )+ + } } +} + +datamaker! { Int(i32), Long(i64), Float(f32), String(Option<String>), - Content(content::Type, u16), IntArray(Vec<i32>), - Point2(i32, i32), Point2Array(Vec<(i16, i16)>), - TechNode(content::Type, u16), Boolean(bool), Double(f64), Building(GridPos), @@ -29,13 +49,12 @@ pub enum DynData { BoolArray(Vec<bool>), Unit(u32), Vec2Array(Vec<(f32, f32)>), - Vec2(f32, f32), Team(Team), } impl DynData { #[must_use] - pub fn get_type(&self) -> DynType { + pub const fn get_type(&self) -> DynType { match self { Self::Empty => DynType::Empty, Self::Int(..) => DynType::Int, @@ -96,14 +115,14 @@ impl Serializer<DynData> for DynSerializer { fn deserialize(&mut self, buff: &mut DataRead<'_>) -> Result<DynData, Self::ReadError> { match buff.read_u8()? { 0 => Ok(DynData::Empty), - 1 => Ok(DynData::Int(buff.read_i32()?)), - 2 => Ok(DynData::Long(buff.read_i64()?)), - 3 => Ok(DynData::Float(buff.read_f32()?)), + 1 => Ok(DynData::from(buff.read_i32()?)), + 2 => Ok(DynData::from(buff.read_i64()?)), + 3 => Ok(DynData::from(buff.read_f32()?)), 4 => { if buff.read_bool()? { - Ok(DynData::String(Some(String::from(buff.read_utf()?)))) + Ok(DynData::from(Some(String::from(buff.read_utf()?)))) } else { - Ok(DynData::String(None)) + Ok(DynData::from(None)) } } 5 => Ok(DynData::Content( @@ -112,87 +131,74 @@ impl Serializer<DynData> for DynSerializer { )), 6 => { let len = buff.read_i16()?; - if len < 0 { + let Ok(len) = usize::try_from(len) else { return Err(ReadError::IntArrayLen(len)); + }; + let mut result = vec![0; len]; + for item in result.iter_mut() { + *item = buff.read_i32()?; } - let mut result = Vec::<i32>::new(); - result.reserve(len as usize); - for _ in 0..len { - result.push(buff.read_i32()?); - } - Ok(DynData::IntArray(result)) + Ok(DynData::from(result)) } 7 => Ok(DynData::Point2(buff.read_i32()?, buff.read_i32()?)), 8 => { let len = buff.read_i8()?; - if len < 0 { + let Ok(len) = usize::try_from(len) else { return Err(ReadError::Point2ArrayLen(len)); - } - let mut result = Vec::<(i16, i16)>::new(); - result.reserve(len as usize); - for _ in 0..len { + }; + let mut result = vec![(0, 0); len]; + for item in result.iter_mut() { let pt = buff.read_i32()?; - result.push(((pt >> 16) as i16, pt as i16)); + *item = ((pt >> 16) as i16, pt as i16); } - Ok(DynData::Point2Array(result)) + Ok(DynData::from(result)) } 9 => Ok(DynData::TechNode( content::Type::try_from(buff.read_u8()?)?, buff.read_u16()?, )), - 10 => Ok(DynData::Boolean(buff.read_bool()?)), - 11 => Ok(DynData::Double(buff.read_f64()?)), - 12 => Ok(DynData::Building(GridPos::from(buff.read_u32()?))), - 13 => { - let id = buff.read_u8()?; - match LogicField::try_from(id) { - Ok(f) => Ok(DynData::LogicField(f)), - Err(..) => Err(ReadError::LogicField(id)), - } - } + 10 => Ok(DynData::from(buff.read_bool()?)), + 11 => Ok(DynData::from(buff.read_f64()?)), + 12 => Ok(DynData::from(GridPos::from(buff.read_u32()?))), + 13 => Ok(DynData::from(LogicField::try_from(buff.read_u8()?)?)), 14 => { let len = buff.read_i32()?; - if len < 0 { + let Ok(len) = usize::try_from(len) else { return Err(ReadError::ByteArrayLen(len)); - } - let mut result = Vec::<u8>::new(); - buff.read_vec(&mut result, len as usize)?; - Ok(DynData::ByteArray(result)) - } - 15 => { - let id = buff.read_u8()?; - match UnitCommand::try_from(id) { - Ok(f) => Ok(DynData::UnitCommand(f)), - Err(e) => Err(ReadError::UnitCommand(e)), - } + }; + let mut result = vec![]; + buff.read_vec(&mut result, len)?; + Ok(DynData::from(result)) } 16 => { let len = buff.read_i32()?; - if len < 0 { + let Ok(len) = usize::try_from(len) else { return Err(ReadError::BoolArrayLen(len)); - } - let mut result = Vec::<bool>::new(); - result.reserve(len as usize); + }; + let mut result = vec![]; + result.reserve(len); for _ in 0..len { result.push(buff.read_bool()?); } - Ok(DynData::BoolArray(result)) + Ok(DynData::from(result)) } 17 => Ok(DynData::Unit(buff.read_u32()?)), 18 => { let len = buff.read_i16()?; - if len < 0 { + let Ok(len) = usize::try_from(len) else { return Err(ReadError::Vec2ArrayLen(len)); + }; + let mut result = vec![(0., 0.); len]; + for item in result.iter_mut() { + *item = (buff.read_f32()?, buff.read_f32()?); } - let mut result = Vec::<(f32, f32)>::new(); - result.reserve(len as usize); - for _ in 0..len { - result.push((buff.read_f32()?, buff.read_f32()?)); - } - Ok(DynData::Vec2Array(result)) + Ok(DynData::from(result)) } 19 => Ok(DynData::Vec2(buff.read_f32()?, buff.read_f32()?)), - 20 => Ok(DynData::Team(Team::of(buff.read_u8()?))), + 20 => Ok(DynData::from(Team::of(buff.read_u8()?))), + 23 => Ok(DynData::from( + UnitCommand::try_from(buff.read_i16()? as u8)?, + )), id => Err(ReadError::Type(id)), } } @@ -303,8 +309,8 @@ impl Serializer<DynData> for DynSerializer { Ok(()) } DynData::UnitCommand(cmd) => { - buff.write_u8(15)?; - buff.write_u8(u8::from(*cmd))?; + buff.write_u8(23)?; + buff.write_u16(u8::from(*cmd).into())?; Ok(()) } DynData::BoolArray(arr) => { @@ -363,7 +369,7 @@ pub enum ReadError { #[error("point2 array too long ({0})")] Point2ArrayLen(i8), #[error("invalid logic field ({0})")] - LogicField(u8), + LogicField(#[from] crate::logic::TryFromU8Error), #[error("byte array too long ({0})")] ByteArrayLen(i32), #[error("unit command not found")] @@ -515,8 +521,8 @@ mod test { ); make_dyn_test!( reparse_unit_command, - DynData::UnitCommand(UnitCommand::Idle), - DynData::UnitCommand(UnitCommand::Rally) + DynData::UnitCommand(UnitCommand::Mine), + DynData::UnitCommand(UnitCommand::Mine) ); make_dyn_test!( reparse_bool_array, diff --git a/src/data/map.rs b/src/data/map.rs index aceddbc..b1405ea 100644 --- a/src/data/map.rs +++ b/src/data/map.rs @@ -108,7 +108,7 @@ macro_rules! lo { pub type EntityMapping = HashMap<u8, Box<dyn Content>>; impl<'l> Tile<'l> { #[must_use] - pub fn new(floor: BlockEnum, ore: BlockEnum) -> Self { + pub const fn new(floor: BlockEnum, ore: BlockEnum) -> Self { Self { floor, ore, @@ -319,7 +319,7 @@ impl<'l> Build<'l> { } #[must_use] - pub fn name(&self) -> &str { + pub const fn name(&self) -> &str { self.block.name() } diff --git a/src/data/mod.rs b/src/data/mod.rs index 5d950bf..0583ea4 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -11,7 +11,7 @@ use thiserror::Error; pub(crate) mod autotile; mod base64; -mod command; +pub mod command; pub mod dynamic; pub mod map; pub mod planet; diff --git a/src/data/renderer.rs b/src/data/renderer.rs index b209c5a..6230e71 100644 --- a/src/data/renderer.rs +++ b/src/data/renderer.rs @@ -97,6 +97,7 @@ pub enum Scale { } impl Scale { + #[must_use] pub const fn px(self) -> u8 { match self { Self::Full => 32, diff --git a/src/data/schematic.rs b/src/data/schematic.rs index 4357ed4..25d2cb7 100644 --- a/src/data/schematic.rs +++ b/src/data/schematic.rs @@ -693,6 +693,7 @@ pub enum W64Error { #[cfg(test)] mod test { use super::*; + #[track_caller] fn unwrap_pretty<T, E: std::fmt::Display + std::error::Error>(r: Result<T, E>) -> T { match r { Ok(t) => t, @@ -746,6 +747,7 @@ mod test { "bXNjaAF4nGNgZGBkZmDJS8xNZeBMrShIzSvOLEtl4E5JLU4uyiwoyczPY2BgYMtJTErNKWZgio5lZODPzUwuytctKMpPTi0uzi8CyjMygAAfA4PQ+Yo5by9u9GxmZGB9GME502nTzKW+Aht/FJq1ez+o8nzYGn5n+wHR70VVf23t9tutu58/Xbm+qr5t/v+PAa93zIn+L1BpFbXfY17fNf1Jyxd/7X7yMuOv0qjQqNCo0KjQqNCo0KjQqNCo0KjQqNCo0KjQqNCo0KjQqNCoEJWFHp987V9uXjv/9y4GAOhu6pc="; "bXNjaAF4nGNgY2BjZmDJS8xNZWBLTswrSyxm4E5JLU4uyiwoyczPY2BgYMtJTErNKWZgio5lhKthYOBkAAE+IDZjIB8wUWoAC2UGMFHqBSaoF1QYGTycJjFMUFHxVPBkmpQyiYXhpAonQ4OnEAPDJBVWBhXPW0wek7bkTlRhvLXNk4khdzYLQ8M2sAEUeoGFUi+wUBoLLJR5AQDzuCAp"; "bXNjaAF4nEWNQRLCIAxFf5O0LhxdewlP5LighQUzCIyl97chVmHx8nmZDyYIQ7J7BUgqruLsw7q8Y22xZABTcnNIK+jxZJyWkv0WGy51S2u4H/Fak2vB/zJww/8MIAVZYh2Gw+jtCx2s+O7pE6nZB0V3bD1sTqtITe8Uc2JOzIm50RpH/U9Bht19AOy5Ge4="; + "bXNjaAF4nBXKPQ6AIAwG0I+fuLjrKTyRcUDo0ASKKdXzq8kbHwJCQJTUCLGxEOZCIytfxl0ATDWdVAf8fjgsqRQ2fmhTyl2G6Z2t69fcz62I/gVp0BSJ"; } #[test] |