diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index d1052d1af5..8c87de7b6a 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -1,9 +1,9 @@ -use crate::path::Chaser; -use specs::{Component, Entity as EcsEntity}; +use crate::{path::Chaser, sync::Uid}; +use specs::{Component, Entity as EcsEntity, FlaggedStorage}; use specs_idvs::IdvStorage; use vek::*; -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum Alignment { /// Wild animals and gentle giants Wild, @@ -14,7 +14,7 @@ pub enum Alignment { /// Farm animals and pets of villagers Tame, /// Pets you've tamed with a collar - Owned(EcsEntity), + Owned(Uid), } impl Alignment { @@ -51,7 +51,7 @@ impl Alignment { } impl Component for Alignment { - type Storage = IdvStorage; + type Storage = FlaggedStorage>; } #[derive(Clone, Debug, Default)] diff --git a/common/src/lib.rs b/common/src/lib.rs index 92c913e50f..5bdead5efe 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -7,7 +7,8 @@ bool_to_option, label_break_value, trait_alias, - type_alias_impl_trait + type_alias_impl_trait, + option_zip, )] #[macro_use] extern crate serde_derive; diff --git a/common/src/msg/ecs_packet.rs b/common/src/msg/ecs_packet.rs index 204b927c19..08cd5f52ad 100644 --- a/common/src/msg/ecs_packet.rs +++ b/common/src/msg/ecs_packet.rs @@ -17,6 +17,7 @@ sum_type! { LightEmitter(comp::LightEmitter), Item(comp::Item), Scale(comp::Scale), + Alignment(comp::Alignment), MountState(comp::MountState), Mounting(comp::Mounting), Mass(comp::Mass), @@ -43,6 +44,7 @@ sum_type! { LightEmitter(PhantomData), Item(PhantomData), Scale(PhantomData), + Alignment(PhantomData), MountState(PhantomData), Mounting(PhantomData), Mass(PhantomData), @@ -69,6 +71,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::LightEmitter(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Item(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Scale(comp) => sync::handle_insert(comp, entity, world), + EcsCompPacket::Alignment(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::MountState(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Mounting(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Mass(comp) => sync::handle_insert(comp, entity, world), @@ -93,6 +96,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::LightEmitter(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Item(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Scale(comp) => sync::handle_modify(comp, entity, world), + EcsCompPacket::Alignment(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::MountState(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Mounting(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Mass(comp) => sync::handle_modify(comp, entity, world), @@ -119,6 +123,7 @@ impl sync::CompPacket for EcsCompPacket { }, EcsCompPhantom::Item(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Scale(_) => sync::handle_remove::(entity, world), + EcsCompPhantom::Alignment(_) => sync::handle_remove::(entity, world), EcsCompPhantom::MountState(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Mounting(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Mass(_) => sync::handle_remove::(entity, world), diff --git a/common/src/state.rs b/common/src/state.rs index 99792738ef..d932b57ef7 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -123,6 +123,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); + ecs.register::(); // Register components send from clients -> server ecs.register::(); @@ -146,7 +147,6 @@ impl State { ecs.register::>(); ecs.register::>(); ecs.register::(); - ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index 70da464d13..6596283491 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -31,11 +31,13 @@ impl CharacterBehavior for Data { handle_move(data, &mut update, 0.3); handle_jump(data, &mut update); - if !self.exhausted && if self.holdable { - data.inputs.holding_ability_key() || self.prepare_timer < self.prepare_duration - } else { - self.prepare_timer < self.prepare_duration - } { + if !self.exhausted + && if self.holdable { + data.inputs.holding_ability_key() || self.prepare_timer < self.prepare_duration + } else { + self.prepare_timer < self.prepare_duration + } + { // Prepare (draw the bow) update.character = CharacterState::BasicRanged(Data { prepare_timer: self.prepare_timer + Duration::from_secs_f32(data.dt.0), diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 8fcf278448..7ebec30dc4 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -45,6 +45,24 @@ impl Body { Body::QuadrupedLow(_) => 120.0, } } + + pub fn base_ori_rate(&self) -> f32 { + match self { + Body::Humanoid(_) => 20.0, + Body::QuadrupedSmall(_) => 15.0, + Body::QuadrupedMedium(_) => 10.0, + Body::BirdMedium(_) => 30.0, + Body::FishMedium(_) => 5.0, + Body::Dragon(_) => 5.0, + Body::BirdSmall(_) => 35.0, + Body::FishSmall(_) => 10.0, + Body::BipedLarge(_) => 12.0, + Body::Object(_) => 5.0, + Body::Golem(_) => 8.0, + Body::Critter(_) => 35.0, + Body::QuadrupedLow(_) => 12.0, + } + } } /// Handles updating `Components` to move player based on state of `JoinData` @@ -68,10 +86,10 @@ fn basic_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { update.vel.0 = update.vel.0 + Vec2::broadcast(data.dt.0) * data.inputs.move_dir * accel * efficiency; - handle_orientation(data, update, 20.0); + handle_orientation(data, update, data.body.base_ori_rate()); } -pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, strength: f32) { +pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, rate: f32) { // Set direction based on move direction let ori_dir = if update.character.is_attack() || update.character.is_block() { data.inputs.look_dir.xy() @@ -82,7 +100,7 @@ pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, strength: f }; // Smooth orientation - update.ori.0 = Dir::slerp_to_vec3(update.ori.0, ori_dir.into(), strength * data.dt.0); + update.ori.0 = Dir::slerp_to_vec3(update.ori.0, ori_dir.into(), rate * data.dt.0); } /// Updates components to move player as if theyre swimming diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 01df0ffb62..21da0ef13e 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -245,7 +245,11 @@ impl<'a> System<'a> for Sys { alignments .get(*target) .copied() - .unwrap_or(Alignment::Owned(*target)), + .unwrap_or(uids + .get(*target) + .copied() + .map(Alignment::Owned) + .unwrap_or(Alignment::Wild)), ) { if let Some(dir) = Dir::from_unnormalized(tgt_pos.0 - pos.0) { inputs.look_dir = dir; @@ -419,7 +423,10 @@ impl<'a> System<'a> for Sys { // Follow owner if we're too far, or if they're under attack if let Some(Alignment::Owned(owner)) = alignment.copied() { - if let Some(owner_pos) = positions.get(owner) { + (|| { + let owner = uid_allocator.retrieve_entity_internal(owner.id())?; + + let owner_pos = positions.get(owner)?; let dist_sqrd = pos.0.distance_squared(owner_pos.0); if dist_sqrd > MAX_FOLLOW_DIST.powf(2.0) && !agent.activity.is_follow() { agent.activity = Activity::Follow { @@ -429,28 +436,27 @@ impl<'a> System<'a> for Sys { } // Attack owner's attacker - if let Some(owner_stats) = stats.get(owner) { - if owner_stats.health.last_change.0 < 5.0 { - if let comp::HealthSource::Attack { by } = - owner_stats.health.last_change.1.cause - { - if !agent.activity.is_attack() { - if let Some(attacker) = - uid_allocator.retrieve_entity_internal(by.id()) - { - agent.activity = Activity::Attack { - target: attacker, - chaser: Chaser::default(), - time: time.0, - been_close: false, - powerup: 0.0, - }; - } - } + let owner_stats = stats.get(owner)?; + if owner_stats.health.last_change.0 < 5.0 { + if let comp::HealthSource::Attack { by } = + owner_stats.health.last_change.1.cause + { + if !agent.activity.is_attack() { + let attacker = uid_allocator.retrieve_entity_internal(by.id())?; + + agent.activity = Activity::Attack { + target: attacker, + chaser: Chaser::default(), + time: time.0, + been_close: false, + powerup: 0.0, + }; } } } - } + + Some(()) + })(); } debug_assert!(inputs.move_dir.map(|e| !e.is_nan()).reduce_and()); diff --git a/common/src/sys/combat.rs b/common/src/sys/combat.rs index 5e6f3dc879..1882b5c390 100644 --- a/common/src/sys/combat.rs +++ b/common/src/sys/combat.rs @@ -119,12 +119,15 @@ impl<'a> System<'a> for Sys { // healthchange = (healthchange / 1.5).min(-1.0); //} - // TODO: remove this when there is a better way to target healing - // Don't heal npc's hp - if alignment_b_maybe + // TODO: remove this when there is a better way to deal with alignment + // Don't heal NPCs + if (healthchange > 0.0 && alignment_b_maybe .map(|a| !a.is_friendly_to_players()) - .unwrap_or(true) - && healthchange > 0.0 + .unwrap_or(true)) + // Don't hurt pets + || (healthchange < 0.0 && alignment_b_maybe + .map(|b| Alignment::Owned(*uid).passive_towards(*b)) + .unwrap_or(false)) { healthchange = 0.0; } diff --git a/common/src/sys/projectile.rs b/common/src/sys/projectile.rs index 7faf63b9a3..056806786c 100644 --- a/common/src/sys/projectile.rs +++ b/common/src/sys/projectile.rs @@ -1,6 +1,7 @@ use crate::{ comp::{ projectile, Energy, EnergySource, HealthSource, Ori, PhysicsState, Pos, Projectile, Vel, + Alignment, }, event::{EventBus, LocalEvent, ServerEvent}, state::DeltaTime, @@ -27,6 +28,7 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Ori>, WriteStorage<'a, Projectile>, WriteStorage<'a, Energy>, + ReadStorage<'a, Alignment>, ); fn run( @@ -43,6 +45,7 @@ impl<'a> System<'a> for Sys { mut orientations, mut projectiles, mut energies, + alignments, ): Self::SystemData, ) { let mut local_emitter = local_bus.emitter(); @@ -82,7 +85,17 @@ impl<'a> System<'a> for Sys { for effect in projectile.hit_entity.drain(..) { match effect { projectile::Effect::Damage(change) => { - if other != projectile.owner.unwrap() { + let owner_uid = projectile.owner.unwrap(); + // Hacky: remove this when groups get implemented + let passive = uid_allocator + .retrieve_entity_internal(other.into()) + .and_then(|other| + alignments + .get(other) + .map(|a| Alignment::Owned(owner_uid) + .passive_towards(*a))) + .unwrap_or(false); + if other != projectile.owner.unwrap() && !passive { server_emitter.emit(ServerEvent::Damage { uid: other, change }); } }, diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 70886e603a..f44c5743e1 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -507,7 +507,8 @@ fn handle_spawn( String ) { (Some(opt_align), Some(npc::NpcBody(id, mut body)), opt_amount, opt_ai) => { - if let Some(alignment) = parse_alignment(target, &opt_align) { + let uid = server.state.read_component_cloned(target).expect("Expected player to have a UID"); + if let Some(alignment) = parse_alignment(uid, &opt_align) { let amount = opt_amount .and_then(|a| a.parse().ok()) .filter(|x| *x > 0) @@ -715,7 +716,7 @@ fn handle_help( } } -fn parse_alignment(owner: EcsEntity, alignment: &str) -> Option { +fn parse_alignment(owner: Uid, alignment: &str) -> Option { match alignment { "wild" => Some(comp::Alignment::Wild), "enemy" => Some(comp::Alignment::Enemy), diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index 0d6c37839d..7b9f5fc753 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -188,13 +188,15 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv let reinsert = if let Some(pos) = state.read_storage::().get(entity) { + let uid = state.read_component_cloned(entity) + .expect("Expected player to have a UID"); if ( &state.read_storage::(), &state.read_storage::(), ) .join() .filter(|(alignment, _)| { - alignment == &&comp::Alignment::Owned(entity) + alignment == &&comp::Alignment::Owned(uid) }) .count() >= 3 @@ -222,7 +224,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv } { let _ = state.ecs().write_storage().insert( tameable_entity, - comp::Alignment::Owned(entity), + comp::Alignment::Owned(uid), ); let _ = state .ecs() diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 20a7baac5d..a9b300dea9 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -171,7 +171,7 @@ impl StateExt for State { }); self.write_component(entity, comp::Gravity(1.0)); self.write_component(entity, comp::CharacterState::default()); - self.write_component(entity, comp::Alignment::Owned(entity)); + self.write_component(entity, comp::Alignment::Owned(self.read_component_cloned(entity).unwrap())); // Set the character id for the player // TODO this results in a warning in the console: "Error modifying synced diff --git a/server/src/sys/sentinel.rs b/server/src/sys/sentinel.rs index bdbde3248b..7fc0f9a59a 100644 --- a/server/src/sys/sentinel.rs +++ b/server/src/sys/sentinel.rs @@ -2,7 +2,7 @@ use super::SysTimer; use common::{ comp::{ Body, CanBuild, CharacterState, Collider, Energy, Gravity, Item, LightEmitter, Loadout, - Mass, MountState, Mounting, Ori, Player, Pos, Scale, Stats, Sticky, Vel, + Mass, MountState, Mounting, Ori, Player, Pos, Scale, Stats, Sticky, Vel, Alignment, }, msg::EcsCompPacket, sync::{CompSyncPackage, EntityPackage, EntitySyncPackage, Uid, UpdateTracker, WorldSyncExt}, @@ -48,6 +48,7 @@ pub struct TrackedComps<'a> { pub scale: ReadStorage<'a, Scale>, pub mounting: ReadStorage<'a, Mounting>, pub mount_state: ReadStorage<'a, MountState>, + pub alignment: ReadStorage<'a, Alignment>, pub mass: ReadStorage<'a, Mass>, pub collider: ReadStorage<'a, Collider>, pub sticky: ReadStorage<'a, Sticky>, @@ -104,6 +105,10 @@ impl<'a> TrackedComps<'a> { .get(entity) .cloned() .map(|c| comps.push(c.into())); + self.alignment + .get(entity) + .cloned() + .map(|c| comps.push(c.into())); self.mass.get(entity).copied().map(|c| comps.push(c.into())); self.collider .get(entity) @@ -146,6 +151,7 @@ pub struct ReadTrackers<'a> { pub scale: ReadExpect<'a, UpdateTracker>, pub mounting: ReadExpect<'a, UpdateTracker>, pub mount_state: ReadExpect<'a, UpdateTracker>, + pub alignment: ReadExpect<'a, UpdateTracker>, pub mass: ReadExpect<'a, UpdateTracker>, pub collider: ReadExpect<'a, UpdateTracker>, pub sticky: ReadExpect<'a, UpdateTracker>, @@ -178,6 +184,7 @@ impl<'a> ReadTrackers<'a> { .with_component(&comps.uid, &*self.scale, &comps.scale, filter) .with_component(&comps.uid, &*self.mounting, &comps.mounting, filter) .with_component(&comps.uid, &*self.mount_state, &comps.mount_state, filter) + .with_component(&comps.uid, &*self.alignment, &comps.alignment, filter) .with_component(&comps.uid, &*self.mass, &comps.mass, filter) .with_component(&comps.uid, &*self.collider, &comps.collider, filter) .with_component(&comps.uid, &*self.sticky, &comps.sticky, filter) @@ -207,6 +214,7 @@ pub struct WriteTrackers<'a> { scale: WriteExpect<'a, UpdateTracker>, mounting: WriteExpect<'a, UpdateTracker>, mount_state: WriteExpect<'a, UpdateTracker>, + alignment: WriteExpect<'a, UpdateTracker>, mass: WriteExpect<'a, UpdateTracker>, collider: WriteExpect<'a, UpdateTracker>, sticky: WriteExpect<'a, UpdateTracker>, @@ -228,6 +236,7 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) { trackers.scale.record_changes(&comps.scale); trackers.mounting.record_changes(&comps.mounting); trackers.mount_state.record_changes(&comps.mount_state); + trackers.alignment.record_changes(&comps.alignment); trackers.mass.record_changes(&comps.mass); trackers.collider.record_changes(&comps.collider); trackers.sticky.record_changes(&comps.sticky); @@ -282,6 +291,7 @@ pub fn register_trackers(world: &mut World) { world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); + world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index d03907e483..5592cad96d 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -57,9 +57,9 @@ impl SessionState { .set_fov_deg(global_state.settings.graphics.fov); let hud = Hud::new(global_state, &client.borrow()); { - let my_entity = client.borrow().entity(); + let mut client = client.borrow_mut(); + let my_entity = client.entity(); client - .borrow_mut() .state_mut() .ecs_mut() .insert(MyEntity(my_entity));