diff --git a/common/src/rtsim.rs b/common/src/rtsim.rs index 19990a9d5b..6bfe625fea 100644 --- a/common/src/rtsim.rs +++ b/common/src/rtsim.rs @@ -9,10 +9,10 @@ use vek::*; use crate::comp::dialogue::MoodState; -pub type RtSimId = usize; +slotmap::new_key_type! { pub struct NpcId; } #[derive(Copy, Clone, Debug)] -pub struct RtSimEntity(pub RtSimId); +pub struct RtSimEntity(pub NpcId); impl Component for RtSimEntity { type Storage = specs::VecStorage; diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index e266db3864..2d67ef61be 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -386,7 +386,7 @@ fn basic_move(data: &JoinData<'_>, update: &mut StateUpdate, efficiency: f32) { let accel = if let Some(block) = data.physics.on_ground { // FRIC_GROUND temporarily used to normalize things around expected values data.body.base_accel() - * data.scale.map_or(1.0, |s| s.0) + * data.scale.map_or(1.0, |s| s.0.sqrt()) * block.get_traction() * block.get_friction() / FRIC_GROUND } else { @@ -438,7 +438,7 @@ pub fn handle_forced_movement( data.body.base_accel() * block.get_traction() * block.get_friction() / FRIC_GROUND }) { update.vel.0 += - Vec2::broadcast(data.dt.0) * accel * data.scale.map_or(1.0, |s| s.0) * Vec2::from(*data.ori) * strength; + Vec2::broadcast(data.dt.0) * accel * data.scale.map_or(1.0, |s| s.0.sqrt()) * Vec2::from(*data.ori) * strength; } }, ForcedMovement::Reverse(strength) => { @@ -448,7 +448,7 @@ pub fn handle_forced_movement( data.body.base_accel() * block.get_traction() * block.get_friction() / FRIC_GROUND }) { update.vel.0 += - Vec2::broadcast(data.dt.0) * accel * data.scale.map_or(1.0, |s| s.0) * -Vec2::from(*data.ori) * strength; + Vec2::broadcast(data.dt.0) * accel * data.scale.map_or(1.0, |s| s.0.sqrt()) * -Vec2::from(*data.ori) * strength; } }, ForcedMovement::Sideways(strength) => { @@ -470,7 +470,7 @@ pub fn handle_forced_movement( } }; - update.vel.0 += Vec2::broadcast(data.dt.0) * accel * data.scale.map_or(1.0, |s| s.0) * direction * strength; + update.vel.0 += Vec2::broadcast(data.dt.0) * accel * data.scale.map_or(1.0, |s| s.0.sqrt()) * direction * strength; } }, ForcedMovement::DirectedReverse(strength) => { @@ -519,7 +519,7 @@ pub fn handle_forced_movement( dir.y, vertical, ) - * data.scale.map_or(1.0, |s| s.0) + * data.scale.map_or(1.0, |s| s.0.sqrt()) // Multiply decreasing amount linearly over time (with average of 1) * 2.0 * progress // Apply direction @@ -533,7 +533,7 @@ pub fn handle_forced_movement( }, ForcedMovement::Hover { move_input } => { update.vel.0 = Vec3::new(data.vel.0.x, data.vel.0.y, 0.0) + move_input - * data.scale.map_or(1.0, |s| s.0) + * data.scale.map_or(1.0, |s| s.0.sqrt()) * data.inputs.move_dir.try_normalized().unwrap_or_default(); }, } @@ -575,7 +575,7 @@ pub fn handle_orientation( }; // unit is multiples of 180° let half_turns_per_tick = data.body.base_ori_rate() - / data.scale.map_or(1.0, |s| s.0) + / data.scale.map_or(1.0, |s| s.0.sqrt()) * efficiency * if data.physics.on_ground.is_some() { 1.0 @@ -1075,7 +1075,7 @@ pub fn handle_jump( output_events.emit_local(LocalEvent::Jump( data.entity, strength * impulse / data.mass.0 - * data.scale.map_or(1.0, |s| s.0.sqrt()) + * data.scale.map_or(1.0, |s| s.0.powf(0.25)) * data.stats.move_speed_modifier, )); }) diff --git a/rtsim/src/data/npc.rs b/rtsim/src/data/npc.rs index ce8e0054fa..6f2f0d6151 100644 --- a/rtsim/src/data/npc.rs +++ b/rtsim/src/data/npc.rs @@ -5,7 +5,7 @@ use vek::*; use std::ops::{Deref, DerefMut}; use common::uid::Uid; -slotmap::new_key_type! { pub struct NpcId; } +pub use common::rtsim::NpcId; #[derive(Clone, Serialize, Deserialize)] pub struct Npc { diff --git a/server/src/cmd.rs b/server/src/cmd.rs index b1413ef4f8..70c678ff74 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -3845,6 +3845,7 @@ fn handle_scale( action: &ServerChatCommand, ) -> CmdResult<()> { if let Some(scale) = parse_cmd_args!(args, f32) { + let scale = scale.clamped(0.025, 1000.0); let _ = server .state .ecs_mut() diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index bb9913681e..8046e23e9b 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -7,7 +7,7 @@ use crate::{ skillset::SkillGroupKind, BuffKind, BuffSource, PhysicsState, }, - rtsim::RtSim, + // rtsim::RtSim, sys::terrain::SAFE_ZONE_RADIUS, Server, SpawnPoint, StateExt, }; @@ -26,8 +26,8 @@ use common::{ event::{EventBus, ServerEvent}, outcome::{HealthChangeInfo, Outcome}, resources::{Secs, Time}, - rtsim::RtSimEntity, - states::utils::StageSection, + // rtsim::RtSimEntity, + states::utils::{AbilityInfo, StageSection}, terrain::{Block, BlockKind, TerrainGrid}, uid::{Uid, UidAllocator}, util::Dir, diff --git a/server/src/lib.rs b/server/src/lib.rs index c1b8fd37a9..de19d3a7c7 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -30,7 +30,8 @@ pub mod metrics; pub mod persistence; mod pet; pub mod presence; -pub mod rtsim; +// TODO: Remove +//pub mod rtsim; pub mod rtsim2; pub mod settings; pub mod state_ext; @@ -65,7 +66,7 @@ use crate::{ login_provider::LoginProvider, persistence::PersistedComponents, presence::{Presence, RegionSubscription, RepositionOnChunkLoad}, - rtsim::RtSim, + // rtsim::RtSim, state_ext::StateExt, sys::sentinel::DeletedEntities, }; @@ -386,6 +387,7 @@ impl Server { state.ecs_mut().register::(); state.ecs_mut().register::(); state.ecs_mut().register::(); + state.ecs_mut().register::(); // Load banned words list let banned_words = settings.moderation.load_banned_words(data_dir); @@ -838,8 +840,8 @@ impl Server { }; for entity in to_delete { - /* // Assimilate entities that are part of the real-time world simulation + #[cfg(feature = "worldgen")] if let Some(rtsim_entity) = self .state .ecs() @@ -849,10 +851,9 @@ impl Server { { self.state .ecs() - .write_resource::() - .assimilate_entity(rtsim_entity.0); + .write_resource::() + .hook_rtsim_entity_unload(rtsim_entity); } - */ if let Err(e) = self.state.delete_entity_recorded(entity) { error!(?e, "Failed to delete agent outside the terrain"); diff --git a/server/src/rtsim2/mod.rs b/server/src/rtsim2/mod.rs index 388ba1b6dc..fa73f1fe0c 100644 --- a/server/src/rtsim2/mod.rs +++ b/server/src/rtsim2/mod.rs @@ -5,13 +5,17 @@ pub mod tick; use common::{ grid::Grid, slowjob::SlowJobPool, - rtsim::ChunkResource, + rtsim::{ChunkResource, RtSimEntity}, terrain::{TerrainChunk, Block}, vol::RectRasterableVol, }; use common_ecs::{dispatch, System}; use rtsim2::{ - data::{Data, ReadError}, + data::{ + npc::NpcMode, + Data, + ReadError, + }, rule::Rule, RtState, }; @@ -116,6 +120,16 @@ impl RtSim { } } + pub fn hook_block_update(&mut self, wpos: Vec3, old: Block, new: Block) { + self.state.emit(event::OnBlockChange { wpos, old, new }); + } + + pub fn hook_rtsim_entity_unload(&mut self, entity: RtSimEntity) { + if let Some(npc) = self.state.data_mut().npcs.get_mut(entity.0) { + npc.mode = NpcMode::Simulated; + } + } + pub fn save(&mut self, slowjob_pool: &SlowJobPool) { info!("Saving rtsim data..."); let file_path = self.file_path.clone(); @@ -157,9 +171,6 @@ impl RtSim { pub fn get_chunk_resources(&self, key: Vec2) -> EnumMap { self.state.data().nature.get_chunk_resources(key) } - pub fn hook_block_update(&mut self, wpos: Vec3, old: Block, new: Block) { - self.state.emit(event::OnBlockChange { wpos, old, new }); - } } struct ChunkStates(pub Grid>); diff --git a/server/src/rtsim2/tick.rs b/server/src/rtsim2/tick.rs index a1a2b906aa..1f1fdc0d3d 100644 --- a/server/src/rtsim2/tick.rs +++ b/server/src/rtsim2/tick.rs @@ -8,6 +8,7 @@ use common::{ generation::{BodyBuilder, EntityConfig, EntityInfo}, resources::{DeltaTime, Time}, slowjob::SlowJobPool, + rtsim::RtSimEntity, }; use common_ecs::{Job, Origin, Phase, System}; use rtsim2::data::npc::NpcMode; @@ -70,7 +71,7 @@ impl<'a> System<'a> for Sys { scale: comp::Scale(10.0), anchor: None, loot: Default::default(), - rtsim_entity: None, // For now, the old one is used! + rtsim_entity: Some(RtSimEntity(npc_id)), projectile: None, }); } diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 2098dac8bb..d32581f147 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -2,7 +2,7 @@ pub mod behavior_tree; pub use server_agent::{action_nodes, attack, consts, data, util}; use crate::{ - rtsim::RtSim, + // rtsim::{entity::PersonalityTrait, RtSim}, sys::agent::{ behavior_tree::{BehaviorData, BehaviorTree}, data::{AgentData, ReadData}, diff --git a/server/src/sys/agent/data.rs b/server/src/sys/agent/data.rs new file mode 100644 index 0000000000..e443e67cff --- /dev/null +++ b/server/src/sys/agent/data.rs @@ -0,0 +1,168 @@ +// use crate::rtsim::Entity as RtSimData; +use common::{ + comp::{ + buff::Buffs, group, item::MaterialStatManifest, ActiveAbilities, Alignment, Body, + CharacterState, Combo, Energy, Health, Inventory, LightEmitter, LootOwner, Ori, + PhysicsState, Pos, Scale, SkillSet, Stats, Vel, + }, + link::Is, + mounting::Mount, + path::TraversalConfig, + resources::{DeltaTime, Time, TimeOfDay}, + // rtsim::RtSimEntity, + terrain::TerrainGrid, + uid::{Uid, UidAllocator}, +}; +use specs::{ + shred::ResourceId, Entities, Entity as EcsEntity, Read, ReadExpect, ReadStorage, SystemData, + World, +}; +use std::sync::Arc; + +pub struct AgentData<'a> { + pub entity: &'a EcsEntity, + //pub rtsim_entity: Option<&'a RtSimData>, + pub uid: &'a Uid, + pub pos: &'a Pos, + pub vel: &'a Vel, + pub ori: &'a Ori, + pub energy: &'a Energy, + pub body: Option<&'a Body>, + pub inventory: &'a Inventory, + pub skill_set: &'a SkillSet, + #[allow(dead_code)] // may be useful for pathing + pub physics_state: &'a PhysicsState, + pub alignment: Option<&'a Alignment>, + pub traversal_config: TraversalConfig, + pub scale: f32, + pub damage: f32, + pub light_emitter: Option<&'a LightEmitter>, + pub glider_equipped: bool, + pub is_gliding: bool, + pub health: Option<&'a Health>, + pub char_state: &'a CharacterState, + pub active_abilities: &'a ActiveAbilities, + pub cached_spatial_grid: &'a common::CachedSpatialGrid, + pub msm: &'a MaterialStatManifest, +} + +pub struct TargetData<'a> { + pub pos: &'a Pos, + pub body: Option<&'a Body>, + pub scale: Option<&'a Scale>, +} + +impl<'a> TargetData<'a> { + pub fn new(pos: &'a Pos, body: Option<&'a Body>, scale: Option<&'a Scale>) -> Self { + Self { pos, body, scale } + } +} + +pub struct AttackData { + pub min_attack_dist: f32, + pub dist_sqrd: f32, + pub angle: f32, + pub angle_xy: f32, +} + +impl AttackData { + pub fn in_min_range(&self) -> bool { self.dist_sqrd < self.min_attack_dist.powi(2) } +} + +#[derive(Eq, PartialEq)] +// When adding a new variant, first decide if it should instead fall under one +// of the pre-existing tactics +pub enum Tactic { + // General tactics + SimpleMelee, + SimpleBackstab, + ElevatedRanged, + Turret, + FixedTurret, + RotatingTurret, + RadialTurret, + + // Tool specific tactics + Axe, + Hammer, + Sword, + Bow, + Staff, + Sceptre, + + // Broad creature tactics + CircleCharge { radius: u32, circle_time: u32 }, + QuadLowRanged, + TailSlap, + QuadLowQuick, + QuadLowBasic, + QuadLowBeam, + QuadMedJump, + QuadMedBasic, + Theropod, + BirdLargeBreathe, + BirdLargeFire, + BirdLargeBasic, + ArthropodMelee, + ArthropodRanged, + ArthropodAmbush, + + // Specific species tactics + Mindflayer, + Minotaur, + ClayGolem, + TidalWarrior, + Yeti, + Harvester, + StoneGolem, + Deadwood, + Mandragora, + WoodGolem, + GnarlingChieftain, + OrganAura, + Dagon, + Cardinal, +} + +#[derive(SystemData)] +pub struct ReadData<'a> { + pub entities: Entities<'a>, + pub uid_allocator: Read<'a, UidAllocator>, + pub dt: Read<'a, DeltaTime>, + pub time: Read<'a, Time>, + pub cached_spatial_grid: Read<'a, common::CachedSpatialGrid>, + pub group_manager: Read<'a, group::GroupManager>, + pub energies: ReadStorage<'a, Energy>, + pub positions: ReadStorage<'a, Pos>, + pub velocities: ReadStorage<'a, Vel>, + pub orientations: ReadStorage<'a, Ori>, + pub scales: ReadStorage<'a, Scale>, + pub healths: ReadStorage<'a, Health>, + pub inventories: ReadStorage<'a, Inventory>, + pub stats: ReadStorage<'a, Stats>, + pub skill_set: ReadStorage<'a, SkillSet>, + pub physics_states: ReadStorage<'a, PhysicsState>, + pub char_states: ReadStorage<'a, CharacterState>, + pub uids: ReadStorage<'a, Uid>, + pub groups: ReadStorage<'a, group::Group>, + pub terrain: ReadExpect<'a, TerrainGrid>, + pub alignments: ReadStorage<'a, Alignment>, + pub bodies: ReadStorage<'a, Body>, + pub is_mounts: ReadStorage<'a, Is>, + pub time_of_day: Read<'a, TimeOfDay>, + pub light_emitter: ReadStorage<'a, LightEmitter>, + #[cfg(feature = "worldgen")] + pub world: ReadExpect<'a, Arc>, + //pub rtsim_entities: ReadStorage<'a, RtSimEntity>, + pub buffs: ReadStorage<'a, Buffs>, + pub combos: ReadStorage<'a, Combo>, + pub active_abilities: ReadStorage<'a, ActiveAbilities>, + pub loot_owners: ReadStorage<'a, LootOwner>, + pub msm: ReadExpect<'a, MaterialStatManifest>, +} + +pub enum Path { + Full, + Separate, + Partial, +}