veloren_common/states/
glide_wield.rs

1use super::utils::*;
2use crate::{
3    comp::{
4        CharacterState, InventoryAction, Ori, StateUpdate, character_state::OutputEvents,
5        controller::InputKind, slot::EquipSlot,
6    },
7    event::LocalEvent,
8    outcome::Outcome,
9    states::{
10        behavior::{CharacterBehavior, JoinData},
11        glide, idle,
12    },
13};
14use serde::{Deserialize, Serialize};
15
16#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
17pub struct Data {
18    pub ori: Ori,
19    span_length: f32,
20    chord_length: f32,
21}
22
23impl From<&JoinData<'_>> for Data {
24    fn from(data: &JoinData) -> Self {
25        let scale = data.body.dimensions().z.sqrt();
26        Self {
27            // Aspect ratio is what really matters for lift/drag ratio
28            // and the aerodynamics model works for ARs up to 25.
29            //
30            // The inflated dimensions are hopefully only a temporary
31            // bandaid for the poor glide ratio experienced under 2.5G.
32            //
33            // The formula is:
34            //  s: span_length_modifier
35            //  c: chord_length_modifier
36            //  h: height (this is a hack to balance different races)
37            //
38            // p_a = Pi/4 * c * h * s * h
39            // AR
40            //  = (s * h)^2 / p_a
41            //  = (s * h)^2  / (Pi / 4 * (c * h) * (s * h))
42            //  = (s * h) / (c * h) / (Pi / 4)
43            //  = s / c / Pi/4
44            //
45            // or if c is 1,
46            //  = s / Pi/4
47            //
48            // In other words, the bigger `span_length` the better.
49            //
50            // A span/chord ratio of 4.5 gives an AR of ~5.73.
51            // A span/chord ratio of 3.0 gives an ARI of ~3.82.
52            span_length: scale * 4.5,
53            chord_length: scale,
54            ori: *data.ori,
55        }
56    }
57}
58
59impl CharacterBehavior for Data {
60    fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
61        let mut update = StateUpdate::from(data);
62
63        handle_orientation(data, &mut update, 1.0, None);
64        handle_move(data, &mut update, 1.0);
65        handle_jump(data, output_events, &mut update, 1.0);
66        if input_is_pressed(data, InputKind::Roll) {
67            handle_input(data, output_events, &mut update, InputKind::Roll);
68        }
69        handle_glider_input_or(data, &mut update, output_events, handle_wield);
70
71        // If still in this state, do the things
72        if matches!(update.character, CharacterState::GlideWield(_)) {
73            // If not on the ground while wielding glider enter gliding state
74            update.character = if data.physics.on_ground.is_none() {
75                CharacterState::Glide(glide::Data::new(
76                    self.span_length,
77                    self.chord_length,
78                    self.ori,
79                ))
80            // make sure we have a glider and we're not (too deep) in water
81            } else if data
82                .inventory
83                .and_then(|inv| inv.equipped(EquipSlot::Glider))
84                .is_some()
85                && data.physics.in_liquid().is_none_or(|depth| depth < 0.5)
86            {
87                CharacterState::GlideWield(Self {
88                    // Glider tilt follows look dir
89                    ori: self.ori.slerped_towards(
90                        data.ori.slerped_towards(
91                            Ori::from(data.inputs.look_dir).pitched_up(0.6),
92                            (1.0 + data.inputs.look_dir.dot(*data.ori.look_dir()).max(0.0)) / 3.0,
93                        ),
94                        5.0 * data.dt.0,
95                    ),
96                    ..*self
97                })
98            } else {
99                CharacterState::Idle(idle::Data::default())
100            };
101        }
102
103        update
104    }
105
106    fn manipulate_loadout(
107        &self,
108        data: &JoinData,
109        output_events: &mut OutputEvents,
110        inv_action: InventoryAction,
111    ) -> StateUpdate {
112        let mut update = StateUpdate::from(data);
113        handle_manipulate_loadout(data, output_events, &mut update, inv_action);
114        update
115    }
116
117    fn unwield(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
118        let mut update = StateUpdate::from(data);
119        output_events.emit_local(LocalEvent::CreateOutcome(Outcome::Glider {
120            pos: data.pos.0,
121            wielded: false,
122        }));
123        update.character = CharacterState::Idle(idle::Data::default());
124        update
125    }
126
127    fn sit(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
128        let mut update = StateUpdate::from(data);
129        attempt_sit(data, &mut update);
130        update
131    }
132
133    fn crawl(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
134        let mut update = StateUpdate::from(data);
135        attempt_crawl(data, &mut update);
136        update
137    }
138
139    fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
140        let mut update = StateUpdate::from(data);
141        attempt_dance(data, &mut update);
142        update
143    }
144
145    fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
146        let mut update = StateUpdate::from(data);
147        attempt_sneak(data, &mut update);
148        update
149    }
150}