mindustry logic execution, map- and schematic- parsing and rendering
deserialize most map state (#6)
probably missing alot of erekir things; i dont have a erekir save yet
bendn 2023-07-26
parent 4f6c57b · commit 81d181e
-rw-r--r--Cargo.toml2
-rw-r--r--src/block/defense.rs18
-rw-r--r--src/block/distribution.rs208
-rw-r--r--src/block/drills.rs38
-rw-r--r--src/block/environment.rs2
-rw-r--r--src/block/liquid.rs25
-rw-r--r--src/block/logic.rs226
-rw-r--r--src/block/mod.rs29
-rw-r--r--src/block/payload.rs163
-rw-r--r--src/block/power.rs48
-rw-r--r--src/block/production.rs30
-rw-r--r--src/block/simple.rs23
-rw-r--r--src/block/turrets.rs137
-rw-r--r--src/block/units.rs38
-rw-r--r--src/block/walls.rs2
-rw-r--r--src/data/autotile.rs45
-rw-r--r--src/data/map.rs221
-rw-r--r--src/data/mod.rs11
-rw-r--r--src/data/renderer.rs21
-rw-r--r--src/data/schematic.rs4
-rw-r--r--src/lib.rs17
21 files changed, 915 insertions, 393 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 81fbf99..a56b643 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "mindus"
-version = "1.3.4"
+version = "1.4.0"
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 88cf0d5..e77863c 100644
--- a/src/block/defense.rs
+++ b/src/block/defense.rs
@@ -1,12 +1,13 @@
//! defense
-use crate::block::make_register;
use crate::block::simple::{cost, make_simple};
+use crate::block::*;
make_simple!(DefenseBlock);
+make_simple!(HeatedBlock => |_, _, _, buff: &mut DataRead| read_heated(buff));
make_register! {
- "mender" => DefenseBlock::new(1, true, cost!(Copper: 25, Lead: 30));
- "mend-projector" => DefenseBlock::new(2, true, cost!(Copper: 50, Lead: 100, Titanium: 25, Silicon: 40));
- "overdrive-projector" => DefenseBlock::new(2, true, cost!(Lead: 100, Titanium: 75, Silicon: 75, Plastanium: 30));
- "overdrive-dome" => DefenseBlock::new(3, true, cost!(Lead: 200, Titanium: 130, Silicon: 130, Plastanium: 80, SurgeAlloy: 120));
+ "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));
+ "overdrive-projector" => HeatedBlock::new(2, true, cost!(Lead: 100, Titanium: 75, Silicon: 75, Plastanium: 30));
+ "overdrive-dome" => HeatedBlock::new(3, true, cost!(Lead: 200, Titanium: 130, Silicon: 130, Plastanium: 80, SurgeAlloy: 120));
"force-projector" => DefenseBlock::new(3, true, cost!(Lead: 100, Titanium: 75, Silicon: 125));
"regen-projector" => DefenseBlock::new(3, true, cost!(Silicon: 80, Tungsten: 60, Oxide: 40, Beryllium: 80));
"shock-mine" => DefenseBlock::new(1, true, cost!(Lead: 25, Silicon: 12));
@@ -18,3 +19,10 @@ make_register! {
"shield-projector" => DefenseBlock::new(3, true, &[]);
"large-shield-projector" => DefenseBlock::new(4, true, &[]);
}
+
+/// format:
+/// - heat: [`f32`]
+/// - phase heat: [`f32`]
+fn read_heated(buff: &mut DataRead) -> Result<(), DataReadError> {
+ buff.skip(8)
+} \ No newline at end of file
diff --git a/src/block/distribution.rs b/src/block/distribution.rs
index 08df5de..db21c17 100644
--- a/src/block/distribution.rs
+++ b/src/block/distribution.rs
@@ -8,53 +8,55 @@ use crate::item;
make_simple!(
ConveyorBlock,
- |_, _, name, _, ctx: Option<&RenderingContext>| {
+ |_, _, name, _, ctx: Option<&RenderingContext>, rot: Rotation| {
let ctx = ctx.unwrap(); // we set want_context to true
- Some(tile(ctx, "distribution", "conveyors", name, ctx.rotation))
+ Some(tile(ctx, "distribution", "conveyors", name, rot))
+ },
+ |_, _, _, buff: &mut DataRead| {
+ // format:
+ // - amount: `i32`
+ // - iterate amount:
+ // - val: `i32`
+ // - id = (((val >> 24) as u8) & 0xff) as u16
+ // - x = (val >> 16) as u8) as f32 / 127.0
+ // - y = ((val >> 8) as u8 as f32 + 128.0) / 255.0
+ let amount = buff.read_i32()?;
+ for _ in 0..amount {
+ buff.skip(4)?;
+ }
+ Ok(())
},
true
);
make_simple!(
DuctBlock,
- |_, _, name, _, ctx: Option<&RenderingContext>| {
+ |_, _, name, _, ctx: Option<&RenderingContext>, rot| {
let ctx = ctx.unwrap();
- Some(tile(ctx, "distribution", "ducts", name, ctx.rotation))
+ Some(tile(ctx, "distribution", "ducts", name, rot))
+ },
+ |_, _, _, buff: &mut DataRead| {
+ // format:
+ // - rec_dir: `i8`
+ buff.skip(1)
},
true
);
make_simple!(
JunctionBlock,
- |_, _, _, _, _| None,
- |_, _, _, _, _, buff: &mut crate::data::DataRead| {
- // format:
- // - iterate 4
- // - u8
- // - iterate u8
- // - i64
- for _ in 0..4 {
- let _ = buff.read_u8()?;
- let n = buff.read_u8()? as usize;
- buff.skip(n * 8)?;
- }
- Ok(())
- },
+ |_, _, _, _, _, _| None,
+ |_, _, _, buff: &mut DataRead| { read_directional_item_buffer(buff) },
false
);
-make_simple!(
- SimpleDuctBlock,
- |_, _, name, _, ctx: Option<&RenderingContext>| {
- let ctx = ctx.unwrap();
- let mut base = load("distribution/ducts", "duct-base").unwrap().clone();
- let mut top = load("distribution/ducts", name).unwrap().clone();
- top.rotate(ctx.rotation.rotated(false).count());
- base.overlay(&top, 0, 0);
- Some(ImageHolder::from(base))
- },
- true
-);
+make_simple!(SimpleDuctBlock, |_, _, name, _, _, rot: Rotation| {
+ let mut base = load("distribution/ducts", "duct-base").unwrap().clone();
+ let mut top = load("distribution/ducts", name).unwrap().clone();
+ top.rotate(rot.rotated(false).count());
+ base.overlay(&top, 0, 0);
+ Some(ImageHolder::from(base))
+});
fn draw_stack(
_: &StackConveyor,
@@ -62,9 +64,10 @@ fn draw_stack(
name: &str,
_: Option<&State>,
ctx: Option<&RenderingContext>,
+ rot: Rotation,
) -> Option<ImageHolder> {
let ctx = ctx.unwrap();
- let mask = mask(ctx, name);
+ let mask = mask(ctx, rot, name);
// clone to not hold lock
let edge = load("distribution/stack-conveyors", &format!("{name}-edge"))
.unwrap()
@@ -84,32 +87,29 @@ fn draw_stack(
.unwrap()
.clone()
};
- let empty = ctx.cross[ctx.rotation.count() as usize].map_or(true, |(v, _)| v.name != name);
+ let empty = ctx.cross[rot.count() as usize].map_or(true, |(v, _)| v.name != name);
// mindustry says fuck this and just draws the arrow convs in schems but im better than that
Some(ImageHolder::from(
- if ctx.rotation.mirrored(true, true).mask() == mask && empty && name != "surge-conveyor" {
+ if rot.mirrored(true, true).mask() == mask && empty && name != "surge-conveyor" {
// end
let mut base = gimme(2);
- edgify(
- ctx.rotation.mirrored(true, true).rotated(false).count(),
- &mut base,
- );
+ edgify(rot.mirrored(true, true).rotated(false).count(), &mut base);
base
} else if mask == B0000 && empty {
// single
let mut base = gimme(0);
- base.rotate(ctx.rotation.rotated(false).count());
+ base.rotate(rot.rotated(false).count());
edgify(5, &mut base);
base
} else if mask == B0000 {
// input
let mut base = gimme(1);
- edgify(ctx.rotation.rotated(false).count(), &mut base);
+ edgify(rot.rotated(false).count(), &mut base);
base
} else {
// directional
let mut base = gimme(0);
- let going = ctx.rotation.rotated(false).count();
+ let going = rot.rotated(false).count();
base.rotate(going);
for [r, i] in [[3, 0b1000], [0, 0b0100], [1, 0b0010], [2, 0b0001]] {
if (mask.into_u8() & i) == 0 && (going != r || empty) {
@@ -123,7 +123,15 @@ fn draw_stack(
))
}
-make_simple!(StackConveyor, draw_stack, true);
+make_simple!(
+ StackConveyor,
+ draw_stack,
+ // format:
+ // - link: `i32`
+ // - cooldown: `f32`
+ |_, _, _, buff: &mut DataRead| { buff.skip(8) },
+ true
+);
make_simple!(ControlBlock);
make_register! {
@@ -224,7 +232,8 @@ impl BlockLogic for ItemBlock {
_: &str,
name: &str,
state: Option<&State>,
- ctx: Option<&RenderingContext>,
+ _: Option<&RenderingContext>,
+ rot: Rotation,
) -> Option<ImageHolder> {
let mut p = load(
match name {
@@ -259,13 +268,13 @@ impl BlockLogic for ItemBlock {
}
if matches!(name, "duct-unloader" | "duct-router") {
let mut null = load("distribution/ducts", "top").unwrap().to_owned();
- null.rotate(ctx.unwrap().rotation.rotated(false).count());
+ null.rotate(rot.rotated(false).count());
if name == "duct-unloader" {
let mut top = load("distribution/ducts", "duct-unloader-top")
.unwrap()
.to_owned();
// this rotate call could be omitted if rotation == Right to save a clone
- top.rotate(ctx.unwrap().rotation.rotated(false).count());
+ top.rotate(rot.rotated(false).count());
null.overlay(&top, 0, 0);
}
p.overlay(&null, 0, 0);
@@ -277,8 +286,43 @@ impl BlockLogic for ItemBlock {
}
}
- fn want_context(&self) -> bool {
- true
+ /// format:
+ /// (sorter | unloader | duct router | item source)
+ /// - item: `i16` as item
+ /// (duct-unloader/directional):
+ /// - tmp: `i16`
+ /// - if tmp != -1: item = tmp as item
+ /// - offset: `u16`
+ /// (unit-cargo-unload-point)
+ /// - item: `u16` as item
+ /// - stale: `bool`
+ fn read(
+ &self,
+ b: &mut Build,
+ _: &BlockRegistry,
+ _: &EntityMapping,
+ buff: &mut DataRead,
+ ) -> Result<(), DataReadError> {
+ match b.block.name() {
+ "duct-unloader" => {
+ let n = buff.read_i16()?;
+ if n != -1 {
+ b.state = Some(Self::create_state(item::Type::try_from(n as u16).ok()));
+ }
+ }
+ "unit-cargo-unload-point" => {
+ b.state = Some(Self::create_state(
+ item::Type::try_from(buff.read_u16()?).ok(),
+ ));
+ buff.skip(1)?;
+ }
+ _ => {
+ b.state = Some(Self::create_state(
+ item::Type::try_from(buff.read_u16()?).ok(),
+ ));
+ }
+ }
+ Ok(())
}
}
@@ -414,25 +458,29 @@ impl BlockLogic for BridgeBlock {
}
/// format:
- /// - out: `i32`
- /// - warmup: `f32`
- /// - iterate `links<u8>`
- /// - in+: `i32`
- /// - moved: `bool`
+ /// (item bridge)
+ /// - become [`read_buffered_item_bridge`]
+ /// (buffered brige)
+ /// - become [`read_item_buffer`]
+ /// (mass driver) (9b)
+ /// - link: `i32`
+ /// - rotation: `f32`
+ /// - state: `i8`
fn read(
&self,
- _: &str,
- _: &str,
+ t: &mut Build,
_: &super::BlockRegistry,
_: &crate::data::map::EntityMapping,
buff: &mut crate::data::DataRead,
) -> Result<(), crate::data::ReadError> {
- buff.read_i32()?;
- buff.read_f32()?;
- for _ in 0..buff.read_u8()? {
- buff.read_i32()?;
+ match t.block.name() {
+ "bridge-conveyor" => read_buffered_item_bridge(buff)?,
+ "phase-conveyor" | "phase-conduit" | "bridge-conduit" => read_item_bridge(buff)?,
+ "mass-driver" => buff.skip(9)?,
+ // no state?
+ "duct-bridge" | "reinforced-bridge-conduit" => {}
+ _ => unreachable!(), // surely no forget
}
- buff.read_bool()?;
Ok(())
}
}
@@ -443,3 +491,47 @@ pub struct BridgeConvertError {
pub x: i16,
pub y: i16,
}
+
+/// format;
+/// - call [`read_item_bridge`]
+/// - become [`read_item_buffer`]
+fn read_buffered_item_bridge(buff: &mut DataRead) -> Result<(), DataReadError> {
+ read_item_bridge(buff)?;
+ read_item_buffer(buff)
+}
+
+/// format:
+/// - index: `u8`
+/// - iter `u8`
+/// l: `i64`
+fn read_item_buffer(buff: &mut DataRead) -> Result<(), DataReadError> {
+ buff.skip(1)?;
+ let n = buff.read_u8()? as usize;
+ buff.skip(n * 8)
+}
+
+/// format:
+/// - link: `i32`
+/// - warmup: `f32`
+/// - iterate `u8`
+/// - incoming: `i32`
+/// - moved: `bool`
+fn read_item_bridge(buff: &mut DataRead) -> Result<(), DataReadError> {
+ buff.skip(8)?;
+ let n = buff.read_u8()? as usize;
+ buff.skip((n * 4) + 1)
+}
+
+/// format:
+/// - iterate 4
+/// - u8
+/// - iterate u8
+/// - i64
+fn read_directional_item_buffer(buff: &mut DataRead) -> Result<(), DataReadError> {
+ for _ in 0..4 {
+ let _ = buff.read_u8()?;
+ let n = buff.read_u8()? as usize;
+ buff.skip(n * 8)?;
+ }
+ Ok(())
+}
diff --git a/src/block/drills.rs b/src/block/drills.rs
index f3d2e8e..fc1c48d 100644
--- a/src/block/drills.rs
+++ b/src/block/drills.rs
@@ -1,27 +1,39 @@
//! extraction of raw resources (mine part)
-use crate::block::make_register;
use crate::block::simple::{cost, make_simple};
-use crate::data::renderer::*;
+use crate::block::*;
-make_simple!(DrillBlock, |me: &DrillBlock, _, name, _, _| {
- if matches!(name, "cliff-crusher" | "large-plasma-bore" | "plasma-bore") {
- const SFX: &[&str; 3] = &["", "-top", "-rotator"];
- return Some(ImageHolder::Own(read_with("drills", name, SFX, me.size)));
- }
- Some(ImageHolder::Borrow(load("drills", name).unwrap()))
-});
+make_simple!(
+ DrillBlock,
+ |me: &DrillBlock, _, name, _, _, _| {
+ if matches!(name, "cliff-crusher" | "large-plasma-bore" | "plasma-bore") {
+ const SFX: &[&str; 3] = &["", "-top", "-rotator"];
+ return Some(ImageHolder::Own(read_with("drills", name, SFX, me.size)));
+ }
+ Some(ImageHolder::Borrow(load("drills", name).unwrap()))
+ },
+ |_, _, _, buff: &mut DataRead| { read_drill(buff) }
+);
+make_simple!(ExtractorBlock);
+make_simple!(WallCrafter);
make_register! {
"mechanical-drill" => DrillBlock::new(2, true, cost!(Copper: 12));
"pneumatic-drill" => DrillBlock::new(2, true, cost!(Copper: 18, Graphite: 10));
"laser-drill" => DrillBlock::new(3, true, cost!(Copper: 35, Graphite: 30, Titanium: 20, Silicon: 30));
"blast-drill" => DrillBlock::new(4, true, cost!(Copper: 65, Titanium: 50, Thorium: 75, Silicon: 60));
- "water-extractor" => DrillBlock::new(2, true, cost!(Copper: 30, Lead: 30, Metaglass: 30, Graphite: 30));
- "oil-extractor" => DrillBlock::new(3, true, cost!(Copper: 150, Lead: 115, Graphite: 175, Thorium: 115, Silicon: 75));
- "vent-condenser" => DrillBlock::new(3, true, cost!(Graphite: 20, Beryllium: 60));
- "cliff-crusher" => DrillBlock::new(2, false, cost!(Beryllium: 100, Graphite: 40));
+ "water-extractor" => ExtractorBlock::new(2, true, cost!(Copper: 30, Lead: 30, Metaglass: 30, Graphite: 30));
+ "oil-extractor" => ExtractorBlock::new(3, true, cost!(Copper: 150, Lead: 115, Graphite: 175, Thorium: 115, Silicon: 75));
+ "vent-condenser" => ExtractorBlock::new(3, true, cost!(Graphite: 20, Beryllium: 60));
+ "cliff-crusher" => WallCrafter::new(2, false, cost!(Beryllium: 100, Graphite: 40));
"plasma-bore" => DrillBlock::new(2, false, cost!(Beryllium: 40));
"large-plasma-bore" => DrillBlock::new(3, false, cost!(Silicon: 100, Oxide: 25, Beryllium: 100, Tungsten: 70));
"impact-drill" => DrillBlock::new(4, true, cost!(Silicon: 70, Beryllium: 90, Graphite: 60));
"eruption-drill" => DrillBlock::new(5, true, cost!(Silicon: 200, Oxide: 20, Tungsten: 200, Thorium: 120));
}
+
+/// format:
+/// - progress: [`f32`]
+/// - warmup: [`f32`]
+fn read_drill(buff: &mut DataRead) -> Result<(), DataReadError> {
+ buff.skip(8)
+}
diff --git a/src/block/environment.rs b/src/block/environment.rs
index b0ee4e3..168bdfd 100644
--- a/src/block/environment.rs
+++ b/src/block/environment.rs
@@ -11,7 +11,7 @@ macro_rules! register_env {
$($field => EnvironmentBlock::new($size, true, &[]);)*
);
- make_simple!(EnvironmentBlock, |_, _, name, _, _| {
+ make_simple!(EnvironmentBlock, |_, _, name, _, _, _| {
let mut rand = StdRand::seed(ClockSeed::default().next_u64());
match name {
$($field => {
diff --git a/src/block/liquid.rs b/src/block/liquid.rs
index 5141025..22961ed 100644
--- a/src/block/liquid.rs
+++ b/src/block/liquid.rs
@@ -13,10 +13,10 @@ use crate::utils::ImageUtils;
make_simple!(LiquidBlock);
make_simple!(
ConduitBlock,
- |_, _, name, _, ctx: Option<&RenderingContext>| {
+ |_, _, name, _, ctx: Option<&RenderingContext>, rot| {
let ctx = ctx.unwrap();
- let mask = mask(ctx, name);
- let (index, rot, flip) = mask2rotations(mask, ctx.rotation);
+ let mask = mask(ctx, rot, name);
+ let (index, rot, flip) = mask2rotations(mask, rot);
let tile = rotations2tile(
(index, rot, flip),
"liquid",
@@ -113,10 +113,6 @@ impl BlockLogic for FluidBlock {
Box::new(Self::create_state(*state))
}
- fn mirror_state(&self, _: &mut State, _: bool, _: bool) {}
-
- fn rotate_state(&self, _: &mut State, _: bool) {}
-
fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
match Self::get_state(state) {
None => Ok(DynData::Empty),
@@ -130,6 +126,7 @@ impl BlockLogic for FluidBlock {
name: &str,
state: Option<&State>,
_: Option<&RenderingContext>,
+ _: Rotation,
) -> Option<ImageHolder> {
let mut p = load(category, name).unwrap().clone();
if let Some(state) = state {
@@ -143,6 +140,20 @@ impl BlockLogic for FluidBlock {
null.overlay(&p, 0, 0);
Some(ImageHolder::Own(null))
}
+
+ /// format:
+ /// - fluid: [`u16`] as [`Fluid`](fluid::Type)
+ fn read(
+ &self,
+ b: &mut Build,
+ _: &BlockRegistry,
+ _: &EntityMapping,
+ buff: &mut DataRead,
+ ) -> Result<(), DataReadError> {
+ let f = buff.read_u16()?;
+ b.state = Some(Self::create_state(fluid::Type::try_from(f).ok()));
+ Ok(())
+ }
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Error)]
diff --git a/src/block/logic.rs b/src/block/logic.rs
index 8a6b447..ee9445e 100644
--- a/src/block/logic.rs
+++ b/src/block/logic.rs
@@ -5,12 +5,23 @@ use std::string::FromUtf8Error;
use image::{Rgb, RgbImage};
use crate::block::simple::*;
-use crate::block::*;
-use crate::data::dynamic::DynType;
+use crate::data::dynamic::{DynSerializer, DynType};
+use crate::{block::*, Serializer};
use crate::data::{self, CompressError, DataRead, DataWrite};
make_simple!(LogicBlock);
+make_simple!(
+ MemoryBlock,
+ |_, _, _, _, _, _| None,
+ |_, _, _, buff: &mut DataRead| {
+ // format:
+ // - iterate [`u32`]
+ // - memory: [`f64`]
+ let n = buff.read_u32()? as usize;
+ buff.skip(n * 8)
+ }
+);
make_register! {
"reinforced-message" => MessageLogic::new(1, true, cost!(Graphite: 10, Beryllium: 5));
@@ -19,15 +30,15 @@ make_register! {
"micro-processor" => ProcessorLogic::new(1, true, cost!(Copper: 90, Lead: 50, Silicon: 50));
"logic-processor" => ProcessorLogic::new(2, true, cost!(Lead: 320, Graphite: 60, Thorium: 50, Silicon: 80));
"hyper-processor" => ProcessorLogic::new(3, true, cost!(Lead: 450, Thorium: 75, Silicon: 150, SurgeAlloy: 50));
- "memory-cell" => LogicBlock::new(1, true, cost!(Copper: 30, Graphite: 30, Silicon: 30));
- "memory-bank" => LogicBlock::new(2, true, cost!(Copper: 30, Graphite: 80, Silicon: 80, PhaseFabric: 30));
+ "memory-cell" => MemoryBlock::new(1, true, cost!(Copper: 30, Graphite: 30, Silicon: 30));
+ "memory-bank" => MemoryBlock::new(2, true, cost!(Copper: 30, Graphite: 80, Silicon: 80, PhaseFabric: 30));
"logic-display" => LogicBlock::new(3, true, cost!(Lead: 100, Metaglass: 50, Silicon: 50));
"large-logic-display" => LogicBlock::new(6, true, cost!(Lead: 200, Metaglass: 100, Silicon: 150, PhaseFabric: 75));
"canvas" => CanvasBlock::new(2, true, cost!(Silicon: 30, Beryllium: 10), 12);
// editor only
"world-processor" => LogicBlock::new(1, true, &[]);
"world-message" => MessageLogic::new(1, true, &[]);
- "world-cell" => LogicBlock::new(1, true, &[]);
+ "world-cell" => MemoryBlock::new(1, true, &[]);
}
pub struct CanvasBlock {
@@ -69,6 +80,24 @@ impl CanvasBlock {
state_impl!(pub RgbImage);
}
+fn deser_canvas_image(b: Vec<u8>, size: usize) -> RgbImage {
+ let mut p = RgbImage::new(size as u32, size as u32);
+ for i in 0..(size * size) {
+ let offset = i * 3;
+ let mut n = 0;
+ for i in 0..3 {
+ let word = (i + offset) >> 3;
+ n |= (((b[word] & (1 << ((i + offset) & 7))) != 0) as u8) << i;
+ }
+ p.put_pixel(
+ i as u32 % size as u32,
+ i as u32 / size as u32,
+ PALETTE[n as usize],
+ )
+ }
+ p
+}
+
impl BlockLogic for CanvasBlock {
impl_block!();
@@ -78,23 +107,10 @@ impl BlockLogic for CanvasBlock {
fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError> {
match data {
- DynData::ByteArray(b) => {
- let mut p = RgbImage::new(self.canvas_size as u32, self.canvas_size as u32);
- for i in 0..(self.canvas_size * self.canvas_size) as usize {
- let offset = i * 3;
- let mut n = 0;
- for i in 0..3 {
- let word = (i + offset) >> 3;
- n |= (((b[word] & (1 << ((i + offset) & 7))) != 0) as u8) << i;
- }
- p.put_pixel(
- i as u32 % self.canvas_size as u32,
- i as u32 / self.canvas_size as u32,
- PALETTE[n as usize],
- )
- }
- Ok(Some(Self::create_state(p)))
- }
+ DynData::ByteArray(b) => Ok(Some(Self::create_state(deser_canvas_image(
+ b,
+ self.canvas_size as usize,
+ )))),
_ => Err(DeserializeError::InvalidType {
have: data.get_type(),
expect: DynType::String,
@@ -135,6 +151,7 @@ impl BlockLogic for CanvasBlock {
n: &str,
state: Option<&State>,
_: Option<&RenderingContext>,
+ _: Rotation,
) -> Option<ImageHolder> {
if let Some(state) = state {
let state = self.clone_state(state);
@@ -162,6 +179,26 @@ impl BlockLogic for CanvasBlock {
self.size as u32 * 32,
)))
}
+
+ /// format:
+ /// - len: [`i32`]
+ /// - read(len) -> [`deser_canvas_image`]
+ fn read(
+ &self,
+ build: &mut Build,
+ _: &BlockRegistry,
+ _: &EntityMapping,
+ buff: &mut DataRead,
+ ) -> Result<(), DataReadError> {
+ let n = buff.read_i32()? as usize;
+ let mut b = vec![0; n];
+ buff.read_bytes(&mut b)?;
+ build.state = Some(Self::create_state(deser_canvas_image(
+ b,
+ self.canvas_size as usize,
+ )));
+ Ok(())
+ }
}
pub struct MessageLogic {
@@ -213,6 +250,17 @@ impl BlockLogic for MessageLogic {
fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
Ok(DynData::String(Some(Self::get_state(state).clone())))
}
+
+ fn read(
+ &self,
+ b: &mut Build,
+ _: &BlockRegistry,
+ _: &EntityMapping,
+ buff: &mut DataRead,
+ ) -> Result<(), DataReadError> {
+ b.state = Some(Self::create_state(buff.read_utf()?.to_string()));
+ Ok(())
+ }
}
pub struct SwitchLogic {
@@ -257,13 +305,40 @@ impl BlockLogic for SwitchLogic {
Box::new(*Self::get_state(state))
}
- fn mirror_state(&self, _: &mut State, _: bool, _: bool) {}
-
- fn rotate_state(&self, _: &mut State, _: bool) {}
-
fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
Ok(DynData::Boolean(*Self::get_state(state)))
}
+
+ fn read(
+ &self,
+ build: &mut Build,
+ _: &BlockRegistry,
+ _: &EntityMapping,
+ buff: &mut DataRead,
+ ) -> Result<(), DataReadError> {
+ build.state = Some(Self::create_state(buff.read_bool()?));
+ Ok(())
+ }
+
+ fn draw(
+ &self,
+ _: &str,
+ _: &str,
+ state: Option<&State>,
+ _: Option<&RenderingContext>,
+ _: Rotation,
+ ) -> Option<ImageHolder> {
+ let base = load("logic", "switch").unwrap();
+ if let Some(state) = state {
+ if *Self::get_state(state) {
+ let mut base = base.clone();
+ let on = load("logic", "switch-on").unwrap();
+ base.overlay(&on, 0, 0);
+ return Some(ImageHolder::from(base));
+ }
+ }
+ Some(ImageHolder::from(base))
+ }
}
pub struct ProcessorLogic {
@@ -286,6 +361,36 @@ impl ProcessorLogic {
state_impl!(pub ProcessorState);
}
+fn read_decompressed(buff: &mut DataRead) -> Result<ProcessorState, ProcessorDeserializeError> {
+ let ver = buff.read_u8()?;
+ if ver != 1 {
+ return Err(ProcessorDeserializeError::Version(ver));
+ }
+
+ let code_len = buff.read_u32()? as usize;
+ if !(0..=500 * 1024).contains(&code_len) {
+ return Err(ProcessorDeserializeError::CodeLength(code_len));
+ }
+ let mut code = vec![];
+ code.resize(code_len, 0);
+ buff.read_bytes(&mut code)?;
+ let code = String::from_utf8(code)?;
+ let link_cnt = buff.read_u32()? as usize;
+ let mut links = vec![];
+ links.reserve(link_cnt);
+ for _ in 0..link_cnt {
+ let name = buff.read_utf()?;
+ let x = buff.read_i16()?;
+ let y = buff.read_i16()?;
+ links.push(ProcessorLink {
+ name: String::from(name),
+ x,
+ y,
+ });
+ }
+ Ok(ProcessorState { code, links })
+}
+
impl BlockLogic for ProcessorLogic {
impl_block!();
@@ -299,43 +404,11 @@ impl BlockLogic for ProcessorLogic {
DynData::ByteArray(arr) => {
let input = arr.as_ref();
let buff = DataRead::new(input).deflate()?;
- let mut buff = DataRead::new(&buff);
- let ver = ProcessorDeserializeError::forward(buff.read_u8())?;
- if ver != 1 {
- return Err(DeserializeError::Custom(Box::new(
- ProcessorDeserializeError::Version(ver),
- )));
- }
-
- let code_len = ProcessorDeserializeError::forward(buff.read_i32())?;
- if !(0..=500 * 1024).contains(&code_len) {
- return Err(DeserializeError::Custom(Box::new(
- ProcessorDeserializeError::CodeLength(code_len),
- )));
- }
- let mut code = Vec::<u8>::new();
- code.resize(code_len as usize, 0);
- ProcessorDeserializeError::forward(buff.read_bytes(&mut code))?;
- let code = ProcessorDeserializeError::forward(String::from_utf8(code))?;
- let link_cnt = ProcessorDeserializeError::forward(buff.read_i32())?;
- if link_cnt < 0 {
- return Err(DeserializeError::Custom(Box::new(
- ProcessorDeserializeError::LinkCount(link_cnt),
- )));
- }
- let mut links = Vec::<ProcessorLink>::new();
- links.reserve(link_cnt as usize);
- for _ in 0..link_cnt {
- let name = ProcessorDeserializeError::forward(buff.read_utf())?;
- let x = ProcessorDeserializeError::forward(buff.read_i16())?;
- let y = ProcessorDeserializeError::forward(buff.read_i16())?;
- links.push(ProcessorLink {
- name: String::from(name),
- x,
- y,
- });
- }
- Ok(Some(Self::create_state(ProcessorState { code, links })))
+ Ok(Some(Self::create_state(
+ ProcessorDeserializeError::forward(read_decompressed(&mut DataRead::new(
+ &buff,
+ )))?,
+ )))
}
_ => Err(DeserializeError::InvalidType {
have: data.get_type(),
@@ -344,6 +417,29 @@ impl BlockLogic for ProcessorLogic {
}
}
+ fn read(
+ &self,
+ b: &mut Build,
+ _: &BlockRegistry,
+ _: &EntityMapping,
+ buff: &mut DataRead,
+ ) -> Result<(), DataReadError> {
+ let n = buff.read_u32()? as usize;
+ let mut v = vec![0; n];
+ buff.read_bytes(&mut v)?;
+ v = DataRead::new(&v).deflate().unwrap();
+ b.state = Some(Self::create_state(
+ read_decompressed(&mut DataRead::new(&v)).unwrap(),
+ ));
+ for _ in 0..buff.read_u32()? {
+ let _ = buff.read_utf()?;
+ let _ = DynSerializer.deserialize(buff).unwrap();
+ }
+ let memory = buff.read_u32()? as usize;
+ buff.skip(memory * 8)?;
+ Ok(())
+ }
+
fn clone_state(&self, state: &State) -> State {
Box::new(Self::get_state(state).clone())
}
@@ -396,9 +492,7 @@ pub enum ProcessorDeserializeError {
#[error("unsupported version ({0})")]
Version(u8),
#[error("invalid code length ({0})")]
- CodeLength(i32),
- #[error("invalid link count {0}")]
- LinkCount(i32),
+ CodeLength(usize),
}
impl ProcessorDeserializeError {
diff --git a/src/block/mod.rs b/src/block/mod.rs
index da52426..a0120ca 100644
--- a/src/block/mod.rs
+++ b/src/block/mod.rs
@@ -11,7 +11,7 @@ use std::fmt;
use crate::access::BoxAccess;
use crate::data::dynamic::{DynData, DynType};
-use crate::data::map::EntityMapping;
+use crate::data::map::{Build, EntityMapping};
use crate::data::{self, renderer::*, CompressError};
use crate::data::{DataRead, GridPos, ReadError as DataReadError};
use crate::item::storage::ItemStorage;
@@ -63,6 +63,7 @@ pub trait BlockLogic {
name: &str,
state: Option<&State>,
context: Option<&RenderingContext>,
+ rot: Rotation,
) -> Option<ImageHolder> {
None
}
@@ -71,12 +72,10 @@ pub trait BlockLogic {
false
}
- // TODO: use data
#[allow(unused_variables)]
fn read(
&self,
- category: &str,
- name: &str,
+ build: &mut Build,
reg: &BlockRegistry,
mapping: &EntityMapping,
buff: &mut DataRead,
@@ -166,7 +165,7 @@ impl SerializeError {
pub struct Block {
category: Cow<'static, str>,
name: Cow<'static, str>,
- logic: BoxAccess<'static, dyn BlockLogic + Sync>,
+ pub(crate) logic: BoxAccess<'static, dyn BlockLogic + Sync>,
}
impl PartialEq for Block {
@@ -204,11 +203,16 @@ impl Block {
}
/// draw this block, with this state
- pub fn image(&self, state: Option<&State>, context: Option<&RenderingContext>) -> ImageHolder {
+ pub fn image(
+ &self,
+ state: Option<&State>,
+ context: Option<&RenderingContext>,
+ rot: Rotation,
+ ) -> ImageHolder {
if let Some(p) = self
.logic
.as_ref()
- .draw(&self.category, &self.name, state, context)
+ .draw(&self.category, &self.name, state, context, rot)
{
return p;
}
@@ -264,17 +268,6 @@ impl Block {
pub(crate) fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
self.logic.serialize_state(state)
}
-
- #[doc(hidden)]
- pub fn read(
- &self,
- buff: &mut DataRead,
- reg: &BlockRegistry,
- mapping: &EntityMapping,
- ) -> Result<(), DataReadError> {
- self.logic
- .read(&self.category, &self.name, reg, mapping, buff)
- }
}
impl fmt::Debug for Block {
diff --git a/src/block/payload.rs b/src/block/payload.rs
index 416adaf..4a842b7 100644
--- a/src/block/payload.rs
+++ b/src/block/payload.rs
@@ -13,11 +13,17 @@ use crate::unit;
use super::BlockRegistry;
make_simple!(SimplePayloadBlock);
+make_simple!(
+ PayloadConveyor,
+ |_, _, _, _, _, _| None,
+ read_payload_conveyor
+);
+// make_simple!(PayloadRouter, |_, _, _, _, _, _| None, read_payload_router);
make_register! {
- "payload-conveyor" => SimplePayloadBlock::new(3, false, cost!(Copper: 10, Graphite: 10));
+ "payload-conveyor" => PayloadConveyor::new(3, false, cost!(Copper: 10, Graphite: 10));
"payload-router" => PayloadBlock::new(3, false, cost!(Copper: 10, Graphite: 15));
- "reinforced-payload-conveyor" => SimplePayloadBlock::new(3, false, cost!(Tungsten: 10));
+ "reinforced-payload-conveyor" => PayloadConveyor::new(3, false, cost!(Tungsten: 10));
"reinforced-payload-router" => SimplePayloadBlock::new(3, false, cost!(Tungsten: 15));
"payload-mass-driver" => BridgeBlock::new(3, true, cost!(Tungsten: 120, Silicon: 120, Graphite: 50), 700, false);
"large-payload-mass-driver" => BridgeBlock::new(5, true, cost!(Thorium: 200, Tungsten: 200, Silicon: 200, Graphite: 100, Oxide: 30), 1100, false);
@@ -33,12 +39,14 @@ make_register! {
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+/// payload item cfg
pub enum Payload {
Empty,
Block(block::content::Type),
Unit(unit::Type),
}
+/// a payload related block with [item cfg](Payload)
pub struct PayloadBlock {
size: u8,
symmetric: bool,
@@ -103,49 +111,124 @@ impl BlockLogic for PayloadBlock {
Payload::Unit(unit) => Ok(DynData::Content(content::Type::Unit, (*unit).into())),
}
}
+}
+
+/// format:
+/// - call [`read_payload_conveyor`]
+/// - t: [`u8`]
+/// - sort: [`u16`]
+/// - recdir: [`u8`]
+fn read_payload_router(
+ b: &mut Build,
+ reg: &BlockRegistry,
+ entity_mapping: &EntityMapping,
+ buff: &mut DataRead,
+) -> Result<(), DataReadError> {
+ read_payload_conveyor(b, reg, entity_mapping, buff)?;
+ buff.skip(4)
+}
+
+/// format:
+/// - [skip(4)](`DataRead::skip`)
+/// - rot: [`f32`]
+/// - become [`read_payload`]
+fn read_payload_conveyor(
+ _: &mut Build,
+ reg: &BlockRegistry,
+ entity_mapping: &EntityMapping,
+ buff: &mut DataRead,
+) -> Result<(), DataReadError> {
+ buff.skip(8)?;
+ read_payload(reg, entity_mapping, buff)
+}
+
+/// format:
+/// - vector: ([`f32`], [`f32`])
+/// - rotation: [`f32`]
+/// - become [`read_payload`]
+pub(crate) fn read_payload_block(
+ reg: &BlockRegistry,
+ entity_mapping: &EntityMapping,
+ buff: &mut DataRead,
+) -> Result<(), DataReadError> {
+ buff.skip(12)?;
+ read_payload(reg, entity_mapping, buff)
+}
- /// format:
- /// - exists: `bool`
- /// - if !exists: ok
- /// - type: `u8`
- /// - if type == 1 (payload block):
- /// - block: `u16`
- /// - version: `u8`
- /// - [`crate::block::Block::read`] (recursion :ferrisHmm:),
- /// - if type == 2 (paylood unit):
- /// - id: `u8`
- /// - unit read???????? TODO
- fn read(
- &self,
- _: &str,
- _: &str,
- reg: &BlockRegistry,
- entity_mapping: &crate::data::map::EntityMapping,
- buff: &mut crate::data::DataRead,
- ) -> Result<(), crate::data::ReadError> {
- if !buff.read_bool()? {
- return Ok(());
+/// format:
+/// - exists: [`bool`]
+/// - if !exists: ok
+/// - type: [`u8`]
+/// - if type == `1` (payload block):
+/// - block: [`u16`]
+/// - version: [`u8`]
+/// - [`BlockLogic::read`] (recursion :ferrisHmm:),
+/// - if type == 2 (paylood unit):
+/// - id: [`u8`]
+/// - unit read???????? TODO
+fn read_payload(
+ reg: &BlockRegistry,
+ entity_mapping: &crate::data::map::EntityMapping,
+ buff: &mut DataRead,
+) -> Result<(), DataReadError> {
+ if !buff.read_bool()? {
+ return Ok(());
+ }
+ let t = buff.read_u8()?;
+ const BLOCK: u8 = 1;
+ const UNIT: u8 = 0;
+ match t {
+ BLOCK => {
+ let b = buff.read_u16()?;
+ let b = BlockEnum::try_from(b).unwrap_or(BlockEnum::Router);
+ let block = reg.get(b.get_name()).unwrap();
+ block
+ .logic
+ .read(&mut Build::new(block), reg, entity_mapping, buff)?;
}
- let t = buff.read_u8()?;
- const BLOCK: u8 = 1;
- const UNIT: u8 = 0;
- match t {
- BLOCK => {
- let b = buff.read_u16()?;
- let b = BlockEnum::try_from(b).unwrap_or(BlockEnum::Router);
- let b = reg.get(b.get_name()).unwrap();
- b.read(buff, reg, entity_mapping)?;
- }
- UNIT => {
- let u = buff.read_u8()?;
- let Some(_u) = entity_mapping.get(&u) else {
+ UNIT => {
+ let u = buff.read_u8()?;
+ let Some(_u) = entity_mapping.get(&u) else {
return Err(ReadError::Expected("map entry"));
};
- // unit::Type::try_from(u).unwrap_or(unit::Type::Alpha).read(todo!());
- }
- _ => return Err(ReadError::Expected("0 | 1")),
+ // unit::Type::try_from(u).unwrap_or(unit::Type::Alpha).read(todo!());
}
- Ok(())
+ _ => return Err(ReadError::Expected("0 | 1")),
+ }
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use std::collections::HashMap;
+
+ use crate::registry::Registry;
+
+ use super::*;
+ #[test]
+ fn payload_conv() {
+ let mut reg = Registry::default();
+ register(&mut reg);
+ let mut r = DataRead::new(&[0, 0, 0, 0, 0, 0, 0, 0, 0]);
+ read_payload_conveyor(
+ &mut Build::new(&PAYLOAD_CONVEYOR),
+ &reg,
+ &HashMap::default(),
+ &mut r,
+ )
+ .unwrap();
+ assert!(r.read_bool().is_err());
+ let mut r = DataRead::new(&[
+ 65, 198, 232, 0, 67, 51, 255, 249, 1, 1, 0, 157, 0, 67, 197, 128, 0, 128, 1, 3,
+ ]);
+ read_payload_conveyor(
+ &mut Build::new(&PAYLOAD_CONVEYOR),
+ &reg,
+ &HashMap::default(),
+ &mut r,
+ )
+ .unwrap();
+ assert!(r.read_bool().is_err());
}
}
diff --git a/src/block/power.rs b/src/block/power.rs
index 69a1857..184203b 100644
--- a/src/block/power.rs
+++ b/src/block/power.rs
@@ -5,7 +5,11 @@ use crate::block::simple::*;
use crate::block::*;
use crate::data::dynamic::DynType;
-make_simple!(GeneratorBlock);
+make_simple!(GeneratorBlock => |_, _, _, buff: &mut DataRead| read_generator(buff));
+make_simple!(NuclearGeneratorBlock => |_, _, _, buff: &mut DataRead| read_nuclear(buff));
+make_simple!(ImpactReactorBlock => |_, _, _, buff: &mut DataRead| read_impact(buff));
+make_simple!(HeaterGeneratorBlock => |_, _, _, buff: &mut DataRead| read_heater(buff));
+make_simple!(BatteryBlock);
make_register! {
// illuminator == power ?????
@@ -14,8 +18,8 @@ make_register! {
"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);
"diode" => GeneratorBlock::new(1, false, cost!(Metaglass: 10, Silicon: 10, Plastanium: 5));
- "battery" => GeneratorBlock::new(1, true, cost!(Copper: 5, Lead: 20));
- "battery-large" => GeneratorBlock::new(3, true, cost!(Lead: 50, Titanium: 20, Silicon: 30));
+ "battery" => BatteryBlock::new(1, true, cost!(Copper: 5, Lead: 20));
+ "battery-large" => BatteryBlock::new(3, true, cost!(Lead: 50, Titanium: 20, Silicon: 30));
"combustion-generator" => GeneratorBlock::new(1, true, cost!(Copper: 25, Lead: 15));
"thermal-generator" => GeneratorBlock::new(2, true, cost!(Copper: 40, Lead: 50, Metaglass: 40, Graphite: 35, Silicon: 35));
"steam-generator" => GeneratorBlock::new(2, true, cost!(Copper: 35, Lead: 40, Graphite: 25, Silicon: 30));
@@ -23,8 +27,8 @@ make_register! {
"rtg-generator" => GeneratorBlock::new(2, true, cost!(Lead: 100, Thorium: 50, Silicon: 75, Plastanium: 75, PhaseFabric: 25));
"solar-panel" => GeneratorBlock::new(1, true, cost!(Lead: 10, Silicon: 15));
"solar-panel-large" => GeneratorBlock::new(3, true, cost!(Lead: 80, Silicon: 110, PhaseFabric: 15));
- "thorium-reactor" => GeneratorBlock::new(3, true, cost!(Lead: 300, Metaglass: 50, Graphite: 150, Thorium: 150, Silicon: 200));
- "impact-reactor" => GeneratorBlock::new(4, true,
+ "thorium-reactor" => NuclearGeneratorBlock::new(3, true, cost!(Lead: 300, Metaglass: 50, Graphite: 150, Thorium: 150, Silicon: 200));
+ "impact-reactor" => ImpactReactorBlock::new(4, true,
cost!(Lead: 500, Metaglass: 250, Graphite: 400, Thorium: 100, Silicon: 300, SurgeAlloy: 250));
"beam-node" => ConnectorBlock::new(1, true, cost!(Beryllium: 8), 4);
"beam-tower" => ConnectorBlock::new(3, true, cost!(Beryllium: 30, Oxide: 10, Silicon: 10), 12);
@@ -32,13 +36,14 @@ make_register! {
"chemical-combustion-chamber" => GeneratorBlock::new(3, true, cost!(Graphite: 40, Tungsten: 40, Oxide: 40, Silicon: 30));
"pyrolysis-generator" => GeneratorBlock::new(3, true, cost!(Graphite: 50, Carbide: 50, Oxide: 60, Silicon: 50));
"flux-reactor" => GeneratorBlock::new(5, true, cost!(Graphite: 300, Carbide: 200, Oxide: 100, Silicon: 600, SurgeAlloy: 300));
- "neoplasia-reactor" => GeneratorBlock::new(5, true, cost!(Tungsten: 1000, Carbide: 300, Oxide: 150, Silicon: 500, PhaseFabric: 300, SurgeAlloy: 200));
+ "neoplasia-reactor" => HeaterGeneratorBlock::new(5, true, cost!(Tungsten: 1000, Carbide: 300, Oxide: 150, Silicon: 500, PhaseFabric: 300, SurgeAlloy: 200));
// editor only
"beam-link" => ConnectorBlock::new(3, true, &[], 12);
// sandbox only
"power-source" => ConnectorBlock::new(1, true, &[], 100);
"power-void" => GeneratorBlock::new(1, true, &[]);
}
+
pub struct ConnectorBlock {
size: u8,
symmetric: bool,
@@ -205,3 +210,34 @@ impl BlockLogic for LampBlock {
Ok(DynData::Int(u32::from(*state) as i32))
}
}
+
+/// format:
+/// - production efficiency: [`f32`]
+/// - generate time: [`f32`]
+fn read_generator(buff: &mut DataRead) -> Result<(), DataReadError> {
+ buff.skip(8)
+}
+
+/// format:
+/// - call [`read_generator`]
+/// - heat: [`f32`]
+fn read_nuclear(buff: &mut DataRead) -> Result<(), DataReadError> {
+ read_generator(buff)?;
+ buff.skip(4)
+}
+
+/// format:
+/// - call [`read_generator`]
+/// - warmup: [`f32`]
+fn read_impact(buff: &mut DataRead) -> Result<(), DataReadError> {
+ read_generator(buff)?;
+ buff.skip(4)
+}
+
+/// format:
+/// - call [`read_generator`]
+/// - heat: [`f32`]
+fn read_heater(buff: &mut DataRead) -> Result<(), DataReadError> {
+ read_generator(buff)?;
+ buff.skip(4)
+}
diff --git a/src/block/production.rs b/src/block/production.rs
index f5fa47b..d268e8d 100644
--- a/src/block/production.rs
+++ b/src/block/production.rs
@@ -1,6 +1,7 @@
//! the industry part of mindustry
use crate::block::make_register;
use crate::block::simple::{cost, make_simple};
+use crate::data::DataRead;
make_register! {
"cultivator" => ProductionBlock::new(2, true, cost!(Copper: 25, Lead: 25, Silicon: 10));
@@ -21,17 +22,17 @@ make_register! {
"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));
- "incinerator" => ProductionBlock::new(1, true, cost!(Lead: 15, Graphite: 5));
+ "incinerator" => Incinerator::new(1, true, cost!(Lead: 15, Graphite: 5));
"silicon-arc-furnace" => ProductionBlock::new(3, true, cost!(Beryllium: 70, Graphite: 80));
"electrolyzer" => ProductionBlock::new(3, true, cost!(Silicon: 50, Graphite: 40, Beryllium: 130, Tungsten: 80));
"atmospheric-concentrator" => ProductionBlock::new(3, true, cost!(Oxide: 60, Beryllium: 180, Silicon: 150));
"oxidation-chamber" => HeatCrafter::new(3, true, cost!(Tungsten: 120, Graphite: 80, Silicon: 100, Beryllium: 120));
"electric-heater" => HeatCrafter::new(2, false, cost!(Tungsten: 30, Oxide: 30));
"slag-heater" => HeatCrafter::new(3, false, cost!(Tungsten: 50, Oxide: 20, Beryllium: 20));
- "phase-heater" => ProductionBlock::new(2, false, cost!(Oxide: 30, Carbide: 30, Beryllium: 30));
- "heat-redirector" => ProductionBlock::new(3, false, cost!(Tungsten: 10, Graphite: 10));
- "heat-router" => ProductionBlock::new(3, false, cost!(Tungsten: 15, Graphite: 10));
- "slag-incinerator" => ProductionBlock::new(1, true, cost!(Tungsten: 15));
+ "phase-heater" => HeatCrafter::new(2, false, cost!(Oxide: 30, Carbide: 30, Beryllium: 30));
+ "heat-redirector" => HeatConduit::new(3, false, cost!(Tungsten: 10, Graphite: 10));
+ "heat-router" => HeatConduit::new(3, false, cost!(Tungsten: 15, Graphite: 10));
+ "slag-incinerator" => Incinerator::new(1, true, cost!(Tungsten: 15));
"carbide-crucible" => ProductionBlock::new(3, true, cost!(Tungsten: 110, Thorium: 150, Oxide: 60));
// slag centrifuge
"surge-crucible" => ProductionBlock::new(3, true, cost!(Silicon: 100, Graphite: 80, Tungsten: 80, Oxide: 80));
@@ -39,33 +40,32 @@ make_register! {
"phase-synthesizer" => ProductionBlock::new(3, true, cost!(Carbide: 90, Silicon: 100, Thorium: 100, Tungsten: 200));
// heat reactor
// sandbox only
- "heat-source" => ProductionBlock::new(1, false, &[]);
+ "heat-source" => HeatCrafter::new(1, false, &[]);
}
make_simple!(
ProductionBlock,
- |_, _, _, _, _| None,
- |_, _, _, _, _, buff: &mut crate::data::DataRead| {
+ |_, _, _, _, _, _| None,
+ |_, _, _, buff: &mut DataRead| {
// format:
// - progress: `f32`
// - warmup: `f32`
- buff.read_f32()?;
- buff.read_f32()?;
+ buff.skip(8)?;
Ok(())
}
);
make_simple!(
HeatCrafter,
- |_, _, _, _, _| None,
- |_, _, _, _, _, buff: &mut crate::data::DataRead| {
+ |_, _, _, _, _, _| None,
+ |_, _, _, buff: &mut DataRead| {
// format:
// - progress: `f32`
// - warmup: `f32`
// - heat: f32
- buff.read_f32()?;
- buff.read_f32()?;
- buff.read_f32()?;
+ buff.skip(12)?;
Ok(())
}
);
+make_simple!(HeatConduit);
+make_simple!(Incinerator);
diff --git a/src/block/simple.rs b/src/block/simple.rs
index 89417c6..0f87f50 100644
--- a/src/block/simple.rs
+++ b/src/block/simple.rs
@@ -22,8 +22,8 @@ macro_rules! state_impl {
pub(crate) use state_impl;
-/// draw is called with self, category, name, state, context
-/// read is called with self, category, name, reg, entity_mapping, buff
+/// draw is called with self, category, name, state, context, rotation
+/// read is called with build, reg, entity_mapping, buff
macro_rules! make_simple {
($name: ident, $draw: expr, $read: expr, $wants_context: literal) => {
pub struct $name {
@@ -90,8 +90,9 @@ macro_rules! make_simple {
name: &str,
state: Option<&crate::block::State>,
context: Option<&crate::data::renderer::RenderingContext>,
+ rot: crate::block::Rotation,
) -> Option<crate::data::renderer::ImageHolder> {
- $draw(self, category, name, state, context)
+ $draw(self, category, name, state, context, rot)
}
fn want_context(&self) -> bool {
@@ -100,30 +101,32 @@ macro_rules! make_simple {
fn read(
&self,
- category: &str,
- name: &str,
+ build: &mut crate::data::map::Build,
reg: &crate::block::BlockRegistry,
entity_mapping: &crate::data::map::EntityMapping,
buff: &mut crate::data::DataRead,
) -> Result<(), crate::data::ReadError> {
- $read(self, category, name, reg, entity_mapping, buff)
+ $read(build, reg, entity_mapping, buff)
}
}
};
($name: ident, $draw: expr) => {
- crate::block::simple::make_simple!($name, $draw, |_, _, _, _, _, _| Ok(()), false);
+ crate::block::simple::make_simple!($name, $draw, |_, _, _, _| Ok(()), false);
};
($name: ident, $draw: expr, $wants_context: literal) => {
- crate::block::simple::make_simple!($name, $draw, |_, _, _, _, _, _| Ok(()), $wants_context);
+ crate::block::simple::make_simple!($name, $draw, |_, _, _, _| Ok(()), $wants_context);
};
($name: ident, $draw: expr, $read: expr) => {
crate::block::simple::make_simple!($name, $draw, $read, false);
};
+ ($name: ident => $read: expr) => {
+ crate::block::simple::make_simple!($name, |_, _, _, _, _, _| None, $read);
+ };
($name: ident) => {
crate::block::simple::make_simple!(
$name,
- |_, _, _, _, _| None,
- |_, _, _, _, _, _| { Ok(()) },
+ |_, _, _, _, _, _| None,
+ |_, _, _, _| Ok(()),
false
);
};
diff --git a/src/block/turrets.rs b/src/block/turrets.rs
index e3c3cce..fda1225 100644
--- a/src/block/turrets.rs
+++ b/src/block/turrets.rs
@@ -1,47 +1,118 @@
//! idk why its not in the [`crate::block::defense`] module
+use super::simple::make_simple;
+use super::{BlockLogic, Rotation, State};
use crate::block::make_register;
use crate::block::simple::cost;
+use crate::data::{renderer::*, DataRead, ReadError};
+use crate::utils::ImageUtils;
make_register! {
- "duo" => TurretBlock::new(1, true, cost!(Copper: 35));
- "scatter" => TurretBlock::new(2, true, cost!(Copper: 85, Lead: 45));
- "scorch" => TurretBlock::new(1, true, cost!(Copper: 25, Graphite: 22));
- "hail" => TurretBlock::new(1, true, cost!(Copper: 40, Graphite: 17));
- "wave" => TurretBlock::new(2, true, cost!(Copper: 25, Lead: 75, Metaglass: 45));
- "lancer" => TurretBlock::new(2, true, cost!(Copper: 60, Lead: 70, Titanium: 30, Silicon: 60));
- "arc" => TurretBlock::new(1, true, cost!(Copper: 50, Lead: 50));
- "parallax" => TurretBlock::new(2, true, cost!(Graphite: 30, Titanium: 90, Silicon: 120));
- "swarmer" => TurretBlock::new(2, true, cost!(Graphite: 35, Titanium: 35, Silicon: 30, Plastanium: 45));
- "salvo" => TurretBlock::new(2, true, cost!(Copper: 100, Graphite: 80, Titanium: 50));
- "segment" => TurretBlock::new(2, true, cost!(Titanium: 40, Thorium: 80, Silicon: 130, PhaseFabric: 40));
- "tsunami" => TurretBlock::new(3, true, cost!(Lead: 400, Metaglass: 100, Titanium: 250, Thorium: 100));
- "fuse" => TurretBlock::new(3, true, cost!(Copper: 225, Graphite: 225, Thorium: 100));
- "ripple" => TurretBlock::new(3, true, cost!(Copper: 150, Graphite: 135, Titanium: 60));
- "cyclone" => TurretBlock::new(3, true, cost!(Copper: 200, Titanium: 125, Plastanium: 80));
- "foreshadow" => TurretBlock::new(4, true, cost!(Copper: 1000, Metaglass: 600, Silicon: 600, Plastanium: 200, SurgeAlloy: 300));
- "spectre" => TurretBlock::new(4, true, cost!(Copper: 900, Graphite: 300, Thorium: 250, Plastanium: 175, SurgeAlloy: 250));
- "meltdown" => TurretBlock::new(4, true, cost!(Copper: 1200, Lead: 350, Graphite: 300, Silicon: 325, SurgeAlloy: 325));
- "breach" => TurretBlock::new(3, true, cost!(Beryllium: 150, Silicon: 150, Graphite: 250));
- "diffuse" => TurretBlock::new(3, true, cost!(Beryllium: 150, Silicon: 200, Graphite: 200, Tungsten: 50));
- "sublimate" => TurretBlock::new(3, true, cost!(Tungsten: 150, Silicon: 200, Oxide: 40, Beryllium: 400));
- "titan" => TurretBlock::new(4, true, cost!(Tungsten: 250, Silicon: 300, Thorium: 400));
- "disperse" => TurretBlock::new(4, true, cost!(Thorium: 50, Oxide: 150, Silicon: 200, Beryllium: 350));
- "afflict" => TurretBlock::new(4, true, cost!(SurgeAlloy: 100, Silicon: 200, Graphite: 250, Oxide: 40));
- "lustre" => TurretBlock::new(4, true, cost!(Silicon: 250, Graphite: 200, Oxide: 50, Carbide: 90));
- "scathe" => TurretBlock::new(5, true, cost!(Oxide: 200, SurgeAlloy: 400, Silicon: 800, Carbide: 500, PhaseFabric: 300));
- "malign" => TurretBlock::new(5, true, cost!(Carbide: 400, Beryllium: 2000, Silicon: 800, Graphite: 800, PhaseFabric: 300));
- "smite" => TurretBlock::new(5, true, cost!(Oxide: 200, SurgeAlloy: 400, Silicon: 800, Carbide: 500, PhaseFabric: 300));
+ "duo" => ItemTurret::new(1, true, cost!(Copper: 35));
+ "scatter" => ItemTurret::new(2, true, cost!(Copper: 85, Lead: 45));
+ "scorch" => ItemTurret::new(1, true, cost!(Copper: 25, Graphite: 22));
+ "hail" => ItemTurret::new(1, true, cost!(Copper: 40, Graphite: 17));
+ "wave" => Turret::new(2, true, cost!(Copper: 25, Lead: 75, Metaglass: 45));
+ "tsunami" => Turret::new(3, true, cost!(Lead: 400, Metaglass: 100, Titanium: 250, Thorium: 100));
+ "lancer" => Turret::new(2, true, cost!(Copper: 60, Lead: 70, Titanium: 30, Silicon: 60));
+ "arc" => Turret::new(1, true, cost!(Copper: 50, Lead: 50));
+ "parallax" => TractorBeamTurret::new(2, true, cost!(Graphite: 30, Titanium: 90, Silicon: 120));
+ "swarmer" => ItemTurret::new(2, true, cost!(Graphite: 35, Titanium: 35, Silicon: 30, Plastanium: 45));
+ "salvo" => ItemTurret::new(2, true, cost!(Copper: 100, Graphite: 80, Titanium: 50));
+ "segment" => PointDefenseTurret::new(2, true, cost!(Titanium: 40, Thorium: 80, Silicon: 130, PhaseFabric: 40));
+ "fuse" => ItemTurret::new(3, true, cost!(Copper: 225, Graphite: 225, Thorium: 100));
+ "ripple" => ItemTurret::new(3, true, cost!(Copper: 150, Graphite: 135, Titanium: 60));
+ "cyclone" => ItemTurret::new(3, true, cost!(Copper: 200, Titanium: 125, Plastanium: 80));
+ "foreshadow" => ItemTurret::new(4, true, cost!(Copper: 1000, Metaglass: 600, Silicon: 600, Plastanium: 200, SurgeAlloy: 300));
+ "spectre" => ItemTurret::new(4, true, cost!(Copper: 900, Graphite: 300, Thorium: 250, Plastanium: 175, SurgeAlloy: 250));
+ "meltdown" => Turret::new(4, true, cost!(Copper: 1200, Lead: 350, Graphite: 300, Silicon: 325, SurgeAlloy: 325));
+ "breach" => ItemTurret::new(3, true, cost!(Beryllium: 150, Silicon: 150, Graphite: 250));
+ "diffuse" => ItemTurret::new(3, true, cost!(Beryllium: 150, Silicon: 200, Graphite: 200, Tungsten: 50));
+ "sublimate" => ContinousTurret::new(3, true, cost!(Tungsten: 150, Silicon: 200, Oxide: 40, Beryllium: 400));
+ "titan" => ItemTurret::new(4, true, cost!(Tungsten: 250, Silicon: 300, Thorium: 400));
+ "disperse" => ItemTurret::new(4, true, cost!(Thorium: 50, Oxide: 150, Silicon: 200, Beryllium: 350));
+ "afflict" => Turret::new(4, true, cost!(SurgeAlloy: 100, Silicon: 200, Graphite: 250, Oxide: 40));
+ "lustre" => ContinousTurret::new(4, true, cost!(Silicon: 250, Graphite: 200, Oxide: 50, Carbide: 90));
+ "scathe" => ItemTurret::new(4, true, cost!(Oxide: 200, SurgeAlloy: 400, Silicon: 800, Carbide: 500, PhaseFabric: 300));
+ "malign" => Turret::new(5, true, cost!(Carbide: 400, Beryllium: 2000, Silicon: 800, Graphite: 800, PhaseFabric: 300));
+ "smite" => ItemTurret::new(5, true, cost!(Oxide: 200, SurgeAlloy: 400, Silicon: 800, Carbide: 500, PhaseFabric: 300));
}
-use crate::data::renderer::*;
-use crate::utils::ImageUtils;
-crate::block::simple::make_simple!(TurretBlock, |me: &Self, _, name, _, _| {
+fn draw_turret(
+ me: &impl BlockLogic,
+ _: &str,
+ name: &str,
+ _: Option<&State>,
+ _: Option<&RenderingContext>,
+ _: Rotation,
+) -> Option<ImageHolder> {
let path = match name {
"breach" | "diffuse" | "sublimate" | "titan" | "disperse" | "afflict" | "lustre"
- | "scathe" | "malign" | "smite" => format!("bases/reinforced-block-{}", me.size),
- _ => format!("bases/block-{}", me.size),
+ | "scathe" | "malign" | "smite" => format!("bases/reinforced-block-{}", me.get_size()),
+ _ => format!("bases/block-{}", me.get_size()),
};
let mut base = load("turrets", &path).unwrap().value().clone();
base.overlay(load("turrets", name).unwrap().value(), 0, 0);
Some(ImageHolder::from(base))
+}
+
+make_simple!(Turret, draw_turret, |_, _, _, buff: &mut DataRead| {
+ read_turret(buff)
});
+make_simple!(
+ PointDefenseTurret,
+ draw_turret,
+ |_, _, _, buff: &mut DataRead| read_point_defense_turret(buff)
+);
+make_simple!(
+ ContinousTurret,
+ draw_turret,
+ |_, _, _, buff: &mut DataRead| { read_continous_turret(buff) }
+);
+make_simple!(
+ TractorBeamTurret,
+ draw_turret,
+ |_, _, _, buff: &mut DataRead| { read_tractor_beam_turret(buff) }
+);
+make_simple!(ItemTurret, draw_turret, |_, _, _, buff: &mut DataRead| {
+ read_item_turret(buff)
+});
+
+/// format:
+/// - call [`read_turret`]
+/// - iterate [`u8`]
+/// - item: [`u16`] as [`Item`](crate::item::Type)
+/// - amount: [`u16`]
+fn read_item_turret(buff: &mut DataRead) -> Result<(), ReadError> {
+ read_turret(buff)?;
+ for _ in 0..buff.read_u8()? {
+ buff.skip(4)?;
+ }
+ Ok(())
+}
+
+/// format:
+/// - reload: f32
+/// - rotation: f32
+fn read_turret(buff: &mut DataRead) -> Result<(), ReadError> {
+ buff.skip(8)
+}
+
+/// format:
+/// - rotation: [`f32`]
+fn read_point_defense_turret(buff: &mut DataRead) -> Result<(), ReadError> {
+ buff.skip(4)
+}
+
+/// format:
+/// - call [`read_turret`]
+/// - last length: [`f32`]
+fn read_continous_turret(buff: &mut DataRead) -> Result<(), ReadError> {
+ read_turret(buff)?;
+ buff.skip(4)
+}
+
+/// format:
+/// - rotation: [`f32`]
+fn read_tractor_beam_turret(buff: &mut DataRead) -> Result<(), ReadError> {
+ buff.skip(4)
+}
diff --git a/src/block/units.rs b/src/block/units.rs
index 2dcfc94..162744f 100644
--- a/src/block/units.rs
+++ b/src/block/units.rs
@@ -1,6 +1,7 @@
//! unit creation related blocks
use thiserror::Error;
+use super::payload::read_payload_block;
use crate::block::simple::*;
use crate::block::*;
use crate::data::dynamic::DynType;
@@ -34,10 +35,9 @@ use crate::unit;
make_simple!(
ConstructorBlock,
- |me: &Self, _, name, _, context: Option<&RenderingContext>| {
- let ctx = context.unwrap();
+ |me: &Self, _, name, _, _, rot: Rotation| {
let mut base = load("units", name).unwrap().to_owned();
- let times = ctx.rotation.rotated(false).count();
+ let times = rot.rotated(false).count();
{
let out = load(
"payload",
@@ -111,10 +111,12 @@ make_simple!(
}
}
Some(ImageHolder::from(base))
- },
- true
+ }
);
make_simple!(UnitBlock);
+make_simple!(RepairTurret => |_, _, _, buff: &mut DataRead| {
+ buff.skip(4) // rotation: [`f32`]
+});
const GROUND_UNITS: &[unit::Type] = &[unit::Type::Dagger, unit::Type::Crawler, unit::Type::Nova];
const AIR_UNITS: &[unit::Type] = &[unit::Type::Flare, unit::Type::Mono];
@@ -130,8 +132,8 @@ make_register! {
cost!(Lead: 2000, Titanium: 2000, Thorium: 750, Silicon: 1000, Plastanium: 450, PhaseFabric: 600));
"tetrative-reconstructor" => ConstructorBlock::new(9, false,
cost!(Lead: 4000, Thorium: 1000, Silicon: 3000, Plastanium: 600, PhaseFabric: 600, SurgeAlloy: 800));
- "repair-point" => UnitBlock::new(1, true, cost!(Copper: 30, Lead: 30, Silicon: 20));
- "repair-turret" => UnitBlock::new(2, true, cost!(Thorium: 80, Silicon: 90, Plastanium: 60));
+ "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]);
@@ -232,9 +234,9 @@ impl BlockLogic for AssemblerBlock {
_: &str,
name: &str,
_: Option<&State>,
- context: Option<&RenderingContext>,
+ _: Option<&RenderingContext>,
+ rot: Rotation,
) -> Option<ImageHolder> {
- let ctx = context.unwrap();
let mut base = load("units", name).unwrap().to_owned();
let out = load(
"payload",
@@ -244,7 +246,7 @@ impl BlockLogic for AssemblerBlock {
},
)
.unwrap();
- let times = ctx.rotation.rotated(false).count();
+ let times = rot.rotated(false).count();
if times != 0 {
let mut out = out.clone();
out.rotate(times);
@@ -272,8 +274,20 @@ impl BlockLogic for AssemblerBlock {
Some(ImageHolder::from(base))
}
- fn want_context(&self) -> bool {
- true
+ /// format:
+ /// - call [`read_payload_block`]
+ /// - progress: [`f32`]
+ /// - plan: [`u16`]
+ /// - point: ([`f32`], [`f32`]) (maybe [`NaN`](f32::NAN))
+ fn read(
+ &self,
+ _: &mut Build,
+ reg: &BlockRegistry,
+ mapping: &EntityMapping,
+ buff: &mut DataRead,
+ ) -> Result<(), DataReadError> {
+ read_payload_block(reg, mapping, buff)?;
+ buff.skip(14)
}
}
diff --git a/src/block/walls.rs b/src/block/walls.rs
index 0317220..59967ac 100644
--- a/src/block/walls.rs
+++ b/src/block/walls.rs
@@ -6,7 +6,7 @@ use crate::data::renderer::{load, read_with, ImageHolder, TOP};
use tinyrand::{Rand, RandRange, Seeded, StdRand};
use tinyrand_std::clock_seed::ClockSeed;
-make_simple!(WallBlock, |_, _, name, _, _| {
+make_simple!(WallBlock, |_, _, name, _, _, _| {
macro_rules! pick {
($name: literal => load $n: literal) => {{
let mut rand = StdRand::seed(ClockSeed::default().next_u64());
diff --git a/src/data/autotile.rs b/src/data/autotile.rs
index 8413359..3075d4f 100644
--- a/src/data/autotile.rs
+++ b/src/data/autotile.rs
@@ -55,7 +55,6 @@ pub type Cross<'l> = [Option<(&'l Block, Rotation)>; 4];
#[derive(Copy, Clone)]
pub struct RenderingContext<'l> {
pub cross: Cross<'l>,
- pub rotation: Rotation,
pub position: PositionContext,
}
@@ -77,34 +76,6 @@ impl std::fmt::Debug for PositionContext {
}
}
-impl std::fmt::Debug for RenderingContext<'_> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- std::fmt::Display::fmt(self, f)
- }
-}
-
-impl std::fmt::Display for RenderingContext<'_> {
- /// this display impl shows RC<$directions=+own rotation>
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "RC<")?;
- macro_rules! f {
- ($f:expr, $z:expr, $x:literal, $at: expr, $srot: expr) => {
- if let Some((_, rot)) = $z {
- if (rot == $at && rot.mirrored(true, true) != $srot) {
- $f.write_str($x)?;
- }
- }
- };
- }
- f!(f, self.cross[0], "N = ", Rotation::Down, self.rotation);
- f!(f, self.cross[1], "E = ", Rotation::Left, self.rotation);
- f!(f, self.cross[2], "S = ", Rotation::Up, self.rotation);
- f!(f, self.cross[3], "W = ", Rotation::Right, self.rotation);
-
- write!(f, "{:?}>", self.rotation)
- }
-}
-
#[cfg(test)]
fn print_crosses(v: Vec<Cross<'_>>, height: usize) -> String {
let mut s = String::new();
@@ -130,7 +101,7 @@ pub fn tile(
rot: Rotation,
) -> ImageHolder {
rotations2tile(
- mask2rotations(mask(ctx, name), rot),
+ mask2rotations(mask(ctx, rot, name), rot),
category,
subcategory,
name,
@@ -272,10 +243,10 @@ pub fn rotations2tile(
) -> ImageHolder {
let mut p = ImageHolder::from(load(category, &format!("{subcategory}/{name}-{index}")));
flrot(flip, rot, p.borrow_mut());
- ImageHolder::from(p)
+ p
}
-pub fn mask(ctx: &RenderingContext, n: &str) -> U4 {
+pub fn mask(ctx: &RenderingContext, rot: Rotation, n: &str) -> U4 {
macro_rules! c {
($in: expr, $srot: expr, $name: expr, $at: expr) => {{
if let Some((b, rot)) = $in {
@@ -293,10 +264,10 @@ pub fn mask(ctx: &RenderingContext, n: &str) -> U4 {
use Rotation::*;
let mut x = 0b0000;
- x |= 8 * c!(ctx.cross[0], ctx.rotation, n, Down);
- x |= 4 * c!(ctx.cross[1], ctx.rotation, n, Left);
- x |= 2 * c!(ctx.cross[2], ctx.rotation, n, Up);
- x |= c!(ctx.cross[3], ctx.rotation, n, Right);
+ x |= 8 * c!(ctx.cross[0], rot, n, Down);
+ x |= 4 * c!(ctx.cross[1], rot, n, Left);
+ x |= 2 * c!(ctx.cross[2], rot, n, Up);
+ x |= c!(ctx.cross[3], rot, n, Right);
U4::from(x)
}
@@ -440,8 +411,8 @@ fn test_mask() {
height: 10,
},
cross: $cross,
- rotation: dir!($rot),
},
+ dir!($rot),
"conveyor",
)
};
diff --git a/src/data/map.rs b/src/data/map.rs
index 18944a1..04938d8 100644
--- a/src/data/map.rs
+++ b/src/data/map.rs
@@ -14,7 +14,7 @@
//! - string map (`u16` for map len, iterate each, read `utf`)
//! - content header section `<u32>`:
//! - iterate `i8` (should = `8`)'
-//! - the type: `i8` (0: item, block: 1, liquid: 4, status: 5, unit: 6, weather: 7, sector: 9, planet: 13//! - item count: `u1\'6` (item: 22, block: 412, liquid: 11, status: 21, unit: 66, weather: 6, sector: 35, planet: 7)
+//! - the type: `i8` (0: item, block: 1, liquid: 4, status: 5, unit: 6, weather: 7, sector: 9, planet: 13//! - item count: `u16` (item: 22, block: 412, liquid: 11, status: 21, unit: 66, weather: 6, sector: 35, planet: 7)
//! - these types all have their own modules: [`item`], [`content`], [`fluid`], [`modifier`], [`mod@unit`], [`weather`], [`sector`], [`planet`]
//! - iterate `u16`
//! - name: `utf`
@@ -41,7 +41,7 @@
//! - chunk len: `u16`
//! - if block == building:
//! - revision: `i8`
-//! - tile.build.readAll
+//! - [`read`]
//! - else skip `chunk len`
//! - or data
//! - data: `i8`
@@ -74,13 +74,13 @@ use std::ops::{Index, IndexMut};
use thiserror::Error;
use crate::block::content::Type as BlockEnum;
-use crate::block::{environment, Block, BlockRegistry, Rotation};
+use crate::block::{environment, Block, BlockRegistry, Rotation, State};
use crate::data::dynamic::DynSerializer;
use crate::data::renderer::*;
use crate::data::DataRead;
use crate::fluid::Type as Fluid;
use crate::item::{storage::Storage, Type as Item};
-use crate::team::Team;
+use crate::team::{self, Team};
#[cfg(doc)]
use crate::{block::content, data::*, fluid, item, modifier, unit};
@@ -109,6 +109,7 @@ impl<'l> Tile<'l> {
fn set_block(&mut self, block: &'l Block) {
self.build = Some(Build {
block,
+ state: None,
items: Storage::new(),
liquids: Storage::new(),
rotation: Rotation::Up,
@@ -142,9 +143,9 @@ impl<'l> Tile<'l> {
}
pub fn floor_image(&self, context: Option<&RenderingContext>) -> ImageHolder {
- let mut i = self.floor.image(None, context).own();
+ let mut i = self.floor.image(None, context, Rotation::Up).own();
if let Some(ore) = self.ore {
- i.overlay(ore.image(None, context).borrow(), 0, 0);
+ i.overlay(ore.image(None, context, Rotation::Up).borrow(), 0, 0);
}
ImageHolder::from(i)
}
@@ -203,78 +204,180 @@ impl<'l> BlockState<'l> for Option<Tile<'_>> {
}
/// a build on a tile in a map
-#[derive(Debug, Clone)]
pub struct Build<'l> {
pub block: &'l Block,
pub items: Storage<Item>,
pub liquids: Storage<Fluid>,
+ pub state: Option<State>,
// pub health: f32,
pub rotation: Rotation,
pub team: Team,
pub data: i8,
}
-impl Build<'_> {
+impl std::fmt::Debug for Build<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "Build<{block}>", block = self.block.name(),)
+ }
+}
+
+impl Clone for Build<'_> {
+ fn clone(&self) -> Self {
+ Self {
+ block: self.block,
+ items: self.items.clone(),
+ liquids: self.liquids.clone(),
+ state: match self.state {
+ None => None,
+ Some(ref s) => Some(self.block.clone_state(s)),
+ },
+ rotation: self.rotation,
+ team: self.team,
+ data: self.data,
+ }
+ }
+}
+
+impl<'l> Build<'l> {
+ pub fn new(block: &'l Block) -> Build<'l> {
+ Self {
+ block,
+ items: Default::default(),
+ liquids: Default::default(),
+ state: Default::default(),
+ rotation: Rotation::Up,
+ team: team::SHARDED,
+ data: 0,
+ }
+ }
+
pub fn image(&self, context: Option<&RenderingContext>) -> ImageHolder {
- self.block.image(None, context)
+ self.block
+ .image(self.state.as_ref(), context, self.rotation)
+ }
+
+ pub fn name(&self) -> &str {
+ self.block.name()
}
pub fn read(
&mut self,
buff: &mut DataRead<'_>,
- _reg: &BlockRegistry,
- _map: &EntityMapping,
+ reg: &BlockRegistry,
+ map: &EntityMapping,
) -> Result<(), ReadError> {
// health
- let _ = buff.read_f32()?; // 4
- let rot = buff.read_u8()?; // 5
- self.rotation = Rotation::try_from(rot & 127).unwrap_or(Rotation::Up);
- if (rot & 128) == 0 {
- return Err(ReadError::Version(rot & 128));
+ let _ = buff.read_f32()?;
+ let rot = buff.read_i8()? as i16;
+ // team
+ let _ = buff.read_i8()?;
+ self.rotation = Rotation::try_from((rot & 127) as u8).unwrap_or(Rotation::Up);
+ let mut mask = 0;
+ let mut version = 0;
+ if rot & 128 != 0 {
+ version = buff.read_u8()?;
+ if version < 3 {
+ return Err(ReadError::Version(version));
+ }
+ buff.skip(1)?;
+ mask = buff.read_u8()?;
}
- let _t = buff.read_u8()?; // 6
- let _v = buff.read_u8()?; // 7
- let _mask = buff.read_u8()?; // 8
-
- // if (mask & 1) != 0 {
- // self.items.clear();
- // // 10
- // for _ in 0..dbg!(buff.read_u16()?) {
- // let item = buff.read_u16()?;
- // let amount = buff.read_u32()?;
- // if let Ok(item) = Item::try_from(item) {
- // self.items.set(item, amount);
- // }
- // }
- // }
- // if mask & 2 == 0 {
- // let n = buff.read_u16()? as usize;
- // buff.skip((n * 4) + 1)?;
- // }
- // if mask & 4 == 0 {
- // self.liquids.clear();
- // for _ in 0..buff.read_u16()? {
- // let fluid = buff.read_u16()?;
- // let amount = buff.read_f32()?;
- // if let Ok(fluid) = Fluid::try_from(fluid) {
- // self.liquids.set(fluid, (amount * 100.0) as u32);
- // }
- // }
- // }
+ const ITEMS: u8 = 1;
+ const POWER: u8 = 2;
+ const LIQUIDS: u8 = 4;
+
+ if mask & ITEMS != 0 {
+ read_items(buff, &mut self.items)?;
+ }
+ if mask & POWER != 0 {
+ read_power(buff)?;
+ }
+ if mask & LIQUIDS != 0 {
+ read_liquids(buff, &mut self.liquids)?;
+ }
// "efficiency"?
- // let _ = buff.read_u8()?;
- // let _ = buff.read_u8()?;
- // visible flags
- // let _ = buff.read_i64()?;
+ buff.skip(2)?;
+ if version == 4 {
+ // visible flags for fog
+ buff.skip(4)?;
+ }
+ // "overridden by subclasses"
+ self.block.logic.read(self, reg, map, buff)?;
// implementation not complete, simply error, causing the remaining bytes in the chunk to be skipped (TODO finish impl)
Err(ReadError::Version(0x0))
- // "overridden by subclasses"
- // self.block.read(buff, reg, map)?;
// Ok(())
}
}
+/// format:
+/// - iterate [`u16`]
+/// - item: [`u16`] as [`Item`]
+/// - amount: [`u32`]
+///
+fn read_items(from: &mut DataRead, to: &mut Storage<Item>) -> Result<(), ReadError> {
+ to.clear();
+ for _ in 0..from.read_u16()? {
+ let item = from.read_u16()?;
+ let amount = from.read_u32()?;
+ if let Ok(item) = Item::try_from(item) {
+ to.set(item, amount);
+ }
+ }
+ Ok(())
+}
+
+/// format:
+/// - iterate [`u16`]
+/// - liquid: [`u16`] as [`Fluid`]
+/// - amount: [`f32`]
+fn read_liquids(from: &mut DataRead, to: &mut Storage<Fluid>) -> Result<(), ReadError> {
+ to.clear();
+ for _ in 0..from.read_u16()? {
+ let fluid = from.read_u16()?;
+ let amount = from.read_f32()?;
+ if let Ok(fluid) = Fluid::try_from(fluid) {
+ to.set(fluid, (amount * 100.0) as u32);
+ }
+ }
+ Ok(())
+}
+
+/// format:
+/// - iterate [`u16`]
+/// - link: [`i32`]
+/// - status: [`f32`]
+fn read_power(from: &mut DataRead) -> Result<(), ReadError> {
+ let n = from.read_u16()? as usize;
+ from.skip((n + 1) * 4)?;
+ Ok(())
+}
+
+#[test]
+fn test_read_items() {
+ let mut s = Storage::new();
+ read_items(
+ &mut DataRead::new(&[
+ 0, 6, 0, 0, 0, 0, 2, 187, 0, 1, 0, 0, 1, 154, 0, 2, 0, 0, 15, 160, 0, 3, 0, 0, 0, 235,
+ 0, 6, 0, 0, 1, 46, 0, 12, 0, 0, 1, 81, 255, 255,
+ ]),
+ &mut s,
+ )
+ .unwrap();
+ assert!(s.get_total() == 5983);
+}
+
+#[test]
+fn test_read_liquids() {
+ let mut s = Storage::new();
+ read_liquids(
+ &mut DataRead::new(&[0, 1, 0, 0, 67, 111, 247, 126, 255, 255]),
+ &mut s,
+ )
+ .unwrap();
+ assert!(s.get(Fluid::Water) == 23996);
+}
+
/// a map.
/// ## Does not support serialization yet!
#[derive(Debug)]
@@ -473,8 +576,18 @@ impl<'l> Serializer<Map<'l>> for MapSerializer<'l> {
}
if entity {
if central {
+ let mut output = [0u8; 2];
+ output.copy_from_slice(&buff.data[..2]);
+ let n = u16::from_be_bytes(output) as usize;
let _ = buff.read_chunk(false, |buff| {
+ #[cfg(debug_assertions)]
+ println!(
+ "reading {:?} {:?}",
+ map[i].build.as_ref().unwrap(),
+ &buff.data[1..n]
+ );
let _ = buff.read_i8()?;
+
map[i]
.build
.as_mut()
@@ -505,13 +618,15 @@ impl<'l> Serializer<Map<'l>> for MapSerializer<'l> {
})?;
let mut mapping = EntityMapping::new();
buff.read_chunk(true, |buff| {
+ // read entity mapping (SaveVersion.java#436)
for _ in 0..buff.read_u16()? {
- let id = buff.read_i16()? as u8;
+ let id = buff.read_u16()? as u8;
let nam = buff.read_utf()?;
dbg!(nam);
mapping.insert(id, Box::new(Item::Copper));
// mapping.push(content::Type::get_name(nam));
}
+ // read team block plans (ghosts) (SaveVersion.java#389)
for _ in 0..buff.read_u32()? {
buff.skip(4)?;
for _ in 0..buff.read_u32()? {
@@ -519,6 +634,7 @@ impl<'l> Serializer<Map<'l>> for MapSerializer<'l> {
let _ = DynSerializer::deserialize(&mut DynSerializer, buff)?;
}
}
+ // read world entities (#412). eg units
for _ in 0..buff.read_u32()? {
let len = buff.read_u16()? as usize;
let ty = buff.read_u8()?;
@@ -533,6 +649,7 @@ impl<'l> Serializer<Map<'l>> for MapSerializer<'l> {
})?;
// skip custom chunks
buff.skip_chunk()?;
+ println!("desered");
Ok(m.unwrap())
}
diff --git a/src/data/mod.rs b/src/data/mod.rs
index 60380df..6a8c533 100644
--- a/src/data/mod.rs
+++ b/src/data/mod.rs
@@ -22,7 +22,7 @@ pub mod weather;
#[derive(Debug)]
pub struct DataRead<'d> {
- data: &'d [u8],
+ pub(crate) data: &'d [u8],
// used with read_chunk
read: usize,
}
@@ -144,11 +144,18 @@ impl<'d> DataRead<'d> {
Err(e) => {
// skip this chunk
if len < self.read {
- eprintln!("overread ({e:?})");
+ #[cfg(debug_assertions)]
+ panic!("overread; supposed to read {len}; read {}", self.read);
+ #[cfg(not(debug_assertions))]
return Err(e);
}
let n = len - self.read;
if n != 0 {
+ #[cfg(debug_assertions)]
+ println!(
+ "supposed to read {len}; read {} - skipping excess",
+ self.read
+ );
self.skip(n)?;
};
Err(e)
diff --git a/src/data/renderer.rs b/src/data/renderer.rs
index 8672edf..06b2cc5 100644
--- a/src/data/renderer.rs
+++ b/src/data/renderer.rs
@@ -11,6 +11,7 @@ use zip::ZipArchive;
pub(crate) use super::autotile::*;
use crate::block::environment::METAL_FLOOR;
+use crate::block::Rotation;
use crate::team::SHARDED;
pub(crate) use crate::utils::ImageUtils;
use crate::Map;
@@ -107,8 +108,13 @@ pub(crate) fn load(category: &str, name: &str) -> Option<Ref<'static, PathBuf, R
})
}
+#[cfg(not(unix))]
+const P: &str = "target/out";
+#[cfg(unix)]
+const P: &str = "/tmp/mindus-tmp";
+
fn load_raw(f: impl AsRef<Path>) -> Option<RgbaImage> {
- let f = std::fs::File::open(Path::new("target/out").join(f)).ok()?;
+ let f = std::fs::File::open(Path::new(P).join(f)).ok()?;
let r = PngDecoder::new(BufReader::new(f)).unwrap();
let p = DynamicImage::from_decoder(r).unwrap().into_rgba8();
assert!(p.width() != 0);
@@ -117,12 +123,12 @@ fn load_raw(f: impl AsRef<Path>) -> Option<RgbaImage> {
}
fn load_zip() {
- if !Path::new("target/out").exists() {
+ if !Path::new(P).exists() {
let mut zip = ZipArchive::new(Cursor::new(
include_bytes!(concat!(env!("OUT_DIR"), "/asset")).to_vec(),
))
.unwrap();
- zip.extract("target/out").unwrap();
+ zip.extract(P).unwrap();
}
}
pub const TOP: &str = "-top";
@@ -180,7 +186,7 @@ impl Renderable for Schematic<'_> {
((self.width + 2) * 32) as u32,
((self.height + 2) * 32) as u32,
);
- bg.repeat(METAL_FLOOR.image(None, None).borrow());
+ bg.repeat(METAL_FLOOR.image(None, None, Rotation::Up).borrow());
let mut canvas = RgbaImage::new(
((self.width + 2) * 32) as u32,
((self.height + 2) * 32) as u32,
@@ -194,7 +200,6 @@ impl Renderable for Schematic<'_> {
};
Some(RenderingContext {
cross: self.cross(&pctx),
- rotation: tile.rot,
position: pctx,
})
} else {
@@ -205,7 +210,8 @@ impl Renderable for Schematic<'_> {
let x = x as u32 - ((tile.block.get_size() - 1) / 2) as u32;
let y = self.height as u32 - y as u32 - ((tile.block.get_size() / 2) + 1) as u32;
canvas.overlay(
- tile.image(ctx.as_ref()).borrow(),
+ tile.image(ctx.as_ref(), tile.get_rotation().unwrap_or(Rotation::Up))
+ .borrow(),
(x + 1) * 32,
(y + 1) * 32,
);
@@ -259,7 +265,6 @@ impl Renderable for Map<'_> {
};
let rctx = RenderingContext {
cross: self.cross(j, &pctx),
- rotation: build.rotation,
position: pctx,
};
Some(rctx)
@@ -306,13 +311,13 @@ fn all_blocks() {
None,
Some(&RenderingContext {
cross: [None; 4],
- rotation: crate::block::Rotation::Up,
position: PositionContext {
position: GridPos(0, 0),
width: 5,
height: 5,
},
}),
+ Rotation::Up,
);
}
}
diff --git a/src/data/schematic.rs b/src/data/schematic.rs
index 5255c4d..5c34bcb 100644
--- a/src/data/schematic.rs
+++ b/src/data/schematic.rs
@@ -59,8 +59,8 @@ impl<'l> Placement<'l> {
}
/// draws this placement in particular
- pub fn image(&self, context: Option<&RenderingContext>) -> ImageHolder {
- self.block.image(self.get_state(), context)
+ pub fn image(&self, context: Option<&RenderingContext>, rot: Rotation) -> ImageHolder {
+ self.block.image(self.get_state(), context, rot)
}
/// set the state
diff --git a/src/lib.rs b/src/lib.rs
index 776e79d..59698ff 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,9 +11,14 @@ mod registry;
mod team;
pub mod unit;
mod utils;
-pub use block::build_registry;
-pub use data::dynamic::DynData;
-pub use data::map::{Map, MapSerializer};
-pub use data::renderer::Renderable;
-pub use data::schematic::{Schematic, SchematicSerializer};
-pub use data::Serializer;
+#[doc(inline)]
+pub use {
+ block::build_registry,
+ data::{
+ dynamic::DynData,
+ map::{Map, MapSerializer},
+ renderer::Renderable,
+ schematic::{Schematic, SchematicSerializer},
+ Serializer,
+ },
+};