Spine Cracker

This commit is contained in:
Sam 2024-02-24 11:48:12 -05:00
parent c9c14c9202
commit 7ed66f29bf
16 changed files with 103 additions and 11 deletions

View File

@ -200,7 +200,7 @@
Simple(Hammer(Tremor), "common.abilities.hammer.tremor"),
Simple(Hammer(VigorousBash), "common.abilities.hammer.vigorous_bash"),
Simple(Hammer(Retaliate), "common.abilities.hammer.retaliate"),
// Simple(Hammer(SpineCracker), "common.abilities.hammer.spine_cracker"),
Simple(Hammer(SpineCracker), "common.abilities.hammer.spine_cracker"),
// Simple(Hammer(Breach), "common.abilities.hammer.breach"),
// Contextualized(
// pseudo_id: "common.abilities.hammer.iron_tempest",

View File

@ -0,0 +1,20 @@
FinisherMelee(
energy_cost: 0,
buildup_duration: 0.1,
swing_duration: 0.1,
recover_duration: 0.3,
melee_constructor: (
kind: Bash(
damage: 20,
poise: 0,
knockback: 0,
energy_regen: 0,
),
range: 4.0,
angle: 15.0,
attack_effect: Some((Poise(50), BehindTarget)),
precision_flank_multiplier: 2.0,
),
minimum_combo: 10,
combo_consumption: Cost,
)

BIN
assets/voxygen/element/skills/hammer/spine_cracker.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -407,3 +407,7 @@ common-abilities-hammer-dual_intercept = Intercept
common-abilities-hammer-retaliate = Retaliate
.desc =
After blocking an attack, retaliate with a heavy strike back.
common-abilities-hammer-spine_cracker = Spine Cracker
.desc =
When you foe turns their back to you, strike them hard in the back, targeting the weak part of their spine.

View File

@ -655,6 +655,14 @@ impl Attack {
CombatRequirement::TargetPoised => {
target.char_state.map_or(false, |cs| cs.is_stunned())
},
CombatRequirement::BehindTarget => {
const REQUIRED_ANGLE: f32 = 45.0;
if let Some(ori) = target.ori {
ori.look_vec().angle_between(dir.with_z(0.0)) < REQUIRED_ANGLE
} else {
false
}
},
});
if requirements_met {
is_applied = true;
@ -1014,6 +1022,7 @@ pub enum CombatRequirement {
Combo(u32),
TargetHasBuff(BuffKind),
TargetPoised,
BehindTarget,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
@ -1630,11 +1639,19 @@ pub fn compute_poise_resilience(
}
/// Used to compute the precision multiplier achieved by flanking a target
pub fn precision_mult_from_flank(attack_dir: Vec3<f32>, target_ori: Option<&Ori>) -> Option<f32> {
pub fn precision_mult_from_flank(
attack_dir: Vec3<f32>,
target_ori: Option<&Ori>,
precision_flank_multiplier: f32,
) -> Option<f32> {
let angle = target_ori.map(|t_ori| t_ori.look_dir().angle_between(attack_dir));
match angle {
Some(angle) if angle < FULL_FLANK_ANGLE => Some(MAX_BACK_FLANK_PRECISION),
Some(angle) if angle < PARTIAL_FLANK_ANGLE => Some(MAX_SIDE_FLANK_PRECISION),
Some(angle) if angle < FULL_FLANK_ANGLE => {
Some(MAX_BACK_FLANK_PRECISION * precision_flank_multiplier)
},
Some(angle) if angle < PARTIAL_FLANK_ANGLE => {
Some(MAX_SIDE_FLANK_PRECISION * precision_flank_multiplier)
},
Some(_) | None => None,
}
}

View File

@ -1110,8 +1110,10 @@ impl Default for CharacterAbility {
angle: 15.0,
multi_target: None,
damage_effect: None,
attack_effect: None,
simultaneous_hits: 1,
custom_combo: None,
precision_flank_multiplier: 1.0,
},
ori_modifier: 1.0,
frontend_specifier: None,

View File

@ -23,6 +23,7 @@ pub struct Melee {
pub multi_target: Option<MultiTarget>,
pub break_block: Option<(Vec3<i32>, Option<ToolKind>)>,
pub simultaneous_hits: u32,
pub precision_flank_multiplier: f32,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -51,6 +52,7 @@ impl Component for Melee {
}
fn default_simultaneous_hits() -> u32 { 1 }
fn default_precision_flank_multiplier() -> f32 { 1.0 }
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Scaled {
@ -73,9 +75,12 @@ pub struct MeleeConstructor {
pub angle: f32,
pub multi_target: Option<MultiTarget>,
pub damage_effect: Option<CombatEffect>,
pub attack_effect: Option<(CombatEffect, CombatRequirement)>,
#[serde(default = "default_simultaneous_hits")]
pub simultaneous_hits: u32,
pub custom_combo: Option<CustomCombo>,
#[serde(default = "default_precision_flank_multiplier")]
pub precision_flank_multiplier: f32,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -354,6 +359,14 @@ impl MeleeConstructor {
},
};
let attack = if let Some((effect, requirement)) = self.attack_effect {
let effect = AttackEffect::new(Some(GroupTarget::OutOfGroup), effect)
.with_requirement(requirement);
attack.with_effect(effect)
} else {
attack
};
let attack = match self.custom_combo {
None => attack.with_combo_increment(),
Some(CustomCombo {
@ -377,6 +390,7 @@ impl MeleeConstructor {
multi_target: self.multi_target,
break_block: None,
simultaneous_hits: self.simultaneous_hits,
precision_flank_multiplier: self.precision_flank_multiplier,
}
}

View File

@ -74,9 +74,11 @@ impl CharacterBehavior for Data {
c.exhausted = true;
}
self.static_data
.combo_consumption
.consume(data, output_events);
self.static_data.combo_consumption.consume(
data,
output_events,
self.static_data.minimum_combo,
);
let mut melee_constructor = self.static_data.melee_constructor;
if let Some(scaling) = self.static_data.scaling {

View File

@ -1732,14 +1732,16 @@ pub enum ComboConsumption {
#[default]
All,
Half,
Cost,
}
impl ComboConsumption {
pub fn consume(&self, data: &JoinData, output_events: &mut OutputEvents) {
pub fn consume(&self, data: &JoinData, output_events: &mut OutputEvents, cost: u32) {
let combo = data.combo.map_or(0, |c| c.counter());
let to_consume = match self {
Self::All => combo,
Self::Half => (combo + 1) / 2,
Self::Cost => cost,
};
output_events.emit_server(ComboChangeEvent {
entity: data.entity,

View File

@ -263,6 +263,7 @@ impl<'a> System<'a> for Sys {
let precision_from_flank = combat::precision_mult_from_flank(
beam.bezier.ctrl - beam.bezier.start,
target_info.ori,
1.0,
);
let precision_from_time = {

View File

@ -248,6 +248,7 @@ impl<'a> System<'a> for Sys {
.try_normalized()
.unwrap_or(ori.look_vec()),
target_ori,
melee_attack.precision_flank_multiplier,
);
let precision_from_poise = {

View File

@ -390,7 +390,7 @@ fn dispatch_hit(
.map_or(false, |i| i.projectiles);
let precision_from_flank =
combat::precision_mult_from_flank(*projectile_dir, target_info.ori);
combat::precision_mult_from_flank(*projectile_dir, target_info.ori, 1.0);
let precision_from_head = {
// This performs a cylinder and line segment intersection check. The cylinder is

View File

@ -1,6 +1,6 @@
use super::{
super::{vek::*, Animation},
CharacterSkeleton, SkeletonAttr,
hammer_start, twist_back, twist_forward, CharacterSkeleton, SkeletonAttr,
};
use common::states::utils::{AbilityInfo, StageSection};
use core::f32::consts::{PI, TAU};
@ -440,6 +440,28 @@ impl Animation for FinisherMeleeAnimation {
next.control.orientation.rotate_z(move2 * -3.5);
next.control.position += Vec3::new(move2 * 14.0, move2 * 6.0, 0.0);
},
Some("common.abilities.hammer.spine_cracker") => {
hammer_start(&mut next, s_a);
let (move1, move2, move3) = match stage_section {
Some(StageSection::Buildup) => (anim_time, 0.0, 0.0),
Some(StageSection::Action) => (1.0, anim_time, 0.0),
Some(StageSection::Recover) => (1.0, 1.0, anim_time),
_ => (0.0, 0.0, 0.0),
};
let pullback = 1.0 - move3;
let move1 = move1 * pullback;
let move2 = move2 * pullback;
twist_back(&mut next, move1, 1.9, 1.5, 0.5, 1.2);
next.head.position += Vec3::new(-2.0, 2.0, 0.0) * move1;
next.control.orientation.rotate_x(move1 * 1.8);
next.control.position += Vec3::new(0.0, 0.0, 8.0) * move1;
next.control.orientation.rotate_y(move1 * 0.4);
twist_forward(&mut next, move2, 2.1, 1.6, 0.4, 1.3);
next.control.orientation.rotate_z(move2 * 1.6);
next.control.position += Vec3::new(-16.0, 12.0, -8.0) * move2;
},
_ => {},
}

View File

@ -86,9 +86,11 @@ fn maps_basic_melee() {
range: 3.5,
angle: 15.0,
damage_effect: None,
attack_effect: None,
multi_target: None,
simultaneous_hits: 1,
combo_gain: 1,
custom_combo: None,
precision_flank_multiplier: 1.0,
},
ori_modifier: 1.0,
ability_info: empty_ability_info(),

View File

@ -323,6 +323,7 @@ image_ids! {
hammer_heavy_whorl: "voxygen.element.skills.hammer.heavy_whorl",
hammer_intercept: "voxygen.element.skills.hammer.intercept",
hammer_retaliate: "voxygen.element.skills.hammer.retaliate",
hammer_spine_cracker: "voxygen.element.skills.hammer.spine_cracker",
// Skilltree Icons
health_plus_skill: "voxygen.element.skills.skilltree.health_plus",
energy_plus_skill: "voxygen.element.skills.skilltree.energy_plus",

View File

@ -632,6 +632,7 @@ pub fn ability_image(imgs: &img_ids::Imgs, ability_id: &str) -> image::Id {
"common.abilities.hammer.intercept" => imgs.hammer_intercept,
"common.abilities.hammer.dual_intercept" => imgs.hammer_intercept,
"common.abilities.hammer.retaliate" => imgs.hammer_retaliate,
"common.abilities.hammer.spine_cracker" => imgs.hammer_spine_cracker,
// Bow
"common.abilities.bow.charged" => imgs.bow_m1,
"common.abilities.bow.repeater" => imgs.bow_m2,