147 lines
4.2 KiB
Rust
147 lines
4.2 KiB
Rust
use super::{Controls, Goal, to_goal_ticks};
|
|
use crate::entity::ai::goal::GoalFuture;
|
|
use crate::entity::ai::target_predicate::TargetPredicate;
|
|
use crate::entity::living::LivingEntity;
|
|
use crate::entity::mob::Mob;
|
|
use crate::entity::{EntityBase, mob::MobEntity};
|
|
use rand::Rng;
|
|
use std::sync::Arc;
|
|
|
|
const UNSET: i32 = 0;
|
|
const CAN_TRACK: i32 = 1;
|
|
const CANNOT_TRACK: i32 = 2;
|
|
|
|
#[expect(dead_code)]
|
|
pub struct TrackTargetGoal {
|
|
goal_control: Controls,
|
|
target: Option<Arc<dyn EntityBase>>,
|
|
check_visibility: bool,
|
|
check_can_navigate: bool,
|
|
can_navigate_flag: i32,
|
|
check_can_navigate_cooldown: i32,
|
|
time_without_visibility: i32,
|
|
max_time_without_visibility: i32, // Default 60
|
|
}
|
|
|
|
#[expect(dead_code)]
|
|
impl TrackTargetGoal {
|
|
#[must_use]
|
|
pub fn new(check_visibility: bool, check_can_navigate: bool) -> Self {
|
|
Self {
|
|
goal_control: Controls::TARGET,
|
|
target: None,
|
|
check_visibility,
|
|
check_can_navigate,
|
|
can_navigate_flag: UNSET,
|
|
check_can_navigate_cooldown: 0,
|
|
time_without_visibility: 0,
|
|
max_time_without_visibility: 60,
|
|
}
|
|
}
|
|
|
|
pub fn with_default(check_visibility: bool) -> Self {
|
|
Self::new(check_visibility, false)
|
|
}
|
|
|
|
// TODO: get from entity attribute
|
|
pub fn get_follow_range(_mob: &MobEntity) -> f32 {
|
|
32.0
|
|
}
|
|
|
|
fn can_navigate_to_entity(&mut self, mob: &dyn Mob, _target: &LivingEntity) -> bool {
|
|
self.check_can_navigate_cooldown = to_goal_ticks(10 + mob.get_random().random_range(0..5));
|
|
// TODO: after implementing path
|
|
false
|
|
}
|
|
|
|
pub fn can_track(
|
|
&mut self,
|
|
mob: &dyn Mob,
|
|
target: Option<&LivingEntity>,
|
|
target_predicate: &TargetPredicate,
|
|
) -> bool {
|
|
if target.is_none() {
|
|
return false;
|
|
}
|
|
let mob_entity = mob.get_mob_entity();
|
|
let target = target.unwrap();
|
|
let world = &mob_entity.living_entity.entity.world;
|
|
if !target_predicate.test(world.clone(), Some(&mob_entity.living_entity), target) {
|
|
return false;
|
|
} /*else if (!this.mob.isInPositionTargetRange(target.getBlockPos())) {
|
|
return false;
|
|
}*/
|
|
// TODO: implement this
|
|
|
|
if self.check_can_navigate {
|
|
self.check_can_navigate_cooldown -= 1;
|
|
if self.check_can_navigate_cooldown <= 0 {
|
|
self.can_navigate_flag = UNSET;
|
|
}
|
|
|
|
if self.can_navigate_flag == UNSET {
|
|
let value = if self.can_navigate_to_entity(mob, target) {
|
|
CAN_TRACK
|
|
} else {
|
|
CANNOT_TRACK
|
|
};
|
|
self.can_navigate_flag = value;
|
|
}
|
|
|
|
if self.can_navigate_flag == CANNOT_TRACK {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
true
|
|
}
|
|
}
|
|
|
|
impl Goal for TrackTargetGoal {
|
|
fn should_continue<'a>(&'a self, mob: &'a dyn Mob) -> GoalFuture<'a, bool> {
|
|
Box::pin(async {
|
|
let mob = mob.get_mob_entity();
|
|
let mob_target = mob.target.lock().await;
|
|
|
|
// We need to decide which target to use for the check
|
|
let target = if mob_target.is_some() {
|
|
(*mob_target).clone()
|
|
} else {
|
|
self.target.clone()
|
|
};
|
|
|
|
// Drop the guard immediately after access to release the lock
|
|
drop(mob_target);
|
|
|
|
if target.is_none() {
|
|
return false;
|
|
} // TODO: continue when scoreboard team are implemented
|
|
true
|
|
})
|
|
}
|
|
|
|
fn start<'a>(&'a mut self, _mob: &'a dyn Mob) -> GoalFuture<'a, ()> {
|
|
Box::pin(async {
|
|
self.can_navigate_flag = 0;
|
|
self.check_can_navigate_cooldown = 0;
|
|
self.time_without_visibility = 0;
|
|
// No await needed here
|
|
})
|
|
}
|
|
|
|
fn stop<'a>(&'a mut self, mob: &'a dyn Mob) -> GoalFuture<'a, ()> {
|
|
Box::pin(async {
|
|
let mob = mob.get_mob_entity();
|
|
|
|
let mut mob_target = mob.target.lock().await;
|
|
*mob_target = None;
|
|
|
|
self.target = None;
|
|
})
|
|
}
|
|
|
|
fn controls(&self) -> Controls {
|
|
self.goal_control
|
|
}
|
|
}
|