1use crate::Serializable;
6use crate::block::payload::read_payload;
7use crate::content::content_enum;
8use crate::data::command::UnitCommand;
9use crate::data::dynamic::DynData;
10use crate::data::entity_mapping::UnitClass;
11use crate::data::{DataRead, ReadError};
12use crate::item::Type as Item;
13use crate::modifier::Type as Status;
14use crate::team::Team;
15use crate::utils::ImageHolder;
16
17macro_rules! units {
18 ($($unit:literal,)+ $(,)?) => { paste::paste! {
19 content_enum! (pub enum Type / Unit for u16 | TryFromU16Error {
20 $($unit,)+
21 });
22
23 impl Type {
24 fn draw(self, s: crate::data::renderer::Scale) -> ImageHolder<4> {
25 match self {
26 $(Type::[<$unit:camel>] => units!(@help $unit + s),)+
27 }
28 }
29 }
30 } };
31 (@help "block" + $s:expr) => { crate::data::renderer::load!("empty4", $s) };
32 (@help $v:literal + $s:expr) => { crate::data::renderer::load!($v, $s) };
33}
34
35units! {
36 "dagger",
37 "mace",
38 "fortress",
39 "scepter",
40 "reign",
41 "nova",
42 "pulsar",
43 "quasar",
44 "vela",
45 "corvus",
46 "crawler",
47 "atrax",
48 "spiroct",
49 "arkyid",
50 "toxopid",
51 "flare",
52 "horizon",
53 "zenith",
54 "antumbra",
55 "eclipse",
56 "mono",
57 "poly",
58 "mega",
59 "quad",
60 "oct",
61 "risso",
62 "minke",
63 "bryde",
64 "sei",
65 "omura",
66 "retusa",
67 "oxynoe",
68 "cyerce",
69 "aegires",
70 "navanax",
71 "alpha",
72 "beta",
73 "gamma",
74 "stell",
75 "locus",
76 "precept",
77 "vanquish",
78 "conquer",
79 "merui",
80 "cleroi",
81 "anthicus",
82 "anthicus-missile",
83 "tecta",
84 "collaris",
85 "elude",
86 "avert",
87 "obviate",
88 "quell",
89 "quell-missile",
90 "disrupt",
91 "disrupt-missile",
92 "renale",
93 "latum",
94 "evoke",
95 "incite",
96 "emanate",
97 "block",
98 "manifold",
99 "assembly-drone",
100 "scathe-missile",
101}
102
103#[derive(Default, Debug)]
104pub struct UnitState {
105 pub ammo: f32,
106 pub elevation: f32,
107 pub flag: i64,
108 pub health: f32,
109 pub is_shooting: bool,
110 pub rotation: f32,
111 pub shield: f32,
112 pub stack: (Option<Item>, u32),
113 pub status: [Status; 3],
115 pub team: Team,
116 pub velocity: (f32, f32),
117 pub position: (f32, f32),
118 pub controller: Controller,
119}
120pub trait TryThen {
146 fn try_then<T, E>(self, f: impl FnMut() -> Result<T, E>) -> Result<Option<T>, E>;
147}
148impl TryThen for bool {
149 fn try_then<T, E>(self, mut f: impl FnMut() -> Result<T, E>) -> Result<Option<T>, E> {
150 self.then_some(()).map(|()| f()).transpose()
151 }
152}
153
154#[derive(Default, Debug)]
155pub enum Controller {
156 Player(i32),
157 Logic(i32),
158 Command {
159 target: Option<i32>,
160 pos: Option<(f32, f32)>,
161 command: Option<UnitCommand>,
162 },
163 #[default]
164 Assembler,
165}
166
167impl Controller {
168 fn read(buff: &mut DataRead) -> Result<Controller, ReadError> {
187 Ok(match buff.read_u8()? {
188 0 => Controller::Player(buff.read_i32()?),
189 3 => Controller::Logic(buff.read_i32()?),
190 t @ (4 | 6 | 7 | 8 | 9) => {
191 let has_attack = buff.read_bool()?;
192 let pos = buff
193 .read_bool()?
194 .try_then::<_, ReadError>(|| try { (buff.read_f32()?, buff.read_f32()?) })?;
195 let target = has_attack.try_then::<_, ReadError>(|| try {
196 buff.skip(1)?;
197 buff.read_i32()?
198 })?;
199 let n = buff.read_i8()?;
200 let command = u8::try_from(n)
201 .ok()
202 .and_then(|x| UnitCommand::try_from(x).ok());
203 if let 7 | 8 | 9 = t {
204 for _ in 0..buff.read_u8()? {
205 match buff.read_u8()? {
206 0 | 1 => {
207 buff.read_u32()?;
208 }
209 2 => {
210 buff.read_f32()?;
211 buff.read_f32()?;
212 }
213 _ => {}
214 }
215 }
216 }
217 if t == 8 {
218 buff.read_u8()?;
220 } else if t == 9 {
221 let stances = buff.read_u8()?;
222 buff.skip(stances as _)?;
223 }
224 Controller::Command {
225 target,
226 pos,
227 command,
228 }
229 }
230 5 => Controller::Assembler,
231 _ => Controller::Assembler,
232 })
233 }
234}
235
236#[derive(Debug)]
237pub struct Unit {
238 pub state: UnitState,
239 pub ty: Type,
240}
241
242impl UnitClass {
243 pub fn read(self, buff: &mut DataRead) -> Result<Unit, ReadError> {
244 let _rev = buff.read_u16()?;
245 let mut state = UnitState::default();
246 read_abilities(buff)?;
247 state.ammo = buff.read_f32()?;
248 match self {
249 Self::Block
250 | Self::Legs
251 | Self::Elevated
252 | Self::Crawl
253 | Self::Boat
254 | Self::Tank
255 | Self::Air => {
256 state.controller = Controller::read(buff)?;
257 state.elevation = buff.read_f32()?;
258 state.flag = buff.read_i64()?;
259 state.health = buff.read_f32()?;
260 state.is_shooting = buff.read_bool()?;
261 read_tile(buff)?;
262 read_mounts(buff)?;
263 }
264 Self::Mech => {
265 buff.skip(4)?; state.controller = Controller::read(buff)?;
267 state.elevation = buff.read_f32()?;
268 state.flag = buff.read_i64()?;
269 state.health = buff.read_f32()?;
270 state.is_shooting = buff.read_bool()?;
271 read_tile(buff)?;
272 read_mounts(buff)?;
273 }
274 Self::Payload => {
275 state.controller = Controller::read(buff)?;
276 state.elevation = buff.read_f32()?;
277 state.flag = buff.read_i64()?;
278 state.health = buff.read_f32()?;
279 state.is_shooting = buff.read_bool()?;
280 read_tile(buff)?;
281 read_mounts(buff)?;
282 for _ in 0..buff.read_i32()? {
283 let _ = read_payload(buff);
287 }
288 }
289 Self::Bomb => {
290 state.controller = Controller::read(buff)?;
291 state.elevation = buff.read_f32()?;
292 state.flag = buff.read_i64()?;
293 state.health = buff.read_f32()?;
294 state.is_shooting = buff.read_bool()?;
295 buff.skip(4)?; read_tile(buff)?;
297 read_mounts(buff)?;
298 }
299 Self::Tethered => {
300 buff.skip(4)?;
301 state.controller = Controller::read(buff)?;
302 state.elevation = buff.read_f32()?;
303 state.flag = buff.read_i64()?;
304 state.health = buff.read_f32()?;
305 state.is_shooting = buff.read_bool()?;
306 read_tile(buff)?;
307 read_mounts(buff)?;
308 for _ in 0..buff.read_i32()? {
309 read_payload(buff)?;
311 }
312 }
313 }
314 read_plans(buff)?;
315 state.rotation = buff.read_f32()?;
316 state.shield = buff.read_f32()?;
317 buff.skip(1)?; state.stack = read_stack(buff)?;
319 state.status = read_status(buff)?;
320 state.team = Team::of(buff.read_u8()?);
321 if self == Self::Bomb {
322 buff.skip(4)?; }
324 let ty = Type::try_from(buff.read_u16()?).unwrap();
325 buff.skip(1)?; state.velocity = (buff.read_f32()?, buff.read_f32()?);
327 state.position = (
328 (buff.read_f32()? / 8.0).floor(),
329 (buff.read_f32()? / 8.0).floor(),
330 );
331 Ok(Unit { state, ty })
332 }
333}
334
335fn read_abilities(buff: &mut DataRead) -> Result<(), ReadError> {
339 let n = buff.read_u8()? as usize;
340 buff.skip(n * 4)
341}
342
343fn read_tile(buff: &mut DataRead) -> Result<(), ReadError> {
346 buff.skip(4)
347}
348
349fn read_mounts(buff: &mut DataRead) -> Result<(), ReadError> {
355 let n = buff.read_u8()? as usize;
356 buff.skip(n * 9)
357}
358
359fn read_plans(buff: &mut DataRead) -> Result<(), ReadError> {
365 let used = buff.read_i32()?;
366 if used == -1 {
367 return Ok(());
368 }
369 for _ in 0..used {
370 read_plan(buff)?;
371 }
372 Ok(())
373}
374
375fn read_plan(buff: &mut DataRead) -> Result<(), ReadError> {
384 let ty = buff.read_u8()?;
385 buff.skip(4)?;
386 if ty != 1 {
387 buff.skip(4)?;
388 let _ = DynData::deserialize(buff).unwrap();
389 }
390 Ok(())
391}
392
393fn read_stack(buff: &mut DataRead) -> Result<(Option<Item>, u32), ReadError> {
397 let n = buff.read_i16()?;
398 Ok((
399 (n != -1).then(|| Item::try_from(n as u16).unwrap()),
400 buff.read_u32()?,
401 ))
402}
403
404fn read_status(buff: &mut DataRead) -> Result<[Status; 3], ReadError> {
412 let mut status = [Status::None, Status::None, Status::None];
413 for i in 0..buff.read_i32()? {
414 let this = Status::try_from(buff.read_u16()?);
415 buff.skip(4)?;
416 if let Ok(s) = this
417 && i < 3
418 {
419 status[i as usize] = s;
420 }
421 }
422 Ok(status)
423}
424
425impl Unit {
426 #[inline]
427 pub fn draw(&self, s: crate::data::renderer::Scale) -> ImageHolder<4> {
428 self.ty.draw(s)
429 }
430}