mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added basic rtsim NPC simulation, rtsim controller support
This commit is contained in:
parent
8ff438bb5b
commit
558dd99fd3
@ -57,7 +57,7 @@ pub struct RtSimController {
|
||||
/// When this field is `Some(..)`, the agent should attempt to make progress
|
||||
/// toward the given location, accounting for obstacles and other
|
||||
/// high-priority situations like being attacked.
|
||||
pub travel_to: Option<(Vec3<f32>, String)>,
|
||||
pub travel_to: Option<Vec3<f32>>,
|
||||
/// Proportion of full speed to move
|
||||
pub speed_factor: f32,
|
||||
/// Events
|
||||
@ -75,12 +75,10 @@ impl Default for RtSimController {
|
||||
}
|
||||
|
||||
impl RtSimController {
|
||||
pub fn reset(&mut self) { *self = Self::default(); }
|
||||
|
||||
pub fn with_destination(pos: Vec3<f32>) -> Self {
|
||||
Self {
|
||||
travel_to: Some((pos, format!("{:0.1?}", pos))),
|
||||
speed_factor: 0.25,
|
||||
travel_to: Some(pos),
|
||||
speed_factor: 0.5,
|
||||
events: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ pub struct Data {
|
||||
pub nature: Nature,
|
||||
pub npcs: Npcs,
|
||||
pub sites: Sites,
|
||||
|
||||
pub time: f64,
|
||||
}
|
||||
|
||||
pub type ReadError = rmp_serde::decode::Error;
|
||||
|
@ -2,50 +2,17 @@ use hashbrown::HashMap;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use slotmap::HopSlotMap;
|
||||
use vek::*;
|
||||
use rand::prelude::*;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use common::{
|
||||
uid::Uid,
|
||||
store::Id,
|
||||
rtsim::SiteId,
|
||||
rtsim::{SiteId, RtSimController},
|
||||
comp,
|
||||
};
|
||||
use world::util::RandomPerm;
|
||||
pub use common::rtsim::NpcId;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Npc {
|
||||
// Persisted state
|
||||
|
||||
/// Represents the location of the NPC.
|
||||
pub loc: NpcLoc,
|
||||
|
||||
// Unpersisted state
|
||||
|
||||
/// The position of the NPC in the world. Note that this is derived from [`Npc::loc`] and cannot be updated manually
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
wpos: Vec3<f32>,
|
||||
/// Whether the NPC is in simulated or loaded mode (when rtsim is run on the server, loaded corresponds to being
|
||||
/// within a loaded chunk). When in loaded mode, the interactions of the NPC should not be simulated but should
|
||||
/// instead be derived from the game.
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub mode: NpcMode,
|
||||
}
|
||||
|
||||
impl Npc {
|
||||
pub fn new(loc: NpcLoc) -> Self {
|
||||
Self {
|
||||
loc,
|
||||
wpos: Vec3::zero(),
|
||||
mode: NpcMode::Simulated,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wpos(&self) -> Vec3<f32> { self.wpos }
|
||||
|
||||
/// You almost certainly *DO NOT* want to use this method.
|
||||
///
|
||||
/// Update the NPC's wpos as a result of routine NPC simulation derived from its location.
|
||||
pub(crate) fn tick_wpos(&mut self, wpos: Vec3<f32>) { self.wpos = wpos; }
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub enum NpcMode {
|
||||
/// The NPC is unloaded and is being simulated via rtsim.
|
||||
@ -56,14 +23,46 @@ pub enum NpcMode {
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub enum NpcLoc {
|
||||
Wild { wpos: Vec3<f32> },
|
||||
Site { site: SiteId, wpos: Vec3<f32> },
|
||||
Travelling {
|
||||
a: SiteId,
|
||||
b: SiteId,
|
||||
frac: f32,
|
||||
},
|
||||
pub struct Npc {
|
||||
// Persisted state
|
||||
|
||||
/// Represents the location of the NPC.
|
||||
pub seed: u32,
|
||||
pub wpos: Vec3<f32>,
|
||||
|
||||
// Unpersisted state
|
||||
|
||||
/// (wpos, speed_factor)
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub target: Option<(Vec3<f32>, f32)>,
|
||||
/// Whether the NPC is in simulated or loaded mode (when rtsim is run on the server, loaded corresponds to being
|
||||
/// within a loaded chunk). When in loaded mode, the interactions of the NPC should not be simulated but should
|
||||
/// instead be derived from the game.
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub mode: NpcMode,
|
||||
}
|
||||
|
||||
impl Npc {
|
||||
const PERM_SPECIES: u32 = 0;
|
||||
const PERM_BODY: u32 = 1;
|
||||
|
||||
pub fn new(seed: u32, wpos: Vec3<f32>) -> Self {
|
||||
Self {
|
||||
seed,
|
||||
wpos,
|
||||
target: None,
|
||||
mode: NpcMode::Simulated,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rng(&self, perm: u32) -> impl Rng { RandomPerm::new(self.seed + perm) }
|
||||
|
||||
pub fn get_body(&self) -> comp::Body {
|
||||
let species = *(&comp::humanoid::ALL_SPECIES)
|
||||
.choose(&mut self.rng(Self::PERM_SPECIES))
|
||||
.unwrap();
|
||||
comp::humanoid::Body::random_with(&mut self.rng(Self::PERM_BODY), &species).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
|
@ -17,5 +17,8 @@ pub struct OnSetup;
|
||||
impl Event for OnSetup {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OnTick { pub dt: f32 }
|
||||
pub struct OnTick {
|
||||
pub dt: f32,
|
||||
pub time: f64,
|
||||
}
|
||||
impl Event for OnTick {}
|
||||
|
@ -1,7 +1,7 @@
|
||||
pub mod site;
|
||||
|
||||
use crate::data::{
|
||||
npc::{Npcs, Npc, NpcLoc},
|
||||
npc::{Npcs, Npc},
|
||||
site::{Sites, Site},
|
||||
Data,
|
||||
Nature,
|
||||
@ -25,6 +25,8 @@ impl Data {
|
||||
nature: Nature::generate(world),
|
||||
npcs: Npcs { npcs: Default::default() },
|
||||
sites: Sites { sites: Default::default() },
|
||||
|
||||
time: 0.0,
|
||||
};
|
||||
|
||||
// Register sites with rtsim
|
||||
@ -39,10 +41,12 @@ impl Data {
|
||||
|
||||
// Spawn some test entities at the sites
|
||||
for (site_id, site) in this.sites.iter() {
|
||||
let wpos = site.wpos.map(|e| e as f32)
|
||||
.with_z(world.sim().get_alt_approx(site.wpos).unwrap_or(0.0));
|
||||
this.npcs.create(Npc::new(NpcLoc::Site { site: site_id, wpos }));
|
||||
println!("Spawned rtsim NPC at {:?}", wpos);
|
||||
for _ in 0..10 {
|
||||
let wpos2d = site.wpos.map(|e| e + rng.gen_range(-10..10));
|
||||
let wpos = wpos2d.map(|e| e as f32 + 0.5)
|
||||
.with_z(world.sim().get_alt_approx(wpos2d).unwrap_or(0.0));
|
||||
this.npcs.create(Npc::new(rng.gen(), wpos));
|
||||
}
|
||||
}
|
||||
|
||||
this
|
||||
|
@ -49,8 +49,8 @@ impl RtState {
|
||||
|
||||
fn start_default_rules(&mut self) {
|
||||
info!("Starting default rtsim rules...");
|
||||
self.start_rule::<rule::simulate_npcs::SimulateNpcs>();
|
||||
self.start_rule::<rule::setup::Setup>();
|
||||
self.start_rule::<rule::simulate_npcs::SimulateNpcs>();
|
||||
}
|
||||
|
||||
pub fn start_rule<R: Rule>(&mut self) {
|
||||
@ -110,6 +110,8 @@ impl RtState {
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, world: &World, index: IndexRef, dt: f32) {
|
||||
self.emit(OnTick { dt }, world, index);
|
||||
self.data_mut().time += dt as f64;
|
||||
let event = OnTick { dt, time: self.data().time };
|
||||
self.emit(event, world, index);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use tracing::info;
|
||||
use vek::*;
|
||||
use crate::{
|
||||
data::npc::NpcLoc,
|
||||
data::npc::NpcMode,
|
||||
event::OnTick,
|
||||
RtState, Rule, RuleError,
|
||||
};
|
||||
@ -11,12 +12,33 @@ impl Rule for SimulateNpcs {
|
||||
fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
|
||||
|
||||
rtstate.bind::<Self, OnTick>(|ctx| {
|
||||
for (_, npc) in ctx.state.data_mut().npcs.iter_mut() {
|
||||
npc.tick_wpos(match npc.loc {
|
||||
NpcLoc::Wild { wpos } => wpos,
|
||||
NpcLoc::Site { site, wpos } => wpos,
|
||||
NpcLoc::Travelling { a, b, frac } => todo!(),
|
||||
});
|
||||
for npc in ctx.state
|
||||
.data_mut()
|
||||
.npcs
|
||||
.values_mut()
|
||||
.filter(|npc| matches!(npc.mode, NpcMode::Simulated))
|
||||
{
|
||||
let body = npc.get_body();
|
||||
|
||||
if let Some((target, speed_factor)) = npc.target {
|
||||
npc.wpos += Vec3::from(
|
||||
(target.xy() - npc.wpos.xy())
|
||||
.try_normalized()
|
||||
.unwrap_or_else(Vec2::zero)
|
||||
* body.max_speed_approx()
|
||||
* speed_factor,
|
||||
) * ctx.event.dt;
|
||||
}
|
||||
}
|
||||
|
||||
// Do some thinking. TODO: Not here!
|
||||
for npc in ctx.state
|
||||
.data_mut()
|
||||
.npcs
|
||||
.values_mut()
|
||||
{
|
||||
// TODO: Not this
|
||||
npc.target = Some((npc.wpos + Vec3::new(ctx.event.time.sin() as f32 * 16.0, ctx.event.time.cos() as f32 * 16.0, 0.0), 1.0));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -212,7 +212,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
|
||||
agent.action_state.timers[ActionTimers::TimerIdle as usize] = 0.0;
|
||||
if let Some((travel_to, _destination)) = &agent.rtsim_controller.travel_to {
|
||||
if let Some(travel_to) = &agent.rtsim_controller.travel_to {
|
||||
// If it has an rtsim destination and can fly, then it should.
|
||||
// If it is flying and bumps something above it, then it should move down.
|
||||
if self.traversal_config.can_fly
|
||||
|
@ -54,6 +54,7 @@ pub struct AgentData<'a> {
|
||||
pub stance: Option<&'a Stance>,
|
||||
pub cached_spatial_grid: &'a common::CachedSpatialGrid,
|
||||
pub msm: &'a MaterialStatManifest,
|
||||
pub rtsim_entity: Option<&'a RtSimEntity>,
|
||||
}
|
||||
|
||||
pub struct TargetData<'a> {
|
||||
@ -236,7 +237,7 @@ pub struct ReadData<'a> {
|
||||
pub light_emitter: ReadStorage<'a, LightEmitter>,
|
||||
#[cfg(feature = "worldgen")]
|
||||
pub world: ReadExpect<'a, Arc<world::World>>,
|
||||
// pub rtsim_entities: ReadStorage<'a, RtSimEntity>,
|
||||
pub rtsim_entities: ReadStorage<'a, RtSimEntity>,
|
||||
pub buffs: ReadStorage<'a, Buffs>,
|
||||
pub combos: ReadStorage<'a, Combo>,
|
||||
pub active_abilities: ReadStorage<'a, ActiveAbilities>,
|
||||
|
@ -8,7 +8,7 @@ use common::{
|
||||
generation::{BodyBuilder, EntityConfig, EntityInfo},
|
||||
resources::{DeltaTime, Time},
|
||||
slowjob::SlowJobPool,
|
||||
rtsim::RtSimEntity,
|
||||
rtsim::{RtSimEntity, RtSimController},
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
use rtsim2::data::npc::NpcMode;
|
||||
@ -26,6 +26,9 @@ impl<'a> System<'a> for Sys {
|
||||
ReadExpect<'a, Arc<world::World>>,
|
||||
ReadExpect<'a, world::IndexOwned>,
|
||||
ReadExpect<'a, SlowJobPool>,
|
||||
ReadStorage<'a, comp::Pos>,
|
||||
ReadStorage<'a, RtSimEntity>,
|
||||
WriteStorage<'a, comp::Agent>,
|
||||
);
|
||||
|
||||
const NAME: &'static str = "rtsim::tick";
|
||||
@ -34,7 +37,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
fn run(
|
||||
_job: &mut Job<Self>,
|
||||
(dt, time, mut server_event_bus, mut rtsim, world, index, slow_jobs): Self::SystemData,
|
||||
(dt, time, mut server_event_bus, mut rtsim, world, index, slow_jobs, positions, rtsim_entities, mut agents): Self::SystemData,
|
||||
) {
|
||||
let mut emitter = server_event_bus.emitter();
|
||||
let rtsim = &mut *rtsim;
|
||||
@ -47,7 +50,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
let chunk_states = rtsim.state.resource::<ChunkStates>();
|
||||
for (npc_id, npc) in rtsim.state.data_mut().npcs.iter_mut() {
|
||||
let chunk = npc.wpos()
|
||||
let chunk = npc.wpos
|
||||
.xy()
|
||||
.map2(TerrainChunk::RECT_SIZE, |e, sz| (e as i32).div_euclid(sz as i32));
|
||||
|
||||
@ -55,18 +58,18 @@ impl<'a> System<'a> for Sys {
|
||||
if matches!(npc.mode, NpcMode::Simulated) && chunk_states.0.get(chunk).map_or(false, |c| c.is_some()) {
|
||||
npc.mode = NpcMode::Loaded;
|
||||
|
||||
let body = comp::Body::Object(comp::object::Body::Scarecrow);
|
||||
let body = npc.get_body();
|
||||
emitter.emit(ServerEvent::CreateNpc {
|
||||
pos: comp::Pos(npc.wpos()),
|
||||
pos: comp::Pos(npc.wpos),
|
||||
stats: comp::Stats::new("Rtsim NPC".to_string()),
|
||||
skill_set: comp::SkillSet::default(),
|
||||
health: None,
|
||||
poise: comp::Poise::new(body),
|
||||
inventory: comp::Inventory::with_empty(),
|
||||
body,
|
||||
agent: None,
|
||||
agent: Some(comp::Agent::from_body(&body)),
|
||||
alignment: comp::Alignment::Wild,
|
||||
scale: comp::Scale(10.0),
|
||||
scale: comp::Scale(1.0),
|
||||
anchor: None,
|
||||
loot: Default::default(),
|
||||
rtsim_entity: Some(RtSimEntity(npc_id)),
|
||||
@ -75,147 +78,24 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
|
||||
// rtsim.tick += 1;
|
||||
|
||||
// Update unloaded rtsim entities, in groups at a time
|
||||
/*
|
||||
const TICK_STAGGER: usize = 30;
|
||||
let entities_per_iteration = rtsim.entities.len() / TICK_STAGGER;
|
||||
let mut to_reify = Vec::new();
|
||||
for (id, entity) in rtsim
|
||||
.entities
|
||||
.iter_mut()
|
||||
.skip((rtsim.tick as usize % TICK_STAGGER) * entities_per_iteration)
|
||||
.take(entities_per_iteration)
|
||||
.filter(|(_, e)| !e.is_loaded)
|
||||
{
|
||||
if rtsim
|
||||
.chunk_states
|
||||
.get(entity.pos.xy())
|
||||
.copied()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
to_reify.push(id);
|
||||
} else {
|
||||
// Simulate behaviour
|
||||
if let Some(travel_to) = &entity.controller.travel_to {
|
||||
// Move towards target at approximate character speed
|
||||
entity.pos += Vec3::from(
|
||||
(travel_to.0.xy() - entity.pos.xy())
|
||||
.try_normalized()
|
||||
.unwrap_or_else(Vec2::zero)
|
||||
* entity.get_body().max_speed_approx()
|
||||
* entity.controller.speed_factor,
|
||||
) * dt;
|
||||
}
|
||||
|
||||
if let Some(alt) = world
|
||||
.sim()
|
||||
.get_alt_approx(entity.pos.xy().map(|e| e.floor() as i32))
|
||||
{
|
||||
entity.pos.z = alt;
|
||||
}
|
||||
}
|
||||
// entity.tick(&time, &terrain, &world, &index.as_index_ref());
|
||||
}
|
||||
*/
|
||||
|
||||
// Tick entity AI each time if it's loaded
|
||||
// for (_, entity) in rtsim.entities.iter_mut().filter(|(_, e)|
|
||||
// e.is_loaded) { entity.last_time_ticked = time.0;
|
||||
// entity.tick(&time, &terrain, &world, &index.as_index_ref());
|
||||
// }
|
||||
|
||||
/*
|
||||
let mut server_emitter = server_event_bus.emitter();
|
||||
for id in to_reify {
|
||||
rtsim.reify_entity(id);
|
||||
let entity = &rtsim.entities[id];
|
||||
let rtsim_entity = Some(RtSimEntity(id));
|
||||
|
||||
let body = entity.get_body();
|
||||
let spawn_pos = terrain
|
||||
.find_space(entity.pos.map(|e| e.floor() as i32))
|
||||
.map(|e| e as f32)
|
||||
+ Vec3::new(0.5, 0.5, body.flying_height());
|
||||
|
||||
let pos = comp::Pos(spawn_pos);
|
||||
|
||||
let event = if let comp::Body::Ship(ship) = body {
|
||||
ServerEvent::CreateShip {
|
||||
pos,
|
||||
ship,
|
||||
mountable: false,
|
||||
agent: Some(comp::Agent::from_body(&body)),
|
||||
rtsim_entity,
|
||||
}
|
||||
} else {
|
||||
let entity_config_path = entity.get_entity_config();
|
||||
let mut loadout_rng = entity.loadout_rng();
|
||||
let ad_hoc_loadout = entity.get_adhoc_loadout();
|
||||
// Body is rewritten so that body parameters
|
||||
// are consistent between reifications
|
||||
let entity_config = EntityConfig::from_asset_expect_owned(entity_config_path)
|
||||
.with_body(BodyBuilder::Exact(body));
|
||||
|
||||
let mut entity_info = EntityInfo::at(pos.0)
|
||||
.with_entity_config(entity_config, Some(entity_config_path), &mut loadout_rng)
|
||||
.with_lazy_loadout(ad_hoc_loadout);
|
||||
// Merchants can be traded with
|
||||
if let Some(economy) = entity.get_trade_info(&world, &index) {
|
||||
entity_info = entity_info
|
||||
.with_agent_mark(comp::agent::Mark::Merchant)
|
||||
.with_economy(&economy);
|
||||
}
|
||||
match NpcData::from_entity_info(entity_info) {
|
||||
NpcData::Data {
|
||||
pos,
|
||||
stats,
|
||||
skill_set,
|
||||
health,
|
||||
poise,
|
||||
inventory,
|
||||
agent,
|
||||
body,
|
||||
alignment,
|
||||
scale,
|
||||
loot,
|
||||
} => ServerEvent::CreateNpc {
|
||||
pos,
|
||||
stats,
|
||||
skill_set,
|
||||
health,
|
||||
poise,
|
||||
inventory,
|
||||
agent,
|
||||
body,
|
||||
alignment,
|
||||
scale,
|
||||
anchor: None,
|
||||
loot,
|
||||
rtsim_entity,
|
||||
projectile: None,
|
||||
},
|
||||
// EntityConfig can't represent Waypoints at all
|
||||
// as of now, and if someone will try to spawn
|
||||
// rtsim waypoint it is definitely error.
|
||||
NpcData::Waypoint(_) => unimplemented!(),
|
||||
}
|
||||
};
|
||||
server_emitter.emit(event);
|
||||
}
|
||||
|
||||
// Update rtsim with real entity data
|
||||
for (pos, rtsim_entity, agent) in (&positions, &rtsim_entities, &mut agents).join() {
|
||||
// Synchronise rtsim NPC with entity data
|
||||
for (pos, rtsim_entity, agent) in (&positions, &rtsim_entities, (&mut agents).maybe()).join() {
|
||||
rtsim
|
||||
.entities
|
||||
.state
|
||||
.data_mut()
|
||||
.npcs
|
||||
.get_mut(rtsim_entity.0)
|
||||
.filter(|e| e.is_loaded)
|
||||
.map(|entity| {
|
||||
entity.pos = pos.0;
|
||||
agent.rtsim_controller = entity.controller.clone();
|
||||
.filter(|npc| matches!(npc.mode, NpcMode::Loaded))
|
||||
.map(|npc| {
|
||||
// Update rtsim NPC state
|
||||
npc.wpos = pos.0;
|
||||
|
||||
// Update entity state
|
||||
if let Some(agent) = agent {
|
||||
agent.rtsim_controller.travel_to = npc.target.map(|(wpos, _)| wpos);
|
||||
agent.rtsim_controller.speed_factor = npc.target.map_or(1.0, |(_, sf)| sf);
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
@ -206,6 +206,7 @@ impl<'a> System<'a> for Sys {
|
||||
msm: &read_data.msm,
|
||||
poise: read_data.poises.get(entity),
|
||||
stance: read_data.stances.get(entity),
|
||||
rtsim_entity: read_data.rtsim_entities.get(entity),
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::rtsim::Entity as RtSimEntity;
|
||||
use common::rtsim::RtSimEntity;
|
||||
use common::{
|
||||
comp::{
|
||||
agent::{
|
||||
@ -40,8 +40,6 @@ mod interaction;
|
||||
pub struct BehaviorData<'a, 'b, 'c> {
|
||||
pub agent: &'a mut Agent,
|
||||
pub agent_data: AgentData<'a>,
|
||||
// TODO: Move rtsim back into AgentData after rtsim2 when it has a separate crate
|
||||
// pub rtsim_entity: Option<&'a RtSimEntity>,
|
||||
pub read_data: &'a ReadData<'a>,
|
||||
pub event_emitter: &'a mut Emitter<'c, ServerEvent>,
|
||||
pub controller: &'a mut Controller,
|
||||
@ -643,7 +641,6 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
||||
let BehaviorData {
|
||||
agent,
|
||||
agent_data,
|
||||
// rtsim_entity,
|
||||
read_data,
|
||||
event_emitter,
|
||||
controller,
|
||||
@ -750,7 +747,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
||||
controller,
|
||||
read_data,
|
||||
event_emitter,
|
||||
will_ambush(/* *rtsim_entity */None, agent_data),
|
||||
will_ambush(agent_data.rtsim_entity, agent_data),
|
||||
);
|
||||
}
|
||||
|
||||
@ -768,9 +765,9 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
||||
read_data,
|
||||
event_emitter,
|
||||
rng,
|
||||
remembers_fight_with(/* *rtsim_entity */None, read_data, target),
|
||||
remembers_fight_with(agent_data.rtsim_entity, read_data, target),
|
||||
);
|
||||
remember_fight(/* *rtsim_entity */ None, read_data, agent, target);
|
||||
remember_fight(agent_data.rtsim_entity, read_data, agent, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -780,10 +777,11 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
||||
|
||||
fn will_ambush(rtsim_entity: Option<&RtSimEntity>, agent_data: &AgentData) -> bool {
|
||||
// TODO: implement for rtsim2
|
||||
agent_data
|
||||
.health
|
||||
.map_or(false, |h| h.current() / h.maximum() > 0.7)
|
||||
&& rtsim_entity.map_or(false, |re| re.brain.personality.will_ambush)
|
||||
// agent_data
|
||||
// .health
|
||||
// .map_or(false, |h| h.current() / h.maximum() > 0.7)
|
||||
// && rtsim_entity.map_or(false, |re| re.brain.personality.will_ambush)
|
||||
false
|
||||
}
|
||||
|
||||
fn remembers_fight_with(
|
||||
@ -794,11 +792,12 @@ fn remembers_fight_with(
|
||||
// TODO: implement for rtsim2
|
||||
let name = || read_data.stats.get(other).map(|stats| stats.name.clone());
|
||||
|
||||
rtsim_entity.map_or(false, |rtsim_entity| {
|
||||
name().map_or(false, |name| {
|
||||
rtsim_entity.brain.remembers_fight_with_character(&name)
|
||||
})
|
||||
})
|
||||
// rtsim_entity.map_or(false, |rtsim_entity| {
|
||||
// name().map_or(false, |name| {
|
||||
// rtsim_entity.brain.remembers_fight_with_character(&name)
|
||||
// })
|
||||
// })
|
||||
false
|
||||
}
|
||||
|
||||
/// Remember target.
|
||||
|
@ -16,7 +16,6 @@ use rand::{thread_rng, Rng};
|
||||
use specs::saveload::Marker;
|
||||
|
||||
use crate::{
|
||||
rtsim::entity::{PersonalityTrait, RtSimEntityKind},
|
||||
sys::agent::util::get_entity_by_id,
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user