Wired up skill UI to check for prerequisites being met and sufficient skill points.

available SP check in general HUD, more skill icons

Trimmed 2000 lines from a file.

UI tweaks
This commit is contained in:
Sam 2021-01-05 01:03:25 -05:00
parent 4b52574750
commit 986c05621a
22 changed files with 1844 additions and 2142 deletions

BIN
assets/voxygen/element/buttons/arrow_down_gold.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/skilltree/leap_cost.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/skilltree/leap_damage.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/skilltree/leap_distance.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/skilltree/leap_knockback.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/skilltree/leap_radius.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/skilltree/spin_amount.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/skilltree/spin_cost.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/skilltree/spin_damage.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/skilltree/spin_helicopter.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/skilltree/spin_infinite.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/skilltree/spin_speed.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

View File

@ -280,11 +280,7 @@ magically infused items?"#,
"hud.bag.feet": "Feet",
"hud.bag.mainhand": "Mainhand",
"hud.bag.offhand": "Offhand",
// Diary
"hud.diary": "Diary",
// Map and Questlog
"hud.map.map_title": "Map",
"hud.map.qlog_title": "Quests",
@ -397,7 +393,7 @@ magically infused items?"#,
"hud.crafting.recipes": "Recipes",
"hud.crafting.ingredients": "Ingredients:",
"hud.crafting.craft": "Craft",
"hud.crafting.tool_cata": "Requires:",
"hud.crafting.tool_cata": "Requires:",
"hud.group": "Group",
"hud.group.invite_to_join": "{name} invited you to their group!",

View File

@ -8,6 +8,7 @@
"hud.quests": "Quests",
"hud.you_died": "You Died",
"hud.waypoint_saved": "Waypoint Saved",
"hud.sp_arrow_txt": "SP",
"hud.press_key_to_show_keybindings_fmt": "[{key}] Keybindings",
"hud.press_key_to_toggle_lantern_fmt": "[{key}] Lantern",
@ -64,6 +65,8 @@ Maybe you can even obtain one of their
magically infused items?"#,
"hud.spell": "Spells",
// Diary
"hud.diary": "Diary",
"hud.free_look_indicator": "Free look active. Press {key} to disable.",
"hud.auto_walk_indicator": "Auto walk active",

View File

@ -6,7 +6,7 @@ use hashbrown::{HashMap, HashSet};
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use std::hash::Hash;
use tracing::warn;
use tracing::{trace, warn};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SkillTreeMap(HashMap<SkillGroupType, HashSet<Skill>>);
@ -338,8 +338,8 @@ impl SkillSet {
} else {
skill.get_max_level().map(|_| 1)
};
let prerequisites_met = self.prerequisites_met(skill, next_level);
if !matches!(self.skills.get(&skill), Some(&None)) {
let prerequisites_met = self.prerequisites_met(skill);
if !matches!(self.skills.get(&skill), Some(level) if *level == skill.get_max_level()) {
if let Some(mut skill_group) = self
.skill_groups
.iter_mut()
@ -359,16 +359,16 @@ impl SkillSet {
}
self.skills.insert(skill, next_level);
} else {
warn!("Tried to unlock skill for skill group with insufficient SP");
trace!("Tried to unlock skill for skill group with insufficient SP");
}
} else {
warn!("Tried to unlock skill without meeting prerequisite skills");
trace!("Tried to unlock skill without meeting prerequisite skills");
}
} else {
warn!("Tried to unlock skill for a skill group that player does not have");
trace!("Tried to unlock skill for a skill group that player does not have");
}
} else {
warn!("Tried to unlock skill the player already has")
trace!("Tried to unlock skill the player already has")
}
} else {
warn!(
@ -488,8 +488,13 @@ impl SkillSet {
/// Checks that the skill set contains all prerequisite skills for a
/// particular skill
pub fn prerequisites_met(&self, skill: Skill, level: Level) -> bool {
skill.prerequisite_skills(level).iter().all(|(s, l)| {
pub fn prerequisites_met(&self, skill: Skill) -> bool {
let next_level = if self.skills.contains_key(&skill) {
self.skills.get(&skill).copied().flatten().map(|l| l + 1)
} else {
skill.get_max_level().map(|_| 1)
};
skill.prerequisite_skills(next_level).iter().all(|(s, l)| {
self.skills.contains_key(s) && self.skills.get(s).map_or(false, |l_b| l_b >= l)
})
}
@ -520,6 +525,37 @@ impl SkillSet {
.filter(|s_g| s_g.skill_group_type == skill_group);
skill_groups.next().map_or(0, |s_g| s_g.exp)
}
/// Checks if player has sufficient skill points to purchase a skill
pub fn sufficient_skill_points(&self, skill: Skill) -> bool {
if let Some(skill_group_type) = SkillSet::get_skill_group_type_for_skill(&skill) {
if let Some(skill_group) = self
.skill_groups
.iter()
.find(|x| x.skill_group_type == skill_group_type)
{
let next_level = if self.skills.contains_key(&skill) {
self.skills.get(&skill).copied().flatten().map(|l| l + 1)
} else {
skill.get_max_level().map(|_| 1)
};
let needed_sp = skill.skill_cost(next_level);
skill_group.available_sp > needed_sp
} else {
false
}
} else {
false
}
}
/// Checks if the player has available SP to spend
pub fn has_available_sp(&self) -> bool {
self.skill_groups.iter().any(|sg| {
sg.available_sp > 0
&& (sg.earned_sp - sg.available_sp) < sg.skill_group_type.get_max_skill_points()
})
}
}
impl Skill {
@ -528,10 +564,7 @@ impl Skill {
pub fn prerequisite_skills(self, level: Level) -> HashMap<Skill, Level> {
let mut prerequisites = HashMap::new();
if let Some(level) = level {
if level > self.get_max_level().unwrap_or(0) {
// Sets a prerequisite of itself for skills beyond the max level
prerequisites.insert(self, Some(level));
} else if level > 1 {
if level > 1 {
// For skills above level 1, sets prerequisite of skill of lower level
prerequisites.insert(self, Some(level - 1));
}

View File

@ -1,6 +1,6 @@
use super::{
img_ids::{Imgs, ImgsRot},
BLACK, CRITICAL_HP_COLOR, LOW_HP_COLOR, TEXT_COLOR,
BLACK, CRITICAL_HP_COLOR, LOW_HP_COLOR, QUALITY_LEGENDARY, TEXT_COLOR,
};
use crate::{
i18n::Localization,
@ -11,10 +11,10 @@ use crate::{
use client::Client;
use common::comp::Stats;
use conrod_core::{
widget::{self, Button, Text},
widget::{self, Button, Image, Text},
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
};
use inline_tweak::*;
widget_ids! {
struct Ids {
bag,
@ -42,6 +42,9 @@ widget_ids! {
crafting_text,
crafting_text_bg,
group_button,
sp_arrow,
sp_arrow_txt_bg,
sp_arrow_txt,
}
}
#[derive(WidgetCommon)]
@ -57,6 +60,7 @@ pub struct Buttons<'a> {
tooltip_manager: &'a mut TooltipManager,
localized_strings: &'a Localization,
stats: &'a Stats,
pulse: f32,
}
impl<'a> Buttons<'a> {
@ -71,6 +75,7 @@ impl<'a> Buttons<'a> {
tooltip_manager: &'a mut TooltipManager,
localized_strings: &'a Localization,
stats: &'a Stats,
pulse: f32,
) -> Self {
Self {
client,
@ -83,6 +88,7 @@ impl<'a> Buttons<'a> {
tooltip_manager,
localized_strings,
stats,
pulse,
}
}
}
@ -122,6 +128,9 @@ impl<'a> Widget for Buttons<'a> {
None => return None,
};
let localized_strings = self.localized_strings;
let arrow_ani =
(self.pulse * tweak!(4.0)/* speed factor */).cos() * tweak!(0.5) + tweak!(0.8); //Animation timer
let button_tooltip = Tooltip::new({
// Edge images [t, b, r, l]
// Corner images [tr, tl, br, bl]
@ -326,22 +335,26 @@ impl<'a> Widget for Buttons<'a> {
.color(TEXT_COLOR)
.set(state.ids.map_text, ui);
}
// Spellbook
if Button::image(self.imgs.spellbook_button)
.w_h(28.0, 25.0)
.left_from(state.ids.map_button, 10.0)
.hover_image(self.imgs.spellbook_hover)
.press_image(self.imgs.spellbook_press)
.with_tooltip(
self.tooltip_manager,
&localized_strings.get("hud.diary"),
"",
&button_tooltip,
TEXT_COLOR,
)
.set(state.ids.spellbook_button, ui)
.was_clicked()
// Diary
let unspent_sp = self.stats.skill_set.has_available_sp();
if Button::image(if !unspent_sp {
self.imgs.spellbook_button
} else {
self.imgs.spellbook_hover
})
.w_h(28.0, 25.0)
.left_from(state.ids.map_button, 10.0)
.hover_image(self.imgs.spellbook_hover)
.press_image(self.imgs.spellbook_press)
.with_tooltip(
self.tooltip_manager,
&localized_strings.get("hud.diary"),
"",
&button_tooltip,
TEXT_COLOR,
)
.set(state.ids.spellbook_button, ui)
.was_clicked()
{
return Some(Event::ToggleSpell);
}
@ -364,7 +377,32 @@ impl<'a> Widget for Buttons<'a> {
.color(TEXT_COLOR)
.set(state.ids.spellbook_text, ui);
}
// Unspent SP indicator
if unspent_sp {
Image::new(self.imgs.sp_indicator_arrow)
.w_h(20.0, 11.0)
.graphics_for(state.ids.spellbook_button)
.mid_top_with_margin_on(
state.ids.spellbook_button,
tweak!(-12.0) + arrow_ani as f64,
)
.color(Some(QUALITY_LEGENDARY))
.set(state.ids.sp_arrow, ui);
Text::new(&localized_strings.get("hud.sp_arrow_txt"))
.mid_top_with_margin_on(state.ids.sp_arrow, tweak!(-18.0))
.graphics_for(state.ids.spellbook_button)
.font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(tweak!(14)))
.color(BLACK)
.set(state.ids.sp_arrow_txt_bg, ui);
Text::new(&localized_strings.get("hud.sp_arrow_txt"))
.graphics_for(state.ids.spellbook_button)
.bottom_right_with_margins_on(state.ids.sp_arrow_txt_bg, 1.0, 1.0)
.font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(tweak!(14)))
.color(QUALITY_LEGENDARY)
.set(state.ids.sp_arrow_txt, ui);
}
// Crafting
if Button::image(self.imgs.crafting_icon)
.w_h(25.0, 25.0)

File diff suppressed because it is too large Load Diff

View File

@ -71,27 +71,47 @@ impl State {
use specs::WorldExt;
let inventories = client.state().ecs().read_storage::<Inventory>();
let inventory = inventories.get(client.entity());
let should_be_present = if let Some(inventory) = inventory {
let stats = client.state().ecs().read_storage::<common::comp::Stats>();
let stat = stats.get(client.entity());
let should_be_present = if let (Some(inventory), Some(stat)) = (inventory, stat) {
inventory
.equipped(EquipSlot::Mainhand)
.map(|i| i.kind())
.filter(|kind| {
use common::comp::item::{
tool::{ToolKind, UniqueKind},
ItemKind,
use common::comp::{
item::{
tool::{ToolKind, UniqueKind},
ItemKind,
},
skills::{self, Skill},
};
if let ItemKind::Tool(kind) = kind {
matches!(
&kind.kind,
ToolKind::Staff
| ToolKind::Debug
| ToolKind::Sword
| ToolKind::Hammer
| ToolKind::Axe
| ToolKind::Bow
| ToolKind::Unique(UniqueKind::QuadMedQuick)
| ToolKind::Unique(UniqueKind::QuadLowBreathe)
)
if let ItemKind::Tool(tool) = kind {
match tool.kind {
ToolKind::Sword => stat
.skill_set
.skills
.contains_key(&Skill::Sword(skills::SwordSkill::SUnlockSpin)),
ToolKind::Axe => stat
.skill_set
.skills
.contains_key(&Skill::Axe(skills::AxeSkill::LUnlockLeap)),
ToolKind::Hammer => stat
.skill_set
.skills
.contains_key(&Skill::Hammer(skills::HammerSkill::LUnlockLeap)),
ToolKind::Bow => stat
.skill_set
.skills
.contains_key(&Skill::Bow(skills::BowSkill::UnlockRepeater)),
ToolKind::Staff => stat
.skill_set
.skills
.contains_key(&Skill::Staff(skills::StaffSkill::UnlockShockwave)),
ToolKind::Debug
| ToolKind::Unique(UniqueKind::QuadMedQuick)
| ToolKind::Unique(UniqueKind::QuadLowBreathe) => true,
_ => false,
}
} else {
false
}

View File

@ -147,6 +147,8 @@ image_ids! {
group_icon_hover: "voxygen.element.buttons.group_hover",
group_icon_press: "voxygen.element.buttons.group_press",
sp_indicator_arrow: "voxygen.element.buttons.arrow_down_gold",
// Skill Icons
twohsword_m1: "voxygen.element.icons.2hsword_m1",
twohsword_m2: "voxygen.element.icons.2hsword_m2",

View File

@ -1895,6 +1895,7 @@ impl Hud {
tooltip_manager,
i18n,
&player_stats,
self.pulse,
)
.set(self.ids.buttons, ui_widgets)
{

View File

@ -37,6 +37,8 @@ use iced::{
};
use vek::Rgba;
use inline_tweak::*;
pub const TEXT_COLOR: iced::Color = iced::Color::from_rgb(1.0, 1.0, 1.0);
pub const DISABLED_TEXT_COLOR: iced::Color = iced::Color::from_rgba(1.0, 1.0, 1.0, 0.2);
pub const TOOLTIP_BACK_COLOR: Rgba<u8> = Rgba::new(20, 18, 10, 255);
@ -444,7 +446,7 @@ impl Controls {
select_button,
Column::with_children(vec![
Text::new(&character.character.alias)
.size(fonts.cyri.scale(30))
.size(fonts.cyri.scale(tweak!(26)))
.into(),
// TODO: only construct string once when characters
// are