mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'knightresspaladin/healing-aura' into 'master'
Knightresspaladin/healing aura See merge request veloren/veloren!2587
This commit is contained in:
commit
f38cde9d40
@ -79,7 +79,7 @@
|
||||
),
|
||||
Tool(Sceptre): (
|
||||
primary: "common.abilities.sceptre.lifestealbeam",
|
||||
secondary: "common.abilities.sceptre.healingbeam",
|
||||
secondary: "common.abilities.sceptre.healingaura",
|
||||
abilities: [
|
||||
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
|
||||
],
|
||||
@ -191,7 +191,7 @@
|
||||
abilities: [],
|
||||
),
|
||||
Custom("Quad Low Beam"): (
|
||||
primary: "common.abilities.custom.quadlowbeam.healingbeam",
|
||||
primary: "common.abilities.custom.quadlowbeam.lifestealbeam",
|
||||
secondary: "common.abilities.custom.quadlowbreathe.triplestrike",
|
||||
abilities: [
|
||||
(None, "common.abilities.custom.quadlowbreathe.dash"),
|
||||
|
@ -11,5 +11,5 @@ BasicBeam(
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Normal,
|
||||
ori_rate: 0.3,
|
||||
specifier: HealingBeam,
|
||||
specifier: LifestealBeam,
|
||||
)
|
17
assets/common/abilities/sceptre/healingaura.ron
Normal file
17
assets/common/abilities/sceptre/healingaura.ron
Normal file
@ -0,0 +1,17 @@
|
||||
BasicAura(
|
||||
buildup_duration: 0.25,
|
||||
cast_duration: 0.5,
|
||||
recover_duration: 0.25,
|
||||
targets: InGroup,
|
||||
aura: (
|
||||
kind: Regeneration,
|
||||
strength: 2.0,
|
||||
duration: Some(10.0),
|
||||
category: Magical,
|
||||
),
|
||||
aura_duration: 1.0,
|
||||
range: 25.0,
|
||||
energy_cost: 200,
|
||||
scales_with_combo: true,
|
||||
specifier: HealingAura,
|
||||
)
|
@ -1,11 +0,0 @@
|
||||
HealingBeam(
|
||||
buildup_duration: 0.25,
|
||||
recover_duration: 0.25,
|
||||
beam_duration: 1.0,
|
||||
heal: 40,
|
||||
tick_rate: 2.0,
|
||||
range: 25.0,
|
||||
max_angle: 1.0,
|
||||
energy_cost: 75,
|
||||
specifier: HealingBeam,
|
||||
)
|
@ -12,4 +12,6 @@ BasicAura(
|
||||
aura_duration: 1.0,
|
||||
range: 25.0,
|
||||
energy_cost: 400,
|
||||
)
|
||||
scales_with_combo: false,
|
||||
specifier: WardingAura,
|
||||
)
|
||||
|
@ -64,8 +64,9 @@
|
||||
Sceptre(LLifesteal): Some(3),
|
||||
Sceptre(LRegen): Some(2),
|
||||
Sceptre(HHeal): Some(3),
|
||||
Sceptre(HCost): Some(2),
|
||||
Sceptre(HRange): Some(2),
|
||||
Sceptre(HDuration): Some(2),
|
||||
Sceptre(HCost): Some(2),
|
||||
Sceptre(AStrength): Some(2),
|
||||
Sceptre(ADuration): Some(2),
|
||||
Sceptre(ARange): Some(2),
|
||||
|
@ -101,8 +101,9 @@
|
||||
Sceptre(LLifesteal),
|
||||
Sceptre(LRegen),
|
||||
Sceptre(HHeal),
|
||||
Sceptre(HCost),
|
||||
Sceptre(HRange),
|
||||
Sceptre(HDuration),
|
||||
Sceptre(HCost),
|
||||
Sceptre(UnlockAura),
|
||||
Sceptre(AStrength),
|
||||
Sceptre(ADuration),
|
||||
|
@ -9,9 +9,9 @@
|
||||
|
||||
// Heal
|
||||
Skill((Sceptre(HHeal), Some(1))),
|
||||
Skill((Sceptre(HCost), Some(1))),
|
||||
Skill((Sceptre(HDuration), Some(1))),
|
||||
Skill((Sceptre(HRange), Some(1))),
|
||||
|
||||
Skill((Sceptre(HCost), Some(1))),
|
||||
// Ward
|
||||
Skill((Sceptre(UnlockAura), None)),
|
||||
Skill((Sceptre(AStrength), Some(1))),
|
||||
|
@ -802,7 +802,7 @@
|
||||
],
|
||||
threshold: 0.2,
|
||||
),
|
||||
HealingBeam: (
|
||||
SceptreBeam: (
|
||||
files: [
|
||||
"voxygen.audio.sfx.abilities.sceptre_channeling",
|
||||
],
|
||||
|
BIN
assets/voxygen/element/skills/heal_aoe.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/skills/heal_aoe.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/skills/sceptre_protection.png
(Stored with Git LFS)
BIN
assets/voxygen/element/skills/sceptre_protection.png
(Stored with Git LFS)
Binary file not shown.
@ -54,14 +54,16 @@
|
||||
"hud.skill.sc_lifesteal_lifesteal": "Convert an additional {boost}% of damage into health{SP}",
|
||||
"hud.skill.sc_lifesteal_regen_title": "Stamina Regen",
|
||||
"hud.skill.sc_lifesteal_regen": "Replenish your stamina by an additional {boost}%{SP}",
|
||||
"hud.skill.sc_heal_title": "Healing Beam",
|
||||
"hud.skill.sc_heal": "Heal your allies using the blood of your enemies",
|
||||
"hud.skill.sc_heal_title": "Healing Aura",
|
||||
"hud.skill.sc_heal": "Heal your allies using the blood of your enemies, requires combo to activate",
|
||||
"hud.skill.sc_heal_heal_title": "Heal",
|
||||
"hud.skill.sc_heal_heal": "Increases the amount you heal others by {boost}%{SP}",
|
||||
"hud.skill.sc_heal_heal": "Increases the amount you heal by {boost}%{SP}",
|
||||
"hud.skill.sc_heal_cost_title": "Stamina Cost",
|
||||
"hud.skill.sc_heal_cost": "Healing others requires {boost}% less stamina{SP}",
|
||||
"hud.skill.sc_heal_range_title": "Range",
|
||||
"hud.skill.sc_heal_range": "Your beam reachs {boost}% further{SP}",
|
||||
"hud.skill.sc_heal_cost": "Healing requires {boost}% less stamina{SP}",
|
||||
"hud.skill.sc_heal_duration_title": "Duration",
|
||||
"hud.skill.sc_heal_duration": "The effects of your healing aura last {boost}% longer{SP}",
|
||||
"hud.skill.sc_heal_range_title": "Radius",
|
||||
"hud.skill.sc_heal_range": "Your healing aura reachs {boost}% further{SP}",
|
||||
"hud.skill.sc_wardaura_unlock_title": "Warding Aura Unlock",
|
||||
"hud.skill.sc_wardaura_unlock": "Allows you to ward your allies against enemy attacks{SP}",
|
||||
"hud.skill.sc_wardaura_strength_title": "Strength",
|
||||
|
@ -52,7 +52,7 @@ const int LEAF = 10;
|
||||
const int FIREFLY = 11;
|
||||
const int BEE = 12;
|
||||
const int GROUND_SHOCKWAVE = 13;
|
||||
const int HEALING_BEAM = 14;
|
||||
const int ENERGY_HEALING = 14;
|
||||
const int ENERGY_NATURE = 15;
|
||||
const int FLAMETHROWER = 16;
|
||||
const int FIRE_SHOCKWAVE = 17;
|
||||
@ -380,13 +380,14 @@ void main() {
|
||||
spin_in_axis(vec3(1,0,0),0)
|
||||
);
|
||||
break;
|
||||
case HEALING_BEAM:
|
||||
case ENERGY_HEALING:
|
||||
f_reflect = 0.0;
|
||||
float spiral_radius = start_end(1 - pow(abs(rand5), 5), 1) * length(inst_dir);
|
||||
attr = Attr(
|
||||
spiral_motion(inst_dir, 0.3 * (floor(2 * rand0 + 0.5) - 0.5) * min(linear_scale(10), 1), lifetime / inst_lifespan, 10.0, inst_time),
|
||||
vec3((1.7 - 0.7 * abs(floor(2 * rand0 - 0.5) + 0.5)) * (1.5 + 0.5 * sin(tick.x * 10 - lifetime * 4))),
|
||||
vec4(vec3(0.4, 1.6 + 0.3 * sin(tick.x * 10 - lifetime * 3 + 4), 1.0 + 0.15 * sin(tick.x * 5 - lifetime * 5)), 1 /*0.3*/),
|
||||
spin_in_axis(inst_dir, tick.z)
|
||||
spiral_motion(vec3(0, 0, rand3 + 1), spiral_radius, lifetime, abs(rand0), rand1 * 2 * PI) + vec3(0, 0, rand2),
|
||||
vec3(6 * abs(rand4) * (1 - slow_start(2)) * pow(spiral_radius / length(inst_dir), 0.5)),
|
||||
vec4(vec3(0, 1.7, 0.7), 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3)
|
||||
);
|
||||
break;
|
||||
case LIFESTEAL_BEAM:
|
||||
@ -402,7 +403,7 @@ void main() {
|
||||
break;
|
||||
case ENERGY_NATURE:
|
||||
f_reflect = 0.0;
|
||||
float spiral_radius = start_end(1 - pow(abs(rand5), 5), 1) * length(inst_dir);
|
||||
spiral_radius = start_end(1 - pow(abs(rand5), 5), 1) * length(inst_dir);
|
||||
attr = Attr(
|
||||
spiral_motion(vec3(0, 0, rand3 + 1), spiral_radius, lifetime, abs(rand0), rand1 * 2 * PI) + vec3(0, 0, rand2),
|
||||
vec3(6 * abs(rand4) * (1 - slow_start(2)) * pow(spiral_radius / length(inst_dir), 0.5)),
|
||||
|
@ -264,17 +264,8 @@ pub enum CharacterAbility {
|
||||
aura_duration: f32,
|
||||
range: f32,
|
||||
energy_cost: f32,
|
||||
},
|
||||
HealingBeam {
|
||||
buildup_duration: f32,
|
||||
recover_duration: f32,
|
||||
beam_duration: f32,
|
||||
heal: f32,
|
||||
tick_rate: f32,
|
||||
range: f32,
|
||||
max_angle: f32,
|
||||
energy_cost: f32,
|
||||
specifier: beam::FrontendSpecifier,
|
||||
scales_with_combo: bool,
|
||||
specifier: aura::Specifier,
|
||||
},
|
||||
Blink {
|
||||
buildup_duration: f32,
|
||||
@ -356,7 +347,6 @@ impl CharacterAbility {
|
||||
| CharacterAbility::ChargedRanged { energy_cost, .. }
|
||||
| CharacterAbility::ChargedMelee { energy_cost, .. }
|
||||
| CharacterAbility::Shockwave { energy_cost, .. }
|
||||
| CharacterAbility::BasicAura { energy_cost, .. }
|
||||
| CharacterAbility::BasicBlock { energy_cost, .. }
|
||||
| CharacterAbility::SelfBuff { energy_cost, .. } => update
|
||||
.energy
|
||||
@ -373,7 +363,17 @@ impl CharacterAbility {
|
||||
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
|
||||
.is_ok()
|
||||
},
|
||||
CharacterAbility::HealingBeam { .. } => data.combo.counter() > 0,
|
||||
CharacterAbility::BasicAura {
|
||||
energy_cost,
|
||||
scales_with_combo,
|
||||
..
|
||||
} => {
|
||||
((*scales_with_combo && data.combo.counter() > 0) | !*scales_with_combo)
|
||||
&& update
|
||||
.energy
|
||||
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
|
||||
.is_ok()
|
||||
},
|
||||
CharacterAbility::ComboMelee { .. }
|
||||
| CharacterAbility::Boost { .. }
|
||||
| CharacterAbility::BasicBeam { .. }
|
||||
@ -782,6 +782,8 @@ impl CharacterAbility {
|
||||
aura_duration: _,
|
||||
ref mut range,
|
||||
ref mut energy_cost,
|
||||
scales_with_combo: _,
|
||||
specifier: _,
|
||||
} => {
|
||||
*buildup_duration /= stats.speed;
|
||||
*cast_duration /= stats.speed;
|
||||
@ -792,26 +794,6 @@ impl CharacterAbility {
|
||||
*range *= stats.range;
|
||||
*energy_cost /= stats.energy_efficiency;
|
||||
},
|
||||
HealingBeam {
|
||||
ref mut buildup_duration,
|
||||
ref mut recover_duration,
|
||||
ref mut beam_duration,
|
||||
ref mut heal,
|
||||
ref mut tick_rate,
|
||||
ref mut range,
|
||||
max_angle: _,
|
||||
ref mut energy_cost,
|
||||
specifier: _,
|
||||
} => {
|
||||
*buildup_duration /= stats.speed;
|
||||
*recover_duration /= stats.speed;
|
||||
*heal *= stats.power;
|
||||
*tick_rate *= stats.speed;
|
||||
*range *= stats.range;
|
||||
// Duration modified to keep velocity constant
|
||||
*beam_duration *= stats.range;
|
||||
*energy_cost /= stats.energy_efficiency;
|
||||
},
|
||||
Blink {
|
||||
ref mut buildup_duration,
|
||||
ref mut recover_duration,
|
||||
@ -886,7 +868,6 @@ impl CharacterAbility {
|
||||
| ChargedMelee { energy_cost, .. }
|
||||
| ChargedRanged { energy_cost, .. }
|
||||
| Shockwave { energy_cost, .. }
|
||||
| HealingBeam { energy_cost, .. }
|
||||
| BasicAura { energy_cost, .. }
|
||||
| BasicBlock { energy_cost, .. }
|
||||
| SelfBuff { energy_cost, .. } => *energy_cost as u32,
|
||||
@ -1381,45 +1362,39 @@ impl CharacterAbility {
|
||||
*lifesteal *= 1.15_f32.powi(level.into());
|
||||
}
|
||||
},
|
||||
HealingBeam {
|
||||
ref mut heal,
|
||||
ref mut energy_cost,
|
||||
ref mut range,
|
||||
ref mut beam_duration,
|
||||
..
|
||||
} => {
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(HHeal)) {
|
||||
*heal *= 1.2_f32.powi(level.into());
|
||||
}
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(HRange)) {
|
||||
let range_mod = 1.2_f32.powi(level.into());
|
||||
*range *= range_mod;
|
||||
// Duration modified to keep velocity constant
|
||||
*beam_duration *= range_mod;
|
||||
}
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(HCost)) {
|
||||
*energy_cost *= 0.8_f32.powi(level.into());
|
||||
}
|
||||
},
|
||||
BasicAura {
|
||||
ref mut aura,
|
||||
ref mut range,
|
||||
ref mut energy_cost,
|
||||
ref specifier,
|
||||
..
|
||||
} => {
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(AStrength)) {
|
||||
aura.strength *= 1.15_f32.powi(level.into());
|
||||
}
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(ADuration)) {
|
||||
if let Some(ref mut duration) = aura.duration {
|
||||
*duration *= 1.2_f32.powi(level.into());
|
||||
if matches!(*specifier, aura::Specifier::WardingAura) {
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(AStrength)) {
|
||||
aura.strength *= 1.15_f32.powi(level.into());
|
||||
}
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(ADuration)) {
|
||||
aura.duration.map(|dur| dur * 1.2_f32.powi(level.into()));
|
||||
}
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(ARange)) {
|
||||
*range *= 1.25_f32.powi(level.into());
|
||||
}
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(ACost)) {
|
||||
*energy_cost *= 0.85_f32.powi(level.into());
|
||||
}
|
||||
} else if matches!(*specifier, aura::Specifier::HealingAura) {
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(HHeal)) {
|
||||
aura.strength *= 1.15_f32.powi(level.into());
|
||||
}
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(HDuration)) {
|
||||
aura.duration.map(|dur| dur * 1.2_f32.powi(level.into()));
|
||||
}
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(HRange)) {
|
||||
*range *= 1.25_f32.powi(level.into());
|
||||
}
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(HCost)) {
|
||||
*energy_cost *= 0.85_f32.powi(level.into());
|
||||
}
|
||||
}
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(ARange)) {
|
||||
*range *= 1.25_f32.powi(level.into());
|
||||
}
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(ACost)) {
|
||||
*energy_cost *= 0.85_f32.powi(level.into());
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
@ -1935,6 +1910,8 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
aura_duration,
|
||||
range,
|
||||
energy_cost: _,
|
||||
scales_with_combo,
|
||||
specifier,
|
||||
} => CharacterState::BasicAura(basic_aura::Data {
|
||||
static_data: basic_aura::StaticData {
|
||||
buildup_duration: Duration::from_secs_f32(*buildup_duration),
|
||||
@ -1945,31 +1922,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
aura_duration: Duration::from_secs_f32(*aura_duration),
|
||||
range: *range,
|
||||
ability_info,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
}),
|
||||
CharacterAbility::HealingBeam {
|
||||
buildup_duration,
|
||||
recover_duration,
|
||||
beam_duration,
|
||||
heal,
|
||||
tick_rate,
|
||||
range,
|
||||
max_angle,
|
||||
energy_cost,
|
||||
specifier,
|
||||
} => CharacterState::HealingBeam(healing_beam::Data {
|
||||
static_data: healing_beam::StaticData {
|
||||
buildup_duration: Duration::from_secs_f32(*buildup_duration),
|
||||
recover_duration: Duration::from_secs_f32(*recover_duration),
|
||||
beam_duration: Duration::from_secs_f32(*beam_duration),
|
||||
heal: *heal,
|
||||
tick_rate: *tick_rate,
|
||||
range: *range,
|
||||
max_angle: *max_angle,
|
||||
energy_cost: *energy_cost,
|
||||
ability_info,
|
||||
scales_with_combo: *scales_with_combo,
|
||||
specifier: *specifier,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
|
@ -71,6 +71,12 @@ pub enum AuraTarget {
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum Specifier {
|
||||
WardingAura,
|
||||
HealingAura,
|
||||
}
|
||||
|
||||
impl From<(Option<GroupTarget>, Option<&Uid>)> for AuraTarget {
|
||||
fn from((target, uid): (Option<GroupTarget>, Option<&Uid>)) -> Self {
|
||||
match (target, uid) {
|
||||
|
@ -50,7 +50,6 @@ impl Component for Beam {
|
||||
pub enum FrontendSpecifier {
|
||||
Flamethrower,
|
||||
LifestealBeam,
|
||||
HealingBeam,
|
||||
Cultist,
|
||||
ClayGolem,
|
||||
Bubbles,
|
||||
|
@ -93,11 +93,6 @@ pub enum CharacterState {
|
||||
BasicBeam(basic_beam::Data),
|
||||
/// Creates an aura that persists as long as you are actively casting
|
||||
BasicAura(basic_aura::Data),
|
||||
/// A directed beam that heals targets in range. This is separate from basic
|
||||
/// beam as a large amount of functionality needed to be special cased
|
||||
/// specifically for the healing beam. There was also functionality present
|
||||
/// on basic beam which was unnecessary for the healing beam.
|
||||
HealingBeam(healing_beam::Data),
|
||||
/// A short teleport that targets either a position or entity
|
||||
Blink(blink::Data),
|
||||
/// Summons creatures that fight for the caster
|
||||
@ -128,7 +123,6 @@ impl CharacterState {
|
||||
| CharacterState::Shockwave(_)
|
||||
| CharacterState::BasicBeam(_)
|
||||
| CharacterState::BasicAura(_)
|
||||
| CharacterState::HealingBeam(_)
|
||||
| CharacterState::SelfBuff(_)
|
||||
| CharacterState::Blink(_)
|
||||
| CharacterState::BasicSummon(_)
|
||||
@ -155,7 +149,6 @@ impl CharacterState {
|
||||
| CharacterState::Shockwave(_)
|
||||
| CharacterState::BasicBeam(_)
|
||||
| CharacterState::BasicAura(_)
|
||||
| CharacterState::HealingBeam(_)
|
||||
| CharacterState::SelfBuff(_)
|
||||
| CharacterState::Blink(_)
|
||||
| CharacterState::BasicSummon(_)
|
||||
@ -181,7 +174,6 @@ impl CharacterState {
|
||||
| CharacterState::UseItem(_)
|
||||
| CharacterState::Wielding
|
||||
| CharacterState::Talk
|
||||
| CharacterState::HealingBeam(_)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -478,8 +478,9 @@ pub enum SceptreSkill {
|
||||
LRegen,
|
||||
// Healing beam upgrades
|
||||
HHeal,
|
||||
HCost,
|
||||
HRange,
|
||||
HDuration,
|
||||
HCost,
|
||||
// Warding aura upgrades
|
||||
UnlockAura,
|
||||
AStrength,
|
||||
@ -499,6 +500,7 @@ impl Boost for SceptreSkill {
|
||||
// Healing beam upgrades
|
||||
Self::HHeal => 20.into(),
|
||||
Self::HRange => 20.into(),
|
||||
Self::HDuration => 20.into(),
|
||||
Self::HCost => (-20_i16).into(),
|
||||
// Warding aura upgrades
|
||||
Self::AStrength => 15.into(),
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
combat::GroupTarget,
|
||||
comp::{
|
||||
aura::{AuraBuffConstructor, AuraChange, AuraTarget},
|
||||
aura::{AuraBuffConstructor, AuraChange, AuraKind, AuraTarget, Specifier},
|
||||
CharacterState, StateUpdate,
|
||||
},
|
||||
event::ServerEvent,
|
||||
@ -32,6 +32,10 @@ pub struct StaticData {
|
||||
pub range: f32,
|
||||
/// What key is used to press ability
|
||||
pub ability_info: AbilityInfo,
|
||||
/// Whether the aura's effect scales with the user's current combo
|
||||
pub scales_with_combo: bool,
|
||||
/// Used to specify aura to the frontend
|
||||
pub specifier: Specifier,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@ -65,12 +69,29 @@ impl CharacterBehavior for Data {
|
||||
// Creates aura
|
||||
let targets =
|
||||
AuraTarget::from((Some(self.static_data.targets), Some(data.uid)));
|
||||
let aura = self.static_data.aura.to_aura(
|
||||
let mut aura = self.static_data.aura.to_aura(
|
||||
data.uid,
|
||||
self.static_data.range,
|
||||
Some(self.static_data.aura_duration),
|
||||
targets,
|
||||
);
|
||||
if self.static_data.scales_with_combo {
|
||||
let combo = data.combo.counter();
|
||||
match aura.aura_kind {
|
||||
AuraKind::Buff {
|
||||
kind: _,
|
||||
ref mut data,
|
||||
category: _,
|
||||
source: _,
|
||||
} => {
|
||||
data.strength *= 1.0 + (combo as f32).log(2.0_f32);
|
||||
},
|
||||
}
|
||||
update.server_events.push_front(ServerEvent::ComboChange {
|
||||
entity: data.entity,
|
||||
change: -(combo as i32),
|
||||
});
|
||||
}
|
||||
update.server_events.push_front(ServerEvent::Aura {
|
||||
entity: data.entity,
|
||||
aura_change: AuraChange::Add(aura),
|
||||
|
@ -1,155 +0,0 @@
|
||||
use crate::{
|
||||
combat::{Attack, AttackEffect, CombatEffect, CombatRequirement, GroupTarget},
|
||||
comp::{beam, CharacterState, Ori, Pos, StateUpdate},
|
||||
event::ServerEvent,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
},
|
||||
uid::Uid,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use vek::*;
|
||||
|
||||
/// Separated out to condense update portions of character state
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct StaticData {
|
||||
/// How long until state should deal damage or heal
|
||||
pub buildup_duration: Duration,
|
||||
/// How long the state has until exiting
|
||||
pub recover_duration: Duration,
|
||||
/// How long each beam segment persists for
|
||||
pub beam_duration: Duration,
|
||||
/// Base healing per tick
|
||||
pub heal: f32,
|
||||
/// Ticks of healing per second
|
||||
pub tick_rate: f32,
|
||||
/// Max range
|
||||
pub range: f32,
|
||||
/// Max angle (45.0 will give you a 90.0 angle window)
|
||||
pub max_angle: f32,
|
||||
/// Energy consumed per second for heal ticks
|
||||
pub energy_cost: f32,
|
||||
/// What key is used to press ability
|
||||
pub ability_info: AbilityInfo,
|
||||
/// Used to specify the beam to the frontend
|
||||
pub specifier: beam::FrontendSpecifier,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Data {
|
||||
/// Struct containing data that does not change over the course of the
|
||||
/// character state
|
||||
pub static_data: StaticData,
|
||||
/// Timer for each stage
|
||||
pub timer: Duration,
|
||||
/// What section the character stage is in
|
||||
pub stage_section: StageSection,
|
||||
}
|
||||
|
||||
impl CharacterBehavior for Data {
|
||||
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
|
||||
handle_orientation(data, &mut update, 0.6);
|
||||
handle_move(data, &mut update, 0.4);
|
||||
handle_jump(data, &mut update, 1.0);
|
||||
|
||||
match self.stage_section {
|
||||
StageSection::Buildup => {
|
||||
if self.timer < self.static_data.buildup_duration {
|
||||
// Build up
|
||||
update.character = CharacterState::HealingBeam(Data {
|
||||
timer: tick_attack_or_default(data, self.timer, None),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Creates beam
|
||||
data.updater.insert(data.entity, beam::Beam {
|
||||
hit_entities: Vec::<Uid>::new(),
|
||||
tick_dur: Duration::from_secs_f32(1.0 / self.static_data.tick_rate),
|
||||
timer: Duration::default(),
|
||||
});
|
||||
// Build up
|
||||
update.character = CharacterState::HealingBeam(Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Cast,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Cast => {
|
||||
if input_is_pressed(data, self.static_data.ability_info.input) {
|
||||
let speed =
|
||||
self.static_data.range / self.static_data.beam_duration.as_secs_f32();
|
||||
let heal = AttackEffect::new(
|
||||
Some(GroupTarget::InGroup),
|
||||
CombatEffect::Heal(self.static_data.heal),
|
||||
)
|
||||
.with_requirement(CombatRequirement::Energy(self.static_data.energy_cost))
|
||||
.with_requirement(CombatRequirement::Combo(1));
|
||||
let attack = Attack::default().with_effect(heal);
|
||||
|
||||
let properties = beam::Properties {
|
||||
attack,
|
||||
angle: self.static_data.max_angle.to_radians(),
|
||||
speed,
|
||||
duration: self.static_data.beam_duration,
|
||||
owner: Some(*data.uid),
|
||||
specifier: self.static_data.specifier,
|
||||
};
|
||||
// Gets offsets
|
||||
let body_offsets = Vec3::new(
|
||||
(data.body.radius() + 0.2) * data.inputs.look_dir.x,
|
||||
(data.body.radius() + 0.2) * data.inputs.look_dir.y,
|
||||
data.body.eye_height() * 0.6,
|
||||
);
|
||||
let pos = Pos(data.pos.0 + body_offsets);
|
||||
// Create beam segment
|
||||
update.server_events.push_front(ServerEvent::BeamSegment {
|
||||
properties,
|
||||
pos,
|
||||
ori: Ori::from(data.inputs.look_dir),
|
||||
});
|
||||
update.character = CharacterState::HealingBeam(Data {
|
||||
timer: tick_attack_or_default(data, self.timer, None),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
update.character = CharacterState::HealingBeam(Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Recover,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Recover => {
|
||||
if self.timer < self.static_data.recover_duration {
|
||||
update.character = CharacterState::HealingBeam(Data {
|
||||
timer: tick_attack_or_default(data, self.timer, None),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
update.character = CharacterState::Wielding;
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<beam::Beam>(data.entity);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
update.character = CharacterState::Wielding;
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<beam::Beam>(data.entity);
|
||||
},
|
||||
}
|
||||
|
||||
// At end of state logic so an interrupt isn't overwritten
|
||||
if !input_is_pressed(data, self.static_data.ability_info.input) {
|
||||
handle_state_interrupt(data, &mut update, false);
|
||||
}
|
||||
|
||||
update
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@ pub mod dash_melee;
|
||||
pub mod equipping;
|
||||
pub mod glide;
|
||||
pub mod glide_wield;
|
||||
pub mod healing_beam;
|
||||
pub mod idle;
|
||||
pub mod leap_melee;
|
||||
pub mod repeater_ranged;
|
||||
|
@ -339,7 +339,6 @@ impl<'a> System<'a> for Sys {
|
||||
CharacterState::Shockwave(data) => data.handle_event(&j, action),
|
||||
CharacterState::BasicBeam(data) => data.handle_event(&j, action),
|
||||
CharacterState::BasicAura(data) => data.handle_event(&j, action),
|
||||
CharacterState::HealingBeam(data) => data.handle_event(&j, action),
|
||||
CharacterState::Blink(data) => data.handle_event(&j, action),
|
||||
CharacterState::BasicSummon(data) => data.handle_event(&j, action),
|
||||
CharacterState::SelfBuff(data) => data.handle_event(&j, action),
|
||||
@ -396,7 +395,6 @@ impl<'a> System<'a> for Sys {
|
||||
CharacterState::Shockwave(data) => data.behavior(&j),
|
||||
CharacterState::BasicBeam(data) => data.behavior(&j),
|
||||
CharacterState::BasicAura(data) => data.behavior(&j),
|
||||
CharacterState::HealingBeam(data) => data.behavior(&j),
|
||||
CharacterState::Blink(data) => data.behavior(&j),
|
||||
CharacterState::BasicSummon(data) => data.behavior(&j),
|
||||
CharacterState::SelfBuff(data) => data.behavior(&j),
|
||||
|
@ -276,7 +276,6 @@ impl<'a> System<'a> for Sys {
|
||||
| CharacterState::Shockwave { .. }
|
||||
| CharacterState::BasicBeam { .. }
|
||||
| CharacterState::BasicAura { .. }
|
||||
| CharacterState::HealingBeam { .. }
|
||||
| CharacterState::Blink { .. }
|
||||
| CharacterState::BasicSummon { .. }
|
||||
| CharacterState::SelfBuff { .. }
|
||||
|
@ -122,8 +122,9 @@ pub fn skill_to_db_string(skill: comp::skills::Skill) -> String {
|
||||
Sceptre(SceptreSkill::LLifesteal) => "Sceptre LLifesteal",
|
||||
Sceptre(SceptreSkill::LRegen) => "Sceptre LRegen",
|
||||
Sceptre(SceptreSkill::HHeal) => "Sceptre HHeal",
|
||||
Sceptre(SceptreSkill::HCost) => "Sceptre HCost",
|
||||
Sceptre(SceptreSkill::HDuration) => "Sceptre HDuration",
|
||||
Sceptre(SceptreSkill::HRange) => "Sceptre HRange",
|
||||
Sceptre(SceptreSkill::HCost) => "Sceptre HCost",
|
||||
Sceptre(SceptreSkill::UnlockAura) => "Sceptre UnlockAura",
|
||||
Sceptre(SceptreSkill::AStrength) => "Sceptre AStrength",
|
||||
Sceptre(SceptreSkill::ADuration) => "Sceptre ADuration",
|
||||
@ -245,8 +246,9 @@ pub fn db_string_to_skill(skill_string: &str) -> comp::skills::Skill {
|
||||
"Sceptre LLifesteal" => Sceptre(SceptreSkill::LLifesteal),
|
||||
"Sceptre LRegen" => Sceptre(SceptreSkill::LRegen),
|
||||
"Sceptre HHeal" => Sceptre(SceptreSkill::HHeal),
|
||||
"Sceptre HCost" => Sceptre(SceptreSkill::HCost),
|
||||
"Sceptre HDuration" => Sceptre(SceptreSkill::HDuration),
|
||||
"Sceptre HRange" => Sceptre(SceptreSkill::HRange),
|
||||
"Sceptre HCost" => Sceptre(SceptreSkill::HCost),
|
||||
"Sceptre UnlockAura" => Sceptre(SceptreSkill::UnlockAura),
|
||||
"Sceptre AStrength" => Sceptre(SceptreSkill::AStrength),
|
||||
"Sceptre ADuration" => Sceptre(SceptreSkill::ADuration),
|
||||
|
@ -18,9 +18,9 @@ use common::{
|
||||
},
|
||||
skills::{AxeSkill, BowSkill, HammerSkill, SceptreSkill, Skill, StaffSkill, SwordSkill},
|
||||
Agent, Alignment, BehaviorCapability, BehaviorState, Body, CharacterAbility,
|
||||
CharacterState, ControlAction, ControlEvent, Controller, Energy, Health, HealthChange,
|
||||
InputKind, Inventory, InventoryAction, LightEmitter, MountState, Ori, PhysicsState, Pos,
|
||||
Scale, SkillSet, Stats, UnresolvedChatMsg, UtteranceKind, Vel,
|
||||
CharacterState, Combo, ControlAction, ControlEvent, Controller, Energy, Health,
|
||||
HealthChange, InputKind, Inventory, InventoryAction, LightEmitter, MountState, Ori,
|
||||
PhysicsState, Pos, Scale, SkillSet, Stats, UnresolvedChatMsg, UtteranceKind, Vel,
|
||||
},
|
||||
consts::GRAVITY,
|
||||
effect::{BuffEffect, Effect},
|
||||
@ -160,6 +160,7 @@ pub struct ReadData<'a> {
|
||||
world: ReadExpect<'a, Arc<world::World>>,
|
||||
rtsim_entities: ReadStorage<'a, RtSimEntity>,
|
||||
buffs: ReadStorage<'a, Buffs>,
|
||||
combos: ReadStorage<'a, Combo>,
|
||||
}
|
||||
|
||||
// This is 3.1 to last longer than the last damage timer (3.0 seconds)
|
||||
@ -2467,6 +2468,7 @@ impl<'a> AgentData<'a> {
|
||||
read_data: &ReadData,
|
||||
) {
|
||||
const DESIRED_ENERGY_LEVEL: u32 = 500;
|
||||
const DESIRED_COMBO_LEVEL: u32 = 8;
|
||||
// Logic to use abilities
|
||||
if attack_data.dist_sqrd > attack_data.min_attack_dist.powi(2)
|
||||
&& can_see_tgt(
|
||||
@ -2478,7 +2480,23 @@ impl<'a> AgentData<'a> {
|
||||
{
|
||||
// If far enough away, and can see target, check which skill is appropriate to
|
||||
// use
|
||||
if self
|
||||
if self.energy.current() > DESIRED_ENERGY_LEVEL
|
||||
&& read_data
|
||||
.combos
|
||||
.get(*self.entity)
|
||||
.map_or(false, |c| c.counter() >= DESIRED_COMBO_LEVEL)
|
||||
&& !read_data.buffs.get(*self.entity).iter().any(|buff| {
|
||||
buff.iter_kind(BuffKind::Regeneration)
|
||||
.peekable()
|
||||
.peek()
|
||||
.is_some()
|
||||
})
|
||||
{
|
||||
// If have enough energy and combo to use healing aura, do so
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
} else if self
|
||||
.skill_set
|
||||
.has_skill(Skill::Sceptre(SceptreSkill::UnlockAura))
|
||||
&& self.energy.current() > DESIRED_ENERGY_LEVEL
|
||||
|
@ -175,7 +175,7 @@ pub enum SfxEvent {
|
||||
Parry,
|
||||
Block,
|
||||
BreakBlock,
|
||||
HealingBeam,
|
||||
SceptreBeam,
|
||||
SkillPointGain,
|
||||
ArrowHit,
|
||||
ArrowMiss,
|
||||
@ -479,9 +479,9 @@ impl SfxMgr {
|
||||
audio.emit_sfx(sfx_trigger_item, *pos, None, false);
|
||||
},
|
||||
Outcome::Beam { pos, specifier } => match specifier {
|
||||
beam::FrontendSpecifier::LifestealBeam | beam::FrontendSpecifier::HealingBeam => {
|
||||
beam::FrontendSpecifier::LifestealBeam => {
|
||||
if thread_rng().gen_bool(0.5) {
|
||||
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::HealingBeam);
|
||||
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::SceptreBeam);
|
||||
audio.emit_sfx(sfx_trigger_item, *pos, None, false);
|
||||
};
|
||||
},
|
||||
|
@ -144,6 +144,7 @@ widget_ids! {
|
||||
skill_sceptre_heal_1,
|
||||
skill_sceptre_heal_2,
|
||||
skill_sceptre_heal_3,
|
||||
skill_sceptre_heal_4,
|
||||
skill_sceptre_aura_0,
|
||||
skill_sceptre_aura_1,
|
||||
skill_sceptre_aura_2,
|
||||
@ -551,7 +552,7 @@ impl<'a> Widget for Diary<'a> {
|
||||
SelectedSkillTree::Weapon(ToolKind::Hammer) => 5,
|
||||
SelectedSkillTree::Weapon(ToolKind::Bow) => 4,
|
||||
SelectedSkillTree::Weapon(ToolKind::Staff) => 5,
|
||||
SelectedSkillTree::Weapon(ToolKind::Sceptre) => 4,
|
||||
SelectedSkillTree::Weapon(ToolKind::Sceptre) => 5,
|
||||
_ => 0,
|
||||
};
|
||||
let skills_bot_l = match sel_tab {
|
||||
@ -1912,10 +1913,10 @@ impl<'a> Widget for Diary<'a> {
|
||||
&diary_tooltip,
|
||||
);
|
||||
self.create_unlock_skill_button(
|
||||
Skill::Sceptre(HCost),
|
||||
self.imgs.heal_cost_skill,
|
||||
Skill::Sceptre(HDuration),
|
||||
self.imgs.heal_duration_skill,
|
||||
state.skills_top_r[2],
|
||||
"sc_heal_cost",
|
||||
"sc_heal_duration",
|
||||
state.skill_sceptre_heal_2,
|
||||
ui,
|
||||
&mut events,
|
||||
@ -1923,7 +1924,7 @@ impl<'a> Widget for Diary<'a> {
|
||||
);
|
||||
self.create_unlock_skill_button(
|
||||
Skill::Sceptre(HRange),
|
||||
self.imgs.heal_distance_skill,
|
||||
self.imgs.heal_radius_skill,
|
||||
state.skills_top_r[3],
|
||||
"sc_heal_range",
|
||||
state.skill_sceptre_heal_3,
|
||||
@ -1931,6 +1932,16 @@ impl<'a> Widget for Diary<'a> {
|
||||
&mut events,
|
||||
&diary_tooltip,
|
||||
);
|
||||
self.create_unlock_skill_button(
|
||||
Skill::Sceptre(HCost),
|
||||
self.imgs.heal_cost_skill,
|
||||
state.skills_top_r[4],
|
||||
"sc_heal_cost",
|
||||
state.skill_sceptre_heal_4,
|
||||
ui,
|
||||
&mut events,
|
||||
&diary_tooltip,
|
||||
);
|
||||
// Bottom left skills
|
||||
self.create_unlock_skill_button(
|
||||
Skill::Sceptre(UnlockAura),
|
||||
|
@ -494,7 +494,7 @@ image_ids! {
|
||||
snake_arrow_0: "voxygen.element.skills.snake",
|
||||
skill_sceptre_lifesteal: "voxygen.element.skills.lifesteal",
|
||||
sword_whirlwind: "voxygen.element.skills.sword_whirlwind",
|
||||
skill_sceptre_heal: "voxygen.element.skills.heal_0",
|
||||
skill_sceptre_heal: "voxygen.element.skills.heal_aoe",
|
||||
hammerleap: "voxygen.element.skills.skill_hammerleap",
|
||||
skill_axe_leap_slash: "voxygen.element.skills.skill_axe_leap_slash",
|
||||
skill_bow_jump_burst: "voxygen.element.skills.skill_bow_jump_burst",
|
||||
|
@ -64,7 +64,7 @@ pub enum ParticleMode {
|
||||
Firefly = 11,
|
||||
Bee = 12,
|
||||
GroundShockwave = 13,
|
||||
HealingBeam = 14,
|
||||
EnergyHealing = 14,
|
||||
EnergyNature = 15,
|
||||
FlameThrower = 16,
|
||||
FireShockwave = 17,
|
||||
|
@ -1302,32 +1302,6 @@ impl FigureMgr {
|
||||
skeleton_attr,
|
||||
)
|
||||
},
|
||||
CharacterState::HealingBeam(s) => {
|
||||
let stage_time = s.timer.as_secs_f32();
|
||||
let stage_progress = match s.stage_section {
|
||||
StageSection::Buildup => {
|
||||
stage_time / s.static_data.buildup_duration.as_secs_f32()
|
||||
},
|
||||
StageSection::Cast => s.timer.as_secs_f32(),
|
||||
StageSection::Recover => {
|
||||
stage_time / s.static_data.recover_duration.as_secs_f32()
|
||||
},
|
||||
_ => 0.0,
|
||||
};
|
||||
anim::character::BeamAnimation::update_skeleton(
|
||||
&target_base,
|
||||
(
|
||||
Some(s.static_data.ability_info),
|
||||
hands,
|
||||
time,
|
||||
rel_vel.magnitude(),
|
||||
Some(s.stage_section),
|
||||
),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
skeleton_attr,
|
||||
)
|
||||
},
|
||||
CharacterState::ComboMelee(s) => {
|
||||
let stage_index = (s.stage - 1) as usize;
|
||||
let stage_time = s.timer.as_secs_f32();
|
||||
|
@ -826,20 +826,6 @@ impl ParticleMgr {
|
||||
},
|
||||
);
|
||||
},
|
||||
beam::FrontendSpecifier::HealingBeam => {
|
||||
// Emit a light when using healing
|
||||
lights.push(Light::new(pos.0, Rgb::new(0.1, 1.0, 0.15), 1.0));
|
||||
self.particles.reserve(beam_tick_count as usize);
|
||||
for i in 0..beam_tick_count {
|
||||
self.particles.push(Particle::new_directed(
|
||||
beam.properties.duration,
|
||||
time + i as f64 / 1000.0,
|
||||
ParticleMode::HealingBeam,
|
||||
pos.0,
|
||||
pos.0 + *ori.look_dir() * range,
|
||||
));
|
||||
}
|
||||
},
|
||||
beam::FrontendSpecifier::LifestealBeam => {
|
||||
// Emit a light when using lifesteal beam
|
||||
lights.push(Light::new(pos.0, Rgb::new(0.8, 1.0, 0.5), 1.0));
|
||||
@ -956,6 +942,28 @@ impl ParticleMgr {
|
||||
},
|
||||
);
|
||||
},
|
||||
aura::AuraKind::Buff {
|
||||
kind: buff::BuffKind::Regeneration,
|
||||
..
|
||||
} => {
|
||||
let heartbeats = self.scheduler.heartbeats(Duration::from_millis(5));
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ aura.radius.powi(2) as usize * usize::from(heartbeats) / 300,
|
||||
|| {
|
||||
let rand_dist = aura.radius * (1.0 - rng.gen::<f32>().powi(100));
|
||||
let init_pos = Vec3::new(rand_dist, 0_f32, 0_f32);
|
||||
let max_dur = Duration::from_secs(1);
|
||||
Particle::new_directed(
|
||||
aura.duration.map_or(max_dur, |dur| dur.min(max_dur)),
|
||||
time,
|
||||
ParticleMode::EnergyHealing,
|
||||
pos.0,
|
||||
pos.0 + init_pos,
|
||||
)
|
||||
},
|
||||
);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user