mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'danielkenji83/block-based-on-poise' into 'master'
Block based on poise See merge request veloren/veloren!4283
This commit is contained in:
commit
94f6c3350c
@ -90,6 +90,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Balance changes; Smoother entity progression consisting of larger variety in (effective) health pools, dps, entity flee_health alterations, and minor weight distribution changes for entity spawns.
|
||||
- Made power of weapon tiers scale non-linearly.
|
||||
- Sword Changes; Pommel Strike has been nerfed -> increased energy cost, increased _durations, and decreased poise damage. Heavy Sweep has been nerfed -> decreased poise damage and stun vulnerability damage. Pillar Thrust has been altered -> decreased maximum base damage with an increase in stun vulnerability damage.
|
||||
- Weapons block are based on poise
|
||||
- Wooden Shield recipe
|
||||
|
||||
### Removed
|
||||
- Medium and large potions from all loot tables
|
||||
|
@ -351,8 +351,9 @@
|
||||
abilities: [],
|
||||
),
|
||||
Tool(Shield): (
|
||||
primary: Simple(None, "common.abilities.shield.tempbasic"),
|
||||
secondary: Simple(None, "common.abilities.shield.block"),
|
||||
guard: Some(Simple(None, "common.abilities.shield.basic_guard")),
|
||||
primary: Simple(None, "common.abilities.shield.singlestrike"),
|
||||
secondary: Simple(None, "common.abilities.shield.power_guard"),
|
||||
abilities: [],
|
||||
),
|
||||
Custom("Stone Golem"): (
|
||||
|
@ -2,7 +2,7 @@ BasicBlock(
|
||||
buildup_duration: 0.25,
|
||||
recover_duration: 0.2,
|
||||
max_angle: 60.0,
|
||||
block_strength: 0.5,
|
||||
block_strength: 5.0,
|
||||
parry_window: (
|
||||
buildup: true,
|
||||
recover: false,
|
||||
|
@ -2,7 +2,7 @@ BasicBlock(
|
||||
buildup_duration: 0.25,
|
||||
recover_duration: 0.2,
|
||||
max_angle: 60.0,
|
||||
block_strength: 0.5,
|
||||
block_strength: 5.0,
|
||||
parry_window: (
|
||||
buildup: true,
|
||||
recover: false,
|
||||
|
@ -2,7 +2,7 @@ BasicBlock(
|
||||
buildup_duration: 0.25,
|
||||
recover_duration: 0.25,
|
||||
max_angle: 90.0,
|
||||
block_strength: 0.5,
|
||||
block_strength: 5.0,
|
||||
parry_window: (
|
||||
buildup: false,
|
||||
recover: false,
|
||||
|
@ -3,6 +3,7 @@ RiposteMelee(
|
||||
buildup_duration: 0.7,
|
||||
swing_duration: 0.3,
|
||||
recover_duration: 0.2,
|
||||
block_strength: 5.0,
|
||||
melee_constructor: (
|
||||
kind: Slash(
|
||||
damage: 30,
|
||||
|
@ -1,14 +1,14 @@
|
||||
BasicBlock(
|
||||
buildup_duration: 0.1,
|
||||
recover_duration: 0.1,
|
||||
buildup_duration: 0.45,
|
||||
recover_duration: 0.2,
|
||||
max_angle: 90.0,
|
||||
block_strength: 0.8,
|
||||
block_strength: 10.0,
|
||||
parry_window: (
|
||||
buildup: true,
|
||||
recover: false,
|
||||
),
|
||||
energy_cost: 0.0,
|
||||
energy_regen: 0.0,
|
||||
energy_cost: 5.0,
|
||||
energy_regen: 2.5,
|
||||
can_hold: true,
|
||||
blocked_attacks: (
|
||||
melee: true,
|
21
assets/common/abilities/shield/power_guard.ron
Normal file
21
assets/common/abilities/shield/power_guard.ron
Normal file
@ -0,0 +1,21 @@
|
||||
BasicBlock(
|
||||
buildup_duration: 0.5,
|
||||
recover_duration: 0.25,
|
||||
max_angle: 90.0,
|
||||
block_strength: 15.0,
|
||||
parry_window: (
|
||||
buildup: true,
|
||||
recover: false,
|
||||
),
|
||||
energy_cost: 15.0,
|
||||
energy_regen: 2.5,
|
||||
can_hold: true,
|
||||
blocked_attacks: (
|
||||
melee: true,
|
||||
projectiles: true,
|
||||
beams: true,
|
||||
ground_shockwaves: false,
|
||||
air_shockwaves: true,
|
||||
explosions: true,
|
||||
),
|
||||
)
|
32
assets/common/abilities/shield/singlestrike.ron
Normal file
32
assets/common/abilities/shield/singlestrike.ron
Normal file
@ -0,0 +1,32 @@
|
||||
ComboMelee2(
|
||||
strikes: [
|
||||
(
|
||||
melee_constructor: (
|
||||
kind: Bash(
|
||||
damage: 6,
|
||||
poise: 5,
|
||||
knockback: 0,
|
||||
energy_regen: 5,
|
||||
),
|
||||
range: 1.5,
|
||||
angle: 45.0,
|
||||
damage_effect: Some(BuffsVulnerable(0.5, Parried)),
|
||||
),
|
||||
buildup_duration: 0.15,
|
||||
swing_duration: 0.2,
|
||||
hit_timing: 0.1,
|
||||
recover_duration: 0.15,
|
||||
ori_modifier: 0.1,
|
||||
movement: (
|
||||
buildup: Some(Forward(0.1)),
|
||||
swing: None,
|
||||
recover: None,
|
||||
),
|
||||
),
|
||||
],
|
||||
energy_cost_per_strike: 0,
|
||||
meta: (
|
||||
// The ability will parry all blockable attacks in the buildup portion
|
||||
capabilities: ("PARRIES"),
|
||||
),
|
||||
)
|
@ -1,18 +0,0 @@
|
||||
BasicMelee(
|
||||
energy_cost: 0,
|
||||
buildup_duration: 0.1,
|
||||
swing_duration: 0.1,
|
||||
hit_timing: 0.0,
|
||||
recover_duration: 0.3,
|
||||
melee_constructor: (
|
||||
kind: Bash(
|
||||
damage: 4.0,
|
||||
poise: 0.0,
|
||||
knockback: 0.0,
|
||||
energy_regen: 0.0,
|
||||
),
|
||||
range: 3.0,
|
||||
angle: 90.0,
|
||||
),
|
||||
ori_modifier: 1.0,
|
||||
)
|
@ -2,7 +2,7 @@ BasicBlock(
|
||||
buildup_duration: 0.25,
|
||||
recover_duration: 0.2,
|
||||
max_angle: 60.0,
|
||||
block_strength: 0.5,
|
||||
block_strength: 5.0,
|
||||
parry_window: (
|
||||
buildup: true,
|
||||
recover: false,
|
||||
|
@ -22,6 +22,6 @@ ComboMelee2(
|
||||
energy_cost_per_strike: 5,
|
||||
meta: (
|
||||
// The ability will parry melee attacks in the buildup & swing portion
|
||||
capabilities: ("PARRIES"),
|
||||
capabilities: ("PARRIES_MELEE"),
|
||||
),
|
||||
)
|
@ -22,6 +22,6 @@ ComboMelee2(
|
||||
energy_cost_per_strike: 5,
|
||||
meta: (
|
||||
// The ability will parry melee attacks in the buildup & swing portion
|
||||
capabilities: ("PARRIES"),
|
||||
capabilities: ("PARRIES_MELEE"),
|
||||
),
|
||||
)
|
@ -41,6 +41,6 @@ ComboMelee2(
|
||||
auto_progress: true,
|
||||
meta: (
|
||||
// The ability will parry melee attacks in the buildup & swing portion
|
||||
capabilities: ("PARRIES"),
|
||||
capabilities: ("PARRIES_MELEE"),
|
||||
),
|
||||
)
|
@ -2,7 +2,7 @@ BasicBlock(
|
||||
buildup_duration: 0.4,
|
||||
recover_duration: 0.2,
|
||||
max_angle: 45.0,
|
||||
block_strength: 0.75,
|
||||
block_strength: 7.5,
|
||||
parry_window: (
|
||||
buildup: true,
|
||||
recover: false,
|
||||
|
@ -23,6 +23,6 @@ ComboMelee2(
|
||||
energy_cost_per_strike: 5,
|
||||
meta: (
|
||||
// The ability will parry melee attacks in the buildup & swing portion
|
||||
capabilities: ("PARRIES"),
|
||||
capabilities: ("PARRIES_MELEE"),
|
||||
),
|
||||
)
|
@ -22,6 +22,6 @@ ComboMelee2(
|
||||
energy_cost_per_strike: 5,
|
||||
meta: (
|
||||
// The ability will parry melee attacks in the buildup & swing portion
|
||||
capabilities: ("PARRIES"),
|
||||
capabilities: ("PARRIES_MELEE"),
|
||||
),
|
||||
)
|
@ -2,7 +2,7 @@ BasicBlock(
|
||||
buildup_duration: 0.4,
|
||||
recover_duration: 0.15,
|
||||
max_angle: 60.0,
|
||||
block_strength: 0.75,
|
||||
block_strength: 7.5,
|
||||
parry_window: (
|
||||
buildup: true,
|
||||
recover: false,
|
||||
|
@ -3,6 +3,7 @@ RiposteMelee(
|
||||
buildup_duration: 0.4,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 0.2,
|
||||
block_strength: 5.0,
|
||||
melee_constructor: (
|
||||
kind: Slash(
|
||||
damage: 18,
|
||||
|
@ -27,6 +27,6 @@ ComboMelee2(
|
||||
energy_cost_per_strike: 5,
|
||||
meta: (
|
||||
// The ability will parry melee attacks in the buildup & swing portion
|
||||
capabilities: ("PARRIES"),
|
||||
capabilities: ("PARRIES_MELEE"),
|
||||
),
|
||||
)
|
@ -708,6 +708,9 @@
|
||||
Simple(
|
||||
"common.items.weapons.shield.shield_1",
|
||||
): "weapon-shield-wood-0",
|
||||
Simple(
|
||||
"common.items.weapons.shield.starter_shield",
|
||||
): "weapon-shield-starter",
|
||||
Simple(
|
||||
"common.items.weapons.dagger.basic_0",
|
||||
): "weapon-dagger-dagger_basic-0",
|
||||
|
20
assets/common/items/weapons/shield/starter_shield.ron
Normal file
20
assets/common/items/weapons/shield/starter_shield.ron
Normal file
@ -0,0 +1,20 @@
|
||||
ItemDef(
|
||||
legacy_name: "Wooden Shield",
|
||||
legacy_description: "May not withstand fire",
|
||||
kind: Tool((
|
||||
kind: Shield,
|
||||
hands: One,
|
||||
stats: (
|
||||
equip_time_secs: 0.3,
|
||||
power: 0.5,
|
||||
effect_power: 1.0,
|
||||
speed: 1.0,
|
||||
range: 1.0,
|
||||
energy_efficiency: 1.0,
|
||||
buff_strength: 1.0,
|
||||
),
|
||||
)),
|
||||
quality: Common,
|
||||
tags: [],
|
||||
ability_spec: None,
|
||||
)
|
@ -5,4 +5,5 @@
|
||||
(1.0, Item("common.items.weapons.axe.starter_axe")),
|
||||
(1.0, Item("common.items.weapons.staff.starter_staff")),
|
||||
(1.0, Item("common.items.weapons.sceptre.starter_sceptre")),
|
||||
(1.0, Item("common.items.weapons.shield.starter_shield")),
|
||||
]
|
@ -2357,4 +2357,12 @@
|
||||
],
|
||||
craft_sprite: Some(Cauldron),
|
||||
),
|
||||
"shield": (
|
||||
output: ("common.items.weapons.shield.starter_shield", 1),
|
||||
inputs: [
|
||||
(Item("common.items.log.wood"), 5, false),
|
||||
(Item("common.items.crafting_ing.leather.leather_strips"), 2, false)
|
||||
],
|
||||
craft_sprite: Some(CraftingBench),
|
||||
),
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ recipes: {
|
||||
ItemDefId("common.items.weapons.staff.starter_staff"): ( inputs: [], ),
|
||||
ItemDefId("common.items.weapons.sceptre.starter_sceptre"): ( inputs: [], ),
|
||||
ItemDefId("common.items.weapons.sword_1h.starter"): ( inputs: [], ),
|
||||
ItemDefId("common.items.weapons.shield.starter_shield"): ( inputs: [], ),
|
||||
ModularWeapon(material: "common.items.mineral.ingot.bronze"): (
|
||||
inputs: [
|
||||
(Item("common.items.mineral.ingot.bronze"), 1),
|
||||
|
@ -954,7 +954,7 @@
|
||||
threshold: 0.5,
|
||||
subtitle: "subtitle-unwield_shield",
|
||||
),
|
||||
Attack(BasicMelee(Action), Shield): (
|
||||
Attack(ComboMelee2(Action), Shield): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.abilities.swing",
|
||||
],
|
||||
|
@ -1,6 +1,9 @@
|
||||
weapon-shield-wood-0 = A Tattered Targe
|
||||
.desc = Should withstand a few more hits, hopefully...
|
||||
|
||||
weapon-shield-starter = Wooden Shield
|
||||
.desc = May not withstand fire
|
||||
|
||||
weapon-dagger-dagger_basic-0 = Suspicious Paper Knife
|
||||
.desc = Opens letters quickly.
|
||||
|
||||
|
@ -1554,7 +1554,11 @@
|
||||
// Shields
|
||||
Simple("common.items.weapons.shield.shield_1"): VoxTrans(
|
||||
"voxel.weapon.shield.wood-0",
|
||||
(0.0, 0.0, 0.0), (-90.0, 90.0, 0.0), 2.4,
|
||||
(0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.0,
|
||||
),
|
||||
Simple("common.items.weapons.shield.starter_shield"): VoxTrans(
|
||||
"voxel.weapon.shield.wood-1",
|
||||
(0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.0,
|
||||
),
|
||||
// Lanterns
|
||||
Simple("common.items.lantern.black_0"): VoxTrans(
|
||||
|
@ -899,7 +899,11 @@
|
||||
),
|
||||
// Shields
|
||||
Tool("common.items.weapons.shield.shield_1"): (
|
||||
vox_spec: ("weapon.shield.wood-0", (-2.5, -5.5, -5.5)),
|
||||
vox_spec: ("weapon.shield.wood-0", (-2.5, -7.5, -5.5)),
|
||||
color: None
|
||||
),
|
||||
Tool("common.items.weapons.shield.starter_shield"): (
|
||||
vox_spec: ("weapon.shield.wood-1", (-3.5, -7.5, -5.5)),
|
||||
color: None
|
||||
),
|
||||
// Bows
|
||||
|
@ -376,6 +376,7 @@
|
||||
Simple("common.items.weapons.dagger.cultist_0"): "voxel.weapon.dagger.dagger_cult-0",
|
||||
// Shields
|
||||
Simple("common.items.weapons.shield.shield_1"): "voxel.weapon.shield.wood-0",
|
||||
Simple("common.items.weapons.shield.starter_shield"): "voxel.weapon.shield.wood-1",
|
||||
// Lanterns
|
||||
Simple("common.items.lantern.black_0"): "voxel.lantern.black-0",
|
||||
Simple("common.items.lantern.green_0"): "voxel.lantern.green-0",
|
||||
|
BIN
assets/voxygen/voxel/weapon/shield/wood-1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/weapon/shield/wood-1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -58,6 +58,9 @@ pub const MAX_HEADSHOT_PRECISION: f32 = 1.0;
|
||||
pub const MAX_TOP_HEADSHOT_PRECISION: f32 = 0.5;
|
||||
pub const MAX_BEAM_DUR_PRECISION: f32 = 0.25;
|
||||
pub const MAX_MELEE_POISE_PRECISION: f32 = 0.5;
|
||||
pub const MAX_BLOCK_POISE_COST: f32 = 50.0;
|
||||
pub const PARRY_BONUS_MULTIPLIER: f32 = 2.0;
|
||||
pub const FALLBACK_BLOCK_STRENGTH: f32 = 5.0;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct AttackerInfo<'a> {
|
||||
@ -140,15 +143,84 @@ impl Attack {
|
||||
|
||||
pub fn effects(&self) -> impl Iterator<Item = &AttackEffect> { self.effects.iter() }
|
||||
|
||||
pub fn compute_damage_reduction(
|
||||
pub fn compute_block_damage_decrement(
|
||||
attacker: Option<&AttackerInfo>,
|
||||
damage_reduction: f32,
|
||||
target: &TargetInfo,
|
||||
source: AttackSource,
|
||||
dir: Dir,
|
||||
damage: Damage,
|
||||
msm: &MaterialStatManifest,
|
||||
emitters: &mut impl EmitExt<ParryHookEvent>,
|
||||
time: Time,
|
||||
emitters: &mut (impl EmitExt<ParryHookEvent> + EmitExt<PoiseChangeEvent>),
|
||||
mut emit_outcome: impl FnMut(Outcome),
|
||||
) -> f32 {
|
||||
if damage.value > 0.0 {
|
||||
if let (Some(char_state), Some(ori), Some(inventory)) =
|
||||
(target.char_state, target.ori, target.inventory)
|
||||
{
|
||||
let is_parry = char_state.is_parry(source);
|
||||
let is_block = char_state.is_block(source);
|
||||
let damage_value = damage.value * (1.0 - damage_reduction);
|
||||
let mut block_strength = block_strength(inventory, char_state);
|
||||
|
||||
if ori.look_vec().angle_between(-dir.with_z(0.0)) < char_state.block_angle()
|
||||
&& (is_parry || is_block)
|
||||
&& block_strength > 0.0
|
||||
{
|
||||
if is_parry {
|
||||
block_strength *= PARRY_BONUS_MULTIPLIER;
|
||||
|
||||
emitters.emit(ParryHookEvent {
|
||||
defender: target.entity,
|
||||
attacker: attacker.map(|a| a.entity),
|
||||
source,
|
||||
});
|
||||
}
|
||||
|
||||
let poise_cost =
|
||||
(damage_value / block_strength).min(1.0) * MAX_BLOCK_POISE_COST;
|
||||
|
||||
let poise_change = Poise::apply_poise_reduction(
|
||||
poise_cost,
|
||||
target.inventory,
|
||||
msm,
|
||||
target.char_state,
|
||||
target.stats,
|
||||
);
|
||||
|
||||
emit_outcome(Outcome::Block {
|
||||
parry: is_parry,
|
||||
pos: target.pos,
|
||||
uid: target.uid,
|
||||
});
|
||||
emitters.emit(PoiseChangeEvent {
|
||||
entity: target.entity,
|
||||
change: PoiseChange {
|
||||
amount: -poise_change,
|
||||
impulse: *dir,
|
||||
by: attacker.map(|x| (*x).into()),
|
||||
cause: Some(damage.source),
|
||||
time,
|
||||
},
|
||||
});
|
||||
block_strength
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_damage_reduction(
|
||||
attacker: Option<&AttackerInfo>,
|
||||
target: &TargetInfo,
|
||||
damage: Damage,
|
||||
msm: &MaterialStatManifest,
|
||||
) -> f32 {
|
||||
if damage.value > 0.0 {
|
||||
let attacker_penetration = attacker
|
||||
@ -157,39 +229,7 @@ impl Attack {
|
||||
.clamp(0.0, 1.0);
|
||||
let raw_damage_reduction =
|
||||
Damage::compute_damage_reduction(Some(damage), target.inventory, target.stats, msm);
|
||||
let damage_reduction = (1.0 - attacker_penetration) * raw_damage_reduction;
|
||||
let block_reduction =
|
||||
if let (Some(char_state), Some(ori)) = (target.char_state, target.ori) {
|
||||
if ori.look_vec().angle_between(-dir.with_z(0.0)) < char_state.block_angle() {
|
||||
if char_state.is_parry(source) {
|
||||
emit_outcome(Outcome::Block {
|
||||
parry: true,
|
||||
pos: target.pos,
|
||||
uid: target.uid,
|
||||
});
|
||||
emitters.emit(ParryHookEvent {
|
||||
defender: target.entity,
|
||||
attacker: attacker.map(|a| a.entity),
|
||||
source,
|
||||
});
|
||||
1.0
|
||||
} else if let Some(block_strength) = char_state.block_strength(source) {
|
||||
emit_outcome(Outcome::Block {
|
||||
parry: false,
|
||||
pos: target.pos,
|
||||
uid: target.uid,
|
||||
});
|
||||
block_strength
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
1.0 - (1.0 - damage_reduction) * (1.0 - block_reduction)
|
||||
(1.0 - attacker_penetration) * raw_damage_reduction
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
@ -260,18 +300,26 @@ impl Attack {
|
||||
{
|
||||
let damage_instance = damage.instance + damage_instance_offset;
|
||||
is_applied = true;
|
||||
let damage_reduction = Attack::compute_damage_reduction(
|
||||
|
||||
let damage_reduction =
|
||||
Attack::compute_damage_reduction(attacker.as_ref(), target, damage.damage, msm);
|
||||
|
||||
let block_damage_decrement = Attack::compute_block_damage_decrement(
|
||||
attacker.as_ref(),
|
||||
damage_reduction,
|
||||
target,
|
||||
attack_source,
|
||||
dir,
|
||||
damage.damage,
|
||||
msm,
|
||||
time,
|
||||
emitters,
|
||||
&mut emit_outcome,
|
||||
);
|
||||
|
||||
let change = damage.damage.calculate_health_change(
|
||||
damage_reduction,
|
||||
block_damage_decrement,
|
||||
attacker.map(|x| x.into()),
|
||||
precision_mult,
|
||||
self.precision_multiplier,
|
||||
@ -1046,6 +1094,7 @@ impl Damage {
|
||||
pub fn calculate_health_change(
|
||||
self,
|
||||
damage_reduction: f32,
|
||||
block_damage_decrement: f32,
|
||||
damage_contributor: Option<DamageContributor>,
|
||||
precision_mult: Option<f32>,
|
||||
precision_power: f32,
|
||||
@ -1065,6 +1114,8 @@ impl Damage {
|
||||
damage += precise_damage;
|
||||
// Armor
|
||||
damage *= 1.0 - damage_reduction;
|
||||
// Block
|
||||
damage = f32::max(damage - block_damage_decrement, 0.0);
|
||||
|
||||
HealthChange {
|
||||
amount: -damage,
|
||||
@ -1496,3 +1547,55 @@ pub fn precision_mult_from_flank(attack_dir: Vec3<f32>, target_ori: Option<&Ori>
|
||||
Some(_) | None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn block_strength(inventory: &Inventory, char_state: &CharacterState) -> f32 {
|
||||
match char_state {
|
||||
CharacterState::BasicBlock(data) => data.static_data.block_strength,
|
||||
CharacterState::RiposteMelee(data) => data.static_data.block_strength,
|
||||
_ => char_state
|
||||
.ability_info()
|
||||
.map(|ability| (ability.ability_meta.capabilities, ability.hand))
|
||||
.map(|(capabilities, hand)| {
|
||||
(
|
||||
if capabilities.contains(Capability::PARRIES)
|
||||
|| capabilities.contains(Capability::PARRIES_MELEE)
|
||||
|| capabilities.contains(Capability::BLOCKS)
|
||||
{
|
||||
FALLBACK_BLOCK_STRENGTH
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
hand.and_then(|hand| inventory.equipped(hand.to_equip_slot()))
|
||||
.map_or(1.0, |item| match &*item.kind() {
|
||||
ItemKind::Tool(tool) => {
|
||||
tool.stats(item.stats_durability_multiplier()).power
|
||||
},
|
||||
_ => 1.0,
|
||||
}),
|
||||
)
|
||||
})
|
||||
.map_or(0.0, |(capability_strength, tool_block_strength)| {
|
||||
capability_strength * tool_block_strength
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_equip_slot_by_block_priority(inventory: Option<&Inventory>) -> EquipSlot {
|
||||
inventory
|
||||
.map(get_weapon_kinds)
|
||||
.map_or(
|
||||
EquipSlot::ActiveMainhand,
|
||||
|weapon_kinds| match weapon_kinds {
|
||||
(Some(mainhand), Some(offhand)) => {
|
||||
if mainhand.block_priority() >= offhand.block_priority() {
|
||||
EquipSlot::ActiveMainhand
|
||||
} else {
|
||||
EquipSlot::ActiveOffhand
|
||||
}
|
||||
},
|
||||
(Some(_), None) => EquipSlot::ActiveMainhand,
|
||||
(None, Some(_)) => EquipSlot::ActiveOffhand,
|
||||
(None, None) => EquipSlot::ActiveMainhand,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -195,34 +195,22 @@ impl ActiveAbilities {
|
||||
};
|
||||
|
||||
match ability {
|
||||
Ability::ToolGuard => ability_set(EquipSlot::ActiveMainhand)
|
||||
.and_then(|abilities| {
|
||||
abilities
|
||||
.guard(Some(skill_set), context)
|
||||
.map(|(a, i)| (a.ability.clone(), i))
|
||||
})
|
||||
.map(|(ability, i)| {
|
||||
(
|
||||
scale_ability(ability, EquipSlot::ActiveMainhand),
|
||||
true,
|
||||
spec_ability(i),
|
||||
)
|
||||
})
|
||||
.or_else(|| {
|
||||
ability_set(EquipSlot::ActiveOffhand)
|
||||
.and_then(|abilities| {
|
||||
abilities
|
||||
.guard(Some(skill_set), context)
|
||||
.map(|(a, i)| (a.ability.clone(), i))
|
||||
})
|
||||
.map(|(ability, i)| {
|
||||
(
|
||||
scale_ability(ability, EquipSlot::ActiveOffhand),
|
||||
false,
|
||||
spec_ability(i),
|
||||
)
|
||||
})
|
||||
}),
|
||||
Ability::ToolGuard => {
|
||||
let equip_slot = combat::get_equip_slot_by_block_priority(inv);
|
||||
ability_set(equip_slot)
|
||||
.and_then(|abilities| {
|
||||
abilities
|
||||
.guard(Some(skill_set), context)
|
||||
.map(|(a, i)| (a.ability.clone(), i))
|
||||
})
|
||||
.map(|(ability, i)| {
|
||||
(
|
||||
scale_ability(ability, equip_slot),
|
||||
matches!(equip_slot, EquipSlot::ActiveOffhand),
|
||||
spec_ability(i),
|
||||
)
|
||||
})
|
||||
},
|
||||
Ability::ToolPrimary => ability_set(EquipSlot::ActiveMainhand)
|
||||
.and_then(|abilities| {
|
||||
abilities
|
||||
@ -396,7 +384,7 @@ impl Ability {
|
||||
};
|
||||
|
||||
match self {
|
||||
Ability::ToolGuard => ability_set(EquipSlot::ActiveMainhand)
|
||||
Ability::ToolGuard => ability_set(combat::get_equip_slot_by_block_priority(inv))
|
||||
.and_then(|abilities| {
|
||||
abilities
|
||||
.guard(skillset, context)
|
||||
@ -407,19 +395,6 @@ impl Ability {
|
||||
.as_ref()
|
||||
.and_then(|g| contextual_id(Some(g)))
|
||||
})
|
||||
})
|
||||
.or_else(|| {
|
||||
ability_set(EquipSlot::ActiveOffhand).and_then(|abilities| {
|
||||
abilities
|
||||
.guard(skillset, context)
|
||||
.map(|a| a.0.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
|
||||
@ -524,12 +499,8 @@ impl SpecifiedAbility {
|
||||
ability_set(EquipSlot::ActiveMainhand)
|
||||
.map(|abilities| ability_id(self, &abilities.secondary))
|
||||
}),
|
||||
Ability::ToolGuard => ability_set(EquipSlot::ActiveMainhand)
|
||||
.and_then(|abilities| abilities.guard.as_ref().map(|a| ability_id(self, a)))
|
||||
.or_else(|| {
|
||||
ability_set(EquipSlot::ActiveOffhand)
|
||||
.and_then(|abilities| abilities.guard.as_ref().map(|a| ability_id(self, a)))
|
||||
}),
|
||||
Ability::ToolGuard => ability_set(combat::get_equip_slot_by_block_priority(inv))
|
||||
.and_then(|abilities| abilities.guard.as_ref().map(|a| ability_id(self, a))),
|
||||
Ability::SpeciesMovement => None, // TODO: Make not None
|
||||
Ability::MainWeaponAux(index) => ability_set(EquipSlot::ActiveMainhand)
|
||||
.and_then(|abilities| abilities.abilities.get(index).map(|a| ability_id(self, a))),
|
||||
@ -1006,6 +977,7 @@ pub enum CharacterAbility {
|
||||
buildup_duration: f32,
|
||||
swing_duration: f32,
|
||||
recover_duration: f32,
|
||||
block_strength: f32,
|
||||
melee_constructor: MeleeConstructor,
|
||||
#[serde(default)]
|
||||
meta: AbilityMeta,
|
||||
@ -1279,8 +1251,7 @@ impl CharacterAbility {
|
||||
ref mut recover_duration,
|
||||
// Do we want angle to be adjusted by range?
|
||||
max_angle: _,
|
||||
// Block strength explicitly not modified by power, that will be a separate stat
|
||||
block_strength: _,
|
||||
ref mut block_strength,
|
||||
parry_window: _,
|
||||
ref mut energy_cost,
|
||||
energy_regen: _,
|
||||
@ -1291,6 +1262,7 @@ impl CharacterAbility {
|
||||
*buildup_duration /= stats.speed;
|
||||
*recover_duration /= stats.speed;
|
||||
*energy_cost /= stats.energy_efficiency;
|
||||
*block_strength *= stats.power;
|
||||
},
|
||||
Roll {
|
||||
ref mut energy_cost,
|
||||
@ -1660,6 +1632,7 @@ impl CharacterAbility {
|
||||
ref mut buildup_duration,
|
||||
ref mut swing_duration,
|
||||
ref mut recover_duration,
|
||||
ref mut block_strength,
|
||||
ref mut melee_constructor,
|
||||
meta: _,
|
||||
} => {
|
||||
@ -1667,6 +1640,7 @@ impl CharacterAbility {
|
||||
*swing_duration /= stats.speed;
|
||||
*recover_duration /= stats.speed;
|
||||
*energy_cost /= stats.energy_efficiency;
|
||||
*block_strength *= stats.power;
|
||||
*melee_constructor = melee_constructor.adjusted_by_stats(stats);
|
||||
},
|
||||
RapidMelee {
|
||||
@ -2914,6 +2888,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
|
||||
buildup_duration,
|
||||
swing_duration,
|
||||
recover_duration,
|
||||
block_strength,
|
||||
melee_constructor,
|
||||
meta: _,
|
||||
} => CharacterState::RiposteMelee(riposte_melee::Data {
|
||||
@ -2921,6 +2896,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
|
||||
buildup_duration: Duration::from_secs_f32(*buildup_duration),
|
||||
swing_duration: Duration::from_secs_f32(*swing_duration),
|
||||
recover_duration: Duration::from_secs_f32(*recover_duration),
|
||||
block_strength: *block_strength,
|
||||
melee_constructor: *melee_constructor,
|
||||
ability_info,
|
||||
},
|
||||
@ -3003,18 +2979,18 @@ bitflags::bitflags! {
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
// If more are ever needed, first check if any not used anymore, as some were only used in intermediary stages so may be free
|
||||
pub struct Capability: u8 {
|
||||
// There used to be a capability here, to keep ordering the same below this is now a placeholder
|
||||
const PLACEHOLDER = 0b00000001;
|
||||
// The ability will parry all blockable attacks in the buildup portion
|
||||
const PARRIES = 0b00000001;
|
||||
// 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
|
||||
// The ability will block melee attacks in the buildup portion
|
||||
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 PARRIES = 0b00100000;
|
||||
const PARRIES_MELEE = 0b00100000;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -308,53 +308,36 @@ impl CharacterState {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn block_strength(&self, attack_source: AttackSource) -> Option<f32> {
|
||||
let from_capability = if let AttackSource::Melee = attack_source {
|
||||
if let Some(capabilities) = self
|
||||
.ability_info()
|
||||
.map(|a| a.ability_meta)
|
||||
.map(|m| m.capabilities)
|
||||
{
|
||||
(capabilities.contains(Capability::BLOCKS)
|
||||
&& matches!(
|
||||
self.stage_section(),
|
||||
Some(StageSection::Buildup | StageSection::Action)
|
||||
))
|
||||
.then_some(0.5)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let from_state = match self {
|
||||
CharacterState::BasicBlock(c) => c
|
||||
.static_data
|
||||
.blocked_attacks
|
||||
.applies(attack_source)
|
||||
.then_some(c.static_data.block_strength),
|
||||
_ => None,
|
||||
};
|
||||
match (from_capability, from_state) {
|
||||
(Some(a), Some(b)) => Some(a.max(b)),
|
||||
(Some(a), None) | (None, Some(a)) => Some(a),
|
||||
(None, None) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_parry(&self, attack_source: AttackSource) -> bool {
|
||||
let melee = matches!(attack_source, AttackSource::Melee);
|
||||
let from_capability = melee
|
||||
let from_capability_melee = melee
|
||||
&& self
|
||||
.ability_info()
|
||||
.map(|a| a.ability_meta.capabilities)
|
||||
.map_or(false, |c| {
|
||||
c.contains(Capability::PARRIES)
|
||||
c.contains(Capability::PARRIES_MELEE)
|
||||
&& matches!(
|
||||
self.stage_section(),
|
||||
Some(StageSection::Buildup | StageSection::Action)
|
||||
)
|
||||
});
|
||||
let from_capability = matches!(
|
||||
attack_source,
|
||||
AttackSource::Melee
|
||||
| AttackSource::Projectile
|
||||
| AttackSource::Beam
|
||||
| AttackSource::AirShockwave
|
||||
| AttackSource::Explosion
|
||||
) && self
|
||||
.ability_info()
|
||||
.map(|a| a.ability_meta.capabilities)
|
||||
.map_or(false, |c| {
|
||||
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) => {
|
||||
@ -366,7 +349,30 @@ impl CharacterState {
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
from_capability || from_state
|
||||
from_capability_melee || from_capability || from_state
|
||||
}
|
||||
|
||||
pub fn is_block(&self, attack_source: AttackSource) -> bool {
|
||||
match self {
|
||||
CharacterState::BasicBlock(data) => {
|
||||
data.static_data.blocked_attacks.applies(attack_source)
|
||||
&& matches!(
|
||||
self.stage_section(),
|
||||
Some(StageSection::Buildup | StageSection::Action)
|
||||
)
|
||||
},
|
||||
_ => self
|
||||
.ability_info()
|
||||
.map(|ability| ability.ability_meta.capabilities)
|
||||
.map_or(false, |capabilities| {
|
||||
capabilities.contains(Capability::BLOCKS)
|
||||
&& matches!(
|
||||
self.stage_section(),
|
||||
Some(StageSection::Buildup | StageSection::Action)
|
||||
)
|
||||
&& matches!(attack_source, AttackSource::Melee)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// In radians
|
||||
|
@ -97,6 +97,28 @@ impl ToolKind {
|
||||
| ToolKind::Dagger
|
||||
)
|
||||
}
|
||||
|
||||
pub fn block_priority(&self) -> i32 {
|
||||
match self {
|
||||
ToolKind::Debug => 0,
|
||||
ToolKind::Blowgun => 1,
|
||||
ToolKind::Bow => 2,
|
||||
ToolKind::Staff => 3,
|
||||
ToolKind::Sceptre => 4,
|
||||
ToolKind::Empty => 5,
|
||||
ToolKind::Natural => 6,
|
||||
ToolKind::Instrument => 7,
|
||||
ToolKind::Farming => 8,
|
||||
ToolKind::Shovel => 9,
|
||||
ToolKind::Pick => 10,
|
||||
ToolKind::Dagger => 11,
|
||||
ToolKind::Spear => 12,
|
||||
ToolKind::Hammer => 13,
|
||||
ToolKind::Axe => 14,
|
||||
ToolKind::Sword => 15,
|
||||
ToolKind::Shield => 16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
|
@ -25,7 +25,8 @@ pub struct StaticData {
|
||||
pub recover_duration: Duration,
|
||||
/// Max angle (45.0 will give you a 90.0 angle window)
|
||||
pub max_angle: f32,
|
||||
/// What percentage incoming damage is reduced by
|
||||
/// Base value that incoming damage is reduced by and converted to poise
|
||||
/// damage
|
||||
pub block_strength: f32,
|
||||
/// What durations are considered a parry
|
||||
pub parry_window: ParryWindow,
|
||||
|
@ -18,6 +18,9 @@ pub struct StaticData {
|
||||
pub swing_duration: Duration,
|
||||
/// How long the state has until exiting
|
||||
pub recover_duration: Duration,
|
||||
/// Base value that incoming damage is reduced by and converted to poise
|
||||
/// damage
|
||||
pub block_strength: f32,
|
||||
/// Used to construct the Melee attack
|
||||
pub melee_constructor: MeleeConstructor,
|
||||
/// What key is used to press ability
|
||||
|
@ -1411,10 +1411,7 @@ pub fn get_hands(data: &JoinData<'_>) -> (Option<Hands>, Option<Hands>) {
|
||||
|
||||
pub fn get_tool_stats(data: &JoinData<'_>, ai: AbilityInfo) -> tool::Stats {
|
||||
ai.hand
|
||||
.map(|hand| match hand {
|
||||
HandInfo::TwoHanded | HandInfo::MainHand => EquipSlot::ActiveMainhand,
|
||||
HandInfo::OffHand => EquipSlot::ActiveOffhand,
|
||||
})
|
||||
.map(|hand| hand.to_equip_slot())
|
||||
.and_then(|slot| data.inventory.and_then(|inv| inv.equipped(slot)))
|
||||
.and_then(|item| {
|
||||
if let ItemKind::Tool(tool) = &*item.kind() {
|
||||
@ -1616,6 +1613,13 @@ impl HandInfo {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_equip_slot(&self) -> EquipSlot {
|
||||
match self {
|
||||
HandInfo::TwoHanded | HandInfo::MainHand => EquipSlot::ActiveMainhand,
|
||||
HandInfo::OffHand => EquipSlot::ActiveOffhand,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn leave_stance(data: &JoinData<'_>, output_events: &mut OutputEvents) {
|
||||
|
@ -819,6 +819,7 @@ impl ServerEvent for LandOnGroundEvent {
|
||||
);
|
||||
let change = damage.calculate_health_change(
|
||||
damage_reduction,
|
||||
0.0,
|
||||
None,
|
||||
None,
|
||||
0.0,
|
||||
@ -1401,6 +1402,7 @@ pub fn emit_effect_events(
|
||||
common::effect::Effect::Damage(damage) => {
|
||||
let change = damage.calculate_health_change(
|
||||
combat::Damage::compute_damage_reduction(Some(damage), inventory, stats, msm),
|
||||
0.0,
|
||||
damage_contributor,
|
||||
None,
|
||||
0.0,
|
||||
@ -1677,7 +1679,7 @@ impl ServerEvent for ParryHookEvent {
|
||||
entity: ev.defender,
|
||||
change: c.static_data.energy_regen,
|
||||
});
|
||||
true
|
||||
false
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
|
@ -202,6 +202,7 @@ impl StateExt for State {
|
||||
stats.get(entity),
|
||||
&msm,
|
||||
),
|
||||
0.0,
|
||||
damage_contributor,
|
||||
None,
|
||||
0.0,
|
||||
|
@ -55,7 +55,9 @@ impl Animation for BlockAnimation {
|
||||
| Some("common.abilities.sword.basic_guard")
|
||||
| Some("common.abilities.axe.basic_guard")
|
||||
| Some("common.abilities.hammer.basic_guard")
|
||||
| Some("common.abilities.sword.defensive_guard") => {
|
||||
| Some("common.abilities.sword.defensive_guard")
|
||||
| Some("common.abilities.shield.basic_guard")
|
||||
| Some("common.abilities.shield.power_guard") => {
|
||||
let speed = Vec2::<f32>::from(velocity).magnitude();
|
||||
|
||||
let (movement1base, move2, movement3) = match stage_section {
|
||||
@ -215,53 +217,95 @@ impl Animation for BlockAnimation {
|
||||
* Quaternion::rotation_y(0.6)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
},
|
||||
Some(ToolKind::Shield) => {
|
||||
next.control.position = Vec3::new(2.0, 9.0, 8.0);
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(0.25) * Quaternion::rotation_z(-1.5);
|
||||
|
||||
next.hand_l.position = Vec3::new(0.0, -2.0, 0.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(PI / 2.0);
|
||||
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(PI / 2.0)
|
||||
* Quaternion::rotation_y(PI / 2.0);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
((Some(Hands::One), Some(Hands::One)), _, Some(ToolKind::Shield)) => {
|
||||
next.control_r.position = Vec3::new(-1.5, 8.0, 4.0 + move1 * 3.0);
|
||||
next.control_r.orientation = Quaternion::rotation_x(0.25)
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(1.5);
|
||||
next.hand_r.position = Vec3::new(0.0, -2.0, 0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(PI / 2.0);
|
||||
|
||||
next.control_l.position = Vec3::new(-9.0, -5.0, 0.0);
|
||||
next.control_l.orientation =
|
||||
Quaternion::rotation_x(-1.75) * Quaternion::rotation_y(-0.3);
|
||||
next.hand_l.position = Vec3::new(0.0, -0.5, 0.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(PI / 2.0);
|
||||
},
|
||||
((Some(Hands::One), _), Some(ToolKind::Shield), _) => {
|
||||
next.control_l.position = Vec3::new(1.5, 8.0, 4.0 + move1 * 3.0);
|
||||
next.control_l.orientation = Quaternion::rotation_x(0.25)
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(-1.5);
|
||||
next.hand_l.position = Vec3::new(0.0, -2.0, 0.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(PI / 2.0);
|
||||
|
||||
next.control_r.position = Vec3::new(9.0, -5.0, 0.0);
|
||||
next.control_r.orientation =
|
||||
Quaternion::rotation_x(-1.75) * Quaternion::rotation_y(0.3);
|
||||
next.hand_r.position = Vec3::new(0.0, -0.5, 0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(PI / 2.0);
|
||||
},
|
||||
((Some(Hands::One), _), _, _) => {
|
||||
match hands {
|
||||
(Some(Hands::One), _) => {
|
||||
next.control_l.position =
|
||||
Vec3::new(-7.0, 8.0 + move1 * 3.0, 2.0 + move1 * 3.0);
|
||||
next.control_l.orientation = Quaternion::rotation_x(-0.3)
|
||||
* Quaternion::rotation_y(move1 * 1.0);
|
||||
next.hand_l.position = Vec3::new(0.0, -0.5, 0.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(PI / 2.0)
|
||||
},
|
||||
(_, _) => {},
|
||||
};
|
||||
match hands {
|
||||
(None | Some(Hands::One), Some(Hands::One)) => {
|
||||
next.control_r.position =
|
||||
Vec3::new(7.0, 8.0 + move1 * 3.0, 2.0 + move1 * 3.0);
|
||||
next.control_r.orientation = Quaternion::rotation_x(-0.3)
|
||||
* Quaternion::rotation_y(move1 * -1.0);
|
||||
next.hand_r.position = Vec3::new(0.0, -0.5, 0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(PI / 2.0)
|
||||
},
|
||||
(_, _) => {},
|
||||
};
|
||||
match hands {
|
||||
(None, None) | (None, Some(Hands::One)) => {
|
||||
next.hand_l.position = Vec3::new(-4.5, 8.0, 5.0);
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(1.9) * Quaternion::rotation_y(-0.5)
|
||||
},
|
||||
(_, _) => {},
|
||||
};
|
||||
match hands {
|
||||
(None, None) | (Some(Hands::One), None) => {
|
||||
next.hand_r.position = Vec3::new(4.5, 8.0, 5.0);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(1.9) * Quaternion::rotation_y(0.5)
|
||||
},
|
||||
(_, _) => {},
|
||||
};
|
||||
|
||||
if let (None, Some(Hands::Two)) = hands {
|
||||
next.second = next.main;
|
||||
}
|
||||
},
|
||||
((_, _), _, _) => {},
|
||||
};
|
||||
match hands {
|
||||
(Some(Hands::One), _) => {
|
||||
next.control_l.position =
|
||||
Vec3::new(-7.0, 8.0 + move1 * 3.0, 2.0 + move1 * 3.0);
|
||||
next.control_l.orientation =
|
||||
Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(move1 * 1.0);
|
||||
next.hand_l.position = Vec3::new(0.0, -0.5, 0.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(PI / 2.0)
|
||||
},
|
||||
(_, _) => {},
|
||||
};
|
||||
match hands {
|
||||
(None | Some(Hands::One), Some(Hands::One)) => {
|
||||
next.control_r.position =
|
||||
Vec3::new(7.0, 8.0 + move1 * 3.0, 2.0 + move1 * 3.0);
|
||||
next.control_r.orientation =
|
||||
Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(move1 * -1.0);
|
||||
next.hand_r.position = Vec3::new(0.0, -0.5, 0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(PI / 2.0)
|
||||
},
|
||||
(_, _) => {},
|
||||
};
|
||||
match hands {
|
||||
(None, None) | (None, Some(Hands::One)) => {
|
||||
next.hand_l.position = Vec3::new(-4.5, 8.0, 5.0);
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(1.9) * Quaternion::rotation_y(-0.5)
|
||||
},
|
||||
(_, _) => {},
|
||||
};
|
||||
match hands {
|
||||
(None, None) | (Some(Hands::One), None) => {
|
||||
next.hand_r.position = Vec3::new(4.5, 8.0, 5.0);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(1.9) * Quaternion::rotation_y(0.5)
|
||||
},
|
||||
(_, _) => {},
|
||||
};
|
||||
|
||||
if let (None, Some(Hands::Two)) = hands {
|
||||
next.second = next.main;
|
||||
}
|
||||
},
|
||||
Some("common.abilities.sword.defensive_deflect") => {
|
||||
let (move1, move2, move3) = match stage_section {
|
||||
|
@ -2,7 +2,7 @@ use super::{
|
||||
super::{vek::*, Animation},
|
||||
CharacterSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::states::utils::{AbilityInfo, StageSection};
|
||||
use common::states::utils::{AbilityInfo, HandInfo, StageSection};
|
||||
use core::f32::consts::{PI, TAU};
|
||||
|
||||
pub struct ComboAnimation;
|
||||
@ -1133,6 +1133,81 @@ impl Animation for ComboAnimation {
|
||||
next.control.orientation.rotate_x(move2 * -1.2);
|
||||
next.control.position += Vec3::new(move2 * 8.0, 0.0, 0.0);
|
||||
},
|
||||
Some("common.abilities.shield.singlestrike") => {
|
||||
let move1 = if strike == current_strike {
|
||||
match stage_section {
|
||||
Some(StageSection::Buildup) => anim_time,
|
||||
Some(StageSection::Action) => 1.0,
|
||||
Some(StageSection::Recover) => 1.0,
|
||||
_ => 0.0,
|
||||
}
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let move1 = move1 * multi_strike_pullback;
|
||||
|
||||
if let Some(ability_info) = _ability_info {
|
||||
match ability_info.hand {
|
||||
Some(HandInfo::TwoHanded) => {
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
next.chest.orientation = Quaternion::rotation_z(move1 * -0.3);
|
||||
next.torso.orientation = Quaternion::rotation_z(move1 * -1.0);
|
||||
next.head.orientation = Quaternion::rotation_z(move1 * 0.75);
|
||||
next.head.position = Vec3::new(0.5, s_a.head.0 + 0.5, s_a.head.1);
|
||||
|
||||
next.control.position = Vec3::new(move1 * -10.0, 6.0, move1 * 6.0);
|
||||
next.control.orientation = Quaternion::rotation_z(-0.25);
|
||||
|
||||
next.hand_l.position = Vec3::new(0.0, -2.0, 0.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(PI / 2.0);
|
||||
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(PI / 2.0)
|
||||
* Quaternion::rotation_y(PI / 2.0);
|
||||
},
|
||||
Some(HandInfo::MainHand) => {
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
next.chest.orientation = Quaternion::rotation_z(move1 * -0.3);
|
||||
next.torso.orientation = Quaternion::rotation_z(move1 * -1.2);
|
||||
next.head.orientation = Quaternion::rotation_z(move1 * 0.75);
|
||||
next.head.position = Vec3::new(0.5, s_a.head.0 + 0.5, s_a.head.1);
|
||||
|
||||
next.control_l.position =
|
||||
Vec3::new(move1 * -12.0, 4.0, move1 * 6.0);
|
||||
next.control_l.orientation = Quaternion::rotation_x(move1 * 0.0)
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(-0.25);
|
||||
next.hand_l.position = Vec3::new(0.0, -1.5, 0.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(PI / 2.0);
|
||||
|
||||
next.control_r.position = Vec3::new(9.0, -1.0, 0.0);
|
||||
next.control_r.orientation = Quaternion::rotation_x(-1.75);
|
||||
next.hand_r.position = Vec3::new(0.0, 0.5, 0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(PI / 2.0);
|
||||
},
|
||||
Some(HandInfo::OffHand) => {
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
next.chest.orientation = Quaternion::rotation_z(move1 * 0.3);
|
||||
next.torso.orientation = Quaternion::rotation_z(move1 * 1.2);
|
||||
next.head.orientation = Quaternion::rotation_z(move1 * -0.75);
|
||||
next.head.position = Vec3::new(-0.5, s_a.head.0 + -0.5, s_a.head.1);
|
||||
|
||||
next.control_r.position = Vec3::new(move1 * 12.0, 4.0, move1 * 6.0);
|
||||
next.control_r.orientation = Quaternion::rotation_x(move1 * 0.0)
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.25);
|
||||
next.hand_r.position = Vec3::new(0.0, -1.5, 0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(PI / 2.0);
|
||||
|
||||
next.control_l.position = Vec3::new(-9.0, -1.0, 0.0);
|
||||
next.control_l.orientation = Quaternion::rotation_x(-1.75);
|
||||
next.hand_l.position = Vec3::new(0.0, 0.5, 0.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(PI / 2.0);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
@ -360,6 +360,11 @@ impl CharacterSkeleton {
|
||||
self.main.orientation =
|
||||
Quaternion::rotation_y(-0.5) * Quaternion::rotation_z(PI / 2.0);
|
||||
},
|
||||
Some(ToolKind::Shield) => {
|
||||
self.main.position = Vec3::new(-2.0, -3.0 - self.back_carry_offset, 1.0);
|
||||
self.main.orientation =
|
||||
Quaternion::rotation_y(-0.75) * Quaternion::rotation_z(PI / 2.0);
|
||||
},
|
||||
_ => {
|
||||
self.main.position = Vec3::new(-7.0, -5.0 - self.back_carry_offset, 15.0);
|
||||
self.main.orientation =
|
||||
@ -382,7 +387,7 @@ impl CharacterSkeleton {
|
||||
Quaternion::rotation_y(2.5) * Quaternion::rotation_z(PI / 2.0);
|
||||
},
|
||||
Some(ToolKind::Shield) => {
|
||||
self.main.position = Vec3::new(-0.0, -4.0 - self.back_carry_offset, 3.0);
|
||||
self.main.position = Vec3::new(-2.0, -4.0 - self.back_carry_offset, 3.0);
|
||||
self.main.orientation =
|
||||
Quaternion::rotation_y(0.25 * PI) * Quaternion::rotation_z(-1.5 * PI);
|
||||
},
|
||||
@ -403,7 +408,7 @@ impl CharacterSkeleton {
|
||||
Quaternion::rotation_y(-2.5) * Quaternion::rotation_z(-PI / 2.0);
|
||||
},
|
||||
Some(ToolKind::Shield) => {
|
||||
self.second.position = Vec3::new(0.0, -4.0 - self.back_carry_offset, 3.0);
|
||||
self.second.position = Vec3::new(1.5, -4.0 - self.back_carry_offset, 3.0);
|
||||
self.second.orientation =
|
||||
Quaternion::rotation_y(-0.25 * PI) * Quaternion::rotation_z(1.5 * PI);
|
||||
},
|
||||
|
@ -167,6 +167,18 @@ impl Animation for RollAnimation {
|
||||
|
||||
next.control.position = Vec3::new(-11.0, 1.8, 4.0);
|
||||
},
|
||||
Some(ToolKind::Shield) => {
|
||||
next.hand_l.position = Vec3::new(0.0, -1.5, 0.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(PI / 2.0);
|
||||
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_y(2.0);
|
||||
|
||||
next.control.position = Vec3::new(0.0, 7.0, 4.0);
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_y(-0.5) * Quaternion::rotation_z(-1.25);
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
(_, _) => {},
|
||||
|
@ -497,6 +497,18 @@ impl Animation for WieldAnimation {
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(ToolKind::Shield) => {
|
||||
next.hand_l.position = Vec3::new(0.0, -2.0, 0.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(PI / 2.0);
|
||||
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_y(2.0);
|
||||
|
||||
next.control.position = Vec3::new(0.0, 7.0, 4.0);
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_y(-0.5) * Quaternion::rotation_z(-1.25);
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
((_, _), _, _) => {},
|
||||
|
@ -145,7 +145,12 @@ impl CombatEventMapper {
|
||||
previous_state: &PreviousEntityState,
|
||||
inventory: &Inventory,
|
||||
) -> SfxEvent {
|
||||
if let Some(item) = inventory.equipped(EquipSlot::ActiveMainhand) {
|
||||
let equip_slot = character_state
|
||||
.ability_info()
|
||||
.and_then(|ability| ability.hand)
|
||||
.map_or(EquipSlot::ActiveMainhand, |hand| hand.to_equip_slot());
|
||||
|
||||
if let Some(item) = inventory.equipped(equip_slot) {
|
||||
if let ItemKind::Tool(data) = &*item.kind() {
|
||||
if character_state.is_attack() {
|
||||
return SfxEvent::Attack(
|
||||
|
@ -1131,6 +1131,7 @@ impl<'a> Widget for Crafting<'a> {
|
||||
RecipeKind::Component(ToolKind::Bow) => self.imgs.icon_log,
|
||||
RecipeKind::Component(ToolKind::Staff) => self.imgs.icon_log,
|
||||
RecipeKind::Component(ToolKind::Sceptre) => self.imgs.icon_log,
|
||||
RecipeKind::Component(ToolKind::Shield) => self.imgs.icon_ingot,
|
||||
_ => self.imgs.not_found,
|
||||
};
|
||||
|
||||
|
@ -563,8 +563,8 @@ pub fn ability_image(imgs: &img_ids::Imgs, ability_id: &str) -> image::Id {
|
||||
"common.abilities.sceptre.healingaura" => imgs.skill_sceptre_heal,
|
||||
"common.abilities.sceptre.wardingaura" => imgs.skill_sceptre_aura,
|
||||
// Shield
|
||||
"common.abilities.shield.tempbasic" => imgs.onehshield_m1,
|
||||
"common.abilities.shield.block" => imgs.onehshield_m2,
|
||||
"common.abilities.shield.singlestrike" => imgs.onehshield_m1,
|
||||
"common.abilities.shield.power_guard" => imgs.onehshield_m1,
|
||||
// Dagger
|
||||
"common.abilities.dagger.tempbasic" => imgs.onehdagger_m1,
|
||||
// Pickaxe
|
||||
|
Loading…
Reference in New Issue
Block a user