mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
sync
This commit is contained in:
parent
3a52cc1fa3
commit
e8b489a71a
@ -9,8 +9,8 @@ use common::{
|
||||
character_state::OutputEvents,
|
||||
inventory::item::{tool::AbilityMap, MaterialStatManifest},
|
||||
ActiveAbilities, Beam, Body, CharacterState, Combo, Controller, Density, Energy, Health,
|
||||
Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise, Pos, SkillSet, Stance,
|
||||
StateUpdate, Stats, Vel, Scale,
|
||||
Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise, Pos, Scale, SkillSet, Stance,
|
||||
StateUpdate, Stats, Vel,
|
||||
},
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
link::Is,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use common::{
|
||||
comp::{Body, Controller, InputKind, Ori, Pos, Vel, Scale},
|
||||
comp::{Body, Controller, InputKind, Ori, Pos, Scale, Vel},
|
||||
link::Is,
|
||||
mounting::Mount,
|
||||
uid::UidAllocator,
|
||||
@ -69,8 +69,10 @@ impl<'a> System<'a> for Sys {
|
||||
let vel = velocities.get(entity).copied();
|
||||
if let (Some(pos), Some(ori), Some(vel)) = (pos, ori, vel) {
|
||||
let mounter_body = bodies.get(rider);
|
||||
let mounting_offset = body.map_or(Vec3::unit_z(), Body::mount_offset) * scales.get(entity).map_or(1.0, |s| s.0)
|
||||
+ mounter_body.map_or(Vec3::zero(), Body::rider_offset) * scales.get(rider).map_or(1.0, |s| s.0);
|
||||
let mounting_offset = body.map_or(Vec3::unit_z(), Body::mount_offset)
|
||||
* scales.get(entity).map_or(1.0, |s| s.0)
|
||||
+ mounter_body.map_or(Vec3::zero(), Body::rider_offset)
|
||||
* scales.get(rider).map_or(1.0, |s| s.0);
|
||||
let _ = positions.insert(rider, Pos(pos.0 + ori.to_quat() * mounting_offset));
|
||||
let _ = orientations.insert(rider, ori);
|
||||
let _ = velocities.insert(rider, vel);
|
||||
|
@ -62,7 +62,13 @@ fn integrate_forces(
|
||||
// Aerodynamic/hydrodynamic forces
|
||||
if !rel_flow.0.is_approx_zero() {
|
||||
debug_assert!(!rel_flow.0.map(|a| a.is_nan()).reduce_or());
|
||||
let impulse = dt.0 * body.aerodynamic_forces(&rel_flow, fluid_density.0, wings, scale.map_or(1.0, |s| s.0));
|
||||
let impulse = dt.0
|
||||
* body.aerodynamic_forces(
|
||||
&rel_flow,
|
||||
fluid_density.0,
|
||||
wings,
|
||||
scale.map_or(1.0, |s| s.0),
|
||||
);
|
||||
debug_assert!(!impulse.map(|a| a.is_nan()).reduce_or());
|
||||
if !impulse.is_approx_zero() {
|
||||
let new_v = vel.0 + impulse / mass.0;
|
||||
|
@ -1,14 +1,11 @@
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use slotmap::HopSlotMap;
|
||||
use vek::*;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use common::{
|
||||
uid::Uid,
|
||||
store::Id,
|
||||
};
|
||||
use super::Actor;
|
||||
pub use common::rtsim::FactionId;
|
||||
use common::{store::Id, uid::Uid};
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use slotmap::HopSlotMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Faction {
|
||||
@ -21,13 +18,12 @@ pub struct Factions {
|
||||
}
|
||||
|
||||
impl Factions {
|
||||
pub fn create(&mut self, faction: Faction) -> FactionId {
|
||||
self.factions.insert(faction)
|
||||
}
|
||||
pub fn create(&mut self, faction: Faction) -> FactionId { self.factions.insert(faction) }
|
||||
}
|
||||
|
||||
impl Deref for Factions {
|
||||
type Target = HopSlotMap<FactionId, Faction>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.factions }
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,26 @@
|
||||
pub mod faction;
|
||||
pub mod nature;
|
||||
pub mod npc;
|
||||
pub mod site;
|
||||
pub mod nature;
|
||||
|
||||
pub use self::{
|
||||
npc::{Npc, NpcId, Npcs},
|
||||
site::{Site, SiteId, Sites},
|
||||
faction::{Faction, FactionId, Factions},
|
||||
nature::Nature,
|
||||
npc::{Npc, NpcId, Npcs},
|
||||
site::{Site, SiteId, Sites},
|
||||
};
|
||||
|
||||
use common::resources::TimeOfDay;
|
||||
use enum_map::{EnumMap, EnumArray, enum_map};
|
||||
use serde::{Serialize, Deserialize, ser, de};
|
||||
use enum_map::{enum_map, EnumArray, EnumMap};
|
||||
use serde::{
|
||||
de::{self, Error as _},
|
||||
ser, Deserialize, Serialize,
|
||||
};
|
||||
use std::{
|
||||
io::{Read, Write},
|
||||
marker::PhantomData,
|
||||
cmp::PartialEq,
|
||||
fmt,
|
||||
io::{Read, Write},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Serialize, Deserialize)]
|
||||
@ -54,11 +57,15 @@ fn rugged_ser_enum_map<
|
||||
V: From<i16> + PartialEq + Serialize,
|
||||
S: ser::Serializer,
|
||||
const DEFAULT: i16,
|
||||
>(map: &EnumMap<K, V>, ser: S) -> Result<S::Ok, S::Error> {
|
||||
ser.collect_map(map
|
||||
.iter()
|
||||
.filter(|(k, v)| v != &&V::from(DEFAULT))
|
||||
.map(|(k, v)| (k, v)))
|
||||
>(
|
||||
map: &EnumMap<K, V>,
|
||||
ser: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
ser.collect_map(
|
||||
map.iter()
|
||||
.filter(|(k, v)| v != &&V::from(DEFAULT))
|
||||
.map(|(k, v)| (k, v)),
|
||||
)
|
||||
}
|
||||
|
||||
fn rugged_de_enum_map<
|
||||
@ -67,7 +74,9 @@ fn rugged_de_enum_map<
|
||||
V: From<i16> + Deserialize<'a>,
|
||||
D: de::Deserializer<'a>,
|
||||
const DEFAULT: i16,
|
||||
>(mut de: D) -> Result<EnumMap<K, V>, D::Error> {
|
||||
>(
|
||||
mut de: D,
|
||||
) -> Result<EnumMap<K, V>, D::Error> {
|
||||
struct Visitor<K, V, const DEFAULT: i16>(PhantomData<(K, V)>);
|
||||
|
||||
impl<'de, K, V, const DEFAULT: i16> de::Visitor<'de> for Visitor<K, V, DEFAULT>
|
||||
|
@ -1,17 +1,15 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use common::{grid::Grid, rtsim::ChunkResource};
|
||||
use enum_map::EnumMap;
|
||||
use common::{
|
||||
grid::Grid,
|
||||
rtsim::ChunkResource,
|
||||
};
|
||||
use world::World;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vek::*;
|
||||
use world::World;
|
||||
|
||||
/// Represents the state of 'natural' elements of the world such as plant/animal/resource populations, weather systems,
|
||||
/// etc.
|
||||
/// Represents the state of 'natural' elements of the world such as
|
||||
/// plant/animal/resource populations, weather systems, etc.
|
||||
///
|
||||
/// Where possible, this data does not define the state of natural aspects of the world, but instead defines
|
||||
/// 'modifications' that sit on top of the world data generated by initial generation.
|
||||
/// Where possible, this data does not define the state of natural aspects of
|
||||
/// the world, but instead defines 'modifications' that sit on top of the world
|
||||
/// data generated by initial generation.
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Nature {
|
||||
chunks: Grid<Chunk>,
|
||||
@ -20,22 +18,17 @@ pub struct Nature {
|
||||
impl Nature {
|
||||
pub fn generate(world: &World) -> Self {
|
||||
Self {
|
||||
chunks: Grid::populate_from(
|
||||
world.sim().get_size().map(|e| e as i32),
|
||||
|pos| Chunk {
|
||||
res: EnumMap::<_, f32>::default().map(|_, _| 1.0),
|
||||
},
|
||||
),
|
||||
chunks: Grid::populate_from(world.sim().get_size().map(|e| e as i32), |pos| Chunk {
|
||||
res: EnumMap::<_, f32>::default().map(|_, _| 1.0),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Clean up this API a bit
|
||||
pub fn get_chunk_resources(&self, key: Vec2<i32>) -> EnumMap<ChunkResource, f32> {
|
||||
self.chunks
|
||||
.get(key)
|
||||
.map(|c| c.res)
|
||||
.unwrap_or_default()
|
||||
self.chunks.get(key).map(|c| c.res).unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn set_chunk_resources(&mut self, key: Vec2<i32>, res: EnumMap<ChunkResource, f32>) {
|
||||
if let Some(chunk) = self.chunks.get_mut(key) {
|
||||
chunk.res = res;
|
||||
@ -45,16 +38,21 @@ impl Nature {
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Chunk {
|
||||
/// Represent the 'naturally occurring' resource proportion that exists in this chunk.
|
||||
/// Represent the 'naturally occurring' resource proportion that exists in
|
||||
/// this chunk.
|
||||
///
|
||||
/// 0.0 => None of the resources generated by terrain generation should be present
|
||||
/// 1.0 => All of the resources generated by terrain generation should be present
|
||||
/// 0.0 => None of the resources generated by terrain generation should be
|
||||
/// present 1.0 => All of the resources generated by terrain generation
|
||||
/// should be present
|
||||
///
|
||||
/// It's important to understand this this number does not represent the total amount of a resource present in a
|
||||
/// chunk, nor is it even proportional to the amount of the resource present. To get the total amount of the
|
||||
/// resource in a chunk, one must first multiply this factor by the amount of 'natural' resources given by terrain
|
||||
/// generation. This value represents only the variable 'depletion' factor of that resource, which shall change
|
||||
/// over time as the world evolves and players interact with it.
|
||||
/// It's important to understand this this number does not represent the
|
||||
/// total amount of a resource present in a chunk, nor is it even
|
||||
/// proportional to the amount of the resource present. To get the total
|
||||
/// amount of the resource in a chunk, one must first multiply this
|
||||
/// factor by the amount of 'natural' resources given by terrain
|
||||
/// generation. This value represents only the variable 'depletion' factor
|
||||
/// of that resource, which shall change over time as the world evolves
|
||||
/// and players interact with it.
|
||||
#[serde(rename = "r")]
|
||||
#[serde(serialize_with = "crate::data::rugged_ser_enum_map::<_, _, _, 1>")]
|
||||
#[serde(deserialize_with = "crate::data::rugged_de_enum_map::<_, _, _, 1>")]
|
||||
|
@ -1,18 +1,20 @@
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use slotmap::HopSlotMap;
|
||||
use vek::*;
|
||||
use rand::prelude::*;
|
||||
use std::{ops::{Deref, DerefMut}, collections::VecDeque};
|
||||
use common::{
|
||||
uid::Uid,
|
||||
store::Id,
|
||||
rtsim::{SiteId, FactionId, RtSimController},
|
||||
comp,
|
||||
};
|
||||
use world::{util::RandomPerm, civ::Track};
|
||||
use world::site::Site as WorldSite;
|
||||
pub use common::rtsim::{NpcId, Profession};
|
||||
use common::{
|
||||
comp,
|
||||
rtsim::{FactionId, RtSimController, SiteId},
|
||||
store::Id,
|
||||
uid::Uid,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use rand::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use slotmap::HopSlotMap;
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
use vek::*;
|
||||
use world::{civ::Track, site::Site as WorldSite, util::RandomPerm};
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub enum NpcMode {
|
||||
@ -39,7 +41,6 @@ pub struct PathingMemory {
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Npc {
|
||||
// Persisted state
|
||||
|
||||
/// Represents the location of the NPC.
|
||||
pub seed: u32,
|
||||
pub wpos: Vec3<f32>,
|
||||
@ -58,17 +59,18 @@ pub struct Npc {
|
||||
/// (wpos, speed_factor)
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub goto: 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.
|
||||
|
||||
/// 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;
|
||||
const PERM_SPECIES: u32 = 0;
|
||||
|
||||
pub fn new(seed: u32, wpos: Vec3<f32>) -> Self {
|
||||
Self {
|
||||
@ -115,13 +117,12 @@ pub struct Npcs {
|
||||
}
|
||||
|
||||
impl Npcs {
|
||||
pub fn create(&mut self, npc: Npc) -> NpcId {
|
||||
self.npcs.insert(npc)
|
||||
}
|
||||
pub fn create(&mut self, npc: Npc) -> NpcId { self.npcs.insert(npc) }
|
||||
}
|
||||
|
||||
impl Deref for Npcs {
|
||||
type Target = HopSlotMap<NpcId, Npc>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.npcs }
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,10 @@
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use slotmap::HopSlotMap;
|
||||
use vek::*;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use common::{
|
||||
uid::Uid,
|
||||
store::Id,
|
||||
rtsim::FactionId,
|
||||
};
|
||||
pub use common::rtsim::SiteId;
|
||||
use common::{rtsim::FactionId, store::Id, uid::Uid};
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use slotmap::HopSlotMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use vek::*;
|
||||
use world::site::Site as WorldSite;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
@ -16,14 +12,19 @@ pub struct Site {
|
||||
pub wpos: Vec2<i32>,
|
||||
pub faction: Option<FactionId>,
|
||||
|
||||
/// The site generated during initial worldgen that this site corresponds to.
|
||||
/// 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.
|
||||
/// 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).
|
||||
/// 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>>,
|
||||
}
|
||||
@ -38,7 +39,7 @@ impl Site {
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Sites {
|
||||
pub sites: HopSlotMap<SiteId, Site>,
|
||||
|
||||
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub world_site_map: HashMap<Id<WorldSite>, SiteId>,
|
||||
}
|
||||
@ -56,6 +57,7 @@ impl Sites {
|
||||
|
||||
impl Deref for Sites {
|
||||
type Target = HopSlotMap<SiteId, Site>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.sites }
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::{RtState, Rule};
|
||||
use common::resources::{Time, TimeOfDay};
|
||||
use world::{World, IndexRef};
|
||||
use super::{Rule, RtState};
|
||||
use world::{IndexRef, World};
|
||||
|
||||
pub trait Event: Clone + 'static {}
|
||||
|
||||
|
@ -1,10 +1,7 @@
|
||||
use crate::data::Faction;
|
||||
use vek::*;
|
||||
use rand::prelude::*;
|
||||
use world::{
|
||||
World,
|
||||
IndexRef,
|
||||
};
|
||||
use vek::*;
|
||||
use world::{IndexRef, World};
|
||||
|
||||
impl Faction {
|
||||
pub fn generate(world: &World, index: IndexRef, rng: &mut impl Rng) -> Self {
|
||||
|
@ -1,40 +1,41 @@
|
||||
pub mod site;
|
||||
pub mod faction;
|
||||
pub mod site;
|
||||
|
||||
use crate::data::{
|
||||
npc::{Npcs, Npc, Profession},
|
||||
site::{Sites, Site},
|
||||
faction::{Factions, Faction},
|
||||
Data,
|
||||
Nature,
|
||||
faction::{Faction, Factions},
|
||||
npc::{Npc, Npcs, Profession},
|
||||
site::{Site, Sites},
|
||||
Data, Nature,
|
||||
};
|
||||
use common::{
|
||||
resources::TimeOfDay, rtsim::WorldSettings, terrain::TerrainChunkSize, vol::RectVolSize,
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use rand::prelude::*;
|
||||
use tracing::info;
|
||||
use vek::*;
|
||||
use common::{
|
||||
rtsim::WorldSettings,
|
||||
resources::TimeOfDay,
|
||||
terrain::TerrainChunkSize,
|
||||
vol::RectVolSize,
|
||||
};
|
||||
use world::{
|
||||
site::SiteKind,
|
||||
IndexRef,
|
||||
World,
|
||||
};
|
||||
use world::{site::SiteKind, IndexRef, World};
|
||||
|
||||
impl Data {
|
||||
pub fn generate(settings: &WorldSettings, 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);
|
||||
seed.iter_mut()
|
||||
.zip(&mut index.seed.to_le_bytes())
|
||||
.for_each(|(dst, src)| *dst = *src);
|
||||
let mut rng = SmallRng::from_seed(seed);
|
||||
|
||||
let mut this = Self {
|
||||
nature: Nature::generate(world),
|
||||
npcs: Npcs { npcs: Default::default() },
|
||||
sites: Sites { sites: Default::default(), world_site_map: Default::default() },
|
||||
factions: Factions { factions: Default::default() },
|
||||
npcs: Npcs {
|
||||
npcs: Default::default(),
|
||||
},
|
||||
sites: Sites {
|
||||
sites: Default::default(),
|
||||
world_site_map: Default::default(),
|
||||
},
|
||||
factions: Factions {
|
||||
factions: Default::default(),
|
||||
},
|
||||
|
||||
time_of_day: TimeOfDay(settings.start_time),
|
||||
};
|
||||
@ -45,44 +46,53 @@ impl Data {
|
||||
let wpos = world
|
||||
.sim()
|
||||
.get_size()
|
||||
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| rng.gen_range(0..(e * sz) as i32));
|
||||
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| {
|
||||
rng.gen_range(0..(e * sz) as i32)
|
||||
});
|
||||
(wpos, this.factions.create(faction))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
info!("Generated {} rtsim factions.", this.factions.len());
|
||||
|
||||
// Register sites with rtsim
|
||||
for (world_site_id, _) in index
|
||||
.sites
|
||||
.iter()
|
||||
{
|
||||
for (world_site_id, _) in index.sites.iter() {
|
||||
let site = Site::generate(world_site_id, world, index, &initial_factions);
|
||||
this.sites.create(site);
|
||||
}
|
||||
info!("Registering {} rtsim sites from world sites.", this.sites.len());
|
||||
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 rand_wpos = |rng: &mut SmallRng| {
|
||||
let wpos2d = site.wpos.map(|e| e + rng.gen_range(-10..10));
|
||||
wpos2d.map(|e| e as f32 + 0.5)
|
||||
wpos2d
|
||||
.map(|e| e as f32 + 0.5)
|
||||
.with_z(world.sim().get_alt_approx(wpos2d).unwrap_or(0.0))
|
||||
};
|
||||
for _ in 0..20 {
|
||||
|
||||
this.npcs.create(Npc::new(rng.gen(), rand_wpos(&mut rng))
|
||||
.with_faction(site.faction)
|
||||
.with_home(site_id).with_profession(match rng.gen_range(0..20) {
|
||||
0 => Profession::Hunter,
|
||||
1 => Profession::Blacksmith,
|
||||
2 => Profession::Chef,
|
||||
3 => Profession::Alchemist,
|
||||
5..=10 => Profession::Farmer,
|
||||
11..=15 => Profession::Guard,
|
||||
_ => Profession::Adventurer(rng.gen_range(0..=3)),
|
||||
}));
|
||||
this.npcs.create(
|
||||
Npc::new(rng.gen(), rand_wpos(&mut rng))
|
||||
.with_faction(site.faction)
|
||||
.with_home(site_id)
|
||||
.with_profession(match rng.gen_range(0..20) {
|
||||
0 => Profession::Hunter,
|
||||
1 => Profession::Blacksmith,
|
||||
2 => Profession::Chef,
|
||||
3 => Profession::Alchemist,
|
||||
5..=10 => Profession::Farmer,
|
||||
11..=15 => Profession::Guard,
|
||||
_ => Profession::Adventurer(rng.gen_range(0..=3)),
|
||||
}),
|
||||
);
|
||||
}
|
||||
this.npcs.create(Npc::new(rng.gen(), rand_wpos(&mut rng)).with_home(site_id).with_profession(Profession::Merchant));
|
||||
this.npcs.create(
|
||||
Npc::new(rng.gen(), rand_wpos(&mut rng))
|
||||
.with_home(site_id)
|
||||
.with_profession(Profession::Merchant),
|
||||
);
|
||||
}
|
||||
info!("Generated {} rtsim NPCs.", this.npcs.len());
|
||||
|
||||
|
@ -1,14 +1,15 @@
|
||||
use crate::data::{Site, FactionId};
|
||||
use crate::data::{FactionId, Site};
|
||||
use common::store::Id;
|
||||
use vek::*;
|
||||
use world::{
|
||||
site::Site as WorldSite,
|
||||
World,
|
||||
IndexRef,
|
||||
};
|
||||
use world::{site::Site as WorldSite, IndexRef, World};
|
||||
|
||||
impl Site {
|
||||
pub fn generate(world_site: Id<WorldSite>, world: &World, index: IndexRef, nearby_factions: &[(Vec2<i32>, FactionId)]) -> Self {
|
||||
pub fn generate(
|
||||
world_site: Id<WorldSite>,
|
||||
world: &World,
|
||||
index: IndexRef,
|
||||
nearby_factions: &[(Vec2<i32>, FactionId)],
|
||||
) -> Self {
|
||||
let wpos = index.sites.get(world_site).get_origin();
|
||||
|
||||
Self {
|
||||
|
@ -10,15 +10,15 @@ pub use self::{
|
||||
event::{Event, EventCtx, OnTick},
|
||||
rule::{Rule, RuleError},
|
||||
};
|
||||
use common::resources::{Time, TimeOfDay};
|
||||
use world::{World, IndexRef};
|
||||
use anymap2::SendSyncAnyMap;
|
||||
use tracing::{info, error};
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
use common::resources::{Time, TimeOfDay};
|
||||
use std::{
|
||||
any::type_name,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
use tracing::{error, info};
|
||||
use world::{IndexRef, World};
|
||||
|
||||
pub struct RtState {
|
||||
resources: SendSyncAnyMap,
|
||||
@ -36,7 +36,7 @@ impl RtState {
|
||||
rules: SendSyncAnyMap::new(),
|
||||
event_handlers: SendSyncAnyMap::new(),
|
||||
}
|
||||
.with_resource(data);
|
||||
.with_resource(data);
|
||||
|
||||
this.start_default_rules();
|
||||
|
||||
@ -58,7 +58,9 @@ impl RtState {
|
||||
pub fn start_rule<R: Rule>(&mut self) {
|
||||
info!("Initiating '{}' rule...", type_name::<R>());
|
||||
match R::start(self) {
|
||||
Ok(rule) => { self.rules.insert::<RuleState<R>>(AtomicRefCell::new(rule)); },
|
||||
Ok(rule) => {
|
||||
self.rules.insert::<RuleState<R>>(AtomicRefCell::new(rule));
|
||||
},
|
||||
Err(e) => error!("Error when initiating '{}' rule: {}", type_name::<R>(), e),
|
||||
}
|
||||
}
|
||||
@ -66,11 +68,19 @@ impl RtState {
|
||||
fn rule_mut<R: Rule>(&self) -> impl DerefMut<Target = R> + '_ {
|
||||
self.rules
|
||||
.get::<RuleState<R>>()
|
||||
.unwrap_or_else(|| panic!("Tried to access rule '{}' but it does not exist", type_name::<R>()))
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"Tried to access rule '{}' but it does not exist",
|
||||
type_name::<R>()
|
||||
)
|
||||
})
|
||||
.borrow_mut()
|
||||
}
|
||||
|
||||
pub fn bind<R: Rule, E: Event>(&mut self, mut f: impl FnMut(EventCtx<R, 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>>()
|
||||
@ -87,33 +97,53 @@ impl RtState {
|
||||
}
|
||||
|
||||
pub fn data(&self) -> impl Deref<Target = Data> + '_ { self.resource() }
|
||||
|
||||
pub fn data_mut(&self) -> impl DerefMut<Target = Data> + '_ { self.resource_mut() }
|
||||
|
||||
pub fn resource<R: Send + Sync + 'static>(&self) -> impl Deref<Target = R> + '_ {
|
||||
self.resources
|
||||
.get::<AtomicRefCell<R>>()
|
||||
.unwrap_or_else(|| panic!("Tried to access resource '{}' but it does not exist", type_name::<R>()))
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"Tried to access resource '{}' but it does not exist",
|
||||
type_name::<R>()
|
||||
)
|
||||
})
|
||||
.borrow()
|
||||
}
|
||||
|
||||
pub fn resource_mut<R: Send + Sync + 'static>(&self) -> impl DerefMut<Target = R> + '_ {
|
||||
self.resources
|
||||
.get::<AtomicRefCell<R>>()
|
||||
.unwrap_or_else(|| panic!("Tried to access resource '{}' but it does not exist", type_name::<R>()))
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"Tried to access resource '{}' but it does not exist",
|
||||
type_name::<R>()
|
||||
)
|
||||
})
|
||||
.borrow_mut()
|
||||
}
|
||||
|
||||
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, world, index, &e)));
|
||||
.map(|handlers| handlers.iter().for_each(|f| f(self, world, index, &e)));
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, world: &World, index: IndexRef, time_of_day: TimeOfDay, time: Time, dt: f32) {
|
||||
pub fn tick(
|
||||
&mut self,
|
||||
world: &World,
|
||||
index: IndexRef,
|
||||
time_of_day: TimeOfDay,
|
||||
time: Time,
|
||||
dt: f32,
|
||||
) {
|
||||
self.data_mut().time_of_day = time_of_day;
|
||||
let event = OnTick { time_of_day, time, dt };
|
||||
let event = OnTick {
|
||||
time_of_day,
|
||||
time,
|
||||
dt,
|
||||
};
|
||||
self.emit(event, world, index);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
pub mod npc_ai;
|
||||
pub mod setup;
|
||||
pub mod simulate_npcs;
|
||||
pub mod npc_ai;
|
||||
|
||||
use std::fmt;
|
||||
use super::RtState;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RuleError {
|
||||
@ -13,7 +13,9 @@ pub enum RuleError {
|
||||
impl fmt::Display for RuleError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::NoSuchRule(r) => write!(f, "tried to fetch rule state '{}' but it does not exist", r),
|
||||
Self::NoSuchRule(r) => {
|
||||
write!(f, "tried to fetch rule state '{}' but it does not exist", r)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,8 @@ use common::{
|
||||
path::Path,
|
||||
rtsim::{Profession, SiteId},
|
||||
store::Id,
|
||||
terrain::TerrainChunkSize, vol::RectVolSize,
|
||||
terrain::TerrainChunkSize,
|
||||
vol::RectVolSize,
|
||||
};
|
||||
use fxhash::FxHasher64;
|
||||
use itertools::Itertools;
|
||||
@ -236,10 +237,12 @@ impl Rule for NpcAi {
|
||||
let data = &mut *ctx.state.data_mut();
|
||||
let mut dynamic_rng = rand::thread_rng();
|
||||
for npc in data.npcs.values_mut() {
|
||||
npc.current_site = ctx.world.sim().get(npc.wpos.xy().as_::<i32>() / TerrainChunkSize::RECT_SIZE.as_()).and_then(|chunk| {
|
||||
data.sites.world_site_map.get(chunk.sites.first()?).copied()
|
||||
});
|
||||
|
||||
npc.current_site = ctx
|
||||
.world
|
||||
.sim()
|
||||
.get(npc.wpos.xy().as_::<i32>() / TerrainChunkSize::RECT_SIZE.as_())
|
||||
.and_then(|chunk| data.sites.world_site_map.get(chunk.sites.first()?).copied());
|
||||
|
||||
if let Some(home_id) = npc.home {
|
||||
if let Some((target, _)) = npc.goto {
|
||||
// Walk to the current target
|
||||
|
@ -1,12 +1,9 @@
|
||||
use crate::{data::Site, event::OnSetup, RtState, Rule, RuleError};
|
||||
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.
|
||||
/// 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 {
|
||||
@ -15,14 +12,20 @@ impl Rule for Setup {
|
||||
let data = &mut *ctx.state.data_mut();
|
||||
// Delete rtsim sites that don't correspond to a world site
|
||||
data.sites.retain(|site_id, site| {
|
||||
if let Some((world_site_id, _)) = ctx.index.sites
|
||||
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);
|
||||
warn!(
|
||||
"{:?} is no longer valid because the site it was derived from no longer \
|
||||
exists. It will now be deleted.",
|
||||
site_id
|
||||
);
|
||||
false
|
||||
}
|
||||
});
|
||||
@ -32,15 +35,20 @@ impl Rule for Setup {
|
||||
npc.home = npc.home.filter(|home| data.sites.contains_key(*home));
|
||||
}
|
||||
|
||||
// Generate rtsim sites for world sites that don't have a corresponding rtsim site yet
|
||||
// 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 !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);
|
||||
data
|
||||
.sites
|
||||
if !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
|
||||
);
|
||||
data.sites
|
||||
.create(Site::generate(world_site_id, ctx.world, ctx.index, &[]));
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,12 @@
|
||||
use crate::{data::npc::NpcMode, event::OnTick, RtState, Rule, RuleError};
|
||||
use common::{terrain::TerrainChunkSize, vol::RectVolSize};
|
||||
use tracing::info;
|
||||
use vek::*;
|
||||
use crate::{
|
||||
data::npc::NpcMode,
|
||||
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| {
|
||||
let data = &mut *ctx.state.data_mut();
|
||||
for npc in data
|
||||
@ -28,14 +23,17 @@ impl Rule for SimulateNpcs {
|
||||
|
||||
if dist2 > 0.5f32.powi(2) {
|
||||
npc.wpos += (diff
|
||||
* (body.max_speed_approx() * speed_factor * ctx.event.dt / dist2.sqrt())
|
||||
.min(1.0))
|
||||
* (body.max_speed_approx() * speed_factor * ctx.event.dt
|
||||
/ dist2.sqrt())
|
||||
.min(1.0))
|
||||
.with_z(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure NPCs remain on the surface
|
||||
npc.wpos.z = ctx.world.sim()
|
||||
npc.wpos.z = ctx
|
||||
.world
|
||||
.sim()
|
||||
.get_alt_approx(npc.wpos.xy().map(|e| e as i32))
|
||||
.unwrap_or(0.0);
|
||||
}
|
||||
|
@ -1,9 +1,6 @@
|
||||
use crate::{
|
||||
metrics::ChunkGenMetrics,
|
||||
rtsim2::RtSim,
|
||||
};
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
use crate::test_world::{IndexOwned, World};
|
||||
use crate::{metrics::ChunkGenMetrics, rtsim2::RtSim};
|
||||
use common::{
|
||||
calendar::Calendar, generation::ChunkSupplement, resources::TimeOfDay, slowjob::SlowJobPool,
|
||||
terrain::TerrainChunk,
|
||||
@ -47,10 +44,8 @@ impl ChunkGenerator {
|
||||
key: Vec2<i32>,
|
||||
slowjob_pool: &SlowJobPool,
|
||||
world: Arc<World>,
|
||||
#[cfg(feature = "worldgen")]
|
||||
rtsim: &RtSim,
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
rtsim: &(),
|
||||
#[cfg(feature = "worldgen")] rtsim: &RtSim,
|
||||
#[cfg(not(feature = "worldgen"))] rtsim: &(),
|
||||
index: IndexOwned,
|
||||
time: (TimeOfDay, Calendar),
|
||||
) {
|
||||
|
@ -1192,9 +1192,19 @@ fn handle_tp_npc(
|
||||
) -> CmdResult<()> {
|
||||
use crate::rtsim2::RtSim;
|
||||
let pos = if let Some(id) = parse_cmd_args!(args, u32) {
|
||||
// TODO: Take some other identifier than an integer to this command.
|
||||
server.state.ecs().read_resource::<RtSim>().state().data().npcs.values().nth(id as usize).ok_or(action.help_string())?.wpos
|
||||
} else {
|
||||
// TODO: Take some other identifier than an integer to this command.
|
||||
server
|
||||
.state
|
||||
.ecs()
|
||||
.read_resource::<RtSim>()
|
||||
.state()
|
||||
.data()
|
||||
.npcs
|
||||
.values()
|
||||
.nth(id as usize)
|
||||
.ok_or(action.help_string())?
|
||||
.wpos
|
||||
} else {
|
||||
return Err(action.help_string());
|
||||
};
|
||||
position_mut(server, target, "target", |target_pos| {
|
||||
@ -3873,12 +3883,7 @@ fn handle_scale(
|
||||
.write_storage::<comp::Scale>()
|
||||
.insert(target, comp::Scale(scale));
|
||||
if reset_mass.unwrap_or(true) {
|
||||
if let Some(body) = server
|
||||
.state
|
||||
.ecs()
|
||||
.read_storage::<comp::Body>()
|
||||
.get(target)
|
||||
{
|
||||
if let Some(body) = server.state.ecs().read_storage::<comp::Body>().get(target) {
|
||||
let _ = server
|
||||
.state
|
||||
.ecs()
|
||||
|
@ -7,9 +7,8 @@ use crate::{
|
||||
skillset::SkillGroupKind,
|
||||
BuffKind, BuffSource, PhysicsState,
|
||||
},
|
||||
// rtsim::RtSim,
|
||||
sys::terrain::SAFE_ZONE_RADIUS,
|
||||
rtsim2,
|
||||
sys::terrain::SAFE_ZONE_RADIUS,
|
||||
Server, SpawnPoint, StateExt,
|
||||
};
|
||||
use authc::Uuid;
|
||||
|
@ -83,7 +83,7 @@ use common::{
|
||||
rtsim::RtSimEntity,
|
||||
shared_server_config::ServerConstants,
|
||||
slowjob::SlowJobPool,
|
||||
terrain::{TerrainChunk, TerrainChunkSize, Block},
|
||||
terrain::{Block, TerrainChunk, TerrainChunkSize},
|
||||
vol::RectRasterableVol,
|
||||
};
|
||||
use common_ecs::run_now;
|
||||
@ -565,7 +565,12 @@ impl Server {
|
||||
// Init rtsim, loading it from disk if possible
|
||||
#[cfg(feature = "worldgen")]
|
||||
{
|
||||
match rtsim2::RtSim::new(&settings.world, index.as_index_ref(), &world, data_dir.to_owned()) {
|
||||
match rtsim2::RtSim::new(
|
||||
&settings.world,
|
||||
index.as_index_ref(),
|
||||
&world,
|
||||
data_dir.to_owned(),
|
||||
) {
|
||||
Ok(rtsim) => {
|
||||
state.ecs_mut().insert(rtsim.state().data().time_of_day);
|
||||
state.ecs_mut().insert(rtsim);
|
||||
@ -706,9 +711,15 @@ impl Server {
|
||||
|
||||
let before_state_tick = Instant::now();
|
||||
|
||||
fn on_block_update(ecs: &specs::World, wpos: Vec3<i32>, old_block: Block, new_block: Block) {
|
||||
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() {
|
||||
if old_block.get_rtsim_resource().is_some() || new_block.get_rtsim_resource().is_some()
|
||||
{
|
||||
ecs.write_resource::<rtsim2::RtSim>().hook_block_update(
|
||||
&ecs.read_resource::<Arc<world::World>>(),
|
||||
ecs.read_resource::<world::IndexOwned>().as_index_ref(),
|
||||
|
@ -1,5 +1,5 @@
|
||||
use rtsim2::Event;
|
||||
use common::terrain::Block;
|
||||
use rtsim2::Event;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -4,33 +4,29 @@ pub mod tick;
|
||||
|
||||
use common::{
|
||||
grid::Grid,
|
||||
slowjob::SlowJobPool,
|
||||
rtsim::{ChunkResource, RtSimEntity, WorldSettings},
|
||||
terrain::{TerrainChunk, Block},
|
||||
slowjob::SlowJobPool,
|
||||
terrain::{Block, TerrainChunk},
|
||||
vol::RectRasterableVol,
|
||||
};
|
||||
use common_ecs::{dispatch, System};
|
||||
use enum_map::EnumMap;
|
||||
use rtsim2::{
|
||||
data::{
|
||||
npc::NpcMode,
|
||||
Data,
|
||||
ReadError,
|
||||
},
|
||||
rule::Rule,
|
||||
data::{npc::NpcMode, Data, ReadError},
|
||||
event::OnSetup,
|
||||
rule::Rule,
|
||||
RtState,
|
||||
};
|
||||
use specs::{DispatcherBuilder, WorldExt};
|
||||
use std::{
|
||||
error::Error,
|
||||
fs::{self, File},
|
||||
io::{self, Write},
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
time::Instant,
|
||||
io::{self, Write},
|
||||
error::Error,
|
||||
};
|
||||
use enum_map::EnumMap;
|
||||
use tracing::{error, warn, info, debug};
|
||||
use tracing::{debug, error, info, warn};
|
||||
use vek::*;
|
||||
use world::{IndexRef, World};
|
||||
|
||||
@ -41,7 +37,12 @@ pub struct RtSim {
|
||||
}
|
||||
|
||||
impl RtSim {
|
||||
pub fn new(settings: &WorldSettings, index: IndexRef, world: &World, data_dir: PathBuf) -> Result<Self, ron::Error> {
|
||||
pub fn new(
|
||||
settings: &WorldSettings,
|
||||
index: IndexRef,
|
||||
world: &World,
|
||||
data_dir: PathBuf,
|
||||
) -> Result<Self, ron::Error> {
|
||||
let file_path = Self::get_file_path(data_dir);
|
||||
|
||||
info!("Looking for rtsim data at {}...", file_path.display());
|
||||
@ -51,7 +52,10 @@ impl RtSim {
|
||||
Ok(file) => {
|
||||
info!("Rtsim data found. Attempting to load...");
|
||||
match Data::from_reader(io::BufReader::new(file)) {
|
||||
Ok(data) => { info!("Rtsim data loaded."); break 'load data },
|
||||
Ok(data) => {
|
||||
info!("Rtsim data loaded.");
|
||||
break 'load data;
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Rtsim data failed to load: {}", e);
|
||||
let mut i = 0;
|
||||
@ -64,7 +68,10 @@ impl RtSim {
|
||||
});
|
||||
if !backup_path.exists() {
|
||||
fs::rename(&file_path, &backup_path)?;
|
||||
warn!("Failed rtsim data was moved to {}", backup_path.display());
|
||||
warn!(
|
||||
"Failed rtsim data was moved to {}",
|
||||
backup_path.display()
|
||||
);
|
||||
info!("A fresh rtsim data will now be generated.");
|
||||
break;
|
||||
}
|
||||
@ -73,12 +80,16 @@ impl RtSim {
|
||||
},
|
||||
}
|
||||
},
|
||||
Err(e) if e.kind() == io::ErrorKind::NotFound =>
|
||||
info!("No rtsim data found. Generating from world..."),
|
||||
Err(e) if e.kind() == io::ErrorKind::NotFound => {
|
||||
info!("No rtsim data found. Generating from world...")
|
||||
},
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
} else {
|
||||
warn!("'RTSIM_NOLOAD' is set, skipping loading of rtsim state (old state will be overwritten).");
|
||||
warn!(
|
||||
"'RTSIM_NOLOAD' is set, skipping loading of rtsim state (old state will be \
|
||||
overwritten)."
|
||||
);
|
||||
}
|
||||
|
||||
let data = Data::generate(settings, &world, index);
|
||||
@ -88,8 +99,10 @@ impl RtSim {
|
||||
|
||||
let mut this = Self {
|
||||
last_saved: None,
|
||||
state: RtState::new(data)
|
||||
.with_resource(ChunkStates(Grid::populate_from(world.sim().get_size().as_(), |_| None))),
|
||||
state: RtState::new(data).with_resource(ChunkStates(Grid::populate_from(
|
||||
world.sim().get_size().as_(),
|
||||
|_| None,
|
||||
))),
|
||||
file_path,
|
||||
};
|
||||
|
||||
@ -123,8 +136,16 @@ impl RtSim {
|
||||
}
|
||||
}
|
||||
|
||||
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_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) {
|
||||
@ -155,10 +176,8 @@ impl RtSim {
|
||||
Ok(dir.join(tmp_file_name))
|
||||
})
|
||||
.unwrap_or_else(|| Ok(tmp_file_name.into()))
|
||||
.and_then(|tmp_file_path| {
|
||||
Ok((File::create(&tmp_file_path)?, tmp_file_path))
|
||||
})
|
||||
.map_err(|e: io::Error| Box::new(e) as Box::<dyn Error>)
|
||||
.and_then(|tmp_file_path| Ok((File::create(&tmp_file_path)?, tmp_file_path)))
|
||||
.map_err(|e: io::Error| Box::new(e) as Box<dyn Error>)
|
||||
.and_then(|(mut file, tmp_file_path)| {
|
||||
debug!("Writing rtsim data to file...");
|
||||
data.write_to(io::BufWriter::new(&mut file))?;
|
||||
@ -180,9 +199,7 @@ impl RtSim {
|
||||
self.state.data().nature.get_chunk_resources(key)
|
||||
}
|
||||
|
||||
pub fn state(&self) -> &RtState {
|
||||
&self.state
|
||||
}
|
||||
pub fn state(&self) -> &RtState { &self.state }
|
||||
}
|
||||
|
||||
struct ChunkStates(pub Grid<Option<LoadedChunkState>>);
|
||||
|
@ -1,7 +1,7 @@
|
||||
pub mod deplete_resources;
|
||||
|
||||
use tracing::info;
|
||||
use rtsim2::RtState;
|
||||
use tracing::info;
|
||||
|
||||
pub fn start_rules(rtstate: &mut RtState) {
|
||||
info!("Starting server rtsim rules...");
|
||||
|
@ -1,13 +1,7 @@
|
||||
use tracing::info;
|
||||
use crate::rtsim2::{event::OnBlockChange, ChunkStates};
|
||||
use common::{terrain::TerrainChunk, vol::RectRasterableVol};
|
||||
use rtsim2::{RtState, Rule, RuleError};
|
||||
use crate::rtsim2::{
|
||||
event::OnBlockChange,
|
||||
ChunkStates,
|
||||
};
|
||||
use common::{
|
||||
terrain::TerrainChunk,
|
||||
vol::RectRasterableVol,
|
||||
};
|
||||
use tracing::info;
|
||||
|
||||
pub struct DepleteResources;
|
||||
|
||||
@ -16,7 +10,9 @@ impl Rule for DepleteResources {
|
||||
info!("Hello from the resource depletion rule!");
|
||||
|
||||
rtstate.bind::<Self, OnBlockChange>(|ctx| {
|
||||
let key = ctx.event.wpos
|
||||
let key = ctx
|
||||
.event
|
||||
.wpos
|
||||
.xy()
|
||||
.map2(TerrainChunk::RECT_SIZE, |e, sz| e.div_euclid(sz as i32));
|
||||
if let Some(Some(chunk_state)) = ctx.state.resource_mut::<ChunkStates>().0.get(key) {
|
||||
@ -26,7 +22,8 @@ impl Rule for DepleteResources {
|
||||
if chunk_state.max_res[res] > 0 {
|
||||
chunk_res[res] = (chunk_res[res] * chunk_state.max_res[res] as f32 - 1.0)
|
||||
.round()
|
||||
.max(0.0) / chunk_state.max_res[res] as f32;
|
||||
.max(0.0)
|
||||
/ chunk_state.max_res[res] as f32;
|
||||
}
|
||||
}
|
||||
// Add resources
|
||||
@ -34,11 +31,15 @@ impl Rule for DepleteResources {
|
||||
if chunk_state.max_res[res] > 0 {
|
||||
chunk_res[res] = (chunk_res[res] * chunk_state.max_res[res] as f32 + 1.0)
|
||||
.round()
|
||||
.max(0.0) / chunk_state.max_res[res] as f32;
|
||||
.max(0.0)
|
||||
/ chunk_state.max_res[res] as f32;
|
||||
}
|
||||
}
|
||||
println!("Chunk resources = {:?}", chunk_res);
|
||||
ctx.state.data_mut().nature.set_chunk_resources(key, chunk_res);
|
||||
ctx.state
|
||||
.data_mut()
|
||||
.nature
|
||||
.set_chunk_resources(key, chunk_res);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -100,7 +100,11 @@ fn profession_extra_loadout(
|
||||
fn profession_agent_mark(profession: Option<&Profession>) -> Option<comp::agent::Mark> {
|
||||
match profession {
|
||||
Some(
|
||||
Profession::Merchant | Profession::Farmer | Profession::Chef | Profession::Blacksmith | Profession::Alchemist,
|
||||
Profession::Merchant
|
||||
| Profession::Farmer
|
||||
| Profession::Chef
|
||||
| Profession::Blacksmith
|
||||
| Profession::Alchemist,
|
||||
) => Some(comp::agent::Mark::Merchant),
|
||||
Some(Profession::Guard) => Some(comp::agent::Mark::Guard),
|
||||
_ => None,
|
||||
|
@ -5,9 +5,9 @@ use crate::{
|
||||
persistence::PersistedComponents,
|
||||
pet::restore_pet,
|
||||
presence::{Presence, RepositionOnChunkLoad},
|
||||
rtsim2::RtSim,
|
||||
settings::Settings,
|
||||
sys::sentinel::DeletedEntities,
|
||||
rtsim2::RtSim,
|
||||
wiring, BattleModeBuffer, SpawnPoint,
|
||||
};
|
||||
use common::{
|
||||
|
@ -1,12 +1,9 @@
|
||||
pub mod behavior_tree;
|
||||
pub use server_agent::{action_nodes, attack, consts, data, util};
|
||||
|
||||
use crate::{
|
||||
// rtsim::{entity::PersonalityTrait, RtSim},
|
||||
sys::agent::{
|
||||
use crate::sys::agent::{
|
||||
behavior_tree::{BehaviorData, BehaviorTree},
|
||||
data::{AgentData, ReadData},
|
||||
},
|
||||
};
|
||||
use common::{
|
||||
comp::{
|
||||
|
@ -9,8 +9,9 @@ use common::{
|
||||
mounting::Mount,
|
||||
path::TraversalConfig,
|
||||
resources::{DeltaTime, Time, TimeOfDay},
|
||||
rtsim::RtSimEntity,
|
||||
terrain::TerrainGrid,
|
||||
uid::{Uid, UidAllocator}, rtsim::RtSimEntity,
|
||||
uid::{Uid, UidAllocator},
|
||||
};
|
||||
use specs::{
|
||||
shred::ResourceId, Entities, Entity as EcsEntity, Read, ReadExpect, ReadStorage, SystemData,
|
||||
@ -154,7 +155,6 @@ pub struct ReadData<'a> {
|
||||
#[cfg(feature = "worldgen")]
|
||||
pub world: ReadExpect<'a, Arc<world::World>>,
|
||||
pub rtsim_entity: 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>,
|
||||
|
@ -149,9 +149,10 @@ impl PlayState for MainMenuState {
|
||||
)
|
||||
.into_owned(),
|
||||
server::Error::RtsimError(e) => localized_strings
|
||||
.get("main.servers.rtsim_error")
|
||||
.to_owned()
|
||||
.replace("{raw_error}", e.to_string().as_str()),
|
||||
.get_msg_ctx("main-servers-rtsim_error", &i18n::fluent_args! {
|
||||
"raw_error" => e.to_string(),
|
||||
})
|
||||
.into_owned(),
|
||||
server::Error::Other(e) => localized_strings
|
||||
.get_msg_ctx("main-servers-other_error", &i18n::fluent_args! {
|
||||
"raw_error" => e,
|
||||
|
@ -293,9 +293,7 @@ impl Debug {
|
||||
id
|
||||
}
|
||||
|
||||
pub fn get_shape(&self, id: DebugShapeId) -> Option<&DebugShape> {
|
||||
self.shapes.get(&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));
|
||||
|
@ -776,7 +776,8 @@ impl FigureMgr {
|
||||
.enumerate()
|
||||
{
|
||||
// Velocity relative to the current ground
|
||||
let rel_vel = anim::vek::Vec3::<f32>::from(vel.0 - physics.ground_vel) / scale.map_or(1.0, |s| s.0);
|
||||
let rel_vel = anim::vek::Vec3::<f32>::from(vel.0 - physics.ground_vel)
|
||||
/ scale.map_or(1.0, |s| s.0);
|
||||
|
||||
let look_dir = controller.map(|c| c.inputs.look_dir).unwrap_or_default();
|
||||
let is_viewpoint = scene_data.viewpoint_entity == entity;
|
||||
@ -809,8 +810,9 @@ impl FigureMgr {
|
||||
const MIN_PERFECT_RATE_DIST: f32 = 100.0;
|
||||
|
||||
if (i as u64 + tick)
|
||||
% ((((pos.0.distance_squared(focus_pos) / scale.map_or(1.0, |s| s.0)).powf(0.25) - MIN_PERFECT_RATE_DIST.sqrt())
|
||||
.max(0.0)
|
||||
% ((((pos.0.distance_squared(focus_pos) / scale.map_or(1.0, |s| s.0)).powf(0.25)
|
||||
- MIN_PERFECT_RATE_DIST.sqrt())
|
||||
.max(0.0)
|
||||
/ 3.0) as u64)
|
||||
.saturating_add(1)
|
||||
!= 0
|
||||
@ -6707,8 +6709,10 @@ impl FigureMgr {
|
||||
} {
|
||||
let model_entry = model_entry?;
|
||||
|
||||
let figure_low_detail_distance = figure_lod_render_distance * scale.map_or(1.0, |s| s.0) * 0.75;
|
||||
let figure_mid_detail_distance = figure_lod_render_distance * scale.map_or(1.0, |s| s.0) * 0.5;
|
||||
let figure_low_detail_distance =
|
||||
figure_lod_render_distance * scale.map_or(1.0, |s| s.0) * 0.75;
|
||||
let figure_mid_detail_distance =
|
||||
figure_lod_render_distance * scale.map_or(1.0, |s| s.0) * 0.5;
|
||||
|
||||
let model = if pos.distance_squared(cam_pos) > figure_low_detail_distance.powi(2) {
|
||||
model_entry.lod_model(2)
|
||||
|
@ -409,7 +409,10 @@ impl Scene {
|
||||
// when zooming in the distance the camera travelles should be based on the
|
||||
// final distance. This is to make sure the camera travelles the
|
||||
// same distance when zooming in and out
|
||||
let player_scale = client.state().read_component_copied::<comp::Scale>(client.entity()).map_or(1.0, |s| s.0);
|
||||
let player_scale = client
|
||||
.state()
|
||||
.read_component_copied::<comp::Scale>(client.entity())
|
||||
.map_or(1.0, |s| s.0);
|
||||
if delta < 0.0 {
|
||||
self.camera.zoom_switch(
|
||||
// Thank you Imbris for doing the math
|
||||
@ -418,7 +421,11 @@ impl Scene {
|
||||
player_scale,
|
||||
);
|
||||
} else {
|
||||
self.camera.zoom_switch(delta * (0.05 + self.camera.get_distance() * 0.01), cap, player_scale);
|
||||
self.camera.zoom_switch(
|
||||
delta * (0.05 + self.camera.get_distance() * 0.01),
|
||||
cap,
|
||||
player_scale,
|
||||
);
|
||||
}
|
||||
true
|
||||
},
|
||||
@ -1477,15 +1484,19 @@ impl Scene {
|
||||
|
||||
// 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) {
|
||||
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(shape)
|
||||
});
|
||||
let shape_id = hitboxes
|
||||
.entry(entity)
|
||||
.or_insert_with(|| self.debug.add_shape(shape));
|
||||
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]
|
||||
|
@ -628,12 +628,17 @@ impl Civs {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the direct track between two places, bool if the track should be reversed or not
|
||||
/// Return the direct track between two places, bool if the track should be
|
||||
/// reversed or not
|
||||
pub fn track_between(&self, a: Id<Site>, b: Id<Site>) -> Option<(Id<Track>, bool)> {
|
||||
self.track_map
|
||||
.get(&a)
|
||||
.and_then(|dests| Some((*dests.get(&b)?, false)))
|
||||
.or_else(|| self.track_map.get(&b).and_then(|dests| Some((*dests.get(&a)?, true))))
|
||||
.or_else(|| {
|
||||
self.track_map
|
||||
.get(&b)
|
||||
.and_then(|dests| Some((*dests.get(&a)?, true)))
|
||||
})
|
||||
}
|
||||
|
||||
/// Return an iterator over a site's neighbors
|
||||
@ -663,8 +668,9 @@ impl Civs {
|
||||
.sqrt()
|
||||
};
|
||||
let neighbors = |p: &Id<Site>| self.neighbors(*p);
|
||||
let transition =
|
||||
|a: &Id<Site>, b: &Id<Site>| self.tracks.get(self.track_between(*a, *b).unwrap().0).cost;
|
||||
let transition = |a: &Id<Site>, b: &Id<Site>| {
|
||||
self.tracks.get(self.track_between(*a, *b).unwrap().0).cost
|
||||
};
|
||||
let satisfied = |p: &Id<Site>| *p == b;
|
||||
// We use this hasher (FxHasher64) because
|
||||
// (1) we don't care about DDOS attacks (ruling out SipHash);
|
||||
|
@ -49,11 +49,11 @@ use common::{
|
||||
generation::{ChunkSupplement, EntityInfo},
|
||||
lod,
|
||||
resources::TimeOfDay,
|
||||
rtsim::ChunkResource,
|
||||
terrain::{
|
||||
Block, BlockKind, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize, TerrainGrid,
|
||||
},
|
||||
vol::{ReadVol, RectVolSize, WriteVol},
|
||||
rtsim::ChunkResource,
|
||||
};
|
||||
use common_net::msg::{world_msg, WorldMapMsg};
|
||||
use enum_map::EnumMap;
|
||||
@ -492,17 +492,19 @@ impl World {
|
||||
// Finally, defragment to minimize space consumption.
|
||||
chunk.defragment();
|
||||
|
||||
// Before we finish, we check candidate rtsim resource blocks, deduplicating positions and only keeping those
|
||||
// that actually do have resources. Although this looks potentially very expensive, only blocks that are rtsim
|
||||
// Before we finish, we check candidate rtsim resource blocks, deduplicating
|
||||
// positions and only keeping those that actually do have resources.
|
||||
// Although this looks potentially very expensive, only blocks that are rtsim
|
||||
// resources (i.e: a relatively small number of sprites) are processed here.
|
||||
if let Some(rtsim_resources) = rtsim_resources {
|
||||
rtsim_resource_blocks.sort_unstable_by_key(|pos| pos.into_array());
|
||||
rtsim_resource_blocks.dedup();
|
||||
for wpos in rtsim_resource_blocks {
|
||||
chunk.map(
|
||||
wpos - chunk_wpos2d.with_z(0),
|
||||
|block| if let Some(res) = block.get_rtsim_resource() {
|
||||
// Note: this represents the upper limit, not the actual number spanwed, so we increment this before deciding whether we're going to spawn the resource.
|
||||
chunk.map(wpos - chunk_wpos2d.with_z(0), |block| {
|
||||
if let Some(res) = block.get_rtsim_resource() {
|
||||
// Note: this represents the upper limit, not the actual number spanwed, so
|
||||
// we increment this before deciding whether we're going to spawn the
|
||||
// resource.
|
||||
supplement.rtsim_max_resources[res] += 1;
|
||||
// Throw a dice to determine whether this resource should actually spawn
|
||||
// TODO: Don't throw a dice, try to generate the *exact* correct number
|
||||
@ -513,12 +515,11 @@ impl World {
|
||||
}
|
||||
} else {
|
||||
block
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok((chunk, supplement))
|
||||
}
|
||||
|
||||
|
@ -1012,8 +1012,11 @@ pub fn merchant_loadout(
|
||||
loadout_builder: LoadoutBuilder,
|
||||
economy: Option<&SiteInformation>,
|
||||
) -> LoadoutBuilder {
|
||||
trader_loadout(loadout_builder
|
||||
.with_asset_expect("common.loadout.village.merchant", &mut thread_rng()), economy, |_| true)
|
||||
trader_loadout(
|
||||
loadout_builder.with_asset_expect("common.loadout.village.merchant", &mut thread_rng()),
|
||||
economy,
|
||||
|_| true,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn trader_loadout(
|
||||
@ -1030,10 +1033,13 @@ pub fn trader_loadout(
|
||||
let mut bag4 = Item::new_from_asset_expect("common.items.armor.misc.bag.sturdy_red_backpack");
|
||||
let slots = backpack.slots().len() + 4 * bag1.slots().len();
|
||||
let mut stockmap: HashMap<Good, f32> = economy
|
||||
.map(|e| e.unconsumed_stock.clone()
|
||||
.into_iter()
|
||||
.filter(|(good, _)| permitted(*good))
|
||||
.collect())
|
||||
.map(|e| {
|
||||
e.unconsumed_stock
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter(|(good, _)| permitted(*good))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
// modify stock for better gameplay
|
||||
|
||||
|
@ -7,7 +7,8 @@ use self::tile::{HazardKind, KeepKind, RoofKind, Tile, TileGrid, TILE_SIZE};
|
||||
pub use self::{
|
||||
gen::{aabr_with_z, Fill, Painter, Primitive, PrimitiveRef, Structure},
|
||||
plot::{Plot, PlotKind},
|
||||
util::Dir, tile::TileKind,
|
||||
tile::TileKind,
|
||||
util::Dir,
|
||||
};
|
||||
use crate::{
|
||||
sim::Path,
|
||||
|
Loading…
Reference in New Issue
Block a user