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, recover_duration: 0.2,
base_damage: 70, base_damage: 70,
base_poise_damage: 55, base_poise_damage: 55,
knockback: 0.0, knockback: ( strength: 0.0, direction: Away),
range: 3.5, range: 3.5,
damage_effect: None,
energy_cost: 100, energy_cost: 100,
is_infinite: true, is_infinite: true,
movement_behavior: AxeHover, movement_behavior: AxeHover,
is_interruptible: false, is_interruptible: false,
forward_speed: 0.0, forward_speed: 0.0,
num_spins: 1, num_spins: 1,
specifier: None,
) )

View File

@ -4,12 +4,14 @@ SpinMelee(
recover_duration: 0.5, recover_duration: 0.5,
base_damage: 160, base_damage: 160,
base_poise_damage: 25, base_poise_damage: 25,
knockback: 10.0, knockback: ( strength: 10.0, direction: Away),
range: 3.5, range: 3.5,
damage_effect: None,
energy_cost: 150, energy_cost: 150,
is_infinite: false, is_infinite: false,
movement_behavior: ForwardGround, movement_behavior: ForwardGround,
is_interruptible: true, is_interruptible: true,
forward_speed: 1.0, forward_speed: 1.0,
num_spins: 3, 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, recover_duration: 0.1,
base_damage: 500, base_damage: 500,
base_poise_damage: 30, base_poise_damage: 30,
knockback: 0.0, knockback: ( strength: 0.0, direction: Away),
range: 7.5, range: 7.5,
damage_effect: None,
energy_cost: 0, energy_cost: 0,
is_infinite: false, is_infinite: false,
movement_behavior: GolemHover, movement_behavior: GolemHover,
is_interruptible: false, is_interruptible: false,
forward_speed: 0.0, forward_speed: 0.0,
num_spins: 1, num_spins: 1,
specifier: None,
) )

View File

@ -395,10 +395,10 @@ void main() {
); );
} else if (inst_mode == CULTIST_FLAME) { } else if (inst_mode == CULTIST_FLAME) {
f_reflect = 0.0; // Fire doesn't reflect light, it emits it 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( attr = Attr(
(inst_dir * slow_end(1.5)) + vec3(rand0, rand1, rand2) * (percent() + 2) * 0.1, (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), vec4(purp_color, 0.0, purp_color, 1),
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9) spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
); );

View File

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

View File

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

View File

@ -16,6 +16,7 @@ use common::{
outcome::Outcome, outcome::Outcome,
resources::DeltaTime, resources::DeltaTime,
spiral::Spiral2d, spiral::Spiral2d,
states,
terrain::TerrainChunk, terrain::TerrainChunk,
vol::{RectRasterableVol, SizedVol}, vol::{RectRasterableVol, SizedVol},
}; };
@ -195,6 +196,7 @@ impl ParticleMgr {
self.maintain_shockwave_particles(scene_data); self.maintain_shockwave_particles(scene_data);
self.maintain_aura_particles(scene_data); self.maintain_aura_particles(scene_data);
self.maintain_buff_particles(scene_data); self.maintain_buff_particles(scene_data);
self.maintain_spin_melee_particles(scene_data);
} else { } else {
// remove all particle lifespans // remove all particle lifespans
self.particles.clear(); self.particles.clear();
@ -563,6 +565,7 @@ impl ParticleMgr {
let state = scene_data.state; let state = scene_data.state;
let ecs = state.ecs(); let ecs = state.ecs();
let time = state.get_time(); let time = state.get_time();
let mut rng = thread_rng();
for (pos, auras) in ( for (pos, auras) in (
&ecs.read_storage::<Pos>(), &ecs.read_storage::<Pos>(),
@ -577,7 +580,6 @@ impl ParticleMgr {
kind: buff::BuffKind::ProtectingWard, kind: buff::BuffKind::ProtectingWard,
.. ..
} => { } => {
let mut rng = thread_rng();
let heartbeats = self.scheduler.heartbeats(Duration::from_millis(5)); let heartbeats = self.scheduler.heartbeats(Duration::from_millis(5));
self.particles.resize_with( self.particles.resize_with(
self.particles.len() self.particles.len()
@ -792,14 +794,13 @@ impl ParticleMgr {
let ecs = state.ecs(); let ecs = state.ecs();
let time = state.get_time(); let time = state.get_time();
for (_i, (_entity, pos, ori, shockwave)) in ( for (_entity, pos, ori, shockwave) in (
&ecs.entities(), &ecs.entities(),
&ecs.read_storage::<Pos>(), &ecs.read_storage::<Pos>(),
&ecs.read_storage::<Ori>(), &ecs.read_storage::<Ori>(),
&ecs.read_storage::<Shockwave>(), &ecs.read_storage::<Shockwave>(),
) )
.join() .join()
.enumerate()
{ {
let elapsed = time - shockwave.creation.unwrap_or_default(); 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) { fn upload_particles(&mut self, renderer: &mut Renderer) {
span!(_guard, "upload_particles", "ParticleMgr::upload_particles"); span!(_guard, "upload_particles", "ParticleMgr::upload_particles");
let all_cpu_instances = self let all_cpu_instances = self