veloren_common/states/
climb.rs1use crate::{
2 comp::{
3 CharacterState, Ori, StateUpdate,
4 character_state::OutputEvents,
5 skills::{ClimbSkill::*, SKILL_MODIFIERS, Skill},
6 },
7 consts::GRAVITY,
8 states::{
9 behavior::{CharacterBehavior, JoinData},
10 idle,
11 utils::*,
12 },
13 util::Dir,
14};
15use serde::{Deserialize, Serialize};
16use vek::*;
17
18#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
20pub struct StaticData {
21 pub energy_cost: f32,
22 pub movement_speed: f32,
23}
24
25#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
26pub struct Data {
27 pub static_data: StaticData,
30}
31
32impl Data {
33 pub fn create_adjusted_by_skills(join_data: &JoinData) -> Self {
34 let modifiers = SKILL_MODIFIERS.general_tree.climb;
35 let mut data = Data::default();
36 if let Ok(level) = join_data.skill_set.skill_level(Skill::Climb(Cost)) {
37 data.static_data.energy_cost *= modifiers.energy_cost.powi(level.into());
38 }
39 if let Ok(level) = join_data.skill_set.skill_level(Skill::Climb(Speed)) {
40 data.static_data.movement_speed *= modifiers.speed.powi(level.into());
41 }
42 data
43 }
44}
45
46impl Default for Data {
47 fn default() -> Self {
48 Data {
49 static_data: StaticData {
50 energy_cost: 15.0,
51 movement_speed: 5.0,
52 },
53 }
54 }
55}
56
57impl CharacterBehavior for Data {
58 fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
59 let mut update = StateUpdate::from(data);
60
61 let Some(wall_dir) = data.physics.on_wall else {
62 update.character = CharacterState::Idle(idle::Data::default());
63 return update;
64 };
65
66 if data.physics.on_ground.is_some() {
68 update.character = CharacterState::Idle(idle::Data::default());
69 return update;
70 }
71
72 let wall_relative_movement =
74 data.inputs.move_dir * data.inputs.look_dir.map(|e| e.signum()) * wall_dir;
75
76 let energy_use = if wall_relative_movement.reduce_partial_max() > 0.5 {
79 self.static_data.energy_cost
80 } else {
81 1.0
82 };
83 handle_walljump(data, output_events, &mut update);
84
85 if update
87 .energy
88 .try_change_by(-energy_use * data.dt.0)
89 .is_err()
90 {
91 update.character = CharacterState::Idle(idle::Data::default());
92 }
93
94 if let Some(ori_dir) = Dir::from_unnormalized(wall_dir.with_z(0.0)) {
96 update.ori = update.ori.slerped_towards(
98 Ori::from(ori_dir),
99 if data.physics.on_ground.is_some() {
100 9.0
101 } else {
102 2.0
103 } * data.dt.0,
104 );
105 };
106
107 update.vel.0.z += data.dt.0 * GRAVITY;
109
110 let upwards_vel = data.inputs.move_dir.dot(wall_dir.xy());
112 let crossed = wall_dir.cross(Vec3::unit_z());
113 let lateral_vel = crossed * data.inputs.move_dir.dot(crossed.xy());
114
115 update.vel.0 += data.dt.0
116 * (lateral_vel.with_z(upwards_vel) + wall_dir)
117 * self.static_data.movement_speed.powi(2)
118 * data.scale.map_or(1.0, |s| s.0);
119
120 update
121 }
122}