mindustry logic execution, map- and schematic- parsing and rendering
deserialize unit factory config
along with a few other fixes
bendn 2023-08-10
parent 86805d0 · commit 5fead62
-rw-r--r--Cargo.toml2
-rw-r--r--src/block/defense.rs15
-rw-r--r--src/block/distribution.rs8
-rw-r--r--src/block/logic.rs2
-rw-r--r--src/block/mod.rs6
-rw-r--r--src/block/payload.rs8
-rw-r--r--src/block/power.rs8
-rw-r--r--src/block/production.rs15
-rw-r--r--src/block/units.rs241
-rw-r--r--src/data/command.rs5
-rw-r--r--src/data/dynamic.rs142
-rw-r--r--src/data/map.rs4
-rw-r--r--src/data/mod.rs2
-rw-r--r--src/data/renderer.rs1
-rw-r--r--src/data/schematic.rs2
15 files changed, 282 insertions, 179 deletions
diff --git a/Cargo.toml b/Cargo.toml
index c1621db..adbb0f9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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]