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,
),
],
meta: Some(Sword(Balanced)),
meta: (
kind: Some(Sword(Balanced)),
capabilities: (
// Roll
bits: 0b00000001,
),
),
)

View File

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

View File

@ -221,7 +221,7 @@ impl CharacterBehavior for Data {
}
// 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
}

View File

@ -97,7 +97,7 @@ impl CharacterBehavior for Data {
}
// 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
}

View File

@ -136,7 +136,7 @@ impl CharacterBehavior for Data {
}
// 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
}

View File

@ -139,7 +139,7 @@ impl CharacterBehavior for Data {
}
// 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
}

View File

@ -172,7 +172,7 @@ impl CharacterBehavior for Data {
}
// 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
}

View File

@ -184,7 +184,7 @@ impl CharacterBehavior for Data {
}
// 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
}

View File

@ -362,7 +362,7 @@ impl CharacterBehavior for Data {
}
// 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
}

View File

@ -97,7 +97,7 @@ impl CharacterBehavior for Data {
handle_orientation(data, &mut update, 1.0, None);
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 =
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
handle_dodge_interrupt(data, &mut update, None);
handle_interrupts(data, &mut update, None);
update
}

View File

@ -155,7 +155,7 @@ impl CharacterBehavior for Data {
}
// 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
}

View File

@ -160,7 +160,7 @@ impl CharacterBehavior for Data {
}
// 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
}

View File

@ -116,7 +116,7 @@ impl CharacterBehavior for Data {
}
// 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
}

View File

@ -169,7 +169,7 @@ impl CharacterBehavior for Data {
}
// 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
}

View File

@ -165,7 +165,7 @@ impl CharacterBehavior for Data {
}
// 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
}

View File

@ -2,7 +2,7 @@ use crate::{
astar::Astar,
combat,
comp::{
ability::AbilityMeta,
ability::{AbilityMeta, InterruptCapability},
arthropod, biped_large, biped_small,
character_state::OutputEvents,
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) {
update.character = CharacterState::from((
&ability,
AbilityInfo::from_input(data, false, InputKind::Block, None),
AbilityInfo::from_input(data, false, InputKind::Block, Default::default()),
data,
));
}
@ -1022,7 +1022,7 @@ pub fn handle_dodge_input(data: &JoinData<'_>, update: &mut StateUpdate) {
if ability.requirements_paid(data, update) {
update.character = CharacterState::from((
&ability,
AbilityInfo::from_input(data, false, InputKind::Roll, None),
AbilityInfo::from_input(data, false, InputKind::Roll, Default::default()),
data,
));
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_dodge_interrupt(
pub fn handle_interrupts(
data: &JoinData,
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>,
) {
// 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))
.map_or(true, |input| !input_is_pressed(data, input))
{
// If there is a stage section, only roll during
if data
.character
.stage_section()
.map_or(true, |stage_section| {
matches!(stage_section, StageSection::Buildup)
})
{
let can_dodge = {
let in_buildup = data
.character
.stage_section()
.map_or(true, |stage_section| {
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);
} else if can_block {
handle_block_input(data, update);
}
}
}
@ -1234,7 +1249,7 @@ pub struct AbilityInfo {
pub hand: Option<HandInfo>,
pub input: InputKind,
pub input_attr: Option<InputAttr>,
pub ability_meta: Option<AbilityMeta>,
pub ability_meta: AbilityMeta,
}
impl AbilityInfo {
@ -1242,7 +1257,7 @@ impl AbilityInfo {
data: &JoinData<'_>,
from_offhand: bool,
input: InputKind,
ability_meta: Option<AbilityMeta>,
ability_meta: AbilityMeta,
) -> Self {
let tool_data = if from_offhand {
unwrap_tool_data(data, EquipSlot::ActiveOffhand)