diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 1c299b8ba3..eafb9b8c68 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -1,16 +1,20 @@ -use specs::{Component, VecStorage}; +use specs::{Component, VecStorage, Entity as EcsEntity}; use vek::*; -#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug)] pub enum Agent { Wanderer(Vec2), + Pet { + target: EcsEntity, + offset: Vec2, + }, } impl Component for Agent { type Storage = VecStorage; } -#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug)] pub struct Control { pub move_dir: Vec2, pub jumping: bool, diff --git a/common/src/comp/character.rs b/common/src/comp/character.rs index d164b6d74b..929c584a48 100644 --- a/common/src/comp/character.rs +++ b/common/src/comp/character.rs @@ -116,6 +116,16 @@ pub struct AnimationHistory { pub time: f64, } +impl AnimationHistory { + pub fn new(animation: Animation) -> Self { + Self { + last: None, + current: animation, + time: 0.0, + } + } +} + #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum Animation { Idle, diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 0a04e995ec..f21e41904d 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -15,13 +15,13 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Control>, ); - fn run(&mut self, (mut agents, pos, mut controls): Self::SystemData) { - for (mut agent, pos, mut control) in (&mut agents, &pos, &mut controls).join() { + fn run(&mut self, (mut agents, positions, mut controls): Self::SystemData) { + for (mut agent, pos, mut control) in (&mut agents, &positions, &mut controls).join() { match agent { Agent::Wanderer(bearing) => { *bearing += Vec2::new( - rand::random::().fract() - 0.5, - rand::random::().fract() - 0.5, + rand::random::() - 0.5, + rand::random::() - 0.5, ) * 0.1 - *bearing * 0.01 - pos.0 * 0.0002; @@ -29,7 +29,37 @@ impl<'a> System<'a> for Sys { if bearing.magnitude_squared() != 0.0 { control.move_dir = bearing.normalized(); } - } + }, + Agent::Pet { target, offset } => { + // Run towards target + match positions.get(*target) { + Some(tgt_pos) => { + let tgt_pos = tgt_pos.0 + *offset; + + // Jump with target + control.jumping = tgt_pos.z > pos.0.z + 1.0; + + // Move towards the target + let dist = tgt_pos.distance(pos.0); + control.move_dir = if dist > 5.0 { + Vec2::from(tgt_pos - pos.0).normalized() + } else if dist < 1.5 && pos.0 != tgt_pos { + Vec2::from(pos.0 - tgt_pos).normalized() + } else { + Vec2::zero() + }; + }, + _ => control.move_dir = Vec2::zero(), + } + + // Change offset occasionally + if rand::random::() < 0.003 { + *offset = Vec2::new( + rand::random::() - 0.5, + rand::random::() - 0.5, + ) * 10.0; + } + }, } } } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 4f02f455c5..e92f6da9f8 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -4,7 +4,11 @@ use crate::Server; use common::{comp, msg::ServerMsg}; -use specs::{join::Join, Entity as EcsEntity}; +use specs::{ + Join, + Entity as EcsEntity, + Builder, +}; use vek::*; use lazy_static::lazy_static; @@ -72,9 +76,15 @@ lazy_static! { ChatCommand::new( "tp", "{}", - "/tp : Teleport to another player", + "/tp : Teleport to another player", handle_tp ), + ChatCommand::new( + "pet", + "{}", + "/pet : Spawn a test pet NPC", + handle_pet + ), ChatCommand::new("help", "", "/help: Display this message", handle_help) ]; } @@ -179,6 +189,23 @@ fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &Chat } } +fn handle_pet(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { + match server + .state + .read_component_cloned::(entity) + { + Some(pos) => { + server.create_npc(comp::Character::random()) + .with(comp::Control::default()) + .with(comp::Agent::Pet{ target: entity, offset: Vec2::zero() }) + .with(pos) + .build(); + server.clients.notify(entity, ServerMsg::Chat("Pet spawned!".to_owned())); + }, + None => server.clients.notify(entity, ServerMsg::Chat("You have no position!".to_owned())), + } +} + fn handle_help(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { for cmd in CHAT_COMMANDS.iter() { server diff --git a/server/src/lib.rs b/server/src/lib.rs index 4a70ffdbe4..43422d5306 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -82,8 +82,8 @@ impl Server { for i in 0..4 { this.create_npc(comp::Character::random()) - .with(comp::Agent::Wanderer(Vec2::zero())) .with(comp::Control::default()) + .with(comp::Agent::Wanderer(Vec2::zero())) .build(); } @@ -121,6 +121,7 @@ impl Server { .with(comp::phys::Pos(Vec3::new(0.0, 0.0, 64.0))) .with(comp::phys::Vel(Vec3::zero())) .with(comp::phys::Dir(Vec3::unit_y())) + .with(comp::AnimationHistory::new(Animation::Idle)) .with(character) } @@ -140,11 +141,7 @@ impl Server { // Set initial animation state.write_component( entity, - comp::AnimationHistory { - last: None, - current: Animation::Idle, - time: 0.0, - }, + comp::AnimationHistory::new(Animation::Idle), ); // Tell the client his request was successful @@ -272,7 +269,7 @@ impl Server { // (All components Sphynx tracks) client.notify(ServerMsg::InitialSync { ecs_state: self.state.ecs().gen_state_package(), - entity_uid: self.state.ecs().uid_from_entity(entity).unwrap().into(), + entity_uid: self.state.ecs().uid_from_entity(entity).unwrap().into(), // Can't fail }); self.clients.add(entity, client); diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 1754854313..f57958ba4e 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -136,7 +136,7 @@ Voxygen has logged information about the problem (including this message) to the The information below is intended for developers and testers. Panic Payload: {:?} -PanicInfo: {:?}", settings_clone.log.file, reason, panic_info); +PanicInfo: {}", settings_clone.log.file, reason, panic_info); log::error!("VOXYGEN HAS PANICKED\n\n{}", msg); diff --git a/voxygen/src/scene/figure.rs b/voxygen/src/scene/figure.rs index b9763f6731..62451a6252 100644 --- a/voxygen/src/scene/figure.rs +++ b/voxygen/src/scene/figure.rs @@ -245,9 +245,10 @@ impl FigureCache { for (entity, &character) in (&ecs.entities(), &ecs.read_storage::()).join() { - let model = Self::get_or_create_model(models, renderer, tick, character); - let state = self.states.get(&entity).unwrap(); - renderer.render_figure(&model.0, globals, &state.locals, &state.bone_consts); + if let Some(state) = self.states.get(&entity) { + let model = Self::get_or_create_model(models, renderer, tick, character); + renderer.render_figure(&model.0, globals, &state.locals, &state.bone_consts); + } } } }