1use azalea_block::{
2 fluid_state::{FluidKind, FluidState},
3 BlockState,
4};
5use azalea_core::{
6 direction::Direction,
7 position::{BlockPos, Vec3},
8};
9use azalea_entity::{InLoadedChunk, LocalEntity, Physics, Position};
10use azalea_world::{Instance, InstanceContainer, InstanceName};
11use bevy_ecs::prelude::*;
12
13use crate::collision::legacy_blocks_motion;
14
15#[allow(clippy::type_complexity)]
16pub fn update_in_water_state_and_do_fluid_pushing(
17 mut query: Query<
18 (&mut Physics, &Position, &InstanceName),
19 (With<LocalEntity>, With<InLoadedChunk>),
20 >,
21 instance_container: Res<InstanceContainer>,
22) {
23 for (mut physics, position, instance_name) in &mut query {
24 let world_lock = instance_container
25 .get(instance_name)
26 .expect("All entities with InLoadedChunk should be in a valid world");
27 let world = world_lock.read();
28
29 physics.water_fluid_height = 0.;
30 physics.lava_fluid_height = 0.;
31
32 update_in_water_state_and_do_water_current_pushing(&mut physics, &world, position);
33
34 let is_ultrawarm = world
35 .registries
36 .dimension_type()
37 .and_then(|d| d.map.get(instance_name).map(|d| d.ultrawarm))
38 == Some(Some(true));
39 let lava_push_factor = if is_ultrawarm {
40 0.007
41 } else {
42 0.0023333333333333335
43 };
44
45 update_fluid_height_and_do_fluid_pushing(
46 &mut physics,
47 &world,
48 FluidKind::Lava,
49 lava_push_factor,
50 );
51 }
52}
53fn update_in_water_state_and_do_water_current_pushing(
54 physics: &mut Physics,
55 world: &Instance,
56 _position: &Position,
57) {
58 if update_fluid_height_and_do_fluid_pushing(physics, world, FluidKind::Water, 0.014) {
67 physics.reset_fall_distance();
72 physics.was_touching_water = true;
73 physics.clear_fire();
74 } else {
75 physics.was_touching_water = false;
76 }
77}
78
79fn update_fluid_height_and_do_fluid_pushing(
80 physics: &mut Physics,
81 world: &Instance,
82 checking_fluid: FluidKind,
83 fluid_push_factor: f64,
84) -> bool {
85 let checking_liquids_aabb = physics.bounding_box.deflate_all(0.001);
90
91 let min_x = checking_liquids_aabb.min.x.floor() as i32;
92 let min_y = checking_liquids_aabb.min.y.floor() as i32;
93 let min_z = checking_liquids_aabb.min.z.floor() as i32;
94
95 let max_x = checking_liquids_aabb.max.x.ceil() as i32;
96 let max_y = checking_liquids_aabb.max.y.ceil() as i32;
97 let max_z = checking_liquids_aabb.max.z.ceil() as i32;
98
99 let mut min_height_touching = 0.;
100 let is_entity_pushable_by_fluid = true;
101 let mut touching_fluid = false;
102 let mut additional_player_delta = Vec3::default();
103 let mut num_fluids_being_touched = 0;
104
105 for cur_x in min_x..=max_x {
106 for cur_y in min_y..=max_y {
107 for cur_z in min_z..=max_z {
108 let cur_pos = BlockPos::new(cur_x, cur_y, cur_z);
109 let Some(fluid_at_cur_pos) = world.get_fluid_state(&cur_pos) else {
110 continue;
111 };
112 if fluid_at_cur_pos.kind != checking_fluid {
113 continue;
114 }
115 let fluid_max_y = (cur_y as f32 + fluid_at_cur_pos.height()) as f64;
116 if fluid_max_y < checking_liquids_aabb.min.y {
117 continue;
118 }
119 touching_fluid = true;
120 min_height_touching = f64::max(
121 fluid_max_y - checking_liquids_aabb.min.y,
122 min_height_touching,
123 );
124 if !is_entity_pushable_by_fluid {
125 continue;
126 }
127 let mut additional_player_delta_for_fluid =
128 get_fluid_flow(&fluid_at_cur_pos, world, cur_pos);
129 if min_height_touching < 0.4 {
130 additional_player_delta_for_fluid *= min_height_touching;
131 };
132
133 additional_player_delta += additional_player_delta_for_fluid;
134 num_fluids_being_touched += 1;
135 }
136 }
137 }
138
139 if additional_player_delta.length() > 0. {
140 additional_player_delta /= num_fluids_being_touched as f64;
141
142 let player_delta = physics.velocity;
147 additional_player_delta *= fluid_push_factor;
148 const MIN_PUSH: f64 = 0.003;
149 const MIN_PUSH_LENGTH: f64 = MIN_PUSH * 1.5;
150
151 if player_delta.x.abs() < MIN_PUSH
152 && player_delta.z.abs() < MIN_PUSH
153 && additional_player_delta.length() < MIN_PUSH_LENGTH
154 {
155 additional_player_delta = additional_player_delta.normalize() * MIN_PUSH_LENGTH;
156 }
157
158 physics.velocity += additional_player_delta;
159 }
160
161 match checking_fluid {
162 FluidKind::Water => physics.water_fluid_height = min_height_touching,
163 FluidKind::Lava => physics.lava_fluid_height = min_height_touching,
164 FluidKind::Empty => panic!("FluidKind::Empty should not be passed to update_fluid_height"),
165 };
166
167 touching_fluid
168}
169
170pub fn update_swimming() {
171 }
173
174pub fn get_fluid_flow(fluid: &FluidState, world: &Instance, pos: BlockPos) -> Vec3 {
176 let mut z_flow: f64 = 0.;
177 let mut x_flow: f64 = 0.;
178
179 for direction in Direction::HORIZONTAL {
180 let adjacent_block_pos = pos.offset_with_direction(direction);
181 let adjacent_fluid_state = world
182 .get_fluid_state(&adjacent_block_pos)
183 .unwrap_or_default();
184 if fluid.affects_flow(&adjacent_fluid_state) {
185 let mut adjacent_fluid_height = adjacent_fluid_state.height();
186 let mut adjacent_height_difference: f32 = 0.;
187
188 if adjacent_fluid_height == 0. {
189 if !legacy_blocks_motion(
190 world
191 .get_block_state(&adjacent_block_pos)
192 .unwrap_or_default(),
193 ) {
194 let block_pos_below_adjacent = adjacent_block_pos.down(1);
195 let fluid_below_adjacent = world
196 .get_fluid_state(&block_pos_below_adjacent)
197 .unwrap_or_default();
198
199 if fluid.affects_flow(&fluid_below_adjacent) {
200 adjacent_fluid_height = fluid_below_adjacent.height();
201 if adjacent_fluid_height > 0. {
202 adjacent_height_difference =
203 fluid.height() - (adjacent_fluid_height - 0.8888889);
204 }
205 }
206 }
207 } else if adjacent_fluid_height > 0. {
208 adjacent_height_difference = fluid.height() - adjacent_fluid_height;
209 }
210
211 if adjacent_height_difference != 0. {
212 x_flow += (direction.x() as f32 * adjacent_height_difference) as f64;
213 z_flow += (direction.z() as f32 * adjacent_height_difference) as f64;
214 }
215 }
216 }
217
218 let mut flow = Vec3::new(x_flow, 0., z_flow);
219 if fluid.falling {
220 for direction in Direction::HORIZONTAL {
221 let adjacent_block_pos = pos.offset_with_direction(direction);
222 if is_solid_face(fluid, world, adjacent_block_pos, direction)
223 || is_solid_face(fluid, world, adjacent_block_pos.up(1), direction)
224 {
225 flow = flow.normalize() + Vec3::new(0., -6., 0.);
226 break;
227 }
228 }
229 }
230
231 flow.normalize()
232}
233
234fn is_solid_face(
236 fluid: &FluidState,
237 world: &Instance,
238 adjacent_pos: BlockPos,
239 direction: Direction,
240) -> bool {
241 let block_state = world.get_block_state(&adjacent_pos).unwrap_or_default();
242 let fluid_state = world.get_fluid_state(&adjacent_pos).unwrap_or_default();
243 if fluid_state.is_same_kind(fluid) {
244 return false;
245 }
246 if direction == Direction::Up {
247 return true;
248 }
249 let registry_block = azalea_registry::Block::from(block_state);
250 if matches!(
251 registry_block,
252 azalea_registry::Block::Ice | azalea_registry::Block::FrostedIce
254 ) {
255 return false;
256 }
257 is_face_sturdy(block_state, world, adjacent_pos, direction)
258}
259
260fn is_face_sturdy(
261 _block_state: BlockState,
262 _world: &Instance,
263 _pos: BlockPos,
264 _direction: Direction,
265) -> bool {
266 false
274}