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: impl Into<BlockPos>) -> Option<&BlockKind> {
69        let pos = pos.into();
70        let (w, d) = (self.max.x - self.min.x + 1, self.max.z - self.min.z + 1);
71        let (x, y, z) = (pos.x - self.min.x, pos.y - self.min.y, pos.z - self.min.z);
72        let inside = (0..w).contains(&x)
73            && (0..d).contains(&z)
74            && (0..=self.max.y - self.min.y).contains(&y);
75        let index = inside
76            .then(|| self.blocks.get(((y * d + z) * w + x) as usize))
77            .flatten()?;
78        self.palette.get(usize::from(*index))
79    }
80}
81
82/// An entity near this client's player.
83#[derive(Debug, Clone, PartialEq, Schema)]
84#[data(view)]
85pub struct NearbyEntity {
86    pub uuid: String,
87    pub entity_type: Atom,
88    pub pos: Point,
89    pub eye_height: f64,
90    /// Blocks a tick.
91    pub velocity: Point,
92    pub health: Option<f32>,
93    /// A player's name.
94    pub name: Option<String>,
95    /// A monster, hostile to players.
96    pub hostile: bool,
97}
98
99/// How a container slot is clicked: the game's own modes (`ClickType`).
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Schema)]
101pub enum ClickMode {
102    Pickup,
103    QuickMove,
104    Swap,
105    Clone,
106    Throw,
107    QuickCraft,
108    PickupAll,
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn an_area_finds_its_blocks() {
117        let kind = |id: &str| BlockKind {
118            id: id.into(),
119            solid: true,
120            liquid: false,
121            climbable: false,
122            harmful: false,
123        };
124        let area = BlockArea {
125            min: BlockPos::new(0, 0, 0),
126            max: BlockPos::new(1, 1, 0),
127            palette: vec![kind("a"), kind("b")],
128            blocks: vec![0, 1, 1, 0],
129        };
130        assert_eq!(
131            area.at(BlockPos::new(1, 0, 0)).map(|k| k.id.as_str()),
132            Some("b")
133        );
134        assert_eq!(
135            area.at(BlockPos::new(0, 1, 0)).map(|k| k.id.as_str()),
136            Some("b")
137        );
138        assert_eq!(area.at(BlockPos::new(2, 0, 0)), None);
139    }
140}