Use Behavior::CanTrade instead of Agent::trade_for_site + addressed comments

This commit is contained in:
Vincent Foulon 2021-03-30 12:55:50 +02:00 committed by Marcel Märtens
parent 51ef3547a1
commit 211ab02897
10 changed files with 64 additions and 47 deletions

View File

@ -2,7 +2,7 @@ use crate::{
comp::{humanoid, quadruped_low, quadruped_medium, quadruped_small, Body}, comp::{humanoid, quadruped_low, quadruped_medium, quadruped_small, Body},
path::Chaser, path::Chaser,
rtsim::RtSimController, rtsim::RtSimController,
trade::{PendingTrade, ReducedInventory, SiteId, SitePrices, TradeId, TradeResult}, trade::{PendingTrade, ReducedInventory, SitePrices, TradeId, TradeResult},
uid::Uid, uid::Uid,
}; };
use specs::{Component, Entity as EcsEntity}; use specs::{Component, Entity as EcsEntity};
@ -212,7 +212,6 @@ pub struct Agent {
pub chaser: Chaser, pub chaser: Chaser,
/// Does the agent talk when e.g. hit by the player /// Does the agent talk when e.g. hit by the player
// TODO move speech patterns into a Behavior component // TODO move speech patterns into a Behavior component
pub trade_for_site: Option<SiteId>,
pub psyche: Psyche, pub psyche: Psyche,
pub inbox: VecDeque<AgentEvent>, pub inbox: VecDeque<AgentEvent>,
pub action_timer: f32, pub action_timer: f32,
@ -233,15 +232,9 @@ impl Agent {
} }
} }
pub fn new( pub fn new(patrol_origin: Option<Vec3<f32>>, body: &Body, no_flee: bool) -> Self {
patrol_origin: Option<Vec3<f32>>,
trade_for_site: Option<SiteId>,
body: &Body,
no_flee: bool,
) -> Self {
Agent { Agent {
patrol_origin, patrol_origin,
trade_for_site,
psyche: if no_flee { psyche: if no_flee {
Psyche { aggro: 1.0 } Psyche { aggro: 1.0 }
} else { } else {

View File

@ -2,7 +2,13 @@ use specs::Component;
use specs_idvs::IdvStorage; use specs_idvs::IdvStorage;
use std::mem; use std::mem;
/// Behavior Component use crate::trade::SiteId;
/// # Behavior Component
/// This component allow an Entity to register one or more behavior tags.
/// These tags act as flags of what an Entity can do, or what it is doing.
/// Behaviors Tags can be added and removed as the Entity lives, to update its
/// state when needed
#[derive(Default, Clone, Debug)] #[derive(Default, Clone, Debug)]
pub struct Behavior { pub struct Behavior {
tags: Vec<BehaviorTag>, tags: Vec<BehaviorTag>,
@ -14,7 +20,7 @@ pub enum BehaviorTag {
/// The entity is allowed to speak /// The entity is allowed to speak
CanSpeak, CanSpeak,
/// The entity is able to trade /// The entity is able to trade
CanTrade, CanTrade(Option<SiteId>),
/// The entity is currently trading /// The entity is currently trading
IsTrading, IsTrading,
@ -23,13 +29,10 @@ pub enum BehaviorTag {
} }
impl Behavior { impl Behavior {
pub fn new(can_speak: bool, can_trade: bool) -> Self { pub fn new(behavior_tags: &[BehaviorTag]) -> Self {
let mut behavior = Self::default(); let mut behavior = Self::default();
if can_speak { for tag in behavior_tags.iter() {
behavior.add_tag(BehaviorTag::CanSpeak); behavior.add_tag(tag.clone())
}
if can_trade {
behavior.add_tag(BehaviorTag::CanTrade);
} }
behavior behavior
} }
@ -56,7 +59,9 @@ impl Behavior {
/// Check if the Behavior possess a specific tag /// Check if the Behavior possess a specific tag
pub fn has_tag(&self, tag: &BehaviorTag) -> bool { pub fn has_tag(&self, tag: &BehaviorTag) -> bool {
self.tags.iter().any(|behavior_tag| behavior_tag == tag) self.tags
.iter()
.any(|behavior_tag| mem::discriminant(behavior_tag) == mem::discriminant(tag))
} }
/// Get a specific tag by variant /// Get a specific tag by variant

View File

@ -2,7 +2,7 @@ use crate::{
comp::{ comp::{
self, self,
inventory::loadout_builder::{LoadoutBuilder, LoadoutConfig}, inventory::loadout_builder::{LoadoutBuilder, LoadoutConfig},
Behavior, CharacterState, StateUpdate, Behavior, BehaviorTag, CharacterState, StateUpdate,
}, },
event::{LocalEvent, ServerEvent}, event::{LocalEvent, ServerEvent},
outcome::Outcome, outcome::Outcome,
@ -104,8 +104,8 @@ impl CharacterBehavior for Data {
poise: comp::Poise::new(body), poise: comp::Poise::new(body),
loadout, loadout,
body, body,
agent: Some(comp::Agent::new(None, None, &body, true)), agent: Some(comp::Agent::new(None, &body, true)),
behavior: Some(Behavior::new(true, false)), behavior: Some(Behavior::new(&[BehaviorTag::CanSpeak])),
alignment: comp::Alignment::Owned(*data.uid), alignment: comp::Alignment::Owned(*data.uid),
scale: self scale: self
.static_data .static_data

View File

@ -15,7 +15,7 @@ use common::{
buff::{BuffCategory, BuffData, BuffKind, BuffSource}, buff::{BuffCategory, BuffData, BuffKind, BuffSource},
inventory::item::MaterialStatManifest, inventory::item::MaterialStatManifest,
invite::InviteKind, invite::InviteKind,
ChatType, Inventory, Item, LightEmitter, WaypointArea, BehaviorTag, ChatType, Inventory, Item, LightEmitter, WaypointArea,
}, },
effect::Effect, effect::Effect,
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
@ -1075,7 +1075,7 @@ fn handle_spawn_airship(
}); });
if let Some(pos) = destination { if let Some(pos) = destination {
builder = builder.with(comp::Agent::with_destination(pos)); builder = builder.with(comp::Agent::with_destination(pos));
builder = builder.with(comp::Behavior::new(true, false)) builder = builder.with(comp::Behavior::new(&[BehaviorTag::CanSpeak]))
} }
builder.build(); builder.build();

View File

@ -6,7 +6,7 @@ use common::{
agent::{Agent, AgentEvent}, agent::{Agent, AgentEvent},
group::GroupManager, group::GroupManager,
invite::{Invite, InviteKind, InviteResponse, PendingInvites}, invite::{Invite, InviteKind, InviteResponse, PendingInvites},
ChatType, Behavior, ChatType,
}, },
trade::Trades, trade::Trades,
uid::Uid, uid::Uid,
@ -167,6 +167,7 @@ pub fn handle_invite_accept(server: &mut Server, entity: specs::Entity) {
let clients = state.ecs().read_storage::<Client>(); let clients = state.ecs().read_storage::<Client>();
let uids = state.ecs().read_storage::<Uid>(); let uids = state.ecs().read_storage::<Uid>();
let mut agents = state.ecs().write_storage::<Agent>(); let mut agents = state.ecs().write_storage::<Agent>();
let behaviors = state.ecs().read_storage::<Behavior>();
let mut invites = state.ecs().write_storage::<Invite>(); let mut invites = state.ecs().write_storage::<Invite>();
if let Some((inviter, kind)) = invites.remove(entity).and_then(|invite| { if let Some((inviter, kind)) = invites.remove(entity).and_then(|invite| {
let Invite { inviter, kind } = invite; let Invite { inviter, kind } = invite;
@ -223,10 +224,10 @@ pub fn handle_invite_accept(server: &mut Server, entity: specs::Entity) {
.inbox .inbox
.push_front(AgentEvent::TradeAccepted(invitee_uid)); .push_front(AgentEvent::TradeAccepted(invitee_uid));
} }
let pricing = agents let pricing = behaviors
.get(inviter) .get(inviter)
.and_then(|a| index.get_site_prices(a)) .and_then(|b| index.get_site_prices(b))
.or_else(|| agents.get(entity).and_then(|a| index.get_site_prices(a))); .or_else(|| behaviors.get(entity).and_then(|b| index.get_site_prices(b)));
clients.get(inviter).map(|c| { clients.get(inviter).map(|c| {
c.send(ServerGeneral::UpdatePendingTrade( c.send(ServerGeneral::UpdatePendingTrade(
id, id,

View File

@ -3,6 +3,7 @@ use common::{
comp::{ comp::{
agent::{Agent, AgentEvent}, agent::{Agent, AgentEvent},
inventory::{item::MaterialStatManifest, Inventory}, inventory::{item::MaterialStatManifest, Inventory},
Behavior,
}, },
trade::{PendingTrade, ReducedInventory, TradeAction, TradeId, TradeResult, Trades}, trade::{PendingTrade, ReducedInventory, TradeAction, TradeId, TradeResult, Trades},
}; };
@ -28,12 +29,13 @@ fn notify_agent_simple(
fn notify_agent_prices( fn notify_agent_prices(
mut agents: specs::WriteStorage<Agent>, mut agents: specs::WriteStorage<Agent>,
behaviors: specs::ReadStorage<Behavior>,
index: &IndexOwned, index: &IndexOwned,
entity: EcsEntity, entity: EcsEntity,
event: AgentEvent, event: AgentEvent,
) { ) {
if let Some(agent) = agents.get_mut(entity) { if let (Some(agent), Some(behavior)) = (agents.get_mut(entity), behaviors.get(entity)) {
let prices = index.get_site_prices(agent); let prices = index.get_site_prices(behavior);
if let AgentEvent::UpdatePendingTrade(boxval) = event { if let AgentEvent::UpdatePendingTrade(boxval) = event {
// Box<(tid, pend, _, inventories)>) = event { // Box<(tid, pend, _, inventories)>) = event {
agent agent
@ -104,6 +106,7 @@ pub fn handle_process_trade_action(
let mut inventories: [Option<ReducedInventory>; 2] = [None, None]; let mut inventories: [Option<ReducedInventory>; 2] = [None, None];
let mut prices = None; let mut prices = None;
let agents = server.state.ecs().read_storage::<Agent>(); let agents = server.state.ecs().read_storage::<Agent>();
let behaviors = server.state.ecs().read_storage::<Behavior>();
// sadly there is no map and collect on arrays // sadly there is no map and collect on arrays
for i in 0..2 { for i in 0..2 {
// parties.len()) { // parties.len()) {
@ -118,7 +121,9 @@ pub fn handle_process_trade_action(
// Get price info from the first Agent in the trade (currently, an // Get price info from the first Agent in the trade (currently, an
// Agent will never initiate a trade with another agent though) // Agent will never initiate a trade with another agent though)
prices = prices.or_else(|| { prices = prices.or_else(|| {
agents.get(e).and_then(|a| server.index.get_site_prices(a)) behaviors
.get(e)
.and_then(|b| server.index.get_site_prices(b))
}); });
} }
} }
@ -135,6 +140,7 @@ pub fn handle_process_trade_action(
); );
notify_agent_prices( notify_agent_prices(
server.state.ecs().write_storage::<Agent>(), server.state.ecs().write_storage::<Agent>(),
server.state.ecs().read_storage::<Behavior>(),
&server.index, &server.index,
e, e,
AgentEvent::UpdatePendingTrade(Box::new(( AgentEvent::UpdatePendingTrade(Box::new((

View File

@ -2,7 +2,7 @@
use super::*; use super::*;
use common::{ use common::{
comp::{self, inventory::loadout_builder::LoadoutBuilder, Behavior}, comp::{self, inventory::loadout_builder::LoadoutBuilder, Behavior, BehaviorTag},
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
resources::{DeltaTime, Time}, resources::{DeltaTime, Time},
terrain::TerrainGrid, terrain::TerrainGrid,
@ -103,9 +103,9 @@ impl<'a> System<'a> for Sys {
.map(|e| e as f32) .map(|e| e as f32)
+ Vec3::new(0.5, 0.5, body.flying_height()); + Vec3::new(0.5, 0.5, body.flying_height());
let pos = comp::Pos(spawn_pos); let pos = comp::Pos(spawn_pos);
let agent = Some(comp::Agent::new(None, None, &body, false)); let agent = Some(comp::Agent::new(None, &body, false));
let behavior = if matches!(body, comp::Body::Humanoid(_)) { let behavior = if matches!(body, comp::Body::Humanoid(_)) {
Some(Behavior::new(true, false)) Some(Behavior::new(&[BehaviorTag::CanSpeak]))
} else { } else {
None None
}; };

View File

@ -935,7 +935,7 @@ impl<'a> AgentData<'a> {
event_emitter.emit(ServerEvent::Chat( event_emitter.emit(ServerEvent::Chat(
UnresolvedChatMsg::npc(*self.uid, msg), UnresolvedChatMsg::npc(*self.uid, msg),
)); ));
} else if self.behavior.has_tag(&BehaviorTag::CanTrade) { } else if self.behavior.has_tag(&BehaviorTag::CanTrade(None)) {
let msg = "npc.speech.merchant_advertisement".to_string(); let msg = "npc.speech.merchant_advertisement".to_string();
event_emitter.emit(ServerEvent::Chat( event_emitter.emit(ServerEvent::Chat(
UnresolvedChatMsg::npc(*self.uid, msg), UnresolvedChatMsg::npc(*self.uid, msg),
@ -948,7 +948,7 @@ impl<'a> AgentData<'a> {
} }
}, },
Subject::Trade => { Subject::Trade => {
if self.behavior.has_tag(&BehaviorTag::CanTrade) { if self.behavior.has_tag(&BehaviorTag::CanTrade(None)) {
if !self.behavior.has_tag(&BehaviorTag::IsTrading) { if !self.behavior.has_tag(&BehaviorTag::IsTrading) {
controller.events.push(ControlEvent::InitiateInvite( controller.events.push(ControlEvent::InitiateInvite(
by, by,
@ -1097,7 +1097,7 @@ impl<'a> AgentData<'a> {
} }
}, },
Some(AgentEvent::TradeInvite(with)) => { Some(AgentEvent::TradeInvite(with)) => {
if self.behavior.has_tag(&BehaviorTag::CanTrade) { if self.behavior.has_tag(&BehaviorTag::CanTrade(None)) {
if !self.behavior.has_tag(&BehaviorTag::IsTrading) { if !self.behavior.has_tag(&BehaviorTag::IsTrading) {
// stand still and looking towards the trading player // stand still and looking towards the trading player
controller.actions.push(ControlAction::Stand); controller.actions.push(ControlAction::Stand);

View File

@ -2,7 +2,9 @@ use crate::{
chunk_generator::ChunkGenerator, client::Client, presence::Presence, rtsim::RtSim, Tick, chunk_generator::ChunkGenerator, client::Client, presence::Presence, rtsim::RtSim, Tick,
}; };
use common::{ use common::{
comp::{self, bird_medium, inventory::loadout_builder::LoadoutConfig, Alignment, Pos}, comp::{
self, bird_medium, inventory::loadout_builder::LoadoutConfig, Alignment, BehaviorTag, Pos,
},
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
generation::get_npc_name, generation::get_npc_name,
npc::NPC_NAMES, npc::NPC_NAMES,
@ -191,7 +193,6 @@ impl<'a> System<'a> for Sys {
agent: if entity.has_agency { agent: if entity.has_agency {
Some(comp::Agent::new( Some(comp::Agent::new(
Some(entity.pos), Some(entity.pos),
trade_for_site,
&body, &body,
matches!( matches!(
loadout_config, loadout_config,
@ -202,7 +203,14 @@ impl<'a> System<'a> for Sys {
None None
}, },
behavior: if entity.has_agency { behavior: if entity.has_agency {
Some(comp::Behavior::new(can_speak, trade_for_site.is_some())) let mut behavior_tags = vec![];
if can_speak {
behavior_tags.push(BehaviorTag::CanSpeak);
}
if trade_for_site.is_some() {
behavior_tags.push(BehaviorTag::CanTrade(trade_for_site));
}
Some(comp::Behavior::new(&behavior_tags))
} else { } else {
None None
}, },

View File

@ -4,7 +4,7 @@ use crate::{
}; };
use common::{ use common::{
assets::{AssetExt, AssetHandle}, assets::{AssetExt, AssetHandle},
comp::Agent, comp::{Behavior, BehaviorTag},
store::Store, store::Store,
trade::SitePrices, trade::SitePrices,
}; };
@ -72,13 +72,17 @@ impl Index {
pub fn colors(&self) -> AssetHandle<Arc<Colors>> { self.colors } pub fn colors(&self) -> AssetHandle<Arc<Colors>> { self.colors }
pub fn get_site_prices(&self, agent: &Agent) -> Option<SitePrices> { pub fn get_site_prices(&self, behavior: &Behavior) -> Option<SitePrices> {
agent if let Some(BehaviorTag::CanTrade(site_id)) = behavior.get_tag(BehaviorTag::CanTrade(None))
.trade_for_site {
.map(|i| self.sites.recreate_id(i)) site_id
.flatten() .map(|i| self.sites.recreate_id(i))
.map(|i| self.sites.get(i)) .flatten()
.map(|s| s.economy.get_site_prices()) .map(|i| self.sites.get(i))
.map(|s| s.economy.get_site_prices())
} else {
None
}
} }
} }