Initial work on attack and hitbox interpolation
This commit is contained in:
+75
-11
@@ -1,9 +1,13 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
|
||||
use bevy::{asset::Handle, ecs::component::Component, reflect::Reflect};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{fighter::animation::AnimKind, math::{int::FGi32, vec3::FGVec3}, scripting::FighterAttackScript};
|
||||
use crate::{
|
||||
fighter::{FighterAttributes, animation::AnimKind},
|
||||
math::{int::FGi32, vec3::FGVec3},
|
||||
scripting::FighterAttackScript,
|
||||
};
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub struct FighterDamage(FGi32);
|
||||
|
||||
@@ -16,7 +20,7 @@ impl FighterDamage {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum KnockbackType {
|
||||
Normal,
|
||||
Fixed
|
||||
Fixed,
|
||||
}
|
||||
|
||||
impl KnockbackType {
|
||||
@@ -41,7 +45,7 @@ impl Knockback {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum AttackAngle {
|
||||
Normal(FGi32),
|
||||
Sakurai
|
||||
Sakurai,
|
||||
}
|
||||
|
||||
impl Default for AttackAngle {
|
||||
@@ -51,7 +55,6 @@ impl Default for AttackAngle {
|
||||
}
|
||||
|
||||
impl AttackAngle {
|
||||
|
||||
pub fn make_normal(angle: FGi32) -> Self {
|
||||
Self::Normal(angle)
|
||||
}
|
||||
@@ -91,16 +94,77 @@ pub struct AttackHitbox {
|
||||
pub knockback: Knockback,
|
||||
pub knockback_growth: FGi32,
|
||||
pub start_frame: u32,
|
||||
pub end_frame: u32
|
||||
pub end_frame: u32,
|
||||
}
|
||||
|
||||
pub struct StaleMoveQueue(pub VecDeque<AttackKind>);
|
||||
|
||||
impl AttackHitbox {
|
||||
/// Knockback calculation
|
||||
pub fn calculate_knockback(
|
||||
&self,
|
||||
attack: AttackKind,
|
||||
|
||||
receiver_attribs: &FighterAttributes,
|
||||
receiver_damage: FighterDamage,
|
||||
total_damage_received_this_frame: FighterDamage,
|
||||
interrupted_smash_charge: bool,
|
||||
receiver_crouching: bool,
|
||||
) -> Knockback {
|
||||
/// Weight that produces a weight modifier of exactly 1.0. Weight-independent
|
||||
/// attacks pass this value in place of the target's real weight.
|
||||
const BASELINE_WEIGHT: FGi32 = FGi32::lit("100.0");
|
||||
|
||||
/// Applied to the weight-scaled damage term.
|
||||
const KB_GAIN: FGi32 = FGi32::lit("1.4");
|
||||
|
||||
/// Constant floor added to the damage term before knockback growth scales it.
|
||||
/// At 0% and 0 damage, knockback reduces to KB_OFFSET * s + b.
|
||||
const KB_OFFSET: FGi32 = FGi32::lit("18.0");
|
||||
|
||||
/// Knockback growth is authored in percent (110 means 1.1x).
|
||||
const KB_GROWTH_SCALE: FGi32 = FGi32::lit("0.01");
|
||||
const DAMAGE_TERM_DIVISOR: FGi32 = FGi32::lit("20.0");
|
||||
const DAMAGE_BONUS: FGi32 = FGi32::lit("2.0"); // effective damage floor in the damage term
|
||||
const CHARGE_SMASH_INTERRUPTION_MODIFIER: FGi32 = FGi32::lit("1.2");
|
||||
const CROUCH_CANCEL_MODIFIER: FGi32 = FGi32::lit("0.666667");
|
||||
|
||||
let attack_damage_unstalled = self.damage.0;
|
||||
|
||||
let modifier = {
|
||||
let mut out = FGi32::ONE;
|
||||
if interrupted_smash_charge {
|
||||
out *= CHARGE_SMASH_INTERRUPTION_MODIFIER
|
||||
} else if receiver_crouching {
|
||||
out *= CROUCH_CANCEL_MODIFIER
|
||||
}
|
||||
out
|
||||
};
|
||||
|
||||
let s = KB_GROWTH_SCALE * self.knockback_growth;
|
||||
let p = receiver_damage.0.floor() + total_damage_received_this_frame.0;
|
||||
let d = attack_damage_unstalled;
|
||||
let damage_term = p / DAMAGE_TERM_DIVISOR * (d + DAMAGE_BONUS);
|
||||
|
||||
let scaled = damage_term * (FGi32::lit("2.0") * BASELINE_WEIGHT)
|
||||
/ (receiver_attribs.weight + BASELINE_WEIGHT);
|
||||
let kb = (scaled * KB_GAIN + KB_OFFSET) * s + self.knockback.0;
|
||||
|
||||
Knockback(modifier * kb)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_active_hitbox_list(script: &FighterAttackScript, list: &mut Vec<usize>, frame: u32) {
|
||||
*list = script.hitboxes.iter().enumerate().filter(|(_, hb)| {
|
||||
hb.start_frame <= frame && hb.end_frame > frame
|
||||
}).map(|(i, _)| i).collect()
|
||||
*list = script
|
||||
.hitboxes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, hb)| hb.start_frame <= frame && hb.end_frame > frame)
|
||||
.map(|(i, _)| i)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct FighterAttackScriptAssets {
|
||||
pub scripts: HashMap<AttackKind, Handle<FighterAttackScript>>
|
||||
}
|
||||
pub scripts: HashMap<AttackKind, Handle<FighterAttackScript>>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user