mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'timo-inventory' into 'master'
Timo inventory See merge request veloren/veloren!468
This commit is contained in:
commit
b5f649eb15
BIN
assets/voxygen/element/icons/2hsword_m1.vox
(Stored with Git LFS)
BIN
assets/voxygen/element/icons/2hsword_m1.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/icons/2hsword_m2.vox
(Stored with Git LFS)
BIN
assets/voxygen/element/icons/2hsword_m2.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/voxel/weapon/debug_wand.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/weapon/debug_wand.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -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))
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use vek::*;
|
||||
|
||||
pub enum LocalEvent {
|
||||
Jump(EcsEntity),
|
||||
Boost { entity: EcsEntity, vel: Vec3<f32> },
|
||||
LandOnGround { entity: EcsEntity, vel: Vec3<f32> },
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 =
|
||||
|
@ -4,13 +4,14 @@ use super::{
|
||||
};
|
||||
use crate::{
|
||||
comp::{
|
||||
ActionState::*, Body, CharacterState, Controller, MovementState::*, PhysicsState, Stats,
|
||||
Vel,
|
||||
item, ActionState::*, Body, CharacterState, Controller, Item, MovementState::*,
|
||||
PhysicsState, Stats, Vel,
|
||||
},
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
};
|
||||
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||
use std::time::Duration;
|
||||
use vek::*;
|
||||
|
||||
/// This system is responsible for validating controller inputs
|
||||
pub struct Sys;
|
||||
@ -96,7 +97,7 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
// Wield
|
||||
if controller.attack
|
||||
if controller.primary
|
||||
&& character.action == Idle
|
||||
&& (character.movement == Stand || character.movement == Run)
|
||||
{
|
||||
@ -105,33 +106,53 @@ impl<'a> System<'a> for Sys {
|
||||
};
|
||||
}
|
||||
|
||||
// Attack
|
||||
if controller.attack
|
||||
&& (character.movement == Stand
|
||||
|| character.movement == Run
|
||||
|| character.movement == Jump)
|
||||
{
|
||||
// TODO: Check if wield ability exists
|
||||
if let Wield { time_left } = character.action {
|
||||
if time_left == Duration::default() {
|
||||
character.action = Attack {
|
||||
time_left: ATTACK_DURATION,
|
||||
applied: false,
|
||||
match stats.equipment.main {
|
||||
Some(Item::Tool { .. }) => {
|
||||
// Attack
|
||||
if controller.primary
|
||||
&& (character.movement == Stand
|
||||
|| character.movement == Run
|
||||
|| character.movement == Jump)
|
||||
{
|
||||
// TODO: Check if wield ability exists
|
||||
if let Wield { time_left } = character.action {
|
||||
if time_left == Duration::default() {
|
||||
character.action = Attack {
|
||||
time_left: ATTACK_DURATION,
|
||||
applied: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Block
|
||||
if controller.secondary
|
||||
&& (character.movement == Stand || character.movement == Run)
|
||||
&& (character.action == Idle || character.action.is_wield())
|
||||
{
|
||||
character.action = Block {
|
||||
time_left: Duration::from_secs(5),
|
||||
};
|
||||
} else if !controller.secondary && character.action.is_block() {
|
||||
character.action = Idle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Block
|
||||
if controller.block
|
||||
&& (character.movement == Stand || character.movement == Run)
|
||||
&& (character.action == Idle || character.action.is_wield())
|
||||
{
|
||||
character.action = Block {
|
||||
time_left: Duration::from_secs(5),
|
||||
};
|
||||
} else if !controller.block && character.action.is_block() {
|
||||
character.action = Idle;
|
||||
Some(Item::Debug(item::Debug::Boost)) => {
|
||||
if controller.primary {
|
||||
local_emitter.emit(LocalEvent::Boost {
|
||||
entity,
|
||||
vel: controller.look_dir * 7.0,
|
||||
});
|
||||
}
|
||||
if controller.secondary {
|
||||
// Go upward
|
||||
local_emitter.emit(LocalEvent::Boost {
|
||||
entity,
|
||||
vel: Vec3::new(0.0, 0.0, 7.0),
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Roll
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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) =
|
||||
|
@ -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)));
|
||||
}
|
||||
|
@ -162,6 +162,7 @@ pub enum Event {
|
||||
ToggleShortcutNumbers(ShortcutNumbers),
|
||||
UiScale(ScaleChange),
|
||||
CharacterSelection,
|
||||
UseInventorySlot(usize),
|
||||
SwapInventorySlots(usize, usize),
|
||||
DropInventorySlot(usize),
|
||||
Logout,
|
||||
|
@ -3,7 +3,7 @@ use super::{
|
||||
/*FOCUS_COLOR, RAGE_COLOR,*/ HP_COLOR, LOW_HP_COLOR, MANA_COLOR, TEXT_COLOR, XP_COLOR,
|
||||
};
|
||||
use crate::GlobalState;
|
||||
use common::comp::Stats;
|
||||
use common::comp::{item::Tool, Item, Stats};
|
||||
use conrod_core::{
|
||||
color,
|
||||
widget::{self, Button, Image, Rectangle, Text},
|
||||
@ -351,10 +351,16 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
.color(Some(BG_COLOR))
|
||||
.middle_of(state.ids.m1_slot)
|
||||
.set(state.ids.m1_slot_bg, ui);
|
||||
Button::image(self.imgs.twohhammer_m1) // Insert Icon here
|
||||
.w_h(38.0 * scale, 38.0 * scale)
|
||||
.middle_of(state.ids.m1_slot_bg)
|
||||
.set(state.ids.m1_content, ui);
|
||||
Button::image(match self.stats.equipment.main {
|
||||
Some(Item::Tool { kind, .. }) => match kind {
|
||||
Tool::Sword => self.imgs.twohsword_m1,
|
||||
_ => self.imgs.twohhammer_m1,
|
||||
},
|
||||
_ => self.imgs.twohhammer_m1,
|
||||
}) // Insert Icon here
|
||||
.w_h(38.0 * scale, 38.0 * scale)
|
||||
.middle_of(state.ids.m1_slot_bg)
|
||||
.set(state.ids.m1_content, ui);
|
||||
// M2 Slot
|
||||
Image::new(self.imgs.skillbar_slot_big)
|
||||
.w_h(40.0 * scale, 40.0 * scale)
|
||||
@ -365,10 +371,16 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
.color(Some(BG_COLOR))
|
||||
.middle_of(state.ids.m2_slot)
|
||||
.set(state.ids.m2_slot_bg, ui);
|
||||
Button::image(self.imgs.twohhammer_m2) // Insert Icon here
|
||||
.w_h(38.0 * scale, 38.0 * scale)
|
||||
.middle_of(state.ids.m2_slot_bg)
|
||||
.set(state.ids.m2_content, ui);
|
||||
Button::image(match self.stats.equipment.main {
|
||||
Some(Item::Tool { kind, .. }) => match kind {
|
||||
Tool::Sword => self.imgs.twohsword_m2,
|
||||
_ => self.imgs.twohhammer_m2,
|
||||
},
|
||||
_ => self.imgs.twohhammer_m2,
|
||||
}) // Insert Icon here
|
||||
.w_h(38.0 * scale, 38.0 * scale)
|
||||
.middle_of(state.ids.m2_slot_bg)
|
||||
.set(state.ids.m2_content, ui);
|
||||
//Slot 5
|
||||
Image::new(self.imgs.skillbar_slot)
|
||||
.w_h(20.0 * scale, 20.0 * scale)
|
||||
|
@ -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.
|
||||
|
@ -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(
|
||||
|
@ -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))
|
||||
|
@ -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 {
|
||||
Tool::Sword => ("weapon.sword.rusty_2h", Vec3::new(-1.5, -6.5, -4.0)),
|
||||
Tool::Axe => ("weapon.axe.rusty_2h", Vec3::new(-1.5, -6.5, -4.0)),
|
||||
Tool::Hammer => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)),
|
||||
Tool::Daggers => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)),
|
||||
Tool::SwordShield => ("weapon.axe.rusty_2h", Vec3::new(-2.5, -6.5, -2.0)),
|
||||
Tool::Bow => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)),
|
||||
Tool::Staff => ("weapon.axe.rusty_2h", Vec3::new(-2.5, -6.5, -2.0)),
|
||||
};
|
||||
Self::load_mesh(name, offset)
|
||||
fn load_main(item: Option<&Item>) -> Mesh<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, -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user