mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Create Behavior component
This commit is contained in:
parent
60efd682e2
commit
41314e9098
@ -212,10 +212,7 @@ pub struct Agent {
|
||||
pub chaser: Chaser,
|
||||
/// Does the agent talk when e.g. hit by the player
|
||||
// TODO move speech patterns into a Behavior component
|
||||
pub can_speak: bool,
|
||||
pub trade_for_site: Option<SiteId>,
|
||||
pub trading: bool,
|
||||
pub trading_issuer: bool,
|
||||
pub psyche: Psyche,
|
||||
pub inbox: VecDeque<AgentEvent>,
|
||||
pub action_timer: f32,
|
||||
@ -230,7 +227,6 @@ impl Agent {
|
||||
|
||||
pub fn with_destination(pos: Vec3<f32>) -> Self {
|
||||
Self {
|
||||
can_speak: true,
|
||||
psyche: Psyche { aggro: 1.0 },
|
||||
rtsim_controller: RtSimController::with_destination(pos),
|
||||
..Default::default()
|
||||
@ -239,14 +235,12 @@ impl Agent {
|
||||
|
||||
pub fn new(
|
||||
patrol_origin: Option<Vec3<f32>>,
|
||||
can_speak: bool,
|
||||
trade_for_site: Option<SiteId>,
|
||||
body: &Body,
|
||||
no_flee: bool,
|
||||
) -> Self {
|
||||
Agent {
|
||||
patrol_origin,
|
||||
can_speak,
|
||||
trade_for_site,
|
||||
psyche: if no_flee {
|
||||
Psyche { aggro: 1.0 }
|
||||
|
73
common/src/comp/behavior.rs
Normal file
73
common/src/comp/behavior.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use specs::Component;
|
||||
use specs_idvs::IdvStorage;
|
||||
use std::mem;
|
||||
|
||||
/// Behavior Component
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Behavior {
|
||||
tags: Vec<BehaviorTag>,
|
||||
}
|
||||
|
||||
/// Versatile tags attached to behaviors
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum BehaviorTag {
|
||||
/// The entity is allowed to speak
|
||||
CanSpeak,
|
||||
/// The entity is able to trade
|
||||
CanTrade,
|
||||
/// The entity has issued a trade
|
||||
TradingIssuer,
|
||||
}
|
||||
|
||||
impl Behavior {
|
||||
pub fn new(can_speak: bool, can_trade: bool) -> Self {
|
||||
let mut behavior = Self::default();
|
||||
if can_speak {
|
||||
behavior.add_tag(BehaviorTag::CanSpeak);
|
||||
}
|
||||
if can_trade {
|
||||
behavior.add_tag(BehaviorTag::CanTrade);
|
||||
}
|
||||
behavior
|
||||
}
|
||||
|
||||
/// Apply a tag to the Behavior
|
||||
pub fn add_tag(&mut self, tag: BehaviorTag) {
|
||||
if !self.has_tag(&tag) {
|
||||
self.tags.push(tag);
|
||||
}
|
||||
}
|
||||
|
||||
/// Revoke a tag to the Behavior
|
||||
pub fn remove_tag(&mut self, tag: BehaviorTag) {
|
||||
if self.has_tag(&tag) {
|
||||
while let Some(position) = self
|
||||
.tags
|
||||
.iter()
|
||||
.position(|behavior_tag| behavior_tag == &tag)
|
||||
{
|
||||
self.tags.remove(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the Behavior possess a specific tag
|
||||
pub fn has_tag(&self, tag: &BehaviorTag) -> bool {
|
||||
self.tags.iter().any(|behavior_tag| behavior_tag == tag)
|
||||
}
|
||||
|
||||
/// Get a specific tag by variant
|
||||
pub fn get_tag(&self, tag: &BehaviorTag) -> Option<&BehaviorTag> {
|
||||
self.tags
|
||||
.iter()
|
||||
.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>;
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
#[cfg(not(target_arch = "wasm32"))] pub mod agent;
|
||||
#[cfg(not(target_arch = "wasm32"))] pub mod aura;
|
||||
#[cfg(not(target_arch = "wasm32"))] pub mod beam;
|
||||
pub mod behavior;
|
||||
#[cfg(not(target_arch = "wasm32"))] pub mod body;
|
||||
pub mod buff;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@ -48,6 +49,7 @@ pub use self::{
|
||||
agent::{Agent, Alignment},
|
||||
aura::{Aura, AuraChange, AuraKind, Auras},
|
||||
beam::{Beam, BeamSegment},
|
||||
behavior::{Behavior, BehaviorTag},
|
||||
body::{
|
||||
biped_large, biped_small, bird_medium, bird_small, dragon, fish_medium, fish_small, golem,
|
||||
humanoid, object, quadruped_low, quadruped_medium, quadruped_small, ship, theropod,
|
||||
|
@ -120,6 +120,7 @@ pub enum ServerEvent {
|
||||
loadout: comp::inventory::loadout::Loadout,
|
||||
body: comp::Body,
|
||||
agent: Option<comp::Agent>,
|
||||
behavior: Option<comp::Behavior>,
|
||||
alignment: comp::Alignment,
|
||||
scale: comp::Scale,
|
||||
home_chunk: Option<comp::HomeChunk>,
|
||||
|
@ -2,7 +2,7 @@ use crate::{
|
||||
comp::{
|
||||
self,
|
||||
inventory::loadout_builder::{LoadoutBuilder, LoadoutConfig},
|
||||
CharacterState, StateUpdate,
|
||||
Behavior, CharacterState, StateUpdate,
|
||||
},
|
||||
event::{LocalEvent, ServerEvent},
|
||||
outcome::Outcome,
|
||||
@ -104,7 +104,8 @@ impl CharacterBehavior for Data {
|
||||
poise: comp::Poise::new(body),
|
||||
loadout,
|
||||
body,
|
||||
agent: Some(comp::Agent::new(None, false, None, &body, true)),
|
||||
agent: Some(comp::Agent::new(None, None, &body, true)),
|
||||
behavior: Some(Behavior::new(true, false)),
|
||||
alignment: comp::Alignment::Owned(*data.uid),
|
||||
scale: self
|
||||
.static_data
|
||||
|
@ -1074,7 +1074,8 @@ fn handle_spawn_airship(
|
||||
animated: true,
|
||||
});
|
||||
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.build();
|
||||
|
||||
|
@ -7,8 +7,9 @@ use common::{
|
||||
beam,
|
||||
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
||||
inventory::loadout::Loadout,
|
||||
shockwave, Agent, Alignment, Body, Gravity, Health, HomeChunk, Inventory, Item, ItemDrop,
|
||||
LightEmitter, Object, Ori, Poise, Pos, Projectile, Scale, Stats, Vel, WaypointArea,
|
||||
shockwave, Agent, Alignment, Behavior, Body, Gravity, Health, HomeChunk, Inventory, Item,
|
||||
ItemDrop, LightEmitter, Object, Ori, Poise, Pos, Projectile, Scale, Stats, Vel,
|
||||
WaypointArea,
|
||||
},
|
||||
outcome::Outcome,
|
||||
rtsim::RtSimEntity,
|
||||
@ -54,6 +55,7 @@ pub fn handle_create_npc(
|
||||
loadout: Loadout,
|
||||
body: Body,
|
||||
agent: impl Into<Option<Agent>>,
|
||||
behavior: Option<Behavior>,
|
||||
alignment: Alignment,
|
||||
scale: Scale,
|
||||
drop_item: Option<Item>,
|
||||
@ -73,6 +75,11 @@ pub fn handle_create_npc(
|
||||
} else {
|
||||
entity
|
||||
};
|
||||
let entity = if let Some(behavior) = behavior {
|
||||
entity.with(behavior)
|
||||
} else {
|
||||
entity.with(Behavior::default())
|
||||
};
|
||||
|
||||
let entity = if let Some(drop_item) = drop_item {
|
||||
entity.with(ItemDrop(drop_item))
|
||||
|
@ -1,11 +1,17 @@
|
||||
use specs::{world::WorldExt, Builder, Entity as EcsEntity};
|
||||
use specs::{world::WorldExt, Builder, Entity as EcsEntity, Join};
|
||||
use tracing::error;
|
||||
use vek::*;
|
||||
|
||||
use common::{
|
||||
comp::{
|
||||
self, agent::AgentEvent, dialogue::Subject, inventory::slot::EquipSlot, item, slot::Slot,
|
||||
tool::ToolKind, Inventory, Pos,
|
||||
self,
|
||||
agent::AgentEvent,
|
||||
dialogue::{AskedPerson, Subject},
|
||||
inventory::slot::EquipSlot,
|
||||
item,
|
||||
slot::Slot,
|
||||
tool::ToolKind,
|
||||
Inventory, Pos,
|
||||
},
|
||||
consts::MAX_MOUNT_RANGE,
|
||||
outcome::Outcome,
|
||||
@ -77,6 +83,45 @@ 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();
|
||||
|
||||
|
@ -142,6 +142,7 @@ impl Server {
|
||||
loadout,
|
||||
body,
|
||||
agent,
|
||||
behavior,
|
||||
alignment,
|
||||
scale,
|
||||
home_chunk,
|
||||
@ -156,6 +157,7 @@ impl Server {
|
||||
loadout,
|
||||
body,
|
||||
agent,
|
||||
behavior,
|
||||
alignment,
|
||||
scale,
|
||||
drop_item,
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use super::*;
|
||||
use common::{
|
||||
comp::{self, inventory::loadout_builder::LoadoutBuilder},
|
||||
comp::{self, inventory::loadout_builder::LoadoutBuilder, Behavior},
|
||||
event::{EventBus, ServerEvent},
|
||||
resources::{DeltaTime, Time},
|
||||
terrain::TerrainGrid,
|
||||
@ -103,13 +103,12 @@ impl<'a> System<'a> for Sys {
|
||||
.map(|e| e as f32)
|
||||
+ Vec3::new(0.5, 0.5, body.flying_height());
|
||||
let pos = comp::Pos(spawn_pos);
|
||||
let agent = Some(comp::Agent::new(
|
||||
None,
|
||||
matches!(body, comp::Body::Humanoid(_)),
|
||||
None,
|
||||
&body,
|
||||
false,
|
||||
));
|
||||
let agent = Some(comp::Agent::new(None, None, &body, false));
|
||||
let behavior = if matches!(body, comp::Body::Humanoid(_)) {
|
||||
Some(Behavior::new(true, false))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let rtsim_entity = Some(RtSimEntity(id));
|
||||
let event = match body {
|
||||
comp::Body::Ship(ship) => ServerEvent::CreateShip {
|
||||
@ -130,6 +129,7 @@ impl<'a> System<'a> for Sys {
|
||||
poise: comp::Poise::new(body),
|
||||
body,
|
||||
agent,
|
||||
behavior,
|
||||
alignment: match body {
|
||||
comp::Body::Humanoid(_) => comp::Alignment::Npc,
|
||||
_ => comp::Alignment::Wild,
|
||||
|
@ -191,7 +191,6 @@ impl<'a> System<'a> for Sys {
|
||||
agent: if entity.has_agency {
|
||||
Some(comp::Agent::new(
|
||||
Some(entity.pos),
|
||||
can_speak,
|
||||
trade_for_site,
|
||||
&body,
|
||||
matches!(
|
||||
@ -202,6 +201,11 @@ impl<'a> System<'a> for Sys {
|
||||
} else {
|
||||
None
|
||||
},
|
||||
behavior: if entity.has_agency {
|
||||
Some(comp::Behavior::new(can_speak, trade_for_site.is_some()))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
body,
|
||||
alignment,
|
||||
scale: comp::Scale(scale),
|
||||
|
Loading…
Reference in New Issue
Block a user