diff --git a/assets/voxygen/element/icons/2hsword_m1.vox b/assets/voxygen/element/icons/2hsword_m1.vox index c920f452af..d1aa6f9d58 100644 Binary files a/assets/voxygen/element/icons/2hsword_m1.vox and b/assets/voxygen/element/icons/2hsword_m1.vox differ diff --git a/assets/voxygen/element/icons/2hsword_m2.vox b/assets/voxygen/element/icons/2hsword_m2.vox index cb1b11c1d7..bc67ba65fe 100644 Binary files a/assets/voxygen/element/icons/2hsword_m2.vox and b/assets/voxygen/element/icons/2hsword_m2.vox differ diff --git a/assets/voxygen/voxel/weapon/debug_wand.vox b/assets/voxygen/voxel/weapon/debug_wand.vox new file mode 100644 index 0000000000..b7a6ff9ef5 Binary files /dev/null and b/assets/voxygen/voxel/weapon/debug_wand.vox differ diff --git a/client/src/lib.rs b/client/src/lib.rs index 5f4ca2798d..285017027f 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -149,9 +149,14 @@ impl Client { } /// Request a state transition to `ClientState::Character`. - pub fn request_character(&mut self, name: String, body: comp::Body) { + pub fn request_character( + &mut self, + name: String, + body: comp::Body, + main: Option, + ) { self.postbox - .send_message(ClientMsg::Character { name, body }); + .send_message(ClientMsg::Character { name, body, main }); self.client_state = ClientState::Pending; } @@ -176,6 +181,10 @@ impl Client { // Can't fail } + pub fn use_inventory_slot(&mut self, x: usize) { + self.postbox.send_message(ClientMsg::UseInventorySlot(x)) + } + pub fn swap_inventory_slots(&mut self, a: usize, b: usize) { self.postbox .send_message(ClientMsg::SwapInventorySlots(a, b)) diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index 45dc34df86..eb4912e107 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -4,11 +4,11 @@ use vek::*; #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct Controller { + pub primary: bool, + pub secondary: bool, pub move_dir: Vec2, pub look_dir: Vec3, pub jump: bool, - pub attack: bool, - pub block: bool, pub roll: bool, pub glide: bool, pub respawn: bool, diff --git a/common/src/comp/inventory/item.rs b/common/src/comp/inventory/item.rs index 7ad0a229fc..deb885dad6 100644 --- a/common/src/comp/inventory/item.rs +++ b/common/src/comp/inventory/item.rs @@ -1,7 +1,7 @@ use specs::{Component, FlaggedStorage}; use specs_idvs::IDVStorage; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Tool { Daggers, SwordShield, @@ -36,7 +36,7 @@ pub const ALL_TOOLS: [Tool; 7] = [ Tool::Staff, ]; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Armor { // TODO: Don't make armor be a body part. Wearing enemy's head is funny but also creepy thing to do. Helmet, @@ -77,6 +77,11 @@ pub enum ConsumptionEffect { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Debug { + Boost, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Item { Tool { kind: Tool, @@ -91,6 +96,7 @@ pub enum Item { effect: ConsumptionEffect, }, Ingredient, + Debug(Debug), } impl Item { @@ -100,6 +106,7 @@ impl Item { Item::Armor { kind, .. } => kind.name(), Item::Consumable { .. } => "", Item::Ingredient => "", + Item::Debug(_) => "Debugging item", } } @@ -109,6 +116,7 @@ impl Item { Item::Armor { .. } => "armour", Item::Consumable { .. } => "consumable", Item::Ingredient => "ingredient", + Item::Debug(_) => "debug", } } diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 808fb1b9f2..e65b9eecd6 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -2,7 +2,7 @@ pub mod item; // Reexports -pub use self::item::{Item, Tool}; +pub use self::item::{Debug, Item, Tool}; use specs::{Component, HashMapStorage, NullStorage}; use specs_idvs::IDVStorage; @@ -21,7 +21,9 @@ impl Inventory { self.slots.len() } - pub fn insert(&mut self, item: Item) -> Option { + /// Adds a new item to the first empty slot of the inventory. Returns the item again if no free + /// slot was found. + pub fn push(&mut self, item: Item) -> Option { match self.slots.iter_mut().find(|slot| slot.is_none()) { Some(slot) => { *slot = Some(item); @@ -31,24 +33,32 @@ impl Inventory { } } - // Get info about an item slot - pub fn get(&self, cell: usize) -> Option { - self.slots.get(cell).cloned().flatten() + /// Replaces an item in a specific slot of the inventory. Returns the old item or the same item again if that slot + /// was not found. + pub fn insert(&mut self, cell: usize, item: Item) -> Result, Item> { + match self.slots.get_mut(cell) { + Some(slot) => { + let old = slot.take(); + *slot = Some(item); + Ok(old) + } + None => Err(item), + } } - // Insert an item to a slot if its empty - pub fn swap(&mut self, cell: usize, item: Item) -> Option { - //TODO: Check if a slot is empty first. - self.slots.get_mut(cell).and_then(|cell| cell.replace(item)) + /// Get content of a slot + pub fn get(&self, cell: usize) -> Option<&Item> { + self.slots.get(cell).and_then(Option::as_ref) } + /// Swap the items inside of two slots pub fn swap_slots(&mut self, a: usize, b: usize) { if a.max(b) < self.slots.len() { self.slots.swap(a, b); } } - // Remove an item from the slot + /// Remove an item from the slot pub fn remove(&mut self, cell: usize) -> Option { self.slots.get_mut(cell).and_then(|item| item.take()) } @@ -60,28 +70,25 @@ impl Default for Inventory { slots: vec![None; 24], }; - inventory.insert(Item::Tool { + inventory.push(Item::Debug(Debug::Boost)); + inventory.push(Item::Tool { kind: Tool::Daggers, power: 10, }); - inventory.insert(Item::Tool { + inventory.push(Item::Tool { kind: Tool::Sword, power: 10, }); - inventory.insert(Item::Tool { + inventory.push(Item::Tool { kind: Tool::Axe, power: 10, }); - inventory.insert(Item::Tool { + inventory.push(Item::Tool { kind: Tool::Hammer, power: 10, }); - inventory.insert(Item::Tool { - kind: Tool::Bow, - power: 10, - }); for _ in 0..10 { - inventory.insert(Item::default()); + inventory.push(Item::default()); } inventory diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 22b9d4c52e..18bb53be8b 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -24,5 +24,5 @@ pub use inventory::{item, Inventory, InventoryUpdate, Item}; pub use last::Last; pub use phys::{ForceUpdate, Ori, PhysicsState, Pos, Scale, Vel}; pub use player::Player; -pub use stats::{Exp, HealthSource, Level, Stats}; +pub use stats::{Equipment, Exp, HealthSource, Level, Stats}; pub use visual::LightEmitter; diff --git a/common/src/comp/stats.rs b/common/src/comp/stats.rs index f147247cbd..e1e77f0549 100644 --- a/common/src/comp/stats.rs +++ b/common/src/comp/stats.rs @@ -1,4 +1,4 @@ -use crate::state::Uid; +use crate::{comp, state::Uid}; use specs::{Component, FlaggedStorage}; use specs_idvs::IDVStorage; @@ -44,6 +44,13 @@ pub struct Level { amount: u32, } +#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] +pub struct Equipment { + pub main: Option, + pub alt: Option, + // TODO: Armor +} + impl Health { pub fn current(&self) -> u32 { self.current @@ -145,12 +152,12 @@ pub struct Stats { pub energy: Energy, pub level: Level, pub exp: Exp, + pub equipment: Equipment, pub is_dead: bool, } impl Stats { pub fn should_die(&self) -> bool { - // TODO: Remove self.health.current == 0 } pub fn revive(&mut self) { @@ -161,7 +168,7 @@ impl Stats { } impl Stats { - pub fn new(name: String) -> Self { + pub fn new(name: String, main: Option) -> Self { Self { name, health: Health { @@ -179,6 +186,10 @@ impl Stats { maximum: 200, last_change: None, }, + equipment: Equipment { + main: main, + alt: None, + }, is_dead: false, } } diff --git a/common/src/event.rs b/common/src/event.rs index fc081c3e01..e28e6e558b 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -6,6 +6,7 @@ use vek::*; pub enum LocalEvent { Jump(EcsEntity), + Boost { entity: EcsEntity, vel: Vec3 }, LandOnGround { entity: EcsEntity, vel: Vec3 }, } diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs index f5c7bf9f7b..c3639562bd 100644 --- a/common/src/msg/client.rs +++ b/common/src/msg/client.rs @@ -12,6 +12,7 @@ pub enum ClientMsg { Character { name: String, body: comp::Body, + main: Option, }, Controller(comp::Controller), RequestState(ClientState), @@ -29,6 +30,7 @@ pub enum ClientMsg { vel: comp::Vel, ori: comp::Ori, }, + UseInventorySlot(usize), SwapInventorySlots(usize, usize), DropInventorySlot(usize), PickUp(u64), diff --git a/common/src/state.rs b/common/src/state.rs index 6d18e06fea..fbe80d206b 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -334,6 +334,15 @@ impl State { vel.0.z = HUMANOID_JUMP_ACCEL; } } + + LocalEvent::Boost { + entity, + vel: extra_vel, + } => { + if let Some(vel) = velocities.get_mut(entity) { + vel.0 += extra_vel; + } + } } } } diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 929579ecce..99ae500a01 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -87,9 +87,9 @@ impl<'a> System<'a> for Sys { Vec2::::from(target_pos.0 - pos.0).normalized() * 0.5; if rand::random::() < 0.05 { - controller.attack = true; + controller.primary = true; } else { - controller.attack = false; + controller.primary = false; } } else if dist < SIGHT_DIST { controller.move_dir = diff --git a/common/src/sys/controller.rs b/common/src/sys/controller.rs index b570ed4013..98a210da7e 100644 --- a/common/src/sys/controller.rs +++ b/common/src/sys/controller.rs @@ -4,13 +4,14 @@ use super::{ }; use crate::{ comp::{ - ActionState::*, Body, CharacterState, Controller, MovementState::*, PhysicsState, Stats, - Vel, + item, ActionState::*, Body, CharacterState, Controller, Item, MovementState::*, + PhysicsState, Stats, Vel, }, event::{EventBus, LocalEvent, ServerEvent}, }; use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; use std::time::Duration; +use vek::*; /// This system is responsible for validating controller inputs pub struct Sys; @@ -96,7 +97,7 @@ impl<'a> System<'a> for Sys { } // Wield - if controller.attack + if controller.primary && character.action == Idle && (character.movement == Stand || character.movement == Run) { @@ -105,33 +106,53 @@ impl<'a> System<'a> for Sys { }; } - // Attack - if controller.attack - && (character.movement == Stand - || character.movement == Run - || character.movement == Jump) - { - // TODO: Check if wield ability exists - if let Wield { time_left } = character.action { - if time_left == Duration::default() { - character.action = Attack { - time_left: ATTACK_DURATION, - applied: false, + match stats.equipment.main { + Some(Item::Tool { .. }) => { + // Attack + if controller.primary + && (character.movement == Stand + || character.movement == Run + || character.movement == Jump) + { + // TODO: Check if wield ability exists + if let Wield { time_left } = character.action { + if time_left == Duration::default() { + character.action = Attack { + time_left: ATTACK_DURATION, + applied: false, + }; + } + } + } + + // Block + if controller.secondary + && (character.movement == Stand || character.movement == Run) + && (character.action == Idle || character.action.is_wield()) + { + character.action = Block { + time_left: Duration::from_secs(5), }; + } else if !controller.secondary && character.action.is_block() { + character.action = Idle; } } - } - - // Block - if controller.block - && (character.movement == Stand || character.movement == Run) - && (character.action == Idle || character.action.is_wield()) - { - character.action = Block { - time_left: Duration::from_secs(5), - }; - } else if !controller.block && character.action.is_block() { - character.action = Idle; + Some(Item::Debug(item::Debug::Boost)) => { + if controller.primary { + local_emitter.emit(LocalEvent::Boost { + entity, + vel: controller.look_dir * 7.0, + }); + } + if controller.secondary { + // Go upward + local_emitter.emit(LocalEvent::Boost { + entity, + vel: Vec3::new(0.0, 0.0, 7.0), + }); + } + } + _ => {} } // Roll diff --git a/common/src/sys/movement.rs b/common/src/sys/movement.rs index 3acabeb997..d89b89b2c5 100644 --- a/common/src/sys/movement.rs +++ b/common/src/sys/movement.rs @@ -15,8 +15,6 @@ pub const ROLL_DURATION: Duration = Duration::from_millis(600); const HUMANOID_ACCEL: f32 = 70.0; const HUMANOID_SPEED: f32 = 120.0; -const WIELD_ACCEL: f32 = 70.0; -const WIELD_SPEED: f32 = 120.0; const HUMANOID_AIR_ACCEL: f32 = 10.0; const HUMANOID_AIR_SPEED: f32 = 100.0; const ROLL_SPEED: f32 = 13.0; diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index cd16ee87a3..42ebc978c5 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -273,9 +273,7 @@ impl<'a> System<'a> for Sys { } // Apply pushback - for (pos, scale, mut vel, _) in - (&positions, scales.maybe(), &mut velocities, &bodies).join() - { + for (pos, scale, vel, _) in (&positions, scales.maybe(), &mut velocities, &bodies).join() { let scale = scale.map(|s| s.0).unwrap_or(1.0); for (pos_other, scale_other, _) in (&positions, scales.maybe(), &bodies).join() { let scale_other = scale_other.map(|s| s.0).unwrap_or(1.0); diff --git a/server/src/cmd.rs b/server/src/cmd.rs index f920f1d0f6..33733b9b26 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -10,8 +10,6 @@ use common::{ msg::ServerMsg, npc::{get_npc_name, NpcKind}, state::TimeOfDay, - terrain::TerrainChunkSize, - vol::VolSize, }; use rand::Rng; use specs::{Builder, Entity as EcsEntity, Join}; @@ -411,7 +409,7 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C let body = kind_to_body(id); server - .create_npc(pos, comp::Stats::new(get_npc_name(id)), body) + .create_npc(pos, comp::Stats::new(get_npc_name(id), None), body) .with(comp::Vel(vel)) .with(agent) .build(); diff --git a/server/src/lib.rs b/server/src/lib.rs index 606b143923..0be77d38d9 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -200,12 +200,13 @@ impl Server { client: &mut Client, name: String, body: comp::Body, + main: Option, server_settings: &ServerSettings, ) { let spawn_point = state.ecs().read_resource::().0; state.write_component(entity, body); - state.write_component(entity, comp::Stats::new(name)); + state.write_component(entity, comp::Stats::new(name, main)); state.write_component(entity, comp::Controller::default()); state.write_component(entity, comp::Pos(spawn_point)); state.write_component(entity, comp::Vel(Vec3::zero())); @@ -433,11 +434,17 @@ impl Server { // Handle chunk supplement for npc in supplement.npcs { let (mut stats, mut body) = if rand::random() { - let stats = comp::Stats::new("Humanoid".to_string()); + let stats = comp::Stats::new( + "Humanoid".to_string(), + Some(comp::Item::Tool { + kind: comp::item::Tool::Sword, + power: 10, + }), + ); let body = comp::Body::Humanoid(comp::humanoid::Body::random()); (stats, body) } else { - let stats = comp::Stats::new("Wolf".to_string()); + let stats = comp::Stats::new("Wolf".to_string(), None); let body = comp::Body::QuadrupedMedium(comp::quadruped_medium::Body::random()); (stats, body) }; @@ -445,7 +452,13 @@ impl Server { if npc.boss { if rand::random::() < 0.8 { - stats = comp::Stats::new("Humanoid".to_string()); + stats = comp::Stats::new( + "Humanoid".to_string(), + Some(comp::Item::Tool { + kind: comp::item::Tool::Sword, + power: 10, + }), + ); body = comp::Body::Humanoid(comp::humanoid::Body::random()); } stats = stats.with_max_health(500 + rand::random::() % 400); @@ -699,6 +712,34 @@ impl Server { } _ => {} }, + ClientMsg::UseInventorySlot(x) => { + let item = state + .ecs() + .write_storage::() + .get_mut(entity) + .and_then(|inv| inv.remove(x)); + + match item { + Some(comp::Item::Tool { .. }) | Some(comp::Item::Debug(_)) => { + if let Some(stats) = + state.ecs().write_storage::().get_mut(entity) + { + // Insert old item into inventory + if let Some(old_item) = stats.equipment.main.take() { + state + .ecs() + .write_storage::() + .get_mut(entity) + .map(|inv| inv.insert(x, old_item)); + } + + stats.equipment.main = item; + } + } + _ => {} + } + state.write_component(entity, comp::InventoryUpdate); + } ClientMsg::SwapInventorySlots(a, b) => { state .ecs() @@ -740,11 +781,11 @@ impl Server { item_entity.and_then(|item_entity| { ecs.write_storage::() .get_mut(item_entity) - .map(|item| (*item, item_entity)) + .map(|item| (item.clone(), item_entity)) }), ecs.write_storage::().get_mut(entity), ) { - if inv.insert(item).is_none() { + if inv.push(item).is_none() { Some(item_entity) } else { None @@ -759,7 +800,7 @@ impl Server { state.write_component(entity, comp::InventoryUpdate); } - ClientMsg::Character { name, body } => match client.client_state { + ClientMsg::Character { name, body, main } => match client.client_state { // Become Registered first. ClientState::Connected => { client.error_state(RequestStateError::Impossible) @@ -773,6 +814,7 @@ impl Server { client, name, body, + main.map(|t| comp::Item::Tool { kind: t, power: 10 }), &server_settings, ); if let Some(player) = diff --git a/voxygen/src/hud/bag.rs b/voxygen/src/hud/bag.rs index 749732e001..523dbdc5b0 100644 --- a/voxygen/src/hud/bag.rs +++ b/voxygen/src/hud/bag.rs @@ -153,7 +153,7 @@ impl<'a> Widget for Bag<'a> { let selected_slot = match state.selected_slot { Some(a) => { if a == i { - event = Some(Event::HudEvent(HudEvent::DropInventorySlot(i))); + event = Some(Event::HudEvent(HudEvent::UseInventorySlot(i))); } else { event = Some(Event::HudEvent(HudEvent::SwapInventorySlots(a, i))); } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index a9f0299f41..79731b13a2 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -162,6 +162,7 @@ pub enum Event { ToggleShortcutNumbers(ShortcutNumbers), UiScale(ScaleChange), CharacterSelection, + UseInventorySlot(usize), SwapInventorySlots(usize, usize), DropInventorySlot(usize), Logout, diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index 88906fe71a..e996ebeb45 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -3,7 +3,7 @@ use super::{ /*FOCUS_COLOR, RAGE_COLOR,*/ HP_COLOR, LOW_HP_COLOR, MANA_COLOR, TEXT_COLOR, XP_COLOR, }; use crate::GlobalState; -use common::comp::Stats; +use common::comp::{item::Tool, Item, Stats}; use conrod_core::{ color, widget::{self, Button, Image, Rectangle, Text}, @@ -351,10 +351,16 @@ impl<'a> Widget for Skillbar<'a> { .color(Some(BG_COLOR)) .middle_of(state.ids.m1_slot) .set(state.ids.m1_slot_bg, ui); - Button::image(self.imgs.twohhammer_m1) // Insert Icon here - .w_h(38.0 * scale, 38.0 * scale) - .middle_of(state.ids.m1_slot_bg) - .set(state.ids.m1_content, ui); + Button::image(match self.stats.equipment.main { + Some(Item::Tool { kind, .. }) => match kind { + Tool::Sword => self.imgs.twohsword_m1, + _ => self.imgs.twohhammer_m1, + }, + _ => self.imgs.twohhammer_m1, + }) // Insert Icon here + .w_h(38.0 * scale, 38.0 * scale) + .middle_of(state.ids.m1_slot_bg) + .set(state.ids.m1_content, ui); // M2 Slot Image::new(self.imgs.skillbar_slot_big) .w_h(40.0 * scale, 40.0 * scale) @@ -365,10 +371,16 @@ impl<'a> Widget for Skillbar<'a> { .color(Some(BG_COLOR)) .middle_of(state.ids.m2_slot) .set(state.ids.m2_slot_bg, ui); - Button::image(self.imgs.twohhammer_m2) // Insert Icon here - .w_h(38.0 * scale, 38.0 * scale) - .middle_of(state.ids.m2_slot_bg) - .set(state.ids.m2_content, ui); + Button::image(match self.stats.equipment.main { + Some(Item::Tool { kind, .. }) => match kind { + Tool::Sword => self.imgs.twohsword_m2, + _ => self.imgs.twohhammer_m2, + }, + _ => self.imgs.twohhammer_m2, + }) // Insert Icon here + .w_h(38.0 * scale, 38.0 * scale) + .middle_of(state.ids.m2_slot_bg) + .set(state.ids.m2_content, ui); //Slot 5 Image::new(self.imgs.skillbar_slot) .w_h(20.0 * scale, 20.0 * scale) diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index c4137b9144..967798c17f 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -65,6 +65,7 @@ impl PlayState for CharSelectionState { self.client.borrow_mut().request_character( self.char_selection_ui.character_name.clone(), comp::Body::Humanoid(self.char_selection_ui.character_body), + self.char_selection_ui.character_tool, ); return PlayStateResult::Push(Box::new(SessionState::new( global_state, @@ -89,6 +90,17 @@ impl PlayState for CharSelectionState { global_state.window.renderer_mut(), &self.client.borrow(), self.char_selection_ui.character_body, + &comp::Equipment { + main: if let Some(kind) = self.char_selection_ui.character_tool { + Some(comp::Item::Tool { + kind: kind, + power: 10, + }) + } else { + None + }, + alt: None, + }, ); // Draw the UI to the screen. diff --git a/voxygen/src/menu/char_selection/scene.rs b/voxygen/src/menu/char_selection/scene.rs index d01df92018..ea57582854 100644 --- a/voxygen/src/menu/char_selection/scene.rs +++ b/voxygen/src/menu/char_selection/scene.rs @@ -15,7 +15,7 @@ use crate::{ }; use client::Client; use common::{ - comp::{humanoid, Body}, + comp::{humanoid, Body, Equipment}, state::DeltaTime, terrain::BlockKind, }; @@ -132,12 +132,23 @@ impl Scene { ); } - pub fn render(&mut self, renderer: &mut Renderer, client: &Client, body: humanoid::Body) { + pub fn render( + &mut self, + renderer: &mut Renderer, + client: &Client, + body: humanoid::Body, + equipment: &Equipment, + ) { renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals); let model = &self .figure_model_cache - .get_or_create_model(renderer, Body::Humanoid(body), client.get_tick()) + .get_or_create_model( + renderer, + Body::Humanoid(body), + Some(equipment), + client.get_tick(), + ) .0; renderer.render_figure( diff --git a/voxygen/src/menu/char_selection/ui.rs b/voxygen/src/menu/char_selection/ui.rs index ed1b42d0b3..3a7264f18a 100644 --- a/voxygen/src/menu/char_selection/ui.rs +++ b/voxygen/src/menu/char_selection/ui.rs @@ -209,7 +209,7 @@ pub struct CharSelectionUi { character_creation: bool, pub character_name: String, pub character_body: humanoid::Body, - pub character_weapon: Tool, // TODO: Move into ecs inventory struct? + pub character_tool: Option, } impl CharSelectionUi { @@ -235,7 +235,7 @@ impl CharSelectionUi { character_creation: false, character_name: "Character Name".to_string(), character_body: humanoid::Body::random(), - character_weapon: Tool::Sword, + character_tool: Some(Tool::Sword), } } @@ -344,7 +344,7 @@ impl CharSelectionUi { .was_clicked() { self.character_creation = true; - self.character_weapon = Tool::Sword; + self.character_tool = Some(Tool::Sword); } // Alpha Version @@ -710,7 +710,7 @@ impl CharSelectionUi { .w_h(70.0, 70.0) .bottom_left_with_margins_on(self.ids.creation_buttons_alignment_2, 0.0, 0.0) .set(self.ids.hammer, ui_widgets); - if Button::image(if let Tool::Hammer = self.character_weapon { + if Button::image(if let Some(Tool::Hammer) = self.character_tool { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -721,7 +721,7 @@ impl CharSelectionUi { .set(self.ids.hammer_button, ui_widgets) .was_clicked() { - self.character_weapon = Tool::Hammer; + self.character_tool = Some(Tool::Hammer); } // REMOVE THIS AFTER IMPLEMENTATION /*Rectangle::fill_with([67.0, 67.0], color::rgba(0.0, 0.0, 0.0, 0.8)) @@ -734,7 +734,7 @@ impl CharSelectionUi { .w_h(70.0, 70.0) .right_from(self.ids.hammer, 2.0) .set(self.ids.bow, ui_widgets); - if Button::image(if let Tool::Bow = self.character_weapon { + if Button::image(if let Some(Tool::Bow) = self.character_tool { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -745,7 +745,7 @@ impl CharSelectionUi { .set(self.ids.bow_button, ui_widgets) .was_clicked() { - //self.character_weapon = Tool::Bow; + //self.character_tool = Some(Tool::Bow); } // REMOVE THIS AFTER IMPLEMENTATION Rectangle::fill_with([67.0, 67.0], color::rgba(0.0, 0.0, 0.0, 0.8)) @@ -756,7 +756,7 @@ impl CharSelectionUi { .w_h(70.0, 70.0) .right_from(self.ids.bow, 2.0) .set(self.ids.staff, ui_widgets); - if Button::image(if let Tool::Staff = self.character_weapon { + if Button::image(if let Some(Tool::Staff) = self.character_tool { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -767,7 +767,7 @@ impl CharSelectionUi { .set(self.ids.staff_button, ui_widgets) .was_clicked() { - //self.character_weapon = Tool::Staff; + //self.character_tool = Some(Tool::Staff); } // REMOVE THIS AFTER IMPLEMENTATION Rectangle::fill_with([67.0, 67.0], color::rgba(0.0, 0.0, 0.0, 0.8)) @@ -778,7 +778,7 @@ impl CharSelectionUi { .w_h(70.0, 70.0) .up_from(self.ids.hammer, 2.0) .set(self.ids.sword, ui_widgets); - if Button::image(if let Tool::Sword = self.character_weapon { + if Button::image(if let Some(Tool::Sword) = self.character_tool { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -789,7 +789,7 @@ impl CharSelectionUi { .set(self.ids.sword_button, ui_widgets) .was_clicked() { - self.character_weapon = Tool::Sword; + self.character_tool = Some(Tool::Sword); } // Daggers @@ -797,7 +797,7 @@ impl CharSelectionUi { .w_h(70.0, 70.0) .right_from(self.ids.sword, 2.0) .set(self.ids.daggers, ui_widgets); - if Button::image(if let Tool::Daggers = self.character_weapon { + if Button::image(if let Some(Tool::Daggers) = self.character_tool { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -808,7 +808,7 @@ impl CharSelectionUi { .set(self.ids.daggers_button, ui_widgets) .was_clicked() { - // self.character_weapon = Tool::Daggers; + // self.character_tool = Some(Tool::Daggers); } // REMOVE THIS AFTER IMPLEMENTATION Rectangle::fill_with([67.0, 67.0], color::rgba(0.0, 0.0, 0.0, 0.8)) .middle_of(self.ids.daggers) @@ -819,7 +819,7 @@ impl CharSelectionUi { .w_h(70.0, 70.0) .right_from(self.ids.daggers, 2.0) .set(self.ids.axe, ui_widgets); - if Button::image(if let Tool::Axe = self.character_weapon { + if Button::image(if let Some(Tool::Axe) = self.character_tool { self.imgs.icon_border_pressed } else { self.imgs.icon_border @@ -830,7 +830,7 @@ impl CharSelectionUi { .set(self.ids.axe_button, ui_widgets) .was_clicked() { - self.character_weapon = Tool::Axe; + self.character_tool = Some(Tool::Axe); } // REMOVE THIS AFTER IMPLEMENTATION /*Rectangle::fill_with([67.0, 67.0], color::rgba(0.0, 0.0, 0.0, 0.8)) diff --git a/voxygen/src/scene/figure.rs b/voxygen/src/scene/figure.rs index ece30c60ec..4c457d93a6 100644 --- a/voxygen/src/scene/figure.rs +++ b/voxygen/src/scene/figure.rs @@ -12,7 +12,9 @@ use crate::{ use client::Client; use common::{ assets, - comp::{self, humanoid, item::Tool, object, quadruped, quadruped_medium, Body}, + comp::{ + self, humanoid, item::Tool, object, quadruped, quadruped_medium, Body, Equipment, Item, + }, figure::Segment, terrain::TerrainChunkSize, vol::VolSize, @@ -26,8 +28,14 @@ use vek::*; const DAMAGE_FADE_COEFFICIENT: f64 = 5.0; +#[derive(PartialEq, Eq, Hash, Clone)] +enum FigureKey { + Simple(Body), + Complex(Body, Option), +} + pub struct FigureModelCache { - models: HashMap, SkeletonAttr), u64)>, + models: HashMap, SkeletonAttr), u64)>, } impl FigureModelCache { @@ -41,15 +49,22 @@ impl FigureModelCache { &mut self, renderer: &mut Renderer, body: Body, + equipment: Option<&Equipment>, tick: u64, ) -> &(Model, SkeletonAttr) { - match self.models.get_mut(&body) { + let key = if equipment.is_some() { + FigureKey::Complex(body, equipment.cloned()) + } else { + FigureKey::Simple(body) + }; + + match self.models.get_mut(&key) { Some((_model, last_used)) => { *last_used = tick; } None => { self.models.insert( - body, + key.clone(), ( { let bone_meshes = match body { @@ -62,7 +77,7 @@ impl FigureModelCache { Some(Self::load_right_hand(body.hand)), Some(Self::load_left_foot(body.foot)), Some(Self::load_right_foot(body.foot)), - Some(Self::load_weapon(Tool::Hammer)), // TODO: Inventory + Some(Self::load_main(equipment.and_then(|e| e.main.as_ref()))), Some(Self::load_left_shoulder(body.shoulder)), Some(Self::load_right_shoulder(body.shoulder)), Some(Self::load_draw()), @@ -151,7 +166,7 @@ impl FigureModelCache { } } - &self.models[&body].0 + &self.models[&key].0 } pub fn clean(&mut self, tick: u64) { @@ -293,17 +308,25 @@ impl FigureModelCache { ) } - fn load_weapon(weapon: Tool) -> Mesh { - let (name, offset) = match weapon { - Tool::Sword => ("weapon.sword.rusty_2h", Vec3::new(-1.5, -6.5, -4.0)), - Tool::Axe => ("weapon.axe.rusty_2h", Vec3::new(-1.5, -6.5, -4.0)), - Tool::Hammer => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)), - Tool::Daggers => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)), - Tool::SwordShield => ("weapon.axe.rusty_2h", Vec3::new(-2.5, -6.5, -2.0)), - Tool::Bow => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)), - Tool::Staff => ("weapon.axe.rusty_2h", Vec3::new(-2.5, -6.5, -2.0)), - }; - Self::load_mesh(name, offset) + fn load_main(item: Option<&Item>) -> Mesh { + if let Some(item) = item { + let (name, offset) = match item { + Item::Tool { kind, .. } => match kind { + Tool::Sword => ("weapon.sword.rusty_2h", Vec3::new(-1.5, -6.5, -4.0)), + Tool::Axe => ("weapon.axe.rusty_2h", Vec3::new(-1.5, -5.0, -4.0)), + Tool::Hammer => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)), + Tool::Daggers => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)), + Tool::SwordShield => ("weapon.axe.rusty_2h", Vec3::new(-2.5, -6.5, -2.0)), + Tool::Bow => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)), + Tool::Staff => ("weapon.axe.rusty_2h", Vec3::new(-2.5, -6.5, -2.0)), + }, + Item::Debug(_) => ("weapon.debug_wand", Vec3::new(-1.5, -9.5, -4.0)), + _ => ("figure.empty", Vec3::default()), + }; + Self::load_mesh(name, offset) + } else { + Self::load_mesh("figure.empty", Vec3::default()) + } } fn load_left_shoulder(shoulder: humanoid::Shoulder) -> Mesh { @@ -651,7 +674,7 @@ impl FigureMgr { let skeleton_attr = &self .model_cache - .get_or_create_model(renderer, *body, tick) + .get_or_create_model(renderer, *body, stats.map(|s| &s.equipment), tick) .1; match body { @@ -862,7 +885,7 @@ impl FigureMgr { let frustum = camera.frustum(client); - for (entity, _, _, _, body, _, _) in ( + for (entity, _, _, _, body, stats, _) in ( &ecs.entities(), &ecs.read_storage::(), &ecs.read_storage::(), @@ -904,7 +927,7 @@ impl FigureMgr { } { let model = &self .model_cache - .get_or_create_model(renderer, *body, tick) + .get_or_create_model(renderer, *body, stats.map(|s| &s.equipment), tick) .0; // Don't render the player's body while in first person mode diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index edf4ec56b5..af62898a32 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -129,7 +129,7 @@ impl PlayState for SessionState { Event::Close => { return PlayStateResult::Shutdown; } - Event::InputUpdate(GameInput::Main, state) => { + Event::InputUpdate(GameInput::Primary, state) => { // Check the existence of CanBuild component. If it's here, use LMB to // place blocks, if not, use it to attack let mut client = self.client.borrow_mut(); @@ -152,11 +152,11 @@ impl PlayState for SessionState { client.place_block(pos, self.selected_block); } } else { - self.controller.attack = state + self.controller.primary = state } } - Event::InputUpdate(GameInput::Alt, state) => { + Event::InputUpdate(GameInput::Secondary, state) => { let mut client = self.client.borrow_mut(); if state && client @@ -176,7 +176,7 @@ impl PlayState for SessionState { client.remove_block(pos); } } else { - self.controller.block = state; + self.controller.secondary = state; } } Event::InputUpdate(GameInput::Roll, state) => { @@ -376,6 +376,7 @@ impl PlayState for SessionState { global_state.settings.graphics.max_fps = fps; global_state.settings.save_to_file_warn(); } + HudEvent::UseInventorySlot(x) => self.client.borrow_mut().use_inventory_slot(x), HudEvent::SwapInventorySlots(a, b) => { self.client.borrow_mut().swap_inventory_slots(a, b) } diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 7ad1dcc2b6..8f46dd5d47 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -13,8 +13,8 @@ use std::{fs, io::prelude::*, path::PathBuf}; #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(default)] pub struct ControlSettings { - pub main: KeyMouse, - pub alt: KeyMouse, + pub primary: KeyMouse, + pub secondary: KeyMouse, pub toggle_cursor: KeyMouse, pub escape: KeyMouse, pub enter: KeyMouse, @@ -46,8 +46,8 @@ pub struct ControlSettings { impl Default for ControlSettings { fn default() -> Self { Self { - main: KeyMouse::Mouse(MouseButton::Left), - alt: KeyMouse::Mouse(MouseButton::Right), + primary: KeyMouse::Mouse(MouseButton::Left), + secondary: KeyMouse::Mouse(MouseButton::Right), toggle_cursor: KeyMouse::Key(VirtualKeyCode::Tab), escape: KeyMouse::Key(VirtualKeyCode::Escape), enter: KeyMouse::Key(VirtualKeyCode::Return), diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index fafc9ac23f..6676c98223 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -11,8 +11,8 @@ use vek::*; /// Represents a key that the game recognises after keyboard mapping. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub enum GameInput { - Main, - Alt, + Primary, + Secondary, ToggleCursor, MoveForward, MoveBack, @@ -109,12 +109,12 @@ impl Window { .map_err(|err| Error::BackendError(Box::new(err)))?; let mut map: HashMap<_, Vec<_>> = HashMap::new(); - map.entry(settings.controls.main) + map.entry(settings.controls.primary) .or_default() - .push(GameInput::Main); - map.entry(settings.controls.alt) + .push(GameInput::Primary); + map.entry(settings.controls.secondary) .or_default() - .push(GameInput::Alt); + .push(GameInput::Secondary); map.entry(settings.controls.toggle_cursor) .or_default() .push(GameInput::ToggleCursor);