mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'holychowders/store_last_known_pos' into 'master'
Store Target's Last Known Position See merge request veloren/veloren!3698
This commit is contained in:
commit
2f990fb06e
@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Sword
|
||||
- Doors now animate opening when entities are near them.
|
||||
- Musical instruments can now be crafted, looted and played
|
||||
- NPCs now move to their target's last known position.
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -420,15 +420,23 @@ pub struct Target {
|
||||
pub selected_at: f64,
|
||||
/// Whether the target has come close enough to trigger aggro.
|
||||
pub aggro_on: bool,
|
||||
pub last_known_pos: Option<Vec3<f32>>,
|
||||
}
|
||||
|
||||
impl Target {
|
||||
pub fn new(target: EcsEntity, hostile: bool, selected_at: f64, aggro_on: bool) -> Self {
|
||||
pub fn new(
|
||||
target: EcsEntity,
|
||||
hostile: bool,
|
||||
selected_at: f64,
|
||||
aggro_on: bool,
|
||||
last_known_pos: Option<Vec3<f32>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
target,
|
||||
hostile,
|
||||
selected_at,
|
||||
aggro_on,
|
||||
last_known_pos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -701,9 +701,8 @@ impl<'a> AgentData<'a> {
|
||||
},
|
||||
};
|
||||
|
||||
let is_detected = |entity: EcsEntity, e_pos: &Pos| {
|
||||
self.can_sense_directly_near(e_pos)
|
||||
|| self.can_see_entity(agent, controller, entity, e_pos, read_data)
|
||||
let is_detected = |entity: &EcsEntity, e_pos: &Pos| {
|
||||
self.detects_other(agent, controller, entity, e_pos, read_data)
|
||||
};
|
||||
|
||||
let target = entities_nearby
|
||||
@ -713,7 +712,7 @@ impl<'a> AgentData<'a> {
|
||||
.filter_map(|(entity, attack_target)| {
|
||||
get_pos(entity).map(|pos| (entity, pos, attack_target))
|
||||
})
|
||||
.filter(|(entity, e_pos, _)| is_detected(*entity, e_pos))
|
||||
.filter(|(entity, e_pos, _)| is_detected(entity, e_pos))
|
||||
.min_by_key(|(_, e_pos, attack_target)| {
|
||||
(
|
||||
*attack_target,
|
||||
@ -735,6 +734,7 @@ impl<'a> AgentData<'a> {
|
||||
hostile: attack_target,
|
||||
selected_at: read_data.time.0,
|
||||
aggro_on,
|
||||
last_known_pos: get_pos(entity).map(|pos| pos.0),
|
||||
})
|
||||
}
|
||||
|
||||
@ -1341,14 +1341,24 @@ impl<'a> AgentData<'a> {
|
||||
controller.push_utterance(UtteranceKind::Angry);
|
||||
}
|
||||
|
||||
agent.target = Some(Target::new(attacker, true, read_data.time.0, true));
|
||||
let attacker_pos = read_data.positions.get(attacker).map(|pos| pos.0);
|
||||
agent.target = Some(Target::new(
|
||||
attacker,
|
||||
true,
|
||||
read_data.time.0,
|
||||
true,
|
||||
attacker_pos,
|
||||
));
|
||||
|
||||
if let Some(tgt_pos) = read_data.positions.get(attacker) {
|
||||
if is_dead_or_invulnerable(attacker, read_data) {
|
||||
// FIXME?: Shouldn't target be set to `None`?
|
||||
// If is dead, then probably. If invulnerable, maybe not.
|
||||
agent.target =
|
||||
Some(Target::new(target, false, read_data.time.0, false));
|
||||
agent.target = Some(Target::new(
|
||||
target,
|
||||
false,
|
||||
read_data.time.0,
|
||||
false,
|
||||
Some(tgt_pos.0),
|
||||
));
|
||||
|
||||
self.idle(agent, controller, read_data, rng);
|
||||
} else {
|
||||
@ -1551,6 +1561,18 @@ impl<'a> AgentData<'a> {
|
||||
&& entities_have_line_of_sight(self.pos, self.body, other_pos, other_body, read_data)
|
||||
}
|
||||
|
||||
pub fn detects_other(
|
||||
&self,
|
||||
agent: &Agent,
|
||||
controller: &Controller,
|
||||
other: &EcsEntity,
|
||||
other_pos: &Pos,
|
||||
read_data: &ReadData,
|
||||
) -> bool {
|
||||
self.can_sense_directly_near(other_pos)
|
||||
|| self.can_see_entity(agent, controller, *other, other_pos, read_data)
|
||||
}
|
||||
|
||||
pub fn can_sense_directly_near(&self, e_pos: &Pos) -> bool {
|
||||
let chance = thread_rng().gen_bool(0.3);
|
||||
e_pos.0.distance_squared(self.pos.0) < 5_f32.powi(2) && chance
|
||||
|
@ -100,8 +100,10 @@ impl BehaviorTree {
|
||||
pub fn target() -> Self {
|
||||
Self {
|
||||
tree: vec![
|
||||
update_last_known_pos,
|
||||
untarget_if_dead,
|
||||
update_target_awareness,
|
||||
search_last_known_pos_if_not_alert,
|
||||
do_hostile_tree_if_hostile_and_aware,
|
||||
do_pet_tree_if_owned,
|
||||
do_pickup_loot,
|
||||
@ -266,6 +268,11 @@ fn target_if_attacked(bdata: &mut BehaviorData) -> bool {
|
||||
hostile: true,
|
||||
selected_at: bdata.read_data.time.0,
|
||||
aggro_on: true,
|
||||
last_known_pos: bdata
|
||||
.read_data
|
||||
.positions
|
||||
.get(attacker)
|
||||
.map(|pos| pos.0),
|
||||
});
|
||||
}
|
||||
|
||||
@ -440,7 +447,15 @@ fn set_owner_if_no_target(bdata: &mut BehaviorData) -> bool {
|
||||
if bdata.agent.target.is_none() && small_chance {
|
||||
if let Some(Alignment::Owned(owner)) = bdata.agent_data.alignment {
|
||||
if let Some(owner) = get_entity_by_id(owner.id(), bdata.read_data) {
|
||||
bdata.agent.target = Some(Target::new(owner, false, bdata.read_data.time.0, false));
|
||||
let owner_pos = bdata.read_data.positions.get(owner).map(|pos| pos.0);
|
||||
|
||||
bdata.agent.target = Some(Target::new(
|
||||
owner,
|
||||
false,
|
||||
bdata.read_data.time.0,
|
||||
false,
|
||||
owner_pos,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -496,6 +511,43 @@ fn handle_timed_events(bdata: &mut BehaviorData) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn update_last_known_pos(bdata: &mut BehaviorData) -> bool {
|
||||
let BehaviorData {
|
||||
agent,
|
||||
agent_data,
|
||||
read_data,
|
||||
controller,
|
||||
..
|
||||
} = bdata;
|
||||
|
||||
if let Some(target_info) = agent.target {
|
||||
let target = target_info.target;
|
||||
|
||||
if let Some(target_pos) = read_data.positions.get(target) {
|
||||
if agent_data.detects_other(agent, controller, &target, target_pos, read_data) {
|
||||
let updated_pos = Some(target_pos.0);
|
||||
|
||||
let Target {
|
||||
hostile,
|
||||
selected_at,
|
||||
aggro_on,
|
||||
..
|
||||
} = target_info;
|
||||
|
||||
agent.target = Some(Target::new(
|
||||
target,
|
||||
hostile,
|
||||
selected_at,
|
||||
aggro_on,
|
||||
updated_pos,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Try to heal self if our damage went below a certain threshold
|
||||
fn heal_self_if_hurt(bdata: &mut BehaviorData) -> bool {
|
||||
if bdata.agent_data.damage < HEALING_ITEM_THRESHOLD
|
||||
@ -556,6 +608,31 @@ fn update_target_awareness(bdata: &mut BehaviorData) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn search_last_known_pos_if_not_alert(bdata: &mut BehaviorData) -> bool {
|
||||
let awareness = &bdata.agent.awareness;
|
||||
if awareness.reached() || awareness.state() < AwarenessState::Low {
|
||||
return false;
|
||||
}
|
||||
|
||||
let BehaviorData {
|
||||
agent,
|
||||
agent_data,
|
||||
controller,
|
||||
read_data,
|
||||
..
|
||||
} = bdata;
|
||||
|
||||
if let Some(target) = agent.target {
|
||||
if let Some(last_known_pos) = target.last_known_pos {
|
||||
agent_data.follow(agent, controller, &read_data.terrain, &Pos(last_known_pos));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn do_combat(bdata: &mut BehaviorData) -> bool {
|
||||
let BehaviorData {
|
||||
agent,
|
||||
|
@ -91,7 +91,15 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
||||
if let Some(AgentEvent::Talk(by, subject)) = agent.inbox.pop_front() {
|
||||
if agent.allowed_to_speak() {
|
||||
if let Some(target) = get_entity_by_id(by.id(), read_data) {
|
||||
agent.target = Some(Target::new(target, false, read_data.time.0, false));
|
||||
let target_pos = read_data.positions.get(target).map(|pos| pos.0);
|
||||
|
||||
agent.target = Some(Target::new(
|
||||
target,
|
||||
false,
|
||||
read_data.time.0,
|
||||
false,
|
||||
target_pos,
|
||||
));
|
||||
|
||||
if agent_data.look_toward(controller, read_data, target) {
|
||||
controller.push_action(ControlAction::Stand);
|
||||
@ -415,7 +423,15 @@ pub fn handle_inbox_trade_invite(bdata: &mut BehaviorData) -> bool {
|
||||
controller.push_action(ControlAction::Stand);
|
||||
controller.push_action(ControlAction::Talk);
|
||||
if let Some(target) = get_entity_by_id(with.id(), read_data) {
|
||||
agent.target = Some(Target::new(target, false, read_data.time.0, false));
|
||||
let target_pos = read_data.positions.get(target).map(|pos| pos.0);
|
||||
|
||||
agent.target = Some(Target::new(
|
||||
target,
|
||||
false,
|
||||
read_data.time.0,
|
||||
false,
|
||||
target_pos,
|
||||
));
|
||||
}
|
||||
controller.push_invite_response(InviteResponse::Accept);
|
||||
agent.behavior.unset(BehaviorState::TRADING_ISSUER);
|
||||
@ -454,7 +470,15 @@ pub fn handle_inbox_trade_accepted(bdata: &mut BehaviorData) -> bool {
|
||||
if let Some(AgentEvent::TradeAccepted(with)) = agent.inbox.pop_front() {
|
||||
if !agent.behavior.is(BehaviorState::TRADING) {
|
||||
if let Some(target) = get_entity_by_id(with.id(), read_data) {
|
||||
agent.target = Some(Target::new(target, false, read_data.time.0, false));
|
||||
let target_pos = read_data.positions.get(target).map(|pos| pos.0);
|
||||
|
||||
agent.target = Some(Target::new(
|
||||
target,
|
||||
false,
|
||||
read_data.time.0,
|
||||
false,
|
||||
target_pos,
|
||||
));
|
||||
}
|
||||
agent.behavior.set(BehaviorState::TRADING);
|
||||
agent.behavior.set(BehaviorState::TRADING_ISSUER);
|
||||
|
Loading…
Reference in New Issue
Block a user