mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'nerasw/defensive_stance_tweaks' into 'master'
Nerasw/defensive stance tweaks See merge request veloren/veloren!4020
This commit is contained in:
commit
05e925d180
16
CHANGELOG.md
16
CHANGELOG.md
@ -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
|
||||
|
@ -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: [
|
||||
|
21
assets/common/abilities/axe/basic_guard.ron
Normal file
21
assets/common/abilities/axe/basic_guard.ron
Normal 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,
|
||||
),
|
||||
)
|
21
assets/common/abilities/hammer/basic_guard.ron
Normal file
21
assets/common/abilities/hammer/basic_guard.ron
Normal 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,
|
||||
),
|
||||
)
|
@ -8,6 +8,7 @@ BasicBlock(
|
||||
recover: false,
|
||||
),
|
||||
energy_cost: 0.0,
|
||||
energy_regen: 0.0,
|
||||
can_hold: true,
|
||||
blocked_attacks: (
|
||||
melee: true,
|
||||
|
21
assets/common/abilities/sword/basic_guard.ron
Normal file
21
assets/common/abilities/sword/basic_guard.ron
Normal 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,
|
||||
),
|
||||
)
|
@ -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"),
|
||||
),
|
||||
)
|
@ -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"),
|
||||
),
|
||||
)
|
@ -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"),
|
||||
),
|
||||
)
|
@ -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,
|
||||
|
@ -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"),
|
||||
),
|
||||
)
|
||||
|
@ -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"),
|
||||
),
|
||||
)
|
@ -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"),
|
||||
),
|
||||
)
|
@ -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"),
|
||||
),
|
||||
)
|
24
assets/common/abilities/sword/defensive_guard.ron
Normal file
24
assets/common/abilities/sword/defensive_guard.ron
Normal 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))),
|
||||
),
|
||||
)
|
@ -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: (
|
||||
|
@ -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"),
|
||||
),
|
||||
)
|
@ -9,7 +9,7 @@ ChargedMelee(
|
||||
energy_regen: 0,
|
||||
),
|
||||
scaled: Some(Stab(
|
||||
damage: 10,
|
||||
damage: 12,
|
||||
poise: 5,
|
||||
knockback: 0,
|
||||
energy_regen: 15,
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user