diff --git a/assets/voxygen/voxel/object/pouch.vox b/assets/voxygen/voxel/object/pouch.vox new file mode 100644 index 0000000000..4f863db0a9 --- /dev/null +++ b/assets/voxygen/voxel/object/pouch.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:41c5c4dacb7050121ce597d73957976e7476630e0078c2d9536fe179855b8edf +size 58277 diff --git a/client/src/lib.rs b/client/src/lib.rs index 70da461859..a296cb567b 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -4,14 +4,13 @@ pub mod error; // Reexports pub use crate::error::Error; -pub use specs::join::Join; -pub use specs::Entity as EcsEntity; +pub use specs::{join::Join, saveload::Marker, Entity as EcsEntity, ReadStorage}; use common::{ comp, msg::{ClientMsg, ClientState, ServerError, ServerInfo, ServerMsg}, net::PostBox, - state::State, + state::{State, Uid}, terrain::{block::Block, chonk::ChonkMetrics, TerrainChunk, TerrainChunkSize}, vol::VolSize, ChatType, @@ -165,6 +164,21 @@ impl Client { .send_message(ClientMsg::SetViewDistance(self.view_distance.unwrap())); // Can't fail } + pub fn swap_inventory_slots(&mut self, a: usize, b: usize) { + self.postbox + .send_message(ClientMsg::SwapInventorySlots(a, b)) + } + + pub fn drop_inventory_slot(&mut self, x: usize) { + self.postbox.send_message(ClientMsg::DropInventorySlot(x)) + } + + pub fn pick_up(&mut self, entity: EcsEntity) { + if let Some(uid) = self.state.ecs().read_storage::().get(entity).copied() { + self.postbox.send_message(ClientMsg::PickUp(uid.id())); + } + } + pub fn view_distance(&self) -> Option { self.view_distance } @@ -188,6 +202,10 @@ impl Client { self.state.terrain().get_key_arc(chunk_pos).cloned() } + pub fn inventories(&self) -> ReadStorage { + self.state.read_storage() + } + /// Send a chat message to the server. #[allow(dead_code)] pub fn send_chat(&mut self, msg: String) { @@ -421,6 +439,9 @@ impl Client { self.state.write_component(entity, action_state); } } + ServerMsg::InventoryUpdate(inventory) => { + self.state.write_component(self.entity, inventory) + } ServerMsg::TerrainChunkUpdate { key, chunk } => { self.state.insert_chunk(key, *chunk); self.pending_chunks.remove(&key); diff --git a/common/src/comp/body/object.rs b/common/src/comp/body/object.rs index f45cea9d2e..0bd17f3fe7 100644 --- a/common/src/comp/body/object.rs +++ b/common/src/comp/body/object.rs @@ -48,6 +48,7 @@ pub enum Body { CarpetHumanSquare, CarpetHumanSquare2, CarpetHumanSquircle, + Pouch, } impl Body { diff --git a/common/src/comp/inventory/item.rs b/common/src/comp/inventory/item.rs index faca9b5b57..f8a38794ef 100644 --- a/common/src/comp/inventory/item.rs +++ b/common/src/comp/inventory/item.rs @@ -1,8 +1,8 @@ -use specs::Component; +use specs::{Component, FlaggedStorage}; use specs_idvs::IDVStorage; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum Weapon { +pub enum Tool { Daggers, SwordShield, Sword, @@ -11,14 +11,29 @@ pub enum Weapon { Bow, Staff, } -pub const ALL_WEAPONS: [Weapon; 7] = [ - Weapon::Daggers, - Weapon::SwordShield, - Weapon::Sword, - Weapon::Axe, - Weapon::Hammer, - Weapon::Bow, - Weapon::Staff, + +impl Tool { + pub fn name(&self) -> &'static str { + match self { + Tool::Daggers => "daggers", + Tool::SwordShield => "sword and shield", + Tool::Sword => "sword", + Tool::Axe => "axe", + Tool::Hammer => "hammer", + Tool::Bow => "bow", + Tool::Staff => "staff", + } + } +} + +pub const ALL_TOOLS: [Tool; 7] = [ + Tool::Daggers, + Tool::SwordShield, + Tool::Sword, + Tool::Axe, + Tool::Hammer, + Tool::Bow, + Tool::Staff, ]; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -37,29 +52,82 @@ pub enum Armor { Necklace, } +impl Armor { + pub fn name(&self) -> &'static str { + match self { + Armor::Helmet => "helmet", + Armor::Shoulders => "shoulder pads", + Armor::Chestplate => "chestplate", + Armor::Belt => "belt", + Armor::Gloves => "gloves", + Armor::Pants => "pants", + Armor::Boots => "boots", + Armor::Back => "back", + Armor::Tabard => "tabard", + Armor::Gem => "gem", + Armor::Necklace => "necklace", + } + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum Rarity { - Common, - Uncommon, - Rare, - Legendary, +pub enum ConsumptionEffect { + Health(i32), + Xp(i32), } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Item { - Weapon { + Tool { + kind: Tool, damage: i32, strength: i32, - rarity: Rarity, }, Armor { + kind: Armor, defense: i32, health_bonus: i32, - rarity: Rarity, - variant: Armor, }, + Consumable { + effect: ConsumptionEffect, + }, + Ingredient, +} + +impl Item { + pub fn name(&self) -> &'static str { + match self { + Item::Tool { kind, .. } => kind.name(), + Item::Armor { kind, .. } => kind.name(), + Item::Consumable { .. } => "", + Item::Ingredient => "", + } + } + + pub fn category(&self) -> &'static str { + match self { + Item::Tool { .. } => "tool", + Item::Armor { .. } => "armour", + Item::Consumable { .. } => "consumable", + Item::Ingredient => "ingredient", + } + } + + pub fn description(&self) -> String { + format!("{} ({})", self.name(), self.category()) + } +} + +impl Default for Item { + fn default() -> Self { + Item::Tool { + kind: Tool::Hammer, + damage: 0, + strength: 0, + } + } } impl Component for Item { - type Storage = IDVStorage; + type Storage = FlaggedStorage>; } diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 7843684e18..598e2f681f 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -1,11 +1,11 @@ -use specs::{Component, HashMapStorage}; -use specs_idvs::IDVStorage; - //Re-Exports pub mod item; -use item::Item; -use std::mem::swap; +// Reexports +pub use self::item::Item; + +use specs::{Component, HashMapStorage, NullStorage}; +use specs_idvs::IDVStorage; #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Inventory { @@ -13,9 +13,21 @@ pub struct Inventory { } impl Inventory { - pub fn new() -> Inventory { - Inventory { - slots: vec![None; 24], + pub fn slots(&self) -> &[Option] { + &self.slots + } + + pub fn len(&self) -> usize { + self.slots.len() + } + + pub fn insert(&mut self, item: Item) -> Option { + match self.slots.iter_mut().find(|slot| slot.is_none()) { + Some(slot) => { + *slot = Some(item); + None + } + None => Some(item), } } @@ -30,18 +42,40 @@ impl Inventory { self.slots.get_mut(cell).and_then(|cell| cell.replace(item)) } - // Remove an item from the slot - pub fn remove(&mut self, cell: usize, item: Item) -> Option { - let mut tmp_item = Some(item); + pub fn swap_slots(&mut self, a: usize, b: usize) { + if a.max(b) < self.slots.len() { + self.slots.swap(a, b); + } + } - if let Some(old_item) = self.slots.get_mut(cell) { - swap(old_item, &mut tmp_item); + // Remove an item from the slot + pub fn remove(&mut self, cell: usize) -> Option { + self.slots.get_mut(cell).and_then(|item| item.take()) + } +} + +impl Default for Inventory { + fn default() -> Inventory { + let mut this = Inventory { + slots: vec![None; 24], + }; + + for _ in 0..18 { + this.insert(Item::default()); } - tmp_item + this } } impl Component for Inventory { type Storage = HashMapStorage; } + +// ForceUpdate +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)] +pub struct InventoryUpdate; + +impl Component for InventoryUpdate { + type Storage = NullStorage; +} diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 86a948dd21..65ce76c53f 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -20,7 +20,7 @@ pub use controller::Controller; pub use inputs::{ Attacking, CanBuild, Gliding, Jumping, MoveDir, OnGround, Respawning, Rolling, Wielding, }; -pub use inventory::{item, Inventory}; +pub use inventory::{item, Inventory, InventoryUpdate, Item}; pub use last::Last; pub use phys::{ForceUpdate, Ori, Pos, Vel}; pub use player::Player; diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs index 46144c8977..977f85f21d 100644 --- a/common/src/comp/phys.rs +++ b/common/src/comp/phys.rs @@ -3,7 +3,7 @@ use specs_idvs::IDVStorage; use vek::*; // Position -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct Pos(pub Vec3); impl Component for Pos { @@ -11,7 +11,7 @@ impl Component for Pos { } // Velocity -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct Vel(pub Vec3); impl Component for Vel { @@ -19,7 +19,7 @@ impl Component for Vel { } // Orientation -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct Ori(pub Vec3); impl Component for Ori { diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs index 6f3919159b..cc325415d8 100644 --- a/common/src/msg/client.rs +++ b/common/src/msg/client.rs @@ -28,6 +28,9 @@ pub enum ClientMsg { vel: comp::Vel, ori: comp::Ori, }, + SwapInventorySlots(usize, usize), + DropInventorySlot(usize), + PickUp(u64), TerrainChunkRequest { key: Vec2, }, diff --git a/common/src/msg/ecs_packet.rs b/common/src/msg/ecs_packet.rs index b2f3d8bbbe..56bf63c68a 100644 --- a/common/src/msg/ecs_packet.rs +++ b/common/src/msg/ecs_packet.rs @@ -25,6 +25,7 @@ sphynx::sum_type! { CanBuild(comp::CanBuild), Stats(comp::Stats), LightEmitter(comp::LightEmitter), + Item(comp::Item), } } // Automatically derive From for EcsCompPhantom @@ -40,6 +41,7 @@ sphynx::sum_type! { CanBuild(PhantomData), Stats(PhantomData), LightEmitter(PhantomData), + Item(PhantomData), } } impl sphynx::CompPacket for EcsCompPacket { diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index 26a0f4f248..8686e6f089 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -55,6 +55,7 @@ pub enum ServerMsg { entity: u64, action_state: comp::ActionState, }, + InventoryUpdate(comp::Inventory), TerrainChunkUpdate { key: Vec2, chunk: Box, diff --git a/common/src/state.rs b/common/src/state.rs index 9269d6e101..ee8bcb7d2e 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -131,6 +131,7 @@ impl State { ecs.register_synced::(); ecs.register_synced::(); ecs.register_synced::(); + ecs.register_synced::(); // Register components send from clients -> server ecs.register::(); @@ -142,6 +143,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); + ecs.register::(); // Register client-local components ecs.register::(); @@ -156,6 +158,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); + ecs.register::(); ecs.register::(); // Controller effects ecs.register::(); diff --git a/common/src/sys/movement.rs b/common/src/sys/movement.rs index a1744d9fa6..f278655d72 100644 --- a/common/src/sys/movement.rs +++ b/common/src/sys/movement.rs @@ -11,7 +11,7 @@ const HUMANOID_ACCEL: f32 = 70.0; const HUMANOID_SPEED: f32 = 120.0; const HUMANOID_AIR_ACCEL: f32 = 10.0; const HUMANOID_AIR_SPEED: f32 = 100.0; -const HUMANOID_JUMP_ACCEL: f32 = 16.5; +const HUMANOID_JUMP_ACCEL: f32 = 18.0; const ROLL_ACCEL: f32 = 120.0; const ROLL_SPEED: f32 = 550.0; const GLIDE_ACCEL: f32 = 15.0; diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index f196a29f9c..1eaf57693a 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -16,8 +16,8 @@ const FRIC_AIR: f32 = 0.015; // lv = linear velocity // damp = linear damping // Friction is a type of damping. -fn integrate_forces(dt: f32, mut lv: Vec3, damp: f32) -> Vec3 { - lv.z = (lv.z - GRAVITY * dt).max(-50.0); +fn integrate_forces(dt: f32, mut lv: Vec3, grav: f32, damp: f32) -> Vec3 { + lv.z = (lv.z - grav * dt).max(-50.0); let linear_damp = (1.0 - dt * damp).max(0.0); @@ -63,8 +63,13 @@ impl<'a> System<'a> for Sys { { // Integrate forces // Friction is assumed to be a constant dependent on location - let friction = 50.0 * if a.on_ground { FRIC_GROUND } else { FRIC_AIR }; - vel.0 = integrate_forces(dt.0, vel.0, friction); + let friction = 50.0 + * if on_grounds.get(entity).is_some() { + FRIC_GROUND + } else { + FRIC_AIR + }; + vel.0 = integrate_forces(dt.0, vel.0, GRAVITY, friction); // Basic collision with terrain let player_rad = 0.3f32; // half-width of the player's AABB @@ -126,6 +131,7 @@ impl<'a> System<'a> for Sys { let increments = (pos_delta.map(|e| e.abs()).reduce_partial_max() / 0.3) .ceil() .max(1.0); + let old_pos = pos.0; for _ in 0..increments as usize { pos.0 += pos_delta / increments; @@ -197,12 +203,12 @@ impl<'a> System<'a> for Sys { && -dir.z > 0.1 // ...and we're falling/standing OR there is a block *directly* beneath our current origin (note: not hitbox)... && (vel.0.z <= 0.0 || terrain - .get((pos.0 - Vec3::unit_z()).map(|e| e.floor() as i32)) + .get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32)) .map(|vox| !vox.is_empty()) .unwrap_or(false)) // ...and there is a collision with a block beneath our current hitbox... && collision_with( - pos.0 + resolve_dir - Vec3::unit_z() * 1.05, + old_pos + resolve_dir - Vec3::unit_z() * 1.05, near_iter.clone(), ) { diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 38e34ef03c..93cf4069d7 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -547,7 +547,7 @@ fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action: } }; server - .create_object(pos, ori, obj_type) + .create_object(pos, obj_type) .with(comp::Ori( // converts player orientation into a 90° rotation for the object by using the axis with the highest value ori.0 diff --git a/server/src/lib.rs b/server/src/lib.rs index b467289571..346099ea9f 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -23,6 +23,7 @@ use common::{ vol::Vox, }; use log::debug; +use rand::Rng; use specs::{join::Join, world::EntityBuilder as EcsEntityBuilder, Builder, Entity as EcsEntity}; use std::{ collections::HashSet, @@ -155,14 +156,12 @@ impl Server { pub fn create_object( &mut self, pos: comp::Pos, - ori: comp::Ori, object: comp::object::Body, ) -> EcsEntityBuilder { self.state .ecs_mut() .create_entity_synced() .with(pos) - .with(ori) .with(comp::Vel(Vec3::zero())) .with(comp::Ori(Vec3::unit_y())) .with(comp::Body::Object(object)) @@ -191,6 +190,8 @@ impl Server { state.write_component(entity, comp::Vel(Vec3::zero())); state.write_component(entity, comp::Ori(Vec3::unit_y())); state.write_component(entity, comp::ActionState::default()); + state.write_component(entity, comp::Inventory::default()); + state.write_component(entity, comp::InventoryUpdate); // Make sure physics are accepted. state.write_component(entity, comp::ForceUpdate); @@ -417,6 +418,7 @@ impl Server { let mut disconnected_clients = Vec::new(); let mut requested_chunks = Vec::new(); let mut modified_blocks = Vec::new(); + let mut dropped_items = Vec::new(); self.clients.remove_if(|entity, client| { let mut disconnect = false; @@ -488,6 +490,66 @@ impl Server { } _ => {} }, + ClientMsg::SwapInventorySlots(a, b) => { + state + .ecs() + .write_storage::() + .get_mut(entity) + .map(|inv| inv.swap_slots(a, b)); + state.write_component(entity, comp::InventoryUpdate); + } + ClientMsg::DropInventorySlot(x) => { + let item = state + .ecs() + .write_storage::() + .get_mut(entity) + .and_then(|inv| inv.remove(x)); + + state.write_component(entity, comp::InventoryUpdate); + + if let (Some(item), Some(pos)) = + (item, state.ecs().read_storage::().get(entity)) + { + dropped_items.push(( + *pos, + state + .ecs() + .read_storage::() + .get(entity) + .copied() + .unwrap_or(comp::Ori(Vec3::unit_y())), + item, + )); + } + } + ClientMsg::PickUp(uid) => { + let item_entity = state.ecs_mut().entity_from_uid(uid); + + let ecs = state.ecs_mut(); + + let item_entity = if let (Some((item, item_entity)), Some(inv)) = ( + item_entity.and_then(|item_entity| { + ecs.write_storage::() + .get_mut(item_entity) + .map(|item| (*item, item_entity)) + }), + ecs.write_storage::().get_mut(entity), + ) { + if inv.insert(item).is_none() { + Some(item_entity) + } else { + None + } + } else { + None + }; + + if let Some(item_entity) = item_entity { + let _ = ecs.delete_entity_synced(item_entity); + } + + state.write_component(entity, comp::InventoryUpdate); + } ClientMsg::Character { name, body } => match client.client_state { // Become Registered first. ClientState::Connected => { @@ -664,6 +726,17 @@ impl Server { self.state.set_block(pos, block); } + for (pos, ori, item) in dropped_items { + let vel = ori.0.normalized() * 5.0 + + Vec3::unit_z() * 10.0 + + Vec3::::zero().map(|_| rand::thread_rng().gen::() - 0.5) * 4.0; + self.create_object(Default::default(), comp::object::Body::Pouch) + .with(comp::Pos(pos.0 + Vec3::unit_z() * 0.25)) + .with(item) + .with(comp::Vel(vel)) + .build(); + } + Ok(frontend_events) } @@ -919,11 +992,27 @@ impl Server { } } + // Sync inventories + for (entity, inventory, _) in ( + &self.state.ecs().entities(), + &self.state.ecs().read_storage::(), + &self.state.ecs().read_storage::(), + ) + .join() + { + self.clients + .notify(entity, ServerMsg::InventoryUpdate(inventory.clone())); + } + // Remove all force flags. self.state .ecs_mut() .write_storage::() .clear(); + self.state + .ecs_mut() + .write_storage::() + .clear(); } pub fn generate_chunk(&mut self, key: Vec2) { diff --git a/voxygen/shaders/terrain.frag b/voxygen/shaders/terrain.frag index 03981a2cb3..75bc338d6f 100644 --- a/voxygen/shaders/terrain.frag +++ b/voxygen/shaders/terrain.frag @@ -30,7 +30,7 @@ void main() { f_norm = vec3(0.0, 0.0, 1.0) * norm_dir; } - vec3 light = (get_sun_diffuse(f_norm, time_of_day.x) + light_at(f_pos, f_norm)) * f_light; + vec3 light = get_sun_diffuse(f_norm, time_of_day.x) * f_light + light_at(f_pos, f_norm); vec3 surf_color = f_col * light; float fog_level = fog(f_pos.xy, focus_pos.xy); diff --git a/voxygen/src/anim/character/cidle.rs b/voxygen/src/anim/character/cidle.rs index 1c7026dacb..c21ce75f9b 100644 --- a/voxygen/src/anim/character/cidle.rs +++ b/voxygen/src/anim/character/cidle.rs @@ -2,7 +2,7 @@ use super::{ super::{Animation, SkeletonAttr}, CharacterSkeleton, }; -use common::comp::item::Weapon; +use common::comp::item::Tool; use std::{f32::consts::PI, ops::Mul}; use vek::*; @@ -61,9 +61,9 @@ impl Animation for CidleAnimation { next.shorts.ori = Quaternion::rotation_x(0.0); next.shorts.scale = Vec3::one(); - match Weapon::Hammer { + match Tool::Hammer { //TODO: Inventory - Weapon::Sword => { + Tool::Sword => { next.l_hand.offset = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.5 + wave_ultra_slow_cos * 0.5, @@ -88,7 +88,7 @@ impl Animation for CidleAnimation { * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); } - Weapon::Axe => { + Tool::Axe => { next.l_hand.offset = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.5 + wave_ultra_slow_cos * 0.5, @@ -113,7 +113,7 @@ impl Animation for CidleAnimation { * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); } - Weapon::Hammer => { + Tool::Hammer => { next.l_hand.offset = Vec3::new(-7.0, 8.25, 2.0); next.l_hand.ori = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.2) @@ -134,7 +134,7 @@ impl Animation for CidleAnimation { * Quaternion::rotation_z(wave_ultra_slow * 0.2); next.weapon.scale = Vec3::one(); } - Weapon::Staff => { + Tool::Staff => { next.l_hand.offset = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.5 + wave_ultra_slow_cos * 0.5, @@ -159,7 +159,7 @@ impl Animation for CidleAnimation { * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); } - Weapon::SwordShield => { + Tool::SwordShield => { next.l_hand.offset = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.5 + wave_ultra_slow_cos * 0.5, @@ -184,7 +184,7 @@ impl Animation for CidleAnimation { * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); } - Weapon::Bow => { + Tool::Bow => { next.l_hand.offset = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.5 + wave_ultra_slow_cos * 0.5, @@ -209,7 +209,7 @@ impl Animation for CidleAnimation { * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); } - Weapon::Daggers => { + Tool::Daggers => { next.l_hand.offset = Vec3::new( -6.0 + wave_ultra_slow_cos * 1.0, 3.5 + wave_ultra_slow_cos * 0.5, diff --git a/voxygen/src/anim/character/cjump.rs b/voxygen/src/anim/character/cjump.rs index d464f40498..33803d7e70 100644 --- a/voxygen/src/anim/character/cjump.rs +++ b/voxygen/src/anim/character/cjump.rs @@ -2,7 +2,7 @@ use super::{ super::{Animation, SkeletonAttr}, CharacterSkeleton, }; -use common::comp::item::Weapon; +use common::comp::item::Tool; use std::f32::consts::PI; use vek::*; @@ -43,9 +43,9 @@ impl Animation for CjumpAnimation { next.shorts.ori = Quaternion::rotation_z(0.0); next.shorts.scale = Vec3::one(); - match Weapon::Hammer { + match Tool::Hammer { //TODO: Inventory - Weapon::Sword => { + Tool::Sword => { next.l_hand.offset = Vec3::new(-7.0, 3.25, 0.25 + wave_stop * 2.0); next.l_hand.ori = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; @@ -62,7 +62,7 @@ impl Animation for CjumpAnimation { * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); } - Weapon::Axe => { + Tool::Axe => { next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); next.l_hand.ori = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; @@ -79,7 +79,7 @@ impl Animation for CjumpAnimation { * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); } - Weapon::Hammer => { + Tool::Hammer => { next.l_hand.offset = Vec3::new(-7.0, 8.25, 2.0 + wave_stop * 2.0); next.l_hand.ori = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.2) @@ -100,7 +100,7 @@ impl Animation for CjumpAnimation { * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); } - Weapon::Staff => { + Tool::Staff => { next.l_hand.offset = Vec3::new(-7.0, 7.5, 0.0); next.l_hand.ori = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.7) @@ -121,7 +121,7 @@ impl Animation for CjumpAnimation { * Quaternion::rotation_z(1.0); next.weapon.scale = Vec3::one(); } - Weapon::SwordShield => { + Tool::SwordShield => { next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); next.l_hand.ori = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; @@ -138,7 +138,7 @@ impl Animation for CjumpAnimation { * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); } - Weapon::Bow => { + Tool::Bow => { next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); next.l_hand.ori = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; @@ -155,7 +155,7 @@ impl Animation for CjumpAnimation { * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); } - Weapon::Daggers => { + Tool::Daggers => { next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); next.l_hand.ori = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; diff --git a/voxygen/src/anim/character/crun.rs b/voxygen/src/anim/character/crun.rs index fcb2d617b4..a4c28bf17a 100644 --- a/voxygen/src/anim/character/crun.rs +++ b/voxygen/src/anim/character/crun.rs @@ -2,7 +2,7 @@ use super::{ super::{Animation, SkeletonAttr}, CharacterSkeleton, }; -use common::comp::item::Weapon; +use common::comp::item::Tool; use std::f32::consts::PI; use std::ops::Mul; use vek::*; @@ -61,9 +61,9 @@ impl Animation for CrunAnimation { next.shorts.ori = Quaternion::rotation_z(wave * 0.6); next.shorts.scale = Vec3::one(); - match Weapon::Hammer { + match Tool::Hammer { //TODO: Inventory - Weapon::Sword => { + Tool::Sword => { next.l_hand.offset = Vec3::new(-6.0, 3.75, 0.25); next.l_hand.ori = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; @@ -80,7 +80,7 @@ impl Animation for CrunAnimation { * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); } - Weapon::Axe => { + Tool::Axe => { next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); next.l_hand.ori = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; @@ -97,7 +97,7 @@ impl Animation for CrunAnimation { * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); } - Weapon::Hammer => { + Tool::Hammer => { next.l_hand.offset = Vec3::new(-7.0, 8.25, 3.0); next.l_hand.ori = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(-1.2) @@ -118,7 +118,7 @@ impl Animation for CrunAnimation { * Quaternion::rotation_z(wave * -0.25); next.weapon.scale = Vec3::one(); } - Weapon::Staff => { + Tool::Staff => { next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); next.l_hand.ori = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; @@ -135,7 +135,7 @@ impl Animation for CrunAnimation { * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); } - Weapon::SwordShield => { + Tool::SwordShield => { next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); next.l_hand.ori = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; @@ -152,7 +152,7 @@ impl Animation for CrunAnimation { * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); } - Weapon::Bow => { + Tool::Bow => { next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); next.l_hand.ori = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; @@ -169,7 +169,7 @@ impl Animation for CrunAnimation { * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); } - Weapon::Daggers => { + Tool::Daggers => { next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); next.l_hand.ori = Quaternion::rotation_x(-0.3); next.l_hand.scale = Vec3::one() * 1.01; diff --git a/voxygen/src/anim/mod.rs b/voxygen/src/anim/mod.rs index 96df4e6cb7..94eb60c25b 100644 --- a/voxygen/src/anim/mod.rs +++ b/voxygen/src/anim/mod.rs @@ -5,7 +5,7 @@ pub mod quadruped; pub mod quadrupedmedium; use crate::render::FigureBoneData; -use common::comp::{self, item::Weapon}; +use common::comp::{self, item::Tool}; use vek::*; #[derive(Copy, Clone)] @@ -145,25 +145,25 @@ impl<'a> From<&'a comp::humanoid::Body> for SkeletonAttr { (Danari, Male) => 0.0, (Danari, Female) => 0.0, }, - weapon_x: match Weapon::Hammer { + weapon_x: match Tool::Hammer { // TODO: Inventory - Weapon::Sword => 0.0, - Weapon::Axe => 3.0, - Weapon::Hammer => 0.0, - Weapon::SwordShield => 3.0, - Weapon::Staff => 3.0, - Weapon::Bow => 0.0, - Weapon::Daggers => 0.0, + Tool::Sword => 0.0, + Tool::Axe => 3.0, + Tool::Hammer => 0.0, + Tool::SwordShield => 3.0, + Tool::Staff => 3.0, + Tool::Bow => 0.0, + Tool::Daggers => 0.0, }, - weapon_y: match Weapon::Hammer { + weapon_y: match Tool::Hammer { // TODO: Inventory - Weapon::Sword => -1.25, - Weapon::Axe => 0.0, - Weapon::Hammer => -2.0, - Weapon::SwordShield => 0.0, - Weapon::Staff => 0.0, - Weapon::Bow => -2.0, - Weapon::Daggers => -2.0, + Tool::Sword => -1.25, + Tool::Axe => 0.0, + Tool::Hammer => -2.0, + Tool::SwordShield => 0.0, + Tool::Staff => 0.0, + Tool::Bow => -2.0, + Tool::Daggers => -2.0, }, } } diff --git a/voxygen/src/hud/bag.rs b/voxygen/src/hud/bag.rs index c0a7f86188..749732e001 100644 --- a/voxygen/src/hud/bag.rs +++ b/voxygen/src/hud/bag.rs @@ -1,4 +1,5 @@ -use super::{img_ids::Imgs, Fonts, TEXT_COLOR}; +use super::{img_ids::Imgs, Event as HudEvent, Fonts, TEXT_COLOR}; +use client::Client; use conrod_core::{ color, position::Relative, @@ -16,17 +17,16 @@ widget_ids! { inv_grid_1, inv_grid_2, inv_scrollbar, - inv_slot_0, + inv_slots_0, map_title, - inv_slot[], - item1, + inv_slots[], + items[], } } #[derive(WidgetCommon)] pub struct Bag<'a> { - inventory_space: usize, - + client: &'a Client, imgs: &'a Imgs, fonts: &'a Fonts, #[conrod(common_builder)] @@ -34,9 +34,9 @@ pub struct Bag<'a> { } impl<'a> Bag<'a> { - pub fn new(inventory_space: usize, imgs: &'a Imgs, fonts: &'a Fonts) -> Self { + pub fn new(client: &'a Client, imgs: &'a Imgs, fonts: &'a Fonts) -> Self { Self { - inventory_space, + client, imgs, fonts, common: widget::CommonBuilder::default(), @@ -46,11 +46,13 @@ impl<'a> Bag<'a> { pub struct State { ids: Ids, + selected_slot: Option, } const BAG_SCALE: f64 = 4.0; pub enum Event { + HudEvent(HudEvent), Close, } @@ -62,6 +64,7 @@ impl<'a> Widget for Bag<'a> { fn init_state(&self, id_gen: widget::id::Generator) -> Self::State { State { ids: Ids::new(id_gen), + selected_slot: None, } } @@ -72,87 +75,115 @@ impl<'a> Widget for Bag<'a> { fn update(self, args: widget::UpdateArgs) -> Self::Event { let widget::UpdateArgs { state, ui, .. } = args; + let mut event = None; + + let invs = self.client.inventories(); + let inventory = match invs.get(self.client.entity()) { + Some(inv) => inv, + None => return None, + }; + // Bag parts Image::new(self.imgs.bag_bot) .w_h(61.0 * BAG_SCALE, 9.0 * BAG_SCALE) .bottom_right_with_margins_on(ui.window, 60.0, 5.0) .set(state.ids.bag_bot, ui); - Image::new(self.imgs.bag_mid) - .w_h( - 61.0 * BAG_SCALE, - ((self.inventory_space + 4) / 5) as f64 * 44.0, - ) - .up_from(state.ids.bag_bot, 0.0) - .set(state.ids.bag_mid, ui); Image::new(self.imgs.bag_top) .w_h(61.0 * BAG_SCALE, 9.0 * BAG_SCALE) .up_from(state.ids.bag_mid, 0.0) .set(state.ids.bag_top, ui); + Image::new(self.imgs.bag_mid) + .w_h(61.0 * BAG_SCALE, ((inventory.len() + 4) / 5) as f64 * 44.0) + .up_from(state.ids.bag_bot, 0.0) + .set(state.ids.bag_mid, ui); // Alignment for Grid Rectangle::fill_with( - [ - 54.0 * BAG_SCALE, - ((self.inventory_space + 4) / 5) as f64 * 44.0, - ], + [54.0 * BAG_SCALE, ((inventory.len() + 4) / 5) as f64 * 44.0], color::TRANSPARENT, ) .top_left_with_margins_on(state.ids.bag_top, 9.0 * BAG_SCALE, 3.0 * BAG_SCALE) .scroll_kids() .scroll_kids_vertically() .set(state.ids.inv_alignment, ui); - - // Grid - /*Image::new(self.imgs.inv_grid) - .w_h(61.0 * BAG_SCALE, 111.0 * BAG_SCALE) - .color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.5))) - .mid_top_with_margin_on(state.ids.inv_alignment, 0.0) - .set(state.ids.inv_grid_1, ui); - Image::new(self.imgs.inv_grid) - .w_h(61.0 * BAG_SCALE, 111.0 * BAG_SCALE) - .color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.5))) - .mid_top_with_margin_on(state.ids.inv_alignment, 110.0 * BAG_SCALE) - .set(state.ids.inv_grid_2, ui); - Scrollbar::y_axis(state.ids.inv_alignment) - .thickness(5.0) - .rgba(0.33, 0.33, 0.33, 1.0) - .set(state.ids.inv_scrollbar, ui);*/ // Create available inventory slot widgets - if state.ids.inv_slot.len() < self.inventory_space { + + if state.ids.inv_slots.len() < inventory.len() { state.update(|s| { s.ids - .inv_slot - .resize(self.inventory_space, &mut ui.widget_id_generator()); + .inv_slots + .resize(inventory.len(), &mut ui.widget_id_generator()); }); } - // "Allowed" max. inventory space should be handled serverside and thus isn't limited in the UI - for i in 0..self.inventory_space { + + if state.ids.items.len() < inventory.len() { + state.update(|s| { + s.ids + .items + .resize(inventory.len(), &mut ui.widget_id_generator()); + }); + } + + // Display inventory contents + + for (i, item) in inventory.slots().iter().enumerate() { let x = i % 5; let y = i / 5; - Button::image(self.imgs.inv_slot) + + let is_selected = Some(i) == state.selected_slot; + + // Slot + if Button::image(self.imgs.inv_slot) .top_left_with_margins_on( state.ids.inv_alignment, 4.0 + y as f64 * (40.0 + 4.0), 4.0 + x as f64 * (40.0 + 4.0), ) // conrod uses a (y,x) format for placing... - .parent(state.ids.bag_mid) // Avoids the background overlapping available slots + .parent(state.ids.inv_alignment) // Avoids the background overlapping available slots .w_h(40.0, 40.0) - .set(state.ids.inv_slot[i], ui); + .image_color(if is_selected { + color::WHITE + } else { + color::DARK_YELLOW + }) + .floating(true) + .set(state.ids.inv_slots[i], ui) + .was_clicked() + { + let selected_slot = match state.selected_slot { + Some(a) => { + if a == i { + event = Some(Event::HudEvent(HudEvent::DropInventorySlot(i))); + } else { + event = Some(Event::HudEvent(HudEvent::SwapInventorySlots(a, i))); + } + None + } + None if item.is_some() => Some(i), + None => None, + }; + state.update(|s| s.selected_slot = selected_slot); + } + + // Item + if item.is_some() { + Button::image(self.imgs.potion_red) // TODO: Insert variable image depending on the item displayed in that slot + .w_h(4.0 * 4.4, 7.0 * 4.4) // TODO: Fix height and scale width correctly to that to avoid a stretched item image + .middle_of(state.ids.inv_slots[i]) // TODO: Items need to be assigned to a certain slot and then placed like in this example + .label("5x") // TODO: Quantity goes here... + .label_font_id(self.fonts.opensans) + .label_font_size(12) + .label_x(Relative::Scalar(10.0)) + .label_y(Relative::Scalar(-10.0)) + .label_color(TEXT_COLOR) + .parent(state.ids.inv_slots[i]) + .graphics_for(state.ids.inv_slots[i]) + .set(state.ids.items[i], ui); + } } - // Test Item - if self.inventory_space > 0 { - Button::image(self.imgs.potion_red) // TODO: Insert variable image depending on the item displayed in that slot - .w_h(4.0 * 4.4, 7.0 * 4.4) // TODO: Fix height and scale width correctly to that to avoid a stretched item image - .middle_of(state.ids.inv_slot[0]) // TODO: Items need to be assigned to a certain slot and then placed like in this example - .label("5x") // TODO: Quantity goes here... - .label_font_id(self.fonts.opensans) - .label_font_size(12) - .label_x(Relative::Scalar(10.0)) - .label_y(Relative::Scalar(-10.0)) - .label_color(TEXT_COLOR) - .set(state.ids.item1, ui); // TODO: Add widget_id generator for displayed items - } - // X-button + + // Close button + if Button::image(self.imgs.close_button) .w_h(28.0, 28.0) .hover_image(self.imgs.close_button_hover) @@ -161,9 +192,9 @@ impl<'a> Widget for Bag<'a> { .set(state.ids.bag_close, ui) .was_clicked() { - Some(Event::Close) - } else { - None + event = Some(Event::Close); } + + event } } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 689f1f5668..1a52b1192e 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -141,6 +141,8 @@ pub enum Event { CrosshairType(CrosshairType), UiScale(ScaleChange), CharacterSelection, + SwapInventorySlots(usize, usize), + DropInventorySlot(usize), Logout, Quit, } @@ -588,9 +590,8 @@ impl Hud { // Bag contents if self.show.bag { - match Bag::new(self.inventory_space, &self.imgs, &self.fonts) - .set(self.ids.bag, ui_widgets) - { + match Bag::new(client, &self.imgs, &self.fonts).set(self.ids.bag, ui_widgets) { + Some(bag::Event::HudEvent(event)) => events.push(event), Some(bag::Event::Close) => { self.show.bag(false); self.force_ungrab = true; diff --git a/voxygen/src/menu/char_selection/ui.rs b/voxygen/src/menu/char_selection/ui.rs index 225e96e504..700532ece5 100644 --- a/voxygen/src/menu/char_selection/ui.rs +++ b/voxygen/src/menu/char_selection/ui.rs @@ -8,7 +8,7 @@ use crate::{ GlobalState, }; use client::Client; -use common::comp::{humanoid, item::Weapon}; +use common::comp::{humanoid, item::Tool}; use conrod_core::{ color, color::TRANSPARENT, @@ -109,7 +109,7 @@ widget_ids! { body_type_1, body_type_2, - // Weapons + // Tools sword, sword_button, daggers, @@ -152,7 +152,7 @@ image_ids! { slider_range: "voxygen/element/slider/track.png", slider_indicator: "voxygen/element/slider/indicator.png", - // Weapon Icons + // Tool Icons daggers: "voxygen/element/icons/daggers.png", sword: "voxygen/element/icons/sword.png", axe: "voxygen/element/icons/axe.png", @@ -209,7 +209,7 @@ pub struct CharSelectionUi { character_creation: bool, pub character_name: String, pub character_body: humanoid::Body, - pub character_weapon: Weapon, // TODO: Move into ecs inventory struct? + pub character_weapon: Tool, // TODO: Move into ecs inventory struct? } impl CharSelectionUi { @@ -235,7 +235,7 @@ impl CharSelectionUi { character_creation: false, character_name: "Character Name".to_string(), character_body: humanoid::Body::random(), - character_weapon: Weapon::Sword, + character_weapon: Tool::Sword, } } @@ -344,7 +344,7 @@ impl CharSelectionUi { .was_clicked() { self.character_creation = true; - self.character_weapon = Weapon::Sword; + self.character_weapon = Tool::Sword; } // Alpha Version @@ -553,7 +553,7 @@ impl CharSelectionUi { self.character_body.body_type = humanoid::BodyType::Female; } - // Alignment for Races and Weapons + // Alignment for Races and Tools Rectangle::fill_with([214.0, 304.0], color::TRANSPARENT) .mid_bottom_with_margin_on(self.ids.creation_buttons_alignment_1, -324.0) .set(self.ids.creation_buttons_alignment_2, ui_widgets); @@ -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 Weapon::Hammer = self.character_weapon { + if Button::image(if let Tool::Hammer = self.character_weapon { 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 = Weapon::Hammer; + self.character_weapon = 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 Weapon::Bow = self.character_weapon { + if Button::image(if let Tool::Bow = self.character_weapon { 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 = Weapon::Bow; + //self.character_weapon = 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 Weapon::Staff = self.character_weapon { + if Button::image(if let Tool::Staff = self.character_weapon { 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 = Weapon::Staff; + //self.character_weapon = 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 Weapon::Sword = self.character_weapon { + if Button::image(if let Tool::Sword = self.character_weapon { 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 = Weapon::Sword; + self.character_weapon = 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 Weapon::Daggers = self.character_weapon { + if Button::image(if let Tool::Daggers = self.character_weapon { 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 = Weapon::Daggers; + // self.character_weapon = 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 Weapon::Axe = self.character_weapon { + if Button::image(if let Tool::Axe = self.character_weapon { 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 = Weapon::Axe; + self.character_weapon = 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 47ecbb8219..8a8d800b83 100644 --- a/voxygen/src/scene/figure.rs +++ b/voxygen/src/scene/figure.rs @@ -12,7 +12,7 @@ use crate::{ use client::Client; use common::{ assets, - comp::{self, humanoid, item::Weapon, object, quadruped, quadruped_medium, Body}, + comp::{self, humanoid, item::Tool, object, quadruped, quadruped_medium, Body}, figure::Segment, terrain::TerrainChunkSize, vol::VolSize, @@ -61,7 +61,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(Weapon::Hammer)), // TODO: Inventory + Some(Self::load_weapon(Tool::Hammer)), // TODO: Inventory Some(Self::load_left_shoulder(body.shoulder)), Some(Self::load_right_shoulder(body.shoulder)), Some(Self::load_draw()), @@ -312,15 +312,15 @@ impl FigureModelCache { ) } - fn load_weapon(weapon: Weapon) -> Mesh { + fn load_weapon(weapon: Tool) -> Mesh { let (name, offset) = match weapon { - Weapon::Sword => ("weapon/sword/rusty_2h.vox", Vec3::new(-1.5, -6.5, -4.0)), - Weapon::Axe => ("weapon/axe/rusty_2h.vox", Vec3::new(-1.5, -6.5, -4.0)), - Weapon::Hammer => ("weapon/hammer/rusty_2h.vox", Vec3::new(-2.5, -5.5, -4.0)), - Weapon::Daggers => ("weapon/hammer/rusty_2h.vox", Vec3::new(-2.5, -5.5, -4.0)), - Weapon::SwordShield => ("weapon/axe/rusty_2h.vox", Vec3::new(-2.5, -6.5, -2.0)), - Weapon::Bow => ("weapon/hammer/rusty_2h.vox", Vec3::new(-2.5, -5.5, -4.0)), - Weapon::Staff => ("weapon/axe/rusty_2h.vox", Vec3::new(-2.5, -6.5, -2.0)), + Tool::Sword => ("weapon/sword/rusty_2h.vox", Vec3::new(-1.5, -6.5, -4.0)), + Tool::Axe => ("weapon/axe/rusty_2h.vox", Vec3::new(-1.5, -6.5, -4.0)), + Tool::Hammer => ("weapon/hammer/rusty_2h.vox", Vec3::new(-2.5, -5.5, -4.0)), + Tool::Daggers => ("weapon/hammer/rusty_2h.vox", Vec3::new(-2.5, -5.5, -4.0)), + Tool::SwordShield => ("weapon/axe/rusty_2h.vox", Vec3::new(-2.5, -6.5, -2.0)), + Tool::Bow => ("weapon/hammer/rusty_2h.vox", Vec3::new(-2.5, -5.5, -4.0)), + Tool::Staff => ("weapon/axe/rusty_2h.vox", Vec3::new(-2.5, -6.5, -2.0)), }; Self::load_mesh(name, offset) } @@ -583,6 +583,7 @@ impl FigureModelCache { "object/carpet_human_squircle.vox", Vec3::new(-21.0, -21.0, -0.5), ), + object::Body::Pouch => ("object/pouch.vox", Vec3::new(-5.5, -4.5, 0.0)), }; Self::load_mesh(name, offset) } @@ -970,8 +971,13 @@ impl FigureState { dt: f32, ) { // Update interpolation values - self.pos = Lerp::lerp(self.pos, pos, 15.0 * dt); - self.ori = Slerp::slerp(self.ori, ori, 7.5 * dt); + if self.pos.distance_squared(pos) < 64.0 * 64.0 { + self.pos = Lerp::lerp(self.pos, pos, 15.0 * dt); + self.ori = Slerp::slerp(self.ori, ori, 7.5 * dt); + } else { + self.pos = pos; + self.ori = ori; + } let mat = Mat4::::identity() * Mat4::translation_3d(self.pos) diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 7279c89470..647d83675a 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -9,6 +9,7 @@ use crate::{ use client::{self, Client}; use common::{clock::Clock, comp, comp::Pos, msg::ClientState, terrain::Block, vol::ReadVol}; use log::error; +use specs::Join; use std::{cell::RefCell, rc::Rc, time::Duration}; use vek::*; @@ -212,6 +213,35 @@ impl PlayState for SessionState { Event::InputUpdate(GameInput::Glide, state) => { self.controller.glide = state; } + Event::InputUpdate(GameInput::Interact, state) => { + let mut client = self.client.borrow_mut(); + + let player_pos = client + .state() + .read_storage::() + .get(client.entity()) + .copied(); + + if let (Some(player_pos), true) = (player_pos, state) { + let entity = ( + &client.state().ecs().entities(), + &client.state().ecs().read_storage::(), + &client.state().ecs().read_storage::(), + ) + .join() + .filter(|(_, pos, _)| { + pos.0.distance_squared(player_pos.0) < 3.0 * 3.0 + }) + .min_by_key(|(_, pos, _)| { + (pos.0.distance_squared(player_pos.0) * 1000.0) as i32 + }) + .map(|(entity, _, _)| entity); + + if let Some(entity) = entity { + client.pick_up(entity); + } + } + } // Pass all other events to the scene event => { @@ -318,6 +348,12 @@ impl PlayState for SessionState { global_state.settings.graphics.max_fps = fps; global_state.settings.save_to_file_warn(); } + HudEvent::SwapInventorySlots(a, b) => { + self.client.borrow_mut().swap_inventory_slots(a, b) + } + HudEvent::DropInventorySlot(x) => { + self.client.borrow_mut().drop_inventory_slot(x) + } } } diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 8995241ffc..938e77bb82 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -35,6 +35,7 @@ pub struct ControlSettings { pub attack: KeyMouse, pub second_attack: KeyMouse, pub roll: KeyMouse, + pub interact: KeyMouse, } impl Default for ControlSettings { @@ -66,6 +67,7 @@ impl Default for ControlSettings { attack: KeyMouse::Mouse(MouseButton::Left), second_attack: KeyMouse::Mouse(MouseButton::Right), roll: KeyMouse::Mouse(MouseButton::Middle), + interact: KeyMouse::Key(VirtualKeyCode::E), } } } diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 88dff356f8..a7b5dc2266 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -38,6 +38,7 @@ pub enum GameInput { SecondAttack, Roll, Respawn, + Interact, } /// Represents an incoming event from the window. @@ -143,6 +144,7 @@ impl Window { key_map.insert(settings.controls.attack, GameInput::Attack); key_map.insert(settings.controls.second_attack, GameInput::SecondAttack); key_map.insert(settings.controls.roll, GameInput::Roll); + key_map.insert(settings.controls.interact, GameInput::Interact); let keypress_map = HashMap::new();