mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Made rtsim aware of character locations
This commit is contained in:
parent
7175f7f02f
commit
2d7d172f49
@ -7,8 +7,10 @@ use common::{
|
|||||||
Actor, ChunkResource, FactionId, NpcAction, NpcActivity, Personality, SiteId, VehicleId,
|
Actor, ChunkResource, FactionId, NpcAction, NpcActivity, Personality, SiteId, VehicleId,
|
||||||
},
|
},
|
||||||
store::Id,
|
store::Id,
|
||||||
|
terrain::TerrainChunkSize,
|
||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
};
|
};
|
||||||
|
use hashbrown::HashMap;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use slotmap::HopSlotMap;
|
use slotmap::HopSlotMap;
|
||||||
@ -87,22 +89,22 @@ pub struct Npc {
|
|||||||
pub personality: Personality,
|
pub personality: Personality,
|
||||||
|
|
||||||
// Unpersisted state
|
// Unpersisted state
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip)]
|
||||||
pub chunk_pos: Option<Vec2<i32>>,
|
pub chunk_pos: Option<Vec2<i32>>,
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip)]
|
||||||
pub current_site: Option<SiteId>,
|
pub current_site: Option<SiteId>,
|
||||||
|
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip)]
|
||||||
pub controller: Controller,
|
pub controller: Controller,
|
||||||
|
|
||||||
/// Whether the NPC is in simulated or loaded mode (when rtsim is run on the
|
/// 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
|
/// server, loaded corresponds to being within a loaded chunk). When in
|
||||||
/// loaded mode, the interactions of the NPC should not be simulated but
|
/// loaded mode, the interactions of the NPC should not be simulated but
|
||||||
/// should instead be derived from the game.
|
/// should instead be derived from the game.
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip)]
|
||||||
pub mode: SimulationMode,
|
pub mode: SimulationMode,
|
||||||
|
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip)]
|
||||||
pub brain: Option<Brain>,
|
pub brain: Option<Brain>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,13 +205,13 @@ pub struct Vehicle {
|
|||||||
|
|
||||||
pub body: comp::ship::Body,
|
pub body: comp::ship::Body,
|
||||||
|
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip)]
|
||||||
pub chunk_pos: Option<Vec2<i32>>,
|
pub chunk_pos: Option<Vec2<i32>>,
|
||||||
|
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip)]
|
||||||
pub driver: Option<Actor>,
|
pub driver: Option<Actor>,
|
||||||
|
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip)]
|
||||||
// TODO: Find a way to detect riders when the vehicle is loaded
|
// TODO: Find a way to detect riders when the vehicle is loaded
|
||||||
pub riders: Vec<Actor>,
|
pub riders: Vec<Actor>,
|
||||||
|
|
||||||
@ -217,7 +219,7 @@ pub struct Vehicle {
|
|||||||
/// the server, loaded corresponds to being within a loaded chunk). When
|
/// the server, loaded corresponds to being within a loaded chunk). When
|
||||||
/// in loaded mode, the interactions of the Vehicle should not be
|
/// in loaded mode, the interactions of the Vehicle should not be
|
||||||
/// simulated but should instead be derived from the game.
|
/// simulated but should instead be derived from the game.
|
||||||
#[serde(skip_serializing, skip_deserializing)]
|
#[serde(skip)]
|
||||||
pub mode: SimulationMode,
|
pub mode: SimulationMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +252,6 @@ impl Vehicle {
|
|||||||
#[derive(Default, Clone, Serialize, Deserialize)]
|
#[derive(Default, Clone, Serialize, Deserialize)]
|
||||||
pub struct GridCell {
|
pub struct GridCell {
|
||||||
pub npcs: Vec<NpcId>,
|
pub npcs: Vec<NpcId>,
|
||||||
pub characters: Vec<common::character::CharacterId>,
|
|
||||||
pub vehicles: Vec<VehicleId>,
|
pub vehicles: Vec<VehicleId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,8 +259,11 @@ pub struct GridCell {
|
|||||||
pub struct Npcs {
|
pub struct Npcs {
|
||||||
pub npcs: HopSlotMap<NpcId, Npc>,
|
pub npcs: HopSlotMap<NpcId, Npc>,
|
||||||
pub vehicles: HopSlotMap<VehicleId, Vehicle>,
|
pub vehicles: HopSlotMap<VehicleId, Vehicle>,
|
||||||
|
// TODO: This feels like it should be its own rtsim resource
|
||||||
#[serde(skip, default = "construct_npc_grid")]
|
#[serde(skip, default = "construct_npc_grid")]
|
||||||
pub npc_grid: Grid<GridCell>,
|
pub npc_grid: Grid<GridCell>,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub character_map: HashMap<Vec2<i32>, Vec<(common::character::CharacterId, Vec3<f32>)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn construct_npc_grid() -> Grid<GridCell> { Grid::new(Vec2::zero(), Default::default()) }
|
fn construct_npc_grid() -> Grid<GridCell> { Grid::new(Vec2::zero(), Default::default()) }
|
||||||
@ -273,8 +277,11 @@ impl Npcs {
|
|||||||
|
|
||||||
/// Queries nearby npcs, not garantueed to work if radius > 32.0
|
/// Queries nearby npcs, not garantueed to work if radius > 32.0
|
||||||
pub fn nearby(&self, wpos: Vec2<f32>, radius: f32) -> impl Iterator<Item = Actor> + '_ {
|
pub fn nearby(&self, wpos: Vec2<f32>, radius: f32) -> impl Iterator<Item = Actor> + '_ {
|
||||||
let chunk_pos =
|
let chunk_pos = wpos
|
||||||
wpos.as_::<i32>() / common::terrain::TerrainChunkSize::RECT_SIZE.as_::<i32>();
|
.as_::<i32>()
|
||||||
|
.map2(TerrainChunkSize::RECT_SIZE.as_::<i32>(), |e, sz| {
|
||||||
|
e.div_euclid(sz)
|
||||||
|
});
|
||||||
let r_sqr = radius * radius;
|
let r_sqr = radius * radius;
|
||||||
LOCALITY
|
LOCALITY
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -289,19 +296,24 @@ impl Npcs {
|
|||||||
.map_or(false, |npc| npc.wpos.xy().distance_squared(wpos) < r_sqr)
|
.map_or(false, |npc| npc.wpos.xy().distance_squared(wpos) < r_sqr)
|
||||||
})
|
})
|
||||||
.map(Actor::Npc)
|
.map(Actor::Npc)
|
||||||
.chain(cell.characters
|
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
// TODO: Filter characters by distance too
|
|
||||||
// .filter(move |npc| {
|
|
||||||
// self.npcs
|
|
||||||
// .get(*npc)
|
|
||||||
// .map_or(false, |npc| npc.wpos.xy().distance_squared(wpos) < r_sqr)
|
|
||||||
// })
|
|
||||||
.map(Actor::Character))
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.flatten()
|
.flatten()
|
||||||
|
.chain(
|
||||||
|
self.character_map
|
||||||
|
.get(&chunk_pos)
|
||||||
|
.map(|characters| {
|
||||||
|
characters.iter().filter_map(move |(character, c_wpos)| {
|
||||||
|
if c_wpos.xy().distance_squared(wpos) < r_sqr {
|
||||||
|
Some(Actor::Character(*character))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.into_iter()
|
||||||
|
.flatten(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ impl Data {
|
|||||||
npcs: Default::default(),
|
npcs: Default::default(),
|
||||||
vehicles: Default::default(),
|
vehicles: Default::default(),
|
||||||
npc_grid: Grid::new(Vec2::zero(), Default::default()),
|
npc_grid: Grid::new(Vec2::zero(), Default::default()),
|
||||||
|
character_map: Default::default(),
|
||||||
},
|
},
|
||||||
sites: Sites {
|
sites: Sites {
|
||||||
sites: Default::default(),
|
sites: Default::default(),
|
||||||
|
@ -457,7 +457,7 @@ fn socialize() -> impl Action {
|
|||||||
just(|ctx| {
|
just(|ctx| {
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
// TODO: Bit odd, should wait for a while after greeting
|
// TODO: Bit odd, should wait for a while after greeting
|
||||||
if thread_rng().gen_bool(0.0002) {
|
if thread_rng().gen_bool(0.0003) {
|
||||||
if let Some(other) = ctx
|
if let Some(other) = ctx
|
||||||
.state
|
.state
|
||||||
.data()
|
.data()
|
||||||
|
@ -3,14 +3,15 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::sys::terrain::NpcData;
|
use crate::sys::terrain::NpcData;
|
||||||
use common::{
|
use common::{
|
||||||
comp::{self, Body},
|
comp::{self, Body, Presence, PresenceKind},
|
||||||
event::{EventBus, NpcBuilder, ServerEvent},
|
event::{EventBus, NpcBuilder, ServerEvent},
|
||||||
generation::{BodyBuilder, EntityConfig, EntityInfo},
|
generation::{BodyBuilder, EntityConfig, EntityInfo},
|
||||||
resources::{DeltaTime, Time, TimeOfDay},
|
resources::{DeltaTime, Time, TimeOfDay},
|
||||||
rtsim::{Actor, RtSimEntity, RtSimVehicle},
|
rtsim::{Actor, RtSimEntity, RtSimVehicle},
|
||||||
slowjob::SlowJobPool,
|
slowjob::SlowJobPool,
|
||||||
terrain::CoordinateConversions,
|
terrain::{CoordinateConversions, TerrainChunkSize},
|
||||||
trade::{Good, SiteInformation},
|
trade::{Good, SiteInformation},
|
||||||
|
vol::RectVolSize,
|
||||||
LoadoutBuilder,
|
LoadoutBuilder,
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
@ -187,6 +188,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
ReadStorage<'a, RtSimEntity>,
|
ReadStorage<'a, RtSimEntity>,
|
||||||
ReadStorage<'a, RtSimVehicle>,
|
ReadStorage<'a, RtSimVehicle>,
|
||||||
WriteStorage<'a, comp::Agent>,
|
WriteStorage<'a, comp::Agent>,
|
||||||
|
ReadStorage<'a, Presence>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const NAME: &'static str = "rtsim::tick";
|
const NAME: &'static str = "rtsim::tick";
|
||||||
@ -208,16 +210,53 @@ impl<'a> System<'a> for Sys {
|
|||||||
rtsim_entities,
|
rtsim_entities,
|
||||||
rtsim_vehicles,
|
rtsim_vehicles,
|
||||||
mut agents,
|
mut agents,
|
||||||
|
presences,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
let mut emitter = server_event_bus.emitter();
|
let mut emitter = server_event_bus.emitter();
|
||||||
let rtsim = &mut *rtsim;
|
let rtsim = &mut *rtsim;
|
||||||
|
|
||||||
rtsim.state.data_mut().time_of_day = *time_of_day;
|
// Set up rtsim inputs
|
||||||
|
{
|
||||||
|
let mut data = rtsim.state.data_mut();
|
||||||
|
|
||||||
|
// Update time of day
|
||||||
|
data.time_of_day = *time_of_day;
|
||||||
|
|
||||||
|
// Update character map (i.e: so that rtsim knows where players are)
|
||||||
|
// TODO: Other entities too? Or do we now care about that?
|
||||||
|
data.npcs.character_map.clear();
|
||||||
|
for (character, wpos) in
|
||||||
|
(&presences, &positions)
|
||||||
|
.join()
|
||||||
|
.filter_map(|(presence, pos)| {
|
||||||
|
if let PresenceKind::Character(character) = &presence.kind {
|
||||||
|
Some((character, pos.0))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
{
|
||||||
|
let chunk_pos = wpos
|
||||||
|
.xy()
|
||||||
|
.as_::<i32>()
|
||||||
|
.map2(TerrainChunkSize::RECT_SIZE.as_::<i32>(), |e, sz| {
|
||||||
|
e.div_euclid(sz)
|
||||||
|
});
|
||||||
|
data.npcs
|
||||||
|
.character_map
|
||||||
|
.entry(chunk_pos)
|
||||||
|
.or_default()
|
||||||
|
.push((*character, wpos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tick rtsim
|
||||||
rtsim
|
rtsim
|
||||||
.state
|
.state
|
||||||
.tick(&world, index.as_index_ref(), *time_of_day, *time, dt.0);
|
.tick(&world, index.as_index_ref(), *time_of_day, *time, dt.0);
|
||||||
|
|
||||||
|
// Perform a save if required
|
||||||
if rtsim
|
if rtsim
|
||||||
.last_saved
|
.last_saved
|
||||||
.map_or(true, |ls| ls.elapsed() > Duration::from_secs(60))
|
.map_or(true, |ls| ls.elapsed() > Duration::from_secs(60))
|
||||||
@ -230,6 +269,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
let chunk_states = rtsim.state.resource::<ChunkStates>();
|
let chunk_states = rtsim.state.resource::<ChunkStates>();
|
||||||
let data = &mut *rtsim.state.data_mut();
|
let data = &mut *rtsim.state.data_mut();
|
||||||
|
|
||||||
|
// Load in vehicles
|
||||||
for (vehicle_id, vehicle) in data.npcs.vehicles.iter_mut() {
|
for (vehicle_id, vehicle) in data.npcs.vehicles.iter_mut() {
|
||||||
let chunk = vehicle.wpos.xy().as_::<i32>().wpos_to_cpos();
|
let chunk = vehicle.wpos.xy().as_::<i32>().wpos_to_cpos();
|
||||||
|
|
||||||
@ -296,6 +336,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load in NPCs
|
||||||
for (npc_id, npc) in data.npcs.npcs.iter_mut() {
|
for (npc_id, npc) in data.npcs.npcs.iter_mut() {
|
||||||
let chunk = npc.wpos.xy().as_::<i32>().wpos_to_cpos();
|
let chunk = npc.wpos.xy().as_::<i32>().wpos_to_cpos();
|
||||||
|
|
||||||
|
@ -478,6 +478,11 @@ fn handle_rtsim_actions(bdata: &mut BehaviorData) -> bool {
|
|||||||
if let Some(target) = bdata.read_data.lookup_actor(actor) {
|
if let Some(target) = bdata.read_data.lookup_actor(actor) {
|
||||||
let target_pos = bdata.read_data.positions.get(target).map(|pos| pos.0);
|
let target_pos = bdata.read_data.positions.get(target).map(|pos| pos.0);
|
||||||
|
|
||||||
|
if bdata.agent_data.look_toward(
|
||||||
|
&mut bdata.controller,
|
||||||
|
&bdata.read_data,
|
||||||
|
target,
|
||||||
|
) {
|
||||||
bdata.agent.target = Some(Target::new(
|
bdata.agent.target = Some(Target::new(
|
||||||
target,
|
target,
|
||||||
false,
|
false,
|
||||||
@ -486,16 +491,11 @@ fn handle_rtsim_actions(bdata: &mut BehaviorData) -> bool {
|
|||||||
target_pos,
|
target_pos,
|
||||||
));
|
));
|
||||||
|
|
||||||
if bdata.agent_data.look_toward(
|
|
||||||
&mut bdata.controller,
|
|
||||||
&bdata.read_data,
|
|
||||||
target,
|
|
||||||
) {
|
|
||||||
bdata.controller.push_action(ControlAction::Talk);
|
bdata.controller.push_action(ControlAction::Talk);
|
||||||
bdata.controller.push_utterance(UtteranceKind::Greeting);
|
bdata.controller.push_utterance(UtteranceKind::Greeting);
|
||||||
bdata
|
bdata
|
||||||
.agent_data
|
.agent_data
|
||||||
.chat_npc("npc-speech-villager", &mut bdata.event_emitter);
|
.chat_npc("npc-speech-villager_open", &mut bdata.event_emitter);
|
||||||
// Start a timer so that they eventually stop interacting
|
// Start a timer so that they eventually stop interacting
|
||||||
bdata
|
bdata
|
||||||
.agent
|
.agent
|
||||||
|
Loading…
Reference in New Issue
Block a user