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
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | src/block/defense.rs | 18 | ||||
| -rw-r--r-- | src/block/distribution.rs | 208 | ||||
| -rw-r--r-- | src/block/drills.rs | 38 | ||||
| -rw-r--r-- | src/block/environment.rs | 2 | ||||
| -rw-r--r-- | src/block/liquid.rs | 25 | ||||
| -rw-r--r-- | src/block/logic.rs | 226 | ||||
| -rw-r--r-- | src/block/mod.rs | 29 | ||||
| -rw-r--r-- | src/block/payload.rs | 163 | ||||
| -rw-r--r-- | src/block/power.rs | 48 | ||||
| -rw-r--r-- | src/block/production.rs | 30 | ||||
| -rw-r--r-- | src/block/simple.rs | 23 | ||||
| -rw-r--r-- | src/block/turrets.rs | 137 | ||||
| -rw-r--r-- | src/block/units.rs | 38 | ||||
| -rw-r--r-- | src/block/walls.rs | 2 | ||||
| -rw-r--r-- | src/data/autotile.rs | 45 | ||||
| -rw-r--r-- | src/data/map.rs | 221 | ||||
| -rw-r--r-- | src/data/mod.rs | 11 | ||||
| -rw-r--r-- | src/data/renderer.rs | 21 | ||||
| -rw-r--r-- | src/data/schematic.rs | 4 | ||||
| -rw-r--r-- | src/lib.rs | 17 |
21 files changed, 915 insertions, 393 deletions
@@ -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), + ®, + &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), + ®, + &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 @@ -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, + }, +}; |