Merge branch 'nerasw/defensive_stance_tweaks' into 'master'

Nerasw/defensive stance tweaks

See merge request veloren/veloren!4020
This commit is contained in:
Isse 2023-07-28 21:29:32 +00:00
commit 05e925d180
29 changed files with 267 additions and 100 deletions

View File

@ -8,10 +8,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Esperanto translation
- Item quantity sort in player inventory.
- Using Block('Alt' by default) in Defensive Stance now feels stronger
- Recipe for twigs from wooden logs
- First version of multisalvage that allows to obtain more than one piece of material from salvage
### Changed
- Plugins now target wasm32-unknown-wasi and all wasm cfgs are gone
- Slightly reduced quantities of ingredients needed to craft cooked foods
- Improved and cleaned loot tables for T1 and T2 dungeons as well as large cave monsters (Good bye, Bowls and Stones!)
- Defensive Fell Strike's dmg raised
- Defensive Cascade's more effective against parried foes
- Defensive Riposte's buildup duration raised a bit
- Capabilities of strikes to parry & block now more reliable
- Defensive Disengage now more responsive and can block melee
- Deflect no longer parry melee hits
- Made helmets, necklaces, rings, twig armors and some gliders salvageable
- Tweaked stats on some foods so they generally increase a tiny bit more HP
- Reduced idle time after consumption from 5 to 4 seconds

View File

@ -3,6 +3,13 @@
// A set of abilities is a primary, a secondary, and a vec of all extra abilities
({
Tool(Sword): (
guard: Some(Contextualized(
pseudo_id: "veloren.core.pseudo_abilities.sword.guard",
abilities: [
([Stance(Sword(Defensive))], (None, "common.abilities.sword.defensive_guard")),
([], (None, "common.abilities.sword.basic_guard")),
],
)),
primary: Contextualized(
pseudo_id: "veloren.core.pseudo_abilities.sword.double_slash",
abilities: [
@ -136,6 +143,7 @@
],
),
Tool(Axe): (
guard: Some(Simple(None, "common.abilities.axe.basic_guard")),
primary: Simple(None, "common.abilities.axe.doublestrike"),
secondary: Simple(None, "common.abilities.axe.spin"),
abilities: [
@ -143,6 +151,7 @@
],
),
Tool(Hammer): (
guard: Some(Simple(None, "common.abilities.hammer.basic_guard")),
primary: Simple(None, "common.abilities.hammer.singlestrike"),
secondary: Simple(None, "common.abilities.hammer.charged"),
abilities: [

View File

@ -0,0 +1,21 @@
BasicBlock(
buildup_duration: 0.25,
recover_duration: 0.2,
max_angle: 60.0,
block_strength: 0.5,
parry_window: (
buildup: true,
recover: false,
),
energy_cost: 5,
energy_regen: 2.5,
can_hold: true,
blocked_attacks: (
melee: true,
projectiles: false,
beams: false,
ground_shockwaves: false,
air_shockwaves: false,
explosions: false,
),
)

View File

@ -0,0 +1,21 @@
BasicBlock(
buildup_duration: 0.25,
recover_duration: 0.2,
max_angle: 60.0,
block_strength: 0.5,
parry_window: (
buildup: true,
recover: false,
),
energy_cost: 5,
energy_regen: 2.5,
can_hold: true,
blocked_attacks: (
melee: true,
projectiles: false,
beams: false,
ground_shockwaves: false,
air_shockwaves: false,
explosions: false,
),
)

View File

@ -8,6 +8,7 @@ BasicBlock(
recover: false,
),
energy_cost: 0.0,
energy_regen: 0.0,
can_hold: true,
blocked_attacks: (
melee: true,

View File

@ -0,0 +1,21 @@
BasicBlock(
buildup_duration: 0.25,
recover_duration: 0.2,
max_angle: 60.0,
block_strength: 0.5,
parry_window: (
buildup: true,
recover: false,
),
energy_cost: 5,
energy_regen: 2.5,
can_hold: true,
blocked_attacks: (
melee: true,
projectiles: false,
beams: false,
ground_shockwaves: false,
air_shockwaves: false,
explosions: false,
),
)

View File

@ -10,7 +10,7 @@ ComboMelee2(
),
range: 4.0,
angle: 15.0,
damage_effect: Some(BuffsVulnerable(0.5, Parried)),
damage_effect: Some(BuffsVulnerable(0.6, Parried)),
),
buildup_duration: 0.4,
swing_duration: 0.1,
@ -21,7 +21,7 @@ ComboMelee2(
],
energy_cost_per_strike: 5,
meta: (
// The ability will parry melee attacks in the buildup portion
capabilities: ("BUILDUP_PARRIES"),
// The ability will parry melee attacks in the buildup & swing portion
capabilities: ("PARRIES"),
),
)

View File

@ -21,7 +21,7 @@ ComboMelee2(
],
energy_cost_per_strike: 5,
meta: (
// The ability will parry melee attacks in the buildup portion
capabilities: ("BUILDUP_PARRIES"),
// The ability will parry melee attacks in the buildup & swing portion
capabilities: ("PARRIES"),
),
)

View File

@ -40,7 +40,7 @@ ComboMelee2(
energy_cost_per_strike: 2.5,
auto_progress: true,
meta: (
// The ability will parry melee attacks in the buildup portion
capabilities: ("BUILDUP_PARRIES"),
// The ability will parry melee attacks in the buildup & swing portion
capabilities: ("PARRIES"),
),
)

View File

@ -1,18 +1,19 @@
BasicBlock(
buildup_duration: 0.25,
recover_duration: 0.15,
buildup_duration: 0.4,
recover_duration: 0.2,
max_angle: 45.0,
block_strength: 0.75,
parry_window: (
buildup: true,
recover: true,
recover: false,
),
energy_cost: 0,
energy_cost: 2.5,
energy_regen: 17.5,
can_hold: false,
blocked_attacks: (
melee: true,
melee: false,
projectiles: true,
beams: false,
beams: true,
ground_shockwaves: false,
air_shockwaves: false,
explosions: false,

View File

@ -11,13 +11,13 @@ ComboMelee2(
range: 6.0,
angle: 45.0,
),
buildup_duration: 0.1,
buildup_duration: 0.05,
swing_duration: 0.1,
hit_timing: 0.6,
recover_duration: 0.4,
recover_duration: 0.3,
movement: (
buildup: None,
swing: None,
swing: Some(Reverse(2)),
recover: Some(Reverse(1.5)),
),
ori_modifier: 0.6,
@ -26,5 +26,6 @@ ComboMelee2(
energy_cost_per_strike: 0,
meta: (
init_event: Some(EnterStance(Sword(Defensive))),
capabilities: ("BLOCKS"),
),
)

View File

@ -37,7 +37,7 @@ ComboMelee2(
],
energy_cost_per_strike: 0,
meta: (
// Blocks melee attacks at 50% strength
capabilities: ("BUILDUP_BLOCKS"),
// Blocks melee attacks at 50% strength during buildup_duration & swing_duration
capabilities: ("BLOCKS"),
),
)

View File

@ -22,7 +22,7 @@ ComboMelee2(
],
energy_cost_per_strike: 5,
meta: (
// The ability will parry melee attacks in the buildup portion
capabilities: ("BUILDUP_PARRIES"),
// The ability will parry melee attacks in the buildup & swing portion
capabilities: ("PARRIES"),
),
)

View File

@ -3,7 +3,7 @@ ComboMelee2(
(
melee_constructor: (
kind: Slash(
damage: 15,
damage: 20,
poise: 5,
knockback: 0,
energy_regen: 0,
@ -21,7 +21,7 @@ ComboMelee2(
],
energy_cost_per_strike: 5,
meta: (
// The ability will parry melee attacks in the buildup portion
capabilities: ("BUILDUP_PARRIES"),
// The ability will parry melee attacks in the buildup & swing portion
capabilities: ("PARRIES"),
),
)

View File

@ -0,0 +1,24 @@
BasicBlock(
buildup_duration: 0.4,
recover_duration: 0.15,
max_angle: 60.0,
block_strength: 0.75,
parry_window: (
buildup: true,
recover: false,
),
energy_cost: 2.5,
energy_regen: 17.5,
can_hold: true,
blocked_attacks: (
melee: true,
projectiles: false,
beams: false,
ground_shockwaves: false,
air_shockwaves: false,
explosions: false,
),
meta: (
requirements: (stance: Some(Sword(Defensive))),
),
)

View File

@ -1,6 +1,6 @@
RiposteMelee(
energy_cost: 5,
buildup_duration: 0.3,
buildup_duration: 0.4,
swing_duration: 0.1,
recover_duration: 0.2,
melee_constructor: (

View File

@ -26,7 +26,7 @@ ComboMelee2(
],
energy_cost_per_strike: 5,
meta: (
// The ability will parry melee attacks in the buildup portion
capabilities: ("BUILDUP_PARRIES"),
// The ability will parry melee attacks in the buildup & swing portion
capabilities: ("PARRIES"),
),
)

View File

@ -9,7 +9,7 @@ ChargedMelee(
energy_regen: 0,
),
scaled: Some(Stab(
damage: 10,
damage: 12,
poise: 5,
knockback: 0,
energy_regen: 15,

View File

@ -148,6 +148,7 @@ impl Attack {
emit(ServerEvent::ParryHook {
defender: target.entity,
attacker: attacker.map(|a| a.entity),
source,
});
1.0
} else if let Some(block_strength) = char_state.block_strength(source) {

View File

@ -42,6 +42,7 @@ pub type AuxiliaryKey = (Option<ToolKind>, Option<ToolKind>);
// considerations.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ActiveAbilities {
pub guard: GuardAbility,
pub primary: PrimaryAbility,
pub secondary: SecondaryAbility,
pub movement: MovementAbility,
@ -55,6 +56,7 @@ impl Component for ActiveAbilities {
impl Default for ActiveAbilities {
fn default() -> Self {
Self {
guard: GuardAbility::Tool,
primary: PrimaryAbility::Tool,
secondary: SecondaryAbility::Tool,
movement: MovementAbility::Species,
@ -123,6 +125,7 @@ impl ActiveAbilities {
skill_set: Option<&SkillSet>,
) -> Ability {
match input {
AbilityInput::Guard => self.guard.into(),
AbilityInput::Primary => self.primary.into(),
AbilityInput::Secondary => self.secondary.into(),
AbilityInput::Movement => self.movement.into(),
@ -165,6 +168,22 @@ impl ActiveAbilities {
};
match ability {
Ability::ToolGuard => ability_set(EquipSlot::ActiveMainhand)
.and_then(|abilities| {
abilities
.guard(Some(skill_set), contexts)
.map(|a| a.ability.clone())
})
.map(|ability| (scale_ability(ability, EquipSlot::ActiveMainhand), true))
.or_else(|| {
ability_set(EquipSlot::ActiveOffhand)
.and_then(|abilities| {
abilities
.secondary(Some(skill_set), contexts)
.map(|a| a.ability.clone())
})
.map(|ability| (scale_ability(ability, EquipSlot::ActiveOffhand), false))
}),
Ability::ToolPrimary => ability_set(EquipSlot::ActiveMainhand)
.and_then(|abilities| {
abilities
@ -250,6 +269,7 @@ impl ActiveAbilities {
}
pub enum AbilityInput {
Guard,
Primary,
Secondary,
Movement,
@ -258,6 +278,7 @@ pub enum AbilityInput {
#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
pub enum Ability {
ToolGuard,
ToolPrimary,
ToolSecondary,
SpeciesMovement,
@ -293,6 +314,31 @@ impl Ability {
};
match self {
Ability::ToolGuard => ability_set(EquipSlot::ActiveMainhand)
.and_then(|abilities| {
abilities
.guard(skillset, contexts)
.map(|a| a.id.as_str())
.or_else(|| {
abilities
.guard
.as_ref()
.and_then(|g| contextual_id(Some(g)))
})
})
.or_else(|| {
ability_set(EquipSlot::ActiveOffhand).and_then(|abilities| {
abilities
.guard(skillset, contexts)
.map(|a| a.id.as_str())
.or_else(|| {
abilities
.guard
.as_ref()
.and_then(|g| contextual_id(Some(g)))
})
})
}),
Ability::ToolPrimary => ability_set(EquipSlot::ActiveMainhand).and_then(|abilities| {
abilities
.primary(skillset, contexts)
@ -335,6 +381,20 @@ impl Ability {
}
}
}
#[derive(Copy, Clone, Serialize, Deserialize, Debug)]
pub enum GuardAbility {
Tool,
Empty,
}
impl From<GuardAbility> for Ability {
fn from(guard: GuardAbility) -> Self {
match guard {
GuardAbility::Tool => Ability::ToolGuard,
GuardAbility::Empty => Ability::Empty,
}
}
}
#[derive(Copy, Clone, Serialize, Deserialize, Debug)]
pub enum PrimaryAbility {
@ -550,6 +610,7 @@ pub enum CharacterAbility {
block_strength: f32,
parry_window: basic_block::ParryWindow,
energy_cost: f32,
energy_regen: f32,
can_hold: bool,
blocked_attacks: AttackFilters,
#[serde(default)]
@ -967,30 +1028,6 @@ impl CharacterAbility {
}
}
pub fn default_block() -> CharacterAbility {
CharacterAbility::BasicBlock {
buildup_duration: 0.25,
recover_duration: 0.2,
max_angle: 60.0,
block_strength: 0.5,
parry_window: basic_block::ParryWindow {
buildup: true,
recover: false,
},
energy_cost: 2.5,
can_hold: true,
blocked_attacks: AttackFilters {
melee: true,
projectiles: false,
ground_shockwaves: false,
air_shockwaves: false,
beams: false,
explosions: false,
},
meta: Default::default(),
}
}
#[must_use]
pub fn adjusted_by_stats(mut self, stats: Stats) -> Self {
use CharacterAbility::*;
@ -1090,6 +1127,7 @@ impl CharacterAbility {
block_strength: _,
parry_window: _,
ref mut energy_cost,
energy_regen: _,
can_hold: _,
blocked_attacks: _,
meta: _,
@ -2247,6 +2285,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
block_strength,
parry_window,
energy_cost,
energy_regen,
can_hold,
blocked_attacks,
meta: _,
@ -2258,6 +2297,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
block_strength: *block_strength,
parry_window: *parry_window,
energy_cost: *energy_cost,
energy_regen: *energy_regen,
can_hold: *can_hold,
blocked_attacks: *blocked_attacks,
ability_info,
@ -2910,13 +2950,13 @@ bitflags::bitflags! {
// Allows blocking to interrupt the ability at any point
const BLOCK_INTERRUPT = 0b00000010;
// When the ability is in the buildup section, it counts as a block with 50% DR
const BUILDUP_BLOCKS = 0b00000100;
const BLOCKS = 0b00000100;
// When in the ability, an entity only receives half as much poise damage
const POISE_RESISTANT = 0b00001000;
// WHen in the ability, an entity only receives half as much knockback
const KNOCKBACK_RESISTANT = 0b00010000;
// The ability will parry melee attacks in the buildup portion
const BUILDUP_PARRIES = 0b00100000;
const PARRIES = 0b00100000;
}
}

View File

@ -289,8 +289,11 @@ impl CharacterState {
.map(|a| a.ability_meta)
.map(|m| m.capabilities)
{
(capabilities.contains(Capability::BUILDUP_BLOCKS)
&& matches!(self.stage_section(), Some(StageSection::Buildup)))
(capabilities.contains(Capability::BLOCKS)
&& matches!(
self.stage_section(),
Some(StageSection::Buildup | StageSection::Action)
))
.then_some(0.5)
} else {
None
@ -320,13 +323,20 @@ impl CharacterState {
.ability_info()
.map(|a| a.ability_meta.capabilities)
.map_or(false, |c| {
c.contains(Capability::BUILDUP_PARRIES)
&& matches!(self.stage_section(), Some(StageSection::Buildup))
c.contains(Capability::PARRIES)
&& matches!(
self.stage_section(),
Some(StageSection::Buildup | StageSection::Action)
)
});
let from_state = match self {
CharacterState::BasicBlock(c) => c.is_parry(attack_source),
CharacterState::RiposteMelee(c) => {
melee && matches!(c.stage_section, StageSection::Buildup)
melee
&& matches!(
c.stage_section,
StageSection::Buildup | StageSection::Action
)
},
_ => false,
};

View File

@ -215,11 +215,12 @@ impl From<InputKind> for Option<ability::AbilityInput> {
fn from(input: InputKind) -> Option<ability::AbilityInput> {
use ability::AbilityInput;
match input {
InputKind::Block => Some(AbilityInput::Guard),
InputKind::Primary => Some(AbilityInput::Primary),
InputKind::Secondary => Some(AbilityInput::Secondary),
InputKind::Roll => Some(AbilityInput::Movement),
InputKind::Ability(index) => Some(AbilityInput::Auxiliary(index)),
InputKind::Jump | InputKind::Fly | InputKind::Block => None,
InputKind::Jump | InputKind::Fly => None,
}
}
}

View File

@ -294,6 +294,7 @@ impl Tool {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AbilitySet<T> {
pub guard: Option<AbilityKind<T>>,
pub primary: AbilityKind<T>,
pub secondary: AbilityKind<T>,
pub abilities: Vec<AbilityKind<T>>,
@ -422,6 +423,7 @@ impl AbilitySet<AbilityItem> {
impl<T> AbilitySet<T> {
pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> AbilitySet<U> {
AbilitySet {
guard: self.guard.map(|g| g.map(&mut f)),
primary: self.primary.map(&mut f),
secondary: self.secondary.map(&mut f),
abilities: self.abilities.into_iter().map(|x| x.map(&mut f)).collect(),
@ -430,12 +432,19 @@ impl<T> AbilitySet<T> {
pub fn map_ref<U, F: FnMut(&T) -> U>(&self, mut f: F) -> AbilitySet<U> {
AbilitySet {
guard: self.guard.as_ref().map(|g| g.map_ref(&mut f)),
primary: self.primary.map_ref(&mut f),
secondary: self.secondary.map_ref(&mut f),
abilities: self.abilities.iter().map(|x| x.map_ref(&mut f)).collect(),
}
}
pub fn guard(&self, skillset: Option<&SkillSet>, contexts: &[AbilityContext]) -> Option<&T> {
self.guard
.as_ref()
.and_then(|g| g.ability(skillset, contexts))
}
pub fn primary(&self, skillset: Option<&SkillSet>, contexts: &[AbilityContext]) -> Option<&T> {
self.primary.ability(skillset, contexts)
}
@ -463,6 +472,7 @@ impl<T> AbilitySet<T> {
impl Default for AbilitySet<AbilityItem> {
fn default() -> Self {
AbilitySet {
guard: None,
primary: AbilityKind::Simple(None, AbilityItem {
id: String::new(),
ability: CharacterAbility::default(),

View File

@ -1,5 +1,6 @@
use crate::{
character::CharacterId,
combat::AttackSource,
comp::{
self,
agent::Sound,
@ -261,6 +262,7 @@ pub enum ServerEvent {
ParryHook {
defender: EcsEntity,
attacker: Option<EcsEntity>,
source: AttackSource,
},
RequestSiteInfo {
entity: EcsEntity,

View File

@ -33,6 +33,8 @@ pub struct StaticData {
pub ability_info: AbilityInfo,
/// Energy consumed to initiate the block
pub energy_cost: f32,
/// Energy recovered upon successful parry
pub energy_regen: f32,
/// Whether block can be held
pub can_hold: bool,
/// What kinds of attacks the block applies to

View File

@ -14,8 +14,8 @@ use crate::{
},
quadruped_low, quadruped_medium, quadruped_small, ship,
skills::{Skill, SwimSkill, SKILL_MODIFIERS},
theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind,
InventoryAction, Melee, StateUpdate,
theropod, Body, CharacterState, Density, InputAttr, InputKind, InventoryAction, Melee,
StateUpdate,
},
consts::{FRIC_GROUND, GRAVITY, MAX_PICKUP_RANGE},
event::{LocalEvent, ServerEvent},
@ -1247,15 +1247,16 @@ pub fn handle_input(
input: InputKind,
) {
match input {
InputKind::Primary | InputKind::Secondary | InputKind::Ability(_) | InputKind::Roll => {
InputKind::Primary
| InputKind::Secondary
| InputKind::Ability(_)
| InputKind::Block
| InputKind::Roll => {
handle_ability(data, update, output_events, input);
},
InputKind::Jump => {
handle_jump(data, output_events, update, 1.0);
},
InputKind::Block => {
handle_block_input(data, update);
},
InputKind::Fly => {},
}
}
@ -1271,30 +1272,6 @@ pub fn attempt_input(
}
}
/// Checks that player can block, then attempts to block
pub fn handle_block_input(data: &JoinData<'_>, update: &mut StateUpdate) -> bool {
let can_block = |equip_slot| matches!(unwrap_tool_data(data, equip_slot), Some((kind, _)) if kind.can_block());
let hands = get_hands(data);
if input_is_pressed(data, InputKind::Block)
&& (can_block(EquipSlot::ActiveMainhand)
|| (hands.0.is_none() && can_block(EquipSlot::ActiveOffhand)))
{
let ability = CharacterAbility::default_block();
if ability.requirements_paid(data, update) {
update.character = CharacterState::from((
&ability,
AbilityInfo::from_input(data, false, InputKind::Block, Default::default()),
data,
));
true
} else {
false
}
} else {
false
}
}
/// Returns whether an interrupt occurred
pub fn handle_interrupts(
data: &JoinData,
@ -1314,8 +1291,8 @@ pub fn handle_interrupts(
});
if can_dodge && input_is_pressed(data, InputKind::Roll) {
handle_ability(data, update, output_events, InputKind::Roll)
} else if can_block {
handle_block_input(data, update)
} else if can_block && input_is_pressed(data, InputKind::Block) {
handle_ability(data, update, output_events, InputKind::Block)
} else {
false
}

View File

@ -14,7 +14,7 @@ use crate::{
use authc::Uuid;
use common::{
combat,
combat::DamageContributor,
combat::{AttackSource, DamageContributor},
comp::{
self, aura, buff,
chat::{KillSource, KillType},
@ -1361,7 +1361,12 @@ pub fn handle_combo_change(server: &Server, entity: EcsEntity, change: i32) {
}
}
pub fn handle_parry_hook(server: &Server, defender: EcsEntity, attacker: Option<EcsEntity>) {
pub fn handle_parry_hook(
server: &Server,
defender: EcsEntity,
attacker: Option<EcsEntity>,
source: AttackSource,
) {
let ecs = &server.state.ecs();
let server_eventbus = ecs.read_resource::<EventBus<ServerEvent>>();
// Reset character state of defender
@ -1379,7 +1384,7 @@ pub fn handle_parry_hook(server: &Server, defender: EcsEntity, attacker: Option<
// Refund half the energy of entering the block for a successful parry
server_eventbus.emit_now(ServerEvent::EnergyChange {
entity: defender,
change: c.static_data.energy_cost / 2.0,
change: c.static_data.energy_regen,
});
true
},
@ -1391,7 +1396,7 @@ pub fn handle_parry_hook(server: &Server, defender: EcsEntity, attacker: Option<
}
};
if let Some(attacker) = attacker {
if let Some(attacker) = attacker && matches!(source, AttackSource::Melee){
// When attacker is parried, add the parried debuff for 2 seconds, which slows
// them
let data = buff::BuffData::new(1.0, Some(Secs(2.0)), None);

View File

@ -237,9 +237,11 @@ impl Server {
ServerEvent::ComboChange { entity, change } => {
handle_combo_change(self, entity, change)
},
ServerEvent::ParryHook { defender, attacker } => {
handle_parry_hook(self, defender, attacker)
},
ServerEvent::ParryHook {
defender,
attacker,
source,
} => handle_parry_hook(self, defender, attacker, source),
ServerEvent::RequestSiteInfo { entity, id } => handle_site_info(self, entity, id),
ServerEvent::MineBlock { entity, pos, tool } => {
handle_mine_block(self, entity, pos, tool)

View File

@ -51,7 +51,11 @@ impl Animation for BlockAnimation {
next.second.orientation = Quaternion::rotation_z(0.0);
match ability_id {
None => {
None
| Some("common.abilities.sword.basic_guard")
| Some("common.abilities.axe.basic_guard")
| Some("common.abilities.hammer.basic_guard")
| Some("common.abilities.sword.defensive_guard") => {
let speed = Vec2::<f32>::from(velocity).magnitude();
let (movement1base, move2, movement3) = match stage_section {