1use std::{borrow::Cow, fmt, slice::Iter};
3
4use enum_iterator::Sequence;
5use js_sys::JsString;
6use num_derive::FromPrimitive;
7use num_traits::FromPrimitive;
8use serde::{
9 de::{Error as _, Unexpected},
10 Deserialize, Serialize,
11};
12use serde_repr::{Deserialize_repr, Serialize_repr};
13use wasm_bindgen::prelude::*;
14
15use super::{macros::named_enum_serialize_deserialize, InvalidConstantString};
16use crate::{
17 constants::find::{Exit, Find},
18 prelude::*,
19};
20
21#[derive(
23 Debug, PartialEq, Eq, Clone, Copy, Hash, FromPrimitive, Deserialize_repr, Serialize_repr,
24)]
25#[repr(i8)]
26pub enum ErrorCode {
27 NotOwner = -1,
28 NoPath = -2,
29 NameExists = -3,
30 Busy = -4,
31 NotFound = -5,
32 NotEnough = -6,
33 InvalidTarget = -7,
34 Full = -8,
35 NotInRange = -9,
36 InvalidArgs = -10,
37 Tired = -11,
38 NoBodypart = -12,
39 RclNotEnough = -14,
40 GclNotEnough = -15,
41 AccessDenied = -16,
42}
43
44impl FromReturnCode for ErrorCode {
45 type Error = Self;
46
47 fn result_from_i8(val: i8) -> Result<(), Self::Error> {
48 match val {
49 0 => Ok(()),
50 -1 => Err(ErrorCode::NotOwner),
51 -2 => Err(ErrorCode::NoPath),
52 -3 => Err(ErrorCode::NameExists),
53 -4 => Err(ErrorCode::Busy),
54 -5 => Err(ErrorCode::NotFound),
55 -6 => Err(ErrorCode::NotEnough),
56 -7 => Err(ErrorCode::InvalidTarget),
57 -8 => Err(ErrorCode::Full),
58 -9 => Err(ErrorCode::NotInRange),
59 -10 => Err(ErrorCode::InvalidArgs),
60 -11 => Err(ErrorCode::Tired),
61 -12 => Err(ErrorCode::NoBodypart),
62 -14 => Err(ErrorCode::RclNotEnough),
63 -15 => Err(ErrorCode::GclNotEnough),
64 -16 => Err(ErrorCode::AccessDenied),
65 #[cfg(feature = "unsafe-return-conversion")]
67 _ => unsafe { std::hint::unreachable_unchecked() },
68 #[cfg(not(feature = "unsafe-return-conversion"))]
69 _ => unreachable!(),
70 }
71 }
72
73 fn try_result_from_i8(val: i8) -> Option<Result<(), Self::Error>> {
74 match val {
75 0 => Some(Ok(())),
76 -1 => Some(Err(ErrorCode::NotOwner)),
77 -2 => Some(Err(ErrorCode::NoPath)),
78 -3 => Some(Err(ErrorCode::NameExists)),
79 -4 => Some(Err(ErrorCode::Busy)),
80 -5 => Some(Err(ErrorCode::NotFound)),
81 -6 => Some(Err(ErrorCode::NotEnough)),
82 -7 => Some(Err(ErrorCode::InvalidTarget)),
83 -8 => Some(Err(ErrorCode::Full)),
84 -9 => Some(Err(ErrorCode::NotInRange)),
85 -10 => Some(Err(ErrorCode::InvalidArgs)),
86 -11 => Some(Err(ErrorCode::Tired)),
87 -12 => Some(Err(ErrorCode::NoBodypart)),
88 -14 => Some(Err(ErrorCode::RclNotEnough)),
89 -15 => Some(Err(ErrorCode::GclNotEnough)),
90 -16 => Some(Err(ErrorCode::AccessDenied)),
91 _ => None,
92 }
93 }
94}
95
96#[wasm_bindgen]
98#[derive(
99 Debug,
100 PartialEq,
101 Eq,
102 Clone,
103 Copy,
104 Hash,
105 FromPrimitive,
106 Serialize_repr,
107 Deserialize_repr,
108 Sequence,
109)]
110#[repr(u8)]
111pub enum Direction {
112 Top = 1,
113 TopRight = 2,
114 Right = 3,
115 BottomRight = 4,
116 Bottom = 5,
117 BottomLeft = 6,
118 Left = 7,
119 TopLeft = 8,
120}
121
122impl Direction {
123 pub fn is_orthogonal(self) -> bool {
134 use Direction::*;
135
136 matches!(self, Top | Right | Bottom | Left)
137 }
138
139 pub fn is_diagonal(self) -> bool {
150 !self.is_orthogonal()
151 }
152
153 pub fn multi_rot(self, times: i8) -> Self {
168 let raw_dir = ((self as u8) - 1).wrapping_add_signed(times) % 8 + 1;
169 Self::from_u8(raw_dir).unwrap()
172 }
173
174 pub fn rot_cw(self) -> Self {
184 self.multi_rot(1)
185 }
186
187 pub fn rot_ccw(self) -> Self {
197 self.multi_rot(-1)
198 }
199
200 pub fn iter() -> Iter<'static, Direction> {
228 use crate::Direction::*;
229 static DIRECTIONS: [Direction; 8] = [
230 Top,
231 TopRight,
232 Right,
233 BottomRight,
234 Bottom,
235 BottomLeft,
236 Left,
237 TopLeft,
238 ];
239 DIRECTIONS.iter()
240 }
241}
242
243impl JsCollectionIntoValue for Direction {
244 fn into_value(self) -> JsValue {
245 (self as u8).into()
246 }
247}
248
249impl JsCollectionFromValue for Direction {
250 fn from_value(val: JsValue) -> Direction {
251 let n = if let Some(val) = val.as_string() {
252 val.parse::<u8>().expect("expected parseable u8 string")
253 } else {
254 val.as_f64().expect("expected number value") as u8
255 };
256
257 Self::from_u8(n).expect("unknown direction")
258 }
259}
260
261impl From<Direction> for (i32, i32) {
262 #[inline]
264 fn from(direction: Direction) -> (i32, i32) {
265 match direction {
266 Direction::Top => (0, -1),
267 Direction::TopRight => (1, -1),
268 Direction::Right => (1, 0),
269 Direction::BottomRight => (1, 1),
270 Direction::Bottom => (0, 1),
271 Direction::BottomLeft => (-1, 1),
272 Direction::Left => (-1, 0),
273 Direction::TopLeft => (-1, -1),
274 }
275 }
276}
277
278impl ::std::ops::Neg for Direction {
279 type Output = Direction;
280
281 #[inline]
294 fn neg(self) -> Direction {
295 use Direction::*;
296
297 match self {
298 Top => Bottom,
299 TopRight => BottomLeft,
300 Right => Left,
301 BottomRight => TopLeft,
302 Bottom => Top,
303 BottomLeft => TopRight,
304 Left => Right,
305 TopLeft => BottomRight,
306 }
307 }
308}
309
310impl fmt::Display for Direction {
311 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312 let ch = match *self {
313 Direction::Top => "↑",
314 Direction::TopRight => "↗",
315 Direction::Right => "→",
316 Direction::BottomRight => "↘",
317 Direction::Bottom => "↓",
318 Direction::BottomLeft => "↙",
319 Direction::Left => "←",
320 Direction::TopLeft => "↖",
321 };
322 f.write_str(ch)
323 }
324}
325
326#[wasm_bindgen]
337#[derive(
338 Debug,
339 PartialEq,
340 Eq,
341 Clone,
342 Copy,
343 Hash,
344 FromPrimitive,
345 Serialize_repr,
346 Deserialize_repr,
347 Sequence,
348)]
349#[repr(u8)]
350pub enum ExitDirection {
351 Top = 1,
352 Right = 3,
353 Bottom = 5,
354 Left = 7,
355}
356
357impl From<ExitDirection> for Find {
358 #[inline]
359 fn from(dir: ExitDirection) -> Self {
360 match dir {
361 ExitDirection::Top => Find::ExitTop,
362 ExitDirection::Right => Find::ExitRight,
363 ExitDirection::Bottom => Find::ExitBottom,
364 ExitDirection::Left => Find::ExitLeft,
365 }
366 }
367}
368
369impl From<ExitDirection> for Direction {
370 #[inline]
371 fn from(dir: ExitDirection) -> Self {
372 match dir {
373 ExitDirection::Top => Direction::Top,
374 ExitDirection::Right => Direction::Right,
375 ExitDirection::Bottom => Direction::Bottom,
376 ExitDirection::Left => Direction::Left,
377 }
378 }
379}
380
381impl From<ExitDirection> for Exit {
382 fn from(value: ExitDirection) -> Self {
383 match value {
384 ExitDirection::Top => Exit::Top,
385 ExitDirection::Right => Exit::Right,
386 ExitDirection::Bottom => Exit::Bottom,
387 ExitDirection::Left => Exit::Left,
388 }
389 }
390}
391
392#[wasm_bindgen]
394#[derive(
395 Debug,
396 PartialEq,
397 Eq,
398 Clone,
399 Copy,
400 FromPrimitive,
401 Hash,
402 Deserialize_repr,
403 Serialize_repr,
404 Sequence,
405)]
406#[repr(u8)]
407pub enum Color {
408 Red = 1,
409 Purple = 2,
410 Blue = 3,
411 Cyan = 4,
412 Green = 5,
413 Yellow = 6,
414 Orange = 7,
415 Brown = 8,
416 Grey = 9,
417 White = 10,
418}
419
420#[wasm_bindgen]
422#[derive(
423 Debug,
424 PartialEq,
425 Eq,
426 Clone,
427 Copy,
428 Hash,
429 FromPrimitive,
430 Serialize_repr,
431 Deserialize_repr,
432 Sequence,
433)]
434#[repr(u8)]
435pub enum Terrain {
436 Plain = 0,
438 Wall = 1,
440 Swamp = 2,
442 }
445
446impl Terrain {
447 pub fn from_look_constant_str(terrain_look_str: &str) -> Self {
451 match terrain_look_str {
452 "wall" => Terrain::Wall,
453 "swamp" => Terrain::Swamp,
454 "plain" => Terrain::Plain,
455 _ => Terrain::Plain,
456 }
457 }
458
459 pub fn from_look_constant_jsvalue(terrain_look_jsvalue: JsValue) -> Self {
460 let terrain_look_string: String = JsString::from(terrain_look_jsvalue).into();
461 Self::from_look_constant_str(&terrain_look_string)
462 }
463}
464
465#[wasm_bindgen]
467#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Sequence)]
468pub enum Part {
469 Move = "move",
470 Work = "work",
471 Carry = "carry",
472 Attack = "attack",
473 RangedAttack = "ranged_attack",
474 Tough = "tough",
475 Heal = "heal",
476 Claim = "claim",
477}
478
479named_enum_serialize_deserialize!(Part);
480
481impl Part {
482 #[inline]
484 pub const fn cost(self) -> u32 {
485 match self {
486 Part::Move => 50,
487 Part::Work => 100,
488 Part::Carry => 50,
489 Part::Attack => 80,
490 Part::RangedAttack => 150,
491 Part::Tough => 10,
492 Part::Heal => 250,
493 Part::Claim => 600,
494 _ => 0,
497 }
498 }
499}
500
501#[wasm_bindgen]
503#[derive(
504 Debug,
505 PartialEq,
506 Eq,
507 Clone,
508 Copy,
509 FromPrimitive,
510 Hash,
511 Serialize_repr,
512 Deserialize_repr,
513 Sequence,
514)]
515#[repr(u8)]
516pub enum Density {
517 Low = 1,
518 Moderate = 2,
519 High = 3,
520 Ultra = 4,
521}
522
523impl Density {
524 #[inline]
527 pub const fn amount(self) -> u32 {
528 match self {
529 Density::Low => 15_000,
530 Density::Moderate => 35_000,
531 Density::High => 70_000,
532 Density::Ultra => 100_000,
533 }
534 }
535
536 #[inline]
554 pub const fn probability(self) -> f32 {
555 match self {
556 Density::Low => 0.1,
557 Density::Moderate => 0.5,
558 Density::High => 0.9,
559 Density::Ultra => 1.0,
560 }
561 }
562}
563
564#[wasm_bindgen]
566#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Sequence)]
567pub enum OrderType {
568 Sell = "sell",
569 Buy = "buy",
570}