Pages to swap between weapon abilities.

This commit is contained in:
Sam 2021-12-03 16:55:52 -05:00
parent ed38272d23
commit 3870688717
2 changed files with 189 additions and 147 deletions
assets/common/abilities
voxygen/src/hud

View File

@ -9,13 +9,71 @@
// TODO: Remove these // TODO: Remove these
(Some(Axe(UnlockLeap)), "common.abilities.axe.leap"), (Some(Axe(UnlockLeap)), "common.abilities.axe.leap"),
(Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"), (Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"),
(Some(Sword(UnlockSpin)), "common.abilities.sword.spin"),
(Some(Axe(UnlockLeap)), "common.abilities.axe.leap"),
(Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"),
(Some(Sword(UnlockSpin)), "common.abilities.sword.spin"),
(Some(Axe(UnlockLeap)), "common.abilities.axe.leap"),
(Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"),
(Some(Sword(UnlockSpin)), "common.abilities.sword.spin"),
(Some(Axe(UnlockLeap)), "common.abilities.axe.leap"),
(Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"),
(Some(Sword(UnlockSpin)), "common.abilities.sword.spin"),
(Some(Axe(UnlockLeap)), "common.abilities.axe.leap"),
(Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"),
(Some(Sword(UnlockSpin)), "common.abilities.sword.spin"),
(Some(Axe(UnlockLeap)), "common.abilities.axe.leap"),
(Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"),
(Some(Sword(UnlockSpin)), "common.abilities.sword.spin"),
(Some(Axe(UnlockLeap)), "common.abilities.axe.leap"),
(Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"),
(Some(Sword(UnlockSpin)), "common.abilities.sword.spin"),
(Some(Axe(UnlockLeap)), "common.abilities.axe.leap"),
(Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"),
(Some(Sword(UnlockSpin)), "common.abilities.sword.spin"),
(Some(Axe(UnlockLeap)), "common.abilities.axe.leap"),
(Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"),
(Some(Sword(UnlockSpin)), "common.abilities.sword.spin"),
(Some(Axe(UnlockLeap)), "common.abilities.axe.leap"),
(Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"),
], ],
), ),
Tool(Axe): ( Tool(Axe): (
primary: "common.abilities.axe.doublestrike", primary: "common.abilities.axe.doublestrike",
secondary: "common.abilities.axe.spin", secondary: "common.abilities.axe.spin",
abilities: [ abilities: [
(Some(Axe(UnlockLeap)), "common.abilities.axe.leap"), // (Some(Axe(UnlockLeap)), "common.abilities.axe.leap"),
// TODO: Remove these
(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
], ],
), ),
Tool(Hammer): ( Tool(Hammer): (
@ -30,9 +88,6 @@
secondary: "common.abilities.bow.repeater", secondary: "common.abilities.bow.repeater",
abilities: [ abilities: [
(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"), (Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
// TODO: Remove these
(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
], ],
), ),
Tool(Staff): ( Tool(Staff): (

View File

@ -11,7 +11,7 @@ use crate::{
use conrod_core::{ use conrod_core::{
color, image, color, image,
input::state::mouse::ButtonPosition, input::state::mouse::ButtonPosition,
widget::{self, Button, Image, Rectangle, Scrollbar, State, Text}, widget::{self, Button, Image, Rectangle, State, Text},
widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, UiCell, Widget, WidgetCommon, widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, UiCell, Widget, WidgetCommon,
}; };
use i18n::Localization; use i18n::Localization;
@ -202,21 +202,21 @@ widget_ids! {
skill_general_swim_1, skill_general_swim_1,
// Ability selection // Ability selection
spellbook_art, spellbook_art,
sb_page_left_align,
sb_page_right_align,
spellbook_skills_bg, spellbook_skills_bg,
spellbook_btn, spellbook_btn,
spellbook_btn_bg, spellbook_btn_bg,
ability_select_title, ability_select_title,
ability_scroller, ability_page_left,
ability_page_right,
active_abilities[], active_abilities[],
active_abilities_bg[], active_abilities_bg[],
main_weap_title, main_weap_select,
main_weap_abilities[], off_weap_select,
main_weap_ability_titles[], abilities[],
main_weap_ability_descs[], ability_titles[],
off_weap_title, ability_descs[],
off_weap_abilities[],
off_weap_ability_titles[],
off_weap_ability_descs[],
dragged_ability, dragged_ability,
// Stats // Stats
stat_names[], stat_names[],
@ -350,10 +350,17 @@ pub enum DiarySection {
Stats, Stats,
} }
enum AbilitySource {
MainWeapon,
OffWeapon,
}
pub struct DiaryState { pub struct DiaryState {
ids: Ids, ids: Ids,
dragged_ability: Option<AuxiliaryAbility>, dragged_ability: Option<AuxiliaryAbility>,
id_ability_map: HashMap<widget::Id, AuxiliaryAbility>, id_ability_map: HashMap<widget::Id, AuxiliaryAbility>,
ability_source: AbilitySource,
ability_page: usize,
} }
impl<'a> Widget for Diary<'a> { impl<'a> Widget for Diary<'a> {
@ -367,6 +374,8 @@ impl<'a> Widget for Diary<'a> {
dragged_ability: None, dragged_ability: None,
// Constructed during update sequence // Constructed during update sequence
id_ability_map: HashMap::new(), id_ability_map: HashMap::new(),
ability_source: AbilitySource::MainWeapon,
ability_page: 0,
} }
} }
@ -835,6 +844,13 @@ impl<'a> Widget for Diary<'a> {
.mid_bottom_with_margin_on(state.ids.content_align, tweak!(8.0)) .mid_bottom_with_margin_on(state.ids.content_align, tweak!(8.0))
.set(state.ids.spellbook_skills_bg, ui); .set(state.ids.spellbook_skills_bg, ui);
Rectangle::fill_with([299.0 * 2.0, 184.0 * 4.0], color::TRANSPARENT)
.top_left_with_margins_on(state.ids.spellbook_art, 0.0, 0.0)
.set(state.ids.sb_page_left_align, ui);
Rectangle::fill_with([299.0 * 2.0, 184.0 * 4.0], color::TRANSPARENT)
.top_right_with_margins_on(state.ids.spellbook_art, 0.0, 0.0)
.set(state.ids.sb_page_right_align, ui);
// Display all active abilities on right of window // Display all active abilities on right of window
state.update(|s| { state.update(|s| {
s.ids s.ids
@ -899,57 +915,116 @@ impl<'a> Widget for Diary<'a> {
} }
} }
// Scrollbar // Switch between mainhand and offhand weapon abilities
Scrollbar::y_axis(state.ids.spellbook_art) if Button::image(self.imgs.spellbook_ico)
.thickness(5.0) .bottom_left_with_margins_on(state.ids.content_align, tweak!(5.0), tweak!(5.0))
.middle_of(state.ids.spellbook_art) .w_h(40.0, 40.0)
.rgba(0.33, 0.33, 0.33, 1.0) .set(state.ids.main_weap_select, ui)
.set(state.ids.ability_scroller, ui); .was_clicked()
{
// Display list of abilities from main weapon state.update(|s| {
Text::new("Main Weapon Abilities") s.ability_source = AbilitySource::MainWeapon;
.top_left_with_margins_on(state.ids.spellbook_art, 75.0, 25.0) s.ability_page = 0;
.font_id(self.fonts.cyri.conrod_id) })
.font_size(self.fonts.cyri.scale(28)) }
.color(BLACK) if Button::image(self.imgs.spellbook_ico)
.set(state.ids.main_weap_title, ui); .mid_right_with_margin_on(state.ids.main_weap_select, tweak!(-45.0))
.w_h(40.0, 40.0)
.set(state.ids.off_weap_select, ui)
.was_clicked()
{
state.update(|s| {
s.ability_source = AbilitySource::OffWeapon;
s.ability_page = 0;
})
}
// TODO: Maybe try to keep this as an iterator. Not sure how to get length // TODO: Maybe try to keep this as an iterator. Not sure how to get length
// though since size_hint didn't work // though since size_hint didn't work
let main_weap_abilities: Vec<_> = ActiveAbilities::iter_unlocked_abilities( let abilities: Vec<_> = match state.ability_source {
Some(self.inventory), AbilitySource::MainWeapon => ActiveAbilities::iter_unlocked_abilities(
Some(self.skill_set), Some(self.inventory),
EquipSlot::ActiveMainhand, Some(self.skill_set),
) EquipSlot::ActiveMainhand,
.map(AuxiliaryAbility::MainWeapon) )
.map(|a| (Ability::from(a).ability_id(Some(self.inventory)), a)) .map(AuxiliaryAbility::MainWeapon)
.collect(); .map(|a| (Ability::from(a).ability_id(Some(self.inventory)), a))
.collect(),
AbilitySource::OffWeapon => ActiveAbilities::iter_unlocked_abilities(
Some(self.inventory),
Some(self.skill_set),
EquipSlot::ActiveOffhand,
)
.map(AuxiliaryAbility::OffWeapon)
.map(|a| (Ability::from(a).ability_id(Some(self.inventory)), a))
.collect(),
};
let main_abilities_length = main_weap_abilities.len(); const ABILITIES_PER_PAGE: usize = 12;
let page_indices = abilities.len() / ABILITIES_PER_PAGE;
if state.ability_page > page_indices {
state.update(|s| s.ability_page = 0);
}
let update_length = abilities.len().min(12);
state.update(|s| { state.update(|s| {
s.ids s.ids
.main_weap_abilities .abilities
.resize(main_abilities_length, &mut ui.widget_id_generator()) .resize(update_length, &mut ui.widget_id_generator())
}); });
state.update(|s| { state.update(|s| {
s.ids s.ids
.main_weap_ability_titles .ability_titles
.resize(main_abilities_length, &mut ui.widget_id_generator()) .resize(update_length, &mut ui.widget_id_generator())
}); });
state.update(|s| { state.update(|s| {
s.ids s.ids
.main_weap_ability_descs .ability_descs
.resize(main_abilities_length, &mut ui.widget_id_generator()) .resize(update_length, &mut ui.widget_id_generator())
}); });
for (i, (ability_id, ability)) in main_weap_abilities.iter().enumerate() { // Page buttons
let map_id = state.ids.main_weap_abilities[i]; if state.ability_page > 0 {
// Only show left button if not on first page
if Button::image(self.imgs.spellbook_ico)
.bottom_left_with_margins_on(state.ids.sb_page_left_align, -5.0, 0.0)
.w_h(40.0, 40.0)
.set(state.ids.ability_page_left, ui)
.was_clicked()
{
state.update(|s| s.ability_page -= 1);
}
}
if state.ability_page < page_indices {
// Only show right button if not on last page
if Button::image(self.imgs.spellbook_ico)
.bottom_right_with_margins_on(state.ids.sb_page_right_align, -5.0, 0.0)
.w_h(40.0, 40.0)
.set(state.ids.ability_page_right, ui)
.was_clicked()
{
state.update(|s| s.ability_page += 1);
}
}
let ability_start = state.ability_page * ABILITIES_PER_PAGE;
let abilities_range = ability_start..(ability_start + ABILITIES_PER_PAGE);
for (id_index, (ability_id, ability)) in abilities
.iter()
.enumerate()
.filter_map(|(i, ability_info)| {
abilities_range.contains(&i).then_some(ability_info)
})
.enumerate()
{
let map_id = state.ids.abilities[id_index];
state.update(|s| { state.update(|s| {
s.id_ability_map.insert(map_id, *ability); s.id_ability_map.insert(map_id, *ability);
}); });
let image_offsets = 120.0 * i as f64;
let ability_image = ability_id let ability_image = ability_id
.map_or(self.imgs.nothing, |id| util::ability_image(self.imgs, id)); .map_or(self.imgs.nothing, |id| util::ability_image(self.imgs, id));
let ability_color = if self.show.diary_fields.selected_ability != Some(*ability) let ability_color = if self.show.diary_fields.selected_ability != Some(*ability)
@ -960,14 +1035,15 @@ impl<'a> Widget for Diary<'a> {
}; };
let (ability_title, ability_desc) = let (ability_title, ability_desc) =
util::ability_description(ability_id.unwrap_or("")); util::ability_description(ability_id.unwrap_or(""));
let (align_state, image_offsets) = if id_index < 6 {
(state.ids.sb_page_left_align, 120.0 * id_index as f64)
} else {
(state.ids.sb_page_right_align, 120.0 * (id_index - 6) as f64)
};
if Button::image(ability_image) if Button::image(ability_image)
.w_h(100.0, 100.0) .w_h(100.0, 100.0)
.top_left_with_margins_on( .top_left_with_margins_on(align_state, 20.0 + image_offsets, 20.0)
state.ids.main_weap_title, .set(state.ids.abilities[id_index], ui)
40.0 + image_offsets,
0.0,
)
.set(state.ids.main_weap_abilities[i], ui)
.was_clicked() .was_clicked()
{ {
if Some(*ability) != self.show.diary_fields.selected_ability { if Some(*ability) != self.show.diary_fields.selected_ability {
@ -977,108 +1053,19 @@ impl<'a> Widget for Diary<'a> {
} }
} }
Text::new(ability_title) Text::new(ability_title)
.top_left_with_margins_on(state.ids.main_weap_abilities[i], 5.0, 125.0) .top_left_with_margins_on(state.ids.abilities[id_index], 5.0, 125.0)
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(tweak!(28))) .font_size(self.fonts.cyri.scale(tweak!(28)))
.color(ability_color) .color(ability_color)
.graphics_for(state.ids.main_weap_abilities[i]) .graphics_for(state.ids.abilities[id_index])
.set(state.ids.main_weap_ability_titles[i], ui); .set(state.ids.ability_titles[id_index], ui);
Text::new(ability_desc) Text::new(ability_desc)
.top_left_with_margins_on(state.ids.main_weap_abilities[i], 30.0, 125.0) .top_left_with_margins_on(state.ids.abilities[id_index], 30.0, 125.0)
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(tweak!(18))) .font_size(self.fonts.cyri.scale(tweak!(18)))
.color(ability_color) .color(ability_color)
.graphics_for(state.ids.main_weap_abilities[i]) .graphics_for(state.ids.abilities[id_index])
.set(state.ids.main_weap_ability_descs[i], ui); .set(state.ids.ability_descs[id_index], ui);
}
// Display list of abilities from off weapon
let offset_state = if main_abilities_length > 0 {
state.ids.main_weap_abilities[main_abilities_length - 1]
} else {
state.ids.main_weap_title
};
Text::new("Off Weapon Abilities")
.bottom_left_with_margins_on(offset_state, -50.0, 0.0)
.font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(28))
.color(BLACK)
.set(state.ids.off_weap_title, ui);
// TODO: Maybe try to keep this as an iterator. Not sure how to get length
// though since size_hint didn't work
let off_weap_abilities: Vec<_> = ActiveAbilities::iter_unlocked_abilities(
Some(self.inventory),
Some(self.skill_set),
EquipSlot::ActiveOffhand,
)
.map(AuxiliaryAbility::OffWeapon)
.map(|a| (Ability::from(a).ability_id(Some(self.inventory)), a))
.collect();
let off_abilities_length = off_weap_abilities.len();
state.update(|s| {
s.ids
.off_weap_abilities
.resize(off_abilities_length, &mut ui.widget_id_generator())
});
state.update(|s| {
s.ids
.off_weap_ability_titles
.resize(off_abilities_length, &mut ui.widget_id_generator())
});
state.update(|s| {
s.ids
.off_weap_ability_descs
.resize(off_abilities_length, &mut ui.widget_id_generator())
});
for (i, (ability_id, ability)) in off_weap_abilities.iter().enumerate() {
let map_id = state.ids.off_weap_abilities[i];
state.update(|s| {
s.id_ability_map.insert(map_id, *ability);
});
let image_offsets = 120.0 * i as f64;
let ability_image = ability_id
.map_or(self.imgs.nothing, |id| util::ability_image(self.imgs, id));
let ability_color = if self.show.diary_fields.selected_ability != Some(*ability)
{
BLACK
} else {
XP_COLOR
};
let (ability_title, ability_desc) =
util::ability_description(ability_id.unwrap_or(""));
if Button::image(ability_image)
.w_h(100.0, 100.0)
.top_left_with_margins_on(
state.ids.off_weap_title,
40.0 + image_offsets,
0.0,
)
.set(state.ids.off_weap_abilities[i], ui)
.was_clicked()
{
if Some(*ability) != self.show.diary_fields.selected_ability {
events.push(Event::SelectAbility(Some(*ability)));
} else {
events.push(Event::SelectAbility(None));
}
}
Text::new(ability_title)
.top_left_with_margins_on(state.ids.off_weap_abilities[i], 5.0, 125.0)
.font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(24))
.color(ability_color)
.set(state.ids.off_weap_ability_titles[i], ui);
Text::new(ability_desc)
.top_left_with_margins_on(state.ids.off_weap_abilities[i], 30.0, 125.0)
.font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(16))
.color(ability_color)
.set(state.ids.off_weap_ability_descs[i], ui);
} }
let mouse_pos = ui.global_input().current.mouse.xy; let mouse_pos = ui.global_input().current.mouse.xy;