diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index 277e5a7f42..a136ffbd82 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -23,10 +23,10 @@ ], ), Tool(Bow): ( - primary: "common.abilities.bow.basic", - secondary: "common.abilities.bow.charged", + primary: "common.abilities.bow.charged", + secondary: "common.abilities.bow.repeater", abilities: [ - (Some(Bow(UnlockRepeater)), "common.abilities.bow.repeater"), + (Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"), ], ), Custom("Husk"): ( diff --git a/assets/common/abilities/bow/charged.ron b/assets/common/abilities/bow/charged.ron index 482c17396b..b8469ad2c8 100644 --- a/assets/common/abilities/bow/charged.ron +++ b/assets/common/abilities/bow/charged.ron @@ -1,18 +1,19 @@ ChargedRanged( - energy_cost: 1, - energy_drain: 300, - initial_damage: 10, - scaled_damage: 190, - initial_knockback: 10.0, + energy_cost: 0, + energy_drain: 0, + initial_regen: 50, + scaled_regen: 150, + initial_damage: 50, + scaled_damage: 200, + initial_knockback: 0.0, scaled_knockback: 10.0, speed: 1.0, - buildup_duration: 0.1, - charge_duration: 1.5, - recover_duration: 0.5, - projectile_body: Object(MultiArrow), + buildup_duration: 0.2, + charge_duration: 2.0, + recover_duration: 0.3, + projectile_body: Object(Arrow), projectile_light: None, - initial_projectile_speed: 120.0, - scaled_projectile_speed: 160.0, - move_speed: 0.3, - damage_kind: Piercing, + initial_projectile_speed: 100.0, + scaled_projectile_speed: 150.0, + move_speed: 0.6, ) diff --git a/assets/common/abilities/bow/repeater.ron b/assets/common/abilities/bow/repeater.ron index b0535b23e6..ae98a6466a 100644 --- a/assets/common/abilities/bow/repeater.ron +++ b/assets/common/abilities/bow/repeater.ron @@ -1,10 +1,10 @@ RepeaterRanged( - energy_cost: 450, - movement_duration: 0.3, + energy_cost: 50, buildup_duration: 0.2, - shoot_duration: 0.2, + shoot_duration: 1.0, recover_duration: 0.8, - leap: Some(5.0), + max_speed: 5.0, + half_speed_at: 4, projectile: Arrow( damage: 40.0, knockback: 5.0, @@ -12,6 +12,5 @@ RepeaterRanged( ), projectile_body: Object(Arrow), projectile_light: None, - projectile_speed: 120.0, - reps_remaining: 3, + projectile_speed: 100.0, ) diff --git a/assets/common/abilities/bow/basic.ron b/assets/common/abilities/bow/shotgun.ron similarity index 55% rename from assets/common/abilities/bow/basic.ron rename to assets/common/abilities/bow/shotgun.ron index d86b0d684f..9911275b59 100644 --- a/assets/common/abilities/bow/basic.ron +++ b/assets/common/abilities/bow/shotgun.ron @@ -1,13 +1,15 @@ BasicRanged( - energy_cost: 0, + energy_cost: 450, buildup_duration: 0.4, recover_duration: 0.3, + projectile_spread: 0.05, projectile: Arrow( - damage: 90.0, + damage: 40.0, knockback: 5.0, - energy_regen: 40, + energy_regen: 0, ), projectile_body: Object(Arrow), projectile_light: None, - projectile_speed: 120.0, + projectile_speed: 60.0, + num_projectiles: 4, ) diff --git a/assets/common/abilities/bowsimple/basic.ron b/assets/common/abilities/bowsimple/basic.ron index 91d4231346..0dd7e0e008 100644 --- a/assets/common/abilities/bowsimple/basic.ron +++ b/assets/common/abilities/bowsimple/basic.ron @@ -10,4 +10,6 @@ BasicRanged( projectile_body: Object(Arrow), projectile_light: None, projectile_speed: 100.0, + num_projectiles: 1, + projectile_spread: 0.0, ) diff --git a/assets/common/abilities/custom/birdlargebreathe/firebomb.ron b/assets/common/abilities/custom/birdlargebreathe/firebomb.ron index def79061ea..6c2b6245ff 100644 --- a/assets/common/abilities/custom/birdlargebreathe/firebomb.ron +++ b/assets/common/abilities/custom/birdlargebreathe/firebomb.ron @@ -14,4 +14,6 @@ BasicRanged( }),*/ projectile_gravity: Some(Gravity(0.15)), projectile_speed: 60.0, + num_projectiles: 1, + projectile_spread: 0.0, ) diff --git a/assets/common/abilities/custom/birdlargefire/firebomb.ron b/assets/common/abilities/custom/birdlargefire/firebomb.ron index def79061ea..6c2b6245ff 100644 --- a/assets/common/abilities/custom/birdlargefire/firebomb.ron +++ b/assets/common/abilities/custom/birdlargefire/firebomb.ron @@ -14,4 +14,6 @@ BasicRanged( }),*/ projectile_gravity: Some(Gravity(0.15)), projectile_speed: 60.0, + num_projectiles: 1, + projectile_spread: 0.0, ) diff --git a/assets/common/abilities/custom/claygolem/rocket.ron b/assets/common/abilities/custom/claygolem/rocket.ron index 2ac6c020e5..852a4db918 100644 --- a/assets/common/abilities/custom/claygolem/rocket.ron +++ b/assets/common/abilities/custom/claygolem/rocket.ron @@ -10,4 +10,6 @@ BasicRanged( projectile_body: Object(ClayRocket), projectile_light: None, projectile_speed: 30.0, + num_projectiles: 1, + projectile_spread: 0.0, ) diff --git a/assets/common/abilities/custom/mindflayer/necroticsphere.ron b/assets/common/abilities/custom/mindflayer/necroticsphere.ron index c643a741c3..e8ab890b78 100644 --- a/assets/common/abilities/custom/mindflayer/necroticsphere.ron +++ b/assets/common/abilities/custom/mindflayer/necroticsphere.ron @@ -8,5 +8,7 @@ BasicRanged( ), projectile_body: Object(FireworkPurple), projectile_speed: 100.0, + num_projectiles: 1, + projectile_spread: 0.0, ) diff --git a/assets/common/abilities/custom/quadlowranged/firebomb.ron b/assets/common/abilities/custom/quadlowranged/firebomb.ron index 5b3917892a..beb3c27f1c 100644 --- a/assets/common/abilities/custom/quadlowranged/firebomb.ron +++ b/assets/common/abilities/custom/quadlowranged/firebomb.ron @@ -13,4 +13,6 @@ BasicRanged( ..Default::default() }),*/ projectile_speed: 70.0, + num_projectiles: 1, + projectile_spread: 0.0, ) diff --git a/assets/common/abilities/custom/turret/arrows.ron b/assets/common/abilities/custom/turret/arrows.ron index 1ae6f9a79f..b86a4b329b 100644 --- a/assets/common/abilities/custom/turret/arrows.ron +++ b/assets/common/abilities/custom/turret/arrows.ron @@ -10,4 +10,6 @@ BasicRanged( projectile_body: Object(ArrowTurret), projectile_light: None, projectile_speed: 130.0, + num_projectiles: 1, + projectile_spread: 0.0, ) diff --git a/assets/common/abilities/custom/wendigomagic/frostbomb.ron b/assets/common/abilities/custom/wendigomagic/frostbomb.ron index b6529121cf..5cf95514b2 100644 --- a/assets/common/abilities/custom/wendigomagic/frostbomb.ron +++ b/assets/common/abilities/custom/wendigomagic/frostbomb.ron @@ -12,4 +12,6 @@ BasicRanged( ..Default::default() }),*/ projectile_speed: 60.0, + num_projectiles: 1, + projectile_spread: 0.0, ) diff --git a/assets/common/abilities/debug/possess.ron b/assets/common/abilities/debug/possess.ron index e36ab3a45e..022d8b3b38 100644 --- a/assets/common/abilities/debug/possess.ron +++ b/assets/common/abilities/debug/possess.ron @@ -9,4 +9,6 @@ BasicRanged( ..Default::default() }),*/ projectile_speed: 100.0, + num_projectiles: 1, + projectile_spread: 0.0, ) \ No newline at end of file diff --git a/assets/common/abilities/hammer/leap.ron b/assets/common/abilities/hammer/leap.ron index 42d1064cf8..f1dd598690 100644 --- a/assets/common/abilities/hammer/leap.ron +++ b/assets/common/abilities/hammer/leap.ron @@ -1,5 +1,5 @@ LeapMelee( - energy_cost: 700, + energy_cost: 500, buildup_duration: 0.1, movement_duration: 0.8, swing_duration: 0.15, diff --git a/assets/common/abilities/staff/firebomb.ron b/assets/common/abilities/staff/firebomb.ron index 79494def06..c803a6a0c1 100644 --- a/assets/common/abilities/staff/firebomb.ron +++ b/assets/common/abilities/staff/firebomb.ron @@ -9,4 +9,6 @@ BasicRanged( ), projectile_body: Object(BoltFire), projectile_speed: 60.0, + num_projectiles: 1, + projectile_spread: 0.0, ) diff --git a/assets/common/abilities/staffsimple/firebomb.ron b/assets/common/abilities/staffsimple/firebomb.ron index 33c14f0f0f..92ec89d2b7 100644 --- a/assets/common/abilities/staffsimple/firebomb.ron +++ b/assets/common/abilities/staffsimple/firebomb.ron @@ -13,4 +13,6 @@ BasicRanged( ..Default::default() }),*/ projectile_speed: 60.0, + num_projectiles: 1, + projectile_spread: 0.0, ) diff --git a/assets/common/skill_trees/skill_max_levels.ron b/assets/common/skill_trees/skill_max_levels.ron index 90747b54c3..275c23b15b 100644 --- a/assets/common/skill_trees/skill_max_levels.ron +++ b/assets/common/skill_trees/skill_max_levels.ron @@ -36,17 +36,18 @@ Hammer(LKnockback): Some(2), Hammer(LRange): Some(2), Bow(ProjSpeed): Some(2), - Bow(BDamage): Some(3), - Bow(BRegen): Some(2), Bow(CDamage): Some(3), + Bow(CRegen): Some(2), Bow(CKnockback): Some(2), - Bow(CProjSpeed): Some(2), - Bow(CDrain): Some(2), Bow(CSpeed): Some(2), Bow(CMove): Some(2), - Bow(RDamage): Some(2), - Bow(RArrows): Some(2), + Bow(RDamage): Some(3), Bow(RCost): Some(2), + Bow(RSpeed): Some(2), + Bow(SDamage): Some(2), + Bow(SCost): Some(2), + Bow(SArrows): Some(2), + Bow(SSpread): Some(2), Staff(BDamage): Some(3), Staff(BRegen): Some(2), Staff(BRadius): Some(3), diff --git a/assets/common/skill_trees/skill_prerequisites.ron b/assets/common/skill_trees/skill_prerequisites.ron index 55a8c1cccd..764f217c89 100644 --- a/assets/common/skill_trees/skill_prerequisites.ron +++ b/assets/common/skill_trees/skill_prerequisites.ron @@ -12,10 +12,10 @@ Hammer(LDistance): {Hammer(UnlockLeap): None}, Hammer(LKnockback): {Hammer(UnlockLeap): None}, Hammer(LRange): {Hammer(UnlockLeap): None}, - Bow(RDamage): {Bow(UnlockRepeater): None}, - Bow(RGlide): {Bow(UnlockRepeater): None}, - Bow(RArrows): {Bow(UnlockRepeater): None}, - Bow(RCost): {Bow(UnlockRepeater): None}, + Bow(SDamage): {Bow(UnlockShotgun): None}, + Bow(SCost): {Bow(UnlockShotgun): None}, + Bow(SArrows): {Bow(UnlockShotgun): None}, + Bow(SSpread): {Bow(UnlockShotgun): None}, Staff(SDamage): {Staff(UnlockShockwave): None}, Staff(SKnockback): {Staff(UnlockShockwave): None}, Staff(SRange): {Staff(UnlockShockwave): None}, diff --git a/assets/common/skill_trees/skills_skill-groups_manifest.ron b/assets/common/skill_trees/skills_skill-groups_manifest.ron index 56cb885f3f..220f2b1e97 100644 --- a/assets/common/skill_trees/skills_skill-groups_manifest.ron +++ b/assets/common/skill_trees/skills_skill-groups_manifest.ron @@ -67,19 +67,19 @@ ], Weapon(Bow): [ Bow(ProjSpeed), - Bow(BDamage), - Bow(BRegen), Bow(CDamage), + Bow(CRegen), Bow(CKnockback), - Bow(CProjSpeed), - Bow(CDrain), Bow(CSpeed), Bow(CMove), - Bow(UnlockRepeater), Bow(RDamage), - Bow(RGlide), - Bow(RArrows), Bow(RCost), + Bow(RSpeed), + Bow(UnlockShotgun), + Bow(SDamage), + Bow(SCost), + Bow(SArrows), + Bow(SSpread), ], Weapon(Staff): [ Staff(BDamage), diff --git a/assets/server/manifests/presets.ron b/assets/server/manifests/presets.ron index ab7764d36a..341049b2b3 100644 --- a/assets/server/manifests/presets.ron +++ b/assets/server/manifests/presets.ron @@ -76,21 +76,23 @@ (UnlockGroup(Weapon(Bow)), 1), (Bow(ProjSpeed), 2), - (Bow(BDamage), 3), - (Bow(BRegen), 2), (Bow(CDamage), 3), (Bow(CKnockback), 2), - (Bow(CProjSpeed), 2), - (Bow(CDrain), 2), (Bow(CSpeed), 2), + (Bow(CRegen), 2), (Bow(CMove), 2), - (Bow(UnlockRepeater), 1), - (Bow(RGlide), 1), - (Bow(RDamage), 2), - (Bow(RArrows), 2), + (Bow(RDamage), 3), (Bow(RCost), 2), + (Bow(RSpeed), 2), + + (Bow(UnlockShotgun), 1), + (Bow(SDamage), 2), + (Bow(SCost), 2), + (Bow(SArrows), 2), + (Bow(SSpread), 2), + // Staff (UnlockGroup(Weapon(Staff)), 1), @@ -126,4 +128,40 @@ (Sceptre(ARange), 2), (Sceptre(ACost), 2), ], + // Basic skill preset to unlock all abilities + "basic": [ + // General skills + + // Sword + (UnlockGroup(Weapon(Sword)), 1), + + (Sword(InterruptingAttacks), 1), + (Sword(DInfinite), 1), + (Sword(UnlockSpin), 1), + + // Axe + (UnlockGroup(Weapon(Axe)), 1), + + (Axe(SInfinite), 1), + (Axe(UnlockLeap), 1), + + // Hammer + (UnlockGroup(Weapon(Hammer)), 1), + + (Hammer(UnlockLeap), 1), + // Bow + (UnlockGroup(Weapon(Bow)), 1), + + (Bow(UnlockShotgun), 1), + + // Staff + (UnlockGroup(Weapon(Staff)), 1), + + (Staff(UnlockShockwave), 1), + + // Sceptre + (UnlockGroup(Weapon(Sceptre)), 1), + + (Sceptre(UnlockAura), 1), + ], }) diff --git a/assets/voxygen/i18n/en/skills.ron b/assets/voxygen/i18n/en/skills.ron index 6c747fafb5..93ae9ffb7e 100644 --- a/assets/voxygen/i18n/en/skills.ron +++ b/assets/voxygen/i18n/en/skills.ron @@ -104,36 +104,36 @@ // Bow "hud.skill.bow_projectile_speed_title" : "Projectile Speed", "hud.skill.bow_projectile_speed" : "Allows you to shoot arrows further, faster, by 30%{SP}", - "hud.skill.bow_arrow_count_title" : "Arrow Count", - "hud.skill.bow_arrow_count" : "shoot an additional arrow when you leap{SP}", - "hud.skill.bow_repeater_cost_title" : "Repeater Cost", - "hud.skill.bow_repeater_cost" : "Decreases the energy cost to become a gliding repeater by 30%{SP}", - "hud.skill.bow_repeater_glide_title" : "Repeater Glide", - "hud.skill.bow_repeater_glide" : "Glide further while repeating{SP}", - "hud.skill.bow_repeater_damage_title" : "Repeater Damage", - "hud.skill.bow_repeater_damage" : "Increases the damage done by 40%{SP}", - "hud.skill.bow_repeater_unlock_title" : "Repeater Unlock", - "hud.skill.bow_repeater_unlock" : "Unlocks the ability to leap in the air and shoot a barrage of arrows{SP}", "hud.skill.bow_charged_title" : "Charged Shoot", "hud.skill.bow_charged" : "Because you waited longer", - "hud.skill.bow_charged_knockback_title" : "Charged Knockback", - "hud.skill.bow_charged_knockback" : "Knock enemies further back by 25%{SP}", - "hud.skill.bow_charged_move_speed_title" : "Charged Move Speed", - "hud.skill.bow_charged_move_speed" : "Increases how fast you can shuffle while charging the attack by 25%{SP}", - "hud.skill.bow_charged_speed_title" : "Charged Speed", - "hud.skill.bow_charged_speed" : "Increases the rate that you charge the attack by 10%{SP}", - "hud.skill.bow_charged_projectile_speed_title" : "Charged Projectile Speed", - "hud.skill.bow_charged_projectile_speed" : "Projectile speed increased by an additional 20% while charging{SP}", - "hud.skill.bow_charged_drain_title" : "Charged Drain", - "hud.skill.bow_charged_drain" : "Decreases the rate of stamina drain by 15%{SP}", "hud.skill.bow_charged_damage_title" : "Charged Damage", "hud.skill.bow_charged_damage" : "Increases damage by 20%{SP}", - "hud.skill.bow_energy_regen_title" : "Energy Regen", - "hud.skill.bow_energy_regen" : "Increases stamina gain by 40%{SP}", - "hud.skill.bow_title" : "Arrow Shoot", - "hud.skill.bow" : "Infinite quiver included, not suitable for children", - "hud.skill.bow_damage_title" : "Damage", - "hud.skill.bow_damage" : "Increases damage by 25%{SP}", + "hud.skill.bow_charged_energy_regen_title" : "Charged Regen", + "hud.skill.bow_charged_energy_regen" : "Increases stamina recovery by 20%{SP}", + "hud.skill.bow_charged_knockback_title" : "Charged Knockback", + "hud.skill.bow_charged_knockback" : "Knock enemies further back by 20%{SP}", + "hud.skill.bow_charged_speed_title" : "Charged Speed", + "hud.skill.bow_charged_speed" : "Increases the rate that you charge the attack by 10%{SP}", + "hud.skill.bow_charged_move_title" : "Charged Move Speed", + "hud.skill.bow_charged_move" : "Increases how fast you can shuffle while charging the attack by 10%{SP}", + "hud.skill.bow_repeater_title" : "Repeater", + "hud.skill.bow_repeater" : "Shoots faster the longer you fire for", + "hud.skill.bow_repeater_damage_title" : "Repeater Damage", + "hud.skill.bow_repeater_damage" : "Increases the damage done by 20%{SP}", + "hud.skill.bow_repeater_cost_title" : "Repeater Cost", + "hud.skill.bow_repeater_cost" : "Decreases the energy cost to become a repeater by 20%{SP}", + "hud.skill.bow_repeater_speed_title" : "Repeater Speed", + "hud.skill.bow_repeater_speed" : "Increases the rate at which you fire arrows by 20%{SP}", + "hud.skill.bow_shotgun_unlock_title" : "Unlocks Shotgun", + "hud.skill.bow_shotgun_unlock" : "Unlocks ability to fire multiple arrows at once{SP}", + "hud.skill.bow_shotgun_damage_title" : "Shotgun Damage", + "hud.skill.bow_shotgun_damage" : "Increases the damage done by 20%{SP}", + "hud.skill.bow_shotgun_cost_title" : "Shotgun Cost", + "hud.skill.bow_shotgun_cost" : "Decreases the cost of shotgun by 20%{SP}", + "hud.skill.bow_shotgun_arrow_count_title" : "Shotgun Arrows", + "hud.skill.bow_shotgun_arrow_count" : "Increases the number of arrows in the burst by 1{SP}", + "hud.skill.bow_shotgun_spread_title" : "Shotgun Spread", + "hud.skill.bow_shotgun_spread" : "Decreases the spread of the arrows by 20%{SP}", // Hammer "hud.skill.hmr_leap_radius_title" : "Leap Radius", "hud.skill.hmr_leap_radius" : "Increases attack radius on ground slam by 1 meter{SP}", diff --git a/common/src/cmd.rs b/common/src/cmd.rs index 361a20ba55..ae753992c6 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -912,3 +912,13 @@ impl ArgumentSpec { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_loading_skill_presets() { + SkillPresetManifest::load_expect("server.manifests.presets"); + } +} diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 21d9550d80..e616a872ac 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -79,19 +79,20 @@ pub enum CharacterAbility { projectile_body: Body, projectile_light: Option, projectile_speed: f32, + num_projectiles: u32, + projectile_spread: f32, }, RepeaterRanged { energy_cost: f32, - movement_duration: f32, buildup_duration: f32, shoot_duration: f32, recover_duration: f32, - leap: Option, + max_speed: f32, + half_speed_at: u32, projectile: ProjectileConstructor, projectile_body: Body, projectile_light: Option, projectile_speed: f32, - reps_remaining: u32, }, Boost { movement_duration: f32, @@ -201,6 +202,8 @@ pub enum CharacterAbility { ChargedRanged { energy_cost: f32, energy_drain: f32, + initial_regen: f32, + scaled_regen: f32, initial_damage: f32, scaled_damage: f32, initial_knockback: f32, @@ -214,7 +217,6 @@ pub enum CharacterAbility { initial_projectile_speed: f32, scaled_projectile_speed: f32, move_speed: f32, - damage_kind: DamageKind, }, Shockwave { energy_cost: f32, @@ -340,14 +342,9 @@ impl CharacterAbility { .energy .try_change_by(-(*energy_cost as i32), EnergySource::Ability) .is_ok(), - CharacterAbility::RepeaterRanged { - energy_cost, leap, .. - } => { - (leap.is_none() || update.vel.0.z >= 0.0) - && update - .energy - .try_change_by(-(*energy_cost as i32), EnergySource::Ability) - .is_ok() + // Consumes energy within state, so value only checked before entering state + CharacterAbility::RepeaterRanged { energy_cost, .. } => { + update.energy.current() as f32 >= *energy_cost }, CharacterAbility::LeapMelee { energy_cost, .. } => { update.vel.0.z >= 0.0 @@ -414,14 +411,12 @@ impl CharacterAbility { *projectile = projectile.modified_projectile(power, 1_f32, 1_f32); }, RepeaterRanged { - ref mut movement_duration, ref mut buildup_duration, ref mut shoot_duration, ref mut recover_duration, ref mut projectile, .. } => { - *movement_duration /= speed; *buildup_duration /= speed; *shoot_duration /= speed; *recover_duration /= speed; @@ -944,81 +939,90 @@ impl CharacterAbility { Some(ToolKind::Bow) => { use skills::BowSkill::*; match self { - BasicRanged { - ref mut projectile, - ref mut projectile_speed, - .. - } => { - if let Ok(Some(level)) = skillset.skill_level(Bow(ProjSpeed)) { - *projectile_speed *= 1.3_f32.powi(level.into()); - } - let damage_level = skillset - .skill_level(Bow(BDamage)) - .unwrap_or(None) - .unwrap_or(0); - let regen_level = skillset - .skill_level(Bow(BRegen)) - .unwrap_or(None) - .unwrap_or(0); - let power = 1.20_f32.powi(damage_level.into()); - let regen = 1.4_f32.powi(regen_level.into()); - *projectile = projectile.modified_projectile(power, regen, 1_f32); - }, ChargedRanged { + ref mut initial_damage, ref mut scaled_damage, + ref mut initial_regen, + ref mut scaled_regen, + ref mut initial_knockback, ref mut scaled_knockback, - ref mut energy_drain, ref mut speed, + ref mut move_speed, ref mut initial_projectile_speed, ref mut scaled_projectile_speed, - ref mut move_speed, .. } => { if let Ok(Some(level)) = skillset.skill_level(Bow(ProjSpeed)) { - *initial_projectile_speed *= 1.3_f32.powi(level.into()); + let projectile_speed_scaling = 1.2_f32.powi(level.into()); + *initial_projectile_speed *= projectile_speed_scaling; + *scaled_projectile_speed *= projectile_speed_scaling; } if let Ok(Some(level)) = skillset.skill_level(Bow(CDamage)) { - *scaled_damage *= 1.2_f32.powi(level.into()); + let damage_scaling = 1.2_f32.powi(level.into()); + *initial_damage *= damage_scaling; + *scaled_damage *= damage_scaling; + } + if let Ok(Some(level)) = skillset.skill_level(Bow(CRegen)) { + let regen_scaling = 1.2_f32.powi(level.into()); + *initial_regen *= regen_scaling; + *scaled_regen *= regen_scaling; } if let Ok(Some(level)) = skillset.skill_level(Bow(CKnockback)) { - *scaled_knockback *= 1.25_f32.powi(level.into()); - } - if let Ok(Some(level)) = skillset.skill_level(Bow(CProjSpeed)) { - *scaled_projectile_speed *= 1.2_f32.powi(level.into()); - } - if let Ok(Some(level)) = skillset.skill_level(Bow(CDrain)) { - *energy_drain *= 0.85_f32.powi(level.into()); + let knockback_scaling = 1.2_f32.powi(level.into()); + *initial_knockback *= knockback_scaling; + *scaled_knockback *= knockback_scaling; } if let Ok(Some(level)) = skillset.skill_level(Bow(CSpeed)) { - *speed *= 1.10_f32.powi(level.into()); + *speed *= 1.1_f32.powi(level.into()); } if let Ok(Some(level)) = skillset.skill_level(Bow(CMove)) { - *move_speed *= 1.25_f32.powi(level.into()); + *move_speed *= 1.1_f32.powi(level.into()); } }, RepeaterRanged { ref mut energy_cost, - ref mut buildup_duration, ref mut projectile, - ref mut reps_remaining, + ref mut max_speed, ref mut projectile_speed, .. } => { if let Ok(Some(level)) = skillset.skill_level(Bow(ProjSpeed)) { - *projectile_speed *= 1.3_f32.powi(level.into()); + *projectile_speed *= 1.2_f32.powi(level.into()); } if let Ok(Some(level)) = skillset.skill_level(Bow(RDamage)) { - let power = 1.4_f32.powi(level.into()); + let power = 1.2_f32.powi(level.into()); *projectile = projectile.modified_projectile(power, 1_f32, 1_f32); } - if !skillset.has_skill(Bow(RGlide)) { - *buildup_duration = 0.001; - } - if let Ok(Some(level)) = skillset.skill_level(Bow(RArrows)) { - *reps_remaining += level as u32; - } if let Ok(Some(level)) = skillset.skill_level(Bow(RCost)) { - *energy_cost *= 0.70_f32.powi(level.into()); + *energy_cost *= 0.8_f32.powi(level.into()); + } + if let Ok(Some(level)) = skillset.skill_level(Bow(RSpeed)) { + *max_speed *= 1.2_f32.powi(level.into()); + } + }, + BasicRanged { + ref mut projectile, + ref mut energy_cost, + ref mut num_projectiles, + ref mut projectile_spread, + ref mut projectile_speed, + .. + } => { + if let Ok(Some(level)) = skillset.skill_level(Bow(ProjSpeed)) { + *projectile_speed *= 1.2_f32.powi(level.into()); + } + if let Ok(Some(level)) = skillset.skill_level(Bow(SDamage)) { + let power = 1.2_f32.powi(level.into()); + *projectile = projectile.modified_projectile(power, 1_f32, 1_f32); + } + if let Ok(Some(level)) = skillset.skill_level(Bow(SCost)) { + *energy_cost *= 0.8_f32.powi(level.into()); + } + if let Ok(Some(level)) = skillset.skill_level(Bow(SArrows)) { + *num_projectiles += level as u32; + } + if let Ok(Some(level)) = skillset.skill_level(Bow(SSpread)) { + *projectile_spread *= 0.8_f32.powi(level.into()); } }, _ => {}, @@ -1233,6 +1237,8 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { projectile_light, projectile_speed, energy_cost: _, + num_projectiles, + projectile_spread, } => CharacterState::BasicRanged(basic_ranged::Data { static_data: basic_ranged::StaticData { buildup_duration: Duration::from_secs_f32(*buildup_duration), @@ -1241,6 +1247,8 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { projectile_body: *projectile_body, projectile_light: *projectile_light, projectile_speed: *projectile_speed, + num_projectiles: *num_projectiles, + projectile_spread: *projectile_spread, ability_info, }, timer: Duration::default(), @@ -1500,6 +1508,8 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { CharacterAbility::ChargedRanged { energy_cost: _, energy_drain, + initial_regen, + scaled_regen, initial_damage, scaled_damage, initial_knockback, @@ -1513,13 +1523,14 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { initial_projectile_speed, scaled_projectile_speed, move_speed, - damage_kind, } => CharacterState::ChargedRanged(charged_ranged::Data { static_data: charged_ranged::StaticData { buildup_duration: Duration::from_secs_f32(*buildup_duration), charge_duration: Duration::from_secs_f32(*charge_duration), recover_duration: Duration::from_secs_f32(*recover_duration), energy_drain: *energy_drain, + initial_regen: *initial_regen, + scaled_regen: *scaled_regen, initial_damage: *initial_damage, scaled_damage: *scaled_damage, speed: *speed, @@ -1531,31 +1542,31 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { scaled_projectile_speed: *scaled_projectile_speed, move_speed: *move_speed, ability_info, - damage_kind: *damage_kind, }, timer: Duration::default(), stage_section: StageSection::Buildup, exhausted: false, }), CharacterAbility::RepeaterRanged { - energy_cost: _, - movement_duration, + energy_cost, buildup_duration, shoot_duration, recover_duration, - leap, + max_speed, + half_speed_at, projectile, projectile_body, projectile_light, projectile_speed, - reps_remaining, } => CharacterState::RepeaterRanged(repeater_ranged::Data { static_data: repeater_ranged::StaticData { - movement_duration: Duration::from_secs_f32(*movement_duration), buildup_duration: Duration::from_secs_f32(*buildup_duration), shoot_duration: Duration::from_secs_f32(*shoot_duration), recover_duration: Duration::from_secs_f32(*recover_duration), - leap: *leap, + energy_cost: *energy_cost, + // 1.0 is subtracted as 1.0 is added in state file + max_speed: *max_speed - 1.0, + half_speed_at: *half_speed_at, projectile: *projectile, projectile_body: *projectile_body, projectile_light: *projectile_light, @@ -1563,8 +1574,9 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { ability_info, }, timer: Duration::default(), - stage_section: StageSection::Movement, - reps_remaining: *reps_remaining, + stage_section: StageSection::Buildup, + projectiles_fired: 0, + speed: 1.0, }), CharacterAbility::Shockwave { energy_cost: _, diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index 5bc5daeffb..a50bbc3788 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -12,7 +12,7 @@ use specs::Component; use specs_idvs::IdvStorage; use std::time::Duration; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub enum Effect { Attack(Attack), Explode(Explosion), @@ -21,7 +21,7 @@ pub enum Effect { Possess, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Projectile { // TODO: use SmallVec for these effects pub hit_solid: Vec, diff --git a/common/src/comp/skills.rs b/common/src/comp/skills.rs index bc16c18e54..320bd6b1f6 100644 --- a/common/src/comp/skills.rs +++ b/common/src/comp/skills.rs @@ -183,22 +183,22 @@ pub enum HammerSkill { pub enum BowSkill { // Passives ProjSpeed, - // Basic ranged upgrades - BDamage, - BRegen, - // Charged ranged upgrades + // Charged upgrades CDamage, + CRegen, CKnockback, - CProjSpeed, - CDrain, CSpeed, CMove, // Repeater upgrades - UnlockRepeater, RDamage, - RGlide, - RArrows, RCost, + RSpeed, + // Shotgun upgrades + UnlockShotgun, + SDamage, + SCost, + SArrows, + SSpread, } #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] diff --git a/common/src/skillset_builder.rs b/common/src/skillset_builder.rs index 7eaa413c85..e23fa36a29 100644 --- a/common/src/skillset_builder.rs +++ b/common/src/skillset_builder.rs @@ -51,11 +51,11 @@ impl SkillSetBuilder { // Bow Self::default() .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::BDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) + .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) }, _ => Self::default(), } @@ -66,11 +66,11 @@ impl SkillSetBuilder { // Bow Self::default() .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::BDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) + .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) }, _ => Self::default(), } @@ -81,11 +81,11 @@ impl SkillSetBuilder { // Bow Self::default() .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::BDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) + .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) }, _ => Self::default(), } @@ -96,11 +96,11 @@ impl SkillSetBuilder { // Bow Self::default() .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::BDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) + .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) }, _ => Self::default(), } @@ -111,11 +111,11 @@ impl SkillSetBuilder { // Bow Self::default() .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::BDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) + .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) }, _ => Self::default(), } @@ -176,11 +176,11 @@ impl SkillSetBuilder { // Bow Self::default() .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::BDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::ProjSpeed), Some(1)) .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::CProjSpeed), Some(1)) + .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) + .with_skill(Skill::Bow(BowSkill::RSpeed), Some(1)) }, Some(ToolKind::Staff) => { // Staff @@ -234,13 +234,13 @@ impl SkillSetBuilder { // Bow Self::default() .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::BDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) - .with_skill(Skill::Bow(BowSkill::UnlockRepeater), None) - .with_skill(Skill::Bow(BowSkill::RArrows), Some(1)) + .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) + .with_skill(Skill::Bow(BowSkill::UnlockShotgun), None) + .with_skill(Skill::Bow(BowSkill::SArrows), Some(1)) }, Some(ToolKind::Staff) => { // Staff @@ -303,14 +303,14 @@ impl SkillSetBuilder { // Bow Self::default() .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::BDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::ProjSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::BRegen), Some(1)) .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::CDrain), Some(1)) + .with_skill(Skill::Bow(BowSkill::CRegen), Some(1)) .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::UnlockRepeater), None) - .with_skill(Skill::Bow(BowSkill::RGlide), None) + .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) + .with_skill(Skill::Bow(BowSkill::RCost), Some(1)) + .with_skill(Skill::Bow(BowSkill::UnlockShotgun), None) + .with_skill(Skill::Bow(BowSkill::SCost), Some(1)) .with_skill(Skill::Bow(BowSkill::RCost), Some(1)) }, Some(ToolKind::Staff) => { @@ -376,16 +376,16 @@ impl SkillSetBuilder { // Bow Self::default() .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::BDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::ProjSpeed), Some(1)) .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::CProjSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::CDrain), Some(1)) - .with_skill(Skill::Bow(BowSkill::UnlockRepeater), None) .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::RGlide), None) - .with_skill(Skill::Bow(BowSkill::RArrows), Some(1)) + .with_skill(Skill::Bow(BowSkill::RSpeed), Some(1)) + .with_skill(Skill::Bow(BowSkill::RCost), Some(1)) + .with_skill(Skill::Bow(BowSkill::UnlockShotgun), None) + .with_skill(Skill::Bow(BowSkill::SDamage), Some(1)) + .with_skill(Skill::Bow(BowSkill::SArrows), Some(1)) + .with_skill(Skill::Bow(BowSkill::SSpread), Some(1)) }, Some(ToolKind::Staff) => { // Staff @@ -453,16 +453,16 @@ impl SkillSetBuilder { // Bow Self::default() .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::BDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::CProjSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::CDrain), Some(1)) - .with_skill(Skill::Bow(BowSkill::UnlockRepeater), None) + .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::RGlide), None) - .with_skill(Skill::Bow(BowSkill::RArrows), Some(1)) .with_skill(Skill::Bow(BowSkill::RCost), Some(1)) + .with_skill(Skill::Bow(BowSkill::UnlockShotgun), None) + .with_skill(Skill::Bow(BowSkill::SDamage), Some(1)) + .with_skill(Skill::Bow(BowSkill::SSpread), Some(1)) + .with_skill(Skill::Bow(BowSkill::SArrows), Some(1)) + .with_skill(Skill::Bow(BowSkill::SCost), Some(1)) }, Some(ToolKind::Staff) => { // Staff @@ -536,18 +536,18 @@ impl SkillSetBuilder { // Bow Self::default() .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::BDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::BRegen), Some(1)) .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) + .with_skill(Skill::Bow(BowSkill::CRegen), Some(1)) .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::CProjSpeed), Some(1)) .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) - .with_skill(Skill::Bow(BowSkill::UnlockRepeater), None) .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::RGlide), None) - .with_skill(Skill::Bow(BowSkill::RArrows), Some(1)) - .with_skill(Skill::Bow(BowSkill::RCost), Some(1)) + .with_skill(Skill::Bow(BowSkill::RSpeed), Some(1)) + .with_skill(Skill::Bow(BowSkill::UnlockShotgun), None) + .with_skill(Skill::Bow(BowSkill::SDamage), Some(1)) + .with_skill(Skill::Bow(BowSkill::SSpread), Some(1)) + .with_skill(Skill::Bow(BowSkill::SArrows), Some(1)) + .with_skill(Skill::Bow(BowSkill::SCost), Some(1)) }, Some(ToolKind::Staff) => { // Staff @@ -629,20 +629,20 @@ impl SkillSetBuilder { // Bow Self::default() .with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow)) - .with_skill(Skill::Bow(BowSkill::BDamage), Some(1)) .with_skill(Skill::Bow(BowSkill::ProjSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::BRegen), Some(1)) .with_skill(Skill::Bow(BowSkill::CDamage), Some(1)) + .with_skill(Skill::Bow(BowSkill::CRegen), Some(1)) .with_skill(Skill::Bow(BowSkill::CKnockback), Some(1)) - .with_skill(Skill::Bow(BowSkill::CProjSpeed), Some(1)) - .with_skill(Skill::Bow(BowSkill::CDrain), Some(1)) .with_skill(Skill::Bow(BowSkill::CSpeed), Some(1)) .with_skill(Skill::Bow(BowSkill::CMove), Some(1)) - .with_skill(Skill::Bow(BowSkill::UnlockRepeater), None) .with_skill(Skill::Bow(BowSkill::RDamage), Some(1)) - .with_skill(Skill::Bow(BowSkill::RGlide), None) - .with_skill(Skill::Bow(BowSkill::RArrows), Some(1)) + .with_skill(Skill::Bow(BowSkill::RSpeed), Some(1)) .with_skill(Skill::Bow(BowSkill::RCost), Some(1)) + .with_skill(Skill::Bow(BowSkill::UnlockShotgun), None) + .with_skill(Skill::Bow(BowSkill::SDamage), Some(1)) + .with_skill(Skill::Bow(BowSkill::SSpread), Some(1)) + .with_skill(Skill::Bow(BowSkill::SArrows), Some(1)) + .with_skill(Skill::Bow(BowSkill::SCost), Some(1)) }, Some(ToolKind::Staff) => { // Staff diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index fa07e96fa0..9e0ddb487a 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -5,7 +5,9 @@ use crate::{ behavior::{CharacterBehavior, JoinData}, utils::*, }, + util::Dir, }; +use rand::{thread_rng, Rng}; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -16,11 +18,15 @@ pub struct StaticData { pub buildup_duration: Duration, /// How long the state has until exiting pub recover_duration: Duration, + /// How much spread there is when more than 1 projectile is created + pub projectile_spread: f32, /// Projectile variables pub projectile: ProjectileConstructor, pub projectile_body: Body, pub projectile_light: Option, pub projectile_speed: f32, + /// How many projectiles are simultaneously fired + pub num_projectiles: u32, /// What key is used to press ability pub ability_info: AbilityInfo, } @@ -76,15 +82,28 @@ impl CharacterBehavior for Data { crit_chance, crit_mult, ); - update.server_events.push_front(ServerEvent::Shoot { - entity: data.entity, - dir: data.inputs.look_dir, - body: self.static_data.projectile_body, - projectile, - light: self.static_data.projectile_light, - speed: self.static_data.projectile_speed, - object: None, - }); + // Shoots all projectiles simultaneously + for i in 0..self.static_data.num_projectiles { + // Adds a slight spread to the projectiles. First projectile has no spread, + // and spread increases linearly with number of projectiles created. + let dir = Dir::from_unnormalized(data.inputs.look_dir.map(|x| { + let offset = (2.0 * thread_rng().gen::() - 1.0) + * self.static_data.projectile_spread + * i as f32; + x + offset + })) + .unwrap_or(data.inputs.look_dir); + // Tells server to create and shoot the projectile + update.server_events.push_front(ServerEvent::Shoot { + entity: data.entity, + dir, + body: self.static_data.projectile_body, + projectile: projectile.clone(), + light: self.static_data.projectile_light, + speed: self.static_data.projectile_speed, + object: None, + }); + } update.character = CharacterState::BasicRanged(Data { exhausted: true, diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index 9fa94ad283..2b8de1ac42 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -1,11 +1,7 @@ use crate::{ - combat::{ - Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement, Damage, - DamageKind, DamageSource, GroupTarget, Knockback, KnockbackDir, - }, comp::{ - projectile, Body, CharacterState, EnergyChange, EnergySource, LightEmitter, Projectile, - StateUpdate, + projectile::ProjectileConstructor, Body, CharacterState, EnergyChange, EnergySource, + LightEmitter, StateUpdate, }, event::ServerEvent, states::{ @@ -27,6 +23,10 @@ pub struct StaticData { pub recover_duration: Duration, /// How much energy is drained per second when charging pub energy_drain: f32, + /// How much energy is gained with no charge + pub initial_regen: f32, + /// How much the energy gain scales as it is charged + pub scaled_regen: f32, /// How much damage is dealt with no charge pub initial_damage: f32, /// How much the damage scales as it is charged @@ -46,8 +46,6 @@ pub struct StaticData { pub move_speed: f32, /// What key is used to press ability pub ability_info: AbilityInfo, - /// What kind of damage the attack does - pub damage_kind: DamageKind, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -96,45 +94,19 @@ impl CharacterBehavior for Data { let charge_frac = (self.timer.as_secs_f32() / self.static_data.charge_duration.as_secs_f32()) .min(1.0); - let knockback = AttackEffect::new( - Some(GroupTarget::OutOfGroup), - CombatEffect::Knockback(Knockback { - strength: self.static_data.initial_knockback - + charge_frac * self.static_data.scaled_knockback, - direction: KnockbackDir::Away, - }), - ) - .with_requirement(CombatRequirement::AnyDamage); - let buff = CombatEffect::Buff(CombatBuff::default_physical()); - let damage = AttackDamage::new( - Damage { - source: DamageSource::Projectile, - kind: self.static_data.damage_kind, - value: self.static_data.initial_damage as f32 - + charge_frac * self.static_data.scaled_damage as f32, - }, - Some(GroupTarget::OutOfGroup), - ) - .with_effect(buff); + let arrow = ProjectileConstructor::Arrow { + damage: self.static_data.initial_damage as f32 + + charge_frac * self.static_data.scaled_damage as f32, + knockback: self.static_data.initial_knockback + + charge_frac * self.static_data.scaled_knockback, + energy_regen: self.static_data.initial_regen + + charge_frac * self.static_data.scaled_regen, + }; + // Fire let (crit_chance, crit_mult) = get_crit_data(data, self.static_data.ability_info); - let attack = Attack::default() - .with_damage(damage) - .with_crit(crit_chance, crit_mult) - .with_effect(knockback) - .with_combo_increment(); - - // Fire - let projectile = Projectile { - hit_solid: vec![projectile::Effect::Stick], - hit_entity: vec![ - projectile::Effect::Attack(attack), - projectile::Effect::Vanish, - ], - time_left: Duration::from_secs(15), - owner: Some(*data.uid), - ignore_group: true, - }; + let projectile = + arrow.create_projectile(Some(*data.uid), crit_chance, crit_mult); update.server_events.push_front(ServerEvent::Shoot { entity: data.entity, dir: data.inputs.look_dir, @@ -166,7 +138,7 @@ impl CharacterBehavior for Data { ..*self }); - // Consumes energy if there's enough left and RMB is held down + // Consumes energy if there's enough left and input is held down update.energy.change_by(EnergyChange { amount: -(self.static_data.energy_drain as f32 * data.dt.0 diff --git a/common/src/states/repeater_ranged.rs b/common/src/states/repeater_ranged.rs index ef385edcdb..f3be51343f 100644 --- a/common/src/states/repeater_ranged.rs +++ b/common/src/states/repeater_ranged.rs @@ -1,29 +1,32 @@ use crate::{ - comp::{Body, CharacterState, LightEmitter, ProjectileConstructor, StateUpdate}, + comp::{ + Body, CharacterState, EnergyChange, EnergySource, LightEmitter, ProjectileConstructor, + StateUpdate, + }, event::ServerEvent, states::{ behavior::{CharacterBehavior, JoinData}, utils::{StageSection, *}, }, - util::dir::*, }; use serde::{Deserialize, Serialize}; use std::time::Duration; -use vek::Vec3; #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] /// Separated out to condense update portions of character state pub struct StaticData { - /// How long the state is in movement - pub movement_duration: Duration, /// How long we've readied the weapon pub buildup_duration: Duration, /// How long the state is shooting pub shoot_duration: Duration, /// How long the state has until exiting pub recover_duration: Duration, - /// Whether there should be a jump and how strong the leap is - pub leap: Option, + /// Energy cost per projectile + pub energy_cost: f32, + /// Max speed that can be reached + pub max_speed: f32, + /// Projectiles required to reach half of max speed + pub half_speed_at: u32, /// Projectile options pub projectile: ProjectileConstructor, pub projectile_body: Body, @@ -42,64 +45,20 @@ pub struct Data { pub timer: Duration, /// What section the character stage is in pub stage_section: StageSection, - /// How many repetitions remaining - pub reps_remaining: u32, + /// Speed of the state while in shoot section + pub speed: f32, + /// Number of projectiles fired so far + pub projectiles_fired: u32, } impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0); - handle_move(data, &mut update, 1.0); - handle_jump(data, &mut update, 1.0); + handle_move(data, &mut update, 0.3); match self.stage_section { - StageSection::Movement => { - // Jumping - if let Some(leap_strength) = self.static_data.leap { - let progress = 1.0 - - self.timer.as_secs_f32() - / self.static_data.movement_duration.as_secs_f32(); - handle_forced_movement( - data, - &mut update, - ForcedMovement::Leap { - vertical: leap_strength, - forward: 10.0, - progress, - direction: MovementDirection::Move, - }, - 1.0, - ); - } - if self.timer < self.static_data.movement_duration { - // Do movement - update.character = CharacterState::RepeaterRanged(Data { - timer: self - .timer - .checked_add(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), - ..*self - }); - } else { - // Transition to buildup - update.character = CharacterState::RepeaterRanged(Data { - timer: Duration::default(), - stage_section: StageSection::Buildup, - ..*self - }); - } - }, StageSection::Buildup => { - // Aim gliding - if self.static_data.leap.is_some() { - handle_forced_movement( - data, - &mut update, - ForcedMovement::Hover { move_input: 0.1 }, - 1.0, - ); - } if self.timer < self.static_data.buildup_duration { // Buildup to attack update.character = CharacterState::RepeaterRanged(Data { @@ -119,12 +78,19 @@ impl CharacterBehavior for Data { } }, StageSection::Shoot => { - // Aim gliding - if self.static_data.leap.is_some() { - update.vel.0 = Vec3::new(data.vel.0.x, data.vel.0.y, 0.0); - } - if self.reps_remaining > 0 { - // Fire + if self.timer < self.static_data.shoot_duration { + // Draw projectile + update.character = CharacterState::RepeaterRanged(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0 * self.speed)) + .unwrap_or_default(), + ..*self + }); + } else if input_is_pressed(data, self.static_data.ability_info.input) + && update.energy.current() as f32 >= self.static_data.energy_cost + { + // Fire if input is pressed still let (crit_chance, crit_mult) = get_crit_data(data, self.static_data.ability_info); let projectile = self.static_data.projectile.create_projectile( @@ -134,23 +100,7 @@ impl CharacterBehavior for Data { ); update.server_events.push_front(ServerEvent::Shoot { entity: data.entity, - // Provides slight variation to projectile direction - dir: Dir::from_unnormalized(Vec3::new( - data.inputs.look_dir[0] - + (if self.reps_remaining % 2 == 0 { - self.reps_remaining as f32 / 400.0 - } else { - -1.0 * self.reps_remaining as f32 / 400.0 - }), - data.inputs.look_dir[1] - + (if self.reps_remaining % 2 == 0 { - -1.0 * self.reps_remaining as f32 / 400.0 - } else { - self.reps_remaining as f32 / 400.0 - }), - data.inputs.look_dir[2], - )) - .unwrap_or(data.inputs.look_dir), + dir: data.inputs.look_dir, body: self.static_data.projectile_body, projectile, light: self.static_data.projectile_light, @@ -158,22 +108,26 @@ impl CharacterBehavior for Data { object: None, }); - // Shoot projectiles - update.character = CharacterState::RepeaterRanged(Data { - timer: self - .timer - .checked_add(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), - reps_remaining: self.reps_remaining - 1, - ..*self + // Removes energy from character when arrow is fired + update.server_events.push_front(ServerEvent::EnergyChange { + entity: data.entity, + change: EnergyChange { + amount: -self.static_data.energy_cost as i32, + source: EnergySource::Ability, + }, }); - } else if self.timer < self.static_data.shoot_duration { - // Finish shooting + + // Sets new speed of shoot. Scales based off of the number of projectiles fired. + let new_speed = 1.0 + + self.projectiles_fired as f32 + / (self.static_data.half_speed_at as f32 + + self.projectiles_fired as f32) + * self.static_data.max_speed; + update.character = CharacterState::RepeaterRanged(Data { - timer: self - .timer - .checked_add(Duration::from_secs_f32(data.dt.0)) - .unwrap_or_default(), + timer: Duration::default(), + speed: new_speed, + projectiles_fired: self.projectiles_fired + 1, ..*self }); } else { @@ -186,10 +140,7 @@ impl CharacterBehavior for Data { } }, StageSection::Recover => { - if self.static_data.leap.is_some() && data.physics.on_ground { - // Done - update.character = CharacterState::Wielding; - } else if self.timer < self.static_data.recover_duration { + if self.timer < self.static_data.recover_duration { // Recover from attack update.character = CharacterState::RepeaterRanged(Data { timer: self diff --git a/server/src/cmd.rs b/server/src/cmd.rs index c5bf6f059f..1338707477 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -3058,12 +3058,13 @@ fn handle_skill_preset( fn clear_skillset(skill_set: &mut comp::SkillSet) { *skill_set = comp::SkillSet::default(); } fn set_skills(skill_set: &mut comp::SkillSet, preset: &str) -> CmdResult<()> { - let presets = - if let Ok(presets) = common::cmd::SkillPresetManifest::load("server.manifests.presets") { - presets.read().0.clone() - } else { + let presets = match common::cmd::SkillPresetManifest::load("server.manifests.presets") { + Ok(presets) => presets.read().0.clone(), + Err(err) => { + warn!("Error in preset: {}", err); return Err("Error while loading presets".to_owned()); - }; + }, + }; if let Some(preset) = presets.get(preset) { for (skill, level) in preset { let group = if let Some(group) = skill.skill_group_kind() { diff --git a/server/src/migrations/V38__reset_bow_skills.sql b/server/src/migrations/V38__reset_bow_skills.sql new file mode 100644 index 0000000000..59651db3db --- /dev/null +++ b/server/src/migrations/V38__reset_bow_skills.sql @@ -0,0 +1,19 @@ +-- Resets bow skill tree by deleting bow skills and setting available skill points to earned skill points +-- Deletes all bow skills, does not delete unlock bow skill +DELETE FROM skill WHERE skill = 'Bow ProjSpeed'; +DELETE FROM skill WHERE skill = 'Bow BDamage'; +DELETE FROM skill WHERE skill = 'Bow BRegen'; +DELETE FROM skill WHERE skill = 'Bow CDamage'; +DELETE FROM skill WHERE skill = 'Bow CKnockback'; +DELETE FROM skill WHERE skill = 'Bow CProjSpeed'; +DELETE FROM skill WHERE skill = 'Bow CDrain'; +DELETE FROM skill WHERE skill = 'Bow CSpeed'; +DELETE FROM skill WHERE skill = 'Bow CMove'; +DELETE FROM skill WHERE skill = 'Bow UnlockRepeater'; +DELETE FROM skill WHERE skill = 'Bow RDamage'; +DELETE FROM skill WHERE skill = 'Bow RGlide'; +DELETE FROM skill WHERE skill = 'Bow RArrows'; +DELETE FROM skill WHERE skill = 'Bow RCost'; +-- Resets available skill points to earned skill points for bow skill tree +UPDATE skill_group +SET available_sp = earned_sp WHERE skill_group_kind = 'Weapon Bow'; \ No newline at end of file diff --git a/server/src/persistence/json_models.rs b/server/src/persistence/json_models.rs index 6dae0286f5..81d9531022 100644 --- a/server/src/persistence/json_models.rs +++ b/server/src/persistence/json_models.rs @@ -92,19 +92,19 @@ pub fn skill_to_db_string(skill: comp::skills::Skill) -> String { Hammer(HammerSkill::LKnockback) => "Hammer LKnockback", Hammer(HammerSkill::LRange) => "Hammer LRange", Bow(BowSkill::ProjSpeed) => "Bow ProjSpeed", - Bow(BowSkill::BDamage) => "Bow BDamage", - Bow(BowSkill::BRegen) => "Bow BRegen", Bow(BowSkill::CDamage) => "Bow CDamage", + Bow(BowSkill::CRegen) => "Bow CRegen", Bow(BowSkill::CKnockback) => "Bow CKnockback", - Bow(BowSkill::CProjSpeed) => "Bow CProjSpeed", - Bow(BowSkill::CDrain) => "Bow CDrain", Bow(BowSkill::CSpeed) => "Bow CSpeed", Bow(BowSkill::CMove) => "Bow CMove", - Bow(BowSkill::UnlockRepeater) => "Bow UnlockRepeater", Bow(BowSkill::RDamage) => "Bow RDamage", - Bow(BowSkill::RGlide) => "Bow RGlide", - Bow(BowSkill::RArrows) => "Bow RArrows", Bow(BowSkill::RCost) => "Bow RCost", + Bow(BowSkill::RSpeed) => "Bow RSpeed", + Bow(BowSkill::UnlockShotgun) => "Bow UnlockShotgun", + Bow(BowSkill::SDamage) => "Bow SDamage", + Bow(BowSkill::SCost) => "Bow SCost", + Bow(BowSkill::SArrows) => "Bow SArrows", + Bow(BowSkill::SSpread) => "Bow SSpread", Staff(StaffSkill::BDamage) => "Staff BDamage", Staff(StaffSkill::BRegen) => "Staff BRegen", Staff(StaffSkill::BRadius) => "Staff BRadius", @@ -212,19 +212,19 @@ pub fn db_string_to_skill(skill_string: &str) -> comp::skills::Skill { "Hammer LKnockback" => Hammer(HammerSkill::LKnockback), "Hammer LRange" => Hammer(HammerSkill::LRange), "Bow ProjSpeed" => Bow(BowSkill::ProjSpeed), - "Bow BDamage" => Bow(BowSkill::BDamage), - "Bow BRegen" => Bow(BowSkill::BRegen), "Bow CDamage" => Bow(BowSkill::CDamage), + "Bow CRegen" => Bow(BowSkill::CRegen), "Bow CKnockback" => Bow(BowSkill::CKnockback), - "Bow CProjSpeed" => Bow(BowSkill::CProjSpeed), - "Bow CDrain" => Bow(BowSkill::CDrain), "Bow CSpeed" => Bow(BowSkill::CSpeed), "Bow CMove" => Bow(BowSkill::CMove), - "Bow UnlockRepeater" => Bow(BowSkill::UnlockRepeater), "Bow RDamage" => Bow(BowSkill::RDamage), - "Bow RGlide" => Bow(BowSkill::RGlide), - "Bow RArrows" => Bow(BowSkill::RArrows), "Bow RCost" => Bow(BowSkill::RCost), + "Bow RSpeed" => Bow(BowSkill::RSpeed), + "Bow UnlockShotgun" => Bow(BowSkill::UnlockShotgun), + "Bow SDamage" => Bow(BowSkill::SDamage), + "Bow SCost" => Bow(BowSkill::SCost), + "Bow SArrows" => Bow(BowSkill::SArrows), + "Bow SSpread" => Bow(BowSkill::SSpread), "Staff BDamage" => Staff(StaffSkill::BDamage), "Staff BRegen" => Staff(StaffSkill::BRegen), "Staff BRadius" => Staff(StaffSkill::BRadius), diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 34be20d648..28c44fbe97 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -2091,7 +2091,7 @@ impl<'a> AgentData<'a> { agent.action_state.timer += read_data.dt.0; } else if self .skill_set - .has_skill(Skill::Bow(BowSkill::UnlockRepeater)) + .has_skill(Skill::Bow(BowSkill::UnlockShotgun)) && self.energy.current() > 400 && thread_rng().gen_bool(0.8) { diff --git a/voxygen/anim/src/character/repeater.rs b/voxygen/anim/src/character/repeater.rs index 1ca3884ded..c8dbde6256 100644 --- a/voxygen/anim/src/character/repeater.rs +++ b/voxygen/anim/src/character/repeater.rs @@ -26,26 +26,28 @@ impl Animation for RepeaterAnimation { #[allow(clippy::approx_constant)] // TODO: Pending review in #587 fn update_skeleton_inner<'a>( skeleton: &Self::Skeleton, - (ability_info, hands, _velocity, _global_time, stage_section): Self::Dependency<'a>, + (ability_info, hands, velocity, _global_time, stage_section): Self::Dependency<'a>, anim_time: f32, rate: &mut f32, s_a: &SkeletonAttr, ) -> Self::Skeleton { *rate = 1.0; let mut next = (*skeleton).clone(); + let speed = Vec2::::from(velocity).magnitude(); - let (move1, move2, move3, _move4) = match stage_section { + let (move1base, move2base, move3base, move4) = match stage_section { Some(StageSection::Movement) => (anim_time, 0.0, 0.0, 0.0), Some(StageSection::Buildup) => (1.0, anim_time, 0.0, 0.0), Some(StageSection::Shoot) => (1.0, 1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.1, 1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0, 0.0), }; - + let pullback = 1.0 - move4; + let move1 = move1base * pullback; + let move2 = move2base * pullback; + let move3 = move3base * pullback; // end spin stuff - fn fire(x: f32) -> f32 { (x * 18.0).sin() } - if let Some(ToolKind::Bow) = ability_info.and_then(|a| a.tool) { next.hand_l.position = Vec3::new(s_a.bhl.0, s_a.bhl.1, s_a.bhl.2); next.hand_l.orientation = Quaternion::rotation_x(s_a.bhl.3); @@ -54,49 +56,46 @@ impl Animation for RepeaterAnimation { next.main.position = Vec3::new(0.0, 0.0, 0.0); next.main.orientation = Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); - next.hold.position = Vec3::new(1.2, -1.0, -5.2); - next.hold.orientation = Quaternion::rotation_x(-1.7) * Quaternion::rotation_z(-0.1); - next.hold.scale = Vec3::one() * 1.0 * (1.0 - move3); + next.hold.position = Vec3::new(0.0, -1.0 + move3 * 2.0, -5.2); + next.hold.orientation = Quaternion::rotation_x(-1.57) * Quaternion::rotation_z(0.0); + next.hold.scale = Vec3::one() * (1.0); + if speed < 0.5 { + next.foot_l.position = Vec3::new( + -s_a.foot.0 + move1 * -0.75, + s_a.foot.1 + move1 * 4.0, + s_a.foot.2, + ); + next.foot_l.orientation = + Quaternion::rotation_x(move1 * 0.2 + move2 * -0.1 + move3 * -0.2) + * Quaternion::rotation_z(move3 * 0.1); - next.foot_l.position = Vec3::new( - -s_a.foot.0 + move1 * -0.75 - 0.75, - s_a.foot.1 + move1 * 4.0 + 4.0, - s_a.foot.2 + move1 * 2.5 + 2.5, - ); - next.foot_l.orientation = Quaternion::rotation_x(move1 * 0.6 + 0.6 + move2 * -0.2) - * Quaternion::rotation_z(move1 * 0.3 + 0.3); - - next.foot_r.position = Vec3::new( - s_a.foot.0 + move1 * 0.75 + 0.75, - s_a.foot.1 + move1 * 4.0 + 4.0, - s_a.foot.2 + move1 * 2.5 + 2.5, - ); - next.foot_r.orientation = Quaternion::rotation_x(move1 * 0.6 + 0.6 + move2 * -0.2) - * Quaternion::rotation_z(move1 * -0.3 - 0.3); - next.shorts.position = - Vec3::new(0.0, s_a.shorts.0 + move1 * 4.0, s_a.shorts.1 + move1 * 1.0); - next.shorts.orientation = Quaternion::rotation_x(move1 * 0.6); - next.belt.position = Vec3::new(0.0, s_a.belt.0 + move1 * 2.0, s_a.belt.1); - next.belt.orientation = Quaternion::rotation_x(move1 * 0.2); + next.foot_r.position = Vec3::new(s_a.foot.0 + move1 * 0.75, s_a.foot.1, s_a.foot.2); + next.foot_r.orientation = + Quaternion::rotation_x(move1 * 0.06 + move2 * -0.2 + move3 * -0.5) + * Quaternion::rotation_z(move1 * -0.6 + move3 * 0.8); + next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1); + next.chest.orientation = Quaternion::rotation_x(0.0); + } else { + }; + next.shorts.position = Vec3::new(0.0, s_a.shorts.0 + move1 * 2.0, s_a.shorts.1); + next.shorts.orientation = Quaternion::rotation_x(move1 * 0.2 + move3 * 0.2); + next.belt.position = Vec3::new(0.0, s_a.belt.0 + move1 * 1.0, s_a.belt.1); + next.belt.orientation = Quaternion::rotation_x(move1 * 0.1 + move3 * 0.1); next.control.position = Vec3::new( s_a.bc.0 + move1 * 5.0, s_a.bc.1 + move1 * 3.0, - s_a.bc.2 + move1 * 1.0, + s_a.bc.2 + move1 * 5.0, ); next.control.orientation = Quaternion::rotation_x(s_a.bc.3 + move1 * 0.4) * Quaternion::rotation_y(s_a.bc.4 + move1 * 0.8) * Quaternion::rotation_z(s_a.bc.5); - next.head.orientation = Quaternion::rotation_y(move1 * 0.15 + move2 * 0.05); - next.torso.orientation = - Quaternion::rotation_x(move1 * 0.1 + move2 * 0.1 + move3 * 0.15); + next.head.orientation = Quaternion::rotation_x(move1 * 0.15) + * Quaternion::rotation_y(move1 * 0.15 + move2 * 0.05); + next.torso.orientation = Quaternion::rotation_x(move1 * 0.25 + move3 * -0.2); - next.hand_l.position = Vec3::new( - 2.0 + fire(move3) * -6.0 - 3.0, - 1.5 + fire(move3) * -6.0 - 3.0, - 0.0, - ); - next.hand_l.orientation = Quaternion::rotation_x(1.20) - * Quaternion::rotation_y(-0.6) + next.hand_l.position = Vec3::new(0.0, -2.5 + move3 * -6.0, 0.0); + next.hand_l.orientation = Quaternion::rotation_x(1.5) + * Quaternion::rotation_y(-0.0) * Quaternion::rotation_z(-0.3); } diff --git a/voxygen/src/hud/diary.rs b/voxygen/src/hud/diary.rs index 1fefcee57e..4cc9925886 100644 --- a/voxygen/src/hud/diary.rs +++ b/voxygen/src/hud/diary.rs @@ -102,21 +102,21 @@ widget_ids! { skill_hammer_leap_4, skill_hammer_leap_5, bow_render, - skill_bow_basic_0, - skill_bow_basic_1, - skill_bow_basic_2, skill_bow_charged_0, skill_bow_charged_1, skill_bow_charged_2, skill_bow_charged_3, skill_bow_charged_4, skill_bow_charged_5, - skill_bow_charged_6, skill_bow_repeater_0, skill_bow_repeater_1, skill_bow_repeater_2, skill_bow_repeater_3, - skill_bow_repeater_4, + skill_bow_shotgun_0, + skill_bow_shotgun_1, + skill_bow_shotgun_2, + skill_bow_shotgun_3, + skill_bow_shotgun_4, skill_bow_passive_0, staff_render, skill_staff_basic_0, @@ -538,7 +538,7 @@ impl<'a> Widget for Diary<'a> { SelectedSkillTree::Weapon(ToolKind::Sword) => 5, SelectedSkillTree::Weapon(ToolKind::Axe) => 5, SelectedSkillTree::Weapon(ToolKind::Hammer) => 5, - SelectedSkillTree::Weapon(ToolKind::Bow) => 3, + SelectedSkillTree::Weapon(ToolKind::Bow) => 6, SelectedSkillTree::Weapon(ToolKind::Staff) => 4, SelectedSkillTree::Weapon(ToolKind::Sceptre) => 5, _ => 0, @@ -548,7 +548,7 @@ impl<'a> Widget for Diary<'a> { SelectedSkillTree::Weapon(ToolKind::Sword) => 7, SelectedSkillTree::Weapon(ToolKind::Axe) => 6, SelectedSkillTree::Weapon(ToolKind::Hammer) => 5, - SelectedSkillTree::Weapon(ToolKind::Bow) => 7, + SelectedSkillTree::Weapon(ToolKind::Bow) => 4, SelectedSkillTree::Weapon(ToolKind::Staff) => 5, SelectedSkillTree::Weapon(ToolKind::Sceptre) => 4, _ => 0, @@ -2507,72 +2507,6 @@ impl<'a> Widget for Diary<'a> { Button::image(self.imgs.bow_m1) .w_h(74.0, 74.0) .mid_top_with_margin_on(state.skills_top_l[0], 3.0) - .with_tooltip( - self.tooltip_manager, - &self.localized_strings.get("hud.skill.bow_title"), - &self.localized_strings.get("hud.skill.bow"), - &diary_tooltip, - TEXT_COLOR, - ) - .set(state.skill_bow_basic_0, ui); - let skill = Skill::Bow(BDamage); - if create_skill_button( - self.imgs.physical_damage_skill, - state.skills_top_l[1], - &self.skill_set, - skill, - self.fonts, - &get_skill_label(skill, &self.skill_set), - ) - .with_tooltip( - self.tooltip_manager, - &self.localized_strings.get("hud.skill.bow_damage_title"), - &add_sp_cost_tooltip( - &self.localized_strings.get("hud.skill.bow_damage"), - skill, - &self.skill_set, - &self.localized_strings, - ), - &diary_tooltip, - TEXT_COLOR, - ) - .set(state.skill_bow_basic_1, ui) - .was_clicked() - { - events.push(Event::UnlockSkill(skill)); - }; - let skill = Skill::Bow(BRegen); - if create_skill_button( - self.imgs.physical_energy_regen_skill, - state.skills_top_l[2], - &self.skill_set, - skill, - self.fonts, - &get_skill_label(skill, &self.skill_set), - ) - .with_tooltip( - self.tooltip_manager, - &self - .localized_strings - .get("hud.skill.bow_energy_regen_title"), - &add_sp_cost_tooltip( - &self.localized_strings.get("hud.skill.bow_energy_regen"), - skill, - &self.skill_set, - &self.localized_strings, - ), - &diary_tooltip, - TEXT_COLOR, - ) - .set(state.skill_bow_basic_2, ui) - .was_clicked() - { - events.push(Event::UnlockSkill(skill)); - }; - // Top right skills - Button::image(self.imgs.bow_m2) - .w_h(74.0, 74.0) - .mid_top_with_margin_on(state.skills_top_r[0], 3.0) .with_tooltip( self.tooltip_manager, &self.localized_strings.get("hud.skill.bow_charged_title"), @@ -2584,7 +2518,7 @@ impl<'a> Widget for Diary<'a> { let skill = Skill::Bow(CDamage); if create_skill_button( self.imgs.physical_damage_skill, - state.skills_top_r[1], + state.skills_top_l[1], &self.skill_set, skill, self.fonts, @@ -2609,10 +2543,10 @@ impl<'a> Widget for Diary<'a> { { events.push(Event::UnlockSkill(skill)); }; - let skill = Skill::Bow(CDrain); + let skill = Skill::Bow(CRegen); if create_skill_button( - self.imgs.physical_energy_drain_skill, - state.skills_top_r[2], + self.imgs.physical_energy_regen_skill, + state.skills_top_l[2], &self.skill_set, skill, self.fonts, @@ -2622,9 +2556,11 @@ impl<'a> Widget for Diary<'a> { self.tooltip_manager, &self .localized_strings - .get("hud.skill.bow_charged_drain_title"), + .get("hud.skill.bow_charged_energy_regen_title"), &add_sp_cost_tooltip( - &self.localized_strings.get("hud.skill.bow_charged_drain"), + &self + .localized_strings + .get("hud.skill.bow_charged_energy_regen"), skill, &self.skill_set, &self.localized_strings, @@ -2637,10 +2573,10 @@ impl<'a> Widget for Diary<'a> { { events.push(Event::UnlockSkill(skill)); }; - let skill = Skill::Bow(CProjSpeed); + let skill = Skill::Bow(CKnockback); if create_skill_button( - self.imgs.physical_projectile_speed_skill, - state.skills_top_r[3], + self.imgs.physical_knockback_skill, + state.skills_top_l[3], &self.skill_set, skill, self.fonts, @@ -2650,11 +2586,11 @@ impl<'a> Widget for Diary<'a> { self.tooltip_manager, &self .localized_strings - .get("hud.skill.bow_charged_projectile_speed_title"), + .get("hud.skill.bow_charged_knockback_title"), &add_sp_cost_tooltip( &self .localized_strings - .get("hud.skill.bow_charged_projectile_speed"), + .get("hud.skill.bow_charged_knockback"), skill, &self.skill_set, &self.localized_strings, @@ -2670,7 +2606,7 @@ impl<'a> Widget for Diary<'a> { let skill = Skill::Bow(CSpeed); if create_skill_button( self.imgs.physical_speed_skill, - state.skills_top_r[4], + state.skills_top_l[4], &self.skill_set, skill, self.fonts, @@ -2698,7 +2634,7 @@ impl<'a> Widget for Diary<'a> { let skill = Skill::Bow(CMove); if create_skill_button( self.imgs.physical_speed_skill, - state.skills_top_r[5], + state.skills_top_l[5], &self.skill_set, skill, self.fonts, @@ -2708,11 +2644,9 @@ impl<'a> Widget for Diary<'a> { self.tooltip_manager, &self .localized_strings - .get("hud.skill.bow_charged_move_speed_title"), + .get("hud.skill.bow_charged_move_title"), &add_sp_cost_tooltip( - &self - .localized_strings - .get("hud.skill.bow_charged_move_speed"), + &self.localized_strings.get("hud.skill.bow_charged_move"), skill, &self.skill_set, &self.localized_strings, @@ -2725,69 +2659,22 @@ impl<'a> Widget for Diary<'a> { { events.push(Event::UnlockSkill(skill)); }; - let skill = Skill::Bow(CKnockback); - if create_skill_button( - self.imgs.physical_knockback_skill, - state.skills_top_r[6], - &self.skill_set, - skill, - self.fonts, - &get_skill_label(skill, &self.skill_set), - ) - .with_tooltip( - self.tooltip_manager, - &self - .localized_strings - .get("hud.skill.bow_charged_knockback_title"), - &add_sp_cost_tooltip( - &self - .localized_strings - .get("hud.skill.bow_charged_knockback"), - skill, - &self.skill_set, - &self.localized_strings, - ), - &diary_tooltip, - TEXT_COLOR, - ) - .set(state.skill_bow_charged_6, ui) - .was_clicked() - { - events.push(Event::UnlockSkill(skill)); - }; - // Bottom left skills - let skill = Skill::Bow(UnlockRepeater); - if create_skill_button( - self.imgs.skill_bow_jump_burst, - state.skills_bot_l[0], - &self.skill_set, - skill, - self.fonts, - &get_skill_label(skill, &self.skill_set), - ) - .with_tooltip( - self.tooltip_manager, - &self - .localized_strings - .get("hud.skill.bow_repeater_unlock_title"), - &add_sp_cost_tooltip( - &self.localized_strings.get("hud.skill.bow_repeater_unlock"), - skill, - &self.skill_set, - &self.localized_strings, - ), - &diary_tooltip, - TEXT_COLOR, - ) - .set(state.skill_bow_repeater_0, ui) - .was_clicked() - { - events.push(Event::UnlockSkill(skill)); - }; + // Top right skills + Button::image(self.imgs.bow_m2) + .w_h(74.0, 74.0) + .mid_top_with_margin_on(state.skills_top_r[0], 3.0) + .with_tooltip( + self.tooltip_manager, + &self.localized_strings.get("hud.skill.bow_repeater_title"), + &self.localized_strings.get("hud.skill.bow_repeater"), + &diary_tooltip, + TEXT_COLOR, + ) + .set(state.skill_bow_repeater_0, ui); let skill = Skill::Bow(RDamage); if create_skill_button( self.imgs.physical_damage_skill, - state.skills_bot_l[1], + state.skills_top_r[1], &self.skill_set, skill, self.fonts, @@ -2812,38 +2699,10 @@ impl<'a> Widget for Diary<'a> { { events.push(Event::UnlockSkill(skill)); }; - let skill = Skill::Bow(RGlide); - if create_skill_button( - self.imgs.physical_helicopter_skill, - state.skills_bot_l[2], - &self.skill_set, - skill, - self.fonts, - &get_skill_label(skill, &self.skill_set), - ) - .with_tooltip( - self.tooltip_manager, - &self - .localized_strings - .get("hud.skill.bow_repeater_glide_title"), - &add_sp_cost_tooltip( - &self.localized_strings.get("hud.skill.bow_repeater_glide"), - skill, - &self.skill_set, - &self.localized_strings, - ), - &diary_tooltip, - TEXT_COLOR, - ) - .set(state.skill_bow_repeater_2, ui) - .was_clicked() - { - events.push(Event::UnlockSkill(skill)); - }; let skill = Skill::Bow(RCost); if create_skill_button( self.imgs.physical_cost_skill, - state.skills_bot_l[3], + state.skills_top_r[2], &self.skill_set, skill, self.fonts, @@ -2863,14 +2722,157 @@ impl<'a> Widget for Diary<'a> { &diary_tooltip, TEXT_COLOR, ) + .set(state.skill_bow_repeater_2, ui) + .was_clicked() + { + events.push(Event::UnlockSkill(skill)); + }; + let skill = Skill::Bow(RSpeed); + if create_skill_button( + self.imgs.physical_speed_skill, + state.skills_top_r[3], + &self.skill_set, + skill, + self.fonts, + &get_skill_label(skill, &self.skill_set), + ) + .with_tooltip( + self.tooltip_manager, + &self + .localized_strings + .get("hud.skill.bow_repeater_speed_title"), + &add_sp_cost_tooltip( + &self.localized_strings.get("hud.skill.bow_repeater_speed"), + skill, + &self.skill_set, + &self.localized_strings, + ), + &diary_tooltip, + TEXT_COLOR, + ) .set(state.skill_bow_repeater_3, ui) .was_clicked() { events.push(Event::UnlockSkill(skill)); }; - let skill = Skill::Bow(RArrows); + // Bottom left skills + let skill = Skill::Bow(UnlockShotgun); + if create_skill_button( + self.imgs.skill_bow_jump_burst, + state.skills_bot_l[0], + &self.skill_set, + skill, + self.fonts, + &get_skill_label(skill, &self.skill_set), + ) + .with_tooltip( + self.tooltip_manager, + &self + .localized_strings + .get("hud.skill.bow_shotgun_unlock_title"), + &add_sp_cost_tooltip( + &self.localized_strings.get("hud.skill.bow_shotgun_unlock"), + skill, + &self.skill_set, + &self.localized_strings, + ), + &diary_tooltip, + TEXT_COLOR, + ) + .set(state.skill_bow_shotgun_0, ui) + .was_clicked() + { + events.push(Event::UnlockSkill(skill)); + }; + let skill = Skill::Bow(SDamage); + if create_skill_button( + self.imgs.physical_damage_skill, + state.skills_bot_l[1], + &self.skill_set, + skill, + self.fonts, + &get_skill_label(skill, &self.skill_set), + ) + .with_tooltip( + self.tooltip_manager, + &self + .localized_strings + .get("hud.skill.bow_shotgun_damage_title"), + &add_sp_cost_tooltip( + &self.localized_strings.get("hud.skill.bow_shotgun_damage"), + skill, + &self.skill_set, + &self.localized_strings, + ), + &diary_tooltip, + TEXT_COLOR, + ) + .set(state.skill_bow_shotgun_1, ui) + .was_clicked() + { + events.push(Event::UnlockSkill(skill)); + }; + let skill = Skill::Bow(SCost); + if create_skill_button( + self.imgs.physical_cost_skill, + state.skills_bot_l[2], + &self.skill_set, + skill, + self.fonts, + &get_skill_label(skill, &self.skill_set), + ) + .with_tooltip( + self.tooltip_manager, + &self + .localized_strings + .get("hud.skill.bow_shotgun_cost_title"), + &add_sp_cost_tooltip( + &self.localized_strings.get("hud.skill.bow_shotgun_cost"), + skill, + &self.skill_set, + &self.localized_strings, + ), + &diary_tooltip, + TEXT_COLOR, + ) + .set(state.skill_bow_shotgun_2, ui) + .was_clicked() + { + events.push(Event::UnlockSkill(skill)); + }; + let skill = Skill::Bow(SArrows); if create_skill_button( self.imgs.physical_amount_skill, + state.skills_bot_l[3], + &self.skill_set, + skill, + self.fonts, + &get_skill_label(skill, &self.skill_set), + ) + .with_tooltip( + self.tooltip_manager, + &self + .localized_strings + .get("hud.skill.bow_shotgun_arrow_count_title"), + &add_sp_cost_tooltip( + &self + .localized_strings + .get("hud.skill.bow_shotgun_arrow_count"), + skill, + &self.skill_set, + &self.localized_strings, + ), + &diary_tooltip, + TEXT_COLOR, + ) + .set(state.skill_bow_shotgun_3, ui) + .was_clicked() + { + events.push(Event::UnlockSkill(skill)); + }; + let skill = Skill::Bow(SSpread); + if create_skill_button( + self.imgs.physical_explosion_skill, state.skills_bot_l[4], &self.skill_set, skill, @@ -2881,9 +2883,9 @@ impl<'a> Widget for Diary<'a> { self.tooltip_manager, &self .localized_strings - .get("hud.skill.bow_arrow_count_title"), + .get("hud.skill.bow_shotgun_spread_title"), &add_sp_cost_tooltip( - &self.localized_strings.get("hud.skill.bow_arrow_count"), + &self.localized_strings.get("hud.skill.bow_shotgun_spread"), skill, &self.skill_set, &self.localized_strings, @@ -2891,7 +2893,7 @@ impl<'a> Widget for Diary<'a> { &diary_tooltip, TEXT_COLOR, ) - .set(state.skill_bow_repeater_4, ui) + .set(state.skill_bow_shotgun_4, ui) .was_clicked() { events.push(Event::UnlockSkill(skill)); diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index b4b16a1778..0f5537c113 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -1015,9 +1015,6 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Movement => { - stage_time / s.static_data.movement_duration.as_secs_f32() - }, StageSection::Shoot => { stage_time / s.static_data.shoot_duration.as_secs_f32() },