1use azalea_block::{BlockState, BlockTrait, fluid_state::FluidState};
2use azalea_core::{
3 aabb::AABB,
4 position::{BlockPos, Vec3},
5};
6use azalea_entity::{
7 Attributes, HasClientLoaded, Jumping, LocalEntity, LookDirection, OnClimbable, Physics,
8 PlayerAbilities, Pose, Position, metadata::Sprinting, move_relative,
9};
10use azalea_world::{Instance, InstanceContainer, InstanceName};
11use bevy_ecs::prelude::*;
12
13use crate::{
14 collision::{
15 MoveCtx, MoverType, Shapes,
16 entity_collisions::{CollidableEntityQuery, PhysicsQuery, get_entity_collisions},
17 move_colliding,
18 world_collisions::{get_block_and_liquid_collisions, get_block_collisions},
19 },
20 get_block_pos_below_that_affects_movement, handle_relative_friction_and_calculate_movement,
21 local_player::PhysicsState,
22};
23
24#[allow(clippy::type_complexity)]
27pub fn travel(
28 mut query: Query<
29 (
30 Entity,
31 &Attributes,
32 &InstanceName,
33 &OnClimbable,
34 &Jumping,
35 Option<&PhysicsState>,
36 Option<&Sprinting>,
37 Option<&Pose>,
38 Option<&PlayerAbilities>,
39 &mut Physics,
40 &mut LookDirection,
41 &mut Position,
42 ),
43 (With<LocalEntity>, With<HasClientLoaded>),
44 >,
45 instance_container: Res<InstanceContainer>,
46 physics_query: PhysicsQuery,
47 collidable_entity_query: CollidableEntityQuery,
48) {
49 for (
50 entity,
51 attributes,
52 world_name,
53 on_climbable,
54 jumping,
55 physics_state,
56 sprinting,
57 pose,
58 abilities,
59 mut physics,
60 direction,
61 position,
62 ) in &mut query
63 {
64 let Some(world_lock) = instance_container.get(world_name) else {
65 continue;
66 };
67 let world = world_lock.read();
68
69 let sprinting = *sprinting.unwrap_or(&Sprinting(false));
70
71 let mut ctx = MoveCtx {
74 mover_type: MoverType::Own,
75 world: &world,
76 position,
77 physics: &mut physics,
78 source_entity: entity,
79 physics_query: &physics_query,
80 collidable_entity_query: &collidable_entity_query,
81 physics_state,
82 attributes,
83 abilities,
84 direction: *direction,
85 sprinting,
86 on_climbable: *on_climbable,
87 pose: pose.copied(),
88 jumping: *jumping,
89 };
90
91 if ctx.physics.is_in_water() || ctx.physics.is_in_lava() {
92 travel_in_fluid(&mut ctx);
96 } else {
97 travel_in_air(&mut ctx);
98 }
99 }
100}
101
102fn travel_in_air(ctx: &mut MoveCtx) {
104 let gravity = get_effective_gravity();
105
106 let block_pos_below = get_block_pos_below_that_affects_movement(*ctx.position);
107
108 let block_state_below = ctx
109 .world
110 .chunks
111 .get_block_state(block_pos_below)
112 .unwrap_or(BlockState::AIR);
113 let block_below: Box<dyn BlockTrait> = block_state_below.into();
114 let block_friction = block_below.behavior().friction;
115
116 let inertia = if ctx.physics.on_ground() {
117 block_friction * 0.91
118 } else {
119 0.91
120 };
121
122 let mut movement = handle_relative_friction_and_calculate_movement(ctx, block_friction);
124
125 movement.y -= gravity;
126
127 if false {
135 ctx.physics.velocity = movement;
136 } else {
137 ctx.physics.velocity = Vec3 {
138 x: movement.x * inertia as f64,
139 y: movement.y * 0.9800000190734863f64,
140 z: movement.z * inertia as f64,
141 };
142 }
143}
144
145fn travel_in_fluid(ctx: &mut MoveCtx) {
146 let moving_down = ctx.physics.velocity.y <= 0.;
147 let y = ctx.position.y;
148 let gravity = get_effective_gravity();
149
150 let acceleration = Vec3::new(
151 ctx.physics.x_acceleration as f64,
152 ctx.physics.y_acceleration as f64,
153 ctx.physics.z_acceleration as f64,
154 );
155
156 if ctx.physics.was_touching_water {
157 let mut water_movement_speed = if *ctx.sprinting { 0.9 } else { 0.8 };
158 let mut speed = 0.02;
159 let mut water_efficiency_modifier =
160 ctx.attributes.water_movement_efficiency.calculate() as f32;
161 if !ctx.physics.on_ground() {
162 water_efficiency_modifier *= 0.5;
163 }
164
165 if water_efficiency_modifier > 0. {
166 water_movement_speed += (0.54600006 - water_movement_speed) * water_efficiency_modifier;
167 speed += (ctx.attributes.movement_speed.calculate() as f32 - speed)
168 * water_efficiency_modifier;
169 }
170
171 move_relative(ctx.physics, ctx.direction, speed, acceleration);
176 move_colliding(ctx, ctx.physics.velocity).expect("Entity should exist");
177
178 let mut new_velocity = ctx.physics.velocity;
179 if ctx.physics.horizontal_collision && *ctx.on_climbable {
180 new_velocity.y = 0.2;
182 }
183 new_velocity.x *= water_movement_speed as f64;
184 new_velocity.y *= 0.8;
185 new_velocity.z *= water_movement_speed as f64;
186 ctx.physics.velocity =
187 get_fluid_falling_adjusted_movement(gravity, moving_down, new_velocity, ctx.sprinting);
188 } else {
189 move_relative(ctx.physics, ctx.direction, 0.02, acceleration);
190 move_colliding(ctx, ctx.physics.velocity).expect("Entity should exist");
191
192 if ctx.physics.lava_fluid_height <= fluid_jump_threshold() {
193 ctx.physics.velocity.x *= 0.5;
194 ctx.physics.velocity.y *= 0.8;
195 ctx.physics.velocity.z *= 0.5;
196 let new_velocity = get_fluid_falling_adjusted_movement(
197 gravity,
198 moving_down,
199 ctx.physics.velocity,
200 ctx.sprinting,
201 );
202 ctx.physics.velocity = new_velocity;
203 } else {
204 ctx.physics.velocity *= 0.5;
205 }
206
207 if gravity != 0.0 {
208 ctx.physics.velocity.y -= gravity / 4.0;
209 }
210 }
211
212 let velocity = ctx.physics.velocity;
213 if ctx.physics.horizontal_collision
214 && is_free(
215 ctx.world,
216 ctx.source_entity,
217 ctx.physics_query,
218 ctx.collidable_entity_query,
219 ctx.physics,
220 ctx.physics.bounding_box,
221 velocity.up(0.6).down(ctx.position.y).up(y),
222 )
223 {
224 ctx.physics.velocity.y = 0.3;
225 }
226}
227
228fn get_fluid_falling_adjusted_movement(
229 gravity: f64,
230 moving_down: bool,
231 new_velocity: Vec3,
232 sprinting: Sprinting,
233) -> Vec3 {
234 if gravity != 0. && !*sprinting {
235 let new_y_velocity = if moving_down
236 && (new_velocity.y - 0.005).abs() >= 0.003
237 && f64::abs(new_velocity.y - gravity / 16.0) < 0.003
238 {
239 -0.003
240 } else {
241 new_velocity.y - gravity / 16.0
242 };
243
244 Vec3 {
245 x: new_velocity.x,
246 y: new_y_velocity,
247 z: new_velocity.z,
248 }
249 } else {
250 new_velocity
251 }
252}
253
254fn is_free(
255 world: &Instance,
256 source_entity: Entity,
257 physics_query: &PhysicsQuery,
258 collidable_entity_query: &CollidableEntityQuery,
259 entity_physics: &Physics,
260 bounding_box: AABB,
261 delta: Vec3,
262) -> bool {
263 let bounding_box = bounding_box.move_relative(delta);
264
265 no_collision(
266 world,
267 Some(source_entity),
268 physics_query,
269 collidable_entity_query,
270 entity_physics,
271 &bounding_box,
272 false,
273 ) && !contains_any_liquid(world, bounding_box)
274}
275
276pub fn no_collision(
277 world: &Instance,
278 source_entity: Option<Entity>,
279 physics_query: &PhysicsQuery,
280 collidable_entity_query: &CollidableEntityQuery,
281 entity_physics: &Physics,
282 aabb: &AABB,
283 include_liquid_collisions: bool,
284) -> bool {
285 let collisions = if include_liquid_collisions {
286 get_block_and_liquid_collisions(world, aabb)
287 } else {
288 get_block_collisions(world, aabb)
289 };
290
291 for collision in collisions {
292 if !collision.is_empty() {
293 return false;
294 }
295 }
296
297 if !get_entity_collisions(
298 world,
299 aabb,
300 source_entity,
301 physics_query,
302 collidable_entity_query,
303 )
304 .is_empty()
305 {
306 false
307 } else if source_entity.is_none() {
308 true
309 } else {
310 let collision = border_collision(entity_physics, aabb);
311 if let Some(collision) = collision {
312 !Shapes::matches_anywhere(&collision.into(), &aabb.into(), |a, b| a && b)
314 } else {
315 true
316 }
317 }
318}
319
320fn border_collision(_entity_physics: &Physics, _aabb: &AABB) -> Option<AABB> {
321 None
324}
325
326fn contains_any_liquid(world: &Instance, bounding_box: AABB) -> bool {
327 let min = bounding_box.min.to_block_pos_floor();
328 let max = bounding_box.max.to_block_pos_ceil();
329
330 for x in min.x..max.x {
331 for y in min.y..max.y {
332 for z in min.z..max.z {
333 let block_state = world
334 .chunks
335 .get_block_state(BlockPos::new(x, y, z))
336 .unwrap_or_default();
337 if !FluidState::from(block_state).is_empty() {
338 return true;
339 }
340 }
341 }
342 }
343
344 false
345}
346
347fn get_effective_gravity() -> f64 {
348 0.08
350}
351
352pub fn fluid_jump_threshold() -> f64 {
353 0.4
357}