mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Use Behavior into Agent
This commit is contained in:
parent
41314e9098
commit
18694b30ad
@ -3,7 +3,7 @@ use specs_idvs::IdvStorage;
|
||||
use std::mem;
|
||||
|
||||
/// Behavior Component
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct Behavior {
|
||||
tags: Vec<BehaviorTag>,
|
||||
}
|
||||
@ -15,8 +15,11 @@ pub enum BehaviorTag {
|
||||
CanSpeak,
|
||||
/// The entity is able to trade
|
||||
CanTrade,
|
||||
|
||||
/// The entity is currently trading
|
||||
IsTrading,
|
||||
/// The entity has issued a trade
|
||||
TradingIssuer,
|
||||
IsTradingIssuer,
|
||||
}
|
||||
|
||||
impl Behavior {
|
||||
@ -57,17 +60,13 @@ impl Behavior {
|
||||
}
|
||||
|
||||
/// Get a specific tag by variant
|
||||
pub fn get_tag(&self, tag: &BehaviorTag) -> Option<&BehaviorTag> {
|
||||
pub fn get_tag(&self, tag: BehaviorTag) -> Option<&BehaviorTag> {
|
||||
self.tags
|
||||
.iter()
|
||||
.find(|behavior_tag| mem::discriminant(*behavior_tag) == mem::discriminant(tag))
|
||||
.find(|behavior_tag| mem::discriminant(*behavior_tag) == mem::discriminant(&tag))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Behavior {
|
||||
fn default() -> Self { Behavior { tags: vec![] } }
|
||||
}
|
||||
|
||||
impl Component for Behavior {
|
||||
type Storage = IdvStorage<Self>;
|
||||
}
|
||||
|
@ -198,6 +198,7 @@ impl State {
|
||||
ecs.register::<comp::Last<comp::Ori>>();
|
||||
ecs.register::<comp::Alignment>();
|
||||
ecs.register::<comp::Agent>();
|
||||
ecs.register::<comp::Behavior>();
|
||||
ecs.register::<comp::WaypointArea>();
|
||||
ecs.register::<comp::ForceUpdate>();
|
||||
ecs.register::<comp::InventoryUpdate>();
|
||||
|
@ -83,45 +83,6 @@ pub fn handle_npc_interaction(server: &mut Server, interactor: EcsEntity, npc_en
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_npc_find_merchant(server: &mut Server, interactor: EcsEntity, npc_entity: EcsEntity) {
|
||||
let state = server.state_mut();
|
||||
let trader_positions: Vec<Vec3<f32>> = {
|
||||
let positions = state.ecs().read_storage::<comp::Pos>();
|
||||
let agents = state.ecs().read_storage::<comp::Agent>();
|
||||
(&agents, &positions)
|
||||
.join()
|
||||
.filter_map(|(a, p)| a.trade_for_site.map(|_| p.0))
|
||||
.collect()
|
||||
};
|
||||
if let Some(agent) = state
|
||||
.ecs()
|
||||
.write_storage::<comp::Agent>()
|
||||
.get_mut(npc_entity)
|
||||
{
|
||||
if let Some(interactor_uid) = state.ecs().uid_from_entity(interactor) {
|
||||
if let Some(mypos) = state.ecs().read_storage::<comp::Pos>().get(interactor) {
|
||||
let mut closest = std::f32::INFINITY;
|
||||
let mut closest_pos = None;
|
||||
for p in trader_positions.iter() {
|
||||
let diff = p - mypos.0;
|
||||
let dist = diff.magnitude();
|
||||
if dist < closest {
|
||||
closest = dist;
|
||||
closest_pos = Some(*p);
|
||||
}
|
||||
}
|
||||
agent.inbox.push_front(AgentEvent::Talk(
|
||||
interactor_uid,
|
||||
Subject::Person(AskedPerson {
|
||||
person_type: comp::dialogue::PersonType::Merchant,
|
||||
origin: closest_pos,
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_mount(server: &mut Server, mounter: EcsEntity, mountee: EcsEntity) {
|
||||
let state = server.state_mut();
|
||||
|
||||
|
@ -14,9 +14,9 @@ use common::{
|
||||
ItemDesc, ItemKind,
|
||||
},
|
||||
skills::{AxeSkill, BowSkill, HammerSkill, Skill, StaffSkill, SwordSkill},
|
||||
Agent, Alignment, Body, CharacterState, ControlAction, ControlEvent, Controller, Energy,
|
||||
Health, InputKind, Inventory, LightEmitter, MountState, Ori, PhysicsState, Pos, Scale,
|
||||
Stats, UnresolvedChatMsg, Vel,
|
||||
Agent, Alignment, Behavior, BehaviorTag, Body, CharacterState, ControlAction, ControlEvent,
|
||||
Controller, Energy, Health, InputKind, Inventory, LightEmitter, MountState, Ori,
|
||||
PhysicsState, Pos, Scale, Stats, UnresolvedChatMsg, Vel,
|
||||
},
|
||||
event::{Emitter, EventBus, ServerEvent},
|
||||
path::TraversalConfig,
|
||||
@ -65,6 +65,7 @@ struct AgentData<'a> {
|
||||
is_gliding: bool,
|
||||
health: Option<&'a Health>,
|
||||
char_state: &'a CharacterState,
|
||||
behavior: &'a mut Behavior,
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
@ -121,6 +122,7 @@ impl<'a> System<'a> for Sys {
|
||||
WriteStorage<'a, Agent>,
|
||||
WriteStorage<'a, Controller>,
|
||||
WriteExpect<'a, RtSim>,
|
||||
WriteStorage<'a, Behavior>,
|
||||
);
|
||||
|
||||
const NAME: &'static str = "agent";
|
||||
@ -130,16 +132,18 @@ impl<'a> System<'a> for Sys {
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
fn run(
|
||||
job: &mut Job<Self>,
|
||||
(read_data, event_bus, mut agents, mut controllers, mut rtsim): Self::SystemData,
|
||||
(read_data, event_bus, mut agents, mut controllers, mut rtsim, mut behaviors): Self::SystemData,
|
||||
) {
|
||||
let rtsim = &mut *rtsim;
|
||||
job.cpu_stats.measure(ParMode::Rayon);
|
||||
(
|
||||
&read_data.entities,
|
||||
(&read_data.energies, read_data.healths.maybe()),
|
||||
(
|
||||
&read_data.positions,
|
||||
&read_data.velocities,
|
||||
&read_data.orientations,
|
||||
),
|
||||
read_data.bodies.maybe(),
|
||||
&read_data.inventories,
|
||||
&read_data.stats,
|
||||
@ -151,16 +155,15 @@ impl<'a> System<'a> for Sys {
|
||||
read_data.groups.maybe(),
|
||||
read_data.mount_states.maybe(),
|
||||
&read_data.char_states,
|
||||
&mut behaviors,
|
||||
)
|
||||
.par_join()
|
||||
.filter(
|
||||
|(_, _, _, _, _, _, _, _, _, _, _, _, _, _, mount_state, _)| {
|
||||
.filter(|(_, _, _, _, _, _, _, _, _, _, _, _, mount_state, _, _)| {
|
||||
// Skip mounted entities
|
||||
mount_state
|
||||
.map(|ms| *ms == MountState::Unmounted)
|
||||
.unwrap_or(true)
|
||||
},
|
||||
)
|
||||
})
|
||||
.for_each_init(
|
||||
|| {
|
||||
prof_span!(guard, "agent rayon job");
|
||||
@ -170,9 +173,7 @@ impl<'a> System<'a> for Sys {
|
||||
(
|
||||
entity,
|
||||
(energy, health),
|
||||
pos,
|
||||
vel,
|
||||
ori,
|
||||
(pos, vel, ori),
|
||||
body,
|
||||
inventory,
|
||||
stats,
|
||||
@ -184,6 +185,7 @@ impl<'a> System<'a> for Sys {
|
||||
groups,
|
||||
_,
|
||||
char_state,
|
||||
behavior,
|
||||
)| {
|
||||
//// Hack, replace with better system when groups are more sophisticated
|
||||
//// Override alignment if in a group unless entity is owned already
|
||||
@ -258,7 +260,7 @@ impl<'a> System<'a> for Sys {
|
||||
.and_then(|rtsim_ent| rtsim.get_entity(rtsim_ent.0));
|
||||
|
||||
// Package all this agent's data into a convenient struct
|
||||
let data = AgentData {
|
||||
let mut data = AgentData {
|
||||
entity: &entity,
|
||||
rtsim_entity,
|
||||
uid,
|
||||
@ -280,6 +282,7 @@ impl<'a> System<'a> for Sys {
|
||||
is_gliding,
|
||||
health: read_data.healths.get(entity),
|
||||
char_state,
|
||||
behavior,
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
@ -527,7 +530,7 @@ impl<'a> AgentData<'a> {
|
||||
// Subtrees
|
||||
////////////////////////////////////////
|
||||
fn idle_tree(
|
||||
&self,
|
||||
&mut self,
|
||||
agent: &mut Agent,
|
||||
controller: &mut Controller,
|
||||
read_data: &ReadData,
|
||||
@ -551,7 +554,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
if agent.action_timer > 0.0 {
|
||||
if agent.action_timer
|
||||
< (if agent.trading {
|
||||
< (if self.behavior.has_tag(&BehaviorTag::IsTrading) {
|
||||
TRADE_INTERACTION_TIME
|
||||
} else {
|
||||
DEFAULT_INTERACTION_TIME
|
||||
@ -588,7 +591,7 @@ impl<'a> AgentData<'a> {
|
||||
let dist_sqrd = self.pos.0.distance_squared(tgt_pos.0);
|
||||
// Should the agent flee?
|
||||
if 1.0 - agent.psyche.aggro > self.damage && self.flees {
|
||||
if agent.action_timer == 0.0 && agent.can_speak {
|
||||
if agent.action_timer == 0.0 && self.behavior.has_tag(&BehaviorTag::CanSpeak) {
|
||||
let msg = "npc.speech.villager_under_attack".to_string();
|
||||
event_emitter
|
||||
.emit(ServerEvent::Chat(UnresolvedChatMsg::npc(*self.uid, msg)));
|
||||
@ -616,7 +619,7 @@ impl<'a> AgentData<'a> {
|
||||
read_data.buffs.get(target),
|
||||
) {
|
||||
agent.target = None;
|
||||
if agent.can_speak {
|
||||
if self.behavior.has_tag(&BehaviorTag::CanSpeak) {
|
||||
let msg = "npc.speech.villager_enemy_killed".to_string();
|
||||
event_emitter
|
||||
.emit(ServerEvent::Chat(UnresolvedChatMsg::npc(*self.uid, msg)));
|
||||
@ -855,7 +858,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
|
||||
fn interact(
|
||||
&self,
|
||||
&mut self,
|
||||
agent: &mut Agent,
|
||||
controller: &mut Controller,
|
||||
read_data: &ReadData,
|
||||
@ -879,7 +882,7 @@ impl<'a> AgentData<'a> {
|
||||
let msg = agent.inbox.pop_back();
|
||||
match msg {
|
||||
Some(AgentEvent::Talk(by, subject)) => {
|
||||
if agent.can_speak {
|
||||
if self.behavior.has_tag(&BehaviorTag::CanSpeak) {
|
||||
if let Some(target) = read_data.uid_allocator.retrieve_entity_internal(by.id())
|
||||
{
|
||||
agent.target = Some(Target {
|
||||
@ -945,8 +948,8 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
Subject::Trade => {
|
||||
if agent.trade_for_site.is_some() {
|
||||
if !agent.trading {
|
||||
if self.behavior.has_tag(&BehaviorTag::CanTrade) {
|
||||
if !self.behavior.has_tag(&BehaviorTag::IsTrading) {
|
||||
controller.events.push(ControlEvent::InitiateInvite(
|
||||
by,
|
||||
InviteKind::Trade,
|
||||
@ -1094,8 +1097,8 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
Some(AgentEvent::TradeInvite(with)) => {
|
||||
if agent.trade_for_site.is_some() {
|
||||
if !agent.trading {
|
||||
if self.behavior.has_tag(&BehaviorTag::CanTrade) {
|
||||
if !self.behavior.has_tag(&BehaviorTag::IsTrading) {
|
||||
// stand still and looking towards the trading player
|
||||
controller.actions.push(ControlAction::Stand);
|
||||
controller.actions.push(ControlAction::Talk);
|
||||
@ -1111,13 +1114,13 @@ impl<'a> AgentData<'a> {
|
||||
controller
|
||||
.events
|
||||
.push(ControlEvent::InviteResponse(InviteResponse::Accept));
|
||||
agent.trading_issuer = false;
|
||||
agent.trading = true;
|
||||
self.behavior.remove_tag(BehaviorTag::IsTradingIssuer);
|
||||
self.behavior.add_tag(BehaviorTag::IsTrading);
|
||||
} else {
|
||||
controller
|
||||
.events
|
||||
.push(ControlEvent::InviteResponse(InviteResponse::Decline));
|
||||
if agent.can_speak {
|
||||
if self.behavior.has_tag(&BehaviorTag::CanSpeak) {
|
||||
event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc(
|
||||
*self.uid,
|
||||
"npc.speech.merchant_busy".to_string(),
|
||||
@ -1129,7 +1132,7 @@ impl<'a> AgentData<'a> {
|
||||
controller
|
||||
.events
|
||||
.push(ControlEvent::InviteResponse(InviteResponse::Decline));
|
||||
if agent.can_speak {
|
||||
if self.behavior.has_tag(&BehaviorTag::CanSpeak) {
|
||||
event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc(
|
||||
*self.uid,
|
||||
"npc.speech.villager_decline_trade".to_string(),
|
||||
@ -1138,7 +1141,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
Some(AgentEvent::TradeAccepted(with)) => {
|
||||
if !agent.trading {
|
||||
if !self.behavior.has_tag(&BehaviorTag::IsTrading) {
|
||||
if let Some(target) =
|
||||
read_data.uid_allocator.retrieve_entity_internal(with.id())
|
||||
{
|
||||
@ -1148,12 +1151,12 @@ impl<'a> AgentData<'a> {
|
||||
selected_at: read_data.time.0,
|
||||
});
|
||||
}
|
||||
agent.trading = true;
|
||||
agent.trading_issuer = true;
|
||||
self.behavior.add_tag(BehaviorTag::IsTrading);
|
||||
self.behavior.add_tag(BehaviorTag::IsTradingIssuer);
|
||||
}
|
||||
},
|
||||
Some(AgentEvent::FinishedTrade(result)) => {
|
||||
if agent.trading {
|
||||
if self.behavior.has_tag(&BehaviorTag::IsTrading) {
|
||||
match result {
|
||||
TradeResult::Completed => {
|
||||
event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc(
|
||||
@ -1166,13 +1169,17 @@ impl<'a> AgentData<'a> {
|
||||
"npc.speech.merchant_trade_declined".to_string(),
|
||||
))),
|
||||
}
|
||||
agent.trading = false;
|
||||
self.behavior.remove_tag(BehaviorTag::IsTrading);
|
||||
}
|
||||
},
|
||||
Some(AgentEvent::UpdatePendingTrade(boxval)) => {
|
||||
let (tradeid, pending, prices, inventories) = *boxval;
|
||||
if agent.trading {
|
||||
let who: usize = if agent.trading_issuer { 0 } else { 1 };
|
||||
if self.behavior.has_tag(&BehaviorTag::IsTrading) {
|
||||
let who: usize = if self.behavior.has_tag(&BehaviorTag::IsTradingIssuer) {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
};
|
||||
let balance0: f32 =
|
||||
prices.balance(&pending.offers, &inventories, 1 - who, true);
|
||||
let balance1: f32 = prices.balance(&pending.offers, &inventories, who, false);
|
||||
@ -1202,7 +1209,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
if pending.phase != TradePhase::Mutate {
|
||||
// we got into the review phase but without balanced goods, decline
|
||||
agent.trading = false;
|
||||
self.behavior.remove_tag(BehaviorTag::IsTrading);
|
||||
event_emitter.emit(ServerEvent::ProcessTradeAction(
|
||||
*self.entity,
|
||||
tradeid,
|
||||
@ -1213,7 +1220,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
None => {
|
||||
if agent.can_speak {
|
||||
if self.behavior.has_tag(&BehaviorTag::CanSpeak) {
|
||||
// no new events, continue looking towards the last interacting player for some
|
||||
// time
|
||||
if let Some(Target { target, .. }) = &agent.target {
|
||||
@ -1336,7 +1343,7 @@ impl<'a> AgentData<'a> {
|
||||
(
|
||||
self.alignment.map_or(false, |alignment| {
|
||||
if matches!(alignment, Alignment::Npc) && e_inventory.equipped_items().filter(|item| item.tags().contains(&ItemTag::Cultist)).count() > 2 {
|
||||
if agent.can_speak {
|
||||
if self.behavior.has_tag(&BehaviorTag::CanSpeak) {
|
||||
if self.rtsim_entity.is_some() {
|
||||
agent.rtsim_controller.events.push(
|
||||
RtSimEvent::AddMemory(Memory {
|
||||
|
Loading…
Reference in New Issue
Block a user