swarmbot_interfaces/
types.rs

1use std::{
2    f32::consts::PI,
3    fmt::{Debug, Display, Formatter},
4    ops::{Add, AddAssign, Index, Mul, MulAssign, Neg, Sub},
5};
6
7use colored::Colorize;
8use itertools::Itertools;
9use once_cell::sync::Lazy;
10use regex::{Captures, Regex};
11use serde::{Deserialize, Serialize};
12use swarm_bot_packets::{
13    read::{ByteReadable, ByteReader},
14    write::{ByteWritable, ByteWriter},
15    *,
16};
17
18use crate::types::{
19    block_data::{Block, BlockData},
20    Origin::{Abs, Rel},
21};
22
23pub mod block_data;
24
25#[derive(Clone)]
26pub struct PacketData {
27    pub id: u32,
28    pub reader: ByteReader,
29}
30
31impl PacketData {
32    #[inline]
33    pub fn read<T: ByteReadable>(&mut self) -> T {
34        self.reader.read()
35    }
36}
37
38#[derive(Serialize, Deserialize, Debug, Clone)]
39pub struct ChatSection {
40    pub color: Option<String>,
41    pub bold: Option<bool>,
42    pub italic: Option<bool>,
43    pub underlined: Option<bool>,
44    pub strikethrough: Option<bool>,
45    pub text: String,
46}
47
48#[derive(Serialize, Deserialize, Debug, Clone)]
49pub struct Chat {
50    pub extra: Option<Vec<ChatSection>>,
51    pub text: Option<String>,
52}
53
54impl Chat {
55    pub fn colorize(self) -> String {
56        if let Some(extra) = self.extra {
57            extra.into_iter().map(|section| section.colorize()).join("")
58        } else {
59            String::new()
60        }
61    }
62}
63
64impl ChatSection {
65    fn colorize(self) -> String {
66        use colored::Color::*;
67
68        let color = match self.color.unwrap_or_default().as_str() {
69            "dark_blue" | "blue" => Blue,
70            "dark_aqua" | "aqua" => Cyan,
71            "red" | "dark_red" => Red,
72            "purple" | "light_purple" => Magenta,
73            "gold" | "yellow" => Yellow,
74            "gray" => White,
75            "dark_gray" => Black,
76            "green" | "dark_green" => Green,
77            "white" => White,
78            _ => Black,
79        };
80
81        let mut res = self.text.color(color);
82
83        if self.bold.unwrap_or_default() {
84            res = res.bold();
85        }
86
87        if self.italic.unwrap_or_default() {
88            res = res.italic();
89        }
90
91        if self.underlined.unwrap_or_default() {
92            res = res.underline();
93        }
94
95        if self.strikethrough.unwrap_or_default() {
96            res = res.strikethrough();
97        }
98
99        res.to_string()
100    }
101}
102
103#[derive(Debug)]
104pub struct Command {
105    pub player: String,
106    pub command: String,
107    pub args: Vec<String>,
108}
109
110#[derive(Debug)]
111pub struct PlayerMessage {
112    pub player: String,
113    pub message: String,
114}
115
116impl PlayerMessage {
117    pub fn into_cmd(self) -> Option<Command> {
118        static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"#(\S+)\s?(.*)").unwrap());
119        let capture = RE.captures(&self.message)?;
120
121        let command = capture.get(1)?.as_str().to_string();
122        let args = capture.get(2)?.as_str().to_string();
123
124        let args = if args.is_empty() {
125            Vec::new()
126        } else {
127            args.split(' ').map(|x| x.to_string()).collect()
128        };
129
130        Some(Command {
131            player: self.player,
132            command,
133            args,
134        })
135    }
136}
137
138impl Chat {
139    pub fn player_dm(&self) -> Option<PlayerMessage> {
140        static RE: Lazy<Regex> =
141            Lazy::new(|| Regex::new(r"^([A-Za-z_0-9]+) whispers: (.*)").unwrap());
142
143        let text = self.extra.as_ref()?.iter().map(|x| &x.text).join("");
144
145        let captures: Captures = RE.captures(&text)?;
146
147        let player = captures.get(1)?.as_str().to_string();
148        let message = captures.get(2)?.as_str().to_string();
149        Some(PlayerMessage { player, message })
150    }
151    pub fn player_message(&self) -> Option<PlayerMessage> {
152        static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"^<([A-Za-z_0-9]+)> (.*)").unwrap());
153
154        let text = self.extra.as_ref()?.iter().map(|x| &x.text).join("");
155
156        let captures: Captures = RE.captures(&text)?;
157
158        let player = captures.get(1)?.as_str().to_string();
159        let message = captures.get(2)?.as_str().to_string();
160
161        Some(PlayerMessage { player, message })
162    }
163}
164
165impl ByteReadable for Chat {
166    fn read_from_bytes(byte_reader: &mut ByteReader) -> Self {
167        let string: String = byte_reader.read();
168        let json: Chat = serde_json::from_str(&string).unwrap();
169        json
170    }
171}
172
173#[derive(Writable, Readable, Debug, Copy, Clone, Default, PartialEq)]
174pub struct LocationFloat {
175    pub x: f32,
176    pub y: f32,
177    pub z: f32,
178}
179
180impl From<LocationFloat> for Location {
181    fn from(loc: LocationFloat) -> Self {
182        Self {
183            x: loc.x as f64,
184            y: loc.y as f64,
185            z: loc.z as f64,
186        }
187    }
188}
189
190impl Add<Displacement> for Location {
191    type Output = Location;
192
193    fn add(self, rhs: Displacement) -> Self::Output {
194        Location {
195            x: self.x + rhs.dx,
196            y: self.y + rhs.dy,
197            z: self.z + rhs.dz,
198        }
199    }
200}
201
202impl Location {
203    pub const fn new(x: f64, y: f64, z: f64) -> Location {
204        Location { x, y, z }
205    }
206}
207
208impl From<Location> for BlockLocation {
209    fn from(location: Location) -> Self {
210        let Location { x, y, z } = location;
211        BlockLocation::from_flts(x, y, z)
212    }
213}
214
215impl Sub<Location> for Location {
216    type Output = Displacement;
217
218    fn sub(self, rhs: Location) -> Self::Output {
219        Displacement {
220            dx: self.x - rhs.x,
221            dy: self.y - rhs.y,
222            dz: self.z - rhs.z,
223        }
224    }
225}
226
227impl Display for Location {
228    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
229        f.write_fmt(format_args!("[{:.2} {:.2} {:.2}]", self.x, self.y, self.z))
230    }
231}
232
233#[derive(Writable, Readable, Debug, Copy, Clone, Default)]
234pub struct Displacement {
235    pub dx: f64,
236    pub dy: f64,
237    pub dz: f64,
238}
239
240#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
241pub struct Enchantment {
242    pub lvl: u16,
243    pub id: u16,
244}
245
246impl Enchantment {
247    pub fn efficiency(self) -> Option<u16> {
248        if self.id == 32 {
249            Some(self.lvl)
250        } else {
251            None
252        }
253    }
254}
255
256pub struct ShortVec<T>(pub Vec<T>);
257
258impl<T: ByteReadable> ByteReadable for ShortVec<T> {
259    fn read_from_bytes(byte_reader: &mut ByteReader) -> Self {
260        let length: u16 = byte_reader.read();
261        let length = length as usize;
262        let mut vec = Vec::with_capacity(length);
263        for _ in 0..length {
264            vec.push(byte_reader.read());
265        }
266        ShortVec(vec)
267    }
268}
269
270pub struct Change {
271    pub dx: i32,
272    pub dy: i16,
273    pub dz: i32,
274}
275
276impl Change {
277    pub fn new(dx: i32, dy: i16, dz: i32) -> Change {
278        Change { dx, dy, dz }
279    }
280}
281
282impl From<Change> for Displacement {
283    fn from(change: Change) -> Self {
284        Self {
285            dx: change.dx as f64,
286            dy: change.dy as f64,
287            dz: change.dz as f64,
288        }
289    }
290}
291
292impl Display for Displacement {
293    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
294        f.write_fmt(format_args!(
295            "[{:.2} {:.2} {:.2}]",
296            self.dx, self.dy, self.dz
297        ))
298    }
299}
300
301impl Neg for Displacement {
302    type Output = Displacement;
303
304    fn neg(self) -> Self::Output {
305        self * (-1.0)
306    }
307}
308
309impl Sub for Displacement {
310    type Output = Displacement;
311
312    fn sub(self, rhs: Self) -> Self::Output {
313        let rhs = rhs * (-1.0);
314        self + rhs
315    }
316}
317
318impl Add for Displacement {
319    type Output = Displacement;
320
321    fn add(self, rhs: Self) -> Self::Output {
322        Self {
323            dx: self.dx + rhs.dx,
324            dy: self.dy + rhs.dy,
325            dz: self.dz + rhs.dz,
326        }
327    }
328}
329
330impl Displacement {
331    pub const EYE_HEIGHT: Displacement = Displacement::new(0., 1.6, 0.);
332    pub const EPSILON_Y: Displacement = Displacement::new(0., 0.01, 0.);
333
334    pub const fn new(dx: f64, dy: f64, dz: f64) -> Displacement {
335        Displacement { dx, dy, dz }
336    }
337
338    pub fn zero_if_reachable(&self) -> Displacement {
339        let dx = if self.dx.abs() < 0.5 { 0. } else { self.dx };
340        let dy = if self.dy.abs() < 0.5 { 0. } else { self.dy };
341        let dz = if self.dz.abs() < 0.5 { 0. } else { self.dz };
342        Self { dx, dy, dz }
343    }
344
345    pub fn make_dy(&self, dy: f64) -> Displacement {
346        Self {
347            dx: self.dx,
348            dy,
349            dz: self.dz,
350        }
351    }
352
353    pub fn mag(&self) -> f64 {
354        self.mag2().sqrt()
355    }
356
357    pub fn dot(&self, other: Displacement) -> f64 {
358        self.dx * other.dx + self.dy * other.dy + self.dz * other.dz
359    }
360
361    pub fn reflect(&self, normal: Displacement) -> Displacement {
362        let rhs = normal * 2.0 * (self.dot(normal));
363        *self - rhs
364    }
365
366    pub fn normalize(self) -> Displacement {
367        let mag = self.mag();
368        if mag == 0. {
369            // we can't normalize 0-length
370            self
371        } else {
372            let mult = 1.0 / mag;
373            self * mult
374        }
375    }
376
377    pub fn mag2(&self) -> f64 {
378        let Displacement { dx, dy, dz } = *self;
379        dx * dx + dy * dy + dz * dz
380    }
381
382    pub fn cross(&self, other: Displacement) -> Displacement {
383        let dx = self[1] * other[2] - self[2] * other[1];
384        let dy = self[2] * other[0] - self[0] * other[2];
385        let dz = self[0] * other[1] - self[1] * other[0];
386        Displacement::new(dx, dy, dz)
387    }
388    pub fn has_length(&self) -> bool {
389        self.dx != 0.0 || self.dy != 0.0 || self.dz != 0.0
390    }
391}
392
393impl MulAssign<f64> for Displacement {
394    fn mul_assign(&mut self, rhs: f64) {
395        self.dx *= rhs;
396        self.dy *= rhs;
397        self.dz *= rhs;
398    }
399}
400
401impl Mul<f64> for Displacement {
402    type Output = Displacement;
403
404    fn mul(self, rhs: f64) -> Self::Output {
405        Self {
406            dx: self.dx * rhs,
407            dy: self.dy * rhs,
408            dz: self.dz * rhs,
409        }
410    }
411}
412
413impl Index<usize> for Displacement {
414    type Output = f64;
415
416    fn index(&self, index: usize) -> &Self::Output {
417        match index {
418            0 => &self.dx,
419            1 => &self.dy,
420            2 => &self.dz,
421            _ => panic!("invalid index"),
422        }
423    }
424}
425
426impl AddAssign<Location> for Location {
427    fn add_assign(&mut self, rhs: Location) {
428        self.x += rhs.x;
429        self.y += rhs.y;
430        self.z += rhs.z;
431    }
432}
433
434impl Add<Location> for Location {
435    type Output = Location;
436
437    fn add(self, rhs: Location) -> Self::Output {
438        Location {
439            x: self.x + rhs.x,
440            y: self.y + rhs.y,
441            z: self.z + rhs.z,
442        }
443    }
444}
445
446// impl Sub<Location> for Location {
447//     type Output = Location;
448//
449//     fn sub(self, rhs: Location) -> Self::Output {
450//         Location {
451//             x: self.x - rhs.x,
452//             y: self.y - rhs.y,
453//             z: self.z - rhs.z,
454//         }
455//     }
456// }
457
458impl From<Location> for LocationOrigin {
459    fn from(loc: Location) -> Self {
460        LocationOrigin {
461            x: Abs(loc.x),
462            y: Abs(loc.y),
463            z: Abs(loc.z),
464        }
465    }
466}
467
468impl Location {
469    pub fn dist2(&self, loc: Location) -> f64 {
470        let dx = loc.x - self.x;
471        let dy = loc.y - self.y;
472        let dz = loc.z - self.z;
473        dx * dx + dy * dy + dz * dz
474    }
475
476    pub fn apply_change(&mut self, loc: LocationOrigin) {
477        loc.x.apply(&mut self.x);
478        loc.y.apply(&mut self.y);
479        loc.z.apply(&mut self.z);
480    }
481}
482
483#[derive(Readable, Writable, Debug)]
484pub struct ShortLoc {
485    dx: i16,
486    dy: i16,
487    dz: i16,
488}
489
490impl From<ShortLoc> for LocationOrigin {
491    fn from(loc: ShortLoc) -> Self {
492        LocationOrigin {
493            x: Rel(loc.dx as f64 / (128.0 * 32.0)),
494            y: Rel(loc.dy as f64 / (128.0 * 32.0)),
495            z: Rel(loc.dz as f64 / (128.0 * 32.0)),
496        }
497    }
498}
499
500impl Add<LocationOrigin> for Location {
501    type Output = Location;
502
503    fn add(mut self, rhs: LocationOrigin) -> Self::Output {
504        rhs.x.apply(&mut self.x);
505        rhs.y.apply(&mut self.y);
506        rhs.z.apply(&mut self.z);
507        self
508    }
509}
510
511#[derive(Debug, Eq, PartialEq)]
512pub enum Origin<T> {
513    Rel(T),
514    Abs(T),
515}
516
517impl<T> Origin<T> {
518    fn from(value: T, relative: bool) -> Origin<T> {
519        if relative {
520            Rel(value)
521        } else {
522            Abs(value)
523        }
524    }
525}
526
527impl Origin<f64> {
528    pub fn apply(&self, other: &mut f64) {
529        match self {
530            Rel(x) => *other += *x,
531            Abs(x) => *other = *x,
532        }
533    }
534}
535
536impl Origin<f32> {
537    pub fn apply(&self, other: &mut f32) {
538        match self {
539            Rel(x) => *other += *x,
540            Abs(x) => *other = *x,
541        }
542    }
543}
544
545#[derive(Debug)]
546pub struct LocationOrigin {
547    pub x: Origin<f64>,
548    pub y: Origin<f64>,
549    pub z: Origin<f64>,
550}
551
552impl LocationOrigin {
553    pub fn from(location: Location, x: bool, y: bool, z: bool) -> LocationOrigin {
554        LocationOrigin {
555            x: Origin::from(location.x, x),
556            y: Origin::from(location.y, y),
557            z: Origin::from(location.z, z),
558        }
559    }
560}
561
562#[derive(Debug)]
563pub struct DirectionOrigin {
564    pub yaw: Origin<f32>,
565    pub pitch: Origin<f32>,
566}
567
568impl DirectionOrigin {
569    pub fn from(dir: Direction, yaw: bool, pitch: bool) -> DirectionOrigin {
570        DirectionOrigin {
571            yaw: Origin::from(dir.yaw, yaw),
572            pitch: Origin::from(dir.pitch, pitch),
573        }
574    }
575}
576
577#[derive(Readable, Writable, Copy, Clone, Default, Debug)]
578pub struct Direction {
579    /// wiki.vg:
580    /// yaw is measured in degrees, and does not follow classical trigonometry
581    /// rules. The unit circle of yaw on the XZ-plane starts at (0, 1) and
582    /// turns counterclockwise, with 90 at (-1, 0), 180 at (0,-1) and 270 at (1,
583    /// 0). Additionally, yaw is not clamped to between 0 and 360 degrees;
584    /// any number is valid, including negative numbers and numbers greater than
585    /// 360.
586    pub yaw: f32,
587    pub pitch: f32,
588}
589
590impl Direction {
591    pub const DOWN: Direction = Direction {
592        yaw: 90.,
593        pitch: 90.,
594    };
595
596    pub fn unit_vector(&self) -> Displacement {
597        let pitch = self.pitch.to_radians();
598        let yaw = self.yaw.to_radians();
599
600        let x = -(pitch).to_radians().cos() * (yaw).sin();
601        let y = -(pitch).sin();
602        let z = (pitch).cos() * (yaw).cos();
603
604        Displacement::new(x as f64, y as f64, z as f64)
605    }
606
607    pub fn horizontal(&self) -> Direction {
608        let mut res = *self;
609        res.pitch = 0.0;
610        res
611    }
612}
613
614impl From<Displacement> for Direction {
615    fn from(displacement: Displacement) -> Self {
616        let Displacement { dx, dy, dz } = displacement;
617        let (dx, dy, dz) = (dx as f32, dy as f32, dz as f32);
618        let r = (dx * dx + dy * dy + dz * dz).sqrt();
619        let mut yaw = -dx.atan2(dz) / PI * 180.0;
620
621        if yaw < 0.0 {
622            yaw += 360.0
623        }
624
625        const EPSILON: f32 = 0.1;
626
627        if yaw.abs() < EPSILON {
628            yaw = 0.0;
629        }
630        let pitch = -(dy / r).asin() / PI * 180.0;
631        Direction { yaw, pitch }
632    }
633}
634
635#[derive(Debug, Copy, Clone, Eq, PartialEq)]
636pub enum Dimension {
637    Nether,
638    Overworld,
639    End,
640}
641
642impl Display for Dimension {
643    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
644        let to_write = match self {
645            Dimension::Nether => "nether",
646            Dimension::Overworld => "overworld",
647            Dimension::End => "end",
648        };
649        f.write_str(to_write)
650    }
651}
652
653impl ByteReadable for Dimension {
654    fn read_from_bytes(byte_reader: &mut ByteReader) -> Self {
655        use Dimension::*;
656        let val: i32 = byte_reader.read();
657        match val {
658            -1 => Nether,
659            0 => Overworld,
660            1 => End,
661            val => panic!("dimension {val} is not valid"),
662        }
663    }
664}
665
666pub type Position = BlockLocation;
667
668impl ByteReadable for Position {
669    ///
670    fn read_from_bytes(byte_reader: &mut ByteReader) -> Self {
671        let val: u64 = byte_reader.read();
672
673        let mut x = (val >> 38) as i32;
674        let mut y = ((val >> 26) & 0xFFF) as i16;
675        let mut z = (val << 38 >> 38) as i32;
676
677        const LAT_LON_THRESHOLD: i32 = 1 << 25;
678        const LAT_LON_SUB: i32 = 1 << 26;
679
680        const Y_THRESH: i16 = 1 << 11;
681        const Y_SUB: i16 = 1 << 12;
682
683        if x >= LAT_LON_THRESHOLD {
684            x -= LAT_LON_SUB
685        }
686        if y >= Y_THRESH {
687            y -= Y_SUB
688        }
689        if z >= LAT_LON_THRESHOLD {
690            z -= LAT_LON_SUB
691        }
692
693        Position { x, y, z }
694    }
695}
696
697impl ByteWritable for Position {
698    fn write_to_bytes(self, writer: &mut ByteWriter) {
699        let Position { x, y, z } = self;
700        let write =
701            ((x as u64 & 0x3FFFFFF) << 38) | ((y as u64 & 0xFFF) << 26) | (z as u64 & 0x3FFFFFF);
702        writer.write(write);
703    }
704}
705
706#[derive(Serialize, Deserialize, Debug)]
707pub struct Selection2D {
708    pub from: BlockLocation2D,
709    pub to: BlockLocation2D,
710}
711
712impl Selection2D {
713    /// Normalize so that the **from** coordinate is always smaller than the
714    /// **to** coord.
715    pub fn normalize(self) -> Self {
716        let min_x = self.from.x.min(self.to.x);
717        let min_z = self.from.z.min(self.to.z);
718
719        let max_x = self.from.x.max(self.to.x);
720        let max_z = self.from.z.max(self.to.z);
721
722        Selection2D {
723            from: BlockLocation2D::new(min_x, min_z),
724            to: BlockLocation2D::new(max_x, max_z),
725        }
726    }
727}
728
729#[derive(Writable, Readable, Debug, Copy, Clone, Default, PartialEq)]
730pub struct Location {
731    pub x: f64,
732    pub y: f64,
733    pub z: f64,
734}
735
736impl Location {
737    pub fn sub_y(&self, dy: f64) -> Location {
738        Location::new(self.x, self.y - dy, self.z)
739    }
740
741    pub fn round(&self) -> Location {
742        let &Location { x, y, z } = self;
743        Location {
744            x: x.round(),
745            y: y.round(),
746            z: z.round(),
747        }
748    }
749
750    pub fn add_y(&self, dy: f64) -> Location {
751        Location::new(self.x, self.y + dy, self.z)
752    }
753}
754
755impl Sub<Displacement> for Location {
756    type Output = Location;
757
758    fn sub(self, rhs: Displacement) -> Self::Output {
759        let Displacement { dx, dy, dz } = rhs;
760        Self {
761            x: self.x - dx,
762            y: self.y - dy,
763            z: self.z - dz,
764        }
765    }
766}
767
768#[derive(Copy, Clone, Hash, Eq, PartialEq)]
769pub struct ChunkLocation(pub i32, pub i32);
770
771impl From<BlockLocation> for ChunkLocation {
772    fn from(loc: BlockLocation) -> Self {
773        Self(loc.x >> 4, loc.z >> 4)
774    }
775}
776
777impl From<Location> for ChunkLocation {
778    fn from(loc: Location) -> Self {
779        let block_loc = BlockLocation::from(loc);
780        Self::from(block_loc)
781    }
782}
783
784/// A block location stored by (x,z) = i32, y = i16. y is signed to preserve
785/// compatibility with 1.17, where the world height can be much higher and goes
786/// to negative values.
787#[derive(
788    Copy, Clone, Debug, Hash, PartialOrd, PartialEq, Ord, Eq, Default, Serialize, Deserialize,
789)]
790pub struct BlockLocation {
791    pub x: i32,
792    pub y: i16,
793    pub z: i32,
794}
795
796impl From<BlockLocation> for BlockLocation2D {
797    fn from(loc: BlockLocation) -> Self {
798        Self { x: loc.x, z: loc.z }
799    }
800}
801
802impl From<BlockLocation2D> for BlockLocation {
803    fn from(loc: BlockLocation2D) -> Self {
804        Self {
805            x: loc.x,
806            y: 0,
807            z: loc.z,
808        }
809    }
810}
811
812#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
813pub struct BlockLocation2D {
814    pub x: i32,
815    pub z: i32,
816}
817
818impl BlockLocation2D {
819    pub fn new(x: i32, z: i32) -> Self {
820        Self { x, z }
821    }
822    pub fn dist2(self, other: BlockLocation2D) -> u64 {
823        // TODO: potential bug here with unsigned abs
824        let dx = self.x.abs_diff(other.x) as u64;
825        let dz = self.z.abs_diff(other.z) as u64;
826        dx * dx + dz * dz
827    }
828}
829
830impl From<Change> for BlockLocation {
831    fn from(change: Change) -> Self {
832        Self {
833            x: change.dx,
834            y: change.dy,
835            z: change.dz,
836        }
837    }
838}
839
840impl Add for BlockLocation {
841    type Output = BlockLocation;
842
843    fn add(self, rhs: Self) -> Self::Output {
844        let BlockLocation { x, y, z } = self;
845        BlockLocation::new(x + rhs.x, y + rhs.y, z + rhs.z)
846    }
847}
848
849impl BlockLocation {
850    pub fn new(x: i32, y: i16, z: i32) -> BlockLocation {
851        BlockLocation { x, y, z }
852    }
853
854    pub fn faces(self) -> [Location; 6] {
855        const DISPLACEMENTS: [Displacement; 6] = {
856            let a = Displacement::new(0.5, 0.0, 0.5);
857            let b = Displacement::new(0.5, 1.0, 0.5);
858
859            let c = Displacement::new(0.5, 0.5, 0.0);
860            let d = Displacement::new(0.5, 0.5, 1.0);
861
862            let e = Displacement::new(0.0, 0.5, 0.5);
863            let f = Displacement::new(1.0, 0.5, 0.5);
864
865            [a, b, c, d, e, f]
866        };
867
868        let lowest = Location::new(self.x as f64, self.y as f64, self.z as f64);
869        let mut res = [Location::default(); 6];
870        for i in 0..6 {
871            res[i] = lowest + DISPLACEMENTS[i]
872        }
873        res
874    }
875
876    pub fn below(&self) -> BlockLocation {
877        Self {
878            x: self.x,
879            y: self.y - 1,
880            z: self.z,
881        }
882    }
883
884    pub fn above(&self) -> BlockLocation {
885        Self {
886            x: self.x,
887            y: self.y + 1,
888            z: self.z,
889        }
890    }
891
892    pub fn get(&self, idx: usize) -> i32 {
893        match idx {
894            0 => self.x,
895            1 => self.y as i32,
896            2 => self.z,
897            _ => panic!("invalid index for block location"),
898        }
899    }
900
901    pub fn set(&mut self, idx: usize, value: i32) {
902        match idx {
903            0 => self.x = value,
904            1 => self.y = value as i16,
905            2 => self.z = value,
906            _ => panic!("invalid index for block location"),
907        }
908    }
909
910    pub fn from_flts(x: impl num::Float, y: impl num::Float, z: impl num::Float) -> BlockLocation {
911        let x = num::cast(x.floor()).unwrap();
912        let y = num::cast(y.floor()).unwrap_or(-100); // TODO: change.. however, this is the best for an invalid number right now.
913        let z = num::cast(z.floor()).unwrap();
914        BlockLocation::new(x, y, z)
915    }
916
917    pub fn add_y(&self, dy: i16) -> BlockLocation {
918        let &BlockLocation { x, y, z } = self;
919        Self { x, y: y + dy, z }
920    }
921
922    pub fn center_bottom(&self) -> Location {
923        Location {
924            x: self.x as f64 + 0.5,
925            y: self.y as f64,
926            z: self.z as f64 + 0.5,
927        }
928    }
929
930    pub fn true_center(&self) -> Location {
931        Location {
932            x: self.x as f64 + 0.5,
933            y: self.y as f64 + 0.5,
934            z: self.z as f64 + 0.5,
935        }
936    }
937}
938
939impl BlockLocation {
940    pub fn dist2(&self, other: BlockLocation) -> f64 {
941        let (dx, dy, dz) = self.abs_dif(other);
942        let (dx, dy, dz) = (dx as f64, dy as f64, dz as f64);
943        dx * dx + dy * dy + dz * dz
944    }
945
946    pub fn abs_dif(&self, other: BlockLocation) -> (u32, u16, u32) {
947        let dx = self.x.abs_diff(other.x);
948        let dy = self.y.abs_diff(other.y);
949        let dz = self.z.abs_diff(other.z);
950        (dx, dy, dz)
951    }
952
953    pub fn manhatten(&self, other: BlockLocation) -> u64 {
954        let (dx, dy, dz) = self.abs_dif(other);
955        let (dx, dy, dz) = (dx as u64, dy as u64, dz as u64);
956        dx + dy + dz
957    }
958
959    pub fn dist(&self, other: BlockLocation) -> f64 {
960        self.dist2(other).sqrt()
961    }
962}
963
964impl Display for BlockLocation {
965    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
966        f.write_fmt(format_args!("[{}, {}, {}]", self.x, self.y, self.z))
967    }
968}
969
970#[derive(Copy, Clone, Debug)]
971pub enum BlockApprox {
972    Realized(BlockState),
973    Estimate(SimpleType),
974}
975
976impl BlockApprox {
977    pub const AIR: BlockApprox = BlockApprox::Estimate(SimpleType::WalkThrough);
978
979    pub fn s_type(&self) -> SimpleType {
980        match self {
981            BlockApprox::Realized(x) => x.simple_type(),
982            BlockApprox::Estimate(x) => *x,
983        }
984    }
985
986    pub fn as_real(&self) -> BlockState {
987        match self {
988            BlockApprox::Realized(inner) => *inner,
989            _ => panic!("was not realized"),
990        }
991    }
992
993    pub fn is_solid(&self) -> bool {
994        self.s_type() == SimpleType::Solid
995    }
996
997    pub fn is_walkable(&self) -> bool {
998        self.s_type() == SimpleType::WalkThrough
999    }
1000}
1001
1002#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
1003pub enum SimpleType {
1004    Solid,
1005    Water,
1006    Avoid,
1007    WalkThrough,
1008}
1009
1010impl SimpleType {
1011    pub fn id(&self) -> u8 {
1012        match self {
1013            SimpleType::Solid => 0,
1014            SimpleType::Water => 1,
1015            SimpleType::Avoid => 2,
1016            SimpleType::WalkThrough => 3,
1017        }
1018    }
1019}
1020
1021impl From<u8> for SimpleType {
1022    fn from(id: u8) -> Self {
1023        match id {
1024            0 => SimpleType::Solid,
1025            1 => SimpleType::Water,
1026            2 => SimpleType::Avoid,
1027            3 => SimpleType::WalkThrough,
1028            _ => panic!("invalid id"),
1029        }
1030    }
1031}
1032
1033#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
1034#[repr(transparent)]
1035pub struct BlockKind(pub u32);
1036
1037impl From<u32> for BlockKind {
1038    fn from(id: u32) -> Self {
1039        Self(id)
1040    }
1041}
1042
1043impl BlockKind {
1044    pub const DEFAULT_SLIP: f64 = 0.6;
1045    pub const LADDER: BlockKind = BlockKind(65);
1046    pub const LEAVES: BlockKind = BlockKind(18);
1047    pub const FLOWING_WATER: BlockKind = BlockKind(8);
1048    pub const STONE: BlockKind = BlockKind(1);
1049    pub const DIRT: BlockKind = BlockKind(3);
1050    pub const GLASS: BlockKind = BlockKind(20);
1051
1052    #[inline]
1053    pub fn id(self) -> u32 {
1054        self.0
1055    }
1056
1057    pub fn hardness(&self, blocks: &BlockData) -> Option<f64> {
1058        let block = blocks
1059            .by_id(self.0)
1060            .unwrap_or_else(|| panic!("no block for id {}", self.0));
1061        block.hardness
1062    }
1063
1064    pub fn data<'a>(&self, blocks: &'a BlockData) -> &'a Block {
1065        blocks
1066            .by_id(self.0)
1067            .unwrap_or_else(|| panic!("no block for id {}", self.0))
1068    }
1069
1070    pub fn throw_away_block(self) -> bool {
1071        // cobblestone
1072        matches!(self.id(), 4)
1073    }
1074
1075    pub fn mineable(&self, blocks: &BlockData) -> bool {
1076        // we can't mine air
1077        if self.0 == 0 {
1078            return false;
1079        }
1080
1081        match self.hardness(blocks) {
1082            None => false,
1083            Some(val) => val < 100.0,
1084        }
1085    }
1086
1087    pub fn slip(&self) -> f64 {
1088        match self.0 {
1089            266 => 0.989,           // blue ice
1090            79 | 174 | 212 => 0.98, // ice, packed ice, or frosted ice
1091            37 => 0.8,              // slime block
1092            _ => Self::DEFAULT_SLIP,
1093        }
1094    }
1095}
1096
1097#[derive(Copy, Clone, Eq, PartialEq, Hash, Default)]
1098#[repr(transparent)]
1099pub struct BlockState(pub u32);
1100
1101impl Debug for BlockState {
1102    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1103        f.write_fmt(format_args!("{}:{}", self.0 >> 4, self.0 % 16))
1104    }
1105}
1106
1107impl BlockState {
1108    pub const AIR: BlockState = BlockState(0);
1109    pub const WATER: BlockState = BlockState(9);
1110    pub const STONE: BlockState = BlockState(16);
1111
1112    pub fn from(id: u32, data: u16) -> BlockState {
1113        BlockState((id << 4) + data as u32)
1114    }
1115
1116    pub fn id(&self) -> u32 {
1117        self.0 >> 4
1118    }
1119
1120    pub fn kind(&self) -> BlockKind {
1121        BlockKind(self.id())
1122    }
1123
1124    pub fn simple_type(&self) -> SimpleType {
1125        if self.full_block() {
1126            return SimpleType::Solid;
1127        }
1128
1129        if self.is_water() {
1130            return SimpleType::Water;
1131        }
1132
1133        if self.walk_through() {
1134            return SimpleType::WalkThrough;
1135        }
1136
1137        SimpleType::Avoid
1138    }
1139
1140    pub fn metadata(&self) -> u8 {
1141        (self.0 & 0b1111) as u8
1142    }
1143
1144    pub fn full_block(&self) -> bool {
1145        // consider 54 |
1146        matches!(self.id(),
1147            1..=5 |7 | 12..=25 | 29 | 33 |35 | 41 ..=43 | 45..=49 | 52 | 56..=58 | 60..=62 | 73 | 74 |
1148            78..=80| // snow, ice
1149            82| // clay
1150            84|86|87|89|91|95|
1151            97| // TODO: avoid this is a monster egg
1152            98..=100|
1153            // TODO: account panes
1154            103|110|112|118|121|123..=125|
1155            129|133|137..=138|155|159|161|162|
1156            165| // TODO: slime block special fall logic
1157            166|
1158            168..=170| // TODO: special haybale logic
1159            172..=174|
1160            179|181|199..=202|
1161            204|206|208..=212|214..=255
1162
1163        )
1164    }
1165
1166    pub fn is_water(&self) -> bool {
1167        matches!(
1168            self.id(),
1169            8 | 9 | 65 // ladder ... this is VERY jank
1170        )
1171    }
1172
1173    pub fn walk_through(&self) -> bool {
1174        self.is_water() || self.no_motion_effect()
1175    }
1176
1177    pub fn no_motion_effect(&self) -> bool {
1178        matches!(
1179            self.id(),
1180            0| // air
1181            6|// sapling
1182            27|28| //  rail
1183            31| // grass/fern/dead shrub
1184            38|37|// flower
1185            39|40| //mushroom
1186            50|//torch
1187            59|// wheat
1188            66|68|69|70|72|75|76|77|83|
1189            90| // portal
1190            104|105|106|
1191            115|119|
1192            175..=177
1193        )
1194    }
1195}