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,
|
||||
},
|
||||
store::Id,
|
||||
terrain::TerrainChunkSize,
|
||||
vol::RectVolSize,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use rand::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use slotmap::HopSlotMap;
|
||||
@ -87,22 +89,22 @@ pub struct Npc {
|
||||
pub personality: Personality,
|
||||
|
||||
// Unpersisted state
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
#[serde(skip)]
|
||||
pub chunk_pos: Option<Vec2<i32>>,
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
#[serde(skip)]
|
||||
pub current_site: Option<SiteId>,
|
||||
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
#[serde(skip)]
|
||||
pub controller: Controller,
|
||||
|
||||
/// 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)]
|
||||
#[serde(skip)]
|
||||
pub mode: SimulationMode,
|
||||
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
#[serde(skip)]
|
||||
pub brain: Option<Brain>,
|
||||
}
|
||||
|
||||
@ -203,13 +205,13 @@ pub struct Vehicle {
|
||||
|
||||
pub body: comp::ship::Body,
|
||||
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
#[serde(skip)]
|
||||
pub chunk_pos: Option<Vec2<i32>>,
|
||||
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
#[serde(skip)]
|
||||
pub driver: Option<Actor>,
|
||||
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
#[serde(skip)]
|
||||
// TODO: Find a way to detect riders when the vehicle is loaded
|
||||
pub riders: Vec<Actor>,
|
||||
|
||||
@ -217,7 +219,7 @@ pub struct Vehicle {
|
||||
/// the server, loaded corresponds to being within a loaded chunk). When
|
||||
/// in loaded mode, the interactions of the Vehicle should not be
|
||||
/// simulated but should instead be derived from the game.
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
#[serde(skip)]
|
||||
pub mode: SimulationMode,
|
||||
}
|
||||
|
||||
@ -250,7 +252,6 @@ impl Vehicle {
|
||||
#[derive(Default, Clone, Serialize, Deserialize)]
|
||||
pub struct GridCell {
|
||||
pub npcs: Vec<NpcId>,
|
||||
pub characters: Vec<common::character::CharacterId>,
|
||||
pub vehicles: Vec<VehicleId>,
|
||||
}
|
||||
|
||||
@ -258,8 +259,11 @@ pub struct GridCell {
|
||||
pub struct Npcs {
|
||||
pub npcs: HopSlotMap<NpcId, Npc>,
|
||||
pub vehicles: HopSlotMap<VehicleId, Vehicle>,
|
||||
// TODO: This feels like it should be its own rtsim resource
|
||||
#[serde(skip, default = "construct_npc_grid")]
|
||||
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()) }
|
||||
@ -273,8 +277,11 @@ impl Npcs {
|
||||
|
||||
/// Queries nearby npcs, not garantueed to work if radius > 32.0
|
||||
pub fn nearby(&self, wpos: Vec2<f32>, radius: f32) -> impl Iterator<Item = Actor> + '_ {
|
||||
let chunk_pos =
|
||||
wpos.as_::<i32>() / common::terrain::TerrainChunkSize::RECT_SIZE.as_::<i32>();
|
||||
let chunk_pos = wpos
|
||||
.as_::<i32>()
|
||||
.map2(TerrainChunkSize::RECT_SIZE.as_::<i32>(), |e, sz| {
|
||||
e.div_euclid(sz)
|
||||
});
|
||||
let r_sqr = radius * radius;
|
||||
LOCALITY
|
||||
.into_iter()
|
||||
@ -289,19 +296,24 @@ impl Npcs {
|
||||
.map_or(false, |npc| npc.wpos.xy().distance_squared(wpos) < r_sqr)
|
||||
})
|
||||
.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()
|
||||
.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(),
|
||||
vehicles: Default::default(),
|
||||
npc_grid: Grid::new(Vec2::zero(), Default::default()),
|
||||
character_map: Default::default(),
|
||||
},
|
||||
sites: Sites {
|
||||
sites: Default::default(),
|
||||
|
@ -457,7 +457,7 @@ fn socialize() -> impl Action {
|
||||
just(|ctx| {
|
||||
let mut rng = thread_rng();
|
||||
// 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
|
||||
.state
|
||||
.data()
|
||||
|
@ -3,14 +3,15 @@
|
||||
use super::*;
|
||||
use crate::sys::terrain::NpcData;
|
||||
use common::{
|
||||
comp::{self, Body},
|
||||
comp::{self, Body, Presence, PresenceKind},
|
||||
event::{EventBus, NpcBuilder, ServerEvent},
|
||||
generation::{BodyBuilder, EntityConfig, EntityInfo},
|
||||
resources::{DeltaTime, Time, TimeOfDay},
|
||||
rtsim::{Actor, RtSimEntity, RtSimVehicle},
|
||||
slowjob::SlowJobPool,
|
||||
terrain::CoordinateConversions,
|
||||
terrain::{CoordinateConversions, TerrainChunkSize},
|
||||
trade::{Good, SiteInformation},
|
||||
vol::RectVolSize,
|
||||
LoadoutBuilder,
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
@ -187,6 +188,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, RtSimEntity>,
|
||||
ReadStorage<'a, RtSimVehicle>,
|
||||
WriteStorage<'a, comp::Agent>,
|
||||
ReadStorage<'a, Presence>,
|
||||
);
|
||||
|
||||
const NAME: &'static str = "rtsim::tick";
|
||||
@ -208,16 +210,53 @@ impl<'a> System<'a> for Sys {
|
||||
rtsim_entities,
|
||||
rtsim_vehicles,
|
||||
mut agents,
|
||||
presences,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let mut emitter = server_event_bus.emitter();
|
||||
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
|
||||
.state
|
||||
.tick(&world, index.as_index_ref(), *time_of_day, *time, dt.0);
|
||||
|
||||
// Perform a save if required
|
||||
if rtsim
|
||||
.last_saved
|
||||
.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 data = &mut *rtsim.state.data_mut();
|
||||
|
||||
// Load in vehicles
|
||||
for (vehicle_id, vehicle) in data.npcs.vehicles.iter_mut() {
|
||||
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() {
|
||||
let chunk = npc.wpos.xy().as_::<i32>().wpos_to_cpos();
|
||||
|
||||
|
@ -478,24 +478,24 @@ fn handle_rtsim_actions(bdata: &mut BehaviorData) -> bool {
|
||||
if let Some(target) = bdata.read_data.lookup_actor(actor) {
|
||||
let target_pos = bdata.read_data.positions.get(target).map(|pos| pos.0);
|
||||
|
||||
bdata.agent.target = Some(Target::new(
|
||||
target,
|
||||
false,
|
||||
bdata.read_data.time.0,
|
||||
false,
|
||||
target_pos,
|
||||
));
|
||||
|
||||
if bdata.agent_data.look_toward(
|
||||
&mut bdata.controller,
|
||||
&bdata.read_data,
|
||||
target,
|
||||
) {
|
||||
bdata.agent.target = Some(Target::new(
|
||||
target,
|
||||
false,
|
||||
bdata.read_data.time.0,
|
||||
false,
|
||||
target_pos,
|
||||
));
|
||||
|
||||
bdata.controller.push_action(ControlAction::Talk);
|
||||
bdata.controller.push_utterance(UtteranceKind::Greeting);
|
||||
bdata
|
||||
.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
|
||||
bdata
|
||||
.agent
|
||||
|
Loading…
Reference in New Issue
Block a user