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