Added capability for attacks to be interrupted by dodging or blocking

This commit is contained in:
Sam 2022-02-18 12:49:51 -05:00
parent c09e858cba
commit 057b1a4ad6
19 changed files with 133 additions and 59 deletions

View File

@ -55,5 +55,11 @@ ComboMelee2(
ori_modifier: 0.6, ori_modifier: 0.6,
), ),
], ],
meta: Some(Sword(Balanced)), meta: (
kind: Some(Sword(Balanced)),
capabilities: (
// Roll
bits: 0b00000001,
),
),
) )

View File

@ -14,5 +14,11 @@ BasicMelee(
range: 5.0, range: 5.0,
angle: 10.0, angle: 10.0,
), ),
meta: Some(Sword(Balanced)), meta: (
kind: Some(Sword(Balanced)),
capabilities: (
// Block
bits: 0b00000010,
),
),
) )

View File

@ -419,7 +419,8 @@ pub enum CharacterAbility {
recover_duration: f32, recover_duration: f32,
melee_constructor: MeleeConstructor, melee_constructor: MeleeConstructor,
ori_modifier: f32, ori_modifier: f32,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
BasicRanged { BasicRanged {
energy_cost: f32, energy_cost: f32,
@ -431,7 +432,8 @@ pub enum CharacterAbility {
projectile_speed: f32, projectile_speed: f32,
num_projectiles: u32, num_projectiles: u32,
projectile_spread: f32, projectile_spread: f32,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
RepeaterRanged { RepeaterRanged {
energy_cost: f32, energy_cost: f32,
@ -444,14 +446,16 @@ pub enum CharacterAbility {
projectile_body: Body, projectile_body: Body,
projectile_light: Option<LightEmitter>, projectile_light: Option<LightEmitter>,
projectile_speed: f32, projectile_speed: f32,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
Boost { Boost {
movement_duration: f32, movement_duration: f32,
only_up: bool, only_up: bool,
speed: f32, speed: f32,
max_exit_velocity: f32, max_exit_velocity: f32,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
DashMelee { DashMelee {
energy_cost: f32, energy_cost: f32,
@ -464,7 +468,8 @@ pub enum CharacterAbility {
melee_constructor: MeleeConstructor, melee_constructor: MeleeConstructor,
ori_modifier: f32, ori_modifier: f32,
charge_through: bool, charge_through: bool,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
BasicBlock { BasicBlock {
buildup_duration: f32, buildup_duration: f32,
@ -472,7 +477,8 @@ pub enum CharacterAbility {
max_angle: f32, max_angle: f32,
block_strength: f32, block_strength: f32,
energy_cost: f32, energy_cost: f32,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
Roll { Roll {
energy_cost: f32, energy_cost: f32,
@ -481,7 +487,8 @@ pub enum CharacterAbility {
recover_duration: f32, recover_duration: f32,
roll_strength: f32, roll_strength: f32,
immune_melee: bool, immune_melee: bool,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
ComboMelee { ComboMelee {
stage_data: Vec<combo_melee::Stage<f32>>, stage_data: Vec<combo_melee::Stage<f32>>,
@ -492,11 +499,13 @@ pub enum CharacterAbility {
max_speed_increase: f32, max_speed_increase: f32,
scales_from_combo: u32, scales_from_combo: u32,
ori_modifier: f32, ori_modifier: f32,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
ComboMelee2 { ComboMelee2 {
strikes: Vec<combo_melee2::Strike<f32>>, strikes: Vec<combo_melee2::Strike<f32>>,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
LeapMelee { LeapMelee {
energy_cost: f32, energy_cost: f32,
@ -507,7 +516,8 @@ pub enum CharacterAbility {
melee_constructor: MeleeConstructor, melee_constructor: MeleeConstructor,
forward_leap_strength: f32, forward_leap_strength: f32,
vertical_leap_strength: f32, vertical_leap_strength: f32,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
SpinMelee { SpinMelee {
buildup_duration: f32, buildup_duration: f32,
@ -520,7 +530,8 @@ pub enum CharacterAbility {
num_spins: u32, num_spins: u32,
specifier: Option<spin_melee::FrontendSpecifier>, specifier: Option<spin_melee::FrontendSpecifier>,
melee_constructor: MeleeConstructor, melee_constructor: MeleeConstructor,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
ChargedMelee { ChargedMelee {
energy_cost: f32, energy_cost: f32,
@ -531,7 +542,8 @@ pub enum CharacterAbility {
recover_duration: f32, recover_duration: f32,
melee_constructor: MeleeConstructor, melee_constructor: MeleeConstructor,
specifier: Option<charged_melee::FrontendSpecifier>, specifier: Option<charged_melee::FrontendSpecifier>,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
ChargedRanged { ChargedRanged {
energy_cost: f32, energy_cost: f32,
@ -550,7 +562,8 @@ pub enum CharacterAbility {
initial_projectile_speed: f32, initial_projectile_speed: f32,
scaled_projectile_speed: f32, scaled_projectile_speed: f32,
move_speed: f32, move_speed: f32,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
Shockwave { Shockwave {
energy_cost: f32, energy_cost: f32,
@ -569,7 +582,8 @@ pub enum CharacterAbility {
damage_kind: DamageKind, damage_kind: DamageKind,
specifier: comp::shockwave::FrontendSpecifier, specifier: comp::shockwave::FrontendSpecifier,
damage_effect: Option<CombatEffect>, damage_effect: Option<CombatEffect>,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
BasicBeam { BasicBeam {
buildup_duration: f32, buildup_duration: f32,
@ -584,7 +598,8 @@ pub enum CharacterAbility {
energy_drain: f32, energy_drain: f32,
ori_rate: f32, ori_rate: f32,
specifier: beam::FrontendSpecifier, specifier: beam::FrontendSpecifier,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
BasicAura { BasicAura {
buildup_duration: f32, buildup_duration: f32,
@ -597,13 +612,15 @@ pub enum CharacterAbility {
energy_cost: f32, energy_cost: f32,
scales_with_combo: bool, scales_with_combo: bool,
specifier: Option<aura::Specifier>, specifier: Option<aura::Specifier>,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
Blink { Blink {
buildup_duration: f32, buildup_duration: f32,
recover_duration: f32, recover_duration: f32,
max_range: f32, max_range: f32,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
BasicSummon { BasicSummon {
buildup_duration: f32, buildup_duration: f32,
@ -613,7 +630,8 @@ pub enum CharacterAbility {
summon_distance: (f32, f32), summon_distance: (f32, f32),
summon_info: basic_summon::SummonInfo, summon_info: basic_summon::SummonInfo,
duration: Option<Duration>, duration: Option<Duration>,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
SelfBuff { SelfBuff {
buildup_duration: f32, buildup_duration: f32,
@ -623,7 +641,8 @@ pub enum CharacterAbility {
buff_strength: f32, buff_strength: f32,
buff_duration: Option<f32>, buff_duration: Option<f32>,
energy_cost: f32, energy_cost: f32,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
SpriteSummon { SpriteSummon {
buildup_duration: f32, buildup_duration: f32,
@ -632,7 +651,8 @@ pub enum CharacterAbility {
sprite: SpriteKind, sprite: SpriteKind,
summon_distance: (f32, f32), summon_distance: (f32, f32),
sparseness: f64, sparseness: f64,
meta: Option<AbilityMeta>, #[serde(default)]
meta: AbilityMeta,
}, },
Music { Music {
play_duration: f32, play_duration: f32,
@ -661,7 +681,7 @@ impl Default for CharacterAbility {
damage_effect: None, damage_effect: None,
}, },
ori_modifier: 1.0, ori_modifier: 1.0,
meta: None, meta: Default::default(),
} }
} }
} }
@ -728,7 +748,7 @@ impl CharacterAbility {
recover_duration: 0.125, recover_duration: 0.125,
roll_strength: 2.0, roll_strength: 2.0,
immune_melee: true, immune_melee: true,
meta: None, meta: Default::default(),
} }
} }
@ -739,7 +759,7 @@ impl CharacterAbility {
max_angle: 60.0, max_angle: 60.0,
block_strength: 0.5, block_strength: 0.5,
energy_cost: 2.5, energy_cost: 2.5,
meta: None, meta: Default::default(),
} }
} }
@ -1165,7 +1185,7 @@ impl CharacterAbility {
} }
// TODO: Maybe consider making CharacterAbility a struct at some point? // TODO: Maybe consider making CharacterAbility a struct at some point?
pub fn ability_meta(&self) -> Option<AbilityMeta> { pub fn ability_meta(&self) -> AbilityMeta {
use CharacterAbility::*; use CharacterAbility::*;
match self { match self {
BasicMelee { meta, .. } BasicMelee { meta, .. }
@ -1194,8 +1214,8 @@ impl CharacterAbility {
#[must_use] #[must_use]
pub fn contextualize(mut self, data: &JoinData) -> Self { pub fn contextualize(mut self, data: &JoinData) -> Self {
if let Some(ability_info) = data.character.ability_info() { if let Some(ability_info) = data.character.ability_info() {
if let Some(AbilityMeta::Sword(old_stance)) = ability_info.ability_meta { if let Some(AbilityKind::Sword(old_stance)) = ability_info.ability_meta.kind {
if matches!(self.ability_meta(), Some(AbilityMeta::Sword(new_stance)) if old_stance == new_stance) if matches!(self.ability_meta().kind, Some(AbilityKind::Sword(new_stance)) if old_stance == new_stance)
{ {
const ENERGY_REDUCTION: f32 = 0.75; const ENERGY_REDUCTION: f32 = 0.75;
use CharacterAbility::*; use CharacterAbility::*;
@ -2266,7 +2286,25 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
} }
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum AbilityMeta { pub struct AbilityMeta {
pub kind: Option<AbilityKind>,
#[serde(default)]
pub capabilities: InterruptCapability,
}
impl Default for AbilityMeta {
fn default() -> Self {
Self {
kind: None,
capabilities: InterruptCapability::NONE,
}
}
}
// Only extend this if it is needed to control certain functionality of
// abilities
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum AbilityKind {
Sword(SwordStance), Sword(SwordStance),
} }
@ -2283,3 +2321,12 @@ pub enum SwordStance {
Reaching, Reaching,
AirSlash, AirSlash,
} }
bitflags::bitflags! {
#[derive(Default, Serialize, Deserialize)]
pub struct InterruptCapability: u8 {
const NONE = 0b00000000;
const ROLL = 0b00000001;
const BLOCK = 0b00000010;
}
}

View File

@ -147,7 +147,7 @@ impl CharacterBehavior for Data {
} }
// At end of state logic so an interrupt isn't overwritten // At end of state logic so an interrupt isn't overwritten
handle_dodge_interrupt(data, &mut update, None); handle_interrupts(data, &mut update, None);
update update
} }

View File

@ -221,7 +221,7 @@ impl CharacterBehavior for Data {
} }
// At end of state logic so an interrupt isn't overwritten // At end of state logic so an interrupt isn't overwritten
handle_dodge_interrupt(data, &mut update, None); handle_interrupts(data, &mut update, None);
update update
} }

View File

@ -97,7 +97,7 @@ impl CharacterBehavior for Data {
} }
// At end of state logic so an interrupt isn't overwritten // At end of state logic so an interrupt isn't overwritten
handle_dodge_interrupt(data, &mut update, None); handle_interrupts(data, &mut update, None);
update update
} }

View File

@ -136,7 +136,7 @@ impl CharacterBehavior for Data {
} }
// At end of state logic so an interrupt isn't overwritten // At end of state logic so an interrupt isn't overwritten
handle_dodge_interrupt(data, &mut update, None); handle_interrupts(data, &mut update, None);
update update
} }

View File

@ -139,7 +139,7 @@ impl CharacterBehavior for Data {
} }
// At end of state logic so an interrupt isn't overwritten // At end of state logic so an interrupt isn't overwritten
handle_dodge_interrupt(data, &mut update, None); handle_interrupts(data, &mut update, None);
update update
} }

View File

@ -172,7 +172,7 @@ impl CharacterBehavior for Data {
} }
// At end of state logic so an interrupt isn't overwritten // At end of state logic so an interrupt isn't overwritten
handle_dodge_interrupt(data, &mut update, None); handle_interrupts(data, &mut update, None);
update update
} }

View File

@ -184,7 +184,7 @@ impl CharacterBehavior for Data {
} }
// At end of state logic so an interrupt isn't overwritten // At end of state logic so an interrupt isn't overwritten
handle_dodge_interrupt(data, &mut update, None); handle_interrupts(data, &mut update, None);
update update
} }

View File

@ -362,7 +362,7 @@ impl CharacterBehavior for Data {
} }
// At end of state logic so an interrupt isn't overwritten // At end of state logic so an interrupt isn't overwritten
handle_dodge_interrupt(data, &mut update, None); handle_interrupts(data, &mut update, None);
update update
} }

View File

@ -97,7 +97,7 @@ impl CharacterBehavior for Data {
handle_orientation(data, &mut update, 1.0, None); handle_orientation(data, &mut update, 1.0, None);
handle_move(data, &mut update, 0.7); handle_move(data, &mut update, 0.7);
handle_dodge_interrupt(data, &mut update, Some(InputKind::Primary)); handle_interrupts(data, &mut update, Some(InputKind::Primary));
let strike_data = let strike_data =
self.static_data.strikes[self.completed_strikes % self.static_data.strikes.len()]; self.static_data.strikes[self.completed_strikes % self.static_data.strikes.len()];

View File

@ -245,7 +245,7 @@ impl CharacterBehavior for Data {
} }
// At end of state logic so an interrupt isn't overwritten // At end of state logic so an interrupt isn't overwritten
handle_dodge_interrupt(data, &mut update, None); handle_interrupts(data, &mut update, None);
update update
} }

View File

@ -155,7 +155,7 @@ impl CharacterBehavior for Data {
} }
// At end of state logic so an interrupt isn't overwritten // At end of state logic so an interrupt isn't overwritten
handle_dodge_interrupt(data, &mut update, None); handle_interrupts(data, &mut update, None);
update update
} }

View File

@ -160,7 +160,7 @@ impl CharacterBehavior for Data {
} }
// At end of state logic so an interrupt isn't overwritten // At end of state logic so an interrupt isn't overwritten
handle_dodge_interrupt(data, &mut update, None); handle_interrupts(data, &mut update, None);
update update
} }

View File

@ -116,7 +116,7 @@ impl CharacterBehavior for Data {
} }
// At end of state logic so an interrupt isn't overwritten // At end of state logic so an interrupt isn't overwritten
handle_dodge_interrupt(data, &mut update, None); handle_interrupts(data, &mut update, None);
update update
} }

View File

@ -169,7 +169,7 @@ impl CharacterBehavior for Data {
} }
// At end of state logic so an interrupt isn't overwritten // At end of state logic so an interrupt isn't overwritten
handle_dodge_interrupt(data, &mut update, None); handle_interrupts(data, &mut update, None);
update update
} }

View File

@ -165,7 +165,7 @@ impl CharacterBehavior for Data {
} }
// At end of state logic so an interrupt isn't overwritten // At end of state logic so an interrupt isn't overwritten
handle_dodge_interrupt(data, &mut update, None); handle_interrupts(data, &mut update, None);
update update
} }

View File

@ -2,7 +2,7 @@ use crate::{
astar::Astar, astar::Astar,
combat, combat,
comp::{ comp::{
ability::AbilityMeta, ability::{AbilityMeta, InterruptCapability},
arthropod, biped_large, biped_small, arthropod, biped_large, biped_small,
character_state::OutputEvents, character_state::OutputEvents,
inventory::slot::{ArmorSlot, EquipSlot, Slot}, inventory::slot::{ArmorSlot, EquipSlot, Slot},
@ -1007,7 +1007,7 @@ pub fn handle_block_input(data: &JoinData<'_>, update: &mut StateUpdate) {
if ability.requirements_paid(data, update) { if ability.requirements_paid(data, update) {
update.character = CharacterState::from(( update.character = CharacterState::from((
&ability, &ability,
AbilityInfo::from_input(data, false, InputKind::Block, None), AbilityInfo::from_input(data, false, InputKind::Block, Default::default()),
data, data,
)); ));
} }
@ -1022,7 +1022,7 @@ pub fn handle_dodge_input(data: &JoinData<'_>, update: &mut StateUpdate) {
if ability.requirements_paid(data, update) { if ability.requirements_paid(data, update) {
update.character = CharacterState::from(( update.character = CharacterState::from((
&ability, &ability,
AbilityInfo::from_input(data, false, InputKind::Roll, None), AbilityInfo::from_input(data, false, InputKind::Roll, Default::default()),
data, data,
)); ));
if let CharacterState::Roll(roll) = &mut update.character { if let CharacterState::Roll(roll) = &mut update.character {
@ -1042,10 +1042,11 @@ pub fn handle_dodge_input(data: &JoinData<'_>, update: &mut StateUpdate) {
} }
} }
/// Contains logic for dodge interrupt pub fn handle_interrupts(
pub fn handle_dodge_interrupt(
data: &JoinData, data: &JoinData,
update: &mut StateUpdate, update: &mut StateUpdate,
// Used when an input other than the one that activated the ability being pressed should block
// an interrupt
input_override: Option<InputKind>, input_override: Option<InputKind>,
) { ) {
// Check that the input used to enter current character state (if there was one) // Check that the input used to enter current character state (if there was one)
@ -1054,15 +1055,29 @@ pub fn handle_dodge_interrupt(
.or_else(|| data.character.ability_info().map(|a| a.input)) .or_else(|| data.character.ability_info().map(|a| a.input))
.map_or(true, |input| !input_is_pressed(data, input)) .map_or(true, |input| !input_is_pressed(data, input))
{ {
// If there is a stage section, only roll during let can_dodge = {
if data let in_buildup = data
.character .character
.stage_section() .stage_section()
.map_or(true, |stage_section| { .map_or(true, |stage_section| {
matches!(stage_section, StageSection::Buildup) matches!(stage_section, StageSection::Buildup)
}) });
{ let interruptible = data.character.ability_info().map_or(false, |info| {
info.ability_meta
.capabilities
.contains(InterruptCapability::ROLL)
});
in_buildup || interruptible
};
let can_block = data.character.ability_info().map_or(false, |info| {
info.ability_meta
.capabilities
.contains(InterruptCapability::BLOCK)
});
if can_dodge {
handle_dodge_input(data, update); handle_dodge_input(data, update);
} else if can_block {
handle_block_input(data, update);
} }
} }
} }
@ -1234,7 +1249,7 @@ pub struct AbilityInfo {
pub hand: Option<HandInfo>, pub hand: Option<HandInfo>,
pub input: InputKind, pub input: InputKind,
pub input_attr: Option<InputAttr>, pub input_attr: Option<InputAttr>,
pub ability_meta: Option<AbilityMeta>, pub ability_meta: AbilityMeta,
} }
impl AbilityInfo { impl AbilityInfo {
@ -1242,7 +1257,7 @@ impl AbilityInfo {
data: &JoinData<'_>, data: &JoinData<'_>,
from_offhand: bool, from_offhand: bool,
input: InputKind, input: InputKind,
ability_meta: Option<AbilityMeta>, ability_meta: AbilityMeta,
) -> Self { ) -> Self {
let tool_data = if from_offhand { let tool_data = if from_offhand {
unwrap_tool_data(data, EquipSlot::ActiveOffhand) unwrap_tool_data(data, EquipSlot::ActiveOffhand)