mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Implemented inventory sorting
This commit is contained in:
parent
e7e7565440
commit
a4cdb89987
@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- One handed weapons can now be used and found in the world
|
||||
- Players can now opt-in to server-authoritiative physics in gameplay settings.
|
||||
- Added `/server_physics` admin command.
|
||||
- Sort inventory button
|
||||
|
||||
|
||||
### Changed
|
||||
|
BIN
assets/voxygen/element/ui/bag/buttons/inv_sort.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/ui/bag/buttons/inv_sort.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/ui/bag/buttons/inv_sort_hover.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/ui/bag/buttons/inv_sort_hover.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/ui/bag/buttons/inv_sort_press.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/ui/bag/buttons/inv_sort_press.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -33,6 +33,9 @@
|
||||
"hud.bag.combat_rating_desc": "Calculated from your\nequipment and health.",
|
||||
"hud.bag.protection_desc": "Damage reduction through armor",
|
||||
"hud.bag.stun_res_desc": "Resilience against being stunned by consecutive hits.\nRegenerates like Stamina.",
|
||||
"hud.bag.sort_by_name": "Sort by Name",
|
||||
"hud.bag.sort_by_quality": "Sort by Quality",
|
||||
"hud.bag.sort_by_category": "Sort by Category",
|
||||
},
|
||||
|
||||
|
||||
|
@ -892,6 +892,10 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sort_inventory(&mut self) {
|
||||
self.control_action(ControlAction::InventoryAction(InventoryAction::Sort));
|
||||
}
|
||||
|
||||
pub fn perform_trade_action(&mut self, action: TradeAction) {
|
||||
if let Some((id, _, _)) = self.pending_trade {
|
||||
if let TradeAction::Decline = action {
|
||||
|
@ -23,6 +23,7 @@ pub enum InventoryEvent {
|
||||
Drop(InvSlotId),
|
||||
SplitDrop(InvSlotId),
|
||||
CraftRecipe(String),
|
||||
Sort,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@ -30,6 +31,7 @@ pub enum InventoryAction {
|
||||
Swap(EquipSlot, Slot),
|
||||
Drop(EquipSlot),
|
||||
Use(Slot),
|
||||
Sort,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@ -42,6 +44,7 @@ pub enum InventoryManip {
|
||||
Drop(Slot),
|
||||
SplitDrop(Slot),
|
||||
CraftRecipe(String),
|
||||
Sort,
|
||||
}
|
||||
|
||||
impl From<InventoryAction> for InventoryManip {
|
||||
@ -50,6 +53,7 @@ impl From<InventoryAction> for InventoryManip {
|
||||
InventoryAction::Use(slot) => Self::Use(slot),
|
||||
InventoryAction::Swap(equip, slot) => Self::Swap(Slot::Equip(equip), slot),
|
||||
InventoryAction::Drop(equip) => Self::Drop(Slot::Equip(equip)),
|
||||
InventoryAction::Sort => Self::Sort,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,6 +72,7 @@ impl From<InventoryEvent> for InventoryManip {
|
||||
InventoryEvent::Drop(inv) => Self::Drop(Slot::Inventory(inv)),
|
||||
InventoryEvent::SplitDrop(inv) => Self::SplitDrop(Slot::Inventory(inv)),
|
||||
InventoryEvent::CraftRecipe(recipe) => Self::CraftRecipe(recipe),
|
||||
InventoryEvent::Sort => Self::Sort,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ impl Lantern {
|
||||
pub struct Glider {
|
||||
pub kind: String,
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Copy)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Copy, PartialOrd, Ord)]
|
||||
pub enum Quality {
|
||||
Low, // Grey
|
||||
Common, // UI Main Color
|
||||
|
@ -1,15 +1,14 @@
|
||||
use core::ops::Not;
|
||||
use std::{collections::HashMap, convert::TryFrom, mem, ops::Range};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::{Component, DerefFlaggedStorage};
|
||||
use specs_idvs::IdvStorage;
|
||||
use std::{collections::HashMap, convert::TryFrom, mem, ops::Range};
|
||||
use tracing::{debug, trace, warn};
|
||||
|
||||
use crate::{
|
||||
comp::{
|
||||
inventory::{
|
||||
item::{ItemDef, ItemKind, MaterialStatManifest},
|
||||
item::{ItemDef, ItemKind, MaterialStatManifest, TagExampleInfo},
|
||||
loadout::Loadout,
|
||||
slot::{EquipSlot, Slot, SlotError},
|
||||
},
|
||||
@ -34,6 +33,7 @@ const DEFAULT_INVENTORY_SLOTS: usize = 18;
|
||||
/// NOTE: Do not add a PartialEq instance for Inventory; that's broken!
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Inventory {
|
||||
next_sort_order: InventorySortOrder,
|
||||
loadout: Loadout,
|
||||
/// The "built-in" slots belonging to the inventory itself, all other slots
|
||||
/// are provided by equipped items
|
||||
@ -48,6 +48,23 @@ pub enum Error {
|
||||
Full(Vec<Item>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum InventorySortOrder {
|
||||
Name,
|
||||
Quality,
|
||||
Tag,
|
||||
}
|
||||
|
||||
impl InventorySortOrder {
|
||||
fn next(&self) -> InventorySortOrder {
|
||||
match self {
|
||||
InventorySortOrder::Name => InventorySortOrder::Quality,
|
||||
InventorySortOrder::Quality => InventorySortOrder::Tag,
|
||||
InventorySortOrder::Tag => InventorySortOrder::Name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the Inventory of an entity. The inventory has 18 "built-in"
|
||||
/// slots, with further slots being provided by items equipped in the Loadout
|
||||
/// sub-struct. Inventory slots are indexed by `InvSlotId` which is
|
||||
@ -66,6 +83,7 @@ impl Inventory {
|
||||
|
||||
pub fn new_with_loadout(loadout: Loadout) -> Inventory {
|
||||
Inventory {
|
||||
next_sort_order: InventorySortOrder::Name,
|
||||
loadout,
|
||||
slots: vec![None; DEFAULT_INVENTORY_SLOTS],
|
||||
}
|
||||
@ -99,6 +117,33 @@ impl Inventory {
|
||||
)
|
||||
}
|
||||
|
||||
/// Sorts the inventory using the next sort order
|
||||
pub fn sort(&mut self) {
|
||||
let sort_order = self.next_sort_order;
|
||||
let mut items: Vec<Item> = self.slots_mut().filter_map(|x| mem::take(x)).collect();
|
||||
|
||||
items.sort_by(|a, b| match sort_order {
|
||||
InventorySortOrder::Name => Ord::cmp(a.name(), b.name()),
|
||||
// Quality is sorted in reverse since we want high quality items first
|
||||
InventorySortOrder::Quality => Ord::cmp(&b.quality(), &a.quality()),
|
||||
InventorySortOrder::Tag => Ord::cmp(
|
||||
a.tags.first().map_or("", |tag| tag.name()),
|
||||
b.tags.first().map_or("", |tag| tag.name()),
|
||||
),
|
||||
});
|
||||
|
||||
self.push_all(items.into_iter()).expect(
|
||||
"It is impossible for there to be insufficient inventory space when sorting the \
|
||||
inventory",
|
||||
);
|
||||
|
||||
self.next_sort_order = self.next_sort_order.next();
|
||||
}
|
||||
|
||||
/// Returns the sort order that will be used when Inventory::sort() is next
|
||||
/// called
|
||||
pub fn next_sort_order(&self) -> InventorySortOrder { self.next_sort_order }
|
||||
|
||||
/// Adds a new item to the first fitting group of the inventory or starts a
|
||||
/// new group. Returns the item in an error if no space was found, otherwise
|
||||
/// returns the found slot.
|
||||
|
@ -15,6 +15,7 @@ lazy_static! {
|
||||
fn push_full() {
|
||||
let msm = &MaterialStatManifest::default();
|
||||
let mut inv = Inventory {
|
||||
next_sort_order: InventorySortOrder::Name,
|
||||
slots: TEST_ITEMS.iter().map(|a| Some(a.duplicate(msm))).collect(),
|
||||
loadout: LoadoutBuilder::new().build(),
|
||||
};
|
||||
@ -29,6 +30,7 @@ fn push_full() {
|
||||
fn push_all_full() {
|
||||
let msm = &MaterialStatManifest::default();
|
||||
let mut inv = Inventory {
|
||||
next_sort_order: InventorySortOrder::Name,
|
||||
slots: TEST_ITEMS.iter().map(|a| Some(a.duplicate(msm))).collect(),
|
||||
loadout: LoadoutBuilder::new().build(),
|
||||
};
|
||||
@ -50,6 +52,7 @@ fn push_all_full() {
|
||||
fn push_unique_all_full() {
|
||||
let msm = &MaterialStatManifest::default();
|
||||
let mut inv = Inventory {
|
||||
next_sort_order: InventorySortOrder::Name,
|
||||
slots: TEST_ITEMS.iter().map(|a| Some(a.duplicate(msm))).collect(),
|
||||
loadout: LoadoutBuilder::new().build(),
|
||||
};
|
||||
@ -63,6 +66,7 @@ fn push_unique_all_full() {
|
||||
fn push_all_empty() {
|
||||
let msm = &MaterialStatManifest::default();
|
||||
let mut inv = Inventory {
|
||||
next_sort_order: InventorySortOrder::Name,
|
||||
slots: vec![None, None],
|
||||
loadout: LoadoutBuilder::new().build(),
|
||||
};
|
||||
@ -76,6 +80,7 @@ fn push_all_empty() {
|
||||
fn push_all_unique_empty() {
|
||||
let msm = &MaterialStatManifest::default();
|
||||
let mut inv = Inventory {
|
||||
next_sort_order: InventorySortOrder::Name,
|
||||
slots: vec![None, None],
|
||||
loadout: LoadoutBuilder::new().build(),
|
||||
};
|
||||
|
@ -611,7 +611,12 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
}
|
||||
}
|
||||
},
|
||||
comp::InventoryManip::Sort => {
|
||||
inventory.sort();
|
||||
drop(inventories);
|
||||
},
|
||||
}
|
||||
|
||||
// Drop items, Debug items should simply disappear when dropped
|
||||
for (pos, ori, mut item) in dropped_items
|
||||
.into_iter()
|
||||
|
@ -19,6 +19,7 @@ use common::{
|
||||
assets::AssetExt,
|
||||
combat::{combat_rating, Damage},
|
||||
comp::{
|
||||
inventory::InventorySortOrder,
|
||||
item::{ItemDef, MaterialStatManifest, Quality},
|
||||
Body, Energy, Health, Inventory, Poise, SkillSet, Stats,
|
||||
},
|
||||
@ -26,7 +27,7 @@ use common::{
|
||||
use conrod_core::{
|
||||
color,
|
||||
widget::{self, Button, Image, Rectangle, Scrollbar, State as ConrodState, Text},
|
||||
widget_ids, Color, Colorable, Positionable, Sizeable, UiCell, Widget, WidgetCommon,
|
||||
widget_ids, Color, Colorable, Positionable, Scalar, Sizeable, UiCell, Widget, WidgetCommon,
|
||||
};
|
||||
|
||||
use crate::hud::slots::SlotKind;
|
||||
@ -423,6 +424,7 @@ widget_ids! {
|
||||
space_txt,
|
||||
inventory_title,
|
||||
inventory_title_bg,
|
||||
inventory_sort,
|
||||
scrollbar_bg,
|
||||
scrollbar_slots,
|
||||
tab_1,
|
||||
@ -540,6 +542,7 @@ pub struct BagState {
|
||||
pub enum Event {
|
||||
BagExpand,
|
||||
Close,
|
||||
SortInventory,
|
||||
}
|
||||
|
||||
impl<'a> Widget for Bag<'a> {
|
||||
@ -563,6 +566,7 @@ impl<'a> Widget for Bag<'a> {
|
||||
#[allow(clippy::useless_format)] // TODO: Pending review in #587
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { state, ui, .. } = args;
|
||||
let i18n = &self.localized_strings;
|
||||
|
||||
let mut event = None;
|
||||
let bag_tooltip = Tooltip::new({
|
||||
@ -681,27 +685,42 @@ impl<'a> Widget for Bag<'a> {
|
||||
} else {
|
||||
self.imgs.expand_btn_press
|
||||
});
|
||||
|
||||
// Only show expand button when it's needed...
|
||||
let space_max = inventory.slots().count();
|
||||
if space_max > 45 && !self.show.bag_inv {
|
||||
if inventory.slots().count() > 45 || self.show.bag_inv {
|
||||
let expand_btn_top = if self.show.bag_inv { 53.0 } else { 460.0 };
|
||||
if expand_btn
|
||||
.top_left_with_margins_on(state.bg_ids.bg_frame, 460.0, 211.5)
|
||||
.top_left_with_margins_on(state.bg_ids.bg_frame, expand_btn_top, 211.5)
|
||||
.with_tooltip(self.tooltip_manager, &txt, "", &bag_tooltip, TEXT_COLOR)
|
||||
.set(state.ids.bag_expand_btn, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
event = Some(Event::BagExpand);
|
||||
}
|
||||
} else if self.show.bag_inv {
|
||||
//... but always show it when the bag is expanded
|
||||
if expand_btn
|
||||
.top_left_with_margins_on(state.bg_ids.bg_frame, 53.0, 211.5)
|
||||
.with_tooltip(self.tooltip_manager, &txt, "", &bag_tooltip, TEXT_COLOR)
|
||||
.set(state.ids.bag_expand_btn, ui)
|
||||
}
|
||||
|
||||
// Sort inventory button
|
||||
let inv_sort_btn_top: Scalar = if !self.show.bag_inv { 460.0 } else { 53.0 };
|
||||
if Button::image(self.imgs.inv_sort_btn)
|
||||
.w_h(30.0, 17.0)
|
||||
.hover_image(self.imgs.inv_sort_btn_hover)
|
||||
.press_image(self.imgs.inv_sort_btn_press)
|
||||
.top_left_with_margins_on(state.bg_ids.bg_frame, inv_sort_btn_top, 47.0)
|
||||
.with_tooltip(
|
||||
self.tooltip_manager,
|
||||
match inventory.next_sort_order() {
|
||||
InventorySortOrder::Name => i18n.get("hud.bag.sort_by_name"),
|
||||
InventorySortOrder::Quality => i18n.get("hud.bag.sort_by_quality"),
|
||||
InventorySortOrder::Tag => i18n.get("hud.bag.sort_by_category"),
|
||||
},
|
||||
"",
|
||||
&tooltip,
|
||||
color::WHITE,
|
||||
)
|
||||
.set(state.ids.inventory_sort, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
event = Some(Event::BagExpand);
|
||||
}
|
||||
event = Some(Event::SortInventory);
|
||||
}
|
||||
|
||||
// Armor Slots
|
||||
@ -726,7 +745,6 @@ impl<'a> Widget for Bag<'a> {
|
||||
slot_manager: Some(self.slot_manager),
|
||||
pulse: self.pulse,
|
||||
};
|
||||
let i18n = &self.localized_strings;
|
||||
let filled_slot = self.imgs.armor_slot;
|
||||
if !self.show.bag_inv {
|
||||
// Stat icons and text
|
||||
@ -1179,6 +1197,7 @@ impl<'a> Widget for Bag<'a> {
|
||||
.set(state.ids.offhand_slot, ui)
|
||||
}
|
||||
}
|
||||
|
||||
// Bag 1
|
||||
let bag1_item = inventory
|
||||
.equipped(EquipSlot::Armor(ArmorSlot::Bag1))
|
||||
|
@ -407,6 +407,9 @@ image_ids! {
|
||||
expand_btn: "voxygen.element.ui.bag.buttons.inv_expand",
|
||||
expand_btn_hover: "voxygen.element.ui.bag.buttons.inv_expand_hover",
|
||||
expand_btn_press: "voxygen.element.ui.bag.buttons.inv_expand_press",
|
||||
inv_sort_btn: "voxygen.element.ui.bag.buttons.inv_sort",
|
||||
inv_sort_btn_hover: "voxygen.element.ui.bag.buttons.inv_sort_hover",
|
||||
inv_sort_btn_press: "voxygen.element.ui.bag.buttons.inv_sort_press",
|
||||
coin_ico: "voxygen.element.items.coin",
|
||||
cheese_ico: "voxygen.element.items.item_cheese",
|
||||
inv_bg_armor: "voxygen.element.ui.bag.inv_bg_0",
|
||||
|
@ -372,6 +372,7 @@ pub enum Event {
|
||||
},
|
||||
DropSlot(comp::slot::Slot),
|
||||
SplitDropSlot(comp::slot::Slot),
|
||||
SortInventory,
|
||||
ChangeHotbarState(Box<HotbarState>),
|
||||
TradeAction(TradeAction),
|
||||
Ability3(bool),
|
||||
@ -2330,6 +2331,7 @@ impl Hud {
|
||||
self.force_ungrab = true
|
||||
};
|
||||
},
|
||||
Some(bag::Event::SortInventory) => self.events.push(Event::SortInventory),
|
||||
None => {},
|
||||
}
|
||||
}
|
||||
|
@ -1126,6 +1126,9 @@ impl PlayState for SessionState {
|
||||
client.disable_lantern();
|
||||
}
|
||||
},
|
||||
HudEvent::SortInventory => {
|
||||
self.client.borrow_mut().sort_inventory();
|
||||
},
|
||||
HudEvent::ChangeHotbarState(state) => {
|
||||
let client = self.client.borrow();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user