Enable unequipping as well as equipping to specific slots

This commit is contained in:
Imbris 2020-04-09 22:36:35 -04:00 committed by Pfauenauge90
parent 66b4c0d529
commit c1c09dce1b
17 changed files with 506 additions and 292 deletions

View File

@ -54,6 +54,13 @@
"voxel.weapon.shield.wood-0",
(0.0, 0.0, 0.0), (-90.0, 90.0, 0.0), 2.4,
),
// Lanterns
Lantern(Black0): Png(
"element.icons.lantern_black-0",
),
Lantern(Green0): Png(
"element.icons.lantern_green-0",
),
// Other
Utility(Collar): Png(
"element.icons.collar",
@ -251,13 +258,6 @@
Armor(Neck(Neck0)): Png(
"element.icons.neck-0",
),
// Lanterns
Armor(Lantern(Black0)): Png(
"element.icons.lantern_black-0",
),
Armor(Lantern(Green0)): Png(
"element.icons.lantern_green-0",
),
// Tabards
Armor(Tabard(Admin)): Png(
"element.icons.tabard_admin",

View File

@ -244,21 +244,21 @@ impl Client {
// Can't fail
}
pub fn use_inventory_slot(&mut self, slot: usize) {
pub fn use_slot(&mut self, slot: comp::slot::Slot) {
self.postbox
.send_message(ClientMsg::ControlEvent(ControlEvent::InventoryManip(
InventoryManip::Use(slot),
)));
}
pub fn swap_inventory_slots(&mut self, a: usize, b: usize) {
pub fn swap_slots(&mut self, a: comp::slot::Slot, b: comp::slot::Slot) {
self.postbox
.send_message(ClientMsg::ControlEvent(ControlEvent::InventoryManip(
InventoryManip::Swap(a, b),
)));
}
pub fn drop_inventory_slot(&mut self, slot: usize) {
pub fn drop_slot(&mut self, slot: comp::slot::Slot) {
self.postbox
.send_message(ClientMsg::ControlEvent(ControlEvent::InventoryManip(
InventoryManip::Drop(slot),

View File

@ -1,4 +1,4 @@
use crate::{sync::Uid, util::Dir};
use crate::{comp::inventory::slot::Slot, sync::Uid, util::Dir};
use specs::{Component, FlaggedStorage};
use specs_idvs::IDVStorage;
use std::time::Duration;
@ -11,9 +11,9 @@ pub const DEFAULT_HOLD_DURATION: Duration = Duration::from_millis(200);
pub enum InventoryManip {
Pickup(Uid),
Collect(Vec3<i32>),
Use(usize),
Swap(usize, usize),
Drop(usize),
Use(Slot),
Swap(Slot, Slot),
Drop(Slot),
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]

View File

@ -188,13 +188,6 @@ pub enum Neck {
pub const ALL_NECKS: [Neck; 1] = [Neck::Neck0];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Lantern {
Black0 = 1,
Green0 = 2,
}
pub const ALL_LANTERNS: [Lantern; 2] = [Lantern::Black0, Lantern::Green0];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Head {
Leather0 = 1,
AssaMask0 = 2,
@ -218,7 +211,6 @@ pub enum Armor {
Back(Back),
Ring(Ring),
Neck(Neck),
Lantern(Lantern),
Head(Head),
Tabard(Tabard),
}

View File

@ -36,12 +36,21 @@ pub enum Ingredient {
Grass,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Lantern {
Black0 = 1,
Green0 = 2,
}
pub const ALL_LANTERNS: [Lantern; 2] = [Lantern::Black0, Lantern::Green0];
fn default_amount() -> u32 { 1 }
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ItemKind {
/// Something wieldable
Tool(tool::Tool),
Lantern(Lantern),
Armor {
kind: armor::Armor,
stats: armor::Stats,

View File

@ -1,4 +1,5 @@
pub mod item;
pub mod slot;
use crate::assets;
use item::{Consumable, Item, ItemKind};
@ -36,7 +37,9 @@ impl Inventory {
/// new group. Returns the item again if no space was found.
pub fn push(&mut self, item: Item) -> Option<Item> {
let item = match item.kind {
ItemKind::Tool(_) | ItemKind::Armor { .. } => self.add_to_first_empty(item),
ItemKind::Tool(_) | ItemKind::Armor { .. } | ItemKind::Lantern(_) => {
self.add_to_first_empty(item)
},
ItemKind::Utility {
kind: item_kind,
amount: new_amount,
@ -239,7 +242,9 @@ impl Inventory {
if let Some(Some(item)) = self.slots.get_mut(cell) {
let mut return_item = item.clone();
match &mut item.kind {
ItemKind::Tool(_) | ItemKind::Armor { .. } => self.remove(cell),
ItemKind::Tool(_) | ItemKind::Armor { .. } | ItemKind::Lantern(_) => {
self.remove(cell)
},
ItemKind::Utility { kind, amount } => {
if *amount <= 1 {
self.remove(cell)

View File

@ -0,0 +1,250 @@
use crate::{comp, comp::item};
use comp::{Inventory, Loadout};
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
pub enum Slot {
Inventory(usize),
Equip(EquipSlot),
}
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
pub enum EquipSlot {
Armor(ArmorSlot),
Mainhand,
Offhand,
Lantern,
}
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
pub enum ArmorSlot {
Head,
Neck,
Shoulders,
Chest,
Hands,
Ring,
Back,
Belt,
Legs,
Feet,
Tabard,
}
//const ALL_ARMOR_SLOTS: [ArmorSlot; 11] = [
// Head, Neck, Shoulders, Chest, Hands, Ring, Back, Belt, Legs, Feet, Tabard,
//];
impl Slot {
pub fn can_hold(self, item_kind: &item::ItemKind) -> bool {
match (self, item_kind) {
(Self::Inventory(_), _) => true,
(Self::Equip(slot), item_kind) => slot.can_hold(item_kind),
}
}
}
impl EquipSlot {
fn can_hold(self, item_kind: &item::ItemKind) -> bool {
use item::ItemKind;
match (self, item_kind) {
(Self::Armor(slot), ItemKind::Armor { kind, .. }) => slot.can_hold(kind),
(Self::Mainhand, ItemKind::Tool(_)) => true,
(Self::Offhand, ItemKind::Tool(_)) => true,
(Self::Lantern, ItemKind::Lantern(_)) => true,
_ => false,
}
}
}
impl ArmorSlot {
fn can_hold(self, armor: &item::armor::Armor) -> bool {
use item::armor::Armor;
match (self, armor) {
(Self::Head, Armor::Head(_)) => true,
(Self::Neck, Armor::Neck(_)) => true,
(Self::Shoulders, Armor::Shoulder(_)) => true,
(Self::Chest, Armor::Chest(_)) => true,
(Self::Hands, Armor::Hand(_)) => true,
(Self::Ring, Armor::Ring(_)) => true,
(Self::Back, Armor::Back(_)) => true,
(Self::Belt, Armor::Belt(_)) => true,
(Self::Legs, Armor::Pants(_)) => true,
(Self::Feet, Armor::Foot(_)) => true,
(Self::Tabard, Armor::Tabard(_)) => true,
_ => false,
}
}
}
// TODO: shouldn't need this
fn item_config(item: item::Item) -> comp::ItemConfig {
let mut abilities = if let item::ItemKind::Tool(tool) = &item.kind {
tool.get_abilities()
} else {
Vec::new()
}
.into_iter();
comp::ItemConfig {
item,
ability1: abilities.next(),
ability2: abilities.next(),
ability3: abilities.next(),
block_ability: Some(comp::CharacterAbility::BasicBlock),
dodge_ability: Some(comp::CharacterAbility::Roll),
}
}
fn loadout_replace(
equip_slot: EquipSlot,
item: Option<item::Item>,
loadout: &mut Loadout,
) -> Option<item::Item> {
use std::mem::replace;
match equip_slot {
EquipSlot::Armor(ArmorSlot::Head) => replace(&mut loadout.head, item),
EquipSlot::Armor(ArmorSlot::Neck) => replace(&mut loadout.neck, item),
EquipSlot::Armor(ArmorSlot::Shoulders) => replace(&mut loadout.shoulder, item),
EquipSlot::Armor(ArmorSlot::Chest) => replace(&mut loadout.chest, item),
EquipSlot::Armor(ArmorSlot::Hands) => replace(&mut loadout.hand, item),
EquipSlot::Armor(ArmorSlot::Ring) => replace(&mut loadout.ring, item),
EquipSlot::Armor(ArmorSlot::Back) => replace(&mut loadout.back, item),
EquipSlot::Armor(ArmorSlot::Belt) => replace(&mut loadout.belt, item),
EquipSlot::Armor(ArmorSlot::Legs) => replace(&mut loadout.pants, item),
EquipSlot::Armor(ArmorSlot::Feet) => replace(&mut loadout.foot, item),
EquipSlot::Armor(ArmorSlot::Tabard) => replace(&mut loadout.tabard, item),
EquipSlot::Lantern => replace(&mut loadout.lantern, item),
EquipSlot::Mainhand => {
replace(&mut loadout.active_item, item.map(item_config)).map(|i| i.item)
},
EquipSlot::Offhand => {
replace(&mut loadout.second_item, item.map(item_config)).map(|i| i.item)
},
}
}
#[must_use]
fn loadout_insert(
equip_slot: EquipSlot,
item: item::Item,
loadout: &mut Loadout,
) -> Option<item::Item> {
loadout_replace(equip_slot, Some(item), loadout)
}
pub fn loadout_remove(equip_slot: EquipSlot, loadout: &mut Loadout) -> Option<item::Item> {
loadout_replace(equip_slot, None, loadout)
}
fn swap_inventory_loadout(
inventory_slot: usize,
equip_slot: EquipSlot,
inventory: &mut Inventory,
loadout: &mut Loadout,
) {
// Check if loadout slot can hold item
if inventory
.get(inventory_slot)
.map_or(true, |item| equip_slot.can_hold(&item.kind))
{
// Take item from loadout
let from_equip = loadout_remove(equip_slot, loadout);
// Swap with item in the inventory
let from_inv = if let Some(item) = from_equip {
// If this fails and we get item back as an err it will just be put back in the
// loadout
inventory
.insert(inventory_slot, item)
.unwrap_or_else(|i| Some(i))
} else {
inventory.remove(inventory_slot)
};
// Put item from the inventory in loadout
if let Some(item) = from_inv {
loadout_insert(equip_slot, item, loadout).unwrap_none(); // Can never fail
}
}
}
fn swap_loadout(slot_a: EquipSlot, slot_b: EquipSlot, loadout: &mut Loadout) {
// Get items from the slots
let item_a = loadout_remove(slot_a, loadout);
let item_b = loadout_remove(slot_b, loadout);
// Check if items can go in the other slots
if item_a.as_ref().map_or(true, |i| slot_b.can_hold(&i.kind))
&& item_b.as_ref().map_or(true, |i| slot_a.can_hold(&i.kind))
{
// Swap
loadout_replace(slot_b, item_a, loadout).unwrap_none();
loadout_replace(slot_a, item_b, loadout).unwrap_none();
} else {
// Otherwise put the items back
loadout_replace(slot_a, item_a, loadout).unwrap_none();
loadout_replace(slot_b, item_b, loadout).unwrap_none();
}
}
// Should this report if a change actually occurred? (might be useful when
// minimizing network use)
pub fn swap(
slot_a: Slot,
slot_b: Slot,
inventory: Option<&mut Inventory>,
loadout: Option<&mut Loadout>,
) {
match (slot_a, slot_b) {
(Slot::Inventory(slot_a), Slot::Inventory(slot_b)) => {
inventory.map(|i| i.swap_slots(slot_a, slot_b));
},
(Slot::Inventory(inv_slot), Slot::Equip(equip_slot))
| (Slot::Equip(equip_slot), Slot::Inventory(inv_slot)) => {
if let Some((inventory, loadout)) = loadout.and_then(|l| inventory.map(|i| (i, l))) {
swap_inventory_loadout(inv_slot, equip_slot, inventory, loadout);
}
},
(Slot::Equip(slot_a), Slot::Equip(slot_b)) => {
loadout.map(|l| swap_loadout(slot_a, slot_b, l));
},
}
}
pub fn equip(slot: usize, inventory: &mut Inventory, loadout: &mut Loadout) {
use item::{armor::Armor, ItemKind};
let equip_slot = inventory.get(slot).and_then(|i| match &i.kind {
ItemKind::Tool(_) => Some(EquipSlot::Mainhand),
ItemKind::Armor { kind, .. } => Some(EquipSlot::Armor(match kind {
Armor::Head(_) => ArmorSlot::Head,
Armor::Neck(_) => ArmorSlot::Neck,
Armor::Shoulder(_) => ArmorSlot::Shoulders,
Armor::Chest(_) => ArmorSlot::Chest,
Armor::Hand(_) => ArmorSlot::Hands,
Armor::Ring(_) => ArmorSlot::Ring,
Armor::Back(_) => ArmorSlot::Back,
Armor::Belt(_) => ArmorSlot::Belt,
Armor::Pants(_) => ArmorSlot::Legs,
Armor::Foot(_) => ArmorSlot::Feet,
Armor::Tabard(_) => ArmorSlot::Tabard,
})),
ItemKind::Lantern(_) => Some(EquipSlot::Lantern),
_ => None,
});
if let Some(equip_slot) = equip_slot {
// If item is going to mainhand, put mainhand in offhand and place offhand in
// inventory
if let EquipSlot::Mainhand = equip_slot {
swap_loadout(EquipSlot::Mainhand, EquipSlot::Offhand, loadout);
}
swap_inventory_loadout(slot, equip_slot, inventory, loadout);
}
}
pub fn unequip(slot: EquipSlot, inventory: &mut Inventory, loadout: &mut Loadout) {
loadout_remove(slot, loadout) // Remove item from loadout
.and_then(|i| inventory.push(i)) // Insert into inventory
.and_then(|i| loadout_insert(slot, i, loadout)) // If that fails put back in loadout
.unwrap(); // Never fails
}

View File

@ -31,7 +31,7 @@ pub use controller::{
pub use energy::{Energy, EnergySource};
pub use inputs::CanBuild;
pub use inventory::{
item, item::Item, Inventory, InventoryUpdate, InventoryUpdateEvent, MAX_PICKUP_RANGE_SQR,
item, item::Item, slot, Inventory, InventoryUpdate, InventoryUpdateEvent, MAX_PICKUP_RANGE_SQR,
};
pub use last::Last;
pub use location::{Waypoint, WaypointArea};

View File

@ -2,6 +2,7 @@
#![type_length_limit = "1664759"]
#![feature(
arbitrary_enum_discriminant,
option_unwrap_none,
bool_to_option,
label_break_value,
trait_alias,

View File

@ -1,6 +1,10 @@
use crate::{Server, StateExt};
use common::{
comp::{self, item, Pos, MAX_PICKUP_RANGE_SQR},
comp::{
self, item,
slot::{self, Slot},
Pos, MAX_PICKUP_RANGE_SQR,
},
sync::WorldSyncExt,
terrain::block::Block,
vol::{ReadVol, Vox},
@ -83,162 +87,145 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
}
},
comp::InventoryManip::Use(slot_idx) => {
let item_opt = state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.and_then(|inv| inv.take(slot_idx));
comp::InventoryManip::Use(slot) => {
let mut inventories = state.ecs().write_storage::<comp::Inventory>();
let inventory = if let Some(inventory) = inventories.get_mut(entity) {
inventory
} else {
error!("Can't manipulate inventory, entity doesn't have one");
return;
};
let mut event = comp::InventoryUpdateEvent::Used;
let mut maybe_effect = None;
if let Some(item) = item_opt {
match &item.kind {
item::ItemKind::Tool(tool) => {
if let Some(loadout) =
state.ecs().write_storage::<comp::Loadout>().get_mut(entity)
{
// Insert old item into inventory
if let Some(old_item) = loadout.active_item.take() {
state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.map(|inv| inv.insert(slot_idx, old_item.item));
}
let mut abilities = tool.get_abilities();
let mut ability_drain = abilities.drain(..);
let active_item = comp::ItemConfig {
item,
ability1: ability_drain.next(),
ability2: ability_drain.next(),
ability3: ability_drain.next(),
block_ability: Some(comp::CharacterAbility::BasicBlock),
dodge_ability: Some(comp::CharacterAbility::Roll),
};
loadout.active_item = Some(active_item);
let event = match slot {
Slot::Inventory(slot) => {
use item::ItemKind;
// Check if item is equipable
if inventory.get(slot).map_or(false, |i| match &i.kind {
ItemKind::Tool(_) | ItemKind::Armor { .. } | ItemKind::Lantern(_) => true,
_ => false,
}) {
if let Some(loadout) = state.ecs().write_storage().get_mut(entity) {
slot::equip(slot, inventory, loadout);
Some(comp::InventoryUpdateEvent::Used)
} else {
None
}
},
item::ItemKind::Consumable { kind, effect, .. } => {
event = comp::InventoryUpdateEvent::Consumed(*kind);
state.apply_effect(entity, *effect);
},
item::ItemKind::Armor { kind, .. } => {
if let Some(loadout) =
state.ecs().write_storage::<comp::Loadout>().get_mut(entity)
{
use comp::item::armor::Armor::*;
let slot = match kind.clone() {
Shoulder(_) => &mut loadout.shoulder,
Chest(_) => &mut loadout.chest,
Belt(_) => &mut loadout.belt,
Hand(_) => &mut loadout.hand,
Pants(_) => &mut loadout.pants,
Foot(_) => &mut loadout.foot,
Back(_) => &mut loadout.back,
Ring(_) => &mut loadout.ring,
Neck(_) => &mut loadout.neck,
Lantern(_) => &mut loadout.lantern,
Head(_) => &mut loadout.head,
Tabard(_) => &mut loadout.tabard,
};
// Insert old item into inventory
if let Some(old_item) = slot.take() {
state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.map(|inv| inv.insert(slot_idx, old_item));
}
*slot = Some(item);
}
},
item::ItemKind::Utility { kind, .. } => match kind {
comp::item::Utility::Collar => {
let reinsert = if let Some(pos) =
state.read_storage::<comp::Pos>().get(entity)
{
if (
&state.read_storage::<comp::Alignment>(),
&state.read_storage::<comp::Agent>(),
)
.join()
.filter(|(alignment, _)| {
alignment == &&comp::Alignment::Owned(entity)
})
.count()
>= 3
} else if let Some(item) = inventory.take(slot) {
match &item.kind {
ItemKind::Consumable { kind, effect, .. } => {
maybe_effect = Some(*effect);
Some(comp::InventoryUpdateEvent::Consumed(*kind))
},
ItemKind::Utility {
kind: comp::item::Utility::Collar,
..
} => {
let reinsert = if let Some(pos) =
state.read_storage::<comp::Pos>().get(entity)
{
true
} else if let Some(tameable_entity) = {
let nearest_tameable = (
&state.ecs().entities(),
&state.ecs().read_storage::<comp::Pos>(),
&state.ecs().read_storage::<comp::Alignment>(),
if (
&state.read_storage::<comp::Alignment>(),
&state.read_storage::<comp::Agent>(),
)
.join()
.filter(|(_, wild_pos, _)| {
wild_pos.0.distance_squared(pos.0) < 5.0f32.powf(2.0)
.filter(|(alignment, _)| {
alignment == &&comp::Alignment::Owned(entity)
})
.filter(|(_, _, alignment)| {
alignment == &&comp::Alignment::Wild
})
.min_by_key(|(_, wild_pos, _)| {
(wild_pos.0.distance_squared(pos.0) * 100.0) as i32
})
.map(|(entity, _, _)| entity);
nearest_tameable
} {
let _ = state
.ecs()
.write_storage()
.insert(tameable_entity, comp::Alignment::Owned(entity));
let _ = state
.ecs()
.write_storage()
.insert(tameable_entity, comp::Agent::default());
false
.count()
>= 3
{
true
} else if let Some(tameable_entity) = {
let nearest_tameable = (
&state.ecs().entities(),
&state.ecs().read_storage::<comp::Pos>(),
&state.ecs().read_storage::<comp::Alignment>(),
)
.join()
.filter(|(_, wild_pos, _)| {
wild_pos.0.distance_squared(pos.0)
< 5.0f32.powf(2.0)
})
.filter(|(_, _, alignment)| {
alignment == &&comp::Alignment::Wild
})
.min_by_key(|(_, wild_pos, _)| {
(wild_pos.0.distance_squared(pos.0) * 100.0) as i32
})
.map(|(entity, _, _)| entity);
nearest_tameable
} {
let _ = state.ecs().write_storage().insert(
tameable_entity,
comp::Alignment::Owned(entity),
);
let _ = state
.ecs()
.write_storage()
.insert(tameable_entity, comp::Agent::default());
false
} else {
true
}
} else {
true
};
if reinsert {
let _ = state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.map(|inv| inv.insert(slot, item));
}
} else {
true
};
if reinsert {
let _ = state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.map(|inv| inv.insert(slot_idx, item));
}
},
},
_ => {
let _ = state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.map(|inv| inv.insert(slot_idx, item));
},
}
Some(comp::InventoryUpdateEvent::Used)
},
_ => {
// TODO: this doesn't work for stackable items
inventory.insert(slot, item).unwrap();
None
},
}
} else {
None
}
},
Slot::Equip(slot) => {
if let Some(loadout) = state.ecs().write_storage().get_mut(entity) {
slot::unequip(slot, inventory, loadout);
Some(comp::InventoryUpdateEvent::Used)
} else {
error!("Entity doesn't have a loadout, can't unequip...");
None
}
},
};
drop(inventories);
if let Some(effect) = maybe_effect {
state.apply_effect(entity, effect);
}
if let Some(event) = event {
state.write_component(entity, comp::InventoryUpdate::new(event));
}
state.write_component(entity, comp::InventoryUpdate::new(event));
},
comp::InventoryManip::Swap(a, b) => {
state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.map(|inv| inv.swap_slots(a, b));
let ecs = state.ecs();
let mut inventories = ecs.write_storage();
let mut loadouts = ecs.write_storage();
let inventory = inventories.get_mut(entity);
let loadout = loadouts.get_mut(entity);
slot::swap(a, b, inventory, loadout);
// :/
drop(loadouts);
drop(inventories);
state.write_component(
entity,
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Swapped),
@ -246,11 +233,18 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
},
comp::InventoryManip::Drop(slot) => {
let item = state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.and_then(|inv| inv.remove(slot));
let item = match slot {
Slot::Inventory(slot) => state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.and_then(|inv| inv.remove(slot)),
Slot::Equip(slot) => state
.ecs()
.write_storage()
.get_mut(entity)
.and_then(|ldt| slot::loadout_remove(slot, ldt)),
};
if let (Some(item), Some(pos)) =
(item, state.ecs().read_storage::<comp::Pos>().get(entity))

View File

@ -1,9 +1,8 @@
use super::{
img_ids::{Imgs, ImgsRot},
item_imgs::ItemImgs,
slots::{ArmorSlot, InventorySlot, SlotManager},
Event as HudEvent, Show, CRITICAL_HP_COLOR, LOW_HP_COLOR, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN,
XP_COLOR,
slots::{ArmorSlot, EquipSlot, InventorySlot, SlotManager},
Show, CRITICAL_HP_COLOR, LOW_HP_COLOR, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, XP_COLOR,
};
use crate::{
i18n::VoxygenLocalization,
@ -135,7 +134,6 @@ pub struct State {
}
pub enum Event {
HudEvent(HudEvent),
Stats,
Close,
}
@ -350,7 +348,7 @@ impl<'a> Widget for Bag<'a> {
(item.name(), item.description())
});
slot_maker
.fabricate(ArmorSlot::Head, [45.0; 2])
.fabricate(EquipSlot::Armor(ArmorSlot::Head), [45.0; 2])
.mid_top_with_margin_on(state.ids.bg_frame, 60.0)
.with_icon(self.imgs.head_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN))
.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip)
@ -363,7 +361,7 @@ impl<'a> Widget for Bag<'a> {
(item.name(), item.description())
});
slot_maker
.fabricate(ArmorSlot::Neck, [45.0; 2])
.fabricate(EquipSlot::Armor(ArmorSlot::Neck), [45.0; 2])
.mid_bottom_with_margin_on(state.ids.head_slot, -55.0)
.with_icon(self.imgs.necklace_bg, Vec2::new(40.0, 31.0), Some(UI_MAIN))
.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip)
@ -377,7 +375,7 @@ impl<'a> Widget for Bag<'a> {
(item.name(), item.description())
});
slot_maker
.fabricate(ArmorSlot::Chest, [85.0; 2])
.fabricate(EquipSlot::Armor(ArmorSlot::Chest), [85.0; 2])
.mid_bottom_with_margin_on(state.ids.neck_slot, -95.0)
.with_icon(self.imgs.chest_bg, Vec2::new(64.0, 42.0), Some(UI_MAIN))
.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip)
@ -388,7 +386,7 @@ impl<'a> Widget for Bag<'a> {
|item| (item.name(), item.description()),
);
slot_maker
.fabricate(ArmorSlot::Shoulders, [70.0; 2])
.fabricate(EquipSlot::Armor(ArmorSlot::Shoulders), [70.0; 2])
.bottom_left_with_margins_on(state.ids.chest_slot, 0.0, -80.0)
.with_icon(self.imgs.shoulders_bg, Vec2::new(60.0, 36.0), Some(UI_MAIN))
.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip)
@ -401,7 +399,7 @@ impl<'a> Widget for Bag<'a> {
(item.name(), item.description())
});
slot_maker
.fabricate(ArmorSlot::Hands, [70.0; 2])
.fabricate(EquipSlot::Armor(ArmorSlot::Hands), [70.0; 2])
.bottom_right_with_margins_on(state.ids.chest_slot, 0.0, -80.0)
.with_icon(self.imgs.hands_bg, Vec2::new(55.0, 60.0), Some(UI_MAIN))
.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip)
@ -414,7 +412,7 @@ impl<'a> Widget for Bag<'a> {
(item.name(), item.description())
});
slot_maker
.fabricate(ArmorSlot::Belt, [45.0; 2])
.fabricate(EquipSlot::Armor(ArmorSlot::Belt), [45.0; 2])
.mid_bottom_with_margin_on(state.ids.chest_slot, -55.0)
.with_icon(self.imgs.belt_bg, Vec2::new(40.0, 23.0), Some(UI_MAIN))
.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip)
@ -427,7 +425,7 @@ impl<'a> Widget for Bag<'a> {
(item.name(), item.description())
});
slot_maker
.fabricate(ArmorSlot::Legs, [85.0; 2])
.fabricate(EquipSlot::Armor(ArmorSlot::Legs), [85.0; 2])
.mid_bottom_with_margin_on(state.ids.belt_slot, -95.0)
.with_icon(self.imgs.legs_bg, Vec2::new(48.0, 70.0), Some(UI_MAIN))
.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip)
@ -438,7 +436,7 @@ impl<'a> Widget for Bag<'a> {
|item| (item.name(), item.description()),
);
slot_maker
.fabricate(ArmorSlot::Lantern, [45.0; 2])
.fabricate(EquipSlot::Lantern, [45.0; 2])
.bottom_right_with_margins_on(state.ids.shoulders_slot, -55.0, 0.0)
.with_icon(self.imgs.lantern_bg, Vec2::new(24.0, 38.0), Some(UI_MAIN))
.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip)
@ -451,7 +449,7 @@ impl<'a> Widget for Bag<'a> {
(item.name(), item.description())
});
slot_maker
.fabricate(ArmorSlot::Ring, [45.0; 2])
.fabricate(EquipSlot::Armor(ArmorSlot::Ring), [45.0; 2])
.bottom_left_with_margins_on(state.ids.hands_slot, -55.0, 0.0)
.with_icon(self.imgs.ring_bg, Vec2::new(36.0, 40.0), Some(UI_MAIN))
.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip)
@ -464,7 +462,7 @@ impl<'a> Widget for Bag<'a> {
(item.name(), item.description())
});
slot_maker
.fabricate(ArmorSlot::Back, [45.0; 2])
.fabricate(EquipSlot::Armor(ArmorSlot::Back), [45.0; 2])
.down_from(state.ids.lantern_slot, 10.0)
.with_icon(self.imgs.back_bg, Vec2::new(33.0, 40.0), Some(UI_MAIN))
.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip)
@ -477,7 +475,7 @@ impl<'a> Widget for Bag<'a> {
(item.name(), item.description())
});
slot_maker
.fabricate(ArmorSlot::Feet, [45.0; 2])
.fabricate(EquipSlot::Armor(ArmorSlot::Feet), [45.0; 2])
.down_from(state.ids.ring_slot, 10.0)
.with_icon(self.imgs.feet_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN))
.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip)
@ -490,7 +488,7 @@ impl<'a> Widget for Bag<'a> {
(item.name(), item.description())
});
slot_maker
.fabricate(ArmorSlot::Tabard, [70.0; 2])
.fabricate(EquipSlot::Armor(ArmorSlot::Tabard), [70.0; 2])
.top_right_with_margins_on(state.ids.bg_frame, 80.5, 53.0)
.with_icon(self.imgs.tabard_bg, Vec2::new(60.0, 60.0), Some(UI_MAIN))
.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip)
@ -501,7 +499,7 @@ impl<'a> Widget for Bag<'a> {
|item| (item.name(), item.description()),
);
slot_maker
.fabricate(ArmorSlot::Mainhand, [85.0; 2])
.fabricate(EquipSlot::Mainhand, [85.0; 2])
.bottom_right_with_margins_on(state.ids.back_slot, -95.0, 0.0)
.with_icon(self.imgs.mainhand_bg, Vec2::new(75.0, 75.0), Some(UI_MAIN))
.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip)
@ -512,7 +510,7 @@ impl<'a> Widget for Bag<'a> {
|item| (item.name(), item.description()),
);
slot_maker
.fabricate(ArmorSlot::Offhand, [85.0; 2])
.fabricate(EquipSlot::Offhand, [85.0; 2])
.bottom_left_with_margins_on(state.ids.feet_slot, -95.0, 0.0)
.with_icon(self.imgs.offhand_bg, Vec2::new(75.0, 75.0), Some(UI_MAIN))
.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip)
@ -681,9 +679,6 @@ impl<'a> Widget for Bag<'a> {
}
}
// Drop selected item
// if ui.widget_input(ui.window).clicks().left().next().is_some() {
// Stats Button
if Button::image(self.imgs.button)
.w_h(92.0, 22.0)

View File

@ -4,7 +4,7 @@ use common::{
comp::item::{
armor::Armor,
tool::{Tool, ToolKind},
Consumable, Ingredient, Item, ItemKind, Utility,
Consumable, Ingredient, Item, ItemKind, Lantern, Utility,
},
figure::Segment,
};
@ -20,6 +20,7 @@ use vek::*;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ItemKey {
Tool(ToolKind),
Lantern(Lantern),
Armor(Armor),
Utility(Utility),
Consumable(Consumable),
@ -30,6 +31,7 @@ impl From<&Item> for ItemKey {
fn from(item: &Item) -> Self {
match &item.kind {
ItemKind::Tool(Tool { kind, .. }) => ItemKey::Tool(kind.clone()),
ItemKind::Lantern(kind) => ItemKey::Lantern(kind.clone()),
ItemKind::Armor { kind, .. } => ItemKey::Armor(kind.clone()),
ItemKind::Utility { kind, .. } => ItemKey::Utility(kind.clone()),
ItemKind::Consumable { kind, .. } => ItemKey::Consumable(kind.clone()),

View File

@ -232,11 +232,9 @@ pub enum Event {
ToggleDebug(bool),
UiScale(ScaleChange),
CharacterSelection,
UseInventorySlot(usize),
SwapInventorySlots(usize, usize),
SwapInventoryArmor(usize, slots::ArmorSlot),
SwapArmorSlots(slots::ArmorSlot, slots::ArmorSlot),
DropInventorySlot(usize),
UseSlot(comp::slot::Slot),
SwapSlots(comp::slot::Slot, comp::slot::Slot),
DropSlot(comp::slot::Slot),
Logout,
Quit,
ChangeLanguage(LanguageMetadata),
@ -1660,7 +1658,6 @@ impl Hud {
)
.set(self.ids.bag, ui_widgets)
{
Some(bag::Event::HudEvent(event)) => events.push(event),
Some(bag::Event::Stats) => self.show.stats = !self.show.stats,
Some(bag::Event::Close) => {
self.show.bag(false);
@ -1961,30 +1958,32 @@ impl Hud {
// Maintain slot manager
for event in self.slot_manager.maintain(ui_widgets) {
use comp::slot::Slot;
use slots::SlotKind;
let to_slot = |slot_kind| match slot_kind {
SlotKind::Inventory(i) => Some(Slot::Inventory(i.0)),
SlotKind::Equip(e) => Some(Slot::Equip(e)),
//SlotKind::Hotbar(h) => None,
};
match event {
slot::Event::Dragged(SlotKind::Inventory(from), SlotKind::Inventory(to)) => {
// Swap between inventory slots
events.push(Event::SwapInventorySlots(from.0, to.0));
slot::Event::Dragged(a, b) => {
// Swap between slots
if let (Some(a), Some(b)) = (to_slot(a), to_slot(b)) {
events.push(Event::SwapSlots(a, b));
}
},
slot::Event::Dragged(SlotKind::Armor(from), SlotKind::Armor(to)) => {
// Swap between two armor slots
events.push(Event::SwapArmorSlots(from, to));
slot::Event::Dropped(from) => {
// Drop item
if let Some(from) = to_slot(from) {
events.push(Event::DropSlot(from));
}
},
slot::Event::Dragged(SlotKind::Inventory(inv), SlotKind::Armor(arm))
| slot::Event::Dragged(SlotKind::Armor(arm), SlotKind::Inventory(inv)) => {
// Swap between inventory and armor slot
events.push(Event::SwapInventoryArmor(inv.0, arm));
slot::Event::Used(from) => {
// Item used (selected and then clicked again)
if let Some(from) = to_slot(from) {
events.push(Event::UseSlot(from));
}
},
slot::Event::Dropped(SlotKind::Inventory(from)) => {
// Drop item from inventory
events.push(Event::DropInventorySlot(from.0));
},
slot::Event::Used(SlotKind::Inventory(inv)) => {
// Item in inventory used (selected and then clicked again)
events.push(Event::UseInventorySlot(inv.0));
},
_ => {},
}
}

View File

@ -3,10 +3,12 @@ use crate::ui::slot::{self, SlotKey, SumSlot};
use common::comp::{item::ItemKind, Inventory, Loadout};
use conrod_core::image;
pub use common::comp::slot::{ArmorSlot, EquipSlot};
#[derive(Clone, Copy, PartialEq)]
pub enum SlotKind {
Inventory(InventorySlot),
Armor(ArmorSlot),
Equip(EquipSlot),
/*Hotbar(HotbarSlot),
*Spellbook(SpellbookSlot), TODO */
}
@ -16,24 +18,6 @@ pub type SlotManager = slot::SlotManager<SlotKind>;
#[derive(Clone, Copy, PartialEq)]
pub struct InventorySlot(pub usize);
#[derive(Clone, Copy, PartialEq)]
pub enum ArmorSlot {
Head,
Neck,
Shoulders,
Chest,
Hands,
Ring,
Lantern,
Back,
Belt,
Legs,
Feet,
Mainhand,
Offhand,
Tabard,
}
/*#[derive(Clone, Copy, PartialEq)]
pub enum HotbarSlot {
One,
@ -59,7 +43,7 @@ impl SlotKey<Inventory, ItemImgs> for InventorySlot {
source
.get(self.0)
.and_then(|item| match item.kind {
ItemKind::Tool { .. } | ItemKind::Armor { .. } => None,
ItemKind::Tool { .. } | ItemKind::Lantern(_) | ItemKind::Armor { .. } => None,
ItemKind::Utility { amount, .. }
| ItemKind::Consumable { amount, .. }
| ItemKind::Ingredient { amount, .. } => Some(amount),
@ -72,26 +56,25 @@ impl SlotKey<Inventory, ItemImgs> for InventorySlot {
}
}
impl SlotKey<Loadout, ItemImgs> for ArmorSlot {
impl SlotKey<Loadout, ItemImgs> for EquipSlot {
type ImageKey = ItemKey;
fn image_key(&self, source: &Loadout) -> Option<Self::ImageKey> {
let item = match self {
ArmorSlot::Shoulders => source.shoulder.as_ref(),
ArmorSlot::Chest => source.chest.as_ref(),
ArmorSlot::Belt => source.belt.as_ref(),
ArmorSlot::Hands => source.hand.as_ref(),
ArmorSlot::Legs => source.pants.as_ref(),
ArmorSlot::Feet => source.foot.as_ref(),
ArmorSlot::Back => source.back.as_ref(),
ArmorSlot::Ring => source.ring.as_ref(),
ArmorSlot::Neck => source.neck.as_ref(),
ArmorSlot::Head => source.head.as_ref(),
ArmorSlot::Lantern => source.lantern.as_ref(),
ArmorSlot::Tabard => source.tabard.as_ref(),
ArmorSlot::Mainhand => source.active_item.as_ref().map(|i| &i.item),
ArmorSlot::Offhand => source.second_item.as_ref().map(|i| &i.item),
_ => None,
EquipSlot::Armor(ArmorSlot::Shoulders) => source.shoulder.as_ref(),
EquipSlot::Armor(ArmorSlot::Chest) => source.chest.as_ref(),
EquipSlot::Armor(ArmorSlot::Belt) => source.belt.as_ref(),
EquipSlot::Armor(ArmorSlot::Hands) => source.hand.as_ref(),
EquipSlot::Armor(ArmorSlot::Legs) => source.pants.as_ref(),
EquipSlot::Armor(ArmorSlot::Feet) => source.foot.as_ref(),
EquipSlot::Armor(ArmorSlot::Back) => source.back.as_ref(),
EquipSlot::Armor(ArmorSlot::Ring) => source.ring.as_ref(),
EquipSlot::Armor(ArmorSlot::Neck) => source.neck.as_ref(),
EquipSlot::Armor(ArmorSlot::Head) => source.head.as_ref(),
EquipSlot::Armor(ArmorSlot::Tabard) => source.tabard.as_ref(),
EquipSlot::Mainhand => source.active_item.as_ref().map(|i| &i.item),
EquipSlot::Offhand => source.second_item.as_ref().map(|i| &i.item),
EquipSlot::Lantern => source.lantern.as_ref(),
};
item.map(Into::into)
@ -132,8 +115,8 @@ impl From<InventorySlot> for SlotKind {
fn from(inventory: InventorySlot) -> Self { Self::Inventory(inventory) }
}
impl From<ArmorSlot> for SlotKind {
fn from(armor: ArmorSlot) -> Self { Self::Armor(armor) }
impl From<EquipSlot> for SlotKind {
fn from(equip: EquipSlot) -> Self { Self::Equip(equip) }
}
//impl From<HotbarSlot> for SlotKind {

View File

@ -12,9 +12,9 @@ use common::{
dragon, fish_medium, fish_small,
humanoid::{Body, BodyType, EyeColor, Eyebrows, Race, Skin},
item::{
armor::{Armor, Back, Belt, Chest, Foot, Hand, Head, Lantern, Pants, Shoulder, Tabard},
armor::{Armor, Back, Belt, Chest, Foot, Hand, Head, Pants, Shoulder, Tabard},
tool::{Tool, ToolKind},
ItemKind,
ItemKind, Lantern,
},
object,
quadruped_medium::{BodyType as QMBodyType, Species as QMSpecies},
@ -701,21 +701,18 @@ impl HumArmorLanternSpec {
}
pub fn mesh_lantern(&self, body: &Body, loadout: &Loadout) -> Mesh<FigurePipeline> {
let spec = if let Some(ItemKind::Armor {
kind: Armor::Lantern(lantern),
..
}) = loadout.lantern.as_ref().map(|i| &i.kind)
{
match self.0.map.get(&lantern) {
Some(spec) => spec,
None => {
error!("No lantern specification exists for {:?}", lantern);
return load_mesh("not_found", Vec3::new(-4.0, -3.5, 2.0));
},
}
} else {
&self.0.default
};
let spec =
if let Some(ItemKind::Lantern(lantern)) = loadout.lantern.as_ref().map(|i| &i.kind) {
match self.0.map.get(&lantern) {
Some(spec) => spec,
None => {
error!("No lantern specification exists for {:?}", lantern);
return load_mesh("not_found", Vec3::new(-4.0, -3.5, 2.0));
},
}
} else {
&self.0.default
};
let lantern_segment = color_segment(
graceful_load_mat_segment(&spec.vox_spec.0),

View File

@ -643,22 +643,9 @@ 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)
},
HudEvent::SwapInventoryArmor(inv_slot, armor_slot) => {
// Swapping between inventory and armor slot
// TODO: don't do this
self.client.borrow_mut().use_inventory_slot(inv_slot)
},
HudEvent::SwapArmorSlots(from, to) => {
// Only works with rings currently
// TODO: implement
},
HudEvent::DropInventorySlot(x) => {
self.client.borrow_mut().drop_inventory_slot(x)
},
HudEvent::UseSlot(x) => self.client.borrow_mut().use_slot(x),
HudEvent::SwapSlots(a, b) => self.client.borrow_mut().swap_slots(a, b),
HudEvent::DropSlot(x) => self.client.borrow_mut().drop_slot(x),
HudEvent::ChangeFOV(new_fov) => {
global_state.settings.graphics.fov = new_fov;
global_state.settings.save_to_file_warn();

View File

@ -153,9 +153,9 @@ where
}
}
// If dragging and mouse if released check if there is a slot widget under the
// If dragging and mouse is released check if there is a slot widget under the
// mouse
if let ManagerState::Dragging(id, slot, content_img) = &self.state {
if let ManagerState::Dragging(_, slot, content_img) = &self.state {
let content_img = *content_img;
let input = &ui.global_input().current;
if let mouse::ButtonPosition::Up = input.mouse.buttons.left() {