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