Skip to main content

flatland_presentation/
player_states.rs

1//! Canonical player presentation keys for gfx `sprite_modes` (designer-facing).
2
3/// Standard presentation keys shown in content-admin and used on the wire.
4pub const PLAYER_PRESENTATION_STATES: &[(&str, &str)] = &[
5    ("combat", "Combat — in melee range or casting"),
6    ("pursue", "Pursue — has target, out of attack range"),
7    ("sprinting", "Sprinting — fast movement"),
8    ("walking", "Walking — normal movement"),
9    ("harvesting", "Harvesting — gathering a resource node"),
10    ("crafting", "Crafting — timed blueprint craft"),
11    ("talking", "Talking — open NPC conversation"),
12    ("chatting", "Chatting — recent nearby / whisper-stone chat"),
13    ("idle", "Idle — stationary"),
14    ("telegraph", "Telegraph — attack or cast wind-up"),
15];
16
17/// Inputs for player presentation (keeps this crate free of sim types).
18#[derive(Debug, Clone, Copy)]
19pub struct PlayerPresentationInput {
20    pub harvesting: bool,
21    pub crafting: bool,
22    pub talking: bool,
23    pub chatting: bool,
24    pub in_combat: bool,
25    pub pursue: bool,
26    pub in_attack_range: bool,
27    pub is_moving: bool,
28    pub sprinting: bool,
29    pub telegraph_active: bool,
30}
31
32pub fn resolve_player_presentation_key(input: PlayerPresentationInput) -> &'static str {
33    if input.telegraph_active {
34        return "telegraph";
35    }
36    if input.harvesting {
37        return "harvesting";
38    }
39    if input.crafting {
40        return "crafting";
41    }
42    if input.talking {
43        return "talking";
44    }
45    if input.chatting {
46        return "chatting";
47    }
48    if input.in_combat && input.in_attack_range {
49        return "combat";
50    }
51    if input.pursue || (input.in_combat && !input.in_attack_range) {
52        return "pursue";
53    }
54    if input.in_combat {
55        return "combat";
56    }
57    if input.is_moving && input.sprinting {
58        return "sprinting";
59    }
60    if input.is_moving {
61        return "walking";
62    }
63    "idle"
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn player_harvesting_wins() {
72        let key = resolve_player_presentation_key(PlayerPresentationInput {
73            harvesting: true,
74            crafting: false,
75            talking: false,
76            chatting: false,
77            in_combat: true,
78            pursue: false,
79            in_attack_range: true,
80            is_moving: true,
81            sprinting: true,
82            telegraph_active: false,
83        });
84        assert_eq!(key, "harvesting");
85    }
86
87    #[test]
88    fn player_talking_over_walking() {
89        let key = resolve_player_presentation_key(PlayerPresentationInput {
90            harvesting: false,
91            crafting: false,
92            talking: true,
93            chatting: false,
94            in_combat: false,
95            pursue: false,
96            in_attack_range: false,
97            is_moving: true,
98            sprinting: false,
99            telegraph_active: false,
100        });
101        assert_eq!(key, "talking");
102    }
103
104    #[test]
105    fn player_pursue_out_of_range() {
106        let key = resolve_player_presentation_key(PlayerPresentationInput {
107            harvesting: false,
108            crafting: false,
109            talking: false,
110            chatting: false,
111            in_combat: true,
112            pursue: false,
113            in_attack_range: false,
114            is_moving: true,
115            sprinting: true,
116            telegraph_active: false,
117        });
118        assert_eq!(key, "pursue");
119    }
120}