Added necrotic vortex attack to mindflayer

This commit is contained in:
Sam 2021-03-20 20:41:56 -04:00
parent 0d3795112c
commit f1156c9ce5
8 changed files with 117 additions and 22 deletions

View File

@ -4,12 +4,14 @@ SpinMelee(
recover_duration: 0.2,
base_damage: 70,
base_poise_damage: 55,
knockback: 0.0,
knockback: ( strength: 0.0, direction: Away),
range: 3.5,
damage_effect: None,
energy_cost: 100,
is_infinite: true,
movement_behavior: AxeHover,
is_interruptible: false,
forward_speed: 0.0,
num_spins: 1,
specifier: None,
)

View File

@ -4,12 +4,14 @@ SpinMelee(
recover_duration: 0.5,
base_damage: 160,
base_poise_damage: 25,
knockback: 10.0,
knockback: ( strength: 10.0, direction: Away),
range: 3.5,
damage_effect: None,
energy_cost: 150,
is_infinite: false,
movement_behavior: ForwardGround,
is_interruptible: true,
forward_speed: 1.0,
num_spins: 3,
specifier: None,
)

View File

@ -1 +1,17 @@
BasicBlock
SpinMelee(
buildup_duration: 0.6,
swing_duration: 0.2,
recover_duration: 0.6,
base_damage: 70.0,
base_poise_damage: 0.0,
knockback: ( strength: 8.0, direction: Towards),
range: 15.0,
damage_effect: Some(Lifesteal(1.0)),
energy_cost: 0.0,
is_infinite: true,
movement_behavior: Stationary,
is_interruptible: false,
forward_speed: 0.0,
num_spins: 1,
specifier: Some(CultistVortex),
)

View File

@ -4,12 +4,14 @@ SpinMelee(
recover_duration: 0.1,
base_damage: 500,
base_poise_damage: 30,
knockback: 0.0,
knockback: ( strength: 0.0, direction: Away),
range: 7.5,
damage_effect: None,
energy_cost: 0,
is_infinite: false,
movement_behavior: GolemHover,
is_interruptible: false,
forward_speed: 0.0,
num_spins: 1,
specifier: None,
)

View File

@ -395,10 +395,10 @@ void main() {
);
} 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;
float purp_color = 0.9 + 0.3 * 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)))),
vec3((3.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)
);

View File

@ -156,14 +156,16 @@ pub enum CharacterAbility {
recover_duration: f32,
base_damage: f32,
base_poise_damage: f32,
knockback: f32,
knockback: Knockback,
range: f32,
damage_effect: Option<CombatEffect>,
energy_cost: f32,
is_infinite: bool,
movement_behavior: spin_melee::MovementBehavior,
is_interruptible: bool,
forward_speed: f32,
num_spins: u32,
specifier: Option<spin_melee::FrontendSpecifier>,
},
ChargedMelee {
energy_cost: f32,
@ -1301,12 +1303,14 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
base_poise_damage,
knockback,
range,
damage_effect,
energy_cost,
is_infinite,
movement_behavior,
is_interruptible,
forward_speed,
num_spins,
specifier,
} => CharacterState::SpinMelee(spin_melee::Data {
static_data: spin_melee::StaticData {
buildup_duration: Duration::from_secs_f32(*buildup_duration),
@ -1316,6 +1320,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
base_poise_damage: *base_poise_damage,
knockback: *knockback,
range: *range,
damage_effect: *damage_effect,
energy_cost: *energy_cost,
is_infinite: *is_infinite,
movement_behavior: *movement_behavior,
@ -1323,6 +1328,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
forward_speed: *forward_speed,
num_spins: *num_spins,
ability_info,
specifier: *specifier,
},
timer: Duration::default(),
spins_remaining: *num_spins - 1,

View File

@ -1,12 +1,14 @@
use crate::{
combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement},
combat::{
Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement, Damage,
DamageSource, GroupTarget, Knockback,
},
comp::{tool::ToolKind, CharacterState, EnergyChange, EnergySource, Melee, StateUpdate},
consts::GRAVITY,
states::{
behavior::{CharacterBehavior, JoinData},
utils::*,
},
Damage, DamageSource, GroupTarget, Knockback, KnockbackDir,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -26,9 +28,11 @@ pub struct StaticData {
/// Base poise damage
pub base_poise_damage: f32,
/// Knockback
pub knockback: f32,
pub knockback: Knockback,
/// Range
pub range: f32,
/// Adds an effect onto the main damage of the attack
pub damage_effect: Option<CombatEffect>,
/// Energy cost per attack
pub energy_cost: f32,
/// Whether spin state is infinite
@ -43,6 +47,8 @@ pub struct StaticData {
pub num_spins: u32,
/// What key is used to press ability
pub ability_info: AbilityInfo,
/// Used to specify the beam to the frontend
pub specifier: Option<FrontendSpecifier>,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -65,7 +71,7 @@ impl CharacterBehavior for Data {
let mut update = StateUpdate::from(data);
match self.static_data.movement_behavior {
MovementBehavior::ForwardGround => {},
MovementBehavior::ForwardGround | MovementBehavior::Stationary => {},
MovementBehavior::AxeHover => {
let new_vel_z = update.vel.0.z + GRAVITY * data.dt.0 * 0.5;
update.vel.0 = Vec3::new(0.0, 0.0, new_vel_z) + data.inputs.move_dir * 5.0;
@ -110,21 +116,23 @@ impl CharacterBehavior for Data {
.with_requirement(CombatRequirement::AnyDamage);
let knockback = AttackEffect::new(
Some(GroupTarget::OutOfGroup),
CombatEffect::Knockback(Knockback {
strength: self.static_data.knockback,
direction: KnockbackDir::Away,
}),
CombatEffect::Knockback(self.static_data.knockback),
)
.with_requirement(CombatRequirement::AnyDamage);
let buff = CombatEffect::Buff(CombatBuff::default_physical());
let damage = AttackDamage::new(
let mut damage = AttackDamage::new(
Damage {
source: DamageSource::Melee,
value: self.static_data.base_damage as f32,
},
Some(GroupTarget::OutOfGroup),
)
.with_effect(buff);
);
match self.static_data.damage_effect {
Some(effect) => damage = damage.with_effect(effect),
None => {
let buff = CombatEffect::Buff(CombatBuff::default_physical());
damage = damage.with_effect(buff);
},
}
let (crit_chance, crit_mult) =
get_crit_data(data, self.static_data.ability_info);
let attack = Attack::default()
@ -204,6 +212,8 @@ impl CharacterBehavior for Data {
stage_section: StageSection::Recover,
..*self
});
// Remove melee attack component
data.updater.remove::<Melee>(data.entity);
}
},
StageSection::Recover => {
@ -242,7 +252,13 @@ impl CharacterBehavior for Data {
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum MovementBehavior {
Stationary,
ForwardGround,
AxeHover,
GolemHover,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum FrontendSpecifier {
CultistVortex,
}

View File

@ -16,6 +16,7 @@ use common::{
outcome::Outcome,
resources::DeltaTime,
spiral::Spiral2d,
states,
terrain::TerrainChunk,
vol::{RectRasterableVol, SizedVol},
};
@ -195,6 +196,7 @@ impl ParticleMgr {
self.maintain_shockwave_particles(scene_data);
self.maintain_aura_particles(scene_data);
self.maintain_buff_particles(scene_data);
self.maintain_spin_melee_particles(scene_data);
} else {
// remove all particle lifespans
self.particles.clear();
@ -563,6 +565,7 @@ impl ParticleMgr {
let state = scene_data.state;
let ecs = state.ecs();
let time = state.get_time();
let mut rng = thread_rng();
for (pos, auras) in (
&ecs.read_storage::<Pos>(),
@ -577,7 +580,6 @@ impl ParticleMgr {
kind: buff::BuffKind::ProtectingWard,
..
} => {
let mut rng = thread_rng();
let heartbeats = self.scheduler.heartbeats(Duration::from_millis(5));
self.particles.resize_with(
self.particles.len()
@ -792,14 +794,13 @@ impl ParticleMgr {
let ecs = state.ecs();
let time = state.get_time();
for (_i, (_entity, pos, ori, shockwave)) in (
for (_entity, pos, ori, shockwave) in (
&ecs.entities(),
&ecs.read_storage::<Pos>(),
&ecs.read_storage::<Ori>(),
&ecs.read_storage::<Shockwave>(),
)
.join()
.enumerate()
{
let elapsed = time - shockwave.creation.unwrap_or_default();
@ -862,6 +863,56 @@ impl ParticleMgr {
}
}
fn maintain_spin_melee_particles(&mut self, scene_data: &SceneData) {
let state = scene_data.state;
let ecs = state.ecs();
let time = state.get_time();
let mut rng = thread_rng();
for (pos, character_state) in (
&ecs.read_storage::<Pos>(),
&ecs.read_storage::<CharacterState>(),
)
.join()
{
if let CharacterState::SpinMelee(c) = character_state {
if let Some(specifier) = c.static_data.specifier {
match specifier {
states::spin_melee::FrontendSpecifier::CultistVortex => {
let heartbeats = self.scheduler.heartbeats(Duration::from_millis(3));
self.particles.resize_with(
self.particles.len()
+ c.static_data.range.powi(2) as usize
* usize::from(heartbeats)
/ 150,
|| {
let rand_dist =
c.static_data.range * (1.0 - rng.gen::<f32>().powi(10));
let init_pos = Vec3::new(
2.0 * rng.gen::<f32>() - 1.0,
2.0 * rng.gen::<f32>() - 1.0,
0.0,
)
.normalized()
* rand_dist
+ pos.0
+ Vec3::unit_z() * 0.05;
Particle::new_directed(
Duration::from_millis(900),
time,
ParticleMode::CultistFlame,
init_pos,
pos.0,
)
},
);
},
}
}
}
}
}
fn upload_particles(&mut self, renderer: &mut Renderer) {
span!(_guard, "upload_particles", "ParticleMgr::upload_particles");
let all_cpu_instances = self