mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added cursed flame attack to mindflayer
This commit is contained in:
parent
f8f1d19684
commit
0d3795112c
@ -6,7 +6,7 @@ BasicBeam(
|
||||
tick_rate: 2.0,
|
||||
range: 25.0,
|
||||
max_angle: 1.0,
|
||||
lifesteal_eff: 0.15,
|
||||
damage_effect: Some(Lifesteal(0.15)),
|
||||
energy_regen: 50,
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Normal,
|
||||
|
@ -5,8 +5,8 @@ BasicBeam(
|
||||
damage: 50,
|
||||
tick_rate: 3.0,
|
||||
range: 20.0,
|
||||
max_angle: 10.0,
|
||||
lifesteal_eff: 0.0,
|
||||
max_angle: 15.0,
|
||||
damage_effect: None,
|
||||
energy_regen: 0,
|
||||
energy_drain: 350,
|
||||
orientation_behavior: Normal,
|
||||
|
@ -5,8 +5,8 @@ BasicBeam(
|
||||
damage: 50,
|
||||
tick_rate: 3.0,
|
||||
range: 20.0,
|
||||
max_angle: 0.1,
|
||||
lifesteal_eff: 0.0,
|
||||
max_angle: 15.0,
|
||||
damage_effect: None,
|
||||
energy_regen: 0,
|
||||
energy_drain: 350,
|
||||
orientation_behavior: Normal,
|
||||
|
19
assets/common/abilities/unique/mindflayer/cursedflames.ron
Normal file
19
assets/common/abilities/unique/mindflayer/cursedflames.ron
Normal file
@ -0,0 +1,19 @@
|
||||
BasicBeam(
|
||||
buildup_duration: 0.50,
|
||||
recover_duration: 0.50,
|
||||
beam_duration: 0.5,
|
||||
damage: 200,
|
||||
tick_rate: 2.0,
|
||||
range: 10.0,
|
||||
max_angle: 15.0,
|
||||
damage_effect: Some(Buff((
|
||||
kind: Cursed,
|
||||
dur_secs: 15.0,
|
||||
strength: Value(0.5),
|
||||
chance: 1.0,
|
||||
))),
|
||||
energy_regen: 0,
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Normal,
|
||||
specifier: Cultist,
|
||||
)
|
@ -0,0 +1 @@
|
||||
BasicBlock
|
@ -0,0 +1 @@
|
||||
BasicBlock
|
@ -0,0 +1 @@
|
||||
BasicBlock
|
@ -6,7 +6,7 @@ BasicBeam(
|
||||
tick_rate: 2.0,
|
||||
range: 25.0,
|
||||
max_angle: 1.0,
|
||||
lifesteal_eff: 0.15,
|
||||
damage_effect: Some(Lifesteal(0.15)),
|
||||
energy_regen: 25,
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Normal,
|
||||
|
@ -6,7 +6,7 @@ BasicBeam(
|
||||
tick_rate: 3.0,
|
||||
range: 15.0,
|
||||
max_angle: 22.5,
|
||||
lifesteal_eff: 0.0,
|
||||
damage_effect: None,
|
||||
energy_regen: 0,
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Normal,
|
||||
|
@ -7,7 +7,7 @@ BasicBeam(
|
||||
tick_rate: 3.0,
|
||||
range: 15.0,
|
||||
max_angle: 22.5,
|
||||
lifesteal_eff: 0.0,
|
||||
damage_effect: None,
|
||||
energy_regen: 0,
|
||||
energy_cost: 0,
|
||||
energy_drain: 0,
|
||||
|
@ -5,8 +5,8 @@ BasicBeam(
|
||||
damage: 3000,
|
||||
tick_rate: 3.0,
|
||||
range: 30.0,
|
||||
max_angle: 1.0,
|
||||
lifesteal_eff: 0.0,
|
||||
max_angle: 15.0,
|
||||
damage_effect: None,
|
||||
energy_regen: 0,
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Turret,
|
||||
|
@ -166,7 +166,7 @@
|
||||
primary: "common.abilities.unique.quadlowbeam.healingbeam",
|
||||
secondary: "common.abilities.unique.quadlowbreathe.triplestrike",
|
||||
abilities: [
|
||||
(None, "common.abilities.unique.quadlowbreathe.dash"),
|
||||
(None, "common.abilities.unique.quadlowbreathe.dash"),
|
||||
],
|
||||
),
|
||||
Unique(QuadSmallBasic): (
|
||||
@ -194,6 +194,14 @@
|
||||
secondary: "common.abilities.unique.turret.arrows",
|
||||
abilities: [],
|
||||
),
|
||||
Unique(MindflayerStaff): (
|
||||
primary: "common.abilities.unique.mindflayer.cursedflames",
|
||||
secondary: "common.abilities.unique.mindflayer.necroticvortex",
|
||||
abilities: [
|
||||
(None, "common.abilities.unique.mindflayer.dimensionaldoor"),
|
||||
(None, "common.abilities.unique.mindflayer.raiseundead"),
|
||||
],
|
||||
),
|
||||
Debug: (
|
||||
primary: "common.abilities.debug.forwardboost",
|
||||
secondary: "common.abilities.debug.upboost",
|
||||
|
@ -2,15 +2,15 @@ ItemDef(
|
||||
name: "Mindflayer Staff",
|
||||
description: "Placeholder",
|
||||
kind: Tool((
|
||||
kind: StaffSimple,
|
||||
kind: Unique(MindflayerStaff),
|
||||
hands: Two,
|
||||
stats: Direct((
|
||||
equip_time_secs: 0.001,
|
||||
power: 3.0,
|
||||
equip_time_secs: 0.01,
|
||||
power: 1.0,
|
||||
poise_strength: 1.0,
|
||||
speed: 1.5,
|
||||
crit_chance: 0.15,
|
||||
crit_mult: 1.2539682,
|
||||
speed: 1.0,
|
||||
crit_chance: 0.0,
|
||||
crit_mult: 1.0,
|
||||
)),
|
||||
)),
|
||||
quality: Legendary,
|
||||
|
@ -61,6 +61,7 @@ const int SNOW = 19;
|
||||
const int EXPLOSION = 20;
|
||||
const int ICE = 21;
|
||||
const int LIFESTEAL_BEAM = 22;
|
||||
const int CULTIST_FLAME = 23;
|
||||
|
||||
// meters per second squared (acceleration)
|
||||
const float earth_gravity = 9.807;
|
||||
@ -363,8 +364,8 @@ void main() {
|
||||
} else if (inst_mode == FLAMETHROWER) {
|
||||
f_reflect = 0.0; // Fire doesn't reflect light, it emits it
|
||||
attr = Attr(
|
||||
(inst_dir * slow_end(1.5)) + vec3(rand0, rand1, rand2) * (lifetime * 5 + 0.25),
|
||||
vec3((2.5 * (1 - slow_start(0.3)))),
|
||||
(inst_dir * slow_end(1.5)) + vec3(rand0, rand1, rand2) * (percent() + 2) * 0.1,
|
||||
vec3((2.5 * (1 - slow_start(0.2)))),
|
||||
vec4(3, 1.6 + rand5 * 0.3 - 0.4 * percent(), 0.2, 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
|
||||
);
|
||||
@ -392,6 +393,15 @@ void main() {
|
||||
vec4(3, 1.6 + rand5 * 0.3 - 0.4 * percent(), 0.2, 1),
|
||||
spin_in_axis(vec3(rand3, rand4, rand5), rand6)
|
||||
);
|
||||
} else if (inst_mode == CULTIST_FLAME) {
|
||||
f_reflect = 0.0; // Fire doesn't reflect light, it emits it
|
||||
float purp_color = 0.8 + 0.5 * rand3;
|
||||
attr = Attr(
|
||||
(inst_dir * slow_end(1.5)) + vec3(rand0, rand1, rand2) * (percent() + 2) * 0.1,
|
||||
vec3((2.5 * (1 - slow_start(0.2)))),
|
||||
vec4(purp_color, 0.0, purp_color, 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
|
||||
);
|
||||
} else {
|
||||
attr = Attr(
|
||||
linear_motion(
|
||||
|
@ -411,7 +411,7 @@ impl AttackEffect {
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum CombatEffect {
|
||||
Heal(f32),
|
||||
Buff(CombatBuff),
|
||||
@ -647,7 +647,7 @@ impl Knockback {
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct CombatBuff {
|
||||
pub kind: BuffKind,
|
||||
pub dur_secs: f32,
|
||||
@ -656,7 +656,7 @@ pub struct CombatBuff {
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum CombatBuffStrength {
|
||||
DamageFraction(f32),
|
||||
Value(f32),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
assets::{self, Asset},
|
||||
combat,
|
||||
combat::{self, CombatEffect},
|
||||
comp::{
|
||||
aura, beam, inventory::item::tool::ToolKind, projectile::ProjectileConstructor, skills,
|
||||
Body, CharacterState, EnergySource, Gravity, LightEmitter, StateUpdate,
|
||||
@ -223,7 +223,7 @@ pub enum CharacterAbility {
|
||||
tick_rate: f32,
|
||||
range: f32,
|
||||
max_angle: f32,
|
||||
lifesteal_eff: f32,
|
||||
damage_effect: Option<CombatEffect>,
|
||||
energy_regen: f32,
|
||||
energy_drain: f32,
|
||||
orientation_behavior: basic_beam::MovementBehavior,
|
||||
@ -1008,7 +1008,7 @@ impl CharacterAbility {
|
||||
ref mut damage,
|
||||
ref mut range,
|
||||
ref mut beam_duration,
|
||||
ref mut lifesteal_eff,
|
||||
ref mut damage_effect,
|
||||
ref mut energy_regen,
|
||||
..
|
||||
} => {
|
||||
@ -1024,8 +1024,10 @@ impl CharacterAbility {
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(LRegen)) {
|
||||
*energy_regen *= 1.25_f32.powi(level.into());
|
||||
}
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(LLifesteal)) {
|
||||
*lifesteal_eff *= 1.3_f32.powi(level.into());
|
||||
if let (Ok(Some(level)), Some(CombatEffect::Lifesteal(ref mut lifesteal))) =
|
||||
(skillset.skill_level(Sceptre(LLifesteal)), damage_effect)
|
||||
{
|
||||
*lifesteal *= 1.3_f32.powi(level.into());
|
||||
}
|
||||
},
|
||||
HealingBeam {
|
||||
@ -1479,7 +1481,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
tick_rate,
|
||||
range,
|
||||
max_angle,
|
||||
lifesteal_eff,
|
||||
damage_effect,
|
||||
energy_regen,
|
||||
energy_drain,
|
||||
orientation_behavior,
|
||||
@ -1493,7 +1495,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
tick_rate: *tick_rate,
|
||||
range: *range,
|
||||
max_angle: *max_angle,
|
||||
lifesteal_eff: *lifesteal_eff,
|
||||
damage_effect: *damage_effect,
|
||||
energy_regen: *energy_regen,
|
||||
energy_drain: *energy_drain,
|
||||
ability_info,
|
||||
|
@ -51,4 +51,5 @@ pub enum FrontendSpecifier {
|
||||
Flamethrower,
|
||||
LifestealBeam,
|
||||
HealingBeam,
|
||||
Cultist,
|
||||
}
|
||||
|
@ -20,8 +20,7 @@ pub enum BuffKind {
|
||||
Saturation,
|
||||
/// Lowers health over time for some duration
|
||||
Bleeding,
|
||||
/// Lower a creature's max health
|
||||
/// Currently placeholder buff to show other stuff is possible
|
||||
/// Lower a creature's max health over time
|
||||
Cursed,
|
||||
/// Applied when drinking a potion
|
||||
Potion,
|
||||
@ -109,6 +108,13 @@ pub enum BuffEffect {
|
||||
MaxEnergyModifier { value: f32, kind: ModifierKind },
|
||||
/// Reduces damage after armor is accounted for by this fraction
|
||||
DamageReduction(f32),
|
||||
/// Gradually changes an entities max health over time
|
||||
MaxHealthChangeOverTime {
|
||||
rate: f32,
|
||||
accumulated: f32,
|
||||
kind: ModifierKind,
|
||||
target_fraction: f32,
|
||||
},
|
||||
}
|
||||
|
||||
/// Actual de/buff.
|
||||
@ -191,10 +197,19 @@ impl Buff {
|
||||
data.duration,
|
||||
),
|
||||
BuffKind::Cursed => (
|
||||
vec![BuffEffect::MaxHealthModifier {
|
||||
value: -100. * data.strength,
|
||||
kind: ModifierKind::Additive,
|
||||
}],
|
||||
vec![
|
||||
BuffEffect::MaxHealthChangeOverTime {
|
||||
rate: -10.0,
|
||||
accumulated: 0.0,
|
||||
kind: ModifierKind::Additive,
|
||||
target_fraction: 1.0 - data.strength,
|
||||
},
|
||||
BuffEffect::HealthChangeOverTime {
|
||||
rate: -10.0,
|
||||
accumulated: 0.0,
|
||||
kind: ModifierKind::Additive,
|
||||
},
|
||||
],
|
||||
data.duration,
|
||||
),
|
||||
BuffKind::IncreaseMaxEnergy => (
|
||||
|
@ -36,10 +36,9 @@ pub enum HealthSource {
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub struct Health {
|
||||
base_max: u32,
|
||||
current: u32,
|
||||
base_max: u32,
|
||||
maximum: u32,
|
||||
last_max: u32,
|
||||
pub last_change: (f64, HealthChange),
|
||||
pub is_dead: bool,
|
||||
}
|
||||
@ -58,9 +57,8 @@ impl Health {
|
||||
pub fn empty() -> Self {
|
||||
Health {
|
||||
current: 0,
|
||||
maximum: 0,
|
||||
base_max: 0,
|
||||
last_max: 0,
|
||||
maximum: 0,
|
||||
last_change: (0.0, HealthChange {
|
||||
amount: 0,
|
||||
cause: HealthSource::Revive,
|
||||
@ -73,6 +71,8 @@ impl Health {
|
||||
|
||||
pub fn maximum(&self) -> u32 { self.maximum }
|
||||
|
||||
pub fn base_max(&self) -> u32 { self.base_max }
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn set_to(&mut self, amount: u32, cause: HealthSource) {
|
||||
let amount = amount.min(self.maximum);
|
||||
@ -97,6 +97,12 @@ impl Health {
|
||||
self.current = self.current.min(self.maximum);
|
||||
}
|
||||
|
||||
// Scales the temporary max health by a modifier.
|
||||
pub fn scale_maximum(&mut self, scaled: f32) {
|
||||
let scaled_max = (self.base_max as f32 * scaled) as u32;
|
||||
self.set_maximum(scaled_max);
|
||||
}
|
||||
|
||||
// This is private because max hp is based on the level
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn set_base_max(&mut self, amount: u32) {
|
||||
@ -124,26 +130,6 @@ impl Health {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn with_max_health(mut self, amount: u32) -> Self {
|
||||
self.maximum = amount;
|
||||
self.current = amount;
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn last_set(&mut self) { self.last_max = self.maximum }
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn reset_max(&mut self) {
|
||||
self.maximum = self.base_max;
|
||||
if self.current > self.last_max {
|
||||
self.current = self.last_max;
|
||||
|
||||
self.last_max = self.base_max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
@ -426,4 +426,5 @@ pub enum UniqueKind {
|
||||
TheropodCharge,
|
||||
ObjectTurret,
|
||||
WoodenSpear,
|
||||
MindflayerStaff,
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ pub struct Stats {
|
||||
// potentially be updated every tick (especially as more buffs are added)
|
||||
pub skill_set: SkillSet,
|
||||
pub damage_reduction: f32,
|
||||
pub max_health_modifier: f32,
|
||||
}
|
||||
|
||||
impl Stats {
|
||||
@ -36,6 +37,7 @@ impl Stats {
|
||||
name,
|
||||
skill_set: SkillSet::default(),
|
||||
damage_reduction: 0.0,
|
||||
max_health_modifier: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,8 +48,15 @@ impl Stats {
|
||||
name: "".to_owned(),
|
||||
skill_set: SkillSet::default(),
|
||||
damage_reduction: 0.0,
|
||||
max_health_modifier: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets temporary modifiers to default values
|
||||
pub fn reset_temp_modifiers(&mut self) {
|
||||
self.damage_reduction = 0.0;
|
||||
self.max_health_modifier = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Stats {
|
||||
|
@ -32,9 +32,8 @@ pub struct StaticData {
|
||||
pub range: f32,
|
||||
/// Max angle (45.0 will give you a 90.0 angle window)
|
||||
pub max_angle: f32,
|
||||
/// Lifesteal efficiency (0 gives 0% conversion of damage to health, 1 gives
|
||||
/// 100% conversion of damage to health)
|
||||
pub lifesteal_eff: f32,
|
||||
/// Adds an effect onto the main damage of the attack
|
||||
pub damage_effect: Option<CombatEffect>,
|
||||
/// Energy regenerated per tick
|
||||
pub energy_regen: f32,
|
||||
/// Energy drained per second
|
||||
@ -111,15 +110,16 @@ impl CharacterBehavior for Data {
|
||||
CombatEffect::EnergyReward(self.static_data.energy_regen),
|
||||
)
|
||||
.with_requirement(CombatRequirement::AnyDamage);
|
||||
let lifesteal = CombatEffect::Lifesteal(self.static_data.lifesteal_eff);
|
||||
let damage = AttackDamage::new(
|
||||
let mut damage = AttackDamage::new(
|
||||
Damage {
|
||||
source: DamageSource::Energy,
|
||||
value: self.static_data.damage,
|
||||
},
|
||||
Some(GroupTarget::OutOfGroup),
|
||||
)
|
||||
.with_effect(lifesteal);
|
||||
);
|
||||
if let Some(effect) = self.static_data.damage_effect {
|
||||
damage = damage.with_effect(effect);
|
||||
}
|
||||
let (crit_chance, crit_mult) =
|
||||
get_crit_data(data, self.static_data.ability_info);
|
||||
let attack = Attack::default()
|
||||
@ -138,8 +138,8 @@ impl CharacterBehavior for Data {
|
||||
};
|
||||
// Gets offsets
|
||||
let body_offsets = Vec3::new(
|
||||
(data.body.radius() + 1.0) * data.inputs.look_dir.x,
|
||||
(data.body.radius() + 1.0) * data.inputs.look_dir.y,
|
||||
(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);
|
||||
|
@ -103,8 +103,8 @@ impl CharacterBehavior for Data {
|
||||
};
|
||||
// Gets offsets
|
||||
let body_offsets = Vec3::new(
|
||||
(data.body.radius() + 1.0) * data.inputs.look_dir.x,
|
||||
(data.body.radius() + 1.0) * data.inputs.look_dir.y,
|
||||
(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);
|
||||
|
@ -19,6 +19,7 @@ pub struct ReadData<'a> {
|
||||
dt: Read<'a, DeltaTime>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
inventories: ReadStorage<'a, Inventory>,
|
||||
healths: ReadStorage<'a, Health>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -26,7 +27,6 @@ pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
ReadData<'a>,
|
||||
WriteStorage<'a, Health>,
|
||||
WriteStorage<'a, Energy>,
|
||||
WriteStorage<'a, Buffs>,
|
||||
WriteStorage<'a, Stats>,
|
||||
@ -38,22 +38,20 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
fn run(
|
||||
_job: &mut Job<Self>,
|
||||
(read_data, mut healths, mut energies, mut buffs, mut stats): Self::SystemData,
|
||||
(read_data, mut energies, mut buffs, mut stats): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
let dt = read_data.dt.0;
|
||||
// Set to false to avoid spamming server
|
||||
buffs.set_event_emission(false);
|
||||
healths.set_event_emission(false);
|
||||
energies.set_event_emission(false);
|
||||
healths.set_event_emission(false);
|
||||
stats.set_event_emission(false);
|
||||
for (entity, mut buff_comp, mut health, mut energy, mut stat) in (
|
||||
for (entity, mut buff_comp, mut energy, mut stat, health) in (
|
||||
&read_data.entities,
|
||||
&mut buffs,
|
||||
&mut healths,
|
||||
&mut energies,
|
||||
&mut stats,
|
||||
&read_data.healths,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
@ -89,12 +87,10 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
|
||||
// Call to reset health and energy to base values
|
||||
health.last_set();
|
||||
// Call to reset energy and stats to base values
|
||||
energy.last_set();
|
||||
health.reset_max();
|
||||
energy.reset_max();
|
||||
stat.damage_reduction = 0.0;
|
||||
stat.reset_temp_modifiers();
|
||||
|
||||
// Iterator over the lists of buffs by kind
|
||||
let buff_comp = &mut *buff_comp;
|
||||
@ -117,9 +113,9 @@ impl<'a> System<'a> for Sys {
|
||||
kind,
|
||||
} => {
|
||||
*accumulated += *rate * dt;
|
||||
// Apply health change only once a second or
|
||||
// Apply health change only once per second, per health, or
|
||||
// when a buff is removed
|
||||
if accumulated.abs() > rate.abs()
|
||||
if accumulated.abs() > rate.abs().min(10.0)
|
||||
|| buff.time.map_or(false, |dur| dur == Duration::default())
|
||||
{
|
||||
let cause = if *accumulated > 0.0 {
|
||||
@ -145,14 +141,10 @@ impl<'a> System<'a> for Sys {
|
||||
},
|
||||
BuffEffect::MaxHealthModifier { value, kind } => match kind {
|
||||
ModifierKind::Additive => {
|
||||
let health = &mut *health;
|
||||
let buffed_health_max =
|
||||
(health.maximum() as f32 + *value) as u32;
|
||||
health.set_maximum(buffed_health_max);
|
||||
stat.max_health_modifier += *value / (health.maximum() as f32);
|
||||
},
|
||||
ModifierKind::Fractional => {
|
||||
let health = &mut *health;
|
||||
health.set_maximum((health.maximum() as f32 * *value) as u32);
|
||||
stat.max_health_modifier *= *value;
|
||||
},
|
||||
},
|
||||
BuffEffect::MaxEnergyModifier { value, kind } => match kind {
|
||||
@ -168,6 +160,35 @@ impl<'a> System<'a> for Sys {
|
||||
BuffEffect::DamageReduction(dr) => {
|
||||
stat.damage_reduction = stat.damage_reduction.max(*dr).min(1.0);
|
||||
},
|
||||
BuffEffect::MaxHealthChangeOverTime {
|
||||
rate,
|
||||
accumulated,
|
||||
kind,
|
||||
target_fraction,
|
||||
} => {
|
||||
*accumulated += *rate * dt;
|
||||
let current_fraction = health.maximum() as f32
|
||||
/ (health.base_max() as f32 * stat.max_health_modifier);
|
||||
let progress = (1.0 - current_fraction) / (1.0 - *target_fraction);
|
||||
if progress > 1.0 {
|
||||
stat.max_health_modifier *= *target_fraction;
|
||||
} else if accumulated.abs() > rate.abs() {
|
||||
match kind {
|
||||
ModifierKind::Additive => {
|
||||
stat.max_health_modifier = stat.max_health_modifier
|
||||
* current_fraction
|
||||
+ *accumulated / health.maximum() as f32;
|
||||
},
|
||||
ModifierKind::Fractional => {
|
||||
stat.max_health_modifier *=
|
||||
current_fraction * (1.0 - *accumulated);
|
||||
},
|
||||
}
|
||||
*accumulated = 0.0;
|
||||
} else {
|
||||
stat.max_health_modifier *= current_fraction;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -195,7 +216,6 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
// Turned back to true
|
||||
buffs.set_event_emission(true);
|
||||
healths.set_event_emission(true);
|
||||
energies.set_event_emission(true);
|
||||
stats.set_event_emission(true);
|
||||
}
|
||||
|
@ -104,6 +104,18 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
let stat = stats.get_unchecked();
|
||||
|
||||
let update_max_hp = {
|
||||
let health = health.get_unchecked();
|
||||
(stat.max_health_modifier - 1.0).abs() > f32::EPSILON
|
||||
|| health.base_max() != health.maximum()
|
||||
};
|
||||
|
||||
if update_max_hp {
|
||||
let mut health = health.get_mut_unchecked();
|
||||
health.scale_maximum(stat.max_health_modifier);
|
||||
}
|
||||
|
||||
let skills_to_level = stat
|
||||
.skill_set
|
||||
.skill_groups
|
||||
|
@ -359,7 +359,7 @@ impl SfxMgr {
|
||||
let file_ref = "voxygen.audio.sfx.abilities.staff_channeling";
|
||||
audio.play_sfx(file_ref, *pos, None);
|
||||
},
|
||||
beam::FrontendSpecifier::Flamethrower => {
|
||||
beam::FrontendSpecifier::Flamethrower | beam::FrontendSpecifier::Cultist => {
|
||||
let file_ref = "voxygen.audio.sfx.abilities.flame_thrower";
|
||||
audio.play_sfx(file_ref, *pos, None);
|
||||
},
|
||||
|
@ -119,6 +119,7 @@ pub enum ParticleMode {
|
||||
Explosion = 20,
|
||||
Ice = 21,
|
||||
LifestealBeam = 22,
|
||||
CultistFlame = 23,
|
||||
}
|
||||
|
||||
impl ParticleMode {
|
||||
|
@ -194,6 +194,7 @@ impl ParticleMgr {
|
||||
self.maintain_block_particles(scene_data, terrain);
|
||||
self.maintain_shockwave_particles(scene_data);
|
||||
self.maintain_aura_particles(scene_data);
|
||||
self.maintain_buff_particles(scene_data);
|
||||
} else {
|
||||
// remove all particle lifespans
|
||||
self.particles.clear();
|
||||
@ -477,7 +478,7 @@ impl ParticleMgr {
|
||||
2.0,
|
||||
));
|
||||
self.particles.resize_with(
|
||||
self.particles.len() + 2 * usize::from(beam_tick_count),
|
||||
self.particles.len() + usize::from(beam_tick_count) / 2,
|
||||
|| {
|
||||
let phi: f32 = rng.gen_range(0.0..beam.properties.angle);
|
||||
let theta: f32 = rng.gen_range(0.0..2.0 * PI);
|
||||
@ -497,6 +498,37 @@ impl ParticleMgr {
|
||||
},
|
||||
);
|
||||
},
|
||||
beam::FrontendSpecifier::Cultist => {
|
||||
let mut rng = thread_rng();
|
||||
let (from, to) = (Vec3::<f32>::unit_z(), *ori.look_dir());
|
||||
let m = Mat3::<f32>::rotation_from_to_3d(from, to);
|
||||
// Emit a light when using flames
|
||||
lights.push(Light::new(
|
||||
pos.0,
|
||||
Rgb::new(1.0, 0.0, 1.0).map(|e| e * rng.gen_range(0.5..1.0)),
|
||||
2.0,
|
||||
));
|
||||
self.particles.resize_with(
|
||||
self.particles.len() + usize::from(beam_tick_count) / 2,
|
||||
|| {
|
||||
let phi: f32 = rng.gen_range(0.0..beam.properties.angle);
|
||||
let theta: f32 = rng.gen_range(0.0..2.0 * PI);
|
||||
let offset_z = Vec3::new(
|
||||
phi.sin() * theta.cos(),
|
||||
phi.sin() * theta.sin(),
|
||||
phi.cos(),
|
||||
);
|
||||
let random_ori = offset_z * m * Vec3::new(-1.0, -1.0, 1.0);
|
||||
Particle::new_directed(
|
||||
beam.properties.duration,
|
||||
time,
|
||||
ParticleMode::CultistFlame,
|
||||
pos.0,
|
||||
pos.0 + random_ori * range,
|
||||
)
|
||||
},
|
||||
);
|
||||
},
|
||||
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));
|
||||
@ -570,6 +602,54 @@ impl ParticleMgr {
|
||||
}
|
||||
}
|
||||
|
||||
fn maintain_buff_particles(&mut self, scene_data: &SceneData) {
|
||||
let state = scene_data.state;
|
||||
let ecs = state.ecs();
|
||||
let time = state.get_time();
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
for (pos, buffs, body) in (
|
||||
&ecs.read_storage::<Pos>(),
|
||||
&ecs.read_storage::<comp::Buffs>(),
|
||||
&ecs.read_storage::<comp::Body>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
for (buff_kind, _) in buffs.kinds.iter() {
|
||||
#[allow(clippy::single_match)]
|
||||
match buff_kind {
|
||||
buff::BuffKind::Cursed => {
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ usize::from(self.scheduler.heartbeats(Duration::from_millis(15))),
|
||||
|| {
|
||||
let start_pos = pos.0
|
||||
+ Vec3::unit_z() * body.height() * 0.25
|
||||
+ Vec3::<f32>::zero()
|
||||
.map(|_| rng.gen_range(-1.0..1.0))
|
||||
.normalized()
|
||||
* 0.25;
|
||||
let end_pos = start_pos
|
||||
+ Vec3::unit_z() * body.height()
|
||||
+ Vec3::<f32>::zero()
|
||||
.map(|_| rng.gen_range(-1.0..1.0))
|
||||
.normalized();
|
||||
Particle::new_directed(
|
||||
Duration::from_secs(1),
|
||||
time,
|
||||
ParticleMode::CultistFlame,
|
||||
start_pos,
|
||||
end_pos,
|
||||
)
|
||||
},
|
||||
);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::same_item_push)] // TODO: Pending review in #587
|
||||
fn maintain_block_particles(
|
||||
&mut self,
|
||||
|
Loading…
Reference in New Issue
Block a user