Initial UI work

This commit is contained in:
Sam 2021-11-13 22:47:18 -05:00
parent 7b7ad62be9
commit cf724bd76c
6 changed files with 566 additions and 275 deletions

View File

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

View File

@ -60,10 +60,12 @@ impl Default for ActiveAbilities {
}
impl ActiveAbilities {
pub fn new(inv: Option<&Inventory>, skill_set: Option<&SkillSet>) -> Self {
let mut pool = Self::default();
pool.auto_update(inv, skill_set);
pool
pub fn new(_inv: Option<&Inventory>, _skill_set: Option<&SkillSet>) -> Self {
// Maybe hook into loading saved variants when they exist here?
// let mut pool = Self::default();
// pool.auto_update(inv, skill_set);
// pool
Self::default()
}
pub fn change_ability(&mut self, slot: usize, new_ability: AuxiliaryAbility) {
@ -150,9 +152,7 @@ impl ActiveAbilities {
}
}
// TODO: Potentially remove after there is an actual UI
pub fn auto_update(&mut self, inv: Option<&Inventory>, skill_set: Option<&SkillSet>) {
fn iter_unlocked_abilities<'a>(
pub fn iter_unlocked_abilities<'a>(
inv: Option<&'a Inventory>,
skill_set: Option<&'a SkillSet>,
equip_slot: EquipSlot,
@ -168,17 +168,20 @@ impl ActiveAbilities {
})
}
let main_abilities = iter_unlocked_abilities(inv, skill_set, EquipSlot::ActiveMainhand)
.map(AuxiliaryAbility::MainWeapon);
let off_abilities = iter_unlocked_abilities(inv, skill_set, EquipSlot::ActiveOffhand)
.map(AuxiliaryAbility::OffWeapon);
// TODO: Maybe keep this for autopopulating a new combination of weapons?
// pub fn auto_update(&mut self, inv: Option<&Inventory>, skill_set:
// Option<&SkillSet>) { let main_abilities =
// Self::iter_unlocked_abilities(inv, skill_set, EquipSlot::ActiveMainhand)
// .map(AuxiliaryAbility::MainWeapon);
// let off_abilities = Self::iter_unlocked_abilities(inv, skill_set,
// EquipSlot::ActiveOffhand) .map(AuxiliaryAbility::OffWeapon);
(0..MAX_ABILITIES)
.zip(main_abilities.chain(off_abilities))
.for_each(|(i, ability)| {
self.change_ability(i, ability);
})
}
// (0..MAX_ABILITIES)
// .zip(main_abilities.chain(off_abilities))
// .for_each(|(i, ability)| {
// self.change_ability(i, ability);
// })
// }
}
pub enum AbilityInput {

View File

@ -48,7 +48,10 @@ pub mod visual;
// Reexports
#[cfg(not(target_arch = "wasm32"))]
pub use self::{
ability::{Ability, AbilityInput, ActiveAbilities, CharacterAbility, CharacterAbilityType},
ability::{
Ability, AbilityInput, ActiveAbilities, CharacterAbility, CharacterAbilityType,
MAX_ABILITIES,
},
admin::{Admin, AdminRole},
agent::{Agent, Alignment, Behavior, BehaviorCapability, BehaviorState, PidController},
anchor::Anchor,

View File

@ -767,19 +767,6 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
// manipulating the inventory mutated the trade, so reset the accept flags
trades.implicit_mutation_occurred(&uid);
}
// After any inventory manipulation, update the ability
// TODO: Make less hacky, probably remove entirely but needs UI
if let Some(mut active_abilities) = state
.ecs()
.write_storage::<comp::ActiveAbilities>()
.get_mut(entity)
{
active_abilities.auto_update(
state.ecs().read_storage::<comp::Inventory>().get(entity),
state.ecs().read_storage::<comp::SkillSet>().get(entity),
);
}
}
fn within_pickup_range<S: FindDist<find_dist::Cylinder>>(

View File

@ -5,7 +5,7 @@ use super::{
UI_MAIN, XP_COLOR,
};
use crate::{
hud,
hud::{self, util},
ui::{fonts::Fonts, ImageFrame, Tooltip, TooltipManager, Tooltipable},
};
use conrod_core::{
@ -18,12 +18,15 @@ use i18n::Localization;
use client::{self, Client};
use common::{
comp::{
item::tool::ToolKind,
self,
ability::{Ability, ActiveAbilities, AuxiliaryAbility, MAX_ABILITIES},
inventory::{item::tool::ToolKind, slot::EquipSlot},
skills::{
self, AxeSkill, BowSkill, ClimbSkill, GeneralSkill, HammerSkill, MiningSkill,
RollSkill, SceptreSkill, Skill, StaffSkill, SwimSkill, SwordSkill, SKILL_MODIFIERS,
},
skillset::{SkillGroupKind, SkillSet},
Inventory,
},
consts::{ENERGY_PER_LEVEL, HP_PER_LEVEL},
};
@ -39,6 +42,7 @@ widget_ids! {
close,
title,
content_align,
// Skill tree stuffs
exp_bar_bg,
exp_bar_frame,
exp_bar_content_align,
@ -184,6 +188,18 @@ widget_ids! {
skill_general_climb_2,
skill_general_swim_0,
skill_general_swim_1,
// Ability selection stuffs
ability_select_title,
active_abilities[],
active_abilities_bg[],
main_weap_title,
main_weap_abilities[],
main_weap_ability_titles[],
main_weap_ability_descs[],
off_weap_title,
off_weap_abilities[],
off_weap_ability_titles[],
off_weap_ability_descs[],
}
}
@ -192,6 +208,8 @@ pub struct Diary<'a> {
show: &'a Show,
_client: &'a Client,
skill_set: &'a SkillSet,
active_abilities: &'a ActiveAbilities,
inventory: &'a Inventory,
imgs: &'a Imgs,
item_imgs: &'a ItemImgs,
fonts: &'a Fonts,
@ -208,11 +226,30 @@ pub struct Diary<'a> {
created_btns_bot_r: usize,
}
pub struct DiaryShow {
pub skilltreetab: SelectedSkillTree,
pub section: DiarySection,
pub selected_ability: Option<AuxiliaryAbility>,
}
impl Default for DiaryShow {
fn default() -> Self {
Self {
skilltreetab: SelectedSkillTree::General,
section: DiarySection::SkillTrees,
selected_ability: None,
}
}
}
#[allow(clippy::too_many_arguments)]
impl<'a> Diary<'a> {
pub fn new(
show: &'a Show,
_client: &'a Client,
skill_set: &'a SkillSet,
active_abilities: &'a ActiveAbilities,
inventory: &'a Inventory,
imgs: &'a Imgs,
item_imgs: &'a ItemImgs,
fonts: &'a Fonts,
@ -225,6 +262,8 @@ impl<'a> Diary<'a> {
show,
_client,
skill_set,
active_abilities,
inventory,
imgs,
item_imgs,
fonts,
@ -259,6 +298,14 @@ pub enum Event {
Close,
ChangeSkillTree(SelectedSkillTree),
UnlockSkill(Skill),
ChangeSection(DiarySection),
SelectAbility(Option<AuxiliaryAbility>),
ChangeAbility(usize, AuxiliaryAbility),
}
pub enum DiarySection {
SkillTrees,
AbilitySelection,
}
impl<'a> Widget for Diary<'a> {
@ -294,7 +341,6 @@ impl<'a> Widget for Diary<'a> {
.font_id(self.fonts.cyri.conrod_id)
.desc_text_color(TEXT_COLOR);
let sel_tab = &self.show.skilltreetab;
//Animation timer Frame
let frame_ani = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.8;
@ -311,10 +357,26 @@ impl<'a> Widget for Diary<'a> {
.set(state.frame, ui);
// Icon
Image::new(self.imgs.spellbook_button)
// Image::new(self.imgs.spellbook_button)
// .w_h(30.0, 27.0)
// .top_left_with_margins_on(state.frame, 8.0, 8.0)
// .set(state.icon, ui);
// Hack to get this to work for now, use spellbook image in top left as a button
if Button::image(self.imgs.spellbook_button)
.w_h(30.0, 27.0)
.top_left_with_margins_on(state.frame, 8.0, 8.0)
.set(state.icon, ui);
.set(state.icon, ui)
.was_clicked()
{
match self.show.diary_fields.section {
DiarySection::SkillTrees => {
events.push(Event::ChangeSection(DiarySection::AbilitySelection))
},
DiarySection::AbilitySelection => {
events.push(Event::ChangeSection(DiarySection::SkillTrees))
},
}
}
// X-Button
if Button::image(self.imgs.close_button)
@ -343,7 +405,10 @@ impl<'a> Widget for Diary<'a> {
// Contents
match self.show.diary_fields.section {
DiarySection::SkillTrees => {
// Skill Trees
let sel_tab = &self.show.diary_fields.skilltreetab;
// Skill Tree Selection
state.update(|s| {
@ -577,6 +642,218 @@ impl<'a> Widget for Diary<'a> {
},
_ => events,
}
},
DiarySection::AbilitySelection => {
use comp::ability::AbilityInput;
// Title for ability selection UI
Text::new("Ability Selection")
.mid_top_with_margin_on(state.content_align, 2.0)
.font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(34))
.color(TEXT_COLOR)
.set(state.ability_select_title, ui);
// Display all active abilities on right of window
state.update(|s| {
s.active_abilities
.resize(MAX_ABILITIES, &mut ui.widget_id_generator())
});
state.update(|s| {
s.active_abilities_bg
.resize(MAX_ABILITIES, &mut ui.widget_id_generator())
});
for i in 0..MAX_ABILITIES {
let ability_id = self
.active_abilities
.get_ability(AbilityInput::Auxiliary(i))
.ability_id(Some(self.inventory));
let image_size = 50.0;
let image_offsets = 60.0 * i as f64;
Image::new(self.imgs.inv_slot)
.w_h(image_size, image_size)
.top_right_with_margins_on(state.content_align, 150.0 + image_offsets, 30.0)
.set(state.active_abilities_bg[i], ui);
let ability_image = ability_id
.map_or(self.imgs.nothing, |id| util::ability_image(self.imgs, id));
let (ability_title, ability_desc) =
util::ability_description(ability_id.unwrap_or(""));
if Button::image(ability_image)
.w_h(image_size, image_size)
.top_right_with_margins_on(state.content_align, 150.0 + image_offsets, 30.0)
.with_tooltip(
self.tooltip_manager,
ability_title,
ability_desc,
&diary_tooltip,
TEXT_COLOR,
)
.set(state.active_abilities[i], ui)
.was_clicked()
{
events.push(Event::ChangeAbility(
i,
self.show
.diary_fields
.selected_ability
.unwrap_or(AuxiliaryAbility::Empty),
));
events.push(Event::SelectAbility(None));
}
}
// Display list of abilities from main weapon
Text::new("Main Weapon Abilities")
.top_left_with_margins_on(state.content_align, 75.0, 25.0)
.font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(28))
.color(TEXT_COLOR)
.set(state.main_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 main_weap_abilities: Vec<_> = ActiveAbilities::iter_unlocked_abilities(
Some(self.inventory),
Some(self.skill_set),
EquipSlot::ActiveMainhand,
)
.map(AuxiliaryAbility::MainWeapon)
.map(|a| (Ability::from(a).ability_id(Some(self.inventory)), a))
.collect();
let main_abilities_length = main_weap_abilities.len();
state.update(|s| {
s.main_weap_abilities
.resize(main_abilities_length, &mut ui.widget_id_generator())
});
state.update(|s| {
s.main_weap_ability_titles
.resize(main_abilities_length, &mut ui.widget_id_generator())
});
state.update(|s| {
s.main_weap_ability_descs
.resize(main_abilities_length, &mut ui.widget_id_generator())
});
for (i, (ability_id, ability)) in main_weap_abilities.iter().enumerate() {
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)
{
TEXT_COLOR
} 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.main_weap_title, 40.0 + image_offsets, 0.0)
.set(state.main_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.main_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.main_weap_ability_titles[i], ui);
Text::new(ability_desc)
.top_left_with_margins_on(state.main_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.main_weap_ability_descs[i], ui);
}
// Display list of abilities from off weapon
let offset_state = if main_abilities_length > 0 {
state.main_weap_abilities[main_abilities_length - 1]
} else {
state.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(TEXT_COLOR)
.set(state.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.off_weap_abilities
.resize(off_abilities_length, &mut ui.widget_id_generator())
});
state.update(|s| {
s.off_weap_ability_titles
.resize(off_abilities_length, &mut ui.widget_id_generator())
});
state.update(|s| {
s.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 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)
{
TEXT_COLOR
} 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.off_weap_title, 40.0 + image_offsets, 0.0)
.set(state.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.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.off_weap_ability_titles[i], ui);
Text::new(ability_desc)
.top_left_with_margins_on(state.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.off_weap_ability_descs[i], ui);
}
events
},
}
}
}

View File

@ -535,7 +535,6 @@ pub enum Event {
RemoveBuff(BuffKind),
UnlockSkill(Skill),
RequestSiteInfo(SiteId),
// TODO: This variant currently unused. UI is needed for it to be properly used.
ChangeAbility(usize, comp::ability::AuxiliaryAbility),
SettingsChange(SettingsChange),
@ -651,7 +650,7 @@ pub struct Show {
ingame: bool,
chat_tab_settings_index: Option<usize>,
settings_tab: SettingsTab,
skilltreetab: SelectedSkillTree,
diary_fields: diary::DiaryShow,
crafting_tab: CraftingTab,
crafting_search_key: Option<String>,
craft_sprite: Option<(Vec3<i32>, SpriteKind)>,
@ -746,6 +745,7 @@ impl Show {
self.salvage = false;
self.bag = false;
self.map = false;
self.diary_fields = diary::DiaryShow::default();
self.diary = open;
self.want_grab = !open;
}
@ -851,7 +851,7 @@ impl Show {
}
fn open_skill_tree(&mut self, tree_sel: SelectedSkillTree) {
self.skilltreetab = tree_sel;
self.diary_fields.skilltreetab = tree_sel;
self.social = false;
}
@ -1045,7 +1045,7 @@ impl Hud {
group_menu: false,
chat_tab_settings_index: None,
settings_tab: SettingsTab::Interface,
skilltreetab: SelectedSkillTree::General,
diary_fields: diary::DiaryShow::default(),
crafting_tab: CraftingTab::All,
crafting_search_key: None,
craft_sprite: None,
@ -3097,11 +3097,17 @@ impl Hud {
if self.show.diary {
let entity = client.entity();
let skill_sets = ecs.read_storage::<comp::SkillSet>();
if let Some(skill_set) = skill_sets.get(entity) {
if let (Some(skill_set), Some(active_abilities), Some(inventory)) = (
skill_sets.get(entity),
active_abilities.get(entity),
inventories.get(entity),
) {
for event in Diary::new(
&self.show,
client,
skill_set,
active_abilities,
inventory,
&self.imgs,
&self.item_imgs,
&self.fonts,
@ -3122,6 +3128,15 @@ impl Hud {
self.show.open_skill_tree(tree_sel)
},
diary::Event::UnlockSkill(skill) => events.push(Event::UnlockSkill(skill)),
diary::Event::ChangeSection(section) => {
self.show.diary_fields.section = section;
},
diary::Event::SelectAbility(ability) => {
self.show.diary_fields.selected_ability = ability;
},
diary::Event::ChangeAbility(slot, new_ability) => {
events.push(Event::ChangeAbility(slot, new_ability))
},
}
}
}