General combat skill tree.

UI for general skill tree
This commit is contained in:
Sam 2020-12-31 13:37:25 -05:00
parent 58d9534496
commit 48c98b11cf
18 changed files with 997 additions and 579 deletions

View File

@ -1,5 +1,6 @@
({ ({
General(HealthIncrease): Some(10), General(HealthIncrease): Some(10),
General(EnergyIncrease): Some(5),
Sword(TsDamage): Some(3), Sword(TsDamage): Some(3),
Sword(TsRegen): Some(2), Sword(TsRegen): Some(2),
Sword(TsSpeed): Some(3), Sword(TsSpeed): Some(3),
@ -69,4 +70,7 @@
Sceptre(PRadius): Some(2), Sceptre(PRadius): Some(2),
Sceptre(PCost): Some(2), Sceptre(PCost): Some(2),
Sceptre(PProjSpeed): Some(2), Sceptre(PProjSpeed): Some(2),
Roll(Cost): Some(2),
Roll(Strength): Some(2),
Roll(Duration): Some(2),
}) })

View File

@ -1,10 +1,7 @@
({ ({
UnlockGroup(Weapon(Sword)): {General(HealthIncrease): Some(1)}, Roll(Cost): {Roll(ImmuneMelee): None},
UnlockGroup(Weapon(Axe)): {General(HealthIncrease): Some(1)}, Roll(Strength): {Roll(ImmuneMelee): None},
UnlockGroup(Weapon(Hammer)): {General(HealthIncrease): Some(1)}, Roll(Duration): {Roll(ImmuneMelee): None},
UnlockGroup(Weapon(Bow)): {General(HealthIncrease): Some(1)},
UnlockGroup(Weapon(Staff)): {General(HealthIncrease): Some(1)},
UnlockGroup(Weapon(Sceptre)): {General(HealthIncrease): Some(1)},
Sword(TsDamage): {Sword(TsCombo): None}, Sword(TsDamage): {Sword(TsCombo): None},
Sword(TsRegen): {Sword(TsCombo): None}, Sword(TsRegen): {Sword(TsCombo): None},
Sword(TsSpeed): {Sword(TsCombo): None}, Sword(TsSpeed): {Sword(TsCombo): None},

View File

@ -1,12 +1,17 @@
({ ({
General: [ General: [
General(HealthIncrease), General(HealthIncrease),
General(EnergyIncrease),
UnlockGroup(Weapon(Sword)), UnlockGroup(Weapon(Sword)),
UnlockGroup(Weapon(Axe)), UnlockGroup(Weapon(Axe)),
UnlockGroup(Weapon(Hammer)), UnlockGroup(Weapon(Hammer)),
UnlockGroup(Weapon(Bow)), UnlockGroup(Weapon(Bow)),
UnlockGroup(Weapon(Staff)), UnlockGroup(Weapon(Staff)),
UnlockGroup(Weapon(Sceptre)), UnlockGroup(Weapon(Sceptre)),
Roll(ImmuneMelee),
Roll(Cost),
Roll(Strength),
Roll(Duration),
], ],
Weapon(Sword): [ Weapon(Sword): [
Sword(InterruptingAttacks), Sword(InterruptingAttacks),

View File

@ -1385,6 +1385,29 @@ impl Client {
GeneralSkill::HealthIncrease, GeneralSkill::HealthIncrease,
))); )));
}, },
"@unlock energy" => {
self.send_msg(ClientGeneral::UnlockSkill(Skill::General(
GeneralSkill::EnergyIncrease,
)));
},
"@unlock roll melee" => {
self.send_msg(ClientGeneral::UnlockSkill(Skill::Roll(
RollSkill::ImmuneMelee,
)));
},
"@unlock roll cost" => {
self.send_msg(ClientGeneral::UnlockSkill(Skill::Roll(RollSkill::Cost)));
},
"@unlock roll strength" => {
self.send_msg(ClientGeneral::UnlockSkill(Skill::Roll(
RollSkill::Strength,
)));
},
"@unlock roll duration" => {
self.send_msg(ClientGeneral::UnlockSkill(Skill::Roll(
RollSkill::Duration,
)));
},
_ => {}, _ => {},
} }
} else { } else {

View File

@ -120,6 +120,7 @@ pub enum CharacterAbility {
movement_duration: u64, movement_duration: u64,
recover_duration: u64, recover_duration: u64,
roll_strength: f32, roll_strength: f32,
immune_melee: bool,
}, },
ComboMelee { ComboMelee {
stage_data: Vec<combo_melee::Stage<u64>>, stage_data: Vec<combo_melee::Stage<u64>>,
@ -305,11 +306,12 @@ impl CharacterAbility {
pub fn default_roll() -> CharacterAbility { pub fn default_roll() -> CharacterAbility {
CharacterAbility::Roll { CharacterAbility::Roll {
energy_cost: 100, energy_cost: 150,
buildup_duration: 100, buildup_duration: 100,
movement_duration: 250, movement_duration: 250,
recover_duration: 150, recover_duration: 150,
roll_strength: 2.5, roll_strength: 2.5,
immune_melee: false,
} }
} }
@ -502,11 +504,10 @@ impl CharacterAbility {
skills: &HashMap<skills::Skill, skills::Level>, skills: &HashMap<skills::Skill, skills::Level>,
tool: Option<ToolKind>, tool: Option<ToolKind>,
) -> Self { ) -> Self {
use skills::Skill::*; use skills::Skill::{self, *};
use CharacterAbility::*; use CharacterAbility::*;
if let Some(tool) = tool {
match tool { match tool {
ToolKind::Sword => { Some(ToolKind::Sword) => {
use skills::SwordSkill::*; use skills::SwordSkill::*;
match self { match self {
ComboMelee { ComboMelee {
@ -534,9 +535,8 @@ impl CharacterAbility {
*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 = if let Some(level) = let energy_level =
skills.get(&Sword(TsRegen)).copied().flatten() if let Some(level) = skills.get(&Sword(TsRegen)).copied().flatten() {
{
level level
} else { } else {
0 0
@ -609,14 +609,13 @@ impl CharacterAbility {
*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 = skills.get(&Sword(SSpins)).copied().flatten().unwrap_or(0) *num_spins =
as u32 skills.get(&Sword(SSpins)).copied().flatten().unwrap_or(0) as u32 + 1;
+ 1;
}, },
_ => {}, _ => {},
} }
}, },
ToolKind::Axe => { Some(ToolKind::Axe) => {
use skills::AxeSkill::*; use skills::AxeSkill::*;
match self { match self {
ComboMelee { ComboMelee {
@ -708,7 +707,7 @@ impl CharacterAbility {
_ => {}, _ => {},
} }
}, },
ToolKind::Hammer => { Some(ToolKind::Hammer) => {
use skills::HammerSkill::*; use skills::HammerSkill::*;
match self { match self {
ComboMelee { ComboMelee {
@ -719,24 +718,21 @@ impl CharacterAbility {
ref mut scales_from_combo, ref mut scales_from_combo,
.. ..
} => { } => {
if let Some(level) = skills.get(&Hammer(SsKnockback)).copied().flatten() if let Some(level) = skills.get(&Hammer(SsKnockback)).copied().flatten() {
{
*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 = let speed_segments = Hammer(SsSpeed).get_max_level().unwrap_or(1) as f32;
Hammer(SsSpeed).get_max_level().unwrap_or(1) as f32;
let speed_level = let speed_level =
skills.get(&Hammer(SsSpeed)).copied().flatten().unwrap_or(0) as f32; skills.get(&Hammer(SsSpeed)).copied().flatten().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 = if let Some(level) = let energy_level =
skills.get(&Hammer(SsRegen)).copied().flatten() if let Some(level) = skills.get(&Hammer(SsRegen)).copied().flatten() {
{
level level
} else { } else {
0 0
@ -746,8 +742,7 @@ impl CharacterAbility {
* ((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).get_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 = skills *scales_from_combo = skills
.get(&Hammer(SsDamage)) .get(&Hammer(SsDamage))
@ -767,8 +762,7 @@ impl CharacterAbility {
*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 Some(level) = skills.get(&Hammer(CKnockback)).copied().flatten() {
{
*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 Some(level) = skills.get(&Hammer(CDrain)).copied().flatten() {
@ -792,8 +786,7 @@ impl CharacterAbility {
*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 Some(level) = skills.get(&Hammer(LKnockback)).copied().flatten() {
{
*knockback *= 1.4_f32.powi(level.into()); *knockback *= 1.4_f32.powi(level.into());
} }
if let Some(level) = skills.get(&Hammer(LCost)).copied().flatten() { if let Some(level) = skills.get(&Hammer(LCost)).copied().flatten() {
@ -811,7 +804,7 @@ impl CharacterAbility {
_ => {}, _ => {},
} }
}, },
ToolKind::Bow => { Some(ToolKind::Bow) => {
use skills::BowSkill::*; use skills::BowSkill::*;
match self { match self {
BasicRanged { BasicRanged {
@ -897,7 +890,7 @@ impl CharacterAbility {
_ => {}, _ => {},
} }
}, },
ToolKind::Staff => { Some(ToolKind::Staff) => {
use skills::StaffSkill::*; use skills::StaffSkill::*;
match self { match self {
BasicRanged { BasicRanged {
@ -960,9 +953,8 @@ impl CharacterAbility {
*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 Some(level) = skills.get(&Staff(SRange)).copied().flatten() {
*shockwave_duration = (*shockwave_duration as f32 *shockwave_duration =
* 1.2_f32.powi(level.into())) (*shockwave_duration as f32 * 1.2_f32.powi(level.into())) as u64;
as u64;
} }
if let Some(level) = skills.get(&Staff(SCost)).copied().flatten() { if let Some(level) = skills.get(&Staff(SCost)).copied().flatten() {
*energy_cost = *energy_cost =
@ -972,7 +964,7 @@ impl CharacterAbility {
_ => {}, _ => {},
} }
}, },
ToolKind::Sceptre => { Some(ToolKind::Sceptre) => {
use skills::SceptreSkill::*; use skills::SceptreSkill::*;
match self { match self {
BasicBeam { BasicBeam {
@ -997,8 +989,7 @@ impl CharacterAbility {
// 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 Some(level) = skills.get(&Sceptre(BLifesteal)).copied().flatten() {
{
*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 Some(level) = skills.get(&Sceptre(BRegen)).copied().flatten() {
@ -1032,23 +1023,43 @@ impl CharacterAbility {
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 = projectile.modified_projectile(power, 1_f32, range, heal);
projectile.modified_projectile(power, 1_f32, range, heal);
} }
if let Some(level) = skills.get(&Sceptre(PCost)).copied().flatten() { if let Some(level) = skills.get(&Sceptre(PCost)).copied().flatten() {
*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 Some(level) = skills.get(&Sceptre(PProjSpeed)).copied().flatten() {
{
*projectile_speed *= 1.25_f32.powi(level.into()); *projectile_speed *= 1.25_f32.powi(level.into());
} }
}, },
_ => {}, _ => {},
} }
}, },
_ => {}, None => {
use skills::RollSkill::*;
if let CharacterAbility::Roll {
ref mut immune_melee,
ref mut energy_cost,
ref mut roll_strength,
ref mut movement_duration,
..
} = self
{
*immune_melee = skills.contains_key(&Skill::Roll(ImmuneMelee));
if let Some(level) = skills.get(&Skill::Roll(Cost)).copied().flatten() {
*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() {
*roll_strength *= 1.3_f32.powi(level.into());
}
if let Some(level) = skills.get(&Skill::Roll(Duration)).copied().flatten() {
*movement_duration =
(*movement_duration as f32 * 2_f32.powi(level.into())) as u64;
}
}
},
_ => {},
} }
self self
} }
@ -1165,12 +1176,14 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState {
movement_duration, movement_duration,
recover_duration, recover_duration,
roll_strength, roll_strength,
immune_melee,
} => CharacterState::Roll(roll::Data { } => CharacterState::Roll(roll::Data {
static_data: roll::StaticData { static_data: roll::StaticData {
buildup_duration: Duration::from_millis(*buildup_duration), buildup_duration: Duration::from_millis(*buildup_duration),
movement_duration: Duration::from_millis(*movement_duration), movement_duration: Duration::from_millis(*movement_duration),
recover_duration: Duration::from_millis(*recover_duration), recover_duration: Duration::from_millis(*recover_duration),
roll_strength: *roll_strength, roll_strength: *roll_strength,
immune_melee: *immune_melee,
}, },
timer: Duration::default(), timer: Duration::default(),
stage_section: StageSection::Buildup, stage_section: StageSection::Buildup,

View File

@ -256,6 +256,7 @@ impl Body {
biped_large::Species::Dullahan => 4000, biped_large::Species::Dullahan => 4000,
_ => 3000, _ => 3000,
}, },
Body::Humanoid(_) => 750,
_ => 1000, _ => 1000,
} }
} }
@ -263,7 +264,7 @@ impl Body {
#[allow(unreachable_patterns)] #[allow(unreachable_patterns)]
pub fn base_health(&self) -> u32 { pub fn base_health(&self) -> u32 {
match self { match self {
Body::Humanoid(_) => 400, Body::Humanoid(_) => 500,
Body::QuadrupedSmall(quadruped_small) => match quadruped_small.species { Body::QuadrupedSmall(quadruped_small) => match quadruped_small.species {
quadruped_small::Species::Boar => 360, quadruped_small::Species::Boar => 360,
quadruped_small::Species::Batfox => 200, quadruped_small::Species::Batfox => 200,

View File

@ -143,6 +143,10 @@ impl CharacterState {
pub fn is_dodge(&self) -> bool { matches!(self, CharacterState::Roll(_)) } pub fn is_dodge(&self) -> bool { matches!(self, CharacterState::Roll(_)) }
pub fn is_melee_dodge(&self) -> bool {
matches!(self, CharacterState::Roll(d) if d.static_data.immune_melee)
}
/// Compares for shallow equality (does not check internal struct equality) /// Compares for shallow equality (does not check internal struct equality)
pub fn same_variant(&self, other: &Self) -> bool { pub fn same_variant(&self, other: &Self) -> bool {
// Check if state is the same without looking at the inner data // Check if state is the same without looking at the inner data

View File

@ -72,6 +72,7 @@ pub enum Skill {
Staff(StaffSkill), Staff(StaffSkill),
Sceptre(SceptreSkill), Sceptre(SceptreSkill),
UnlockGroup(SkillGroupType), UnlockGroup(SkillGroupType),
Roll(RollSkill),
} }
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
@ -202,6 +203,15 @@ pub enum SceptreSkill {
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum GeneralSkill { pub enum GeneralSkill {
HealthIncrease, HealthIncrease,
EnergyIncrease,
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum RollSkill {
ImmuneMelee,
Cost,
Strength,
Duration,
} }
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
@ -237,6 +247,8 @@ impl SkillGroup {
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, Level>,
pub modify_health: bool,
pub modify_energy: bool,
} }
pub type Level = Option<u16>; pub type Level = Option<u16>;
@ -249,6 +261,8 @@ impl Default for SkillSet {
Self { Self {
skill_groups: vec![SkillGroup::new(SkillGroupType::General)], skill_groups: vec![SkillGroup::new(SkillGroupType::General)],
skills: HashMap::new(), skills: HashMap::new(),
modify_health: false,
modify_energy: false,
} }
} }
} }
@ -312,6 +326,12 @@ impl SkillSet {
if let Skill::UnlockGroup(group) = skill { if let Skill::UnlockGroup(group) = skill {
self.unlock_skill_group(group); self.unlock_skill_group(group);
} }
if matches!(skill, Skill::General(GeneralSkill::HealthIncrease)) {
self.modify_health = true;
}
if matches!(skill, Skill::General(GeneralSkill::EnergyIncrease)) {
self.modify_energy = true;
}
self.skills.insert(skill, next_level); self.skills.insert(skill, next_level);
} else { } else {
warn!("Tried to unlock skill for skill group with insufficient SP"); warn!("Tried to unlock skill for skill group with insufficient SP");

View File

@ -19,6 +19,8 @@ pub struct StaticData {
pub recover_duration: Duration, pub recover_duration: Duration,
/// Affects the speed and distance of the roll /// Affects the speed and distance of the roll
pub roll_strength: f32, pub roll_strength: f32,
/// Affects whether you are immune to melee attacks while rolling
pub immune_melee: bool,
} }
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]

View File

@ -551,21 +551,26 @@ pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) {
if let Some(ability) = data if let Some(ability) = data
.inventory .inventory
.equipped(EquipSlot::Mainhand) .equipped(EquipSlot::Mainhand)
.and_then(|i| i.item_config_expect().dodge_ability.as_ref()) .and_then(|i| {
i.item_config_expect().dodge_ability.as_ref().map(|a| {
a.clone()
.adjusted_by_skills(&data.stats.skill_set.skills, None)
})
})
.filter(|ability| ability.requirements_paid(data, update)) .filter(|ability| ability.requirements_paid(data, update))
{ {
if data.character.is_wield() { if data.character.is_wield() {
update.character = (ability, AbilityKey::Dodge).into(); update.character = (&ability, AbilityKey::Dodge).into();
if let CharacterState::Roll(roll) = &mut update.character { if let CharacterState::Roll(roll) = &mut update.character {
roll.was_wielded = true; roll.was_wielded = true;
} }
} else if data.character.is_stealthy() { } else if data.character.is_stealthy() {
update.character = (ability, AbilityKey::Dodge).into(); update.character = (&ability, AbilityKey::Dodge).into();
if let CharacterState::Roll(roll) = &mut update.character { if let CharacterState::Roll(roll) = &mut update.character {
roll.was_sneak = true; roll.was_sneak = true;
} }
} else { } else {
update.character = (ability, AbilityKey::Dodge).into(); update.character = (&ability, AbilityKey::Dodge).into();
} }
} }
} }

View File

@ -95,7 +95,7 @@ impl<'a> System<'a> for Sys {
let rad_b = body_b.radius() * scale_b; let rad_b = body_b.radius() * scale_b;
// Check if entity is dodging // Check if entity is dodging
let is_dodge = char_state_b_maybe.map_or(false, |c_s| c_s.is_dodge()); let is_dodge = char_state_b_maybe.map_or(false, |c_s| c_s.is_melee_dodge());
// Check if it is a hit // Check if it is a hit
if entity != b if entity != b

View File

@ -1,6 +1,7 @@
use common::{ use common::{
comp::{ comp::{
skills::SkillGroupType, CharacterState, Energy, EnergyChange, EnergySource, Health, Stats, skills::{GeneralSkill, Skill, SkillGroupType},
CharacterState, Energy, EnergyChange, EnergySource, Health, Stats,
}, },
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
metrics::SysMetrics, metrics::SysMetrics,
@ -93,6 +94,45 @@ impl<'a> System<'a> for Sys {
} }
} }
// Apply effects from leveling skills
for (mut stats, mut health, mut energy) in (
&mut stats.restrict_mut(),
&mut healths.restrict_mut(),
&mut energies.restrict_mut(),
)
.join()
{
let stat = stats.get_unchecked();
if stat.skill_set.modify_health {
let mut health = health.get_mut_unchecked();
let health_level = stat
.skill_set
.skills
.get(&Skill::General(GeneralSkill::HealthIncrease))
.copied()
.flatten()
.unwrap_or(0);
health.update_max_hp(Some(stat.body_type), health_level.into());
let mut stat = stats.get_mut_unchecked();
stat.skill_set.modify_health = false;
}
let stat = stats.get_unchecked();
if stat.skill_set.modify_energy {
let mut energy = energy.get_mut_unchecked();
let energy_level = stat
.skill_set
.skills
.get(&Skill::General(GeneralSkill::EnergyIncrease))
.copied()
.flatten()
.unwrap_or(0) as u32;
let energy_max = stat.body_type.base_energy() + 50 * energy_level;
energy.set_maximum(energy_max);
let mut stat = stats.get_mut_unchecked();
stat.skill_set.modify_energy = false;
}
}
// Update energies // Update energies
for (character_state, mut energy) in for (character_state, mut energy) in
(&character_states, &mut energies.restrict_mut()).join() (&character_states, &mut energies.restrict_mut()).join()

View File

@ -366,6 +366,8 @@ pub fn convert_stats_from_database(
new_stats.skill_set = skills::SkillSet { new_stats.skill_set = skills::SkillSet {
skill_groups: convert_skill_groups_from_database(skill_groups), skill_groups: convert_skill_groups_from_database(skill_groups),
skills: convert_skills_from_database(skills), skills: convert_skills_from_database(skills),
modify_health: true,
modify_energy: true,
}; };
new_stats new_stats

View File

@ -124,16 +124,8 @@ impl<'a> Widget for BuffsBar<'a> {
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.desc_text_color(TEXT_COLOR); .desc_text_color(TEXT_COLOR);
if let BuffPosition::Bar = buff_position { if let BuffPosition::Bar = buff_position {
let show_health = if self.health.current() != self.health.maximum() { let show_health = self.health.current() != self.health.maximum();
true let show_stamina = self.energy.current() != self.energy.maximum();
} else {
false
};
let show_stamina = if self.energy.current() != self.energy.maximum() {
true
} else {
false
};
let offset = if show_health && show_stamina { let offset = if show_health && show_stamina {
140.0 140.0
} else if show_health || show_stamina { } else if show_health || show_stamina {

View File

@ -14,7 +14,10 @@ use conrod_core::{
}; };
use client::{self, Client}; use client::{self, Client};
use common::comp::skills::{self, Skill}; use common::comp::{
item::tool::ToolKind,
skills::{self, Skill},
};
use inline_tweak::*; use inline_tweak::*;
widget_ids! { widget_ids! {
@ -123,6 +126,19 @@ widget_ids! {
skill_sceptre_bomb_2, skill_sceptre_bomb_2,
skill_sceptre_bomb_3, skill_sceptre_bomb_3,
skill_sceptre_bomb_4, skill_sceptre_bomb_4,
general_combat_render,
skill_general_stat_0,
skill_general_stat_1,
skill_general_tree_0,
skill_general_tree_1,
skill_general_tree_2,
skill_general_tree_3,
skill_general_tree_4,
skill_general_tree_5,
skill_general_roll_0,
skill_general_roll_1,
skill_general_roll_2,
skill_general_roll_3,
} }
} }
@ -194,9 +210,18 @@ pub enum SelectedSkillTree {
Sceptre, Sceptre,
Bow, Bow,
StaffFire, StaffFire,
GeneralCombat,
} }
const WEAPONS: [&str; 6] = ["Sword", "Hammer", "Axe", "Sceptre", "Bow", "Fire Staff"]; const WEAPONS: [&str; 7] = [
"General Combat",
"Sword",
"Hammer",
"Axe",
"Sceptre",
"Bow",
"Fire Staff",
];
pub enum Event { pub enum Event {
Close, Close,
@ -298,6 +323,7 @@ impl<'a> Widget for Diary<'a> {
for i in WEAPONS.iter().copied().enumerate() { for i in WEAPONS.iter().copied().enumerate() {
// Background weapon image // Background weapon image
let img = Image::new(match i.1 { let img = Image::new(match i.1 {
"General Combat" => self.imgs.not_found,
"Sword" => self.imgs.sword, "Sword" => self.imgs.sword,
"Hammer" => self.imgs.hammer, "Hammer" => self.imgs.hammer,
"Axe" => self.imgs.axe, "Axe" => self.imgs.axe,
@ -317,6 +343,10 @@ impl<'a> Widget for Diary<'a> {
.set(state.weapon_imgs[i.0], ui); .set(state.weapon_imgs[i.0], ui);
// Weapon icons // Weapon icons
if Button::image(match i.1 { if Button::image(match i.1 {
"General Combat" => match sel_tab {
SelectedSkillTree::GeneralCombat => self.imgs.wpn_icon_border_pressed,
_ => self.imgs.wpn_icon_border,
},
"Sword" => match sel_tab { "Sword" => match sel_tab {
SelectedSkillTree::Sword => self.imgs.wpn_icon_border_pressed, SelectedSkillTree::Sword => self.imgs.wpn_icon_border_pressed,
_ => self.imgs.wpn_icon_border, _ => self.imgs.wpn_icon_border,
@ -345,6 +375,10 @@ impl<'a> Widget for Diary<'a> {
}) })
.w_h(tweak!(50.0), tweak!(50.0)) .w_h(tweak!(50.0), tweak!(50.0))
.hover_image(match i.1 { .hover_image(match i.1 {
"General Combat" => match sel_tab {
SelectedSkillTree::GeneralCombat => self.imgs.wpn_icon_border_pressed,
_ => self.imgs.wpn_icon_border_mo,
},
"Sword" => match sel_tab { "Sword" => match sel_tab {
SelectedSkillTree::Sword => self.imgs.wpn_icon_border_pressed, SelectedSkillTree::Sword => self.imgs.wpn_icon_border_pressed,
_ => self.imgs.wpn_icon_border_mo, _ => self.imgs.wpn_icon_border_mo,
@ -372,6 +406,10 @@ impl<'a> Widget for Diary<'a> {
_ => self.imgs.wpn_icon_border, _ => self.imgs.wpn_icon_border,
}) })
.press_image(match i.1 { .press_image(match i.1 {
"General Combat" => match sel_tab {
SelectedSkillTree::GeneralCombat => self.imgs.wpn_icon_border_pressed,
_ => self.imgs.wpn_icon_border_press,
},
"Sword" => match sel_tab { "Sword" => match sel_tab {
SelectedSkillTree::Sword => self.imgs.wpn_icon_border_pressed, SelectedSkillTree::Sword => self.imgs.wpn_icon_border_pressed,
_ => self.imgs.wpn_icon_border_press, _ => self.imgs.wpn_icon_border_press,
@ -403,6 +441,9 @@ impl<'a> Widget for Diary<'a> {
.was_clicked() .was_clicked()
{ {
match i.1 { match i.1 {
"General Combat" => {
events.push(Event::ChangeWeaponTree(SelectedSkillTree::GeneralCombat))
},
"Sword" => events.push(Event::ChangeWeaponTree(SelectedSkillTree::Sword)), "Sword" => events.push(Event::ChangeWeaponTree(SelectedSkillTree::Sword)),
"Hammer" => events.push(Event::ChangeWeaponTree(SelectedSkillTree::Hammer)), "Hammer" => events.push(Event::ChangeWeaponTree(SelectedSkillTree::Hammer)),
"Axe" => events.push(Event::ChangeWeaponTree(SelectedSkillTree::Axe)), "Axe" => events.push(Event::ChangeWeaponTree(SelectedSkillTree::Axe)),
@ -436,6 +477,7 @@ impl<'a> Widget for Diary<'a> {
// Number of skills per rectangle per weapon, start counting at 0 // Number of skills per rectangle per weapon, start counting at 0
// Maximum of 9 skills/8 indices // Maximum of 9 skills/8 indices
let skills_top_l = match sel_tab { let skills_top_l = match sel_tab {
SelectedSkillTree::GeneralCombat => 2,
SelectedSkillTree::Sword => 4, SelectedSkillTree::Sword => 4,
SelectedSkillTree::Axe => 4, SelectedSkillTree::Axe => 4,
SelectedSkillTree::Hammer => 4, SelectedSkillTree::Hammer => 4,
@ -445,6 +487,7 @@ impl<'a> Widget for Diary<'a> {
_ => 0, _ => 0,
}; };
let skills_top_r = match sel_tab { let skills_top_r = match sel_tab {
SelectedSkillTree::GeneralCombat => 6,
SelectedSkillTree::Sword => 6, SelectedSkillTree::Sword => 6,
SelectedSkillTree::Axe => 5, SelectedSkillTree::Axe => 5,
SelectedSkillTree::Hammer => 4, SelectedSkillTree::Hammer => 4,
@ -454,6 +497,7 @@ impl<'a> Widget for Diary<'a> {
_ => 0, _ => 0,
}; };
let skills_bot_l = match sel_tab { let skills_bot_l = match sel_tab {
SelectedSkillTree::GeneralCombat => 4,
SelectedSkillTree::Sword => 5, SelectedSkillTree::Sword => 5,
SelectedSkillTree::Axe => 5, SelectedSkillTree::Axe => 5,
SelectedSkillTree::Hammer => 6, SelectedSkillTree::Hammer => 6,
@ -565,14 +609,283 @@ impl<'a> Widget for Diary<'a> {
} }
// Skill-Icons and Functionality // Skill-Icons and Functionality
// Art dimensions // Art dimensions
let art_size = [tweak!(490.0), tweak!(490.0)]; let art_size = [tweak!(320.0), tweak!(320.0)];
match sel_tab { match sel_tab {
SelectedSkillTree::GeneralCombat => {
use skills::{GeneralSkill::*, RollSkill::*, SkillGroupType::*};
use ToolKind::*;
// General Combat
Image::new(self.imgs.not_found)
.wh(art_size)
.middle_of(state.content_align)
.graphics_for(state.content_align)
.color(Some(Color::Rgba(1.0, 1.0, 1.0, tweak!(1.0))))
.set(state.general_combat_render, ui);
// Top Left skills
// 5 1 6
// 3 0 4
// 8 2 7
if Button::image(self.imgs.not_found)
.w_h(tweak!(74.0), tweak!(74.0))
.middle_of(state.skills_top_l[0])
.label(&self.example_skill_count.to_string())
.label_y(conrod_core::position::Relative::Scalar(tweak!(-28.0)))
.label_x(conrod_core::position::Relative::Scalar(tweak!(32.0)))
.label_color(TEXT_COLOR)
.label_font_size(self.fonts.cyri.scale(tweak!(16)))
.label_font_id(self.fonts.cyri.conrod_id)
.with_tooltip(
self.tooltip_manager,
"Increase Health",
"Increases health",
&diary_tooltip,
TEXT_COLOR,
)
.set(state.skill_general_stat_0, ui)
.was_clicked()
{
events.push(Event::UnlockSkill(Skill::General(HealthIncrease)));
};
if Button::image(self.imgs.not_found)
.w_h(tweak!(74.0), tweak!(74.0))
.middle_of(state.skills_top_l[1])
.label(&self.example_skill_count.to_string())
.label_y(conrod_core::position::Relative::Scalar(tweak!(-28.0)))
.label_x(conrod_core::position::Relative::Scalar(tweak!(32.0)))
.label_color(TEXT_COLOR)
.label_font_size(self.fonts.cyri.scale(tweak!(16)))
.label_font_id(self.fonts.cyri.conrod_id)
.with_tooltip(
self.tooltip_manager,
"Increase Energy",
"Increases energy",
&diary_tooltip,
TEXT_COLOR,
)
.set(state.skill_general_stat_1, ui)
.was_clicked()
{
events.push(Event::UnlockSkill(Skill::General(EnergyIncrease)));
};
// Top right skills
if Button::image(self.imgs.not_found)
.w_h(tweak!(74.0), tweak!(74.0))
.middle_of(state.skills_top_r[0])
.label(&self.example_skill_count.to_string())
.label_y(conrod_core::position::Relative::Scalar(tweak!(-28.0)))
.label_x(conrod_core::position::Relative::Scalar(tweak!(32.0)))
.label_color(TEXT_COLOR)
.label_font_size(self.fonts.cyri.scale(tweak!(16)))
.label_font_id(self.fonts.cyri.conrod_id)
.with_tooltip(
self.tooltip_manager,
"Unlock Sword",
"Unlocks sword skill tree",
&diary_tooltip,
TEXT_COLOR,
)
.set(state.skill_general_tree_0, ui)
.was_clicked()
{
events.push(Event::UnlockSkill(Skill::UnlockGroup(Weapon(Sword))));
};
if Button::image(self.imgs.not_found)
.w_h(tweak!(74.0), tweak!(74.0))
.middle_of(state.skills_top_r[1])
.label(&self.example_skill_count.to_string())
.label_y(conrod_core::position::Relative::Scalar(tweak!(-28.0)))
.label_x(conrod_core::position::Relative::Scalar(tweak!(32.0)))
.label_color(TEXT_COLOR)
.label_font_size(self.fonts.cyri.scale(tweak!(16)))
.label_font_id(self.fonts.cyri.conrod_id)
.with_tooltip(
self.tooltip_manager,
"Unlock Axe",
"Unlocks axe skill tree",
&diary_tooltip,
TEXT_COLOR,
)
.set(state.skill_general_tree_1, ui)
.was_clicked()
{
events.push(Event::UnlockSkill(Skill::UnlockGroup(Weapon(Axe))));
};
if Button::image(self.imgs.not_found)
.w_h(tweak!(74.0), tweak!(74.0))
.middle_of(state.skills_top_r[2])
.label(&self.example_skill_count.to_string())
.label_y(conrod_core::position::Relative::Scalar(tweak!(-28.0)))
.label_x(conrod_core::position::Relative::Scalar(tweak!(32.0)))
.label_color(TEXT_COLOR)
.label_font_size(self.fonts.cyri.scale(tweak!(16)))
.label_font_id(self.fonts.cyri.conrod_id)
.with_tooltip(
self.tooltip_manager,
"Unlock Hammer",
"Unlocks hammer skill tree",
&diary_tooltip,
TEXT_COLOR,
)
.set(state.skill_general_tree_2, ui)
.was_clicked()
{
events.push(Event::UnlockSkill(Skill::UnlockGroup(Weapon(Hammer))));
};
if Button::image(self.imgs.not_found)
.w_h(tweak!(74.0), tweak!(74.0))
.middle_of(state.skills_top_r[3])
.label(&self.example_skill_count.to_string())
.label_y(conrod_core::position::Relative::Scalar(tweak!(-28.0)))
.label_x(conrod_core::position::Relative::Scalar(tweak!(32.0)))
.label_color(TEXT_COLOR)
.label_font_size(self.fonts.cyri.scale(tweak!(16)))
.label_font_id(self.fonts.cyri.conrod_id)
.with_tooltip(
self.tooltip_manager,
"Unlock Bow",
"Unlocks bow skill tree",
&diary_tooltip,
TEXT_COLOR,
)
.set(state.skill_general_tree_3, ui)
.was_clicked()
{
events.push(Event::UnlockSkill(Skill::UnlockGroup(Weapon(Bow))));
};
if Button::image(self.imgs.not_found)
.w_h(tweak!(74.0), tweak!(74.0))
.middle_of(state.skills_top_r[4])
.label(&self.example_skill_count.to_string())
.label_y(conrod_core::position::Relative::Scalar(tweak!(-28.0)))
.label_x(conrod_core::position::Relative::Scalar(tweak!(32.0)))
.label_color(TEXT_COLOR)
.label_font_size(self.fonts.cyri.scale(tweak!(16)))
.label_font_id(self.fonts.cyri.conrod_id)
.with_tooltip(
self.tooltip_manager,
"Unlock Staff",
"Unlocks staff skill tree",
&diary_tooltip,
TEXT_COLOR,
)
.set(state.skill_general_tree_4, ui)
.was_clicked()
{
events.push(Event::UnlockSkill(Skill::UnlockGroup(Weapon(Staff))));
};
if Button::image(self.imgs.not_found)
.w_h(tweak!(74.0), tweak!(74.0))
.middle_of(state.skills_top_r[5])
.label(&self.example_skill_count.to_string())
.label_y(conrod_core::position::Relative::Scalar(tweak!(-28.0)))
.label_x(conrod_core::position::Relative::Scalar(tweak!(32.0)))
.label_color(TEXT_COLOR)
.label_font_size(self.fonts.cyri.scale(tweak!(16)))
.label_font_id(self.fonts.cyri.conrod_id)
.with_tooltip(
self.tooltip_manager,
"Unlock Sceptre",
"Unlocks sceptre skill tree",
&diary_tooltip,
TEXT_COLOR,
)
.set(state.skill_general_tree_5, ui)
.was_clicked()
{
events.push(Event::UnlockSkill(Skill::UnlockGroup(Weapon(Sceptre))));
};
// Bottom left skills
if Button::image(self.imgs.not_found)
.w_h(tweak!(74.0), tweak!(74.0))
.middle_of(state.skills_bot_l[0])
.label(&self.example_skill_count.to_string())
.label_y(conrod_core::position::Relative::Scalar(tweak!(-28.0)))
.label_x(conrod_core::position::Relative::Scalar(tweak!(32.0)))
.label_color(TEXT_COLOR)
.label_font_size(self.fonts.cyri.scale(tweak!(16)))
.label_font_id(self.fonts.cyri.conrod_id)
.with_tooltip(
self.tooltip_manager,
"Dodge",
"Ground-yeeting dodges melee attacks",
&diary_tooltip,
TEXT_COLOR,
)
.set(state.skill_general_roll_0, ui)
.was_clicked()
{
events.push(Event::UnlockSkill(Skill::Roll(ImmuneMelee)));
};
if Button::image(self.imgs.not_found)
.w_h(tweak!(74.0), tweak!(74.0))
.middle_of(state.skills_bot_l[1])
.label(&self.example_skill_count.to_string())
.label_y(conrod_core::position::Relative::Scalar(tweak!(-28.0)))
.label_x(conrod_core::position::Relative::Scalar(tweak!(32.0)))
.label_color(TEXT_COLOR)
.label_font_size(self.fonts.cyri.scale(tweak!(16)))
.label_font_id(self.fonts.cyri.conrod_id)
.with_tooltip(
self.tooltip_manager,
"Cost",
"Decreases cost of ground-yeeting yourself",
&diary_tooltip,
TEXT_COLOR,
)
.set(state.skill_general_roll_1, ui)
.was_clicked()
{
events.push(Event::UnlockSkill(Skill::Roll(Cost)));
};
if Button::image(self.imgs.not_found)
.w_h(tweak!(74.0), tweak!(74.0))
.middle_of(state.skills_bot_l[2])
.label(&self.example_skill_count.to_string())
.label_y(conrod_core::position::Relative::Scalar(tweak!(-28.0)))
.label_x(conrod_core::position::Relative::Scalar(tweak!(32.0)))
.label_color(TEXT_COLOR)
.label_font_size(self.fonts.cyri.scale(tweak!(16)))
.label_font_id(self.fonts.cyri.conrod_id)
.with_tooltip(
self.tooltip_manager,
"Strength",
"Increases how far you ground-yeet yourself",
&diary_tooltip,
TEXT_COLOR,
)
.set(state.skill_general_roll_2, ui)
.was_clicked()
{
events.push(Event::UnlockSkill(Skill::Roll(Strength)));
};
if Button::image(self.imgs.not_found)
.w_h(tweak!(74.0), tweak!(74.0))
.middle_of(state.skills_bot_l[3])
.label(&self.example_skill_count.to_string())
.label_y(conrod_core::position::Relative::Scalar(tweak!(-28.0)))
.label_x(conrod_core::position::Relative::Scalar(tweak!(32.0)))
.label_color(TEXT_COLOR)
.label_font_size(self.fonts.cyri.scale(tweak!(16)))
.label_font_id(self.fonts.cyri.conrod_id)
.with_tooltip(
self.tooltip_manager,
"Duration",
"Increases for how long you ground-yeet yourself",
&diary_tooltip,
TEXT_COLOR,
)
.set(state.skill_general_roll_3, ui)
.was_clicked()
{
events.push(Event::UnlockSkill(Skill::Roll(Duration)));
};
},
SelectedSkillTree::Sword => { SelectedSkillTree::Sword => {
use skills::SwordSkill::*; use skills::SwordSkill::*;
// Sword // Sword
Image::new( Image::new(
self.item_imgs self.item_imgs
.img_id_or_not_found_img(Tool("example_sword".to_string()).clone()), .img_id_or_not_found_img(Tool("example_sword".to_string())),
) )
.wh(art_size) .wh(art_size)
.middle_of(state.content_align) .middle_of(state.content_align)
@ -928,7 +1241,7 @@ impl<'a> Widget for Diary<'a> {
// Axe // Axe
Image::new( Image::new(
self.item_imgs self.item_imgs
.img_id_or_not_found_img(Tool("example_axe".to_string()).clone()), .img_id_or_not_found_img(Tool("example_axe".to_string())),
) )
.wh(art_size) .wh(art_size)
.middle_of(state.content_align) .middle_of(state.content_align)
@ -1241,7 +1554,7 @@ impl<'a> Widget for Diary<'a> {
// Hammer // Hammer
Image::new( Image::new(
self.item_imgs self.item_imgs
.img_id_or_not_found_img(Tool("example_hammer".to_string()).clone()), .img_id_or_not_found_img(Tool("example_hammer".to_string())),
) )
.wh(art_size) .wh(art_size)
.middle_of(state.content_align) .middle_of(state.content_align)
@ -1554,7 +1867,7 @@ impl<'a> Widget for Diary<'a> {
// Bow // Bow
Image::new( Image::new(
self.item_imgs self.item_imgs
.img_id_or_not_found_img(Tool("example_bow".to_string()).clone()), .img_id_or_not_found_img(Tool("example_bow".to_string())),
) )
.wh(art_size) .wh(art_size)
.middle_of(state.content_align) .middle_of(state.content_align)
@ -1867,7 +2180,7 @@ impl<'a> Widget for Diary<'a> {
// Staff // Staff
Image::new( Image::new(
self.item_imgs self.item_imgs
.img_id_or_not_found_img(Tool("example_staff_fire".to_string()).clone()), .img_id_or_not_found_img(Tool("example_staff_fire".to_string())),
) )
.wh(art_size) .wh(art_size)
.middle_of(state.content_align) .middle_of(state.content_align)
@ -2159,7 +2472,7 @@ impl<'a> Widget for Diary<'a> {
// Sceptre // Sceptre
Image::new( Image::new(
self.item_imgs self.item_imgs
.img_id_or_not_found_img(Tool("example_sceptre".to_string()).clone()), .img_id_or_not_found_img(Tool("example_sceptre".to_string())),
) )
.wh(art_size) .wh(art_size)
.middle_of(state.content_align) .middle_of(state.content_align)

View File

@ -757,7 +757,7 @@ impl Hud {
group_menu: false, group_menu: false,
mini_map: true, mini_map: true,
settings_tab: SettingsTab::Interface, settings_tab: SettingsTab::Interface,
skilltreetab: SelectedSkillTree::Sword, skilltreetab: SelectedSkillTree::GeneralCombat,
social_tab: SocialTab::Online, social_tab: SocialTab::Online,
want_grab: true, want_grab: true,
ingame: true, ingame: true,

View File

@ -351,16 +351,8 @@ impl<'a> Widget for Skillbar<'a> {
.mid_bottom_with_margin_on(ui.window, 10.0) .mid_bottom_with_margin_on(ui.window, 10.0)
.set(state.ids.frame, ui); .set(state.ids.frame, ui);
// Health and Stamina bar // Health and Stamina bar
let show_health = if self.health.current() != self.health.maximum() { let show_health = self.health.current() != self.health.maximum();
true let show_stamina = self.energy.current() != self.energy.maximum();
} else {
false
};
let show_stamina = if self.energy.current() != self.energy.maximum() {
true
} else {
false
};
if show_health && !self.health.is_dead { if show_health && !self.health.is_dead {
let offset = if show_stamina { let offset = if show_stamina {
@ -424,7 +416,12 @@ impl<'a> Widget for Skillbar<'a> {
* living players */ * living players */
(self.health.maximum() / 10) as u32 (self.health.maximum() / 10) as u32
); );
let mut energy_txt = format!("{}", energy_percentage as u32); let mut energy_txt = format!(
"{}/{}",
(self.energy.current() / 10) as u32,
(self.energy.maximum() / 10) as u32
);
//let mut energy_txt = format!("{}", energy_percentage as u32);
if self.health.is_dead { if self.health.is_dead {
hp_txt = self.localized_strings.get("hud.group.dead").to_string(); hp_txt = self.localized_strings.get("hud.group.dead").to_string();
energy_txt = self.localized_strings.get("hud.group.dead").to_string(); energy_txt = self.localized_strings.get("hud.group.dead").to_string();