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