mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added rtsim sites
This commit is contained in:
parent
1dc7518200
commit
c856f2625c
@ -11,6 +11,8 @@ use crate::comp::dialogue::MoodState;
|
||||
|
||||
slotmap::new_key_type! { pub struct NpcId; }
|
||||
|
||||
slotmap::new_key_type! { pub struct SiteId; }
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct RtSimEntity(pub NpcId);
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
pub mod npc;
|
||||
pub mod site;
|
||||
pub mod nature;
|
||||
|
||||
pub use self::{
|
||||
npc::{Npc, NpcId, Npcs},
|
||||
site::{Site, SiteId, Sites},
|
||||
nature::Nature,
|
||||
};
|
||||
|
||||
@ -25,6 +27,7 @@ pub enum Actor {
|
||||
pub struct Data {
|
||||
pub nature: Nature,
|
||||
pub npcs: Npcs,
|
||||
pub sites: Sites,
|
||||
}
|
||||
|
||||
pub type ReadError = rmp_serde::decode::Error;
|
||||
@ -40,13 +43,6 @@ impl Data {
|
||||
}
|
||||
}
|
||||
|
||||
// fn rugged_ser_enum_map<K: EnumArray<V> + Serialize, V: PartialEq + Default + Serialize, S: ser::Serializer>(map: &EnumMap<K, V>, mut ser: S) -> Result<S::Ok, S::Error> {
|
||||
// ser.collect_map(map
|
||||
// .iter()
|
||||
// .filter(|(k, v)| v != &&V::default())
|
||||
// .map(|(k, v)| (k, v)))
|
||||
// }
|
||||
|
||||
fn rugged_ser_enum_map<
|
||||
K: EnumArray<V> + Serialize,
|
||||
V: From<i16> + PartialEq + Serialize,
|
||||
|
@ -3,21 +3,47 @@ use serde::{Serialize, Deserialize};
|
||||
use slotmap::HopSlotMap;
|
||||
use vek::*;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use common::uid::Uid;
|
||||
|
||||
use common::{
|
||||
uid::Uid,
|
||||
store::Id,
|
||||
rtsim::SiteId,
|
||||
};
|
||||
pub use common::rtsim::NpcId;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Npc {
|
||||
pub wpos: Vec3<f32>,
|
||||
// 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 at(wpos: Vec3<f32>) -> Self {
|
||||
Self { wpos, mode: NpcMode::Simulated }
|
||||
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)]
|
||||
@ -29,13 +55,24 @@ pub enum NpcMode {
|
||||
Loaded,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub enum NpcLoc {
|
||||
Wild { wpos: Vec3<f32> },
|
||||
Site { site: SiteId, wpos: Vec3<f32> },
|
||||
Travelling {
|
||||
a: SiteId,
|
||||
b: SiteId,
|
||||
frac: f32,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Npcs {
|
||||
pub npcs: HopSlotMap<NpcId, Npc>,
|
||||
}
|
||||
|
||||
impl Npcs {
|
||||
pub fn spawn(&mut self, npc: Npc) -> NpcId {
|
||||
pub fn create(&mut self, npc: Npc) -> NpcId {
|
||||
self.npcs.insert(npc)
|
||||
}
|
||||
}
|
||||
|
47
rtsim/src/data/site.rs
Normal file
47
rtsim/src/data/site.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use slotmap::HopSlotMap;
|
||||
use vek::*;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use common::{
|
||||
uid::Uid,
|
||||
store::Id,
|
||||
};
|
||||
pub use common::rtsim::SiteId;
|
||||
use world::site::Site as WorldSite;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Site {
|
||||
pub wpos: Vec2<i32>,
|
||||
|
||||
/// The site generated during initial worldgen that this site corresponds to.
|
||||
///
|
||||
/// Eventually, rtsim should replace initial worldgen's site system and this will not be necessary.
|
||||
///
|
||||
/// When setting up rtsim state, we try to 'link' these two definitions of a site: but if initial worldgen has
|
||||
/// changed, this might not be possible. We try to delete sites that no longer exist during setup, but this is an
|
||||
/// inherent fallible process. If linking fails, we try to delete the site in rtsim2 in order to avoid an
|
||||
/// 'orphaned' site. (TODO: create new sites for new initial worldgen sites that come into being too).
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub world_site: Option<Id<WorldSite>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Sites {
|
||||
pub sites: HopSlotMap<SiteId, Site>,
|
||||
}
|
||||
|
||||
impl Sites {
|
||||
pub fn create(&mut self, site: Site) -> SiteId {
|
||||
self.sites.insert(site)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Sites {
|
||||
type Target = HopSlotMap<SiteId, Site>;
|
||||
fn deref(&self) -> &Self::Target { &self.sites }
|
||||
}
|
||||
|
||||
impl DerefMut for Sites {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.sites }
|
||||
}
|
@ -1,8 +1,21 @@
|
||||
use common::resources::Time;
|
||||
use world::{World, IndexRef};
|
||||
use super::{Rule, RtState};
|
||||
|
||||
pub trait Event: Clone + 'static {}
|
||||
|
||||
pub struct EventCtx<'a, R: Rule, E: Event> {
|
||||
pub state: &'a RtState,
|
||||
pub rule: &'a mut R,
|
||||
pub event: &'a E,
|
||||
pub world: &'a World,
|
||||
pub index: IndexRef<'a>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OnSetup;
|
||||
impl Event for OnSetup {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OnTick { pub dt: f32 }
|
||||
|
||||
impl Event for OnTick {}
|
||||
|
@ -1,6 +1,14 @@
|
||||
use crate::data::{Npcs, Npc, Data, Nature};
|
||||
pub mod site;
|
||||
|
||||
use crate::data::{
|
||||
npc::{Npcs, Npc, NpcLoc},
|
||||
site::{Sites, Site},
|
||||
Data,
|
||||
Nature,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use rand::prelude::*;
|
||||
use tracing::info;
|
||||
use world::{
|
||||
site::SiteKind,
|
||||
IndexRef,
|
||||
@ -8,7 +16,7 @@ use world::{
|
||||
};
|
||||
|
||||
impl Data {
|
||||
pub fn generate(index: IndexRef, world: &World) -> Self {
|
||||
pub fn generate(world: &World, index: IndexRef) -> Self {
|
||||
let mut seed = [0; 32];
|
||||
seed.iter_mut().zip(&mut index.seed.to_le_bytes()).for_each(|(dst, src)| *dst = *src);
|
||||
let mut rng = SmallRng::from_seed(seed);
|
||||
@ -16,25 +24,25 @@ impl Data {
|
||||
let mut this = Self {
|
||||
nature: Nature::generate(world),
|
||||
npcs: Npcs { npcs: Default::default() },
|
||||
sites: Sites { sites: Default::default() },
|
||||
};
|
||||
|
||||
for (site_id, site) in world
|
||||
.civs()
|
||||
// Register sites with rtsim
|
||||
for (world_site_id, _) in index
|
||||
.sites
|
||||
.iter()
|
||||
.filter_map(|(site_id, site)| site.site_tmp.map(|id| (site_id, &index.sites[id])))
|
||||
{
|
||||
match &site.kind {
|
||||
SiteKind::Refactor(site2) => {
|
||||
let wpos = site.get_origin()
|
||||
.map(|e| e as f32 + 0.5)
|
||||
.with_z(world.sim().get_alt_approx(site.get_origin()).unwrap_or(0.0));
|
||||
// TODO: Better API
|
||||
this.npcs.spawn(Npc::at(wpos));
|
||||
println!("Spawned rtsim NPC at {:?}", wpos);
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
let site = Site::generate(world_site_id, world, index);
|
||||
this.sites.create(site);
|
||||
}
|
||||
info!("Registering {} rtsim sites from world sites.", this.sites.len());
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
this
|
||||
|
25
rtsim/src/gen/site.rs
Normal file
25
rtsim/src/gen/site.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use crate::data::Site;
|
||||
use common::store::Id;
|
||||
use world::{
|
||||
site::Site as WorldSite,
|
||||
World,
|
||||
IndexRef,
|
||||
};
|
||||
|
||||
impl Site {
|
||||
pub fn generate(world_site: Id<WorldSite>, world: &World, index: IndexRef) -> Self {
|
||||
// match &world_site.kind {
|
||||
// SiteKind::Refactor(site2) => {
|
||||
// let site = Site::generate(world_site_id, world, index);
|
||||
// println!("Registering rtsim site at {:?}...", site.wpos);
|
||||
// this.sites.create(site);
|
||||
// }
|
||||
// _ => {},
|
||||
// }
|
||||
|
||||
Self {
|
||||
wpos: index.sites.get(world_site).get_origin(),
|
||||
world_site: Some(world_site),
|
||||
}
|
||||
}
|
||||
}
|
@ -7,9 +7,10 @@ pub mod rule;
|
||||
|
||||
pub use self::{
|
||||
data::Data,
|
||||
event::{Event, OnTick},
|
||||
event::{Event, EventCtx, OnTick},
|
||||
rule::{Rule, RuleError},
|
||||
};
|
||||
use world::{World, IndexRef};
|
||||
use anymap2::SendSyncAnyMap;
|
||||
use tracing::{info, error};
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
@ -25,7 +26,7 @@ pub struct RtState {
|
||||
}
|
||||
|
||||
type RuleState<R> = AtomicRefCell<R>;
|
||||
type EventHandlersOf<E> = Vec<Box<dyn Fn(&RtState, E) + Send + Sync + 'static>>;
|
||||
type EventHandlersOf<E> = Vec<Box<dyn Fn(&RtState, &World, IndexRef, &E) + Send + Sync + 'static>>;
|
||||
|
||||
impl RtState {
|
||||
pub fn new(data: Data) -> Self {
|
||||
@ -48,7 +49,8 @@ impl RtState {
|
||||
|
||||
fn start_default_rules(&mut self) {
|
||||
info!("Starting default rtsim rules...");
|
||||
self.start_rule::<rule::example::RuleState>();
|
||||
self.start_rule::<rule::simulate_npcs::SimulateNpcs>();
|
||||
self.start_rule::<rule::setup::Setup>();
|
||||
}
|
||||
|
||||
pub fn start_rule<R: Rule>(&mut self) {
|
||||
@ -66,13 +68,19 @@ impl RtState {
|
||||
.borrow_mut()
|
||||
}
|
||||
|
||||
pub fn bind<R: Rule, E: Event>(&mut self, mut f: impl FnMut(&mut R, &RtState, E) + Send + Sync + 'static) {
|
||||
pub fn bind<R: Rule, E: Event>(&mut self, mut f: impl FnMut(EventCtx<R, E>) + Send + Sync + 'static) {
|
||||
let f = AtomicRefCell::new(f);
|
||||
self.event_handlers
|
||||
.entry::<EventHandlersOf<E>>()
|
||||
.or_default()
|
||||
.push(Box::new(move |rtstate, event| {
|
||||
(f.borrow_mut())(&mut rtstate.rule_mut(), rtstate, event)
|
||||
.push(Box::new(move |state, world, index, event| {
|
||||
(f.borrow_mut())(EventCtx {
|
||||
state,
|
||||
rule: &mut state.rule_mut(),
|
||||
event,
|
||||
world,
|
||||
index,
|
||||
})
|
||||
}));
|
||||
}
|
||||
|
||||
@ -93,15 +101,15 @@ impl RtState {
|
||||
.borrow_mut()
|
||||
}
|
||||
|
||||
pub fn emit<E: Event>(&mut self, e: E) {
|
||||
pub fn emit<E: Event>(&mut self, e: E, world: &World, index: IndexRef) {
|
||||
self.event_handlers
|
||||
.get::<EventHandlersOf<E>>()
|
||||
.map(|handlers| handlers
|
||||
.iter()
|
||||
.for_each(|f| f(self, e.clone())));
|
||||
.for_each(|f| f(self, world, index, &e)));
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, dt: f32) {
|
||||
self.emit(OnTick { dt });
|
||||
pub fn tick(&mut self, world: &World, index: IndexRef, dt: f32) {
|
||||
self.emit(OnTick { dt }, world, index);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub mod example;
|
||||
pub mod setup;
|
||||
pub mod simulate_npcs;
|
||||
|
||||
use std::fmt;
|
||||
use super::RtState;
|
||||
|
@ -1,19 +0,0 @@
|
||||
use tracing::info;
|
||||
use crate::{
|
||||
event::OnTick,
|
||||
RtState, Rule, RuleError,
|
||||
};
|
||||
|
||||
pub struct RuleState;
|
||||
|
||||
impl Rule for RuleState {
|
||||
fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
|
||||
info!("Hello from example rule!");
|
||||
|
||||
rtstate.bind::<Self, OnTick>(|this, rtstate, event| {
|
||||
// println!("Tick!");
|
||||
});
|
||||
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
48
rtsim/src/rule/setup.rs
Normal file
48
rtsim/src/rule/setup.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use tracing::warn;
|
||||
use crate::{
|
||||
data::Site,
|
||||
event::OnSetup,
|
||||
RtState, Rule, RuleError,
|
||||
};
|
||||
|
||||
/// This rule runs at rtsim startup and broadly acts to perform some primitive migration/sanitisation in order to
|
||||
/// ensure that the state of rtsim is mostly sensible.
|
||||
pub struct Setup;
|
||||
|
||||
impl Rule for Setup {
|
||||
fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
|
||||
rtstate.bind::<Self, OnSetup>(|ctx| {
|
||||
// Delete rtsim sites that don't correspond to a world site
|
||||
ctx.state.data_mut().sites.retain(|site_id, site| {
|
||||
if let Some((world_site_id, _)) = ctx.index.sites
|
||||
.iter()
|
||||
.find(|(_, world_site)| world_site.get_origin() == site.wpos)
|
||||
{
|
||||
site.world_site = Some(world_site_id);
|
||||
true
|
||||
} else {
|
||||
warn!("{:?} is no longer valid because the site it was derived from no longer exists. It will now be deleted.", site_id);
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
// Generate rtsim sites for world sites that don't have a corresponding rtsim site yet
|
||||
for (world_site_id, _) in ctx.index.sites.iter() {
|
||||
if !ctx.state.data().sites
|
||||
.values()
|
||||
.any(|site| site.world_site.expect("Rtsim site not assigned to world site") == world_site_id)
|
||||
{
|
||||
warn!("{:?} is new and does not have a corresponding rtsim site. One will now be generated afresh.", world_site_id);
|
||||
ctx.state
|
||||
.data_mut()
|
||||
.sites
|
||||
.create(Site::generate(world_site_id, ctx.world, ctx.index));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Reassign sites for NPCs if they don't have one
|
||||
});
|
||||
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
25
rtsim/src/rule/simulate_npcs.rs
Normal file
25
rtsim/src/rule/simulate_npcs.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use tracing::info;
|
||||
use crate::{
|
||||
data::npc::NpcLoc,
|
||||
event::OnTick,
|
||||
RtState, Rule, RuleError,
|
||||
};
|
||||
|
||||
pub struct SimulateNpcs;
|
||||
|
||||
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!(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ use crate::{
|
||||
},
|
||||
// rtsim::RtSim,
|
||||
sys::terrain::SAFE_ZONE_RADIUS,
|
||||
rtsim2,
|
||||
Server, SpawnPoint, StateExt,
|
||||
};
|
||||
use authc::Uuid;
|
||||
@ -26,7 +27,7 @@ use common::{
|
||||
event::{EventBus, ServerEvent},
|
||||
outcome::{HealthChangeInfo, Outcome},
|
||||
resources::{Secs, Time},
|
||||
// rtsim::RtSimEntity,
|
||||
rtsim::RtSimEntity,
|
||||
states::utils::{AbilityInfo, StageSection},
|
||||
terrain::{Block, BlockKind, TerrainGrid},
|
||||
uid::{Uid, UidAllocator},
|
||||
@ -519,7 +520,6 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt
|
||||
}
|
||||
|
||||
if should_delete {
|
||||
/*
|
||||
if let Some(rtsim_entity) = state
|
||||
.ecs()
|
||||
.read_storage::<RtSimEntity>()
|
||||
@ -528,10 +528,9 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt
|
||||
{
|
||||
state
|
||||
.ecs()
|
||||
.write_resource::<RtSim>()
|
||||
.destroy_entity(rtsim_entity.0);
|
||||
.write_resource::<rtsim2::RtSim>()
|
||||
.hook_rtsim_entity_delete(rtsim_entity);
|
||||
}
|
||||
*/
|
||||
|
||||
if let Err(e) = state.delete_entity_recorded(entity) {
|
||||
error!(?e, ?entity, "Failed to delete destroyed entity");
|
||||
|
@ -707,7 +707,13 @@ impl Server {
|
||||
fn on_block_update(ecs: &specs::World, wpos: Vec3<i32>, old_block: Block, new_block: Block) {
|
||||
// When a resource block updates, inform rtsim
|
||||
if old_block.get_rtsim_resource().is_some() || new_block.get_rtsim_resource().is_some() {
|
||||
ecs.write_resource::<rtsim2::RtSim>().hook_block_update(wpos, old_block, new_block);
|
||||
ecs.write_resource::<rtsim2::RtSim>().hook_block_update(
|
||||
&ecs.read_resource::<Arc<world::World>>(),
|
||||
ecs.read_resource::<Arc<world::IndexOwned>>().as_index_ref(),
|
||||
wpos,
|
||||
old_block,
|
||||
new_block,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ use rtsim2::{
|
||||
ReadError,
|
||||
},
|
||||
rule::Rule,
|
||||
event::OnSetup,
|
||||
RtState,
|
||||
};
|
||||
use specs::{DispatcherBuilder, WorldExt};
|
||||
@ -80,7 +81,7 @@ impl RtSim {
|
||||
warn!("'RTSIM_NOLOAD' is set, skipping loading of rtsim state (old state will be overwritten).");
|
||||
}
|
||||
|
||||
let data = Data::generate(index, &world);
|
||||
let data = Data::generate(&world, index);
|
||||
info!("Rtsim data generated.");
|
||||
data
|
||||
};
|
||||
@ -94,6 +95,8 @@ impl RtSim {
|
||||
|
||||
rule::start_rules(&mut this.state);
|
||||
|
||||
this.state.emit(OnSetup, world, index);
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
@ -120,8 +123,8 @@ impl RtSim {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hook_block_update(&mut self, wpos: Vec3<i32>, old: Block, new: Block) {
|
||||
self.state.emit(event::OnBlockChange { wpos, old, new });
|
||||
pub fn hook_block_update(&mut self, world: &World, index: IndexRef, wpos: Vec3<i32>, old: Block, new: Block) {
|
||||
self.state.emit(event::OnBlockChange { wpos, old, new }, world, index);
|
||||
}
|
||||
|
||||
pub fn hook_rtsim_entity_unload(&mut self, entity: RtSimEntity) {
|
||||
@ -130,6 +133,11 @@ impl RtSim {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hook_rtsim_entity_delete(&mut self, entity: RtSimEntity) {
|
||||
// TODO: Emit event on deletion to catch death?
|
||||
self.state.data_mut().npcs.remove(entity.0);
|
||||
}
|
||||
|
||||
pub fn save(&mut self, slowjob_pool: &SlowJobPool) {
|
||||
info!("Saving rtsim data...");
|
||||
let file_path = self.file_path.clone();
|
||||
|
@ -5,5 +5,5 @@ use rtsim2::RtState;
|
||||
|
||||
pub fn start_rules(rtstate: &mut RtState) {
|
||||
info!("Starting server rtsim rules...");
|
||||
rtstate.start_rule::<deplete_resources::State>();
|
||||
rtstate.start_rule::<deplete_resources::DepleteResources>();
|
||||
}
|
||||
|
@ -9,20 +9,20 @@ use common::{
|
||||
vol::RectRasterableVol,
|
||||
};
|
||||
|
||||
pub struct State;
|
||||
pub struct DepleteResources;
|
||||
|
||||
impl Rule for State {
|
||||
impl Rule for DepleteResources {
|
||||
fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
|
||||
info!("Hello from the resource depletion rule!");
|
||||
|
||||
rtstate.bind::<Self, OnBlockChange>(|this, rtstate, event| {
|
||||
let key = event.wpos
|
||||
rtstate.bind::<Self, OnBlockChange>(|ctx| {
|
||||
let key = ctx.event.wpos
|
||||
.xy()
|
||||
.map2(TerrainChunk::RECT_SIZE, |e, sz| e.div_euclid(sz as i32));
|
||||
if let Some(Some(chunk_state)) = rtstate.resource_mut::<ChunkStates>().0.get(key) {
|
||||
let mut chunk_res = rtstate.data().nature.get_chunk_resources(key);
|
||||
if let Some(Some(chunk_state)) = ctx.state.resource_mut::<ChunkStates>().0.get(key) {
|
||||
let mut chunk_res = ctx.state.data().nature.get_chunk_resources(key);
|
||||
// Remove resources
|
||||
if let Some(res) = event.old.get_rtsim_resource() {
|
||||
if let Some(res) = ctx.event.old.get_rtsim_resource() {
|
||||
if chunk_state.max_res[res] > 0 {
|
||||
chunk_res[res] = (chunk_res[res] * chunk_state.max_res[res] as f32 - 1.0)
|
||||
.round()
|
||||
@ -30,7 +30,7 @@ impl Rule for State {
|
||||
}
|
||||
}
|
||||
// Add resources
|
||||
if let Some(res) = event.new.get_rtsim_resource() {
|
||||
if let Some(res) = ctx.event.new.get_rtsim_resource() {
|
||||
if chunk_state.max_res[res] > 0 {
|
||||
chunk_res[res] = (chunk_res[res] * chunk_state.max_res[res] as f32 + 1.0)
|
||||
.round()
|
||||
@ -38,7 +38,7 @@ impl Rule for State {
|
||||
}
|
||||
}
|
||||
println!("Chunk resources = {:?}", chunk_res);
|
||||
rtstate.data_mut().nature.set_chunk_resources(key, chunk_res);
|
||||
ctx.state.data_mut().nature.set_chunk_resources(key, chunk_res);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -39,7 +39,7 @@ impl<'a> System<'a> for Sys {
|
||||
let mut emitter = server_event_bus.emitter();
|
||||
let rtsim = &mut *rtsim;
|
||||
|
||||
rtsim.state.tick(dt.0);
|
||||
rtsim.state.tick(&world, index.as_index_ref(), dt.0);
|
||||
|
||||
if rtsim.last_saved.map_or(true, |ls| ls.elapsed() > Duration::from_secs(60)) {
|
||||
rtsim.save(&slow_jobs);
|
||||
@ -47,7 +47,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,11 +55,9 @@ 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;
|
||||
|
||||
println!("Loading in rtsim NPC at {:?}", npc.wpos);
|
||||
|
||||
let body = comp::Body::Object(comp::object::Body::Scarecrow);
|
||||
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,
|
||||
|
@ -7,7 +7,7 @@ use hashbrown::{HashMap, HashSet};
|
||||
use tracing::warn;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DebugShape {
|
||||
Line([Vec3<f32>; 2]),
|
||||
Cylinder {
|
||||
@ -261,7 +261,8 @@ pub struct DebugShapeId(pub u64);
|
||||
|
||||
pub struct Debug {
|
||||
next_shape_id: DebugShapeId,
|
||||
pending_shapes: HashMap<DebugShapeId, DebugShape>,
|
||||
shapes: HashMap<DebugShapeId, DebugShape>,
|
||||
pending: HashSet<DebugShapeId>,
|
||||
pending_locals: HashMap<DebugShapeId, ([f32; 4], [f32; 4], [f32; 4])>,
|
||||
pending_deletes: HashSet<DebugShapeId>,
|
||||
models: HashMap<DebugShapeId, (Model<DebugVertex>, Bound<Consts<DebugLocals>>)>,
|
||||
@ -272,7 +273,8 @@ impl Debug {
|
||||
pub fn new() -> Debug {
|
||||
Debug {
|
||||
next_shape_id: DebugShapeId(0),
|
||||
pending_shapes: HashMap::new(),
|
||||
shapes: HashMap::new(),
|
||||
pending: HashSet::new(),
|
||||
pending_locals: HashMap::new(),
|
||||
pending_deletes: HashSet::new(),
|
||||
models: HashMap::new(),
|
||||
@ -286,10 +288,15 @@ impl Debug {
|
||||
if matches!(shape, DebugShape::TrainTrack { .. }) {
|
||||
self.casts_shadow.insert(id);
|
||||
}
|
||||
self.pending_shapes.insert(id, shape);
|
||||
self.shapes.insert(id, shape);
|
||||
self.pending.insert(id);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn get_shape(&self, id: DebugShapeId) -> Option<&DebugShape> {
|
||||
self.shapes.get(&id)
|
||||
}
|
||||
|
||||
pub fn set_context(&mut self, id: DebugShapeId, pos: [f32; 4], color: [f32; 4], ori: [f32; 4]) {
|
||||
self.pending_locals.insert(id, (pos, color, ori));
|
||||
}
|
||||
@ -297,19 +304,21 @@ impl Debug {
|
||||
pub fn remove_shape(&mut self, id: DebugShapeId) { self.pending_deletes.insert(id); }
|
||||
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer) {
|
||||
for (id, shape) in self.pending_shapes.drain() {
|
||||
if let Some(model) = renderer.create_model(&shape.mesh()) {
|
||||
let locals = renderer.create_debug_bound_locals(&[DebugLocals {
|
||||
pos: [0.0; 4],
|
||||
color: [1.0, 0.0, 0.0, 1.0],
|
||||
ori: [0.0, 0.0, 0.0, 1.0],
|
||||
}]);
|
||||
self.models.insert(id, (model, locals));
|
||||
} else {
|
||||
warn!(
|
||||
"Failed to create model for debug shape {:?}: {:?}",
|
||||
id, shape
|
||||
);
|
||||
for id in self.pending.drain() {
|
||||
if let Some(shape) = self.shapes.get(&id) {
|
||||
if let Some(model) = renderer.create_model(&shape.mesh()) {
|
||||
let locals = renderer.create_debug_bound_locals(&[DebugLocals {
|
||||
pos: [0.0; 4],
|
||||
color: [1.0, 0.0, 0.0, 1.0],
|
||||
ori: [0.0, 0.0, 0.0, 1.0],
|
||||
}]);
|
||||
self.models.insert(id, (model, locals));
|
||||
} else {
|
||||
warn!(
|
||||
"Failed to create model for debug shape {:?}: {:?}",
|
||||
id, shape
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (id, (pos, color, ori)) in self.pending_locals.drain() {
|
||||
@ -330,6 +339,7 @@ impl Debug {
|
||||
}
|
||||
for id in self.pending_deletes.drain() {
|
||||
self.models.remove(&id);
|
||||
self.shapes.remove(&id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1465,17 +1465,28 @@ impl Scene {
|
||||
z_min,
|
||||
z_max,
|
||||
} => {
|
||||
let scale = scale.map_or(1.0, |s| s.0);
|
||||
current_entities.insert(entity);
|
||||
let s = scale.map_or(1.0, |sc| sc.0);
|
||||
|
||||
let shape = DebugShape::CapsulePrism {
|
||||
p0: *p0 * scale,
|
||||
p1: *p1 * scale,
|
||||
radius: *radius * scale,
|
||||
height: (*z_max - *z_min) * scale,
|
||||
};
|
||||
|
||||
// If this shape no longer matches, remove the old one
|
||||
if let Some(shape_id) = hitboxes.get(&entity) {
|
||||
if self.debug.get_shape(*shape_id).map_or(false, |s| s != &shape) {
|
||||
self.debug.remove_shape(*shape_id);
|
||||
hitboxes.remove(&entity);
|
||||
}
|
||||
}
|
||||
|
||||
let shape_id = hitboxes.entry(entity).or_insert_with(|| {
|
||||
self.debug.add_shape(DebugShape::CapsulePrism {
|
||||
p0: *p0 * s,
|
||||
p1: *p1 * s,
|
||||
radius: *radius * s,
|
||||
height: (*z_max - *z_min) * s,
|
||||
})
|
||||
self.debug.add_shape(shape)
|
||||
});
|
||||
let hb_pos = [pos.0.x, pos.0.y, pos.0.z + *z_min, 0.0];
|
||||
let hb_pos = [pos.0.x, pos.0.y, pos.0.z + *z_min * scale, 0.0];
|
||||
let color = if group == Some(&comp::group::ENEMY) {
|
||||
[1.0, 0.0, 0.0, 0.5]
|
||||
} else if group == Some(&comp::group::NPC) {
|
||||
|
@ -1430,6 +1430,7 @@ fn find_site_loc(
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Failed to place site {:?}.", site_kind);
|
||||
None
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user