Skip to main content

yog_core/
local.rs

1//! What this client's player is and sees (`yog_event::client::local`), and
2//! how it clicks a container (`client::control`): records the host builds.
3
4use yog_data::{Atom, Schema};
5
6use crate::{BlockPos, Point};
7
8/// This client's player: where it is and looks, how it moves and fares.
9#[derive(Debug, Clone, PartialEq, Schema)]
10#[data(view)]
11pub struct LocalState {
12    pub pos: Point,
13    /// Degrees: yaw 0 looks south (+z), 90 west; pitch 90 looks down.
14    pub yaw: f32,
15    pub pitch: f32,
16    /// Blocks a tick.
17    pub velocity: Point,
18    pub on_ground: bool,
19    pub in_water: bool,
20    pub eye_height: f64,
21    pub health: f32,
22    pub food: i32,
23    pub dimension: Atom,
24    /// The selected hotbar slot, 0–8.
25    pub slot: u32,
26    /// How charged the next attack is: 1 does full damage.
27    pub attack_strength: f32,
28}
29
30impl LocalState {
31    /// Where its eyes are.
32    pub fn eye_pos(&self) -> Point {
33        Point {
34            y: self.pos.y + self.eye_height,
35            ..self.pos
36        }
37    }
38}
39
40/// One kind of block in a [`BlockArea`]: how it is to walk through.
41#[derive(Debug, Clone, PartialEq, Schema)]
42#[data(view)]
43pub struct BlockKind {
44    pub id: Atom,
45    /// Can be stood on and can't be walked through.
46    pub solid: bool,
47    pub liquid: bool,
48    /// Ladders, vines: climbed.
49    pub climbable: bool,
50    /// Hurts to touch (fire, lava, cactus, berry bush…).
51    pub harmful: bool,
52}
53
54/// The blocks between `min` and `max` (what isn't loaded reads as
55/// bedrock): a palette and an index into it per block, x fastest, then z,
56/// then y.
57#[derive(Debug, Clone, PartialEq, Schema)]
58#[data(view)]
59pub struct BlockArea {
60    pub min: BlockPos,
61    pub max: BlockPos,
62    pub palette: Vec<BlockKind>,
63    pub blocks: Vec<u16>,
64}
65
66impl BlockArea {
67    /// The kind of block at `pos`, if it's inside.
68    pub fn at(&self, pos: BlockPos) -> Option<&BlockKind> {
69        let (w, d) = (self.max.x - self.min.x + 1, self.max.z - self.min.z + 1);
70        let (x, y, z) = (pos.x - self.min.x, pos.y - self.min.y, pos.z - self.min.z);
71        let inside = (0..w).contains(&x)
72            && (0..d).contains(&z)
73            && (0..=self.max.y - self.min.y).contains(&y);
74        let index = inside
75            .then(|| self.blocks.get(((y * d + z) * w + x) as usize))
76            .flatten()?;
77        self.palette.get(usize::from(*index))
78    }
79}
80
81/// An entity near this client's player.
82#[derive(Debug, Clone, PartialEq, Schema)]
83#[data(view)]
84pub struct NearbyEntity {
85    pub uuid: String,
86    pub entity_type: Atom,
87    pub pos: Point,
88    pub eye_height: f64,
89    /// Blocks a tick.
90    pub velocity: Point,
91    pub health: Option<f32>,
92    /// A player's name.
93    pub name: Option<String>,
94    /// A monster, hostile to players.
95    pub hostile: bool,
96}
97
98/// How a container slot is clicked: the game's own modes (`ClickType`).
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Schema)]
100pub enum ClickMode {
101    Pickup,
102    QuickMove,
103    Swap,
104    Clone,
105    Throw,
106    QuickCraft,
107    PickupAll,
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn an_area_finds_its_blocks() {
116        let kind = |id: &str| BlockKind {
117            id: id.into(),
118            solid: true,
119            liquid: false,
120            climbable: false,
121            harmful: false,
122        };
123        let area = BlockArea {
124            min: BlockPos::new(0, 0, 0),
125            max: BlockPos::new(1, 1, 0),
126            palette: vec![kind("a"), kind("b")],
127            blocks: vec![0, 1, 1, 0],
128        };
129        assert_eq!(
130            area.at(BlockPos::new(1, 0, 0)).map(|k| k.id.as_str()),
131            Some("b")
132        );
133        assert_eq!(
134            area.at(BlockPos::new(0, 1, 0)).map(|k| k.id.as_str()),
135            Some("b")
136        );
137        assert_eq!(area.at(BlockPos::new(2, 0, 0)), None);
138    }
139}