Merge branch 'james/fix-merchant-wander' into 'master'

Fix merchant wandering

See merge request veloren/veloren!2035
This commit is contained in:
Marcel 2021-03-30 10:52:42 +00:00
commit 895de16dd6

View File

@ -285,31 +285,31 @@ impl<'a> System<'a> for Sys {
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
// Behavior tree // Behavior tree
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
// The behavior tree is meant to make decisions for agents
// *but should not* mutate any data (only action nodes
// should do that). Each path should lead to one (and only
// one) action node. This makes bugfinding much easier and
// debugging way easier. If you don't think so, try
// debugging the agent code before this MR
// (https://gitlab.com/veloren/veloren/-/merge_requests/1801).
// Each tick should arrive at one (1) action node which
// then determines what the agent does. If this makes you
// uncomfortable, consider dt the response time of the
// NPC. To make the tree easier to read, subtrees can be
// created as methods on `AgentData`. Action nodes are
// also methods on the `AgentData` struct. Action nodes
// are the only parts of this tree that should provide
// inputs.
// If falling fast and can glide, save yourself! // If falling fast and can glide, save yourself!
if data.fall_glide() { if data.glider_equipped && !data.physics_state.on_ground {
// toggle glider when vertical velocity is above some threshold (here ~ // toggle glider when vertical velocity is above some threshold (here ~
// glider fall vertical speed) // glider fall vertical speed)
if vel.0.z < -26.0 { data.glider_fall(agent, controller, &read_data);
controller.actions.push(ControlAction::GlideWield);
if let Some(Target { target, .. }) = agent.target {
if let Some(tgt_pos) = read_data.positions.get(target) {
controller.inputs.move_dir = (pos.0 - tgt_pos.0)
.xy()
.try_normalized()
.unwrap_or_else(Vec2::zero);
}
}
}
} else if let Some(Target { } else if let Some(Target {
target, target, hostile, ..
hostile,
selected_at,
}) = agent.target }) = agent.target
{ {
if read_data.time.0 - selected_at > RETARGETING_THRESHOLD_SECONDS {
data.choose_target(agent, controller, &read_data, &mut event_emitter);
}
if let Some(tgt_health) = read_data.healths.get(target) { if let Some(tgt_health) = read_data.healths.get(target) {
// If the target is hostile (either based on alignment or if // If the target is hostile (either based on alignment or if
// the target just attacked // the target just attacked
@ -523,8 +523,9 @@ impl<'a> System<'a> for Sys {
} }
impl<'a> AgentData<'a> { impl<'a> AgentData<'a> {
fn fall_glide(&self) -> bool { self.glider_equipped && !self.physics_state.on_ground } ////////////////////////////////////////
// Subtrees
////////////////////////////////////////
fn idle_tree( fn idle_tree(
&self, &self,
agent: &mut Agent, agent: &mut Agent,
@ -577,7 +578,12 @@ impl<'a> AgentData<'a> {
read_data: &ReadData, read_data: &ReadData,
event_emitter: &mut Emitter<'_, ServerEvent>, event_emitter: &mut Emitter<'_, ServerEvent>,
) { ) {
if let Some(Target { target, .. }) = agent.target { if let Some(Target {
target,
selected_at,
..
}) = agent.target
{
if let Some(tgt_pos) = read_data.positions.get(target) { if let Some(tgt_pos) = read_data.positions.get(target) {
let dist_sqrd = self.pos.0.distance_squared(tgt_pos.0); let dist_sqrd = self.pos.0.distance_squared(tgt_pos.0);
// Should the agent flee? // Should the agent flee?
@ -615,6 +621,13 @@ impl<'a> AgentData<'a> {
event_emitter event_emitter
.emit(ServerEvent::Chat(UnresolvedChatMsg::npc(*self.uid, msg))); .emit(ServerEvent::Chat(UnresolvedChatMsg::npc(*self.uid, msg)));
} }
// Choose a new target every 10 seconds
// TODO: This should be more principled. Consider factoring
// health, combat rating, wielded
// weapon, etc, into the decision to change
// target.
} else if read_data.time.0 - selected_at > RETARGETING_THRESHOLD_SECONDS {
self.choose_target(agent, controller, &read_data, event_emitter);
} else if dist_sqrd < SIGHT_DIST.powi(2) { } else if dist_sqrd < SIGHT_DIST.powi(2) {
self.attack( self.attack(
agent, agent,
@ -634,6 +647,24 @@ impl<'a> AgentData<'a> {
} }
} }
////////////////////////////////////////
// Action Nodes
////////////////////////////////////////
fn glider_fall(&self, agent: &mut Agent, controller: &mut Controller, read_data: &ReadData) {
if self.vel.0.z < -26.0 {
controller.actions.push(ControlAction::GlideWield);
if let Some(Target { target, .. }) = agent.target {
if let Some(tgt_pos) = read_data.positions.get(target) {
controller.inputs.move_dir = (self.pos.0 - tgt_pos.0)
.xy()
.try_normalized()
.unwrap_or_else(Vec2::zero);
}
}
}
}
fn idle(&self, agent: &mut Agent, controller: &mut Controller, read_data: &ReadData) { fn idle(&self, agent: &mut Agent, controller: &mut Controller, read_data: &ReadData) {
// Light lanterns at night // Light lanterns at night
// TODO Add a method to turn on NPC lanterns underground // TODO Add a method to turn on NPC lanterns underground