Basic hotbar functionality

This commit is contained in:
Imbris 2020-04-11 02:33:06 -04:00 committed by Pfauenauge90
parent 77d13376d3
commit f81f91162d
9 changed files with 441 additions and 157 deletions

97
voxygen/src/hud/hotbar.rs Normal file
View File

@ -0,0 +1,97 @@
#[derive(Clone, Copy, PartialEq)]
pub enum Slot {
One = 0,
Two = 1,
Three = 2,
Four = 3,
Five = 4,
Six = 5,
Seven = 6,
Eight = 7,
Nine = 8,
Ten = 9,
}
#[derive(Clone, Copy, PartialEq)]
pub enum SlotContents {
Inventory(usize),
Ability3,
}
pub struct State {
slots: [Option<SlotContents>; 10],
inputs: [bool; 10],
}
impl State {
pub fn new() -> Self {
Self {
slots: [None; 10],
inputs: [false; 10],
}
}
/// Returns true is the button was just pressed
pub fn process_input(&mut self, slot: Slot, state: bool) -> bool {
let slot = slot as usize;
let just_pressed = !self.inputs[slot] && state;
self.inputs[slot] = state;
just_pressed
}
pub fn get(&self, slot: Slot) -> Option<SlotContents> { self.slots[slot as usize] }
pub fn swap(&mut self, a: Slot, b: Slot) { self.slots.swap(a as usize, b as usize); }
pub fn clear_slot(&mut self, slot: Slot) { self.slots[slot as usize] = None; }
pub fn add_inventory_link(&mut self, slot: Slot, inventory_index: usize) {
self.slots[slot as usize] = Some(SlotContents::Inventory(inventory_index));
}
// TODO: remove
// Adds ability3 slot if it is missing and should be present
// Removes if it is there and shouldn't be present
pub fn maintain_ability3(&mut self, client: &client::Client) {
use specs::WorldExt;
let loadouts = client.state().ecs().read_storage::<common::comp::Loadout>();
let loadout = loadouts.get(client.entity());
let should_be_present = if let Some(loadout) = loadout {
loadout
.active_item
.as_ref()
.map(|i| &i.item.kind)
.filter(|kind| {
use common::comp::item::{
tool::{StaffKind, Tool, ToolKind},
ItemKind,
};
matches!(
kind,
ItemKind::Tool(Tool {
kind: ToolKind::Staff(StaffKind::BasicStaff),
..
})
)
})
.is_some()
} else {
false
};
if should_be_present {
if !self
.slots
.iter()
.any(|s| matches!(s, Some(SlotContents::Ability3)))
{
self.slots[0] = Some(SlotContents::Ability3);
}
} else {
self.slots
.iter_mut()
.filter(|s| matches!(s, Some(SlotContents::Ability3)))
.for_each(|s| *s = None)
}
}
}

View File

@ -2,6 +2,7 @@ mod bag;
mod buttons;
mod chat;
mod esc_menu;
mod hotbar;
mod img_ids;
mod item_imgs;
mod map;
@ -235,6 +236,7 @@ pub enum Event {
UseSlot(comp::slot::Slot),
SwapSlots(comp::slot::Slot, comp::slot::Slot),
DropSlot(comp::slot::Slot),
Ability3(bool),
Logout,
Quit,
ChangeLanguage(LanguageMetadata),
@ -442,6 +444,8 @@ pub struct Hud {
velocity: f32,
voxygen_i18n: std::sync::Arc<VoxygenLocalization>,
slot_manager: slots::SlotManager,
hotbar: hotbar::State,
events: Vec<Event>,
}
impl Hud {
@ -513,6 +517,8 @@ impl Hud {
velocity: 0.0,
voxygen_i18n,
slot_manager,
hotbar: hotbar::State::new(),
events: Vec::new(),
}
}
@ -529,7 +535,7 @@ impl Hud {
debug_info: DebugInfo,
dt: Duration,
) -> Vec<Event> {
let mut events = Vec::new();
let mut events = std::mem::replace(&mut self.events, Vec::new());
let (ref mut ui_widgets, ref mut tooltip_manager) = self.ui.set_widgets();
// pulse time for pulsating elements
self.pulse = self.pulse + dt.as_secs_f32();
@ -1676,16 +1682,26 @@ impl Hud {
let energies = ecs.read_storage::<comp::Energy>();
let character_states = ecs.read_storage::<comp::CharacterState>();
let controllers = ecs.read_storage::<comp::Controller>();
if let (Some(stats), Some(loadout), Some(energy), Some(character_state), Some(controller)) = (
let inventories = ecs.read_storage::<comp::Inventory>();
if let (
Some(stats),
Some(loadout),
Some(energy),
Some(character_state),
Some(controller),
Some(inventory),
) = (
stats.get(entity),
loadouts.get(entity),
energies.get(entity),
character_states.get(entity),
controllers.get(entity).map(|c| &c.inputs),
inventories.get(entity),
) {
Skillbar::new(
global_state,
&self.imgs,
&self.item_imgs,
&self.fonts,
&stats,
&loadout,
@ -1693,6 +1709,9 @@ impl Hud {
&character_state,
self.pulse,
&controller,
&inventory,
&self.hotbar,
&mut self.slot_manager,
)
.set(self.ids.skillbar, ui_widgets);
}
@ -1959,33 +1978,50 @@ impl Hud {
// Maintain slot manager
for event in self.slot_manager.maintain(ui_widgets) {
use comp::slot::Slot;
use slots::SlotKind;
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,
Inventory(i) => Some(Slot::Inventory(i.0)),
Equip(e) => Some(Slot::Equip(e)),
Hotbar(_) => None,
};
match event {
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));
} else if let (Inventory(i), Hotbar(h)) = (a, b) {
self.hotbar.add_inventory_link(h, i.0);
} else if let (Hotbar(a), Hotbar(b)) = (a, b) {
self.hotbar.swap(a, b);
}
},
slot::Event::Dropped(from) => {
// Drop item
if let Some(from) = to_slot(from) {
events.push(Event::DropSlot(from));
} else if let Hotbar(h) = from {
self.hotbar.clear_slot(h);
}
},
slot::Event::Used(from) => {
// Item used (selected and then clicked again)
if let Some(from) = to_slot(from) {
events.push(Event::UseSlot(from));
} else if let Hotbar(h) = from {
self.hotbar.get(h).map(|s| {
match s {
hotbar::SlotContents::Inventory(i) => {
events.push(Event::UseSlot(comp::slot::Slot::Inventory(i)));
},
hotbar::SlotContents::Ability3 => {}, /* Event::Ability3(true),
* sticks */
}
});
}
},
}
}
self.hotbar.maintain_ability3(client);
events
}
@ -2018,6 +2054,30 @@ impl Hud {
}
pub fn handle_event(&mut self, event: WinEvent, global_state: &mut GlobalState) -> bool {
// Helper
fn handle_slot(
slot: hotbar::Slot,
state: bool,
events: &mut Vec<Event>,
slot_manager: &mut slots::SlotManager,
hotbar: &mut hotbar::State,
) {
if let Some(slots::SlotKind::Inventory(i)) = slot_manager.selected() {
hotbar.add_inventory_link(slot, i.0);
slot_manager.idle();
} else {
let just_pressed = hotbar.process_input(slot, state);
hotbar.get(slot).map(|s| match s {
hotbar::SlotContents::Inventory(i) => {
if just_pressed {
events.push(Event::UseSlot(comp::slot::Slot::Inventory(i)));
}
},
hotbar::SlotContents::Ability3 => events.push(Event::Ability3(state)),
});
}
}
let cursor_grabbed = global_state.window.is_cursor_grabbed();
let handled = match event {
WinEvent::Ui(event) => {
@ -2058,46 +2118,147 @@ impl Hud {
},
// Press key while not typing
WinEvent::InputUpdate(key, true) if !self.typing() => match key {
GameInput::Command => {
WinEvent::InputUpdate(key, state) if !self.typing() => match key {
GameInput::Command if state => {
self.force_chat_input = Some("/".to_owned());
self.force_chat_cursor = Some(Index { line: 0, char: 1 });
self.ui.focus_widget(Some(self.ids.chat));
true
},
GameInput::Map => {
GameInput::Map if state => {
self.show.toggle_map();
true
},
GameInput::Bag => {
GameInput::Bag if state => {
self.show.toggle_bag();
true
},
GameInput::Social => {
GameInput::Social if state => {
self.show.toggle_social();
true
},
GameInput::Spellbook => {
GameInput::Spellbook if state => {
self.show.toggle_spell();
true
},
GameInput::Settings => {
GameInput::Settings if state => {
self.show.toggle_settings();
true
},
GameInput::Help => {
GameInput::Help if state => {
self.show.toggle_help();
true
},
GameInput::ToggleDebug => {
GameInput::ToggleDebug if state => {
global_state.settings.gameplay.toggle_debug =
!global_state.settings.gameplay.toggle_debug;
true
},
GameInput::ToggleIngameUi => {
GameInput::ToggleIngameUi if state => {
self.show.ingame = !self.show.ingame;
true
},
// Skillbar
GameInput::Slot1 => {
handle_slot(
hotbar::Slot::One,
state,
&mut self.events,
&mut self.slot_manager,
&mut self.hotbar,
);
true
},
GameInput::Slot2 => {
handle_slot(
hotbar::Slot::Two,
state,
&mut self.events,
&mut self.slot_manager,
&mut self.hotbar,
);
true
},
GameInput::Slot3 => {
handle_slot(
hotbar::Slot::Three,
state,
&mut self.events,
&mut self.slot_manager,
&mut self.hotbar,
);
true
},
GameInput::Slot4 => {
handle_slot(
hotbar::Slot::Four,
state,
&mut self.events,
&mut self.slot_manager,
&mut self.hotbar,
);
true
},
GameInput::Slot5 => {
handle_slot(
hotbar::Slot::Five,
state,
&mut self.events,
&mut self.slot_manager,
&mut self.hotbar,
);
true
},
GameInput::Slot6 => {
handle_slot(
hotbar::Slot::Six,
state,
&mut self.events,
&mut self.slot_manager,
&mut self.hotbar,
);
true
},
GameInput::Slot7 => {
handle_slot(
hotbar::Slot::Seven,
state,
&mut self.events,
&mut self.slot_manager,
&mut self.hotbar,
);
true
},
GameInput::Slot8 => {
handle_slot(
hotbar::Slot::Eight,
state,
&mut self.events,
&mut self.slot_manager,
&mut self.hotbar,
);
true
},
GameInput::Slot9 => {
handle_slot(
hotbar::Slot::Nine,
state,
&mut self.events,
&mut self.slot_manager,
&mut self.hotbar,
);
true
},
GameInput::Slot10 => {
handle_slot(
hotbar::Slot::Ten,
state,
&mut self.events,
&mut self.slot_manager,
&mut self.hotbar,
);
true
},
_ => false,
},
// Else the player is typing in chat

View File

@ -1,10 +1,13 @@
use super::{
img_ids::Imgs, BarNumbers, ShortcutNumbers, XpBar, BLACK, CRITICAL_HP_COLOR, HP_COLOR,
LOW_HP_COLOR, MANA_COLOR, TEXT_COLOR, XP_COLOR,
hotbar, img_ids::Imgs, item_imgs::ItemImgs, slots, BarNumbers, ShortcutNumbers, XpBar, BLACK,
CRITICAL_HP_COLOR, HP_COLOR, LOW_HP_COLOR, MANA_COLOR, TEXT_COLOR, XP_COLOR,
};
use crate::{
i18n::{i18n_asset_key, VoxygenLocalization},
ui::fonts::ConrodVoxygenFonts,
ui::{
fonts::ConrodVoxygenFonts,
slot::{ContentSize, SlotMaker},
},
window::GameInput,
GlobalState,
};
@ -15,7 +18,7 @@ use common::{
tool::{DebugKind, StaffKind, Tool, ToolKind},
ItemKind,
},
CharacterState, ControllerInputs, Energy, Loadout, Stats,
CharacterState, ControllerInputs, Energy, Inventory, Loadout, Stats,
},
};
use conrod_core::{
@ -23,8 +26,8 @@ use conrod_core::{
widget::{self, Button, Image, Rectangle, Text},
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
};
use std::time::{Duration, Instant};
use vek::*;
/*
use const_tweaker::tweak;
#[tweak(min = 0.0, max = 1.0, step = 0.01)]
@ -130,12 +133,16 @@ pub enum ResourceType {
pub struct Skillbar<'a> {
global_state: &'a GlobalState,
imgs: &'a Imgs,
item_imgs: &'a ItemImgs,
fonts: &'a ConrodVoxygenFonts,
stats: &'a Stats,
loadout: &'a Loadout,
energy: &'a Energy,
character_state: &'a CharacterState,
controller: &'a ControllerInputs,
inventory: &'a Inventory,
hotbar: &'a hotbar::State,
slot_manager: &'a mut slots::SlotManager,
pulse: f32,
#[conrod(common_builder)]
common: widget::CommonBuilder,
@ -146,6 +153,7 @@ impl<'a> Skillbar<'a> {
pub fn new(
global_state: &'a GlobalState,
imgs: &'a Imgs,
item_imgs: &'a ItemImgs,
fonts: &'a ConrodVoxygenFonts,
stats: &'a Stats,
loadout: &'a Loadout,
@ -153,10 +161,14 @@ impl<'a> Skillbar<'a> {
character_state: &'a CharacterState,
pulse: f32,
controller: &'a ControllerInputs,
inventory: &'a Inventory,
hotbar: &'a hotbar::State,
slot_manager: &'a mut slots::SlotManager,
) -> Self {
Self {
global_state,
imgs,
item_imgs,
fonts,
stats,
loadout,
@ -166,6 +178,9 @@ impl<'a> Skillbar<'a> {
character_state,
pulse,
controller,
inventory,
hotbar,
slot_manager,
}
}
}
@ -783,51 +798,53 @@ impl<'a> Widget for Skillbar<'a> {
},
)
.set(state.ids.m2_content, ui);
// Slots
let content_source = (self.hotbar, self.inventory, self.loadout, self.energy); // TODO: avoid this
let image_source = (self.item_imgs, self.imgs);
let mut slot_maker = SlotMaker {
// TODO: is a separate image needed for the frame?
empty_slot: self.imgs.skillbar_slot,
filled_slot: self.imgs.skillbar_slot,
selected_slot: self.imgs.skillbar_slot,
background_color: Some(BG_COLOR),
content_size: ContentSize {
width_height_ratio: 1.0,
max_fraction: 0.9,
},
selected_content_scale: 1.067,
amount_font: self.fonts.cyri.conrod_id,
amount_margins: Vec2::new(-4.0, 0.0),
amount_font_size: self.fonts.cyri.scale(12),
amount_text_color: TEXT_COLOR,
content_source: &content_source,
image_source: &image_source,
slot_manager: Some(self.slot_manager),
};
//Slot 5
Image::new(self.imgs.skillbar_slot)
.w_h(20.0 * scale, 20.0 * scale)
slot_maker
.fabricate(hotbar::Slot::Five, [20.0 * scale as f32; 2])
.bottom_left_with_margins_on(state.ids.m1_slot, 0.0, -20.0 * scale)
.set(state.ids.slot5, ui);
Image::new(self.imgs.skillbar_slot_bg)
.w_h(19.0 * scale, 19.0 * scale)
.color(Some(BG_COLOR))
.middle_of(state.ids.slot5)
.set(state.ids.slot5_bg, ui);
// Slot 4
Image::new(self.imgs.skillbar_slot)
.w_h(20.0 * scale, 20.0 * scale)
slot_maker
.fabricate(hotbar::Slot::Four, [20.0 * scale as f32; 2])
.left_from(state.ids.slot5, 0.0)
.set(state.ids.slot4, ui);
Image::new(self.imgs.skillbar_slot_bg)
.w_h(19.0 * scale, 19.0 * scale)
.color(Some(BG_COLOR))
.middle_of(state.ids.slot4)
.set(state.ids.slot4_bg, ui);
// Slot 3
Image::new(self.imgs.skillbar_slot)
.w_h(20.0 * scale, 20.0 * scale)
slot_maker
.fabricate(hotbar::Slot::Three, [20.0 * scale as f32; 2])
.left_from(state.ids.slot4, 0.0)
.set(state.ids.slot3, ui);
Image::new(self.imgs.skillbar_slot_bg)
.w_h(19.0 * scale, 19.0 * scale)
.color(Some(BG_COLOR))
.middle_of(state.ids.slot3)
.set(state.ids.slot3_bg, ui);
// Slot 2
Image::new(self.imgs.skillbar_slot)
.w_h(20.0 * scale, 20.0 * scale)
slot_maker
.fabricate(hotbar::Slot::Two, [20.0 * scale as f32; 2])
.left_from(state.ids.slot3, 0.0)
.set(state.ids.slot2, ui);
Image::new(self.imgs.skillbar_slot_bg)
.w_h(19.0 * scale, 19.0 * scale)
.color(Some(BG_COLOR))
.middle_of(state.ids.slot2)
.set(state.ids.slot2_bg, ui);
// Slot 1
// TODO: Don't hardcode this to one Skill...
// Frame flashes whenever the active skill inside this slot is activated
match self.character_state {
/*
/*match self.character_state {
CharacterState::Charge { time_left } => {
let fade = time_left.as_secs_f32() * 10.0;
Image::new(self.imgs.skillbar_slot_l)
@ -845,29 +862,14 @@ impl<'a> Widget for Skillbar<'a> {
)))
.floating(true)
.set(state.ids.slot1_act, ui);
},*/
_ => {
Image::new(self.imgs.skillbar_slot_l)
.w_h(20.0 * scale, 20.0 * scale)
.left_from(state.ids.slot2, 0.0)
.set(state.ids.slot1, ui);
},
}
Image::new(self.imgs.skillbar_slot_bg)
.w_h(19.5 * scale, 19.5 * scale)
.color(
match self.loadout.active_item.as_ref().map(|i| &i.item.kind) {
Some(ItemKind::Tool(Tool { kind, .. })) => match kind {
ToolKind::Staff(StaffKind::BasicStaff) => Some(BLACK),
_ => Some(BG_COLOR),
},
_ => Some(BG_COLOR),
},
)
.middle_of(state.ids.slot1)
.set(state.ids.slot1_bg, ui);
}*/
slot_maker
.fabricate(hotbar::Slot::One, [20.0 * scale as f32; 2])
.left_from(state.ids.slot2, 0.0)
.set(state.ids.slot1, ui);
// TODO: Changeable slot image
match self.loadout.active_item.as_ref().map(|i| &i.item.kind) {
/*match self.loadout.active_item.as_ref().map(|i| &i.item.kind) {
Some(ItemKind::Tool(Tool { kind, .. })) => match kind {
ToolKind::Staff(StaffKind::BasicStaff) => {
Image::new(self.imgs.fire_spell_1)
@ -883,65 +885,43 @@ impl<'a> Widget for Skillbar<'a> {
_ => {},
},
_ => {},
}
}*/
// Slot 6
Image::new(self.imgs.skillbar_slot)
.w_h(20.0 * scale, 20.0 * scale)
slot_maker
.fabricate(hotbar::Slot::Six, [20.0 * scale as f32; 2])
.bottom_right_with_margins_on(state.ids.m2_slot, 0.0, -20.0 * scale)
.set(state.ids.slot6, ui);
Image::new(self.imgs.skillbar_slot_bg)
.w_h(19.0 * scale, 19.0 * scale)
.color(Some(BG_COLOR))
.middle_of(state.ids.slot6)
.set(state.ids.slot6_bg, ui);
// Slot 7
Image::new(self.imgs.skillbar_slot)
.w_h(20.0 * scale, 20.0 * scale)
slot_maker
.fabricate(hotbar::Slot::Seven, [20.0 * scale as f32; 2])
.right_from(state.ids.slot6, 0.0)
.set(state.ids.slot7, ui);
Image::new(self.imgs.skillbar_slot_bg)
.w_h(19.0 * scale, 19.0 * scale)
.color(Some(BG_COLOR))
.middle_of(state.ids.slot7)
.set(state.ids.slot7_bg, ui);
// Slot 8
Image::new(self.imgs.skillbar_slot)
.w_h(20.0 * scale, 20.0 * scale)
slot_maker
.fabricate(hotbar::Slot::Eight, [20.0 * scale as f32; 2])
.right_from(state.ids.slot7, 0.0)
.set(state.ids.slot8, ui);
Image::new(self.imgs.skillbar_slot_bg)
.w_h(19.0 * scale, 19.0 * scale)
.color(Some(BG_COLOR))
.middle_of(state.ids.slot8)
.set(state.ids.slot8_bg, ui);
// Slot 9
Image::new(self.imgs.skillbar_slot)
.w_h(20.0 * scale, 20.0 * scale)
slot_maker
.fabricate(hotbar::Slot::Nine, [20.0 * scale as f32; 2])
.right_from(state.ids.slot8, 0.0)
.set(state.ids.slot9, ui);
Image::new(self.imgs.skillbar_slot_bg)
.w_h(19.0 * scale, 19.0 * scale)
.color(Some(BG_COLOR))
.middle_of(state.ids.slot9)
.set(state.ids.slot9_bg, ui);
// Quickslot
Image::new(self.imgs.skillbar_slot_r)
.w_h(20.0 * scale, 20.0 * scale)
slot_maker.filled_slot = self.imgs.skillbar_slot_r;
slot_maker.selected_slot = self.imgs.skillbar_slot_r;
slot_maker.empty_slot = self.imgs.skillbar_slot_r;
slot_maker
.fabricate(hotbar::Slot::Ten, [20.0 * scale as f32; 2])
.right_from(state.ids.slot9, 0.0)
.set(state.ids.slot10, ui);
Image::new(self.imgs.skillbar_slot_bg)
.w_h(19.0 * scale, 19.0 * scale)
.color(Some(BG_COLOR))
.middle_of(state.ids.slot10)
.set(state.ids.slot10_bg, ui);
// Shortcuts
// Shortcuts
if let ShortcutNumbers::On = shortcuts {
if let Some(slot1) = &self
.global_state
.settings
.controls
.get_binding(GameInput::Ability3)
.get_binding(GameInput::Slot1)
{
Text::new(slot1.to_string().as_str())
.top_right_with_margins_on(state.ids.slot1_bg, 1.0, 2.0)

View File

@ -1,7 +1,11 @@
use super::item_imgs::{ItemImgs, ItemKey};
use super::{
hotbar::{self, Slot as HotbarSlot},
img_ids,
item_imgs::{ItemImgs, ItemKey},
};
use crate::ui::slot::{self, SlotKey, SumSlot};
use common::comp::{item::ItemKind, Inventory, Loadout};
use conrod_core::image;
use common::comp::{item::ItemKind, Energy, Inventory, Loadout};
use conrod_core::{image, Color};
pub use common::comp::slot::{ArmorSlot, EquipSlot};
@ -9,8 +13,8 @@ pub use common::comp::slot::{ArmorSlot, EquipSlot};
pub enum SlotKind {
Inventory(InventorySlot),
Equip(EquipSlot),
/*Hotbar(HotbarSlot),
*Spellbook(SpellbookSlot), TODO */
Hotbar(HotbarSlot),
/* Spellbook(SpellbookSlot), TODO */
}
pub type SlotManager = slot::SlotManager<SlotKind>;
@ -18,25 +22,11 @@ pub type SlotManager = slot::SlotManager<SlotKind>;
#[derive(Clone, Copy, PartialEq)]
pub struct InventorySlot(pub usize);
/*#[derive(Clone, Copy, PartialEq)]
pub enum HotbarSlot {
One,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
}*/
impl SlotKey<Inventory, ItemImgs> for InventorySlot {
type ImageKey = ItemKey;
fn image_key(&self, source: &Inventory) -> Option<Self::ImageKey> {
source.get(self.0).map(Into::into)
fn image_key(&self, source: &Inventory) -> Option<(Self::ImageKey, Option<Color>)> {
source.get(self.0).map(|i| (i.into(), None))
}
fn amount(&self, source: &Inventory) -> Option<u32> {
@ -59,7 +49,7 @@ impl SlotKey<Inventory, ItemImgs> for InventorySlot {
impl SlotKey<Loadout, ItemImgs> for EquipSlot {
type ImageKey = ItemKey;
fn image_key(&self, source: &Loadout) -> Option<Self::ImageKey> {
fn image_key(&self, source: &Loadout) -> Option<(Self::ImageKey, Option<Color>)> {
let item = match self {
EquipSlot::Armor(ArmorSlot::Shoulders) => source.shoulder.as_ref(),
EquipSlot::Armor(ArmorSlot::Chest) => source.chest.as_ref(),
@ -77,7 +67,7 @@ impl SlotKey<Loadout, ItemImgs> for EquipSlot {
EquipSlot::Lantern => source.lantern.as_ref(),
};
item.map(Into::into)
item.map(|i| (i.into(), None))
}
fn amount(&self, _: &Loadout) -> Option<u32> { None }
@ -87,18 +77,58 @@ impl SlotKey<Loadout, ItemImgs> for EquipSlot {
}
}
/*impl SlotKey<Hotbar, ItemImgs> for HotbarSlot {
type ImageKey = ItemKey;
#[derive(Clone, PartialEq)]
pub enum HotbarImage {
Item(ItemKey),
Ability3,
}
fn image_key(&self, source: &Inventory) -> Option<Self::ImageKey> {
source.get(self.0).map(Into::into)
type HotbarSource<'a> = (&'a hotbar::State, &'a Inventory, &'a Loadout, &'a Energy);
type HotbarImageSource<'a> = (&'a ItemImgs, &'a img_ids::Imgs);
impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
type ImageKey = HotbarImage;
fn image_key(
&self,
(hotbar, inventory, loadout, energy): &HotbarSource<'a>,
) -> Option<(Self::ImageKey, Option<Color>)> {
hotbar.get(*self).and_then(|contents| match contents {
hotbar::SlotContents::Inventory(idx) => inventory
.get(idx)
.map(|item| HotbarImage::Item(item.into()))
.map(|i| (i, None)),
hotbar::SlotContents::Ability3 => loadout
.active_item
.as_ref()
.map(|i| &i.item.kind)
.and_then(|kind| {
use common::comp::item::tool::{StaffKind, Tool, ToolKind};
matches!(
kind,
ItemKind::Tool(Tool {
kind: ToolKind::Staff(StaffKind::BasicStaff),
..
})
)
.then_some((
HotbarImage::Ability3,
// Darken if not enough energy to use attack
(energy.current() < 500).then_some(Color::Rgba(0.3, 0.3, 0.3, 0.8)),
))
}),
})
}
fn amount(&self, source: &Inventory) -> Option<u32> {
source
.get(self.0)
fn amount(&self, (hotbar, inventory, _, _): &HotbarSource<'a>) -> Option<u32> {
hotbar
.get(*self)
.and_then(|content| match content {
hotbar::SlotContents::Inventory(idx) => inventory.get(idx),
hotbar::SlotContents::Ability3 => None,
})
.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),
@ -106,10 +136,13 @@ impl SlotKey<Loadout, ItemImgs> for EquipSlot {
.filter(|amount| *amount > 1)
}
fn image_id(key: &Self::ImageKey, source: &ItemImgs) -> image::Id {
source.img_id_or_not_found_img(key.clone())
fn image_id(key: &Self::ImageKey, (item_imgs, imgs): &HotbarImageSource<'a>) -> image::Id {
match key {
HotbarImage::Item(key) => item_imgs.img_id_or_not_found_img(key.clone()),
HotbarImage::Ability3 => imgs.fire_spell_1,
}
}
}*/
}
impl From<InventorySlot> for SlotKind {
fn from(inventory: InventorySlot) -> Self { Self::Inventory(inventory) }
@ -119,8 +152,8 @@ impl From<EquipSlot> for SlotKind {
fn from(equip: EquipSlot) -> Self { Self::Equip(equip) }
}
//impl From<HotbarSlot> for SlotKind {
// fn from(hotbar: HotbarSlot) -> Self { Self::Hotbar(hotbar) }
//}
impl From<HotbarSlot> for SlotKind {
fn from(hotbar: HotbarSlot) -> Self { Self::Hotbar(hotbar) }
}
impl SumSlot for SlotKind {}

View File

@ -1,5 +1,5 @@
#![deny(unsafe_code)]
#![feature(drain_filter)]
#![feature(drain_filter, bool_to_option)]
#![recursion_limit = "2048"]
#[macro_use]

View File

@ -282,9 +282,6 @@ impl PlayState for SessionState {
}
},
Event::InputUpdate(GameInput::Ability3, state) => {
self.inputs.ability3.set_state(state);
},
Event::InputUpdate(GameInput::Roll, state) => {
let client = self.client.borrow();
if client
@ -646,6 +643,7 @@ impl PlayState for SessionState {
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::Ability3(state) => self.inputs.ability3.set_state(state),
HudEvent::ChangeFOV(new_fov) => {
global_state.settings.graphics.fov = new_fov;
global_state.settings.save_to_file_warn();

View File

@ -138,7 +138,7 @@ impl ControlSettings {
GameInput::ToggleWield => KeyMouse::Key(VirtualKeyCode::T),
//GameInput::Charge => KeyMouse::Key(VirtualKeyCode::Key1),
GameInput::FreeLook => KeyMouse::Key(VirtualKeyCode::L),
GameInput::Ability3 => KeyMouse::Key(VirtualKeyCode::Key1),
GameInput::Slot1 => KeyMouse::Key(VirtualKeyCode::Key1),
GameInput::Slot2 => KeyMouse::Key(VirtualKeyCode::Key2),
GameInput::Slot3 => KeyMouse::Key(VirtualKeyCode::Key3),
GameInput::Slot4 => KeyMouse::Key(VirtualKeyCode::Key4),
@ -196,7 +196,7 @@ impl Default for ControlSettings {
GameInput::ToggleWield,
//GameInput::Charge,
GameInput::FreeLook,
GameInput::Ability3,
GameInput::Slot1,
GameInput::Slot2,
GameInput::Slot3,
GameInput::Slot4,

View File

@ -13,7 +13,7 @@ const AMOUNT_SHADOW_OFFSET: [f64; 2] = [1.0, 1.0];
pub trait SlotKey<C, I>: Copy {
type ImageKey: PartialEq + Send + 'static;
/// Returns an Option since the slot could be empty
fn image_key(&self, source: &C) -> Option<Self::ImageKey>;
fn image_key(&self, source: &C) -> Option<(Self::ImageKey, Option<Color>)>;
fn amount(&self, source: &C) -> Option<u32>;
fn image_id(key: &Self::ImageKey, source: &I) -> image::Id;
}
@ -285,6 +285,18 @@ where
_ => Interaction::None,
}
}
/// Returns Some(slot) if a slot is selected
pub fn selected(&self) -> Option<S> {
if let ManagerState::Selected(_, s) = self.state {
Some(s)
} else {
None
}
}
/// Sets the SlotManager into an idle state
pub fn idle(&mut self) { self.state = ManagerState::Idle; }
}
#[derive(WidgetCommon)]
@ -435,7 +447,9 @@ where
} = self;
// If the key changed update the cached image id
let image_key = slot_key.image_key(content_source);
let (image_key, content_color) = slot_key
.image_key(content_source)
.map_or((None, None), |(i, c)| (Some(i), c));
if state.cached_image.as_ref().map(|c| &c.0) != image_key.as_ref() {
state.update(|state| {
state.cached_image = image_key.map(|key| {
@ -510,6 +524,7 @@ where
})
.map(|e| e as f64)
.into_array())
.color(content_color)
.parent(id)
.graphics_for(id)
.set(state.ids.content, ui);

View File

@ -17,7 +17,7 @@ use vek::*;
pub enum GameInput {
Primary,
Secondary,
Ability3,
Slot1,
Slot2,
Slot3,
Slot4,
@ -99,7 +99,7 @@ impl GameInput {
GameInput::ToggleWield => "gameinput.togglewield",
//GameInput::Charge => "gameinput.charge",
GameInput::FreeLook => "gameinput.freelook",
GameInput::Ability3 => "gameinput.slot1",
GameInput::Slot1 => "gameinput.slot1",
GameInput::Slot2 => "gameinput.slot2",
GameInput::Slot3 => "gameinput.slot3",
GameInput::Slot4 => "gameinput.slot4",