mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Skills are now factored into combat rating. New formula for exp scaling per SP.
Adjust lvl up msg fade Add female humanoid names WIP Changed text formatting when skill is max level. Added message to show you have 0 skill points available. Addressed a lot of comments. various changes to UI - fix skillbar offset - remove CR indicators for group members - add CR indicators to group member frames - use unified CR indicator icon Exp reward tweaks. Fixed flamethrower range skill description.
This commit is contained in:
parent
0b156542e4
commit
eaa41c7dea
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -5995,7 +5995,6 @@ dependencies = [
|
|||||||
"hashbrown 0.9.1",
|
"hashbrown 0.9.1",
|
||||||
"image",
|
"image",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"inline_tweak",
|
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
"num-traits 0.2.14",
|
"num-traits 0.2.14",
|
||||||
@ -6100,7 +6099,6 @@ dependencies = [
|
|||||||
"futures-timer 3.0.2",
|
"futures-timer 3.0.2",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hashbrown 0.9.1",
|
"hashbrown 0.9.1",
|
||||||
"inline_tweak",
|
|
||||||
"itertools",
|
"itertools",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libsqlite3-sys",
|
"libsqlite3-sys",
|
||||||
|
@ -106,6 +106,99 @@
|
|||||||
"Zenner"
|
"Zenner"
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
/*keyword: "humanoid_f",
|
||||||
|
names: [
|
||||||
|
"Acele",
|
||||||
|
"Autumn",
|
||||||
|
"Acholate",
|
||||||
|
"Ada",
|
||||||
|
"Adorra",
|
||||||
|
"Ahanna",
|
||||||
|
"Brana",
|
||||||
|
"Bathelie",
|
||||||
|
"Calene",
|
||||||
|
"Calina",
|
||||||
|
"Celestine",
|
||||||
|
"Caela",
|
||||||
|
"Cassia",
|
||||||
|
"Celoa",
|
||||||
|
"Dalavesta",
|
||||||
|
"Dylena",
|
||||||
|
"Desini",
|
||||||
|
"Diva",
|
||||||
|
"Ebatryne",
|
||||||
|
"Efari",
|
||||||
|
"Enona",
|
||||||
|
"Enaldie",
|
||||||
|
"Ember",
|
||||||
|
"Esdel",
|
||||||
|
"Eune",
|
||||||
|
"Fayne",
|
||||||
|
"Frida",
|
||||||
|
"Ferra",
|
||||||
|
"Flora",
|
||||||
|
"Fintis",
|
||||||
|
"Gatlen",
|
||||||
|
"Gatline",
|
||||||
|
"Gronalyn",
|
||||||
|
"Helenia",
|
||||||
|
"Halete",
|
||||||
|
"Hyza",
|
||||||
|
"Helena",
|
||||||
|
"Halin",
|
||||||
|
"Hera",
|
||||||
|
"Hilda",
|
||||||
|
"Hydra",
|
||||||
|
"Ismeria",
|
||||||
|
"Iris",
|
||||||
|
"Joss",
|
||||||
|
"Kadra",
|
||||||
|
"Kagra",
|
||||||
|
"Kyra",
|
||||||
|
"Konta",
|
||||||
|
"Krinn",
|
||||||
|
"Lydia",
|
||||||
|
"Laelia",
|
||||||
|
"Leda",
|
||||||
|
"Leta",
|
||||||
|
"Lisbeth",
|
||||||
|
"Lyra",
|
||||||
|
"Luna",
|
||||||
|
"Medora",
|
||||||
|
"Mazarine",
|
||||||
|
"Merlyn",
|
||||||
|
"Marina",
|
||||||
|
"Nephele",
|
||||||
|
"Odessa",
|
||||||
|
"Orla",
|
||||||
|
"Perl",
|
||||||
|
"Rhodeia",
|
||||||
|
"Rosella",
|
||||||
|
"Raven",
|
||||||
|
"Rachel",
|
||||||
|
"Ryven",
|
||||||
|
"Solenne",
|
||||||
|
"Seren",
|
||||||
|
"Summer",
|
||||||
|
"Solstice",
|
||||||
|
"Stella",
|
||||||
|
"Sarah",
|
||||||
|
"Syrin",
|
||||||
|
"Tessa",
|
||||||
|
"Thea",
|
||||||
|
"Tez",
|
||||||
|
"Vivien",
|
||||||
|
"Varda",
|
||||||
|
"Veridia",
|
||||||
|
"Victoria",
|
||||||
|
"Vale",
|
||||||
|
"Vega",
|
||||||
|
"Yorja",
|
||||||
|
"Xaviera",
|
||||||
|
"Zorina",
|
||||||
|
"Zephyra"
|
||||||
|
]
|
||||||
|
),*/
|
||||||
species: (
|
species: (
|
||||||
danari: (
|
danari: (
|
||||||
keyword: "danari",
|
keyword: "danari",
|
||||||
|
BIN
assets/voxygen/element/frames/enemybar_1.png
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/enemybar_1.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/icons/combat_rating_shadow.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/combat_rating_shadow.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -28,7 +28,6 @@ spin_sleep = "1.0"
|
|||||||
tracing = { version = "0.1", default-features = false }
|
tracing = { version = "0.1", default-features = false }
|
||||||
vek = { version = "0.12.0", features = ["serde"] }
|
vek = { version = "0.12.0", features = ["serde"] }
|
||||||
uuid = { version = "0.8.1", default-features = false, features = ["serde", "v4"] }
|
uuid = { version = "0.8.1", default-features = false, features = ["serde", "v4"] }
|
||||||
inline_tweak = "1.0.2"
|
|
||||||
|
|
||||||
# Assets
|
# Assets
|
||||||
assets_manager = {version = "0.4.2", features = ["bincode", "ron", "json", "hot-reloading"]}
|
assets_manager = {version = "0.4.2", features = ["bincode", "ron", "json", "hot-reloading"]}
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{
|
comp::{
|
||||||
inventory::{
|
inventory::{
|
||||||
item::{armor::Protection, tool::ToolKind, ItemKind},
|
item::{
|
||||||
|
armor::Protection,
|
||||||
|
tool::{Tool, ToolKind},
|
||||||
|
ItemKind,
|
||||||
|
},
|
||||||
slot::EquipSlot,
|
slot::EquipSlot,
|
||||||
},
|
},
|
||||||
Body, BuffKind, Health, HealthChange, HealthSource, Inventory,
|
skills::{SkillGroupType, SkillSet},
|
||||||
|
BuffKind, Health, HealthChange, HealthSource, Inventory, Stats,
|
||||||
},
|
},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
util::Dir,
|
util::Dir,
|
||||||
};
|
};
|
||||||
use inline_tweak::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -210,52 +214,47 @@ impl Knockback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn equipped_tool(inv: &Inventory, slot: EquipSlot) -> Option<&Tool> {
|
||||||
|
inv.equipped(slot).and_then(|i| {
|
||||||
|
if let ItemKind::Tool(tool) = &i.kind() {
|
||||||
|
Some(tool)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_weapons(inv: &Inventory) -> (Option<ToolKind>, Option<ToolKind>) {
|
pub fn get_weapons(inv: &Inventory) -> (Option<ToolKind>, Option<ToolKind>) {
|
||||||
(
|
(
|
||||||
inv.equipped(EquipSlot::Mainhand).and_then(|i| {
|
equipped_tool(inv, EquipSlot::Mainhand).map(|tool| tool.kind),
|
||||||
if let ItemKind::Tool(tool) = &i.kind() {
|
equipped_tool(inv, EquipSlot::Offhand).map(|tool| tool.kind),
|
||||||
Some(tool.kind)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
inv.equipped(EquipSlot::Offhand).and_then(|i| {
|
|
||||||
if let ItemKind::Tool(tool) = &i.kind() {
|
|
||||||
Some(tool.kind)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn max_equipped_weapon_damage(inv: &Inventory) -> f32 {
|
fn max_equipped_weapon_damage(inv: &Inventory, skillset: &SkillSet) -> f32 {
|
||||||
let active_damage = inv.equipped(EquipSlot::Mainhand).map_or(0.0, |i| {
|
let active_damage = equipped_tool(inv, EquipSlot::Mainhand).map_or(0.0, |tool| {
|
||||||
if let ItemKind::Tool(tool) = &i.kind() {
|
tool.base_power()
|
||||||
tool.base_power() * tool.base_speed()
|
* tool.base_speed()
|
||||||
} else {
|
* (1.0 + 0.05 * skillset.earned_sp(SkillGroupType::Weapon(tool.kind)) as f32)
|
||||||
0.0
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
let second_damage = inv.equipped(EquipSlot::Offhand).map_or(0.0, |i| {
|
let second_damage = equipped_tool(inv, EquipSlot::Offhand).map_or(0.0, |tool| {
|
||||||
if let ItemKind::Tool(tool) = &i.kind() {
|
tool.base_power()
|
||||||
tool.base_power() * tool.base_speed()
|
* tool.base_speed()
|
||||||
} else {
|
* (1.0 + 0.05 * skillset.earned_sp(SkillGroupType::Weapon(tool.kind)) as f32)
|
||||||
0.0
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
active_damage.max(second_damage)
|
active_damage.max(second_damage)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn combat_rating(inventory: &Inventory, health: &Health, body: &Body) -> f32 {
|
pub fn combat_rating(inventory: &Inventory, health: &Health, stats: &Stats) -> f32 {
|
||||||
let defensive_weighting = tweak!(1.0);
|
let defensive_weighting = 1.0;
|
||||||
let offensive_weighting = tweak!(1.0);
|
let offensive_weighting = 1.0;
|
||||||
let defensive_rating = health.maximum() as f32
|
let defensive_rating = health.maximum() as f32
|
||||||
/ (1.0 - Damage::compute_damage_reduction(inventory)).max(0.00001)
|
/ (1.0 - Damage::compute_damage_reduction(inventory)).max(0.00001)
|
||||||
/ 100.0;
|
/ 100.0;
|
||||||
let offensive_rating = max_equipped_weapon_damage(inventory).max(0.1);
|
let offensive_rating = max_equipped_weapon_damage(inventory, &stats.skill_set).max(0.1)
|
||||||
|
+ 0.05 * stats.skill_set.earned_sp(SkillGroupType::General) as f32;
|
||||||
let combined_rating = (offensive_rating * offensive_weighting
|
let combined_rating = (offensive_rating * offensive_weighting
|
||||||
+ defensive_rating * defensive_weighting)
|
+ defensive_rating * defensive_weighting)
|
||||||
/ (2.0 * offensive_weighting.max(defensive_weighting));
|
/ (offensive_weighting + defensive_weighting);
|
||||||
combined_rating * body.combat_multiplier()
|
combined_rating * stats.body_type.combat_multiplier()
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
Knockback,
|
Knockback,
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use vek::Vec3;
|
use vek::Vec3;
|
||||||
@ -500,7 +499,7 @@ impl CharacterAbility {
|
|||||||
|
|
||||||
pub fn adjusted_by_skills(
|
pub fn adjusted_by_skills(
|
||||||
mut self,
|
mut self,
|
||||||
skills: &HashMap<skills::Skill, skills::Level>,
|
skillset: &skills::SkillSet,
|
||||||
tool: Option<ToolKind>,
|
tool: Option<ToolKind>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
use skills::Skill::{self, *};
|
use skills::Skill::{self, *};
|
||||||
@ -518,14 +517,12 @@ impl CharacterAbility {
|
|||||||
ref mut scales_from_combo,
|
ref mut scales_from_combo,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
*is_interruptible = skills.contains_key(&Sword(InterruptingAttacks));
|
*is_interruptible = skillset.has_skill(Sword(InterruptingAttacks));
|
||||||
let speed_segments =
|
let speed_segments = Sword(TsSpeed).max_level().map_or(1, |l| l + 1) as f32;
|
||||||
Sword(TsSpeed).get_max_level().map_or(1, |l| l + 1) as f32;
|
let speed_level = if skillset.has_skill(Sword(TsCombo)) {
|
||||||
let speed_level = if skills.contains_key(&Sword(TsCombo)) {
|
skillset
|
||||||
skills
|
.skill_level(Sword(TsSpeed))
|
||||||
.get(&Sword(TsSpeed))
|
.unwrap_or(None)
|
||||||
.copied()
|
|
||||||
.flatten()
|
|
||||||
.map_or(1, |l| l + 1) as f32
|
.map_or(1, |l| l + 1) as f32
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
@ -535,20 +532,18 @@ impl CharacterAbility {
|
|||||||
*max_speed_increase *= speed_level / speed_segments;
|
*max_speed_increase *= speed_level / speed_segments;
|
||||||
}
|
}
|
||||||
let energy_level =
|
let energy_level =
|
||||||
if let Some(level) = skills.get(&Sword(TsRegen)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sword(TsRegen)) {
|
||||||
level
|
level
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
*max_energy_gain = (*max_energy_gain as f32
|
*max_energy_gain = (*max_energy_gain as f32
|
||||||
* ((energy_level + 1) * stage_data.len() as u16 - 1) as f32
|
* ((energy_level + 1) * stage_data.len() as u16 - 1) as f32
|
||||||
/ ((Sword(TsRegen).get_max_level().unwrap() + 1)
|
/ ((Sword(TsRegen).max_level().unwrap() + 1) * stage_data.len() as u16
|
||||||
* stage_data.len() as u16
|
|
||||||
- 1) as f32) as u32;
|
- 1) as f32) as u32;
|
||||||
*scales_from_combo = skills
|
*scales_from_combo = skillset
|
||||||
.get(&Sword(TsDamage))
|
.skill_level(Sword(TsDamage))
|
||||||
.copied()
|
.unwrap_or(None)
|
||||||
.flatten()
|
|
||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
.into();
|
.into();
|
||||||
},
|
},
|
||||||
@ -562,27 +557,27 @@ impl CharacterAbility {
|
|||||||
ref mut infinite_charge,
|
ref mut infinite_charge,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
*is_interruptible = skills.contains_key(&Sword(InterruptingAttacks));
|
*is_interruptible = skillset.has_skill(Sword(InterruptingAttacks));
|
||||||
if let Some(level) = skills.get(&Sword(DCost)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sword(DCost)) {
|
||||||
*energy_cost =
|
*energy_cost =
|
||||||
(*energy_cost as f32 * 0.75_f32.powi(level.into())) as u32;
|
(*energy_cost as f32 * 0.75_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Sword(DDrain)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sword(DDrain)) {
|
||||||
*energy_drain =
|
*energy_drain =
|
||||||
(*energy_drain as f32 * 0.75_f32.powi(level.into())) as u32;
|
(*energy_drain as f32 * 0.75_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Sword(DDamage)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sword(DDamage)) {
|
||||||
*base_damage =
|
*base_damage =
|
||||||
(*base_damage as f32 * 1.2_f32.powi(level.into())) as u32;
|
(*base_damage as f32 * 1.2_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Sword(DScaling)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sword(DScaling)) {
|
||||||
*scaled_damage =
|
*scaled_damage =
|
||||||
(*scaled_damage as f32 * 1.2_f32.powi(level.into())) as u32;
|
(*scaled_damage as f32 * 1.2_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if skills.contains_key(&Sword(DSpeed)) {
|
if skillset.has_skill(Sword(DSpeed)) {
|
||||||
*forward_speed *= 1.3;
|
*forward_speed *= 1.3;
|
||||||
}
|
}
|
||||||
*infinite_charge = skills.contains_key(&Sword(DInfinite));
|
*infinite_charge = skillset.has_skill(Sword(DInfinite));
|
||||||
},
|
},
|
||||||
SpinMelee {
|
SpinMelee {
|
||||||
ref mut is_interruptible,
|
ref mut is_interruptible,
|
||||||
@ -592,21 +587,24 @@ impl CharacterAbility {
|
|||||||
ref mut num_spins,
|
ref mut num_spins,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
*is_interruptible = skills.contains_key(&Sword(InterruptingAttacks));
|
*is_interruptible = skillset.has_skill(Sword(InterruptingAttacks));
|
||||||
if let Some(level) = skills.get(&Sword(SDamage)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sword(SDamage)) {
|
||||||
*base_damage =
|
*base_damage =
|
||||||
(*base_damage as f32 * 1.4_f32.powi(level.into())) as u32;
|
(*base_damage as f32 * 1.4_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Sword(SSpeed)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sword(SSpeed)) {
|
||||||
*swing_duration =
|
*swing_duration =
|
||||||
(*swing_duration as f32 * 0.8_f32.powi(level.into())) as u64;
|
(*swing_duration as f32 * 0.8_f32.powi(level.into())) as u64;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Sword(SCost)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sword(SCost)) {
|
||||||
*energy_cost =
|
*energy_cost =
|
||||||
(*energy_cost as f32 * 0.75_f32.powi(level.into())) as u32;
|
(*energy_cost as f32 * 0.75_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
*num_spins =
|
*num_spins = skillset
|
||||||
skills.get(&Sword(SSpins)).copied().flatten().unwrap_or(0) as u32 + 1;
|
.skill_level(Sword(SSpins))
|
||||||
|
.unwrap_or(None)
|
||||||
|
.unwrap_or(0) as u32
|
||||||
|
+ 1;
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
@ -622,31 +620,31 @@ impl CharacterAbility {
|
|||||||
ref mut scales_from_combo,
|
ref mut scales_from_combo,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if !skills.contains_key(&Axe(DsCombo)) {
|
if !skillset.has_skill(Axe(DsCombo)) {
|
||||||
stage_data.pop();
|
stage_data.pop();
|
||||||
}
|
}
|
||||||
let speed_segments = Axe(DsSpeed).get_max_level().unwrap_or(1) as f32;
|
let speed_segments = Axe(DsSpeed).max_level().unwrap_or(1) as f32;
|
||||||
let speed_level =
|
let speed_level = skillset
|
||||||
skills.get(&Axe(DsSpeed)).copied().flatten().unwrap_or(0) as f32;
|
.skill_level(Axe(DsSpeed))
|
||||||
|
.unwrap_or(None)
|
||||||
|
.unwrap_or(0) as f32;
|
||||||
{
|
{
|
||||||
*speed_increase *= speed_level / speed_segments;
|
*speed_increase *= speed_level / speed_segments;
|
||||||
*max_speed_increase *= speed_level / speed_segments;
|
*max_speed_increase *= speed_level / speed_segments;
|
||||||
}
|
}
|
||||||
let energy_level =
|
let energy_level =
|
||||||
if let Some(level) = skills.get(&Axe(DsRegen)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Axe(DsRegen)) {
|
||||||
level
|
level
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
*max_energy_gain = (*max_energy_gain as f32
|
*max_energy_gain = (*max_energy_gain as f32
|
||||||
* ((energy_level + 1) * stage_data.len() as u16 - 1) as f32
|
* ((energy_level + 1) * stage_data.len() as u16 - 1) as f32
|
||||||
/ ((Axe(DsRegen).get_max_level().unwrap() + 1)
|
/ ((Axe(DsRegen).max_level().unwrap() + 1) * stage_data.len() as u16
|
||||||
* stage_data.len() as u16
|
|
||||||
- 1) as f32) as u32;
|
- 1) as f32) as u32;
|
||||||
*scales_from_combo = skills
|
*scales_from_combo = skillset
|
||||||
.get(&Axe(DsDamage))
|
.skill_level(Axe(DsDamage))
|
||||||
.copied()
|
.unwrap_or(None)
|
||||||
.flatten()
|
|
||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
.into();
|
.into();
|
||||||
},
|
},
|
||||||
@ -658,17 +656,17 @@ impl CharacterAbility {
|
|||||||
ref mut is_helicopter,
|
ref mut is_helicopter,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
*is_infinite = skills.contains_key(&Axe(SInfinite));
|
*is_infinite = skillset.has_skill(Axe(SInfinite));
|
||||||
*is_helicopter = skills.contains_key(&Axe(SHelicopter));
|
*is_helicopter = skillset.has_skill(Axe(SHelicopter));
|
||||||
if let Some(level) = skills.get(&Axe(SDamage)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Axe(SDamage)) {
|
||||||
*base_damage =
|
*base_damage =
|
||||||
(*base_damage as f32 * 1.3_f32.powi(level.into())) as u32;
|
(*base_damage as f32 * 1.3_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Axe(SSpeed)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Axe(SSpeed)) {
|
||||||
*swing_duration =
|
*swing_duration =
|
||||||
(*swing_duration as f32 * 0.8_f32.powi(level.into())) as u64;
|
(*swing_duration as f32 * 0.8_f32.powi(level.into())) as u64;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Axe(SCost)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Axe(SCost)) {
|
||||||
*energy_cost =
|
*energy_cost =
|
||||||
(*energy_cost as f32 * 0.75_f32.powi(level.into())) as u32;
|
(*energy_cost as f32 * 0.75_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
@ -681,18 +679,18 @@ impl CharacterAbility {
|
|||||||
ref mut vertical_leap_strength,
|
ref mut vertical_leap_strength,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Some(level) = skills.get(&Axe(LDamage)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Axe(LDamage)) {
|
||||||
*base_damage =
|
*base_damage =
|
||||||
(*base_damage as f32 * 1.35_f32.powi(level.into())) as u32;
|
(*base_damage as f32 * 1.35_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Axe(LKnockback)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Axe(LKnockback)) {
|
||||||
*knockback *= 1.4_f32.powi(level.into());
|
*knockback *= 1.4_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Axe(LCost)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Axe(LCost)) {
|
||||||
*energy_cost =
|
*energy_cost =
|
||||||
(*energy_cost as f32 * 0.75_f32.powi(level.into())) as u32;
|
(*energy_cost as f32 * 0.75_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Axe(LDistance)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Axe(LDistance)) {
|
||||||
*forward_leap_strength *= 1.2_f32.powi(level.into());
|
*forward_leap_strength *= 1.2_f32.powi(level.into());
|
||||||
*vertical_leap_strength *= 1.2_f32.powi(level.into());
|
*vertical_leap_strength *= 1.2_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
@ -711,34 +709,34 @@ impl CharacterAbility {
|
|||||||
ref mut scales_from_combo,
|
ref mut scales_from_combo,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Some(level) = skills.get(&Hammer(SsKnockback)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Hammer(SsKnockback)) {
|
||||||
*stage_data = (*stage_data)
|
*stage_data = (*stage_data)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| s.modify_strike(1.5_f32.powi(level.into())))
|
.map(|s| s.modify_strike(1.5_f32.powi(level.into())))
|
||||||
.collect::<Vec<combo_melee::Stage<u64>>>();
|
.collect::<Vec<combo_melee::Stage<u64>>>();
|
||||||
}
|
}
|
||||||
let speed_segments = Hammer(SsSpeed).get_max_level().unwrap_or(1) as f32;
|
let speed_segments = Hammer(SsSpeed).max_level().unwrap_or(1) as f32;
|
||||||
let speed_level =
|
let speed_level = skillset
|
||||||
skills.get(&Hammer(SsSpeed)).copied().flatten().unwrap_or(0) as f32;
|
.skill_level(Hammer(SsSpeed))
|
||||||
|
.unwrap_or(None)
|
||||||
|
.unwrap_or(0) as f32;
|
||||||
{
|
{
|
||||||
*speed_increase *= speed_level / speed_segments;
|
*speed_increase *= speed_level / speed_segments;
|
||||||
*max_speed_increase *= speed_level / speed_segments;
|
*max_speed_increase *= speed_level / speed_segments;
|
||||||
}
|
}
|
||||||
let energy_level =
|
let energy_level =
|
||||||
if let Some(level) = skills.get(&Hammer(SsRegen)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Hammer(SsRegen)) {
|
||||||
level
|
level
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
*max_energy_gain = (*max_energy_gain as f32
|
*max_energy_gain = (*max_energy_gain as f32
|
||||||
* ((energy_level + 1) * stage_data.len() as u16) as f32
|
* ((energy_level + 1) * stage_data.len() as u16) as f32
|
||||||
/ ((Hammer(SsRegen).get_max_level().unwrap() + 1)
|
/ ((Hammer(SsRegen).max_level().unwrap() + 1) * stage_data.len() as u16)
|
||||||
* stage_data.len() as u16) as f32)
|
as f32) as u32;
|
||||||
as u32;
|
*scales_from_combo = skillset
|
||||||
*scales_from_combo = skills
|
.skill_level(Hammer(SsDamage))
|
||||||
.get(&Hammer(SsDamage))
|
.unwrap_or(None)
|
||||||
.copied()
|
|
||||||
.flatten()
|
|
||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
.into();
|
.into();
|
||||||
},
|
},
|
||||||
@ -749,18 +747,18 @@ impl CharacterAbility {
|
|||||||
ref mut speed,
|
ref mut speed,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Some(level) = skills.get(&Hammer(CDamage)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Hammer(CDamage)) {
|
||||||
*scaled_damage =
|
*scaled_damage =
|
||||||
(*scaled_damage as f32 * 1.25_f32.powi(level.into())) as u32;
|
(*scaled_damage as f32 * 1.25_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Hammer(CKnockback)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Hammer(CKnockback)) {
|
||||||
*scaled_knockback *= 1.5_f32.powi(level.into());
|
*scaled_knockback *= 1.5_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Hammer(CDrain)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Hammer(CDrain)) {
|
||||||
*energy_drain =
|
*energy_drain =
|
||||||
(*energy_drain as f32 * 0.75_f32.powi(level.into())) as u32;
|
(*energy_drain as f32 * 0.75_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Hammer(CSpeed)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Hammer(CSpeed)) {
|
||||||
*speed *= 1.25_f32.powi(level.into());
|
*speed *= 1.25_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -773,22 +771,22 @@ impl CharacterAbility {
|
|||||||
ref mut range,
|
ref mut range,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Some(level) = skills.get(&Hammer(LDamage)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Hammer(LDamage)) {
|
||||||
*base_damage =
|
*base_damage =
|
||||||
(*base_damage as f32 * 1.4_f32.powi(level.into())) as u32;
|
(*base_damage as f32 * 1.4_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Hammer(LKnockback)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Hammer(LKnockback)) {
|
||||||
*knockback *= 1.5_f32.powi(level.into());
|
*knockback *= 1.5_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Hammer(LCost)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Hammer(LCost)) {
|
||||||
*energy_cost =
|
*energy_cost =
|
||||||
(*energy_cost as f32 * 0.75_f32.powi(level.into())) as u32;
|
(*energy_cost as f32 * 0.75_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Hammer(LDistance)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Hammer(LDistance)) {
|
||||||
*forward_leap_strength *= 1.25_f32.powi(level.into());
|
*forward_leap_strength *= 1.25_f32.powi(level.into());
|
||||||
*vertical_leap_strength *= 1.25_f32.powi(level.into());
|
*vertical_leap_strength *= 1.25_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Hammer(LRange)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Hammer(LRange)) {
|
||||||
*range += 1.0 * level as f32;
|
*range += 1.0 * level as f32;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -803,12 +801,17 @@ impl CharacterAbility {
|
|||||||
ref mut projectile_speed,
|
ref mut projectile_speed,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Some(level) = skills.get(&Bow(ProjSpeed)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Bow(ProjSpeed)) {
|
||||||
*projectile_speed *= 1.5_f32.powi(level.into());
|
*projectile_speed *= 1.5_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
let damage_level =
|
let damage_level = skillset
|
||||||
skills.get(&Bow(BDamage)).copied().flatten().unwrap_or(0);
|
.skill_level(Bow(BDamage))
|
||||||
let regen_level = skills.get(&Bow(BRegen)).copied().flatten().unwrap_or(0);
|
.unwrap_or(None)
|
||||||
|
.unwrap_or(0);
|
||||||
|
let regen_level = skillset
|
||||||
|
.skill_level(Bow(BRegen))
|
||||||
|
.unwrap_or(None)
|
||||||
|
.unwrap_or(0);
|
||||||
let power = 1.3_f32.powi(damage_level.into());
|
let power = 1.3_f32.powi(damage_level.into());
|
||||||
let regen = 1.5_f32.powi(regen_level.into());
|
let regen = 1.5_f32.powi(regen_level.into());
|
||||||
*projectile = projectile.modified_projectile(power, regen, 1_f32, 1_f32);
|
*projectile = projectile.modified_projectile(power, regen, 1_f32, 1_f32);
|
||||||
@ -823,27 +826,27 @@ impl CharacterAbility {
|
|||||||
ref mut move_speed,
|
ref mut move_speed,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Some(level) = skills.get(&Bow(ProjSpeed)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Bow(ProjSpeed)) {
|
||||||
*initial_projectile_speed *= 1.5_f32.powi(level.into());
|
*initial_projectile_speed *= 1.5_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Bow(CDamage)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Bow(CDamage)) {
|
||||||
*scaled_damage =
|
*scaled_damage =
|
||||||
(*scaled_damage as f32 * 1.25_f32.powi(level.into())) as u32;
|
(*scaled_damage as f32 * 1.25_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Bow(CKnockback)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Bow(CKnockback)) {
|
||||||
*scaled_knockback *= 1.5_f32.powi(level.into());
|
*scaled_knockback *= 1.5_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Bow(CProjSpeed)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Bow(CProjSpeed)) {
|
||||||
*scaled_projectile_speed *= 1.2_f32.powi(level.into());
|
*scaled_projectile_speed *= 1.2_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Bow(CDrain)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Bow(CDrain)) {
|
||||||
*energy_drain =
|
*energy_drain =
|
||||||
(*energy_drain as f32 * 0.75_f32.powi(level.into())) as u32;
|
(*energy_drain as f32 * 0.75_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Bow(CSpeed)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Bow(CSpeed)) {
|
||||||
*speed *= 1.25_f32.powi(level.into());
|
*speed *= 1.25_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Bow(CMove)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Bow(CMove)) {
|
||||||
*move_speed *= 1.25_f32.powi(level.into());
|
*move_speed *= 1.25_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -855,21 +858,21 @@ impl CharacterAbility {
|
|||||||
ref mut projectile_speed,
|
ref mut projectile_speed,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Some(level) = skills.get(&Bow(ProjSpeed)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Bow(ProjSpeed)) {
|
||||||
*projectile_speed *= 1.5_f32.powi(level.into());
|
*projectile_speed *= 1.5_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Bow(RDamage)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Bow(RDamage)) {
|
||||||
let power = 1.3_f32.powi(level.into());
|
let power = 1.3_f32.powi(level.into());
|
||||||
*projectile =
|
*projectile =
|
||||||
projectile.modified_projectile(power, 1_f32, 1_f32, 1_f32);
|
projectile.modified_projectile(power, 1_f32, 1_f32, 1_f32);
|
||||||
}
|
}
|
||||||
if !skills.contains_key(&Bow(RGlide)) {
|
if !skillset.has_skill(Bow(RGlide)) {
|
||||||
*buildup_duration = 1;
|
*buildup_duration = 1;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Bow(RArrows)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Bow(RArrows)) {
|
||||||
*reps_remaining += level as u32;
|
*reps_remaining += level as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Bow(RCost)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Bow(RCost)) {
|
||||||
*energy_cost =
|
*energy_cost =
|
||||||
(*energy_cost as f32 * 0.75_f32.powi(level.into())) as u32;
|
(*energy_cost as f32 * 0.75_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
@ -883,15 +886,21 @@ impl CharacterAbility {
|
|||||||
BasicRanged {
|
BasicRanged {
|
||||||
ref mut projectile, ..
|
ref mut projectile, ..
|
||||||
} => {
|
} => {
|
||||||
if !skills.contains_key(&Staff(BExplosion)) {
|
if !skillset.has_skill(Staff(BExplosion)) {
|
||||||
*projectile = projectile.fireball_to_firebolt();
|
*projectile = projectile.fireball_to_firebolt();
|
||||||
}
|
}
|
||||||
let damage_level =
|
let damage_level = skillset
|
||||||
skills.get(&Staff(BDamage)).copied().flatten().unwrap_or(0);
|
.skill_level(Staff(BDamage))
|
||||||
let regen_level =
|
.unwrap_or(None)
|
||||||
skills.get(&Staff(BRegen)).copied().flatten().unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let range_level =
|
let regen_level = skillset
|
||||||
skills.get(&Staff(BRadius)).copied().flatten().unwrap_or(0);
|
.skill_level(Staff(BRegen))
|
||||||
|
.unwrap_or(None)
|
||||||
|
.unwrap_or(0);
|
||||||
|
let range_level = skillset
|
||||||
|
.skill_level(Staff(BRadius))
|
||||||
|
.unwrap_or(None)
|
||||||
|
.unwrap_or(0);
|
||||||
let power = 1.2_f32.powi(damage_level.into());
|
let power = 1.2_f32.powi(damage_level.into());
|
||||||
let regen = 1.2_f32.powi(regen_level.into());
|
let regen = 1.2_f32.powi(regen_level.into());
|
||||||
let range = 1.1_f32.powi(range_level.into());
|
let range = 1.1_f32.powi(range_level.into());
|
||||||
@ -904,20 +913,20 @@ impl CharacterAbility {
|
|||||||
ref mut beam_duration,
|
ref mut beam_duration,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Some(level) = skills.get(&Staff(FDamage)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Staff(FDamage)) {
|
||||||
*base_dps = (*base_dps as f32 * 1.3_f32.powi(level.into())) as u32;
|
*base_dps = (*base_dps as f32 * 1.3_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Staff(FRange)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Staff(FRange)) {
|
||||||
let range_mod = 1.25_f32.powi(level.into());
|
let range_mod = 1.25_f32.powi(level.into());
|
||||||
*range *= range_mod;
|
*range *= range_mod;
|
||||||
// Duration modified to keep velocity constant
|
// Duration modified to keep velocity constant
|
||||||
*beam_duration = (*beam_duration as f32 * range_mod) as u64;
|
*beam_duration = (*beam_duration as f32 * range_mod) as u64;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Staff(FDrain)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Staff(FDrain)) {
|
||||||
*energy_drain =
|
*energy_drain =
|
||||||
(*energy_drain as f32 * 0.8_f32.powi(level.into())) as u32;
|
(*energy_drain as f32 * 0.8_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Staff(FVelocity)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Staff(FVelocity)) {
|
||||||
let velocity_increase = 1.25_f32.powi(level.into());
|
let velocity_increase = 1.25_f32.powi(level.into());
|
||||||
let duration_mod = 1.0 / (1.0 + velocity_increase);
|
let duration_mod = 1.0 / (1.0 + velocity_increase);
|
||||||
*beam_duration = (*beam_duration as f32 * duration_mod) as u64;
|
*beam_duration = (*beam_duration as f32 * duration_mod) as u64;
|
||||||
@ -930,17 +939,17 @@ impl CharacterAbility {
|
|||||||
ref mut energy_cost,
|
ref mut energy_cost,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Some(level) = skills.get(&Staff(SDamage)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Staff(SDamage)) {
|
||||||
*damage = (*damage as f32 * 1.3_f32.powi(level.into())) as u32;
|
*damage = (*damage as f32 * 1.3_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Staff(SKnockback)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Staff(SKnockback)) {
|
||||||
*knockback = knockback.modify_strength(1.3_f32.powi(level.into()));
|
*knockback = knockback.modify_strength(1.3_f32.powi(level.into()));
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Staff(SRange)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Staff(SRange)) {
|
||||||
*shockwave_duration =
|
*shockwave_duration =
|
||||||
(*shockwave_duration as f32 * 1.2_f32.powi(level.into())) as u64;
|
(*shockwave_duration as f32 * 1.2_f32.powi(level.into())) as u64;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Staff(SCost)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Staff(SCost)) {
|
||||||
*energy_cost =
|
*energy_cost =
|
||||||
(*energy_cost as f32 * 0.8_f32.powi(level.into())) as u32;
|
(*energy_cost as f32 * 0.8_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
@ -961,26 +970,26 @@ impl CharacterAbility {
|
|||||||
ref mut beam_duration,
|
ref mut beam_duration,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Some(level) = skills.get(&Sceptre(BHeal)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sceptre(BHeal)) {
|
||||||
*base_hps = (*base_hps as f32 * 1.2_f32.powi(level.into())) as u32;
|
*base_hps = (*base_hps as f32 * 1.2_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Sceptre(BDamage)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sceptre(BDamage)) {
|
||||||
*base_dps = (*base_dps as f32 * 1.3_f32.powi(level.into())) as u32;
|
*base_dps = (*base_dps as f32 * 1.3_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Sceptre(BRange)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sceptre(BRange)) {
|
||||||
let range_mod = 1.25_f32.powi(level.into());
|
let range_mod = 1.25_f32.powi(level.into());
|
||||||
*range *= range_mod;
|
*range *= range_mod;
|
||||||
// Duration modified to keep velocity constant
|
// Duration modified to keep velocity constant
|
||||||
*beam_duration = (*beam_duration as f32 * range_mod) as u64;
|
*beam_duration = (*beam_duration as f32 * range_mod) as u64;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Sceptre(BLifesteal)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sceptre(BLifesteal)) {
|
||||||
*lifesteal_eff *= 1.5_f32.powi(level.into());
|
*lifesteal_eff *= 1.5_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Sceptre(BRegen)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sceptre(BRegen)) {
|
||||||
*energy_regen =
|
*energy_regen =
|
||||||
(*energy_regen as f32 * 1.1_f32.powi(level.into())) as u32;
|
(*energy_regen as f32 * 1.1_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Sceptre(BCost)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sceptre(BCost)) {
|
||||||
*energy_cost =
|
*energy_cost =
|
||||||
(*energy_cost as f32 * 0.9_f32.powi(level.into())) as u32;
|
(*energy_cost as f32 * 0.9_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
@ -992,28 +1001,28 @@ impl CharacterAbility {
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
{
|
{
|
||||||
let heal_level =
|
let heal_level = skillset
|
||||||
skills.get(&Sceptre(PHeal)).copied().flatten().unwrap_or(0);
|
.skill_level(Sceptre(PHeal))
|
||||||
let damage_level = skills
|
.unwrap_or(None)
|
||||||
.get(&Sceptre(PDamage))
|
|
||||||
.copied()
|
|
||||||
.flatten()
|
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let range_level = skills
|
let damage_level = skillset
|
||||||
.get(&Sceptre(PRadius))
|
.skill_level(Sceptre(PDamage))
|
||||||
.copied()
|
.unwrap_or(None)
|
||||||
.flatten()
|
.unwrap_or(0);
|
||||||
|
let range_level = skillset
|
||||||
|
.skill_level(Sceptre(PRadius))
|
||||||
|
.unwrap_or(None)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let heal = 1.2_f32.powi(heal_level.into());
|
let heal = 1.2_f32.powi(heal_level.into());
|
||||||
let power = 1.2_f32.powi(damage_level.into());
|
let power = 1.2_f32.powi(damage_level.into());
|
||||||
let range = 1.4_f32.powi(range_level.into());
|
let range = 1.4_f32.powi(range_level.into());
|
||||||
*projectile = projectile.modified_projectile(power, 1_f32, range, heal);
|
*projectile = projectile.modified_projectile(power, 1_f32, range, heal);
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Sceptre(PCost)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sceptre(PCost)) {
|
||||||
*energy_cost =
|
*energy_cost =
|
||||||
(*energy_cost as f32 * 0.8_f32.powi(level.into())) as u32;
|
(*energy_cost as f32 * 0.8_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Sceptre(PProjSpeed)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Sceptre(PProjSpeed)) {
|
||||||
*projectile_speed *= 1.25_f32.powi(level.into());
|
*projectile_speed *= 1.25_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1030,14 +1039,14 @@ impl CharacterAbility {
|
|||||||
..
|
..
|
||||||
} = self
|
} = self
|
||||||
{
|
{
|
||||||
*immune_melee = skills.contains_key(&Skill::Roll(ImmuneMelee));
|
*immune_melee = skillset.has_skill(Skill::Roll(ImmuneMelee));
|
||||||
if let Some(level) = skills.get(&Skill::Roll(Cost)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Skill::Roll(Cost)) {
|
||||||
*energy_cost = (*energy_cost as f32 * 0.8_f32.powi(level.into())) as u32;
|
*energy_cost = (*energy_cost as f32 * 0.8_f32.powi(level.into())) as u32;
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Skill::Roll(Strength)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Skill::Roll(Strength)) {
|
||||||
*roll_strength *= 1.2_f32.powi(level.into());
|
*roll_strength *= 1.2_f32.powi(level.into());
|
||||||
}
|
}
|
||||||
if let Some(level) = skills.get(&Skill::Roll(Duration)).copied().flatten() {
|
if let Ok(Some(level)) = skillset.skill_level(Skill::Roll(Duration)) {
|
||||||
*movement_duration =
|
*movement_duration =
|
||||||
(*movement_duration as f32 * 1.2_f32.powi(level.into())) as u64;
|
(*movement_duration as f32 * 1.2_f32.powi(level.into())) as u64;
|
||||||
}
|
}
|
||||||
|
@ -428,7 +428,7 @@ impl Body {
|
|||||||
/// Returns a multiplier representing increased difficulty not accounted for
|
/// Returns a multiplier representing increased difficulty not accounted for
|
||||||
/// due to AI or not using an actual weapon
|
/// due to AI or not using an actual weapon
|
||||||
// TODO: Match on species
|
// TODO: Match on species
|
||||||
pub fn combat_multiplier(&self) -> f32 { 1.0 }
|
pub fn combat_multiplier(&self) -> f32 { if let Body::Object(_) = self { 0.0 } else { 1.0 } }
|
||||||
|
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
pub fn base_exp(&self) -> u32 {
|
pub fn base_exp(&self) -> u32 {
|
||||||
|
@ -18,7 +18,7 @@ impl Asset for SkillTreeMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct SkillLevelMap(HashMap<Skill, Level>);
|
pub struct SkillLevelMap(HashMap<Skill, Option<u16>>);
|
||||||
|
|
||||||
impl Asset for SkillLevelMap {
|
impl Asset for SkillLevelMap {
|
||||||
type Loader = assets::RonLoader;
|
type Loader = assets::RonLoader;
|
||||||
@ -27,7 +27,7 @@ impl Asset for SkillLevelMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct SkillPrerequisitesMap(HashMap<Skill, HashMap<Skill, Level>>);
|
pub struct SkillPrerequisitesMap(HashMap<Skill, HashMap<Skill, Option<u16>>>);
|
||||||
|
|
||||||
impl Asset for SkillPrerequisitesMap {
|
impl Asset for SkillPrerequisitesMap {
|
||||||
type Loader = assets::RonLoader;
|
type Loader = assets::RonLoader;
|
||||||
@ -45,13 +45,13 @@ lazy_static! {
|
|||||||
).0
|
).0
|
||||||
};
|
};
|
||||||
// Loads the maximum level that a skill can obtain
|
// Loads the maximum level that a skill can obtain
|
||||||
pub static ref SKILL_MAX_LEVEL: HashMap<Skill, Level> = {
|
pub static ref SKILL_MAX_LEVEL: HashMap<Skill, Option<u16>> = {
|
||||||
SkillLevelMap::load_expect_cloned(
|
SkillLevelMap::load_expect_cloned(
|
||||||
"common.skill_trees.skill_max_levels",
|
"common.skill_trees.skill_max_levels",
|
||||||
).0
|
).0
|
||||||
};
|
};
|
||||||
// Loads the prerequisite skills for a particular skill
|
// Loads the prerequisite skills for a particular skill
|
||||||
pub static ref SKILL_PREREQUISITES: HashMap<Skill, HashMap<Skill, Level>> = {
|
pub static ref SKILL_PREREQUISITES: HashMap<Skill, HashMap<Skill, Option<u16>>> = {
|
||||||
SkillPrerequisitesMap::load_expect_cloned(
|
SkillPrerequisitesMap::load_expect_cloned(
|
||||||
"common.skill_trees.skill_prerequisites",
|
"common.skill_trees.skill_prerequisites",
|
||||||
).0
|
).0
|
||||||
@ -75,6 +75,10 @@ pub enum Skill {
|
|||||||
Roll(RollSkill),
|
Roll(RollSkill),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum SkillError {
|
||||||
|
MissingSkill,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum SwordSkill {
|
pub enum SwordSkill {
|
||||||
// Sword passives
|
// Sword passives
|
||||||
@ -225,34 +229,39 @@ impl SkillGroupType {
|
|||||||
#[allow(clippy::many_single_char_names)]
|
#[allow(clippy::many_single_char_names)]
|
||||||
pub fn skill_point_cost(self, level: u16) -> u16 {
|
pub fn skill_point_cost(self, level: u16) -> u16 {
|
||||||
let exp_increment = 10.0;
|
let exp_increment = 10.0;
|
||||||
let starting_exp = 150.0;
|
let starting_exp = 100.0;
|
||||||
let exp_ceiling = 1000.0;
|
let exp_ceiling = 1000.0;
|
||||||
let scaling_factor = 0.1;
|
let scaling_factor = 0.1;
|
||||||
let a = exp_increment;
|
(exp_increment
|
||||||
let b = (exp_ceiling - starting_exp) / ((1.0 + std::f32::consts::PI / 2.0) * exp_increment);
|
* (exp_ceiling
|
||||||
let c = scaling_factor;
|
/ exp_increment
|
||||||
let d = (-1.0_f32).tan();
|
/ (1.0
|
||||||
let e = starting_exp / exp_increment + b;
|
+ std::f32::consts::E.powf(-scaling_factor * level as f32)
|
||||||
(a * (b * (c * level as f32 + d).atan() + e).floor()) as u16
|
* (exp_ceiling / starting_exp - 1.0)))
|
||||||
|
.floor()) as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the total amount of skill points that can be spent in a particular
|
/// Gets the total amount of skill points that can be spent in a particular
|
||||||
/// skill group
|
/// skill group
|
||||||
pub fn get_max_skill_points(self) -> u16 {
|
pub fn max_skill_points(self) -> u16 {
|
||||||
let mut cost = 0;
|
|
||||||
if let Some(skill_list) = SKILL_GROUP_DEFS.get(&self) {
|
if let Some(skill_list) = SKILL_GROUP_DEFS.get(&self) {
|
||||||
for skill in skill_list {
|
skill_list
|
||||||
if let Some(max_level) = skill.get_max_level() {
|
.iter()
|
||||||
for level in 1..=max_level {
|
.map(|skill| {
|
||||||
cost += skill.skill_cost(Some(level));
|
if let Some(max_level) = skill.max_level() {
|
||||||
}
|
(1..=max_level)
|
||||||
|
.into_iter()
|
||||||
|
.map(|level| skill.skill_cost(Some(level)))
|
||||||
|
.sum()
|
||||||
} else {
|
} else {
|
||||||
cost += skill.skill_cost(None);
|
skill.skill_cost(None)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.sum()
|
||||||
|
} else {
|
||||||
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cost
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A group of skills that have been unlocked by a player. Each skill group has
|
/// A group of skills that have been unlocked by a player. Each skill group has
|
||||||
@ -283,13 +292,11 @@ impl SkillGroup {
|
|||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct SkillSet {
|
pub struct SkillSet {
|
||||||
pub skill_groups: Vec<SkillGroup>,
|
pub skill_groups: Vec<SkillGroup>,
|
||||||
pub skills: HashMap<Skill, Level>,
|
pub skills: HashMap<Skill, Option<u16>>,
|
||||||
pub modify_health: bool,
|
pub modify_health: bool,
|
||||||
pub modify_energy: bool,
|
pub modify_energy: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Level = Option<u16>;
|
|
||||||
|
|
||||||
impl Default for SkillSet {
|
impl Default for SkillSet {
|
||||||
/// Instantiate a new skill set with the default skill groups with no
|
/// Instantiate a new skill set with the default skill groups with no
|
||||||
/// unlocked skills in them - used when adding a skill set to a new
|
/// unlocked skills in them - used when adding a skill set to a new
|
||||||
@ -344,14 +351,10 @@ impl SkillSet {
|
|||||||
/// assert_eq!(skillset.skills.len(), 1);
|
/// assert_eq!(skillset.skills.len(), 1);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn unlock_skill(&mut self, skill: Skill) {
|
pub fn unlock_skill(&mut self, skill: Skill) {
|
||||||
if let Some(skill_group_type) = skill.get_skill_group_type() {
|
if let Some(skill_group_type) = skill.skill_group_type() {
|
||||||
let next_level = if self.skills.contains_key(&skill) {
|
let next_level = self.next_skill_level(skill);
|
||||||
self.skills.get(&skill).copied().flatten().map(|l| l + 1)
|
|
||||||
} else {
|
|
||||||
skill.get_max_level().map(|_| 1)
|
|
||||||
};
|
|
||||||
let prerequisites_met = self.prerequisites_met(skill);
|
let prerequisites_met = self.prerequisites_met(skill);
|
||||||
if !matches!(self.skills.get(&skill), Some(level) if *level == skill.get_max_level()) {
|
if !matches!(self.skills.get(&skill), Some(level) if *level == skill.max_level()) {
|
||||||
if let Some(mut skill_group) = self
|
if let Some(mut skill_group) = self
|
||||||
.skill_groups
|
.skill_groups
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
@ -406,21 +409,22 @@ impl SkillSet {
|
|||||||
/// assert_eq!(skillset.skills.len(), 0);
|
/// assert_eq!(skillset.skills.len(), 0);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn refund_skill(&mut self, skill: Skill) {
|
pub fn refund_skill(&mut self, skill: Skill) {
|
||||||
if self.skills.contains_key(&skill) {
|
if self.has_skill(skill) {
|
||||||
if let Some(skill_group_type) = skill.get_skill_group_type() {
|
if let Some(skill_group_type) = skill.skill_group_type() {
|
||||||
if let Some(mut skill_group) = self
|
if let Some(mut skill_group) = self
|
||||||
.skill_groups
|
.skill_groups
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|x| x.skill_group_type == skill_group_type)
|
.find(|x| x.skill_group_type == skill_group_type)
|
||||||
{
|
{
|
||||||
// We know key is already contained, so unwrap is safe
|
let level = self.skills.get(&skill).copied();
|
||||||
let level = *(self.skills.get(&skill).unwrap());
|
if let Some(level) = level {
|
||||||
skill_group.available_sp += skill.skill_cost(level);
|
skill_group.available_sp += skill.skill_cost(level);
|
||||||
if level.map_or(false, |l| l > 1) {
|
if level.map_or(false, |l| l > 1) {
|
||||||
self.skills.insert(skill, level.map(|l| l - 1));
|
self.skills.insert(skill, level.map(|l| l - 1));
|
||||||
} else {
|
} else {
|
||||||
self.skills.remove(&skill);
|
self.skills.remove(&skill);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
warn!("Tried to refund skill for a skill group that player does not have");
|
warn!("Tried to refund skill for a skill group that player does not have");
|
||||||
}
|
}
|
||||||
@ -468,9 +472,16 @@ impl SkillSet {
|
|||||||
|
|
||||||
/// Adds a skill point while subtracting the necessary amount of experience
|
/// Adds a skill point while subtracting the necessary amount of experience
|
||||||
pub fn earn_skill_point(&mut self, skill_group_type: SkillGroupType) {
|
pub fn earn_skill_point(&mut self, skill_group_type: SkillGroupType) {
|
||||||
let sp_cost = self.get_skill_point_cost(skill_group_type) as i32;
|
let sp_cost = self.skill_point_cost(skill_group_type);
|
||||||
self.change_experience(skill_group_type, -sp_cost);
|
if let Some(mut skill_group) = self
|
||||||
self.add_skill_points(skill_group_type, 1);
|
.skill_groups
|
||||||
|
.iter_mut()
|
||||||
|
.find(|x| x.skill_group_type == skill_group_type)
|
||||||
|
{
|
||||||
|
skill_group.exp = skill_group.exp.saturating_sub(sp_cost);
|
||||||
|
skill_group.available_sp = skill_group.available_sp.saturating_add(1);
|
||||||
|
skill_group.earned_sp = skill_group.earned_sp.saturating_add(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the skill set of an entity contains a particular skill group
|
/// Checks if the skill set of an entity contains a particular skill group
|
||||||
@ -498,62 +509,53 @@ impl SkillSet {
|
|||||||
/// Checks that the skill set contains all prerequisite skills for a
|
/// Checks that the skill set contains all prerequisite skills for a
|
||||||
/// particular skill
|
/// particular skill
|
||||||
pub fn prerequisites_met(&self, skill: Skill) -> bool {
|
pub fn prerequisites_met(&self, skill: Skill) -> bool {
|
||||||
let next_level = if self.skills.contains_key(&skill) {
|
let next_level = self.next_skill_level(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)| {
|
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)
|
self.skill_level(*s).map_or(false, |l_b| l_b >= *l)
|
||||||
|
/* self.has_skill(*s) && self.skills.get(s).map_or(false, |l_b|
|
||||||
|
* l_b >= l) */
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the available points for a particular skill group
|
/// Gets the available points for a particular skill group
|
||||||
pub fn get_available_sp(&self, skill_group: SkillGroupType) -> u16 {
|
pub fn available_sp(&self, skill_group: SkillGroupType) -> u16 {
|
||||||
let mut skill_groups = self
|
self.skill_groups
|
||||||
.skill_groups
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|s_g| s_g.skill_group_type == skill_group);
|
.find(|s_g| s_g.skill_group_type == skill_group)
|
||||||
skill_groups.next().map_or(0, |s_g| s_g.available_sp)
|
.map_or(0, |s_g| s_g.available_sp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the total earned points for a particular skill group
|
/// Gets the total earned points for a particular skill group
|
||||||
pub fn get_earned_sp(&self, skill_group: SkillGroupType) -> u16 {
|
pub fn earned_sp(&self, skill_group: SkillGroupType) -> u16 {
|
||||||
let mut skill_groups = self
|
self.skill_groups
|
||||||
.skill_groups
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|s_g| s_g.skill_group_type == skill_group);
|
.find(|s_g| s_g.skill_group_type == skill_group)
|
||||||
skill_groups.next().map_or(0, |s_g| s_g.earned_sp)
|
.map_or(0, |s_g| s_g.earned_sp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the available experience for a particular skill group
|
/// Gets the available experience for a particular skill group
|
||||||
pub fn get_experience(&self, skill_group: SkillGroupType) -> u16 {
|
pub fn experience(&self, skill_group: SkillGroupType) -> u16 {
|
||||||
let mut skill_groups = self
|
self.skill_groups
|
||||||
.skill_groups
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|s_g| s_g.skill_group_type == skill_group);
|
.find(|s_g| s_g.skill_group_type == skill_group)
|
||||||
skill_groups.next().map_or(0, |s_g| s_g.exp)
|
.map_or(0, |s_g| s_g.exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets skill point cost to purchase skill of next level
|
/// Gets skill point cost to purchase skill of next level
|
||||||
pub fn skill_point_cost(&self, skill: Skill) -> u16 {
|
pub fn skill_cost(&self, skill: Skill) -> u16 {
|
||||||
let next_level = if self.skills.contains_key(&skill) {
|
let next_level = self.next_skill_level(skill);
|
||||||
self.skills.get(&skill).copied().flatten().map(|l| l + 1)
|
|
||||||
} else {
|
|
||||||
skill.get_max_level().map(|_| 1)
|
|
||||||
};
|
|
||||||
skill.skill_cost(next_level)
|
skill.skill_cost(next_level)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if player has sufficient skill points to purchase a skill
|
/// Checks if player has sufficient skill points to purchase a skill
|
||||||
pub fn sufficient_skill_points(&self, skill: Skill) -> bool {
|
pub fn sufficient_skill_points(&self, skill: Skill) -> bool {
|
||||||
if let Some(skill_group_type) = skill.get_skill_group_type() {
|
if let Some(skill_group_type) = skill.skill_group_type() {
|
||||||
if let Some(skill_group) = self
|
if let Some(skill_group) = self
|
||||||
.skill_groups
|
.skill_groups
|
||||||
.iter()
|
.iter()
|
||||||
.find(|x| x.skill_group_type == skill_group_type)
|
.find(|x| x.skill_group_type == skill_group_type)
|
||||||
{
|
{
|
||||||
let needed_sp = self.skill_point_cost(skill);
|
let needed_sp = self.skill_cost(skill);
|
||||||
skill_group.available_sp >= needed_sp
|
skill_group.available_sp >= needed_sp
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -567,12 +569,12 @@ impl SkillSet {
|
|||||||
pub fn has_available_sp(&self) -> bool {
|
pub fn has_available_sp(&self) -> bool {
|
||||||
self.skill_groups.iter().any(|sg| {
|
self.skill_groups.iter().any(|sg| {
|
||||||
sg.available_sp > 0
|
sg.available_sp > 0
|
||||||
&& (sg.earned_sp - sg.available_sp) < sg.skill_group_type.get_max_skill_points()
|
&& (sg.earned_sp - sg.available_sp) < sg.skill_group_type.max_skill_points()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks how much experience is needed for the next skill point in a tree
|
/// Checks how much experience is needed for the next skill point in a tree
|
||||||
pub fn get_skill_point_cost(&self, skill_group: SkillGroupType) -> u16 {
|
pub fn skill_point_cost(&self, skill_group: SkillGroupType) -> u16 {
|
||||||
if let Some(level) = self
|
if let Some(level) = self
|
||||||
.skill_groups
|
.skill_groups
|
||||||
.iter()
|
.iter()
|
||||||
@ -584,12 +586,42 @@ impl SkillSet {
|
|||||||
skill_group.skill_point_cost(0)
|
skill_group.skill_point_cost(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the skill is at max level in a skill set
|
||||||
|
pub fn is_at_max_level(&self, skill: Skill) -> bool {
|
||||||
|
if let Ok(level) = self.skill_level(skill) {
|
||||||
|
level == skill.max_level()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if skill set contains a skill
|
||||||
|
pub fn has_skill(&self, skill: Skill) -> bool { self.skills.contains_key(&skill) }
|
||||||
|
|
||||||
|
/// Returns the level of the skill
|
||||||
|
pub fn skill_level(&self, skill: Skill) -> Result<Option<u16>, SkillError> {
|
||||||
|
if let Some(level) = self.skills.get(&skill).copied() {
|
||||||
|
Ok(level)
|
||||||
|
} else {
|
||||||
|
Err(SkillError::MissingSkill)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks the next level of a skill
|
||||||
|
fn next_skill_level(&self, skill: Skill) -> Option<u16> {
|
||||||
|
if let Ok(level) = self.skill_level(skill) {
|
||||||
|
level.map(|l| l + 1)
|
||||||
|
} else {
|
||||||
|
skill.max_level().map(|_| 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Skill {
|
impl Skill {
|
||||||
/// Returns a vec of prerequisite skills (it should only be necessary to
|
/// Returns a vec of prerequisite skills (it should only be necessary to
|
||||||
/// note direct prerequisites)
|
/// note direct prerequisites)
|
||||||
pub fn prerequisite_skills(self, level: Level) -> HashMap<Skill, Level> {
|
pub fn prerequisite_skills(self, level: Option<u16>) -> HashMap<Skill, Option<u16>> {
|
||||||
let mut prerequisites = HashMap::new();
|
let mut prerequisites = HashMap::new();
|
||||||
if let Some(level) = level {
|
if let Some(level) = level {
|
||||||
if level > 1 {
|
if level > 1 {
|
||||||
@ -604,25 +636,21 @@ impl Skill {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the cost in skill points of unlocking a particular skill
|
/// Returns the cost in skill points of unlocking a particular skill
|
||||||
pub fn skill_cost(self, level: Level) -> u16 {
|
pub fn skill_cost(self, level: Option<u16>) -> u16 {
|
||||||
// TODO: Better balance the costs later
|
// TODO: Better balance the costs later
|
||||||
level.unwrap_or(1)
|
level.unwrap_or(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the maximum level a skill can reach, returns None if the skill
|
/// Returns the maximum level a skill can reach, returns None if the skill
|
||||||
/// doesn't level
|
/// doesn't level
|
||||||
pub fn get_max_level(self) -> Option<u16> { SKILL_MAX_LEVEL.get(&self).copied().flatten() }
|
pub fn max_level(self) -> Option<u16> { SKILL_MAX_LEVEL.get(&self).copied().flatten() }
|
||||||
|
|
||||||
/// Returns the skill group type for a skill from the static skill group
|
/// Returns the skill group type for a skill from the static skill group
|
||||||
/// definitions.
|
/// definitions.
|
||||||
pub fn get_skill_group_type(self) -> Option<SkillGroupType> {
|
pub fn skill_group_type(self) -> Option<SkillGroupType> {
|
||||||
SKILL_GROUP_DEFS.iter().find_map(|(key, val)| {
|
SKILL_GROUP_DEFS
|
||||||
if val.contains(&self) {
|
.iter()
|
||||||
Some(*key)
|
.find_map(|(key, val)| val.contains(&self).then_some(*key))
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -639,12 +667,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(skillset.skill_groups[1].available_sp, 0);
|
assert_eq!(skillset.skill_groups[1].available_sp, 0);
|
||||||
assert_eq!(skillset.skills.len(), 1);
|
assert_eq!(skillset.skills.len(), 1);
|
||||||
assert_eq!(
|
assert_eq!(skillset.has_skill(Skill::Axe(AxeSkill::UnlockLeap)), true);
|
||||||
skillset
|
|
||||||
.skills
|
|
||||||
.contains_key(&Skill::Axe(AxeSkill::UnlockLeap)),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
skillset.refund_skill(Skill::Axe(AxeSkill::UnlockLeap));
|
skillset.refund_skill(Skill::Axe(AxeSkill::UnlockLeap));
|
||||||
|
|
||||||
@ -679,12 +702,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(skillset.skill_groups[1].available_sp, 0);
|
assert_eq!(skillset.skill_groups[1].available_sp, 0);
|
||||||
assert_eq!(skillset.skills.len(), 1);
|
assert_eq!(skillset.skills.len(), 1);
|
||||||
assert_eq!(
|
assert_eq!(skillset.has_skill(Skill::Axe(AxeSkill::UnlockLeap)), true);
|
||||||
skillset
|
|
||||||
.skills
|
|
||||||
.contains_key(&Skill::Axe(AxeSkill::UnlockLeap)),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
// Try unlocking a skill without enough skill points
|
// Try unlocking a skill without enough skill points
|
||||||
skillset.unlock_skill(Skill::Axe(AxeSkill::DsCombo));
|
skillset.unlock_skill(Skill::Axe(AxeSkill::DsCombo));
|
||||||
|
@ -34,6 +34,7 @@ pub enum Outcome {
|
|||||||
uid: Uid,
|
uid: Uid,
|
||||||
skill_tree: comp::skills::SkillGroupType,
|
skill_tree: comp::skills::SkillGroupType,
|
||||||
total_points: u16,
|
total_points: u16,
|
||||||
|
// TODO: Access ECS to get position from Uid to conserve bandwidth
|
||||||
pos: Vec3<f32>,
|
pos: Vec3<f32>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -595,11 +595,11 @@ impl SkillSetBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_skill(mut self, skill: Skill) -> Self {
|
pub fn with_skill(mut self, skill: Skill) -> Self {
|
||||||
if let Some(skill_group) = skill.get_skill_group_type() {
|
if let Some(skill_group) = skill.skill_group_type() {
|
||||||
self.0
|
self.0
|
||||||
.add_skill_points(skill_group, self.0.skill_point_cost(skill));
|
.add_skill_points(skill_group, self.0.skill_cost(skill));
|
||||||
self.0.unlock_skill(skill);
|
self.0.unlock_skill(skill);
|
||||||
if !self.0.skills.contains_key(&skill) {
|
if !self.0.has_skill(skill) {
|
||||||
warn!(
|
warn!(
|
||||||
"Failed to add skill: {:?}. Verify that it has the appropriate skill group \
|
"Failed to add skill: {:?}. Verify that it has the appropriate skill group \
|
||||||
available and meets all prerequisite skills.",
|
available and meets all prerequisite skills.",
|
||||||
|
@ -394,8 +394,7 @@ pub fn handle_ability1_input(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
ItemKind::Tool(tool) => Some(tool.kind),
|
ItemKind::Tool(tool) => Some(tool.kind),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
a.clone()
|
a.clone().adjusted_by_skills(&data.stats.skill_set, tool)
|
||||||
.adjusted_by_skills(&data.stats.skill_set.skills, tool)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.filter(|ability| ability.requirements_paid(data, update))
|
.filter(|ability| ability.requirements_paid(data, update))
|
||||||
@ -439,8 +438,7 @@ pub fn handle_ability2_input(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
ItemKind::Tool(tool) => Some(tool.kind),
|
ItemKind::Tool(tool) => Some(tool.kind),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
a.clone()
|
a.clone().adjusted_by_skills(&data.stats.skill_set, tool)
|
||||||
.adjusted_by_skills(&data.stats.skill_set.skills, tool)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.filter(|ability| ability.requirements_paid(data, update))
|
.filter(|ability| ability.requirements_paid(data, update))
|
||||||
@ -458,8 +456,7 @@ pub fn handle_ability2_input(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
ItemKind::Tool(tool) => Some(tool.kind),
|
ItemKind::Tool(tool) => Some(tool.kind),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
a.clone()
|
a.clone().adjusted_by_skills(&data.stats.skill_set, tool)
|
||||||
.adjusted_by_skills(&data.stats.skill_set.skills, tool)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.filter(|ability| ability.requirements_paid(data, update))
|
.filter(|ability| ability.requirements_paid(data, update))
|
||||||
@ -486,12 +483,12 @@ pub fn handle_ability3_input(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
.ability3
|
.ability3
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|s| match tool {
|
.and_then(|s| match tool {
|
||||||
|
// TODO: Make this so abilities aren't hardcoded to ability3
|
||||||
Some(ToolKind::Sword)
|
Some(ToolKind::Sword)
|
||||||
if !&data
|
if !&data
|
||||||
.stats
|
.stats
|
||||||
.skill_set
|
.skill_set
|
||||||
.skills
|
.has_skill(Skill::Sword(SwordSkill::UnlockSpin)) =>
|
||||||
.contains_key(&Skill::Sword(SwordSkill::UnlockSpin)) =>
|
|
||||||
{
|
{
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
@ -499,8 +496,7 @@ pub fn handle_ability3_input(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
if !&data
|
if !&data
|
||||||
.stats
|
.stats
|
||||||
.skill_set
|
.skill_set
|
||||||
.skills
|
.has_skill(Skill::Axe(AxeSkill::UnlockLeap)) =>
|
||||||
.contains_key(&Skill::Axe(AxeSkill::UnlockLeap)) =>
|
|
||||||
{
|
{
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
@ -508,8 +504,7 @@ pub fn handle_ability3_input(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
if !&data
|
if !&data
|
||||||
.stats
|
.stats
|
||||||
.skill_set
|
.skill_set
|
||||||
.skills
|
.has_skill(Skill::Hammer(HammerSkill::UnlockLeap)) =>
|
||||||
.contains_key(&Skill::Hammer(HammerSkill::UnlockLeap)) =>
|
|
||||||
{
|
{
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
@ -517,8 +512,7 @@ pub fn handle_ability3_input(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
if !&data
|
if !&data
|
||||||
.stats
|
.stats
|
||||||
.skill_set
|
.skill_set
|
||||||
.skills
|
.has_skill(Skill::Bow(BowSkill::UnlockRepeater)) =>
|
||||||
.contains_key(&Skill::Bow(BowSkill::UnlockRepeater)) =>
|
|
||||||
{
|
{
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
@ -526,17 +520,13 @@ pub fn handle_ability3_input(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
if !&data
|
if !&data
|
||||||
.stats
|
.stats
|
||||||
.skill_set
|
.skill_set
|
||||||
.skills
|
.has_skill(Skill::Staff(StaffSkill::UnlockShockwave)) =>
|
||||||
.contains_key(&Skill::Staff(StaffSkill::UnlockShockwave)) =>
|
|
||||||
{
|
{
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
_ => Some(s),
|
_ => Some(s),
|
||||||
})
|
})
|
||||||
.map(|a| {
|
.map(|a| a.clone().adjusted_by_skills(&data.stats.skill_set, tool))
|
||||||
a.clone()
|
|
||||||
.adjusted_by_skills(&data.stats.skill_set.skills, tool)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.filter(|ability| ability.requirements_paid(data, update))
|
.filter(|ability| ability.requirements_paid(data, update))
|
||||||
{
|
{
|
||||||
@ -553,10 +543,10 @@ pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
.inventory
|
.inventory
|
||||||
.equipped(EquipSlot::Mainhand)
|
.equipped(EquipSlot::Mainhand)
|
||||||
.and_then(|i| {
|
.and_then(|i| {
|
||||||
i.item_config_expect().dodge_ability.as_ref().map(|a| {
|
i.item_config_expect()
|
||||||
a.clone()
|
.dodge_ability
|
||||||
.adjusted_by_skills(&data.stats.skill_set.skills, None)
|
.as_ref()
|
||||||
})
|
.map(|a| a.clone().adjusted_by_skills(&data.stats.skill_set, None))
|
||||||
})
|
})
|
||||||
.filter(|ability| ability.requirements_paid(data, update))
|
.filter(|ability| ability.requirements_paid(data, update))
|
||||||
{
|
{
|
||||||
|
@ -583,7 +583,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
} else if *powerup > 4.0 && energy.current() > 10 {
|
} else if *powerup > 4.0 && energy.current() > 10 {
|
||||||
inputs.secondary.set_state(true);
|
inputs.secondary.set_state(true);
|
||||||
*powerup += dt.0;
|
*powerup += dt.0;
|
||||||
} else if stats.skill_set.skills.contains_key(&Skill::Axe(AxeSkill::UnlockLeap)) && energy.current() > 800 && thread_rng().gen_bool(0.5) {
|
} else if stats.skill_set.has_skill(Skill::Axe(AxeSkill::UnlockLeap)) && energy.current() > 800 && thread_rng().gen_bool(0.5) {
|
||||||
inputs.ability3.set_state(true);
|
inputs.ability3.set_state(true);
|
||||||
*powerup += dt.0;
|
*powerup += dt.0;
|
||||||
} else {
|
} else {
|
||||||
@ -632,7 +632,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
} else if *powerup > 2.0 {
|
} else if *powerup > 2.0 {
|
||||||
inputs.secondary.set_state(true);
|
inputs.secondary.set_state(true);
|
||||||
*powerup += dt.0;
|
*powerup += dt.0;
|
||||||
} else if stats.skill_set.skills.contains_key(&Skill::Hammer(HammerSkill::UnlockLeap)) && energy.current() > 700
|
} else if stats.skill_set.has_skill(Skill::Hammer(HammerSkill::UnlockLeap)) && energy.current() > 700
|
||||||
&& thread_rng().gen_bool(0.9) {
|
&& thread_rng().gen_bool(0.9) {
|
||||||
inputs.ability3.set_state(true);
|
inputs.ability3.set_state(true);
|
||||||
*powerup += dt.0;
|
*powerup += dt.0;
|
||||||
@ -662,7 +662,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
.try_normalized()
|
.try_normalized()
|
||||||
.unwrap_or(Vec2::zero())
|
.unwrap_or(Vec2::zero())
|
||||||
* speed;
|
* speed;
|
||||||
if stats.skill_set.skills.contains_key(&Skill::Hammer(HammerSkill::UnlockLeap)) && *powerup > 5.0 {
|
if stats.skill_set.has_skill(Skill::Hammer(HammerSkill::UnlockLeap)) && *powerup > 5.0 {
|
||||||
inputs.ability3.set_state(true);
|
inputs.ability3.set_state(true);
|
||||||
*powerup = 0.0;
|
*powerup = 0.0;
|
||||||
} else {
|
} else {
|
||||||
@ -690,7 +690,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
Tactic::Sword => {
|
Tactic::Sword => {
|
||||||
if dist_sqrd < (MIN_ATTACK_DIST * scale).powi(2) {
|
if dist_sqrd < (MIN_ATTACK_DIST * scale).powi(2) {
|
||||||
inputs.move_dir = Vec2::zero();
|
inputs.move_dir = Vec2::zero();
|
||||||
if stats.skill_set.skills.contains_key(&Skill::Sword(SwordSkill::UnlockSpin)) && *powerup < 2.0 && energy.current() > 600 {
|
if stats.skill_set.has_skill(Skill::Sword(SwordSkill::UnlockSpin)) && *powerup < 2.0 && energy.current() > 600 {
|
||||||
inputs.ability3.set_state(true);
|
inputs.ability3.set_state(true);
|
||||||
*powerup += dt.0;
|
*powerup += dt.0;
|
||||||
} else if *powerup > 2.0 {
|
} else if *powerup > 2.0 {
|
||||||
@ -782,7 +782,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
{
|
{
|
||||||
inputs.secondary.set_state(true);
|
inputs.secondary.set_state(true);
|
||||||
*powerup += dt.0;
|
*powerup += dt.0;
|
||||||
} else if stats.skill_set.skills.contains_key(&Skill::Bow(BowSkill::UnlockRepeater)) && energy.current() > 400
|
} else if stats.skill_set.has_skill(Skill::Bow(BowSkill::UnlockRepeater)) && energy.current() > 400
|
||||||
&& thread_rng().gen_bool(0.8)
|
&& thread_rng().gen_bool(0.8)
|
||||||
{
|
{
|
||||||
inputs.secondary.set_state(false);
|
inputs.secondary.set_state(false);
|
||||||
@ -835,7 +835,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
} else {
|
} else {
|
||||||
*powerup = 0.0;
|
*powerup = 0.0;
|
||||||
}
|
}
|
||||||
if stats.skill_set.skills.contains_key(&Skill::Staff(StaffSkill::UnlockShockwave)) && energy.current() > 800
|
if stats.skill_set.has_skill(Skill::Staff(StaffSkill::UnlockShockwave)) && energy.current() > 800
|
||||||
&& thread_rng().gen::<f32>() > 0.8
|
&& thread_rng().gen::<f32>() > 0.8
|
||||||
{
|
{
|
||||||
inputs.ability3.set_state(true);
|
inputs.ability3.set_state(true);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{
|
comp::{
|
||||||
skills::{GeneralSkill, Skill, SkillGroupType},
|
skills::{GeneralSkill, Skill},
|
||||||
CharacterState, Energy, EnergyChange, EnergySource, Health, Pos, Stats,
|
CharacterState, Energy, EnergyChange, EnergySource, Health, Pos, Stats,
|
||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
@ -85,28 +85,25 @@ impl<'a> System<'a> for Sys {
|
|||||||
health.is_dead = true;
|
health.is_dead = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut skills_to_level = HashSet::<SkillGroupType>::new();
|
|
||||||
let stat = stats.get_unchecked();
|
let stat = stats.get_unchecked();
|
||||||
{
|
let skills_to_level = stat
|
||||||
for skill_group in stat.skill_set.skill_groups.iter() {
|
|
||||||
if skill_group.exp
|
|
||||||
>= stat
|
|
||||||
.skill_set
|
.skill_set
|
||||||
.get_skill_point_cost(skill_group.skill_group_type)
|
.skill_groups
|
||||||
{
|
.iter()
|
||||||
skills_to_level.insert(skill_group.skill_group_type);
|
.filter_map(|s_g| {
|
||||||
}
|
(s_g.exp >= stat.skill_set.skill_point_cost(s_g.skill_group_type))
|
||||||
}
|
.then(|| s_g.skill_group_type)
|
||||||
}
|
})
|
||||||
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
if !skills_to_level.is_empty() {
|
if !skills_to_level.is_empty() {
|
||||||
let mut stat = stats.get_mut_unchecked();
|
let mut stat = stats.get_mut_unchecked();
|
||||||
for skill_group in skills_to_level.drain() {
|
for skill_group in skills_to_level {
|
||||||
stat.skill_set.earn_skill_point(skill_group);
|
stat.skill_set.earn_skill_point(skill_group);
|
||||||
outcomes.push(Outcome::SkillPointGain {
|
outcomes.push(Outcome::SkillPointGain {
|
||||||
uid: *uid,
|
uid: *uid,
|
||||||
skill_tree: skill_group,
|
skill_tree: skill_group,
|
||||||
total_points: stat.skill_set.get_earned_sp(skill_group),
|
total_points: stat.skill_set.earned_sp(skill_group),
|
||||||
pos: pos.0,
|
pos: pos.0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -126,10 +123,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
let mut health = health.get_mut_unchecked();
|
let mut health = health.get_mut_unchecked();
|
||||||
let health_level = stat
|
let health_level = stat
|
||||||
.skill_set
|
.skill_set
|
||||||
.skills
|
.skill_level(Skill::General(GeneralSkill::HealthIncrease))
|
||||||
.get(&Skill::General(GeneralSkill::HealthIncrease))
|
.unwrap_or(None)
|
||||||
.copied()
|
|
||||||
.flatten()
|
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
health.update_max_hp(Some(stat.body_type), health_level);
|
health.update_max_hp(Some(stat.body_type), health_level);
|
||||||
let mut stat = stats.get_mut_unchecked();
|
let mut stat = stats.get_mut_unchecked();
|
||||||
@ -140,10 +135,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
let mut energy = energy.get_mut_unchecked();
|
let mut energy = energy.get_mut_unchecked();
|
||||||
let energy_level = stat
|
let energy_level = stat
|
||||||
.skill_set
|
.skill_set
|
||||||
.skills
|
.skill_level(Skill::General(GeneralSkill::EnergyIncrease))
|
||||||
.get(&Skill::General(GeneralSkill::EnergyIncrease))
|
.unwrap_or(None)
|
||||||
.copied()
|
|
||||||
.flatten()
|
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
energy.update_max_energy(Some(stat.body_type), energy_level);
|
energy.update_max_energy(Some(stat.body_type), energy_level);
|
||||||
let mut stat = stats.get_mut_unchecked();
|
let mut stat = stats.get_mut_unchecked();
|
||||||
|
@ -49,7 +49,6 @@ diesel_migrations = "1.4.0"
|
|||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
slab = "0.4"
|
slab = "0.4"
|
||||||
const-tweaker = {version = "0.3.1", optional = true}
|
const-tweaker = {version = "0.3.1", optional = true}
|
||||||
inline_tweak = "1.0.2"
|
|
||||||
|
|
||||||
# Plugins
|
# Plugins
|
||||||
plugin-api = { package = "veloren-plugin-api", path = "../plugin/api"}
|
plugin-api = { package = "veloren-plugin-api", path = "../plugin/api"}
|
||||||
|
@ -29,7 +29,6 @@ use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
|
|||||||
use common_sys::state::BlockChange;
|
use common_sys::state::BlockChange;
|
||||||
use comp::item::Reagent;
|
use comp::item::Reagent;
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use inline_tweak::*;
|
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use specs::{join::Join, saveload::MarkerAllocator, Entity as EcsEntity, WorldExt};
|
use specs::{join::Join, saveload::MarkerAllocator, Entity as EcsEntity, WorldExt};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
@ -195,25 +194,24 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
|
|||||||
|
|
||||||
// Maximum distance for other group members to receive exp
|
// Maximum distance for other group members to receive exp
|
||||||
const MAX_EXP_DIST: f32 = 150.0;
|
const MAX_EXP_DIST: f32 = 150.0;
|
||||||
// Attacker gets same as exp of everyone else
|
|
||||||
const ATTACKER_EXP_WEIGHT: f32 = 1.0;
|
|
||||||
// TODO: Scale xp from skillset rather than health, when NPCs have their own
|
// TODO: Scale xp from skillset rather than health, when NPCs have their own
|
||||||
// skillsets
|
// skillsets
|
||||||
/*let mut exp_reward = entity_stats.body_type.base_exp() as f32
|
/*let mut exp_reward = entity_stats.body_type.base_exp() as f32
|
||||||
* (entity_health.maximum() as f32 / entity_stats.body_type.base_health() as
|
* (entity_health.maximum() as f32 / entity_stats.body_type.base_health() as
|
||||||
* f32); */
|
* f32); */
|
||||||
let mut exp_reward =
|
let mut exp_reward =
|
||||||
combat::combat_rating(entity_inventory, entity_health, &entity_stats.body_type)
|
combat::combat_rating(entity_inventory, entity_health, entity_stats) * 2.5;
|
||||||
* tweak!(2.5);
|
|
||||||
|
|
||||||
// Distribute EXP to group
|
// Distribute EXP to group
|
||||||
let positions = state.ecs().read_storage::<Pos>();
|
let positions = state.ecs().read_storage::<Pos>();
|
||||||
let alignments = state.ecs().read_storage::<Alignment>();
|
let alignments = state.ecs().read_storage::<Alignment>();
|
||||||
let uids = state.ecs().read_storage::<Uid>();
|
let uids = state.ecs().read_storage::<Uid>();
|
||||||
|
let mut outcomes = state.ecs().write_resource::<Vec<Outcome>>();
|
||||||
|
let inventories = state.ecs().read_storage::<comp::Inventory>();
|
||||||
if let (Some(attacker_group), Some(pos)) = (attacker_group, positions.get(entity)) {
|
if let (Some(attacker_group), Some(pos)) = (attacker_group, positions.get(entity)) {
|
||||||
// TODO: rework if change to groups makes it easier to iterate entities in a
|
// TODO: rework if change to groups makes it easier to iterate entities in a
|
||||||
// group
|
// group
|
||||||
let mut num_not_pets_in_range = 0;
|
let mut non_pet_group_members_in_range = 1;
|
||||||
let members_in_range = (
|
let members_in_range = (
|
||||||
&state.ecs().entities(),
|
&state.ecs().entities(),
|
||||||
&groups,
|
&groups,
|
||||||
@ -230,100 +228,36 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
|
|||||||
})
|
})
|
||||||
.map(|(entity, _, _, alignment, uid)| {
|
.map(|(entity, _, _, alignment, uid)| {
|
||||||
if !matches!(alignment, Some(Alignment::Owned(owner)) if owner != uid) {
|
if !matches!(alignment, Some(Alignment::Owned(owner)) if owner != uid) {
|
||||||
num_not_pets_in_range += 1;
|
non_pet_group_members_in_range += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
(entity, uid)
|
(entity, uid)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let exp = exp_reward / (num_not_pets_in_range as f32 + ATTACKER_EXP_WEIGHT);
|
// Devides exp reward by square root of number of people in group
|
||||||
exp_reward = exp * ATTACKER_EXP_WEIGHT;
|
exp_reward /= (non_pet_group_members_in_range as f32).sqrt();
|
||||||
members_in_range.into_iter().for_each(|(e, uid)| {
|
members_in_range.into_iter().for_each(|(e, uid)| {
|
||||||
let (main_tool_kind, second_tool_kind) =
|
if let (Some(inventory), Some(mut stats)) = (inventories.get(e), stats.get_mut(e)) {
|
||||||
if let Some(inventory) = state.ecs().read_storage::<comp::Inventory>().get(e) {
|
handle_exp_gain(exp_reward, inventory, &mut stats, uid, &mut outcomes);
|
||||||
combat::get_weapons(inventory)
|
|
||||||
} else {
|
|
||||||
(None, None)
|
|
||||||
};
|
|
||||||
if let Some(mut stats) = stats.get_mut(e) {
|
|
||||||
let mut xp_pools = HashSet::<SkillGroupType>::new();
|
|
||||||
xp_pools.insert(SkillGroupType::General);
|
|
||||||
if let Some(w) = main_tool_kind {
|
|
||||||
if stats
|
|
||||||
.skill_set
|
|
||||||
.contains_skill_group(SkillGroupType::Weapon(w))
|
|
||||||
{
|
|
||||||
xp_pools.insert(SkillGroupType::Weapon(w));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(w) = second_tool_kind {
|
|
||||||
if stats
|
|
||||||
.skill_set
|
|
||||||
.contains_skill_group(SkillGroupType::Weapon(w))
|
|
||||||
{
|
|
||||||
xp_pools.insert(SkillGroupType::Weapon(w));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let num_pools = xp_pools.len() as f32;
|
|
||||||
for pool in xp_pools.drain() {
|
|
||||||
stats
|
|
||||||
.skill_set
|
|
||||||
.change_experience(pool, (exp / num_pools).ceil() as i32);
|
|
||||||
}
|
|
||||||
state
|
|
||||||
.ecs()
|
|
||||||
.write_resource::<Vec<Outcome>>()
|
|
||||||
.push(Outcome::ExpChange {
|
|
||||||
uid: *uid,
|
|
||||||
exp: exp as i32,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let (main_tool_kind, second_tool_kind) =
|
if let (Some(mut attacker_stats), Some(attacker_uid), Some(attacker_inventory)) = (
|
||||||
if let Some(inventory) = state.ecs().read_storage::<comp::Inventory>().get(attacker) {
|
stats.get_mut(attacker),
|
||||||
combat::get_weapons(inventory)
|
uids.get(attacker),
|
||||||
} else {
|
inventories.get(attacker),
|
||||||
(None, None)
|
) {
|
||||||
};
|
|
||||||
if let (Some(mut attacker_stats), Some(attacker_uid)) =
|
|
||||||
(stats.get_mut(attacker), uids.get(attacker))
|
|
||||||
{
|
|
||||||
// TODO: Discuss whether we should give EXP by Player
|
// TODO: Discuss whether we should give EXP by Player
|
||||||
// Killing or not.
|
// Killing or not.
|
||||||
// attacker_stats.exp.change_by(exp_reward.ceil() as i64);
|
// attacker_stats.exp.change_by(exp_reward.ceil() as i64);
|
||||||
let mut xp_pools = HashSet::<SkillGroupType>::new();
|
handle_exp_gain(
|
||||||
xp_pools.insert(SkillGroupType::General);
|
exp_reward,
|
||||||
if let Some(w) = main_tool_kind {
|
attacker_inventory,
|
||||||
if attacker_stats
|
&mut attacker_stats,
|
||||||
.skill_set
|
attacker_uid,
|
||||||
.contains_skill_group(SkillGroupType::Weapon(w))
|
&mut outcomes,
|
||||||
{
|
);
|
||||||
xp_pools.insert(SkillGroupType::Weapon(w));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(w) = second_tool_kind {
|
|
||||||
if attacker_stats
|
|
||||||
.skill_set
|
|
||||||
.contains_skill_group(SkillGroupType::Weapon(w))
|
|
||||||
{
|
|
||||||
xp_pools.insert(SkillGroupType::Weapon(w));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let num_pools = xp_pools.len() as f32;
|
|
||||||
for pool in xp_pools.drain() {
|
|
||||||
attacker_stats
|
|
||||||
.skill_set
|
|
||||||
.change_experience(pool, (exp_reward / num_pools).ceil() as i32);
|
|
||||||
}
|
|
||||||
state
|
|
||||||
.ecs()
|
|
||||||
.write_resource::<Vec<Outcome>>()
|
|
||||||
.push(Outcome::ExpChange {
|
|
||||||
uid: *attacker_uid,
|
|
||||||
exp: exp_reward as i32,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -853,3 +787,41 @@ pub fn handle_energy_change(server: &Server, entity: EcsEntity, change: EnergyCh
|
|||||||
energy.change_by(change);
|
energy.change_by(change);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_exp_gain(
|
||||||
|
exp_reward: f32,
|
||||||
|
inventory: &Inventory,
|
||||||
|
stats: &mut Stats,
|
||||||
|
uid: &Uid,
|
||||||
|
outcomes: &mut Vec<Outcome>,
|
||||||
|
) {
|
||||||
|
let (main_tool_kind, second_tool_kind) = combat::get_weapons(inventory);
|
||||||
|
let mut xp_pools = HashSet::<SkillGroupType>::new();
|
||||||
|
xp_pools.insert(SkillGroupType::General);
|
||||||
|
if let Some(w) = main_tool_kind {
|
||||||
|
if stats
|
||||||
|
.skill_set
|
||||||
|
.contains_skill_group(SkillGroupType::Weapon(w))
|
||||||
|
{
|
||||||
|
xp_pools.insert(SkillGroupType::Weapon(w));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(w) = second_tool_kind {
|
||||||
|
if stats
|
||||||
|
.skill_set
|
||||||
|
.contains_skill_group(SkillGroupType::Weapon(w))
|
||||||
|
{
|
||||||
|
xp_pools.insert(SkillGroupType::Weapon(w));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let num_pools = xp_pools.len() as f32;
|
||||||
|
for pool in xp_pools {
|
||||||
|
stats
|
||||||
|
.skill_set
|
||||||
|
.change_experience(pool, (exp_reward / num_pools).ceil() as i32);
|
||||||
|
}
|
||||||
|
outcomes.push(Outcome::ExpChange {
|
||||||
|
uid: *uid,
|
||||||
|
exp: exp_reward as i32,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -379,7 +379,7 @@ fn convert_skill_groups_from_database(skill_groups: &[SkillGroup]) -> Vec<skills
|
|||||||
new_skill_groups
|
new_skill_groups
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_skills_from_database(skills: &[Skill]) -> HashMap<skills::Skill, skills::Level> {
|
fn convert_skills_from_database(skills: &[Skill]) -> HashMap<skills::Skill, Option<u16>> {
|
||||||
let mut new_skills = HashMap::new();
|
let mut new_skills = HashMap::new();
|
||||||
for skill in skills.iter() {
|
for skill in skills.iter() {
|
||||||
let new_skill = json_models::db_string_to_skill(&skill.skill_type);
|
let new_skill = json_models::db_string_to_skill(&skill.skill_type);
|
||||||
@ -392,7 +392,7 @@ pub fn convert_skill_groups_to_database(
|
|||||||
entity_id: CharacterId,
|
entity_id: CharacterId,
|
||||||
skill_groups: Vec<skills::SkillGroup>,
|
skill_groups: Vec<skills::SkillGroup>,
|
||||||
) -> Vec<SkillGroup> {
|
) -> Vec<SkillGroup> {
|
||||||
let db_skill_groups: Vec<_> = skill_groups
|
skill_groups
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|sg| SkillGroup {
|
.map(|sg| SkillGroup {
|
||||||
entity_id,
|
entity_id,
|
||||||
@ -401,21 +401,19 @@ pub fn convert_skill_groups_to_database(
|
|||||||
available_sp: sg.available_sp as i32,
|
available_sp: sg.available_sp as i32,
|
||||||
earned_sp: sg.earned_sp as i32,
|
earned_sp: sg.earned_sp as i32,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect()
|
||||||
db_skill_groups
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_skills_to_database(
|
pub fn convert_skills_to_database(
|
||||||
entity_id: CharacterId,
|
entity_id: CharacterId,
|
||||||
skills: HashMap<skills::Skill, skills::Level>,
|
skills: HashMap<skills::Skill, Option<u16>>,
|
||||||
) -> Vec<Skill> {
|
) -> Vec<Skill> {
|
||||||
let db_skills: Vec<_> = skills
|
skills
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(s, l)| Skill {
|
.map(|(s, l)| Skill {
|
||||||
entity_id,
|
entity_id,
|
||||||
skill_type: json_models::skill_to_db_string(*s),
|
skill_type: json_models::skill_to_db_string(*s),
|
||||||
level: l.map(|l| l as i32),
|
level: l.map(|l| l as i32),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect()
|
||||||
db_skills
|
|
||||||
}
|
}
|
||||||
|
@ -139,10 +139,17 @@ impl StateExt for State {
|
|||||||
})
|
})
|
||||||
.with(comp::Controller::default())
|
.with(comp::Controller::default())
|
||||||
.with(body)
|
.with(body)
|
||||||
|
.with(comp::Energy::new(
|
||||||
|
body,
|
||||||
|
stats
|
||||||
|
.skill_set
|
||||||
|
.skill_level(Skill::General(GeneralSkill::EnergyIncrease))
|
||||||
|
.unwrap_or(None)
|
||||||
|
.unwrap_or(0),
|
||||||
|
))
|
||||||
.with(stats)
|
.with(stats)
|
||||||
.with(health)
|
.with(health)
|
||||||
.with(comp::Alignment::Npc)
|
.with(comp::Alignment::Npc)
|
||||||
.with(comp::Energy::new(body, 0))
|
|
||||||
.with(comp::Gravity(1.0))
|
.with(comp::Gravity(1.0))
|
||||||
.with(comp::CharacterState::default())
|
.with(comp::CharacterState::default())
|
||||||
.with(inventory)
|
.with(inventory)
|
||||||
@ -274,17 +281,13 @@ impl StateExt for State {
|
|||||||
let (health_level, energy_level) = (
|
let (health_level, energy_level) = (
|
||||||
stats
|
stats
|
||||||
.skill_set
|
.skill_set
|
||||||
.skills
|
.skill_level(Skill::General(GeneralSkill::HealthIncrease))
|
||||||
.get(&Skill::General(GeneralSkill::HealthIncrease))
|
.unwrap_or(None)
|
||||||
.copied()
|
|
||||||
.flatten()
|
|
||||||
.unwrap_or(0),
|
.unwrap_or(0),
|
||||||
stats
|
stats
|
||||||
.skill_set
|
.skill_set
|
||||||
.skills
|
.skill_level(Skill::General(GeneralSkill::EnergyIncrease))
|
||||||
.get(&Skill::General(GeneralSkill::EnergyIncrease))
|
.unwrap_or(None)
|
||||||
.copied()
|
|
||||||
.flatten()
|
|
||||||
.unwrap_or(0),
|
.unwrap_or(0),
|
||||||
);
|
);
|
||||||
self.write_component(entity, comp::Health::new(stats.body_type, health_level));
|
self.write_component(entity, comp::Health::new(stats.body_type, health_level));
|
||||||
|
@ -125,8 +125,6 @@ impl<'a> System<'a> for Sys {
|
|||||||
let alignment = entity.alignment;
|
let alignment = entity.alignment;
|
||||||
let main_tool = entity.main_tool;
|
let main_tool = entity.main_tool;
|
||||||
let mut stats = comp::Stats::new(name, body);
|
let mut stats = comp::Stats::new(name, body);
|
||||||
// let damage = stats.level.level() as i32; TODO: Make NPC base damage
|
|
||||||
// non-linearly depend on their level
|
|
||||||
|
|
||||||
let mut scale = entity.scale;
|
let mut scale = entity.scale;
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use find_folder::Search;
|
use find_folder::Search;
|
||||||
|
use hashbrown::HashSet;
|
||||||
use std::{env, path::PathBuf};
|
use std::{env, path::PathBuf};
|
||||||
use tracing::{debug, error, info};
|
use tracing::{debug, error, info};
|
||||||
|
|
||||||
@ -147,7 +148,7 @@ pub fn init() {
|
|||||||
// "Debounces" events since I can't find the option to do this in the latest
|
// "Debounces" events since I can't find the option to do this in the latest
|
||||||
// `notify`
|
// `notify`
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut modified_paths = std::collections::HashSet::new();
|
let mut modified_paths = HashSet::new();
|
||||||
|
|
||||||
while let Ok(path) = reload_recv.recv() {
|
while let Ok(path) = reload_recv.recv() {
|
||||||
modified_paths.insert(path);
|
modified_paths.insert(path);
|
||||||
|
@ -358,7 +358,7 @@ impl SfxMgr {
|
|||||||
audio.play_sfx(file_ref, *pos, None);
|
audio.play_sfx(file_ref, *pos, None);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
Outcome::ExpChange { .. } => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@ use conrod_core::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::hud::slots::SlotKind;
|
use crate::hud::slots::SlotKind;
|
||||||
use inline_tweak::*;
|
|
||||||
use vek::Vec2;
|
use vek::Vec2;
|
||||||
|
|
||||||
widget_ids! {
|
widget_ids! {
|
||||||
@ -430,16 +429,15 @@ impl<'a> Widget for Bag<'a> {
|
|||||||
.resize(STATS.len(), &mut ui.widget_id_generator())
|
.resize(STATS.len(), &mut ui.widget_id_generator())
|
||||||
});
|
});
|
||||||
// Thresholds (lower)
|
// Thresholds (lower)
|
||||||
let common = tweak!(4.3);
|
let common = 4.3;
|
||||||
let moderate = tweak!(6.0);
|
let moderate = 6.0;
|
||||||
let high = tweak!(8.0);
|
let high = 8.0;
|
||||||
let epic = tweak!(10.0);
|
let epic = 10.0;
|
||||||
let legendary = tweak!(79.0);
|
let legendary = 79.0;
|
||||||
let artifact = tweak!(122.0);
|
let artifact = 122.0;
|
||||||
let debug = tweak!(200.0);
|
let debug = 200.0;
|
||||||
// Stats
|
// Stats
|
||||||
let combat_rating =
|
let combat_rating = combat_rating(inventory, self.health, self.stats).min(999.9);
|
||||||
combat_rating(inventory, self.health, &self.stats.body_type).min(999.9);
|
|
||||||
let indicator_col = match combat_rating {
|
let indicator_col = match combat_rating {
|
||||||
x if (0.0..common).contains(&x) => QUALITY_LOW,
|
x if (0.0..common).contains(&x) => QUALITY_LOW,
|
||||||
x if (common..moderate).contains(&x) => QUALITY_COMMON,
|
x if (common..moderate).contains(&x) => QUALITY_COMMON,
|
||||||
@ -474,9 +472,9 @@ impl<'a> Widget for Bag<'a> {
|
|||||||
let combat_rating_txt = format!("{}", (combat_rating * 10.0) as usize);
|
let combat_rating_txt = format!("{}", (combat_rating * 10.0) as usize);
|
||||||
|
|
||||||
let btn = if i.0 == 0 {
|
let btn = if i.0 == 0 {
|
||||||
btn.top_left_with_margins_on(state.ids.bg_frame, tweak!(55.0), tweak!(10.0))
|
btn.top_left_with_margins_on(state.ids.bg_frame, 55.0, 10.0)
|
||||||
} else {
|
} else {
|
||||||
btn.down_from(state.ids.stat_icons[i.0 - 1], tweak!(7.0))
|
btn.down_from(state.ids.stat_icons[i.0 - 1], 7.0)
|
||||||
};
|
};
|
||||||
// TODO: Translation
|
// TODO: Translation
|
||||||
let tooltip_head = match i.1 {
|
let tooltip_head = match i.1 {
|
||||||
@ -507,9 +505,9 @@ impl<'a> Widget for Bag<'a> {
|
|||||||
"Protection" => &protection_txt,
|
"Protection" => &protection_txt,
|
||||||
_ => "",
|
_ => "",
|
||||||
})
|
})
|
||||||
.right_from(state.ids.stat_icons[i.0], tweak!(10.0))
|
.right_from(state.ids.stat_icons[i.0], 10.0)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.font_size(self.fonts.cyri.scale(tweak!(14)))
|
.font_size(self.fonts.cyri.scale(14))
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.graphics_for(state.ids.stat_icons[i.0])
|
.graphics_for(state.ids.stat_icons[i.0])
|
||||||
.set(state.ids.stat_txts[i.0], ui);
|
.set(state.ids.stat_txts[i.0], ui);
|
||||||
|
@ -14,7 +14,6 @@ use conrod_core::{
|
|||||||
widget::{self, Button, Image, Text},
|
widget::{self, Button, Image, Text},
|
||||||
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||||
};
|
};
|
||||||
use inline_tweak::*;
|
|
||||||
widget_ids! {
|
widget_ids! {
|
||||||
struct Ids {
|
struct Ids {
|
||||||
bag,
|
bag,
|
||||||
@ -128,8 +127,7 @@ impl<'a> Widget for Buttons<'a> {
|
|||||||
None => return None,
|
None => return None,
|
||||||
};
|
};
|
||||||
let localized_strings = self.localized_strings;
|
let localized_strings = self.localized_strings;
|
||||||
let arrow_ani =
|
let arrow_ani = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.8; //Animation timer
|
||||||
(self.pulse * tweak!(4.0)/* speed factor */).cos() * tweak!(0.5) + tweak!(0.8); //Animation timer
|
|
||||||
|
|
||||||
let button_tooltip = Tooltip::new({
|
let button_tooltip = Tooltip::new({
|
||||||
// Edge images [t, b, r, l]
|
// Edge images [t, b, r, l]
|
||||||
@ -382,24 +380,21 @@ impl<'a> Widget for Buttons<'a> {
|
|||||||
Image::new(self.imgs.sp_indicator_arrow)
|
Image::new(self.imgs.sp_indicator_arrow)
|
||||||
.w_h(20.0, 11.0)
|
.w_h(20.0, 11.0)
|
||||||
.graphics_for(state.ids.spellbook_button)
|
.graphics_for(state.ids.spellbook_button)
|
||||||
.mid_top_with_margin_on(
|
.mid_top_with_margin_on(state.ids.spellbook_button, -12.0 + arrow_ani as f64)
|
||||||
state.ids.spellbook_button,
|
|
||||||
tweak!(-12.0) + arrow_ani as f64,
|
|
||||||
)
|
|
||||||
.color(Some(QUALITY_LEGENDARY))
|
.color(Some(QUALITY_LEGENDARY))
|
||||||
.set(state.ids.sp_arrow, ui);
|
.set(state.ids.sp_arrow, ui);
|
||||||
Text::new(&localized_strings.get("hud.sp_arrow_txt"))
|
Text::new(&localized_strings.get("hud.sp_arrow_txt"))
|
||||||
.mid_top_with_margin_on(state.ids.sp_arrow, tweak!(-18.0))
|
.mid_top_with_margin_on(state.ids.sp_arrow, -18.0)
|
||||||
.graphics_for(state.ids.spellbook_button)
|
.graphics_for(state.ids.spellbook_button)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.font_size(self.fonts.cyri.scale(tweak!(14)))
|
.font_size(self.fonts.cyri.scale(14))
|
||||||
.color(BLACK)
|
.color(BLACK)
|
||||||
.set(state.ids.sp_arrow_txt_bg, ui);
|
.set(state.ids.sp_arrow_txt_bg, ui);
|
||||||
Text::new(&localized_strings.get("hud.sp_arrow_txt"))
|
Text::new(&localized_strings.get("hud.sp_arrow_txt"))
|
||||||
.graphics_for(state.ids.spellbook_button)
|
.graphics_for(state.ids.spellbook_button)
|
||||||
.bottom_right_with_margins_on(state.ids.sp_arrow_txt_bg, 1.0, 1.0)
|
.bottom_right_with_margins_on(state.ids.sp_arrow_txt_bg, 1.0, 1.0)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.font_size(self.fonts.cyri.scale(tweak!(14)))
|
.font_size(self.fonts.cyri.scale(14))
|
||||||
.color(QUALITY_LEGENDARY)
|
.color(QUALITY_LEGENDARY)
|
||||||
.set(state.ids.sp_arrow_txt, ui);
|
.set(state.ids.sp_arrow_txt, ui);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ use common::comp::{
|
|||||||
skills::{self, Skill},
|
skills::{self, Skill},
|
||||||
Stats,
|
Stats,
|
||||||
};
|
};
|
||||||
use inline_tweak::*;
|
|
||||||
|
|
||||||
widget_ids! {
|
widget_ids! {
|
||||||
pub struct Ids {
|
pub struct Ids {
|
||||||
@ -351,24 +350,23 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let img = if i.0 == 0 {
|
let img = if i.0 == 0 {
|
||||||
img.top_left_with_margins_on(state.content_align, tweak!(10.0), tweak!(5.0))
|
img.top_left_with_margins_on(state.content_align, 10.0, 5.0)
|
||||||
} else {
|
} else {
|
||||||
img.down_from(state.weapon_btns[i.0 - 1], tweak!(5.0))
|
img.down_from(state.weapon_btns[i.0 - 1], 5.0)
|
||||||
};
|
};
|
||||||
let tooltip_txt = if !locked {
|
let tooltip_txt = if !locked {
|
||||||
""
|
""
|
||||||
} else {
|
} else {
|
||||||
&self.localized_strings.get("hud.skill.not_unlocked")
|
&self.localized_strings.get("hud.skill.not_unlocked")
|
||||||
};
|
};
|
||||||
img.w_h(tweak!(50.0), tweak!(50.0))
|
img.w_h(50.0, 50.0).set(state.weapon_imgs[i.0], ui);
|
||||||
.set(state.weapon_imgs[i.0], ui);
|
|
||||||
// Lock Image
|
// Lock Image
|
||||||
if locked {
|
if locked {
|
||||||
Image::new(self.imgs.lock)
|
Image::new(self.imgs.lock)
|
||||||
.w_h(50.0, 50.0)
|
.w_h(50.0, 50.0)
|
||||||
.middle_of(state.weapon_imgs[i.0])
|
.middle_of(state.weapon_imgs[i.0])
|
||||||
.graphics_for(state.weapon_imgs[i.0])
|
.graphics_for(state.weapon_imgs[i.0])
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(0.8))))
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.8)))
|
||||||
.set(state.lock_imgs[i.0], ui);
|
.set(state.lock_imgs[i.0], ui);
|
||||||
}
|
}
|
||||||
// Weapon icons
|
// Weapon icons
|
||||||
@ -376,12 +374,12 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
.map(|st| {
|
.map(|st| {
|
||||||
(
|
(
|
||||||
st,
|
st,
|
||||||
self.stats.skill_set.get_available_sp(st),
|
self.stats.skill_set.available_sp(st),
|
||||||
self.stats.skill_set.get_earned_sp(st),
|
self.stats.skill_set.earned_sp(st),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map_or(false, |(st, a_pts, e_pts)| {
|
.map_or(false, |(st, a_pts, e_pts)| {
|
||||||
a_pts > 0 && (e_pts - a_pts) < st.get_max_skill_points()
|
a_pts > 0 && (e_pts - a_pts) < st.max_skill_points()
|
||||||
});
|
});
|
||||||
if Button::image(
|
if Button::image(
|
||||||
if skill_tree_from_str(i.1).map_or(false, |st| st == *sel_tab || available_pts) {
|
if skill_tree_from_str(i.1).map_or(false, |st| st == *sel_tab || available_pts) {
|
||||||
@ -390,7 +388,7 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
self.imgs.wpn_icon_border
|
self.imgs.wpn_icon_border
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.w_h(tweak!(50.0), tweak!(50.0))
|
.w_h(50.0, 50.0)
|
||||||
.hover_image(match skill_tree_from_str(i.1).map(|st| st == *sel_tab) {
|
.hover_image(match skill_tree_from_str(i.1).map(|st| st == *sel_tab) {
|
||||||
Some(true) => self.imgs.wpn_icon_border_pressed,
|
Some(true) => self.imgs.wpn_icon_border_pressed,
|
||||||
Some(false) => self.imgs.wpn_icon_border_mo,
|
Some(false) => self.imgs.wpn_icon_border_mo,
|
||||||
@ -423,17 +421,17 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Exp Bars and Rank Display
|
// Exp Bars and Rank Display
|
||||||
let current_exp = self.stats.skill_set.get_experience(*sel_tab) as f64;
|
let current_exp = self.stats.skill_set.experience(*sel_tab) as f64;
|
||||||
let max_exp = self.stats.skill_set.get_skill_point_cost(*sel_tab) as f64;
|
let max_exp = self.stats.skill_set.skill_point_cost(*sel_tab) as f64;
|
||||||
let exp_percentage = current_exp / max_exp;
|
let exp_percentage = current_exp / max_exp;
|
||||||
let rank = self.stats.skill_set.get_earned_sp(*sel_tab);
|
let rank = self.stats.skill_set.earned_sp(*sel_tab);
|
||||||
let rank_txt = format!("{}", rank);
|
let rank_txt = format!("{}", rank);
|
||||||
let exp_txt = format!("{}/{}", current_exp, max_exp);
|
let exp_txt = format!("{}/{}", current_exp, max_exp);
|
||||||
let available_pts = self.stats.skill_set.get_available_sp(*sel_tab);
|
let available_pts = self.stats.skill_set.available_sp(*sel_tab);
|
||||||
let available_pts_txt = format!("{}", available_pts);
|
let available_pts_txt = format!("{}", available_pts);
|
||||||
Image::new(self.imgs.diary_exp_bg)
|
Image::new(self.imgs.diary_exp_bg)
|
||||||
.w_h(480.0, 76.0)
|
.w_h(480.0, 76.0)
|
||||||
.mid_bottom_with_margin_on(state.content_align, tweak!(10.0))
|
.mid_bottom_with_margin_on(state.content_align, 10.0)
|
||||||
.set(state.exp_bar_bg, ui);
|
.set(state.exp_bar_bg, ui);
|
||||||
Rectangle::fill_with([400.0, 40.0], color::TRANSPARENT)
|
Rectangle::fill_with([400.0, 40.0], color::TRANSPARENT)
|
||||||
.top_left_with_margins_on(state.exp_bar_bg, 32.0, 40.0)
|
.top_left_with_margins_on(state.exp_bar_bg, 32.0, 40.0)
|
||||||
@ -455,32 +453,34 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
.map_or(false, |m| m.is_over());
|
.map_or(false, |m| m.is_over());
|
||||||
if self.hovering_exp_bar {
|
if self.hovering_exp_bar {
|
||||||
Text::new(&exp_txt)
|
Text::new(&exp_txt)
|
||||||
.mid_top_with_margin_on(state.exp_bar_frame, tweak!(47.0))
|
.mid_top_with_margin_on(state.exp_bar_frame, 47.0)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.font_size(self.fonts.cyri.scale(tweak!(14)))
|
.font_size(self.fonts.cyri.scale(14))
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.graphics_for(state.exp_bar_frame)
|
.graphics_for(state.exp_bar_frame)
|
||||||
.set(state.exp_bar_txt, ui);
|
.set(state.exp_bar_txt, ui);
|
||||||
}
|
}
|
||||||
Text::new(&rank_txt)
|
Text::new(&rank_txt)
|
||||||
.mid_top_with_margin_on(state.exp_bar_frame, tweak!(5.0))
|
.mid_top_with_margin_on(state.exp_bar_frame, 5.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(28))
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(state.exp_bar_rank, ui);
|
.set(state.exp_bar_rank, ui);
|
||||||
if available_pts > 0 {
|
|
||||||
Text::new(
|
Text::new(
|
||||||
&self
|
&self
|
||||||
.localized_strings
|
.localized_strings
|
||||||
.get("hud.skill.sp_available")
|
.get("hud.skill.sp_available")
|
||||||
.replace("{number}", &available_pts_txt),
|
.replace("{number}", &available_pts_txt),
|
||||||
)
|
)
|
||||||
.mid_top_with_margin_on(state.content_align, tweak!(700.0))
|
.mid_top_with_margin_on(state.content_align, 700.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(28))
|
||||||
.color(Color::Rgba(0.92, 0.76, 0.0, frame_ani))
|
.color(if available_pts > 0 {
|
||||||
|
Color::Rgba(0.92, 0.76, 0.0, frame_ani)
|
||||||
|
} else {
|
||||||
|
TEXT_COLOR
|
||||||
|
})
|
||||||
.set(state.available_pts_txt, ui);
|
.set(state.available_pts_txt, ui);
|
||||||
}
|
|
||||||
let tree_title = match sel_tab {
|
let tree_title = match sel_tab {
|
||||||
SelectedSkillTree::General => "General Combat",
|
SelectedSkillTree::General => "General Combat",
|
||||||
SelectedSkillTree::Weapon(ToolKind::Sword) => "Sword",
|
SelectedSkillTree::Weapon(ToolKind::Sword) => "Sword",
|
||||||
@ -492,15 +492,15 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
_ => "Unknown",
|
_ => "Unknown",
|
||||||
};
|
};
|
||||||
Text::new(&tree_title)
|
Text::new(&tree_title)
|
||||||
.mid_top_with_margin_on(state.content_align, tweak!(2.0))
|
.mid_top_with_margin_on(state.content_align, 2.0)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.font_size(self.fonts.cyri.scale(tweak!(34)))
|
.font_size(self.fonts.cyri.scale(34))
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(state.tree_title_txt, ui);
|
.set(state.tree_title_txt, ui);
|
||||||
// Skill Trees
|
// Skill Trees
|
||||||
// Alignment Placing
|
// Alignment Placing
|
||||||
let x = tweak!(200.0);
|
let x = 200.0;
|
||||||
let y = tweak!(100.0);
|
let y = 100.0;
|
||||||
// Alignment rectangles for skills
|
// Alignment rectangles for skills
|
||||||
Rectangle::fill_with([124.0 * 2.0, 124.0 * 2.0], color::TRANSPARENT)
|
Rectangle::fill_with([124.0 * 2.0, 124.0 * 2.0], color::TRANSPARENT)
|
||||||
.top_left_with_margins_on(state.content_align, y, x)
|
.top_left_with_margins_on(state.content_align, y, x)
|
||||||
@ -649,7 +649,7 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
}
|
}
|
||||||
// Skill-Icons and Functionality
|
// Skill-Icons and Functionality
|
||||||
// Art dimensions
|
// Art dimensions
|
||||||
let art_size = [tweak!(320.0), tweak!(320.0)];
|
let art_size = [320.0, 320.0];
|
||||||
match sel_tab {
|
match sel_tab {
|
||||||
SelectedSkillTree::General => {
|
SelectedSkillTree::General => {
|
||||||
use skills::{GeneralSkill::*, RollSkill::*, SkillGroupType::*};
|
use skills::{GeneralSkill::*, RollSkill::*, SkillGroupType::*};
|
||||||
@ -661,7 +661,7 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
)
|
)
|
||||||
.wh(art_size)
|
.wh(art_size)
|
||||||
.middle_of(state.content_align)
|
.middle_of(state.content_align)
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0))))
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
||||||
.set(state.general_combat_render_0, ui);
|
.set(state.general_combat_render_0, ui);
|
||||||
Image::new(
|
Image::new(
|
||||||
self.item_imgs
|
self.item_imgs
|
||||||
@ -669,7 +669,7 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
)
|
)
|
||||||
.wh(art_size)
|
.wh(art_size)
|
||||||
.middle_of(state.general_combat_render_0)
|
.middle_of(state.general_combat_render_0)
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0))))
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
||||||
.set(state.general_combat_render_1, ui);
|
.set(state.general_combat_render_1, ui);
|
||||||
// Top Left skills
|
// Top Left skills
|
||||||
// 5 1 6
|
// 5 1 6
|
||||||
@ -999,7 +999,7 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
)
|
)
|
||||||
.wh(art_size)
|
.wh(art_size)
|
||||||
.middle_of(state.content_align)
|
.middle_of(state.content_align)
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0))))
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
||||||
.set(state.sword_render, ui);
|
.set(state.sword_render, ui);
|
||||||
// Top Left skills
|
// Top Left skills
|
||||||
// 5 1 6
|
// 5 1 6
|
||||||
@ -1442,7 +1442,7 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
)
|
)
|
||||||
.wh(art_size)
|
.wh(art_size)
|
||||||
.middle_of(state.content_align)
|
.middle_of(state.content_align)
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0))))
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
||||||
.set(state.axe_render, ui);
|
.set(state.axe_render, ui);
|
||||||
// Top Left skills
|
// Top Left skills
|
||||||
// 5 1 6
|
// 5 1 6
|
||||||
@ -1856,7 +1856,7 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
)
|
)
|
||||||
.wh(art_size)
|
.wh(art_size)
|
||||||
.middle_of(state.content_align)
|
.middle_of(state.content_align)
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0))))
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
||||||
.set(state.hammer_render, ui);
|
.set(state.hammer_render, ui);
|
||||||
// Top Left skills
|
// Top Left skills
|
||||||
// 5 1 6
|
// 5 1 6
|
||||||
@ -2692,7 +2692,7 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
)
|
)
|
||||||
.wh(art_size)
|
.wh(art_size)
|
||||||
.middle_of(state.content_align)
|
.middle_of(state.content_align)
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0))))
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
||||||
.set(state.staff_render, ui);
|
.set(state.staff_render, ui);
|
||||||
// Top Left skills
|
// Top Left skills
|
||||||
// 5 1 6
|
// 5 1 6
|
||||||
@ -2878,9 +2878,11 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
self.tooltip_manager,
|
self.tooltip_manager,
|
||||||
&self
|
&self
|
||||||
.localized_strings
|
.localized_strings
|
||||||
.get("hud.skill.st_flame_velocity_title"),
|
.get("hud.skill.st_flamethrower_range_title"),
|
||||||
&add_sp_cost_tooltip(
|
&add_sp_cost_tooltip(
|
||||||
&self.localized_strings.get("hud.skill.st_flame_velocity"),
|
&self
|
||||||
|
.localized_strings
|
||||||
|
.get("hud.skill.st_flamethrower_range"),
|
||||||
skill,
|
skill,
|
||||||
&self.stats.skill_set,
|
&self.stats.skill_set,
|
||||||
&self.localized_strings,
|
&self.localized_strings,
|
||||||
@ -3074,7 +3076,7 @@ impl<'a> Widget for Diary<'a> {
|
|||||||
)
|
)
|
||||||
.wh(art_size)
|
.wh(art_size)
|
||||||
.middle_of(state.content_align)
|
.middle_of(state.content_align)
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0))))
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
|
||||||
.set(state.sceptre_render, ui);
|
.set(state.sceptre_render, ui);
|
||||||
// Top Left skills
|
// Top Left skills
|
||||||
// 5 1 6
|
// 5 1 6
|
||||||
@ -3392,22 +3394,24 @@ fn create_skill_button<'a>(
|
|||||||
label: &'a str,
|
label: &'a str,
|
||||||
) -> Button<'a, button::Image> {
|
) -> Button<'a, button::Image> {
|
||||||
Button::image(image)
|
Button::image(image)
|
||||||
.w_h(tweak!(74.0), tweak!(74.0))
|
.w_h(74.0, 74.0)
|
||||||
.middle_of(state)
|
.middle_of(state)
|
||||||
.label(label)
|
.label(label)
|
||||||
.label_y(conrod_core::position::Relative::Scalar(tweak!(-28.0)))
|
.label_y(conrod_core::position::Relative::Scalar(-28.0))
|
||||||
.label_x(conrod_core::position::Relative::Scalar(tweak!(32.0)))
|
.label_x(conrod_core::position::Relative::Scalar(32.0))
|
||||||
.label_color(if skill_set.sufficient_skill_points(skill) {
|
.label_color(if skill_set.is_at_max_level(skill) {
|
||||||
|
TEXT_COLOR
|
||||||
|
} else if skill_set.sufficient_skill_points(skill) {
|
||||||
HP_COLOR
|
HP_COLOR
|
||||||
} else {
|
} else {
|
||||||
CRITICAL_HP_COLOR
|
CRITICAL_HP_COLOR
|
||||||
})
|
})
|
||||||
.label_font_size(fonts.cyri.scale(tweak!(16)))
|
.label_font_size(fonts.cyri.scale(16))
|
||||||
.label_font_id(fonts.cyri.conrod_id)
|
.label_font_id(fonts.cyri.conrod_id)
|
||||||
.image_color(if skill_set.prerequisites_met(skill) {
|
.image_color(if skill_set.prerequisites_met(skill) {
|
||||||
TEXT_COLOR
|
TEXT_COLOR
|
||||||
} else {
|
} else {
|
||||||
Color::Rgba(0.41, 0.41, 0.41, tweak!(0.7))
|
Color::Rgba(0.41, 0.41, 0.41, 0.7)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3415,12 +3419,8 @@ fn get_skill_label(skill: Skill, skill_set: &skills::SkillSet) -> String {
|
|||||||
if skill_set.prerequisites_met(skill) {
|
if skill_set.prerequisites_met(skill) {
|
||||||
format!(
|
format!(
|
||||||
"{}/{}",
|
"{}/{}",
|
||||||
skill_set
|
skill_set.skill_level(skill).map_or(0, |l| l.unwrap_or(1)),
|
||||||
.skills
|
skill.max_level().unwrap_or(1)
|
||||||
.get(&skill)
|
|
||||||
.copied()
|
|
||||||
.map_or(0, |l| l.unwrap_or(1)),
|
|
||||||
skill.get_max_level().unwrap_or(1)
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
@ -3446,14 +3446,13 @@ fn add_sp_cost_tooltip<'a>(
|
|||||||
skill_set: &'a skills::SkillSet,
|
skill_set: &'a skills::SkillSet,
|
||||||
localized_strings: &'a Localization,
|
localized_strings: &'a Localization,
|
||||||
) -> String {
|
) -> String {
|
||||||
match skill_set.skills.get(&skill).copied() {
|
match skill_set.skill_level(skill) {
|
||||||
Some(level) if level == skill.get_max_level() => tooltip.replace("{}", ""),
|
Ok(level) if level == skill.max_level() => tooltip.replace("{}", ""),
|
||||||
_ => tooltip.replace(
|
_ => tooltip.replace(
|
||||||
"{SP}",
|
"{SP}",
|
||||||
&localized_strings.get("hud.skill.req_sp").replace(
|
&localized_strings
|
||||||
"{number}",
|
.get("hud.skill.req_sp")
|
||||||
&format!("{}", skill_set.skill_point_cost(skill)),
|
.replace("{number}", &format!("{}", skill_set.skill_cost(skill))),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use super::{
|
use super::{
|
||||||
img_ids::{Imgs, ImgsRot},
|
img_ids::{Imgs, ImgsRot},
|
||||||
Show, BLACK, BUFF_COLOR, DEBUFF_COLOR, ERROR_COLOR, GROUP_COLOR, HP_COLOR, KILL_COLOR,
|
Show, BLACK, BUFF_COLOR, DEBUFF_COLOR, ERROR_COLOR, GROUP_COLOR, HP_COLOR, KILL_COLOR,
|
||||||
LOW_HP_COLOR, STAMINA_COLOR, TEXT_COLOR, TEXT_COLOR_GREY, UI_HIGHLIGHT_0, UI_MAIN,
|
LOW_HP_COLOR, QUALITY_ARTIFACT, QUALITY_COMMON, QUALITY_DEBUG, QUALITY_EPIC, QUALITY_HIGH,
|
||||||
|
QUALITY_LEGENDARY, QUALITY_LOW, QUALITY_MODERATE, STAMINA_COLOR, TEXT_COLOR, TEXT_COLOR_GREY,
|
||||||
|
UI_HIGHLIGHT_0, UI_MAIN, XP_COLOR,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -14,6 +16,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use client::{self, Client};
|
use client::{self, Client};
|
||||||
use common::{
|
use common::{
|
||||||
|
combat,
|
||||||
comp::{group::Role, BuffKind, Stats},
|
comp::{group::Role, BuffKind, Stats},
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
};
|
};
|
||||||
@ -26,6 +29,8 @@ use conrod_core::{
|
|||||||
};
|
};
|
||||||
use specs::{saveload::MarkerAllocator, WorldExt};
|
use specs::{saveload::MarkerAllocator, WorldExt};
|
||||||
|
|
||||||
|
use inline_tweak::*;
|
||||||
|
|
||||||
widget_ids! {
|
widget_ids! {
|
||||||
pub struct Ids {
|
pub struct Ids {
|
||||||
group_button,
|
group_button,
|
||||||
@ -54,6 +59,7 @@ widget_ids! {
|
|||||||
buff_timers[],
|
buff_timers[],
|
||||||
dead_txt[],
|
dead_txt[],
|
||||||
health_txt[],
|
health_txt[],
|
||||||
|
combat_rating_indicators[],
|
||||||
timeout_bg,
|
timeout_bg,
|
||||||
timeout,
|
timeout,
|
||||||
}
|
}
|
||||||
@ -323,12 +329,19 @@ impl<'a> Widget for Group<'a> {
|
|||||||
.resize(group_size, &mut ui.widget_id_generator())
|
.resize(group_size, &mut ui.widget_id_generator())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
if state.ids.combat_rating_indicators.len() < group_size {
|
||||||
|
state.update(|s| {
|
||||||
|
s.ids
|
||||||
|
.combat_rating_indicators
|
||||||
|
.resize(group_size, &mut ui.widget_id_generator())
|
||||||
|
})
|
||||||
|
};
|
||||||
let client_state = self.client.state();
|
let client_state = self.client.state();
|
||||||
let stats = client_state.ecs().read_storage::<common::comp::Stats>();
|
let stats = client_state.ecs().read_storage::<common::comp::Stats>();
|
||||||
let healths = client_state.ecs().read_storage::<common::comp::Health>();
|
let healths = client_state.ecs().read_storage::<common::comp::Health>();
|
||||||
let energy = client_state.ecs().read_storage::<common::comp::Energy>();
|
let energy = client_state.ecs().read_storage::<common::comp::Energy>();
|
||||||
let buffs = client_state.ecs().read_storage::<common::comp::Buffs>();
|
let buffs = client_state.ecs().read_storage::<common::comp::Buffs>();
|
||||||
|
let inventory = client_state.ecs().read_storage::<common::comp::Inventory>();
|
||||||
let uid_allocator = client_state.ecs().read_resource::<UidAllocator>();
|
let uid_allocator = client_state.ecs().read_resource::<UidAllocator>();
|
||||||
|
|
||||||
// Keep track of the total number of widget ids we are using for buffs
|
// Keep track of the total number of widget ids we are using for buffs
|
||||||
@ -340,10 +353,12 @@ impl<'a> Widget for Group<'a> {
|
|||||||
let health = entity.and_then(|entity| healths.get(entity));
|
let health = entity.and_then(|entity| healths.get(entity));
|
||||||
let energy = entity.and_then(|entity| energy.get(entity));
|
let energy = entity.and_then(|entity| energy.get(entity));
|
||||||
let buffs = entity.and_then(|entity| buffs.get(entity));
|
let buffs = entity.and_then(|entity| buffs.get(entity));
|
||||||
|
let inventory = entity.and_then(|entity| inventory.get(entity));
|
||||||
let is_leader = uid == leader;
|
let is_leader = uid == leader;
|
||||||
|
|
||||||
if let Some(stats) = stats {
|
if let Some(stats) = stats {
|
||||||
|
let combat_rating =
|
||||||
|
combat::combat_rating(inventory.unwrap(), &health.unwrap(), stats); // We can unwrap here because we check for stats first
|
||||||
let char_name = stats.name.to_string();
|
let char_name = stats.name.to_string();
|
||||||
if let Some(health) = health {
|
if let Some(health) = health {
|
||||||
let health_perc = health.current() as f64 / health.maximum() as f64;
|
let health_perc = health.current() as f64 / health.maximum() as f64;
|
||||||
@ -421,9 +436,38 @@ impl<'a> Widget for Group<'a> {
|
|||||||
.middle_of(state.ids.member_panels_bg[i])
|
.middle_of(state.ids.member_panels_bg[i])
|
||||||
.color(Some(UI_HIGHLIGHT_0))
|
.color(Some(UI_HIGHLIGHT_0))
|
||||||
.set(state.ids.member_panels_frame[i], ui);
|
.set(state.ids.member_panels_frame[i], ui);
|
||||||
|
// Thresholds (lower)
|
||||||
|
let common = 4.3;
|
||||||
|
let moderate = 6.0;
|
||||||
|
let high = 8.0;
|
||||||
|
let epic = 10.0;
|
||||||
|
let legendary = 79.0;
|
||||||
|
let artifact = 122.0;
|
||||||
|
let debug = 200.0;
|
||||||
|
|
||||||
|
let indicator_col = match combat_rating {
|
||||||
|
x if (0.0..common).contains(&x) => QUALITY_LOW,
|
||||||
|
x if (common..moderate).contains(&x) => QUALITY_COMMON,
|
||||||
|
x if (moderate..high).contains(&x) => QUALITY_MODERATE,
|
||||||
|
x if (high..epic).contains(&x) => QUALITY_HIGH,
|
||||||
|
x if (epic..legendary).contains(&x) => QUALITY_EPIC,
|
||||||
|
x if (legendary..artifact).contains(&x) => QUALITY_LEGENDARY,
|
||||||
|
x if (artifact..debug).contains(&x) => QUALITY_ARTIFACT,
|
||||||
|
x if x >= debug => QUALITY_DEBUG,
|
||||||
|
_ => XP_COLOR,
|
||||||
|
};
|
||||||
|
Image::new(self.imgs.combat_rating_ico_shadow)
|
||||||
|
.w_h(tweak!(18.0), tweak!(18.0))
|
||||||
|
.top_left_with_margins_on(
|
||||||
|
state.ids.member_panels_frame[i],
|
||||||
|
tweak!(-20.0),
|
||||||
|
tweak!(2.0),
|
||||||
|
)
|
||||||
|
.color(Some(indicator_col))
|
||||||
|
.set(state.ids.combat_rating_indicators[i], ui);
|
||||||
// Panel Text
|
// Panel Text
|
||||||
Text::new(&char_name)
|
Text::new(&char_name)
|
||||||
.top_left_with_margins_on(state.ids.member_panels_frame[i], -22.0, 0.0)
|
.top_left_with_margins_on(state.ids.member_panels_frame[i], tweak!(-22.0), tweak!(22.0))
|
||||||
.font_size(20)
|
.font_size(20)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.color(BLACK)
|
.color(BLACK)
|
||||||
|
@ -89,24 +89,19 @@ impl State {
|
|||||||
match tool.kind {
|
match tool.kind {
|
||||||
ToolKind::Sword => stat
|
ToolKind::Sword => stat
|
||||||
.skill_set
|
.skill_set
|
||||||
.skills
|
.has_skill(Skill::Sword(skills::SwordSkill::UnlockSpin)),
|
||||||
.contains_key(&Skill::Sword(skills::SwordSkill::UnlockSpin)),
|
|
||||||
ToolKind::Axe => stat
|
ToolKind::Axe => stat
|
||||||
.skill_set
|
.skill_set
|
||||||
.skills
|
.has_skill(Skill::Axe(skills::AxeSkill::UnlockLeap)),
|
||||||
.contains_key(&Skill::Axe(skills::AxeSkill::UnlockLeap)),
|
|
||||||
ToolKind::Hammer => stat
|
ToolKind::Hammer => stat
|
||||||
.skill_set
|
.skill_set
|
||||||
.skills
|
.has_skill(Skill::Hammer(skills::HammerSkill::UnlockLeap)),
|
||||||
.contains_key(&Skill::Hammer(skills::HammerSkill::UnlockLeap)),
|
|
||||||
ToolKind::Bow => stat
|
ToolKind::Bow => stat
|
||||||
.skill_set
|
.skill_set
|
||||||
.skills
|
.has_skill(Skill::Bow(skills::BowSkill::UnlockRepeater)),
|
||||||
.contains_key(&Skill::Bow(skills::BowSkill::UnlockRepeater)),
|
|
||||||
ToolKind::Staff => stat
|
ToolKind::Staff => stat
|
||||||
.skill_set
|
.skill_set
|
||||||
.skills
|
.has_skill(Skill::Staff(skills::StaffSkill::UnlockShockwave)),
|
||||||
.contains_key(&Skill::Staff(skills::StaffSkill::UnlockShockwave)),
|
|
||||||
ToolKind::Debug
|
ToolKind::Debug
|
||||||
| ToolKind::Unique(UniqueKind::QuadMedQuick)
|
| ToolKind::Unique(UniqueKind::QuadMedQuick)
|
||||||
| ToolKind::Unique(UniqueKind::QuadLowBreathe) => true,
|
| ToolKind::Unique(UniqueKind::QuadLowBreathe) => true,
|
||||||
|
@ -211,7 +211,6 @@ image_ids! {
|
|||||||
// Other Icons/Art
|
// Other Icons/Art
|
||||||
skull: "voxygen.element.icons.skull",
|
skull: "voxygen.element.icons.skull",
|
||||||
skull_2: "voxygen.element.icons.skull_2",
|
skull_2: "voxygen.element.icons.skull_2",
|
||||||
indicator_bubble: "voxygen.element.icons.indicator_bubble",
|
|
||||||
fireplace: "voxygen.element.misc_bg.fireplace",
|
fireplace: "voxygen.element.misc_bg.fireplace",
|
||||||
|
|
||||||
// Crosshair
|
// Crosshair
|
||||||
@ -338,6 +337,7 @@ image_ids! {
|
|||||||
health_ico: "voxygen.element.icons.health",
|
health_ico: "voxygen.element.icons.health",
|
||||||
protection_ico: "voxygen.element.icons.protection",
|
protection_ico: "voxygen.element.icons.protection",
|
||||||
combat_rating_ico: "voxygen.element.icons.combat_rating",
|
combat_rating_ico: "voxygen.element.icons.combat_rating",
|
||||||
|
combat_rating_ico_shadow: "voxygen.element.icons.combat_rating_shadow",
|
||||||
|
|
||||||
not_found: "voxygen.element.not_found",
|
not_found: "voxygen.element.not_found",
|
||||||
|
|
||||||
@ -366,8 +366,11 @@ image_ids! {
|
|||||||
// Enemy Healthbar
|
// Enemy Healthbar
|
||||||
enemy_health: "voxygen.element.frames.enemybar",
|
enemy_health: "voxygen.element.frames.enemybar",
|
||||||
enemy_health_bg: "voxygen.element.frames.enemybar_bg",
|
enemy_health_bg: "voxygen.element.frames.enemybar_bg",
|
||||||
|
health_bar_group: "voxygen.element.frames.enemybar_1",
|
||||||
|
health_bar_group_bg: "voxygen.element.frames.enemybar_bg_1",
|
||||||
// Enemy Bar Content:
|
// Enemy Bar Content:
|
||||||
enemy_bar: "voxygen.element.skillbar.enemy_bar_content",
|
enemy_bar: "voxygen.element.skillbar.enemy_bar_content",
|
||||||
|
|
||||||
// Bag
|
// Bag
|
||||||
bag: "voxygen.element.buttons.bag.closed",
|
bag: "voxygen.element.buttons.bag.closed",
|
||||||
bag_hover: "voxygen.element.buttons.bag.closed_hover",
|
bag_hover: "voxygen.element.buttons.bag.closed_hover",
|
||||||
|
@ -79,7 +79,6 @@ use conrod_core::{
|
|||||||
widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use inline_tweak::*;
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use specs::{Join, WorldExt};
|
use specs::{Join, WorldExt};
|
||||||
use std::{
|
use std::{
|
||||||
@ -1142,14 +1141,16 @@ impl Hud {
|
|||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|d| d.owner == *uid)
|
.find(|d| d.owner == *uid)
|
||||||
{
|
{
|
||||||
let fade = if display.timer < 1.0 {
|
let fade = if display.timer < 3.0 {
|
||||||
display.timer as f32
|
display.timer as f32 * 0.33
|
||||||
|
} else if display.timer < 2.0 {
|
||||||
|
display.timer as f32 * 0.33 * 0.1
|
||||||
} else {
|
} else {
|
||||||
1.0
|
1.0
|
||||||
};
|
};
|
||||||
// Background image
|
// Background image
|
||||||
let offset = if display.timer < tweak!(2.0) {
|
let offset = if display.timer < 2.0 {
|
||||||
300.0 - (display.timer as f64 - tweak!(2.0)) * tweak!(-300.0)
|
300.0 - (display.timer as f64 - 2.0) * -300.0
|
||||||
} else {
|
} else {
|
||||||
300.0
|
300.0
|
||||||
};
|
};
|
||||||
@ -1162,20 +1163,20 @@ impl Hud {
|
|||||||
// Rank Number
|
// Rank Number
|
||||||
let rank = display.total_points;
|
let rank = display.total_points;
|
||||||
Text::new(&format!("{}", rank))
|
Text::new(&format!("{}", rank))
|
||||||
.font_size(tweak!(20))
|
.font_size(20)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.color(Color::Rgba(1.0, 1.0, 1.0, fade))
|
.color(Color::Rgba(1.0, 1.0, 1.0, fade))
|
||||||
.mid_top_with_margin_on(self.ids.player_rank_up, tweak!(8.0))
|
.mid_top_with_margin_on(self.ids.player_rank_up, 8.0)
|
||||||
.set(self.ids.player_rank_up_txt_number, ui_widgets);
|
.set(self.ids.player_rank_up_txt_number, ui_widgets);
|
||||||
// Static "New Rank!" text
|
// Static "New Rank!" text
|
||||||
Text::new(&i18n.get("hud.rank_up"))
|
Text::new(&i18n.get("hud.rank_up"))
|
||||||
.font_size(tweak!(40))
|
.font_size(40)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.color(Color::Rgba(0.0, 0.0, 0.0, fade))
|
.color(Color::Rgba(0.0, 0.0, 0.0, fade))
|
||||||
.mid_bottom_with_margin_on(self.ids.player_rank_up, tweak!(20.0))
|
.mid_bottom_with_margin_on(self.ids.player_rank_up, 20.0)
|
||||||
.set(self.ids.player_rank_up_txt_0_bg, ui_widgets);
|
.set(self.ids.player_rank_up_txt_0_bg, ui_widgets);
|
||||||
Text::new(&i18n.get("hud.rank_up"))
|
Text::new(&i18n.get("hud.rank_up"))
|
||||||
.font_size(tweak!(40))
|
.font_size(40)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.color(Color::Rgba(1.0, 1.0, 1.0, fade))
|
.color(Color::Rgba(1.0, 1.0, 1.0, fade))
|
||||||
.bottom_left_with_margins_on(self.ids.player_rank_up_txt_0_bg, 2.0, 2.0)
|
.bottom_left_with_margins_on(self.ids.player_rank_up_txt_0_bg, 2.0, 2.0)
|
||||||
@ -1192,13 +1193,13 @@ impl Hud {
|
|||||||
_ => "Unknown",
|
_ => "Unknown",
|
||||||
};
|
};
|
||||||
Text::new(skill)
|
Text::new(skill)
|
||||||
.font_size(tweak!(20))
|
.font_size(20)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.color(Color::Rgba(0.0, 0.0, 0.0, fade))
|
.color(Color::Rgba(0.0, 0.0, 0.0, fade))
|
||||||
.mid_top_with_margin_on(self.ids.player_rank_up, tweak!(45.0))
|
.mid_top_with_margin_on(self.ids.player_rank_up, 45.0)
|
||||||
.set(self.ids.player_rank_up_txt_1_bg, ui_widgets);
|
.set(self.ids.player_rank_up_txt_1_bg, ui_widgets);
|
||||||
Text::new(skill)
|
Text::new(skill)
|
||||||
.font_size(tweak!(20))
|
.font_size(20)
|
||||||
.font_id(self.fonts.cyri.conrod_id)
|
.font_id(self.fonts.cyri.conrod_id)
|
||||||
.color(Color::Rgba(1.0, 1.0, 1.0, fade))
|
.color(Color::Rgba(1.0, 1.0, 1.0, fade))
|
||||||
.bottom_left_with_margins_on(self.ids.player_rank_up_txt_1_bg, 2.0, 2.0)
|
.bottom_left_with_margins_on(self.ids.player_rank_up_txt_1_bg, 2.0, 2.0)
|
||||||
@ -1215,8 +1216,8 @@ impl Hud {
|
|||||||
Weapon(ToolKind::Staff) => self.imgs.staff,
|
Weapon(ToolKind::Staff) => self.imgs.staff,
|
||||||
_ => self.imgs.swords_crossed,
|
_ => self.imgs.swords_crossed,
|
||||||
})
|
})
|
||||||
.w_h(tweak!(20.0), tweak!(20.0))
|
.w_h(20.0, 20.0)
|
||||||
.left_from(self.ids.player_rank_up_txt_1_bg, tweak!(5.0))
|
.left_from(self.ids.player_rank_up_txt_1_bg, 5.0)
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, fade)))
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, fade)))
|
||||||
.set(self.ids.player_rank_up_icon, ui_widgets);
|
.set(self.ids.player_rank_up_icon, ui_widgets);
|
||||||
|
|
||||||
@ -1335,11 +1336,7 @@ impl Hud {
|
|||||||
health,
|
health,
|
||||||
buffs,
|
buffs,
|
||||||
energy,
|
energy,
|
||||||
combat_rating: combat::combat_rating(
|
combat_rating: combat::combat_rating(inventory, health, stats),
|
||||||
inventory,
|
|
||||||
health,
|
|
||||||
&stats.body_type,
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
let bubble = if dist_sqr < SPEECH_BUBBLE_RANGE.powi(2) {
|
let bubble = if dist_sqr < SPEECH_BUBBLE_RANGE.powi(2) {
|
||||||
speech_bubbles.get(uid)
|
speech_bubbles.get(uid)
|
||||||
|
@ -18,9 +18,7 @@ use conrod_core::{
|
|||||||
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||||
};
|
};
|
||||||
const MAX_BUBBLE_WIDTH: f64 = 250.0;
|
const MAX_BUBBLE_WIDTH: f64 = 250.0;
|
||||||
|
|
||||||
use inline_tweak::*;
|
use inline_tweak::*;
|
||||||
|
|
||||||
widget_ids! {
|
widget_ids! {
|
||||||
struct Ids {
|
struct Ids {
|
||||||
// Speech bubble
|
// Speech bubble
|
||||||
@ -313,7 +311,7 @@ impl<'a> Widget for Overhead<'a> {
|
|||||||
let crit_hp_color: Color = Color::Rgba(0.93, 0.59, 0.03, hp_ani);
|
let crit_hp_color: Color = Color::Rgba(0.93, 0.59, 0.03, hp_ani);
|
||||||
|
|
||||||
// Background
|
// Background
|
||||||
Image::new(self.imgs.enemy_health_bg)
|
Image::new(if self.in_group {self.imgs.health_bar_group_bg} else {self.imgs.enemy_health_bg})
|
||||||
.w_h(84.0 * BARSIZE, 10.0 * BARSIZE)
|
.w_h(84.0 * BARSIZE, 10.0 * BARSIZE)
|
||||||
.x_y(0.0, MANA_BAR_Y + 6.5) //-25.5)
|
.x_y(0.0, MANA_BAR_Y + 6.5) //-25.5)
|
||||||
.color(Some(Color::Rgba(0.1, 0.1, 0.1, 0.8)))
|
.color(Some(Color::Rgba(0.1, 0.1, 0.1, 0.8)))
|
||||||
@ -321,12 +319,21 @@ impl<'a> Widget for Overhead<'a> {
|
|||||||
.set(state.ids.health_bar_bg, ui);
|
.set(state.ids.health_bar_bg, ui);
|
||||||
|
|
||||||
// % HP Filling
|
// % HP Filling
|
||||||
|
let size_factor = (hp_percentage / 100.0) * BARSIZE;
|
||||||
|
let w = if self.in_group {
|
||||||
|
tweak!(82.0) * size_factor
|
||||||
|
} else {
|
||||||
|
73.0 * size_factor
|
||||||
|
};
|
||||||
|
let h = 6.0 * BARSIZE;
|
||||||
|
let x = if self.in_group {
|
||||||
|
(tweak!(0.0) + (hp_percentage / 100.0 * tweak!(41.0) - tweak!(41.0))) * BARSIZE
|
||||||
|
} else {
|
||||||
|
(4.5 + (hp_percentage / 100.0 * 36.45 - 36.45)) * BARSIZE
|
||||||
|
};
|
||||||
Image::new(self.imgs.enemy_bar)
|
Image::new(self.imgs.enemy_bar)
|
||||||
.w_h(73.0 * (hp_percentage / 100.0) * BARSIZE, 6.0 * BARSIZE)
|
.w_h(w, h)
|
||||||
.x_y(
|
.x_y(x, MANA_BAR_Y + tweak!(8.0))
|
||||||
(4.5 + (hp_percentage / 100.0 * 36.45 - 36.45)) * BARSIZE,
|
|
||||||
MANA_BAR_Y + 7.5,
|
|
||||||
)
|
|
||||||
.color(if self.in_group {
|
.color(if self.in_group {
|
||||||
// Different HP bar colors only for group members
|
// Different HP bar colors only for group members
|
||||||
Some(match hp_percentage {
|
Some(match hp_percentage {
|
||||||
@ -354,37 +361,41 @@ impl<'a> Widget for Overhead<'a> {
|
|||||||
// % Mana Filling
|
// % Mana Filling
|
||||||
if let Some(energy) = energy {
|
if let Some(energy) = energy {
|
||||||
let energy_factor = energy.current() as f64 / energy.maximum() as f64;
|
let energy_factor = energy.current() as f64 / energy.maximum() as f64;
|
||||||
|
let size_factor = energy_factor * BARSIZE;
|
||||||
Rectangle::fill_with(
|
let w = if self.in_group {
|
||||||
[72.0 * energy_factor * BARSIZE, MANA_BAR_HEIGHT],
|
tweak!(80.0) * size_factor
|
||||||
STAMINA_COLOR,
|
} else {
|
||||||
)
|
72.0 * size_factor
|
||||||
|
};
|
||||||
|
let x = if self.in_group {
|
||||||
|
((tweak!(0.0) + (energy_factor * tweak!(40.0))) - tweak!(40.0)) * BARSIZE
|
||||||
|
} else {
|
||||||
|
((3.5 + (energy_factor * 36.5)) - 36.45) * BARSIZE
|
||||||
|
};
|
||||||
|
Rectangle::fill_with([w, MANA_BAR_HEIGHT], STAMINA_COLOR)
|
||||||
.x_y(
|
.x_y(
|
||||||
((3.5 + (energy_factor * 36.5)) - 36.45) * BARSIZE,
|
x, MANA_BAR_Y, //-32.0,
|
||||||
MANA_BAR_Y, //-32.0,
|
|
||||||
)
|
)
|
||||||
.parent(id)
|
.parent(id)
|
||||||
.set(state.ids.mana_bar, ui);
|
.set(state.ids.mana_bar, ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Foreground
|
// Foreground
|
||||||
Image::new(self.imgs.enemy_health)
|
Image::new(if self.in_group {self.imgs.health_bar_group} else {self.imgs.enemy_health})
|
||||||
.w_h(84.0 * BARSIZE, 10.0 * BARSIZE)
|
.w_h(84.0 * BARSIZE, 10.0 * BARSIZE)
|
||||||
.x_y(0.0, MANA_BAR_Y + 6.5) //-25.5)
|
.x_y(0.0, MANA_BAR_Y + 6.5) //-25.5)
|
||||||
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.99)))
|
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 0.99)))
|
||||||
.parent(id)
|
.parent(id)
|
||||||
.set(state.ids.health_bar_fg, ui);
|
.set(state.ids.health_bar_fg, ui);
|
||||||
|
|
||||||
// TODO: Add strength comparison here, this is just an example
|
|
||||||
|
|
||||||
// Thresholds (lower)
|
// Thresholds (lower)
|
||||||
let common = tweak!(4.3);
|
let common = 4.3;
|
||||||
let moderate = tweak!(6.0);
|
let moderate = 6.0;
|
||||||
let high = tweak!(8.0);
|
let high = 8.0;
|
||||||
let epic = tweak!(10.0);
|
let epic = 10.0;
|
||||||
let legendary = tweak!(79.0);
|
let legendary = 79.0;
|
||||||
let artifact = tweak!(122.0);
|
let artifact = 122.0;
|
||||||
let debug = tweak!(200.0);
|
let debug = 200.0;
|
||||||
|
|
||||||
let indicator_col = match combat_rating {
|
let indicator_col = match combat_rating {
|
||||||
x if (0.0..common).contains(&x) => QUALITY_LOW,
|
x if (0.0..common).contains(&x) => QUALITY_LOW,
|
||||||
@ -411,9 +422,13 @@ impl<'a> Widget for Overhead<'a> {
|
|||||||
.parent(id)
|
.parent(id)
|
||||||
.set(state.ids.level_skull, ui);
|
.set(state.ids.level_skull, ui);
|
||||||
} else {
|
} else {
|
||||||
Image::new(self.imgs.indicator_bubble)
|
Image::new(if self.in_group {
|
||||||
.w_h(5.0 * BARSIZE, 5.0 * BARSIZE)
|
self.imgs.nothing
|
||||||
.x_y(tweak!(-37.0) * BARSIZE, MANA_BAR_Y + tweak!(7.5))
|
} else {
|
||||||
|
self.imgs.combat_rating_ico
|
||||||
|
})
|
||||||
|
.w_h(tweak!(7.0) * BARSIZE, tweak!(7.0) * BARSIZE)
|
||||||
|
.x_y(tweak!(-37.0) * BARSIZE, MANA_BAR_Y + tweak!(6.0))
|
||||||
.color(Some(indicator_col))
|
.color(Some(indicator_col))
|
||||||
.parent(id)
|
.parent(id)
|
||||||
.set(state.ids.level, ui);
|
.set(state.ids.level, ui);
|
||||||
|
@ -28,7 +28,6 @@ use conrod_core::{
|
|||||||
widget::{self, Button, Image, Rectangle, Text},
|
widget::{self, Button, Image, Rectangle, Text},
|
||||||
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||||
};
|
};
|
||||||
use inline_tweak::*;
|
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
widget_ids! {
|
widget_ids! {
|
||||||
@ -222,7 +221,7 @@ impl<'a> Widget for Skillbar<'a> {
|
|||||||
|
|
||||||
let localized_strings = self.localized_strings;
|
let localized_strings = self.localized_strings;
|
||||||
|
|
||||||
let slot_offset = tweak!(3.0);
|
let slot_offset = 3.0;
|
||||||
|
|
||||||
// Death message
|
// Death message
|
||||||
if self.health.is_dead {
|
if self.health.is_dead {
|
||||||
@ -277,11 +276,7 @@ impl<'a> Widget for Skillbar<'a> {
|
|||||||
let show_stamina = self.energy.current() != self.energy.maximum();
|
let show_stamina = self.energy.current() != self.energy.maximum();
|
||||||
|
|
||||||
if show_health && !self.health.is_dead {
|
if show_health && !self.health.is_dead {
|
||||||
let offset = if show_stamina {
|
let offset = 1.0;
|
||||||
tweak!(1.0)
|
|
||||||
} else {
|
|
||||||
tweak!(1.0)
|
|
||||||
};
|
|
||||||
Image::new(self.imgs.health_bg)
|
Image::new(self.imgs.health_bg)
|
||||||
.w_h(484.0, 24.0)
|
.w_h(484.0, 24.0)
|
||||||
.mid_top_with_margin_on(state.ids.frame, -offset)
|
.mid_top_with_margin_on(state.ids.frame, -offset)
|
||||||
@ -306,11 +301,7 @@ impl<'a> Widget for Skillbar<'a> {
|
|||||||
.set(state.ids.frame_health, ui);
|
.set(state.ids.frame_health, ui);
|
||||||
}
|
}
|
||||||
if show_stamina && !self.health.is_dead {
|
if show_stamina && !self.health.is_dead {
|
||||||
let offset = if show_health {
|
let offset = if show_health { 34.0 } else { 1.0 };
|
||||||
tweak!(34.0)
|
|
||||||
} else {
|
|
||||||
tweak!(1.0)
|
|
||||||
};
|
|
||||||
Image::new(self.imgs.stamina_bg)
|
Image::new(self.imgs.stamina_bg)
|
||||||
.w_h(323.0, 16.0)
|
.w_h(323.0, 16.0)
|
||||||
.mid_top_with_margin_on(state.ids.frame, -offset)
|
.mid_top_with_margin_on(state.ids.frame, -offset)
|
||||||
@ -533,7 +524,7 @@ impl<'a> Widget for Skillbar<'a> {
|
|||||||
let slot = slot_maker
|
let slot = slot_maker
|
||||||
.fabricate(hotbar::Slot::Four, [40.0; 2])
|
.fabricate(hotbar::Slot::Four, [40.0; 2])
|
||||||
.filled_slot(self.imgs.skillbar_slot)
|
.filled_slot(self.imgs.skillbar_slot)
|
||||||
.right_from(state.ids.slot3, 0.0);
|
.right_from(state.ids.slot3, slot_offset);
|
||||||
if let Some((title, desc)) = tooltip_text(hotbar::Slot::Four) {
|
if let Some((title, desc)) = tooltip_text(hotbar::Slot::Four) {
|
||||||
slot.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip, TEXT_COLOR)
|
slot.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip, TEXT_COLOR)
|
||||||
.set(state.ids.slot4, ui);
|
.set(state.ids.slot4, ui);
|
||||||
@ -544,7 +535,7 @@ impl<'a> Widget for Skillbar<'a> {
|
|||||||
let slot = slot_maker
|
let slot = slot_maker
|
||||||
.fabricate(hotbar::Slot::Five, [40.0; 2])
|
.fabricate(hotbar::Slot::Five, [40.0; 2])
|
||||||
.filled_slot(self.imgs.skillbar_slot)
|
.filled_slot(self.imgs.skillbar_slot)
|
||||||
.right_from(state.ids.slot4, 0.0);
|
.right_from(state.ids.slot4, slot_offset);
|
||||||
if let Some((title, desc)) = tooltip_text(hotbar::Slot::Five) {
|
if let Some((title, desc)) = tooltip_text(hotbar::Slot::Five) {
|
||||||
slot.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip, TEXT_COLOR)
|
slot.with_tooltip(self.tooltip_manager, title, desc, &item_tooltip, TEXT_COLOR)
|
||||||
.set(state.ids.slot5, ui);
|
.set(state.ids.slot5, ui);
|
||||||
|
@ -37,8 +37,6 @@ use iced::{
|
|||||||
};
|
};
|
||||||
use vek::Rgba;
|
use vek::Rgba;
|
||||||
|
|
||||||
use inline_tweak::*;
|
|
||||||
|
|
||||||
pub const TEXT_COLOR: iced::Color = iced::Color::from_rgb(1.0, 1.0, 1.0);
|
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 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);
|
pub const TOOLTIP_BACK_COLOR: Rgba<u8> = Rgba::new(20, 18, 10, 255);
|
||||||
@ -446,11 +444,8 @@ impl Controls {
|
|||||||
select_button,
|
select_button,
|
||||||
Column::with_children(vec![
|
Column::with_children(vec![
|
||||||
Text::new(&character.character.alias)
|
Text::new(&character.character.alias)
|
||||||
.size(fonts.cyri.scale(tweak!(26)))
|
.size(fonts.cyri.scale(26))
|
||||||
.into(),
|
.into(),
|
||||||
// TODO: only construct string once when characters
|
|
||||||
// are
|
|
||||||
// loaded
|
|
||||||
Text::new(
|
Text::new(
|
||||||
i18n.get("char_selection.uncanny_valley"),
|
i18n.get("char_selection.uncanny_valley"),
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user