Merge branch 'zesterer/inventories' into 'master'

Inventories

Closes #167 and #166

See merge request veloren/veloren!371
This commit is contained in:
Timo Koesters 2019-08-01 15:21:29 +00:00
commit 6f73d27431
27 changed files with 499 additions and 190 deletions

BIN
assets/voxygen/voxel/object/pouch.vox (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -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::<Uid>().get(entity).copied() {
self.postbox.send_message(ClientMsg::PickUp(uid.id()));
}
}
pub fn view_distance(&self) -> Option<u32> {
self.view_distance
}
@ -188,6 +202,10 @@ impl Client {
self.state.terrain().get_key_arc(chunk_pos).cloned()
}
pub fn inventories(&self) -> ReadStorage<comp::Inventory> {
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);

View File

@ -48,6 +48,7 @@ pub enum Body {
CarpetHumanSquare,
CarpetHumanSquare2,
CarpetHumanSquircle,
Pouch,
}
impl Body {

View File

@ -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 { .. } => "<consumable>",
Item::Ingredient => "<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<Self>;
type Storage = FlaggedStorage<Self, IDVStorage<Self>>;
}

View File

@ -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<Item>] {
&self.slots
}
pub fn len(&self) -> usize {
self.slots.len()
}
pub fn insert(&mut self, item: Item) -> Option<Item> {
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<Item> {
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<Item> {
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<Self>;
}
// ForceUpdate
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
pub struct InventoryUpdate;
impl Component for InventoryUpdate {
type Storage = NullStorage<Self>;
}

View File

@ -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;

View File

@ -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<f32>);
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<f32>);
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<f32>);
impl Component for Ori {

View File

@ -28,6 +28,9 @@ pub enum ClientMsg {
vel: comp::Vel,
ori: comp::Ori,
},
SwapInventorySlots(usize, usize),
DropInventorySlot(usize),
PickUp(u64),
TerrainChunkRequest {
key: Vec2<i32>,
},

View File

@ -25,6 +25,7 @@ sphynx::sum_type! {
CanBuild(comp::CanBuild),
Stats(comp::Stats),
LightEmitter(comp::LightEmitter),
Item(comp::Item),
}
}
// Automatically derive From<T> for EcsCompPhantom
@ -40,6 +41,7 @@ sphynx::sum_type! {
CanBuild(PhantomData<comp::CanBuild>),
Stats(PhantomData<comp::Stats>),
LightEmitter(PhantomData<comp::LightEmitter>),
Item(PhantomData<comp::Item>),
}
}
impl sphynx::CompPacket for EcsCompPacket {

View File

@ -55,6 +55,7 @@ pub enum ServerMsg {
entity: u64,
action_state: comp::ActionState,
},
InventoryUpdate(comp::Inventory),
TerrainChunkUpdate {
key: Vec2<i32>,
chunk: Box<TerrainChunk>,

View File

@ -131,6 +131,7 @@ impl State {
ecs.register_synced::<comp::Stats>();
ecs.register_synced::<comp::CanBuild>();
ecs.register_synced::<comp::LightEmitter>();
ecs.register_synced::<comp::Item>();
// Register components send from clients -> server
ecs.register::<comp::Controller>();
@ -142,6 +143,7 @@ impl State {
ecs.register::<comp::Pos>();
ecs.register::<comp::Vel>();
ecs.register::<comp::Ori>();
ecs.register::<comp::Inventory>();
// Register client-local components
ecs.register::<comp::AnimationInfo>();
@ -156,6 +158,7 @@ impl State {
ecs.register::<comp::Respawning>();
ecs.register::<comp::Dying>();
ecs.register::<comp::ForceUpdate>();
ecs.register::<comp::InventoryUpdate>();
ecs.register::<comp::Inventory>();
// Controller effects
ecs.register::<comp::MoveDir>();

View File

@ -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;

View File

@ -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<f32>, damp: f32) -> Vec3<f32> {
lv.z = (lv.z - GRAVITY * dt).max(-50.0);
fn integrate_forces(dt: f32, mut lv: Vec3<f32>, grav: f32, damp: f32) -> Vec3<f32> {
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(),
)
{

View File

@ -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

View File

@ -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::<comp::Inventory>()
.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::<comp::Inventory>()
.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::<comp::Pos>().get(entity))
{
dropped_items.push((
*pos,
state
.ecs()
.read_storage::<comp::Ori>()
.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::<comp::Item>()
.get_mut(item_entity)
.map(|item| (*item, item_entity))
}),
ecs.write_storage::<comp::Inventory>().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::<f32>::zero().map(|_| rand::thread_rng().gen::<f32>() - 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::<comp::Inventory>(),
&self.state.ecs().read_storage::<comp::InventoryUpdate>(),
)
.join()
{
self.clients
.notify(entity, ServerMsg::InventoryUpdate(inventory.clone()));
}
// Remove all force flags.
self.state
.ecs_mut()
.write_storage::<comp::ForceUpdate>()
.clear();
self.state
.ecs_mut()
.write_storage::<comp::InventoryUpdate>()
.clear();
}
pub fn generate_chunk(&mut self, key: Vec2<i32>) {

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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,
},
}
}

View File

@ -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<usize>,
}
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>) -> 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
}
}

View File

@ -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;

View File

@ -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))

View File

@ -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<FigurePipeline> {
fn load_weapon(weapon: Tool) -> Mesh<FigurePipeline> {
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<S: Skeleton> FigureState<S> {
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::<f32>::identity()
* Mat4::translation_3d(self.pos)

View File

@ -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::<comp::Pos>()
.get(client.entity())
.copied();
if let (Some(player_pos), true) = (player_pos, state) {
let entity = (
&client.state().ecs().entities(),
&client.state().ecs().read_storage::<comp::Pos>(),
&client.state().ecs().read_storage::<comp::Item>(),
)
.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)
}
}
}

View File

@ -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),
}
}
}

View File

@ -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();