mindustry logic execution, map- and schematic- parsing and rendering
Diffstat (limited to 'src/unit/mod.rs')
-rw-r--r--src/unit/mod.rs217
1 files changed, 215 insertions, 2 deletions
diff --git a/src/unit/mod.rs b/src/unit/mod.rs
index 603b376..f96f5a3 100644
--- a/src/unit/mod.rs
+++ b/src/unit/mod.rs
@@ -2,10 +2,17 @@
//!
//! [source](https://github.com/Anuken/Mindustry/blob/master/core/src/mindustry/content/UnitTypes.java)
use crate::content::content_enum;
+use crate::data::command::UnitCommand;
+use crate::data::dynamic::DynSerializer;
+use crate::data::entity_mapping::UnitClass;
+use crate::data::{DataRead, ReadError};
+use crate::item::Type as Item;
+use crate::modifier::Type as Status;
+use crate::team::Team;
+use crate::Serializer;
content_enum! {
- pub enum Type / Unit for u16 | TryFromU16Error
- {
+ pub enum Type / Unit for u16 | TryFromU16Error {
"dagger",
"mace",
"fortress",
@@ -73,3 +80,209 @@ content_enum! {
"scathe-missile",
}
}
+
+#[derive(Default, Debug)]
+pub struct UnitState {
+ pub ammo: f32,
+ pub elevation: f32,
+ pub flag: i64,
+ pub health: f32,
+ pub is_shooting: bool,
+ pub rotation: f32,
+ pub shield: f32,
+ pub stack: (Option<Item>, u32),
+ /// how many status can you realistically be afflicted with
+ pub status: [Status; 3],
+ pub team: Team,
+ pub velocity: (f32, f32),
+ pub position: (f32, f32),
+ pub controller: Controller,
+}
+
+#[derive(Default, Debug)]
+pub enum Controller {
+ Player(i32),
+ Logic(i32),
+ Command {
+ target: Option<i32>,
+ pos: Option<(f32, f32)>,
+ command: Option<UnitCommand>,
+ },
+ #[default]
+ Assembler,
+}
+
+impl Controller {
+ /// format:
+ /// - match [`u8`]
+ /// - (0): player
+ /// - player: [`i32`]
+ ///
+ /// - (3): logic ai
+ /// - position: [`i32`]
+ ///
+ /// - (6): command
+ /// - has_attack: [`bool`]
+ /// - has_pos: [`bool`]
+ /// - if `has_pos`:
+ /// - pos: ([`f32`], [`f32`])
+ /// - if `has_attack`:
+ /// - ty: [`i8`]
+ /// - target_id: [`i32`]
+ /// - command: [`i8`] attempt as [`UnitCommand`]
+ /// - (_): assembler
+ fn read(buff: &mut DataRead) -> Result<Controller, ReadError> {
+ Ok(match buff.read_u8()? {
+ 0 => Controller::Player(buff.read_i32()?),
+ 3 => Controller::Logic(buff.read_i32()?),
+ 6 => {
+ let has_attack = buff.read_bool()?;
+ let pos = if buff.read_bool()? {
+ Some((buff.read_f32()?, buff.read_f32()?))
+ } else {
+ None
+ };
+ let target = if has_attack {
+ buff.skip(1)?;
+ Some(buff.read_i32()?)
+ } else {
+ None
+ };
+ let n = buff.read_i8()?;
+ let command = if let Ok(n) = u8::try_from(n) && let Ok(u) = UnitCommand::try_from(n) {
+ Some(u)
+ } else {
+ None
+ };
+ Controller::Command {
+ target,
+ pos,
+ command,
+ }
+ }
+ _ => Controller::Assembler,
+ })
+ }
+}
+
+#[derive(Debug)]
+pub struct Unit {
+ pub state: UnitState,
+ pub ty: Type,
+}
+
+impl UnitClass {
+ pub fn read(self, buff: &mut DataRead) -> Result<Unit, ReadError> {
+ buff.skip(2)?;
+ let mut state = UnitState::default();
+ read_abilities(buff)?;
+ state.ammo = buff.read_f32()?;
+ state.controller = Controller::read(buff)?;
+ state.elevation = buff.read_f32()?;
+ state.flag = buff.read_i64()?;
+ state.health = buff.read_f32()?;
+ state.is_shooting = buff.read_bool()?;
+ dbg!(&state);
+ read_tile(buff)?;
+ read_mounts(buff)?;
+ read_plans(buff)?;
+ state.rotation = buff.read_f32()?;
+ state.shield = buff.read_f32()?;
+ buff.skip(1)?; // spawned_by_core
+ state.stack = read_stack(buff)?;
+ state.status = read_status(buff)?;
+ state.team = Team::of(buff.read_u8()?);
+ let ty = Type::try_from(buff.read_u16()?).unwrap();
+ buff.skip(1)?; // update_building
+ state.velocity = (buff.read_f32()?, buff.read_f32()?);
+ state.position = (buff.read_f32()?, buff.read_f32()?);
+
+ Ok(Unit { state, ty })
+ }
+}
+
+/// format:
+/// - iterate [u8]
+/// - ability: [`f32`]
+fn read_abilities(buff: &mut DataRead) -> Result<(), ReadError> {
+ let n = buff.read_u8()? as usize;
+ buff.skip(n * 4)
+}
+
+/// format:
+/// - tile: [`i32`]
+fn read_tile(buff: &mut DataRead) -> Result<(), ReadError> {
+ buff.skip(4)
+}
+
+/// format:
+/// - iterate [`u8`]
+/// - state: [`u8`]
+/// - x aim: [`f32`]
+/// - y aim: [`f32`]
+fn read_mounts(buff: &mut DataRead) -> Result<(), ReadError> {
+ let n = dbg!(buff.read_u8()? as usize);
+ buff.skip(n * 9)
+}
+
+/// format:
+/// - plan count: [`i32`]
+/// - plan_count == -1 => return
+/// - iterate `plan_count`
+/// - call [`read_plan`]
+fn read_plans(buff: &mut DataRead) -> Result<(), ReadError> {
+ let used = dbg!(buff.read_i32()?);
+ if used == -1 {
+ return Ok(());
+ }
+ for _ in 0..used {
+ read_plan(buff)?;
+ }
+ Ok(())
+}
+
+/// format:
+/// - ty: [`u8`]
+/// - position: [`i32`]
+/// - if ty != 1
+/// - block: [`u16`]
+/// - rotation: [`i8`]
+/// - has_config: [`bool`]
+/// - config: [`DynData`](crate::data::dynamic::DynData)
+fn read_plan(buff: &mut DataRead) -> Result<(), ReadError> {
+ let ty = buff.read_u8()?;
+ buff.skip(4)?;
+ if ty != 1 {
+ buff.skip(4)?;
+ let _ = DynSerializer.deserialize(buff).unwrap();
+ }
+ Ok(())
+}
+
+/// format:
+/// - item: [`i16`] attempt into [`Item`]
+/// - count: [`u32`]
+fn read_stack(buff: &mut DataRead) -> Result<(Option<Item>, u32), ReadError> {
+ let n = buff.read_i16()?;
+ Ok((
+ (n != -1).then(|| Item::try_from(n as u16).unwrap()),
+ buff.read_u32()?,
+ ))
+}
+
+/// read the status.
+/// i take only 3
+///
+/// format:
+/// - iterate [`i32`]
+/// - status: [`u16`] attempt into [`Status`]
+fn read_status(buff: &mut DataRead) -> Result<[Status; 3], ReadError> {
+ let mut status = [Status::None, Status::None, Status::None];
+ for i in 0..buff.read_i32()? {
+ let this = Status::try_from(buff.read_u16()?).unwrap_or_default();
+ if i < 3 {
+ status[i as usize] = this;
+ }
+ }
+ Ok(status)
+}