diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 68843c66bc..69711533c4 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -73,6 +73,7 @@ impl Default for Inventory { }; inventory.push(Item::Debug(Debug::Boost)); + inventory.push(Item::Debug(Debug::Possess)); inventory.push(Item::Tool { kind: Tool::Bow, power: 10, diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index 95d2a23a88..63b4aa59d9 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -1,3 +1,4 @@ +use crate::state::Uid; use specs::{Component, FlaggedStorage}; use specs_idvs::IDVStorage; use std::time::Duration; @@ -7,10 +8,12 @@ pub enum Effect { Damage(u32), Vanish, Stick, + Possess, } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Projectile { + pub owner: Option, pub hit_ground: Vec, pub hit_wall: Vec, pub hit_entity: Vec, diff --git a/common/src/comp/stats.rs b/common/src/comp/stats.rs index c5c7e29f1d..3aa080e249 100644 --- a/common/src/comp/stats.rs +++ b/common/src/comp/stats.rs @@ -5,7 +5,6 @@ use specs_idvs::IDVStorage; #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub enum HealthSource { Attack { by: Uid }, // TODO: Implement weapon - Projectile, Suicide, World, Revive, diff --git a/common/src/event.rs b/common/src/event.rs index e86bc8ab93..0b6e895385 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -35,6 +35,8 @@ pub enum ServerEvent { Shoot { entity: EcsEntity, dir: Vec3, + body: comp::Body, + light: Option, projectile: comp::Projectile, }, LandOnGround { @@ -43,6 +45,7 @@ pub enum ServerEvent { }, Mount(EcsEntity, EcsEntity), Unmount(EcsEntity), + Possess(Uid, Uid), } pub struct EventBus { diff --git a/common/src/sys/controller.rs b/common/src/sys/controller.rs index ff05dadde9..d6ff8f7815 100644 --- a/common/src/sys/controller.rs +++ b/common/src/sys/controller.rs @@ -4,8 +4,8 @@ use super::{ }; use crate::{ comp::{ - item, projectile, ActionState::*, Body, CharacterState, ControlEvent, Controller, Item, - MovementState::*, PhysicsState, Projectile, Stats, Vel, + self, item, projectile, ActionState::*, Body, CharacterState, ControlEvent, Controller, + Item, MovementState::*, PhysicsState, Projectile, Stats, Vel, }, event::{EventBus, LocalEvent, ServerEvent}, }; @@ -13,7 +13,7 @@ use specs::{ saveload::{Marker, MarkerAllocator}, Entities, Join, Read, ReadStorage, System, WriteStorage, }; -use sphynx::UidAllocator; +use sphynx::{Uid, UidAllocator}; use std::time::Duration; use vek::*; @@ -30,6 +30,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Body>, ReadStorage<'a, Vel>, ReadStorage<'a, PhysicsState>, + ReadStorage<'a, Uid>, WriteStorage<'a, CharacterState>, ); @@ -45,6 +46,7 @@ impl<'a> System<'a> for Sys { bodies, velocities, physics_states, + uid, mut character_states, ): Self::SystemData, ) { @@ -143,7 +145,10 @@ impl<'a> System<'a> for Sys { server_emitter.emit(ServerEvent::Shoot { entity, dir: controller.look_dir, + body: comp::Body::Object(comp::object::Body::Arrow), + light: None, projectile: Projectile { + owner: uid.get(entity).copied(), hit_ground: vec![projectile::Effect::Stick], hit_wall: vec![projectile::Effect::Stick], hit_entity: vec![ @@ -203,6 +208,39 @@ impl<'a> System<'a> for Sys { }); } } + Some(Item::Debug(item::Debug::Possess)) => { + if controller.primary + && (character.movement == Stand + || character.movement == Run + || character.movement == Jump) + { + if let Wield { time_left } = character.action { + if time_left == Duration::default() { + // Immediately end the wield + character.action = Idle; + server_emitter.emit(ServerEvent::Shoot { + entity, + dir: controller.look_dir, + body: comp::Body::Object(comp::object::Body::PotionRed), + light: Some(comp::LightEmitter { + col: (0.0, 1.0, 0.3).into(), + ..Default::default() + }), + projectile: Projectile { + owner: uid.get(entity).copied(), + hit_ground: vec![projectile::Effect::Vanish], + hit_wall: vec![projectile::Effect::Vanish], + hit_entity: vec![ + projectile::Effect::Vanish, + projectile::Effect::Possess, + ], + time_left: Duration::from_secs(60 * 5), + }, + }); + } + } + } + } None => { // Attack if controller.primary diff --git a/common/src/sys/projectile.rs b/common/src/sys/projectile.rs index a397284b91..25cc6a388a 100644 --- a/common/src/sys/projectile.rs +++ b/common/src/sys/projectile.rs @@ -46,6 +46,10 @@ impl<'a> System<'a> for Sys { if physics.on_ground { for effect in projectile.hit_ground.drain(..) { match effect { + projectile::Effect::Vanish => server_emitter.emit(ServerEvent::Destroy { + entity, + cause: HealthSource::World, + }), _ => {} } } @@ -54,6 +58,10 @@ impl<'a> System<'a> for Sys { else if physics.on_wall.is_some() { for effect in projectile.hit_wall.drain(..) { match effect { + projectile::Effect::Vanish => server_emitter.emit(ServerEvent::Destroy { + entity, + cause: HealthSource::World, + }), _ => {} } } @@ -66,13 +74,21 @@ impl<'a> System<'a> for Sys { server_emitter.emit(ServerEvent::Damage { uid: other, dmg: power, - cause: HealthSource::Projectile, + cause: match projectile.owner { + Some(uid) => HealthSource::Attack { by: uid }, + None => HealthSource::Unknown, + }, }) } projectile::Effect::Vanish => server_emitter.emit(ServerEvent::Destroy { entity, cause: HealthSource::World, }), + projectile::Effect::Possess => { + if let Some(uid) = projectile.owner { + server_emitter.emit(ServerEvent::Possess(uid.into(), other)) + } + } _ => {} } } diff --git a/server/src/client.rs b/server/src/client.rs index e174ff411f..a6856c08f8 100644 --- a/server/src/client.rs +++ b/server/src/client.rs @@ -57,6 +57,10 @@ impl Clients { self.clients.get_mut(entity) } + pub fn remove<'a>(&'a mut self, entity: &EcsEntity) -> Option { + self.clients.remove(entity) + } + pub fn remove_if bool>(&mut self, mut f: F) { self.clients.retain(|entity, client| !f(*entity, client)); } diff --git a/server/src/lib.rs b/server/src/lib.rs index 811454ea8d..5415b504b5 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -199,9 +199,10 @@ impl Server { pos: comp::Pos, vel: comp::Vel, body: comp::Body, + maybe_light: Option, projectile: comp::Projectile, ) -> EcsEntityBuilder { - state + let builder = state .ecs_mut() .create_entity_synced() .with(pos) @@ -210,7 +211,13 @@ impl Server { .with(comp::Mass(0.0)) .with(body) .with(projectile) - .with(comp::Sticky) + .with(comp::Sticky); + + if let Some(light) = maybe_light { + builder.with(light) + } else { + builder + } } pub fn create_player_character( @@ -291,6 +298,8 @@ impl Server { ServerEvent::Shoot { entity, dir, + body, + light, projectile, } => { let mut pos = state @@ -307,7 +316,8 @@ impl Server { state, comp::Pos(pos), comp::Vel(dir * 100.0), - comp::Body::Object(comp::object::Body::Arrow), + body, + light, projectile, ) .build(); @@ -458,6 +468,70 @@ impl Server { } state.delete_component::(mounter); } + ServerEvent::Possess(possessor_uid, possesse_uid) => { + if let (Some(possessor), Some(possesse)) = ( + state.ecs().entity_from_uid(possessor_uid.into()), + state.ecs().entity_from_uid(possesse_uid.into()), + ) { + // You can't possess other players + if clients.get(&possesse).is_none() { + if let Some(mut client) = clients.remove(&possessor) { + client.notify(ServerMsg::SetPlayerEntity(possesse_uid.into())); + clients.add(possesse, client); + // Create inventory if it doesn't exist + { + let mut inventories = + state.ecs_mut().write_storage::(); + if let Some(inventory) = inventories.get_mut(possesse) { + inventory + .push(comp::Item::Debug(comp::item::Debug::Possess)); + } else { + let _ = inventories.insert( + possesse, + comp::Inventory { + slots: vec![Some(comp::Item::Debug( + comp::item::Debug::Possess, + ))], + }, + ); + } + } + let _ = state + .ecs_mut() + .write_storage::() + .insert(possesse, comp::InventoryUpdate); + // Move player component + { + let mut players = + state.ecs_mut().write_storage::(); + if let Some(player) = players.get(possessor).cloned() { + let _ = players.insert(possesse, player); + } + } + // Remove will of the entity + let _ = state + .ecs_mut() + .write_storage::() + .remove(possesse); + // Transfer admin powers + { + let mut admins = state.ecs_mut().write_storage::(); + if let Some(admin) = admins.remove(possessor) { + let _ = admins.insert(possesse, admin); + } + } + // Transfer waypoint + { + let mut waypoints = + state.ecs_mut().write_storage::(); + if let Some(waypoint) = waypoints.remove(possessor) { + let _ = waypoints.insert(possesse, waypoint); + } + } + } + } + } + } } if let Some(entity) = todo_remove {