mindustry logic execution, map- and schematic- parsing and rendering
Diffstat (limited to 'src/data/map.rs')
| -rw-r--r-- | src/data/map.rs | 344 |
1 files changed, 214 insertions, 130 deletions
diff --git a/src/data/map.rs b/src/data/map.rs index 4b1e5bd..5431d01 100644 --- a/src/data/map.rs +++ b/src/data/map.rs @@ -1,6 +1,6 @@ //! the map module //! ### format -//! note: utf = `len<u16>` + utf8(read(len)) +//! note: utf = `len<u16>` + `utf8(read(len))` //! //! note: each section has a `u32` denoting its length //! @@ -12,10 +12,10 @@ //! - tag section `<u32>` //! - 1 byte of idk (skip) //! - string map (`u16` for map len, iterate each, read `utf`) -//! - 4 bytes of idk (skip) //! - 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) -//! - these types all have their own modules: [`crate::item`], [`crate::block::content`], [`crate::fluid`], [`crate::modifier`], [`crate::unit`], [`crate::data::weather`], [`crate::data::sector`], [`crate::data::planet`] +//! - 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) +//! - these types all have their own modules: [`item`], [`content`], [`fluid`], [`modifier`], [`mod@unit`], [`weather`], [`sector`], [`planet`] //! - iterate `u16` //! - name: `utf` //! - map section `<u32>` @@ -59,7 +59,7 @@ //! - team = `team#<u32>` //! - iterate `plans<u32>` //! - x: `u16`, y: `u16`, rot: `u16`, id: `u16` -//! - o: `DynData` (refer to [crate::data::dynamic::DynSerializer]) +//! - o: `DynData` (refer to [`DynSerializer`]) //! - world entities //! - iterate `u32` //! - len: `u16` @@ -70,33 +70,42 @@ //! - id: `u32` //! - entity read use std::collections::HashMap; +use std::ops::{Index, IndexMut}; use thiserror::Error; use crate::block::content::Type as BlockEnum; -use crate::block::{Block, BlockRegistry, Rotation}; +use crate::block::{environment, Block, BlockRegistry, Rotation}; use crate::data::dynamic::DynSerializer; use crate::data::renderer::*; use crate::data::DataRead; use crate::fluid::Type as Fluid; -use crate::item::storage::Storage; -use crate::item::Type as Item; +use crate::item::{storage::Storage, Type as Item}; use crate::team::Team; +#[allow(unused_imports)] +use crate::{block::content, data::*, fluid, item, modifier, unit}; -use super::GridPos; use super::Serializer; use crate::content::Content; use crate::utils::image::ImageUtils; /// a tile in a map +#[derive(Clone)] pub struct Tile<'l> { - pub pos: GridPos, pub floor: &'l Block, pub ore: Option<&'l Block>, - pub build: Option<Build<'l>>, + build: Option<Build<'l>>, } pub type EntityMapping = HashMap<u8, Box<dyn Content>>; impl<'l> Tile<'l> { + pub fn new(floor: &'l Block, ore: Option<&'l Block>) -> Self { + Self { + floor, + ore, + build: None, + } + } + fn set_block(&mut self, block: &'l Block) { self.build = Some(Build { block, @@ -108,6 +117,10 @@ impl<'l> Tile<'l> { }); } + pub fn build(&self) -> Option<&Build<'l>> { + self.build.as_ref() + } + /// check if this tile contains a building. pub fn has_building(&self) -> bool { if let Some(b) = &self.build { @@ -128,14 +141,14 @@ impl<'l> Tile<'l> { 1 } - pub fn image(&self) -> ImageHolder { + pub fn image(&self, context: Option<&RenderingContext>) -> ImageHolder { // building covers floore let i = if let Some(b) = &self.build { - b.image() + b.image(context) } else { - let mut i = self.floor.image(None).own(); + let mut i = self.floor.image(None, context).own(); if let Some(ore) = self.ore { - i.overlay(ore.image(None).borrow(), 0, 0); + i.overlay(ore.image(None, context).borrow(), 0, 0); } ImageHolder::from(i) }; @@ -147,14 +160,12 @@ impl std::fmt::Debug for Tile<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "Tile<{},{}>@{}+{}{}", - self.pos.0, - self.pos.1, + "Tile@{}{}{}", self.floor.name(), if let Some(ore) = &self.ore { - ore.name() + format!("+{}", ore.name()) } else { - "" + "".into() }, if let Some(build) = &self.build { format!(":{}", build.block.name()) @@ -165,8 +176,32 @@ impl std::fmt::Debug for Tile<'_> { } } +impl<'l> BlockState<'l> for Tile<'l> { + fn get_block(&self) -> Option<&'l Block> { + Some(self.build()?.block) + } +} + +impl RotationState for Tile<'_> { + fn get_rotation(&self) -> Option<Rotation> { + Some(self.build()?.rotation) + } +} + +impl RotationState for Option<Tile<'_>> { + fn get_rotation(&self) -> Option<Rotation> { + self.as_ref().unwrap().get_rotation() + } +} + +impl<'l> BlockState<'l> for Option<Tile<'_>> { + fn get_block(&'l self) -> Option<&'l Block> { + self.as_ref().unwrap().get_block() + } +} + /// a build on a tile in a map -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Build<'l> { pub block: &'l Block, pub items: Storage<Item>, @@ -178,79 +213,163 @@ pub struct Build<'l> { } impl Build<'_> { - pub fn image(&self) -> ImageHolder { - self.block.image(None) + pub fn image(&self, context: Option<&RenderingContext>) -> ImageHolder { + self.block.image(None, context) } 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 = dbg!(buff.read_u8()?); // 5 + 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 _t = dbg!(buff.read_u8()?); // 6 - let _v = dbg!(buff.read_u8()?); // 7 - let mask = dbg!(buff.read_u8()?); // 8 - if dbg!((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); - } - } - } + 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); + // } + // } + // } // "efficiency"? - let _ = buff.read_u8()?; - let _ = buff.read_u8()?; + // let _ = buff.read_u8()?; + // let _ = buff.read_u8()?; // visible flags - let _ = buff.read_i64()?; - // "overriden by subclasses" - self.block.read(buff, reg, map)?; - Ok(()) + // let _ = buff.read_i64()?; + // 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(()) } } -/// a map +/// a map. +/// ## Does not support serialization yet! #[derive(Debug)] pub struct Map<'l> { - pub width: u32, - pub height: u32, + pub width: usize, + pub height: usize, pub tags: HashMap<String, String>, + /// row major 2d array + /// ```rs + /// (0, 0), (1, 0), (2, 0) + /// (0, 1), (1, 1), (2, 1) + /// (0, 2), (1, 2), (2, 2) + /// ``` pub tiles: Vec<Tile<'l>>, } +macro_rules! cond { + ($cond: expr, $do: expr) => { + if $cond { + None + } else { + $do + } + }; +} + +impl<'l> Crossable for Map<'l> { + // N + // cond!(pos.position.1 >= (pos.height - 1) as u16, get(j + 1)), + // // E + // cond!( + // pos.position.0 >= (pos.height - 1) as u16, + // get(j + pos.height) + // ), + // // S + // cond!( + // pos.position.1 == 0 || pos.position.1 >= pos.height as u16, + // cond!(pos.position.1 >= (pos.height - 1), get(j + 1)), + // // E + // cond!(pos.position.0 >= (pos.height - 1), get(j + pos.height)), + // // S + // cond!( + // pos.position.1 == 0 || pos.position.1 >= pos.height, + // get(j - 1) + // ), + // // W + // cond!(j < pos.height, get(j - pos.height)), + fn cross(&self, j: usize, c: &PositionContext) -> Cross { + let get = |i| { + let b = &self[i]; + Some((b.get_block()?, b.get_rotation()?)) + }; + [ + cond![ + c.position.1 == 0 || c.position.1 >= c.height, + get(j + self.height) + ], + cond![c.position.0 >= (c.height - 1), get(j + 1)], + cond![c.position.1 >= (c.height - 1), get(j - self.width)], + cond![j < c.height, get(j - 1)], + ] + } +} + +impl<'l> Map<'l> { + pub fn new(width: usize, height: usize, tags: HashMap<String, String>) -> Self { + Self { + tiles: vec![Tile::new(&environment::STONE, None); width * height], + height, + width, + tags, + } + } +} + +impl<'l> Index<usize> for Map<'l> { + type Output = Tile<'l>; + fn index(&self, index: usize) -> &Self::Output { + &self.tiles[index] + } +} + +impl<'l> IndexMut<usize> for Map<'l> { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.tiles[index] + } +} + const MAP_HEADER: [u8; 4] = [b'M', b'S', b'A', b'V']; -/// error ocurring when reading a map fails +/// error occurring when reading a map fails #[derive(Debug, Error)] pub enum ReadError { #[error("failed to read from buffer")] Read(#[from] super::ReadError), + #[error(transparent)] + Decompress(#[from] super::DecompressError), #[error("incorrect header ({0:?})")] Header([u8; 4]), #[error("unsupported version ({0})")] @@ -286,7 +405,7 @@ impl<'l> Serializer<Map<'l>> for MapSerializer<'l> { return Err(ReadError::Version(version.try_into().unwrap_or(0))); } let mut tags = HashMap::new(); - buff.read_chunk(|buff| { + buff.read_chunk(true, |buff| { buff.skip(1)?; for _ in 0..buff.read_u8()? { let key = buff.read_utf()?; @@ -295,71 +414,40 @@ impl<'l> Serializer<Map<'l>> for MapSerializer<'l> { } Ok::<(), super::ReadError>(()) })?; - buff.read_chunk(|buff| { - // we skip these (just keep the respective modules updated) - for _ in 0..buff.read_i8()? { - // let _ty = buff.read_u8()?; - // for _ in 0..buff.read_i16()? { - // let name = dbg!(buff.read_utf()?); - // } - buff.skip(1)?; - for _ in 0..buff.read_u16()? { - let n = buff.read_u16()?; - buff.skip(n as usize)?; - } - } - Ok::<(), super::ReadError>(()) - })?; - - // map section + // we skip the content header (just keep the respective modules updated) + buff.skip_chunk()?; + // map section let mut w = 0; let mut h = 0; - let mut tiles = vec![]; - buff.read_chunk(|buff| { - w = buff.read_u16()? as u32; - h = buff.read_u16()? as u32; + let mut m = None; + buff.read_chunk(true, |buff| { + w = buff.read_u16()? as usize; + h = buff.read_u16()? as usize; + let mut map = Map::new(w, h, tags); let count = w * h; let mut i = 0; while i < count { - let x = (i % w) as u16; - let y = (i / w) as u16; let floor_id = buff.read_u16()?; let overlay_id = buff.read_u16()?; let floor = BlockEnum::try_from(floor_id) .unwrap_or(BlockEnum::Stone) .to(self.0) - .unwrap_or(&crate::block::environment::STONE); + .unwrap_or(&environment::STONE); let ore = BlockEnum::try_from(overlay_id) .unwrap_or(BlockEnum::Air) .to(self.0); - debug_assert!( - x < w as u16 && y < h as u16, - "{x} or {y} out of bounds ({floor:?} {ore:?})" - ); - tiles.push(Tile { - floor, - ore, - pos: GridPos(x, y), - build: None, - }); - let consecutives = buff.read_u8()? as u32; + map[i] = Tile::new(floor, ore); + let consecutives = buff.read_u8()? as usize; if consecutives > 0 { for i in (i + 1)..(i + 1 + consecutives) { - let x = (i % w) as u16; - let y = (i / w) as u16; - tiles.push(Tile { - floor, - ore, - pos: GridPos(x, y), - build: None, - }) + map[i] = Tile::new(floor, ore) } i += consecutives; } i += 1; } let mut i = 0usize; - while i < count as usize { + while i < count { let block_id = buff.read_u16()?; let packed = buff.read_u8()?; let entity = (packed & 1) != 0; @@ -378,30 +466,30 @@ impl<'l> Serializer<Map<'l>> for MapSerializer<'l> { }; if central { if let Some(block) = block { - tiles[i].set_block(block); + map[i].set_block(block); } } if entity { if central { - // TODO: actually read - let n = buff.read_u16()?; - buff.skip(n as usize)?; - // let _ = buff.read_i8()?; - // tiles[i] - // .build - // .as_mut() - // .unwrap() - // // map not initialized yet - // .read(&mut buff, self.0, &HashMap::new())?; + let _ = buff.read_chunk(false, |buff| { + let _ = buff.read_i8()?; + map[i] + .build + .as_mut() + .unwrap() + // map not initialized yet + .read(buff, self.0, &HashMap::new())?; + Ok::<(), ReadError>(()) + }); } } else if data { if let Some(block) = block { - tiles[i].set_block(block); + map[i].set_block(block); } - tiles[i].build.as_mut().unwrap().data = buff.read_i8()?; + map[i].build.as_mut().unwrap().data = buff.read_i8()?; } else { let consecutives = buff.read_u8()? as usize; - for tile in tiles.iter_mut().take(consecutives).skip(i + 1) { + for tile in map.tiles.iter_mut().take(consecutives).skip(i + 1) { if let Some(block) = block { tile.set_block(block); } @@ -410,10 +498,11 @@ impl<'l> Serializer<Map<'l>> for MapSerializer<'l> { } i += 1 } + m = Some(map); Ok::<(), ReadError>(()) })?; let mut mapping = EntityMapping::new(); - buff.read_chunk(|buff| { + buff.read_chunk(true, |buff| { for _ in 0..buff.read_u16()? { let id = buff.read_i16()? as u8; let nam = buff.read_utf()?; @@ -442,12 +531,7 @@ impl<'l> Serializer<Map<'l>> for MapSerializer<'l> { })?; // skip custom chunks buff.skip_chunk()?; - Ok(Map { - width: w, - height: h, - tags, - tiles, - }) + Ok(m.unwrap()) } /// serialize a map (todo) |