Merge branch 'timo-inventory' into 'master'

Timo inventory

See merge request veloren/veloren!468
This commit is contained in:
Timo Koesters 2019-08-30 22:37:40 +00:00
commit b5f649eb15
28 changed files with 306 additions and 139 deletions

Binary file not shown.

Binary file not shown.

BIN
assets/voxygen/voxel/weapon/debug_wand.vox (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -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<comp::item::Tool>,
) {
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))

View File

@ -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<f32>,
pub look_dir: Vec3<f32>,
pub jump: bool,
pub attack: bool,
pub block: bool,
pub roll: bool,
pub glide: bool,
pub respawn: bool,

View File

@ -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 { .. } => "<consumable>",
Item::Ingredient => "<ingredient>",
Item::Debug(_) => "Debugging item",
}
}
@ -109,6 +116,7 @@ impl Item {
Item::Armor { .. } => "armour",
Item::Consumable { .. } => "consumable",
Item::Ingredient => "ingredient",
Item::Debug(_) => "debug",
}
}

View File

@ -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<Item> {
/// 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<Item> {
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<Item> {
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<Option<Item>, 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<Item> {
//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<Item> {
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

View File

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

View File

@ -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<comp::Item>,
pub alt: Option<comp::Item>,
// 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<comp::Item>) -> 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,
}
}

View File

@ -6,6 +6,7 @@ use vek::*;
pub enum LocalEvent {
Jump(EcsEntity),
Boost { entity: EcsEntity, vel: Vec3<f32> },
LandOnGround { entity: EcsEntity, vel: Vec3<f32> },
}

View File

@ -12,6 +12,7 @@ pub enum ClientMsg {
Character {
name: String,
body: comp::Body,
main: Option<comp::item::Tool>,
},
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),

View File

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

View File

@ -87,9 +87,9 @@ impl<'a> System<'a> for Sys {
Vec2::<f32>::from(target_pos.0 - pos.0).normalized() * 0.5;
if rand::random::<f32>() < 0.05 {
controller.attack = true;
controller.primary = true;
} else {
controller.attack = false;
controller.primary = false;
}
} else if dist < SIGHT_DIST {
controller.move_dir =

View File

@ -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,8 +106,10 @@ impl<'a> System<'a> for Sys {
};
}
match stats.equipment.main {
Some(Item::Tool { .. }) => {
// Attack
if controller.attack
if controller.primary
&& (character.movement == Stand
|| character.movement == Run
|| character.movement == Jump)
@ -123,16 +126,34 @@ impl<'a> System<'a> for Sys {
}
// Block
if controller.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.block && character.action.is_block() {
} else if !controller.secondary && 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
if controller.roll

View File

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

View File

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

View File

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

View File

@ -200,12 +200,13 @@ impl Server {
client: &mut Client,
name: String,
body: comp::Body,
main: Option<comp::Item>,
server_settings: &ServerSettings,
) {
let spawn_point = state.ecs().read_resource::<SpawnPoint>().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::<f32>() < 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::<u32>() % 400);
@ -699,6 +712,34 @@ impl Server {
}
_ => {}
},
ClientMsg::UseInventorySlot(x) => {
let item = state
.ecs()
.write_storage::<comp::Inventory>()
.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::<comp::Stats>().get_mut(entity)
{
// Insert old item into inventory
if let Some(old_item) = stats.equipment.main.take() {
state
.ecs()
.write_storage::<comp::Inventory>()
.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::<comp::Item>()
.get_mut(item_entity)
.map(|item| (*item, item_entity))
.map(|item| (item.clone(), item_entity))
}),
ecs.write_storage::<comp::Inventory>().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) =

View File

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

View File

@ -162,6 +162,7 @@ pub enum Event {
ToggleShortcutNumbers(ShortcutNumbers),
UiScale(ScaleChange),
CharacterSelection,
UseInventorySlot(usize),
SwapInventorySlots(usize, usize),
DropInventorySlot(usize),
Logout,

View File

@ -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,7 +351,13 @@ 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
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);
@ -365,7 +371,13 @@ 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
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);

View File

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

View File

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

View File

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

View File

@ -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<Equipment>),
}
pub struct FigureModelCache {
models: HashMap<Body, ((Model<FigurePipeline>, SkeletonAttr), u64)>,
models: HashMap<FigureKey, ((Model<FigurePipeline>, SkeletonAttr), u64)>,
}
impl FigureModelCache {
@ -41,15 +49,22 @@ impl FigureModelCache {
&mut self,
renderer: &mut Renderer,
body: Body,
equipment: Option<&Equipment>,
tick: u64,
) -> &(Model<FigurePipeline>, 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<FigurePipeline> {
let (name, offset) = match weapon {
fn load_main(item: Option<&Item>) -> Mesh<FigurePipeline> {
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, -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<FigurePipeline> {
@ -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::<comp::Pos>(),
&ecs.read_storage::<comp::Vel>(),
@ -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

View File

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

View File

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

View File

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