steel_core/entity/ai/goal/
squid_flee_goal.rs1use steel_registry::{blocks::block_state_ext::BlockStateExt, fluid::FluidStateExt};
2use steel_utils::{BlockPos, Downcast, TICKS_PER_SECOND};
3
4use crate::{
5 entity::{
6 PathfinderMob, SharedEntity,
7 ai::goal::selector::{Goal, GoalControls},
8 entities::SquidEntity,
9 },
10 world::LevelReader,
11};
12
13const SQUID_FLEE_SPEED: f64 = 3.0;
14const SQUID_FLEE_MIN_DISTANCE: f64 = 5.0;
15
16pub struct SquidFleeGoal {
17 flee_ticks: i32,
18}
19
20impl SquidFleeGoal {
21 pub const fn new() -> Self {
22 Self { flee_ticks: 0 }
23 }
24}
25
26impl Goal for SquidFleeGoal {
27 fn controls(&self) -> GoalControls {
28 GoalControls::EMPTY
29 }
30
31 fn can_use(&mut self, mob: &dyn PathfinderMob) -> bool {
32 mob.is_in_water()
33 && mob
34 .last_hurt_by_mob()
35 .is_some_and(|attacker| mob.distance_to_sqr(attacker.position()) < 100.0)
36 }
37
38 fn requires_update_every_tick(&self) -> bool {
39 true
40 }
41
42 fn start(&mut self, _mob: &dyn PathfinderMob) {
43 self.flee_ticks = 0;
44 }
45
46 fn tick(&mut self, mob: &dyn PathfinderMob, entity: &SharedEntity) {
47 self.flee_ticks += 1;
48
49 let Some(attacker) = mob.last_hurt_by_mob() else {
50 return;
51 };
52
53 let Some(squid) = mob.downcast_ref::<SquidEntity>() else {
54 tracing::warn!("SquidFleeGoal assigned to non-squid entity");
55 return;
56 };
57
58 let Some(world) = mob.level() else {
59 return;
60 };
61
62 let squid_pos = entity.position();
63 let attacker_pos = attacker.position();
64 let mut flee_to = squid_pos - attacker_pos;
65
66 let block_pos = BlockPos::from(squid_pos + flee_to);
67 let block_state = world.get_block_state(block_pos);
68 let fluid_state = block_state.get_fluid_state();
69
70 if fluid_state.is_water() || block_state.is_air() {
71 let distance = flee_to.length();
72
73 if distance > 0.0 {
74 let avoid_speed = if distance > SQUID_FLEE_MIN_DISTANCE {
78 SQUID_FLEE_SPEED
79 - (distance - SQUID_FLEE_MIN_DISTANCE) / SQUID_FLEE_MIN_DISTANCE
80 } else {
81 SQUID_FLEE_SPEED
82 };
83
84 if avoid_speed > 0.0 {
85 flee_to *= avoid_speed;
86 }
87 }
88
89 if block_state.is_air() {
90 flee_to.y = 0.0;
91 }
92
93 squid.set_movement_vector(flee_to / f64::from(TICKS_PER_SECOND));
94 }
95 }
96}