Merge branch 'sam/multiple-contexts' into 'master'

Abilities can have multiple contexts, added sword abilities which use dual sword context

See merge request veloren/veloren!3898
This commit is contained in:
Samuel Keiffer 2023-05-08 02:36:13 +00:00
commit 07bb4f72aa
36 changed files with 646 additions and 171 deletions

View File

@ -5,92 +5,101 @@
Tool(Sword): (
primary: Contextualized(
pseudo_id: "veloren.core.pseudo_abilities.sword.double_slash",
abilities: {
None: (None, "common.abilities.sword.basic_double_slash"),
Stance(Sword(Heavy)): (None, "common.abilities.sword.heavy_double_slash"),
Stance(Sword(Agile)): (None, "common.abilities.sword.agile_double_slash"),
Stance(Sword(Defensive)): (None, "common.abilities.sword.defensive_double_slash"),
Stance(Sword(Crippling)): (None, "common.abilities.sword.crippling_double_slash"),
Stance(Sword(Cleaving)): (None, "common.abilities.sword.cleaving_double_slash"),
},
abilities: [
([Stance(Sword(Heavy))], (None, "common.abilities.sword.heavy_double_slash")),
([Stance(Sword(Agile))], (None, "common.abilities.sword.agile_double_slash")),
([Stance(Sword(Defensive))], (None, "common.abilities.sword.defensive_double_slash")),
([Stance(Sword(Crippling))], (None, "common.abilities.sword.crippling_double_slash")),
([Stance(Sword(Cleaving))], (None, "common.abilities.sword.cleaving_double_slash")),
([], (None, "common.abilities.sword.basic_double_slash")),
],
),
secondary: Contextualized(
pseudo_id: "veloren.core.pseudo_abilities.sword.secondary_ability",
abilities: {
None: (None, "common.abilities.sword.basic_thrust"),
Stance(Sword(Heavy)): (None, "common.abilities.sword.heavy_slam"),
Stance(Sword(Agile)): (None, "common.abilities.sword.agile_perforate"),
Stance(Sword(Defensive)): (None, "common.abilities.sword.defensive_vital_jab"),
Stance(Sword(Crippling)): (None, "common.abilities.sword.crippling_deep_rend"),
Stance(Sword(Cleaving)): (None, "common.abilities.sword.cleaving_spiral_slash"),
},
abilities: [
([Stance(Sword(Heavy))], (None, "common.abilities.sword.heavy_slam")),
([Stance(Sword(Agile)), DualWieldingSameKind], (None, "common.abilities.sword.agile_dual_perforate")),
([Stance(Sword(Agile))], (None, "common.abilities.sword.agile_perforate")),
([Stance(Sword(Defensive))], (None, "common.abilities.sword.defensive_vital_jab")),
([Stance(Sword(Crippling))], (None, "common.abilities.sword.crippling_deep_rend")),
([Stance(Sword(Cleaving)), DualWieldingSameKind], (None, "common.abilities.sword.cleaving_dual_spiral_slash")),
([Stance(Sword(Cleaving))], (None, "common.abilities.sword.cleaving_spiral_slash")),
([], (None, "common.abilities.sword.basic_thrust")),
],
),
abilities: [
Contextualized(
pseudo_id: "veloren.core.pseudo_abilities.sword.crescent_slash",
abilities: {
None: (Sword(CrescentSlash), "common.abilities.sword.basic_crescent_slash"),
Stance(Sword(Heavy)): (Sword(CrescentSlash), "common.abilities.sword.heavy_crescent_slash"),
Stance(Sword(Agile)): (Sword(CrescentSlash), "common.abilities.sword.agile_crescent_slash"),
Stance(Sword(Defensive)): (Sword(CrescentSlash), "common.abilities.sword.defensive_crescent_slash"),
Stance(Sword(Crippling)): (Sword(CrescentSlash), "common.abilities.sword.crippling_crescent_slash"),
Stance(Sword(Cleaving)): (Sword(CrescentSlash), "common.abilities.sword.cleaving_crescent_slash"),
},
abilities: [
([Stance(Sword(Heavy))], (Sword(CrescentSlash), "common.abilities.sword.heavy_crescent_slash")),
([Stance(Sword(Agile))], (Sword(CrescentSlash), "common.abilities.sword.agile_crescent_slash")),
([Stance(Sword(Defensive))], (Sword(CrescentSlash), "common.abilities.sword.defensive_crescent_slash")),
([Stance(Sword(Crippling))], (Sword(CrescentSlash), "common.abilities.sword.crippling_crescent_slash")),
([Stance(Sword(Cleaving))], (Sword(CrescentSlash), "common.abilities.sword.cleaving_crescent_slash")),
([], (Sword(CrescentSlash), "common.abilities.sword.basic_crescent_slash")),
],
),
Contextualized(
pseudo_id: "veloren.core.pseudo_abilities.sword.fell_strike",
abilities: {
None: (Sword(FellStrike), "common.abilities.sword.basic_fell_strike"),
Stance(Sword(Heavy)): (Sword(FellStrike), "common.abilities.sword.heavy_fell_strike"),
Stance(Sword(Agile)): (Sword(FellStrike), "common.abilities.sword.agile_fell_strike"),
Stance(Sword(Defensive)): (Sword(FellStrike), "common.abilities.sword.defensive_fell_strike"),
Stance(Sword(Crippling)): (Sword(FellStrike), "common.abilities.sword.crippling_fell_strike"),
Stance(Sword(Cleaving)): (Sword(FellStrike), "common.abilities.sword.cleaving_fell_strike"),
},
abilities: [
([Stance(Sword(Heavy))], (Sword(FellStrike), "common.abilities.sword.heavy_fell_strike")),
([Stance(Sword(Agile))], (Sword(FellStrike), "common.abilities.sword.agile_fell_strike")),
([Stance(Sword(Defensive))], (Sword(FellStrike), "common.abilities.sword.defensive_fell_strike")),
([Stance(Sword(Crippling))], (Sword(FellStrike), "common.abilities.sword.crippling_fell_strike")),
([Stance(Sword(Cleaving))], (Sword(FellStrike), "common.abilities.sword.cleaving_fell_strike")),
([], (Sword(FellStrike), "common.abilities.sword.basic_fell_strike")),
],
),
Contextualized(
pseudo_id: "veloren.core.pseudo_abilities.sword.skewer",
abilities: {
None: (Sword(Skewer), "common.abilities.sword.basic_skewer"),
Stance(Sword(Heavy)): (Sword(Skewer), "common.abilities.sword.heavy_skewer"),
Stance(Sword(Agile)): (Sword(Skewer), "common.abilities.sword.agile_skewer"),
Stance(Sword(Defensive)): (Sword(Skewer), "common.abilities.sword.defensive_skewer"),
Stance(Sword(Crippling)): (Sword(Skewer), "common.abilities.sword.crippling_skewer"),
Stance(Sword(Cleaving)): (Sword(Skewer), "common.abilities.sword.cleaving_skewer"),
},
abilities: [
([Stance(Sword(Heavy))], (Sword(Skewer), "common.abilities.sword.heavy_skewer")),
([Stance(Sword(Agile))], (Sword(Skewer), "common.abilities.sword.agile_skewer")),
([Stance(Sword(Defensive))], (Sword(Skewer), "common.abilities.sword.defensive_skewer")),
([Stance(Sword(Crippling))], (Sword(Skewer), "common.abilities.sword.crippling_skewer")),
([Stance(Sword(Cleaving))], (Sword(Skewer), "common.abilities.sword.cleaving_skewer")),
([], (Sword(Skewer), "common.abilities.sword.basic_skewer")),
],
),
Contextualized(
pseudo_id: "veloren.core.pseudo_abilities.sword.cascade",
abilities: {
None: (Sword(Cascade), "common.abilities.sword.basic_cascade"),
Stance(Sword(Heavy)): (Sword(Cascade), "common.abilities.sword.heavy_cascade"),
Stance(Sword(Agile)): (Sword(Cascade), "common.abilities.sword.agile_cascade"),
Stance(Sword(Defensive)): (Sword(Cascade), "common.abilities.sword.defensive_cascade"),
Stance(Sword(Crippling)): (Sword(Cascade), "common.abilities.sword.crippling_cascade"),
Stance(Sword(Cleaving)): (Sword(Cascade), "common.abilities.sword.cleaving_cascade"),
},
abilities: [
([Stance(Sword(Heavy))], (Sword(Cascade), "common.abilities.sword.heavy_cascade")),
([Stance(Sword(Agile))], (Sword(Cascade), "common.abilities.sword.agile_cascade")),
([Stance(Sword(Defensive))], (Sword(Cascade), "common.abilities.sword.defensive_cascade")),
([Stance(Sword(Crippling))], (Sword(Cascade), "common.abilities.sword.crippling_cascade")),
([Stance(Sword(Cleaving))], (Sword(Cascade), "common.abilities.sword.cleaving_cascade")),
([], (Sword(Cascade), "common.abilities.sword.basic_cascade")),
],
),
Contextualized(
pseudo_id: "veloren.core.pseudo_abilities.sword.cross_cut",
abilities: {
None: (Sword(CrossCut), "common.abilities.sword.basic_cross_cut"),
Stance(Sword(Heavy)): (Sword(CrossCut), "common.abilities.sword.heavy_cross_cut"),
Stance(Sword(Agile)): (Sword(CrossCut), "common.abilities.sword.agile_cross_cut"),
Stance(Sword(Defensive)): (Sword(CrossCut), "common.abilities.sword.defensive_cross_cut"),
Stance(Sword(Crippling)): (Sword(CrossCut), "common.abilities.sword.crippling_cross_cut"),
Stance(Sword(Cleaving)): (Sword(CrossCut), "common.abilities.sword.cleaving_cross_cut"),
},
abilities: [
([Stance(Sword(Heavy)), DualWieldingSameKind], (Sword(CrossCut), "common.abilities.sword.heavy_dual_cross_cut")),
([Stance(Sword(Agile)), DualWieldingSameKind], (Sword(CrossCut), "common.abilities.sword.agile_dual_cross_cut")),
([Stance(Sword(Defensive)), DualWieldingSameKind], (Sword(CrossCut), "common.abilities.sword.defensive_dual_cross_cut")),
([Stance(Sword(Crippling)), DualWieldingSameKind], (Sword(CrossCut), "common.abilities.sword.crippling_dual_cross_cut")),
([Stance(Sword(Cleaving)), DualWieldingSameKind], (Sword(CrossCut), "common.abilities.sword.cleaving_dual_cross_cut")),
([DualWieldingSameKind], (Sword(CrossCut), "common.abilities.sword.basic_dual_cross_cut")),
([Stance(Sword(Heavy))], (Sword(CrossCut), "common.abilities.sword.heavy_cross_cut")),
([Stance(Sword(Agile))], (Sword(CrossCut), "common.abilities.sword.agile_cross_cut")),
([Stance(Sword(Defensive))], (Sword(CrossCut), "common.abilities.sword.defensive_cross_cut")),
([Stance(Sword(Crippling))], (Sword(CrossCut), "common.abilities.sword.crippling_cross_cut")),
([Stance(Sword(Cleaving))], (Sword(CrossCut), "common.abilities.sword.cleaving_cross_cut")),
([], (Sword(CrossCut), "common.abilities.sword.basic_cross_cut")),
],
),
Contextualized(
pseudo_id: "veloren.core.pseudo_abilities.sword.finisher",
abilities: {
None: (Sword(Finisher), "common.abilities.sword.basic_mighty_strike"),
Stance(Sword(Heavy)): (Sword(Finisher), "common.abilities.sword.heavy_guillotine"),
Stance(Sword(Agile)): (Sword(Finisher), "common.abilities.sword.agile_hundred_cuts"),
Stance(Sword(Defensive)): (Sword(Finisher), "common.abilities.sword.defensive_counter"),
Stance(Sword(Crippling)): (Sword(Finisher), "common.abilities.sword.crippling_mutilate"),
Stance(Sword(Cleaving)): (Sword(Finisher), "common.abilities.sword.cleaving_bladestorm"),
},
abilities: [
([Stance(Sword(Heavy))], (Sword(Finisher), "common.abilities.sword.heavy_guillotine")),
([Stance(Sword(Agile))], (Sword(Finisher), "common.abilities.sword.agile_hundred_cuts")),
([Stance(Sword(Defensive))], (Sword(Finisher), "common.abilities.sword.defensive_counter")),
([Stance(Sword(Crippling))], (Sword(Finisher), "common.abilities.sword.crippling_mutilate")),
([Stance(Sword(Cleaving)), DualWieldingSameKind], (Sword(Finisher), "common.abilities.sword.cleaving_dual_bladestorm")),
([Stance(Sword(Cleaving))], (Sword(Finisher), "common.abilities.sword.cleaving_bladestorm")),
([], (Sword(Finisher), "common.abilities.sword.basic_mighty_strike")),
],
),
Simple(Sword(HeavySweep), "common.abilities.sword.heavy_sweep"),
Simple(Sword(HeavyPommelStrike), "common.abilities.sword.heavy_pommel_strike"),
@ -100,12 +109,24 @@
Simple(Sword(DefensiveDisengage), "common.abilities.sword.defensive_disengage"),
Simple(Sword(CripplingGouge), "common.abilities.sword.crippling_gouge"),
Simple(Sword(CripplingHamstring), "common.abilities.sword.crippling_hamstring"),
Simple(Sword(CleavingWhirlwindSlice), "common.abilities.sword.cleaving_whirlwind_slice"),
Contextualized(
pseudo_id: "common.abilities.sword.cleaving_whirlwind_slice",
abilities: [
([DualWieldingSameKind], (Sword(CleavingWhirlwindSlice), "common.abilities.sword.cleaving_dual_whirlwind_slice")),
([], (Sword(CleavingWhirlwindSlice), "common.abilities.sword.cleaving_whirlwind_slice")),
],
),
Simple(Sword(CleavingEarthSplitter), "common.abilities.sword.cleaving_earth_splitter"),
Simple(Sword(HeavyFortitude), "common.abilities.sword.heavy_fortitude"),
Simple(Sword(HeavyPillarThrust), "common.abilities.sword.heavy_pillar_thrust"),
Simple(Sword(AgileDancingEdge), "common.abilities.sword.agile_dancing_edge"),
Simple(Sword(AgileFlurry), "common.abilities.sword.agile_flurry"),
Contextualized(
pseudo_id: "common.abilities.sword.agile_flurry",
abilities: [
([Stance(Sword(Agile)), DualWieldingSameKind], (Sword(AgileFlurry), "common.abilities.sword.agile_dual_flurry")),
([Stance(Sword(Agile))], (Sword(AgileFlurry), "common.abilities.sword.agile_flurry")),
],
),
Simple(Sword(DefensiveStalwartSword), "common.abilities.sword.defensive_stalwart_sword"),
Simple(Sword(DefensiveDeflect), "common.abilities.sword.defensive_deflect"),
Simple(Sword(CripplingEviscerate), "common.abilities.sword.crippling_eviscerate"),

View File

@ -0,0 +1,23 @@
ComboMelee2(
strikes: [
(
melee_constructor: (
kind: Slash(
damage: 5,
poise: 5,
knockback: 0,
energy_regen: 0,
),
range: 3.5,
angle: 90.0,
simultaneous_hits: 2,
),
buildup_duration: 0.1,
swing_duration: 0.05,
hit_timing: 0.5,
recover_duration: 0.1,
ori_modifier: 0.6,
),
],
energy_cost_per_strike: 5,
)

View File

@ -0,0 +1,22 @@
RapidMelee(
buildup_duration: 0.1,
swing_duration: 0.07,
recover_duration: 0.1,
melee_constructor: (
kind: Slash(
damage: 10,
poise: 3,
knockback: 0,
energy_regen: 0,
),
range: 6.0,
angle: 10.0,
),
energy_cost: 2.5,
max_strikes: Some(6),
move_modifier: 0.35,
ori_modifier: 0.25,
meta: (
requirements: (stance: Some(Sword(Agile))),
),
)

View File

@ -0,0 +1,19 @@
RapidMelee(
buildup_duration: 0.2,
swing_duration: 0.14,
recover_duration: 0.3,
melee_constructor: (
kind: Slash(
damage: 2,
poise: 1,
knockback: 0,
energy_regen: 2,
),
range: 4.0,
angle: 5.0,
),
energy_cost: 0,
max_strikes: None,
move_modifier: 0.3,
ori_modifier: 0.2,
)

View File

@ -0,0 +1,23 @@
ComboMelee2(
strikes: [
(
melee_constructor: (
kind: Slash(
damage: 10,
poise: 5,
knockback: 0,
energy_regen: 0,
),
range: 3.5,
angle: 90.0,
simultaneous_hits: 2,
),
buildup_duration: 0.15,
swing_duration: 0.1,
hit_timing: 0.5,
recover_duration: 0.2,
ori_modifier: 0.6,
),
],
energy_cost_per_strike: 5,
)

View File

@ -0,0 +1,22 @@
RapidMelee(
buildup_duration: 0.3,
swing_duration: 0.15,
recover_duration: 0.3,
melee_constructor: (
kind: Slash(
damage: 6,
poise: 10,
knockback: 0,
energy_regen: 0,
),
range: 4.0,
angle: 360.0,
multi_target: Some(Normal),
simultaneous_hits: 2,
),
energy_cost: 3.33,
max_strikes: Some(6),
move_modifier: 0.8,
ori_modifier: 1.0,
minimum_combo: 10,
)

View File

@ -0,0 +1,24 @@
ComboMelee2(
strikes: [
(
melee_constructor: (
kind: Slash(
damage: 10,
poise: 5,
knockback: 0,
energy_regen: 0,
),
range: 3.5,
angle: 90.0,
multi_target: Some(Normal),
simultaneous_hits: 2,
),
buildup_duration: 0.2,
swing_duration: 0.15,
hit_timing: 0.5,
recover_duration: 0.25,
ori_modifier: 0.6,
),
],
energy_cost_per_strike: 5,
)

View File

@ -0,0 +1,26 @@
ChargedMelee(
energy_cost: 0,
energy_drain: 0,
melee_constructor: (
kind: Slash(
damage: 0,
poise: 0,
knockback: 0,
energy_regen: 0,
),
scaled: Some(Slash(
damage: 7,
poise: 10,
knockback: 0,
energy_regen: 10,
)),
range: 3.0,
angle: 360.0,
multi_target: Some(Normal),
simultaneous_hits: 2,
),
charge_duration: 0.5,
swing_duration: 0.2,
hit_timing: 0.2,
recover_duration: 0.3,
)

View File

@ -0,0 +1,24 @@
RapidMelee(
buildup_duration: 0.2,
swing_duration: 0.2,
recover_duration: 0.4,
melee_constructor: (
kind: Slash(
damage: 10,
poise: 5,
knockback: 0,
energy_regen: 0,
),
range: 4.0,
angle: 360.0,
multi_target: Some(Normal),
simultaneous_hits: 2,
),
energy_cost: 5,
max_strikes: Some(2),
ori_modifier: 1.0,
move_modifier: 1.0,
meta: (
init_event: Some(EnterStance(Sword(Cleaving))),
),
)

View File

@ -0,0 +1,29 @@
ComboMelee2(
strikes: [
(
melee_constructor: (
kind: Slash(
damage: 7,
poise: 5,
knockback: 0,
energy_regen: 0,
),
range: 3.5,
angle: 90.0,
damage_effect: Some(Buff((
kind: Bleeding,
dur_secs: 8.0,
strength: DamageFraction(0.25),
chance: 1.0,
))),
simultaneous_hits: 2,
),
buildup_duration: 0.15,
swing_duration: 0.1,
hit_timing: 0.5,
recover_duration: 0.2,
ori_modifier: 0.6,
),
],
energy_cost_per_strike: 5,
)

View File

@ -0,0 +1,30 @@
ComboMelee2(
strikes: [
(
melee_constructor: (
kind: Slash(
damage: 6,
poise: 5,
knockback: 0,
energy_regen: 0,
),
range: 3.5,
angle: 90.0,
damage_effect: Some(BuffsVulnerable(0.5, Parried)),
simultaneous_hits: 2,
),
buildup_duration: 0.15,
swing_duration: 0.1,
hit_timing: 0.5,
recover_duration: 0.2,
ori_modifier: 0.6,
),
],
energy_cost_per_strike: 5,
meta: (
capabilities: (
// The ability will parry melee attacks in the buildup portion
bits: 0b00100000,
),
),
)

View File

@ -0,0 +1,24 @@
ComboMelee2(
strikes: [
(
melee_constructor: (
kind: Slash(
damage: 12,
poise: 5,
knockback: 0,
energy_regen: 0,
),
range: 3.5,
angle: 90.0,
damage_effect: Some(StunnedVulnerable(0.5)),
simultaneous_hits: 2,
),
buildup_duration: 0.2,
swing_duration: 0.15,
hit_timing: 0.5,
recover_duration: 0.25,
ori_modifier: 0.6,
),
],
energy_cost_per_strike: 5,
)

View File

@ -44,12 +44,16 @@ common-abilities-sword-heavy_slam = Heavy Slam
.desc = A strong overhead slash that can be charged to be more staggering
common-abilities-sword-agile_perforate = Perforate
.desc = A rapid flurry of light attacks
common-abilities-sword-agile_dual_perforate = Perforate
.desc = A rapid flurry of light attacks with both swords
common-abilities-sword-defensive_vital_jab = Defensive Vital Jab
.desc = A quickly charged jab that does more damage against parried foes
common-abilities-sword-crippling_deep_rend = Deep Rend
.desc = A strike aimed at an already open wound, deals more damage to bleeding enemies
common-abilities-sword-cleaving_spiral_slash = Spiral Slash
.desc = Sweep your blade fully around you to hit anyone nearby
common-abilities-sword-cleaving_dual_spiral_slash = Spiral Slash
.desc = Sweep both of your blades fully around you to hit anyone nearby
veloren-core-pseudo_abilities-sword-crescent_slash = Crescent Slash
.desc =
An upwards diagonal slash
@ -130,6 +134,18 @@ common-abilities-sword-crippling_cross_cut = Crippling Cross Cut
.desc = A right and left slash that can bleed
common-abilities-sword-cleaving_cross_cut = Cleaving Cross Cut
.desc = A right and left slash which cleave through enemies
common-abilities-sword-basic_dual_cross_cut = Basic Cross Cut
.desc = A simultaneous basic right and left slash
common-abilities-sword-heavy_dual_cross_cut = Heavy Cross Cut
.desc = A simultaneous right and left slash that can each stagger
common-abilities-sword-agile_dual_cross_cut = Agile Cross Cut
.desc = A simultaneous quick right and left slash
common-abilities-sword-defensive_dual_cross_cut = Defensive Cross Cut
.desc = A simultaneous parrying right and left slash
common-abilities-sword-crippling_dual_cross_cut = Crippling Cross Cut
.desc = A simultaneous right and left slash that can bleed
common-abilities-sword-cleaving_dual_cross_cut = Cleaving Cross Cut
.desc = A simultaneous right and left slash which cleave through enemies
veloren-core-pseudo_abilities-sword-finisher = Finisher
.desc =
An ability that consumes combo and is meant to end a fight
@ -158,6 +174,10 @@ common-abilities-sword-cleaving_bladestorm = Bladestorm
.desc =
Decimate your enemies with multiple cyclic swings of your sword
Requires a moderate amount of combo to use
common-abilities-sword-cleaving_dual_bladestorm = Bladestorm
.desc =
Decimate your enemies with multiple cyclic swings of both of your swords
Requires a moderate amount of combo to use
common-abilities-sword-heavy_sweep = Heavy Sweep
.desc =
A heavy, wide, sweeping strike that deals more damage to a staggered enemy
@ -194,6 +214,10 @@ common-abilities-sword-cleaving_whirlwind_slice = Whirlwind Slice
.desc =
Strike all surrounding enemies with circular attacks
Enters cleaving stance
common-abilities-sword-cleaving_dual_whirlwind_slice = Whirlwind Slice
.desc =
Strike all surrounding enemies with circular attacks using both of your swords
Enters cleaving stance
common-abilities-sword-cleaving_earth_splitter = Earth Splitter
.desc =
Split the earth, if used while falling will have a much stronger impact
@ -214,6 +238,10 @@ common-abilities-sword-agile_flurry = Flurry
.desc =
Multiple rapid stabs
Requires agile stance
common-abilities-sword-agile_dual_flurry = Flurry
.desc =
Multiple rapid stabs with both swords
Requires agile stance
common-abilities-sword-defensive_stalwart_sword = Stalwart Sword
.desc =
Shrug off the brunt of attacks, incoming damage is reduced

View File

@ -188,7 +188,7 @@ impl Attack {
pub fn apply_attack(
&self,
attacker: Option<AttackerInfo>,
target: TargetInfo,
target: &TargetInfo,
dir: Dir,
options: AttackOptions,
// Currently strength_modifier just modifies damage,
@ -199,6 +199,7 @@ impl Attack {
mut emit: impl FnMut(ServerEvent),
mut emit_outcome: impl FnMut(Outcome),
rng: &mut rand::rngs::ThreadRng,
damage_instance_offset: u64,
) -> bool {
// TODO: Maybe move this higher and pass it as argument into this function?
let msm = &MaterialStatManifest::load().read();
@ -238,10 +239,11 @@ impl Attack {
.filter(|d| d.target.map_or(true, |t| t == target_group))
.filter(|d| !avoid_damage(d))
{
let damage_instance = damage.instance + damage_instance_offset;
is_applied = true;
let damage_reduction = Attack::compute_damage_reduction(
attacker.as_ref(),
&target,
target,
attack_source,
dir,
damage.damage,
@ -256,7 +258,7 @@ impl Attack {
self.crit_multiplier,
strength_modifier * damage_modifier,
time,
damage.instance,
damage_instance,
);
let applied_damage = -change.amount;
accumulated_damage += applied_damage;
@ -282,7 +284,7 @@ impl Attack {
cause: Some(damage.damage.source),
time,
crit: is_crit,
instance: damage.instance,
instance: damage_instance,
};
emit(ServerEvent::HealthChange {
entity: target.entity,
@ -331,7 +333,7 @@ impl Attack {
amount: health_change,
by: attacker.map(|x| x.into()),
cause: Some(damage.damage.source),
instance: damage.instance,
instance: damage_instance,
crit: is_crit,
time,
};

View File

@ -144,7 +144,7 @@ impl ActiveAbilities {
skill_set: &SkillSet,
body: Option<&Body>,
char_state: Option<&CharacterState>,
context: AbilityContext,
contexts: &[AbilityContext],
// bool is from_offhand
) -> Option<(CharacterAbility, bool)> {
let ability = self.get_ability(input, inv, Some(skill_set));
@ -168,14 +168,14 @@ impl ActiveAbilities {
Ability::ToolPrimary => ability_set(EquipSlot::ActiveMainhand)
.and_then(|abilities| {
abilities
.primary(Some(skill_set), context)
.primary(Some(skill_set), contexts)
.map(|a| a.ability.clone())
})
.map(|ability| (scale_ability(ability, EquipSlot::ActiveMainhand), false)),
Ability::ToolSecondary => ability_set(EquipSlot::ActiveOffhand)
.and_then(|abilities| {
abilities
.secondary(Some(skill_set), context)
.secondary(Some(skill_set), contexts)
.map(|a| a.ability.clone())
})
.map(|ability| (scale_ability(ability, EquipSlot::ActiveOffhand), true))
@ -183,7 +183,7 @@ impl ActiveAbilities {
ability_set(EquipSlot::ActiveMainhand)
.and_then(|abilities| {
abilities
.secondary(Some(skill_set), context)
.secondary(Some(skill_set), contexts)
.map(|a| a.ability.clone())
})
.map(|ability| (scale_ability(ability, EquipSlot::ActiveMainhand), false))
@ -194,14 +194,14 @@ impl ActiveAbilities {
Ability::MainWeaponAux(index) => ability_set(EquipSlot::ActiveMainhand)
.and_then(|abilities| {
abilities
.auxiliary(index, Some(skill_set), context)
.auxiliary(index, Some(skill_set), contexts)
.map(|a| a.ability.clone())
})
.map(|ability| (scale_ability(ability, EquipSlot::ActiveMainhand), false)),
Ability::OffWeaponAux(index) => ability_set(EquipSlot::ActiveOffhand)
.and_then(|abilities| {
abilities
.auxiliary(index, Some(skill_set), context)
.auxiliary(index, Some(skill_set), contexts)
.map(|a| a.ability.clone())
})
.map(|ability| (scale_ability(ability, EquipSlot::ActiveOffhand), true)),
@ -226,8 +226,8 @@ impl ActiveAbilities {
pseudo_id: _,
abilities,
} => abilities
.values()
.any(|(skill, _)| {
.iter()
.any(|(_contexts, (skill, _))| {
skill.map_or(true, |s| skill_set.map_or(false, |ss| ss.has_skill(s)))
})
.then_some(i),
@ -273,7 +273,7 @@ impl Ability {
self,
inv: Option<&'a Inventory>,
skillset: Option<&'a SkillSet>,
context: AbilityContext,
contexts: &[AbilityContext],
) -> Option<&'a str> {
let ability_set = |equip_slot| {
inv.and_then(|inv| inv.equipped(equip_slot))
@ -295,21 +295,21 @@ impl Ability {
match self {
Ability::ToolPrimary => ability_set(EquipSlot::ActiveMainhand).and_then(|abilities| {
abilities
.primary(skillset, context)
.primary(skillset, contexts)
.map(|a| a.id.as_str())
.or_else(|| contextual_id(Some(&abilities.primary)))
}),
Ability::ToolSecondary => ability_set(EquipSlot::ActiveOffhand)
.and_then(|abilities| {
abilities
.secondary(skillset, context)
.secondary(skillset, contexts)
.map(|a| a.id.as_str())
.or_else(|| contextual_id(Some(&abilities.secondary)))
})
.or_else(|| {
ability_set(EquipSlot::ActiveMainhand).and_then(|abilities| {
abilities
.secondary(skillset, context)
.secondary(skillset, contexts)
.map(|a| a.id.as_str())
.or_else(|| contextual_id(Some(&abilities.secondary)))
})
@ -318,7 +318,7 @@ impl Ability {
Ability::MainWeaponAux(index) => {
ability_set(EquipSlot::ActiveMainhand).and_then(|abilities| {
abilities
.auxiliary(index, skillset, context)
.auxiliary(index, skillset, contexts)
.map(|a| a.id.as_str())
.or_else(|| contextual_id(abilities.abilities.get(index)))
})
@ -326,7 +326,7 @@ impl Ability {
Ability::OffWeaponAux(index) => {
ability_set(EquipSlot::ActiveOffhand).and_then(|abilities| {
abilities
.auxiliary(index, skillset, context)
.auxiliary(index, skillset, contexts)
.map(|a| a.id.as_str())
.or_else(|| contextual_id(abilities.abilities.get(index)))
})
@ -834,6 +834,7 @@ impl Default for CharacterAbility {
angle: 15.0,
multi_target: None,
damage_effect: None,
simultaneous_hits: 1,
},
ori_modifier: 1.0,
meta: Default::default(),

View File

@ -4,7 +4,14 @@
use crate::{
assets::{self, Asset, AssetExt, AssetHandle},
comp::{
ability::Stance, item::DurabilityMultiplier, skills::Skill, CharacterAbility, SkillSet,
ability::Stance,
inventory::{
item::{DurabilityMultiplier, ItemKind},
slot::EquipSlot,
Inventory,
},
skills::Skill,
CharacterAbility, SkillSet,
},
};
use hashbrown::HashMap;
@ -297,7 +304,7 @@ pub enum AbilityKind<T> {
Simple(Option<Skill>, T),
Contextualized {
pseudo_id: String,
abilities: HashMap<AbilityContext, (Option<Skill>, T)>,
abilities: Vec<(Vec<AbilityContext>, (Option<Skill>, T))>,
},
}
@ -327,14 +334,14 @@ impl<T> AbilityKind<T> {
} => AbilityKind::<U>::Contextualized {
pseudo_id: pseudo_id.clone(),
abilities: abilities
.into_iter()
.map(|(c, (s, x))| (*c, (*s, f(x))))
.iter()
.map(|(c, (s, x))| (c.clone(), (*s, f(x))))
.collect(),
},
}
}
pub fn ability(&self, skillset: Option<&SkillSet>, context: AbilityContext) -> Option<&T> {
pub fn ability(&self, skillset: Option<&SkillSet>, contexts: &[AbilityContext]) -> Option<&T> {
let unlocked = |s: Option<Skill>, a| {
// If there is a skill requirement and the skillset does not contain the
// required skill, return None
@ -347,19 +354,15 @@ impl<T> AbilityKind<T> {
AbilityKind::Contextualized {
pseudo_id: _,
abilities,
} => {
// In the event that the ability from the current context is not unlocked with
// the required skill, try falling back to the ability from this input that does
// not require a context
abilities
.get(&context)
.and_then(|(s, a)| unlocked(*s, a))
.or_else(|| {
abilities
.get(&AbilityContext::None)
.and_then(|(s, a)| unlocked(*s, a))
})
},
} => abilities
.iter()
.filter_map(|(req_contexts, (s, a))| unlocked(*s, a).map(|a| (req_contexts, a)))
.find_map(|(req_contexts, a)| {
req_contexts
.iter()
.all(|req| contexts.contains(req))
.then_some(a)
}),
}
}
}
@ -371,16 +374,32 @@ pub enum AbilityContext {
/// `AbilityContext::Stance(Stance::None)` in the ability map config
/// files(s).
Stance(Stance),
None,
DualWieldingSameKind,
}
impl AbilityContext {
pub fn from(stance: Option<&Stance>) -> Self {
pub fn from(stance: Option<&Stance>, inv: Option<&Inventory>) -> Vec<Self> {
let mut contexts = Vec::new();
match stance {
Some(Stance::None) => AbilityContext::None,
Some(stance) => AbilityContext::Stance(*stance),
None => AbilityContext::None,
Some(Stance::None) => {},
Some(stance) => contexts.push(AbilityContext::Stance(*stance)),
None => {},
}
if let Some(inv) = inv {
let tool_kind = |slot| {
inv.equipped(slot).and_then(|i| {
if let ItemKind::Tool(tool) = &*i.kind() {
Some(tool.kind)
} else {
None
}
})
};
if tool_kind(EquipSlot::ActiveMainhand) == tool_kind(EquipSlot::ActiveOffhand) {
contexts.push(AbilityContext::DualWieldingSameKind)
}
}
contexts
}
}
@ -417,23 +436,27 @@ impl<T> AbilitySet<T> {
}
}
pub fn primary(&self, skillset: Option<&SkillSet>, context: AbilityContext) -> Option<&T> {
self.primary.ability(skillset, context)
pub fn primary(&self, skillset: Option<&SkillSet>, contexts: &[AbilityContext]) -> Option<&T> {
self.primary.ability(skillset, contexts)
}
pub fn secondary(&self, skillset: Option<&SkillSet>, context: AbilityContext) -> Option<&T> {
self.secondary.ability(skillset, context)
pub fn secondary(
&self,
skillset: Option<&SkillSet>,
contexts: &[AbilityContext],
) -> Option<&T> {
self.secondary.ability(skillset, contexts)
}
pub fn auxiliary(
&self,
index: usize,
skillset: Option<&SkillSet>,
context: AbilityContext,
contexts: &[AbilityContext],
) -> Option<&T> {
self.abilities
.get(index)
.and_then(|a| a.ability(skillset, context))
.and_then(|a| a.ability(skillset, contexts))
}
}

View File

@ -22,6 +22,7 @@ pub struct Melee {
pub hit_count: u32,
pub multi_target: Option<MultiTarget>,
pub break_block: Option<(Vec3<i32>, Option<ToolKind>)>,
pub simultaneous_hits: u32,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -49,6 +50,8 @@ impl Component for Melee {
type Storage = VecStorage<Self>;
}
fn default_simultaneous_hits() -> u32 { 1 }
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MeleeConstructor {
@ -61,6 +64,8 @@ pub struct MeleeConstructor {
pub angle: f32,
pub multi_target: Option<MultiTarget>,
pub damage_effect: Option<CombatEffect>,
#[serde(default = "default_simultaneous_hits")]
pub simultaneous_hits: u32,
}
impl MeleeConstructor {
@ -306,6 +311,7 @@ impl MeleeConstructor {
hit_count: 0,
multi_target: self.multi_target,
break_block: None,
simultaneous_hits: self.simultaneous_hits,
}
}

View File

@ -272,6 +272,7 @@ impl CharacterBehavior for Data {
// TODO: Evaluate if we want to leave this true. State will be removed at
// some point anyways and this does preserve behavior
multi_target: Some(MultiTarget::Normal),
simultaneous_hits: 1,
break_block: data
.inputs
.break_block_pos

View File

@ -276,6 +276,7 @@ fn create_test_melee(static_data: StaticData) -> Melee {
angle: static_data.melee_constructor.angle,
multi_target: None,
damage_effect: None,
simultaneous_hits: 1,
};
melee.create_melee((0.0, 0.0), tool::Stats::one())
}

View File

@ -1107,7 +1107,7 @@ fn handle_ability(
output_events: &mut OutputEvents,
input: InputKind,
) -> bool {
let context = AbilityContext::from(data.stance);
let contexts = AbilityContext::from(data.stance, data.inventory);
if let Some(ability_input) = input.into() {
if let Some((ability, from_offhand)) = data
.active_abilities
@ -1118,7 +1118,7 @@ fn handle_ability(
data.skill_set,
Some(data.body),
Some(data.character),
context,
&contexts,
)
})
.filter(|(ability, _)| ability.requirements_paid(data, update))

View File

@ -255,7 +255,7 @@ impl<'a> System<'a> for Sys {
beam_segment.properties.attack.apply_attack(
attacker_info,
target_info,
&target_info,
ori.look_dir(),
attack_options,
1.0,
@ -264,6 +264,7 @@ impl<'a> System<'a> for Sys {
|e| server_events.push(e),
|o| outcomes.push(o),
&mut rng,
0,
);
add_hit_entities.push((beam_owner, *uid_b));

View File

@ -231,21 +231,25 @@ impl<'a> System<'a> for Sys {
1.0
};
let is_applied = melee_attack.attack.apply_attack(
attacker_info,
target_info,
dir,
attack_options,
strength,
AttackSource::Melee,
*read_data.time,
|e| server_emitter.emit(e),
|o| outcomes_emitter.emit(o),
&mut rng,
);
let mut is_applied = false;
for offset in 0..melee_attack.simultaneous_hits {
is_applied = melee_attack.attack.apply_attack(
attacker_info,
&target_info,
dir,
attack_options,
strength,
AttackSource::Melee,
*read_data.time,
|e| server_emitter.emit(e),
|o| outcomes_emitter.emit(o),
&mut rng,
offset as u64,
) || is_applied;
}
if is_applied {
melee_attack.hit_count += 1;
melee_attack.hit_count += melee_attack.simultaneous_hits;
}
}
}

View File

@ -352,7 +352,7 @@ fn dispatch_hit(
attack.apply_attack(
attacker_info,
target_info,
&target_info,
projectile_dir,
attack_options,
1.0,
@ -361,6 +361,7 @@ fn dispatch_hit(
|e| server_emitter.emit(e),
|o| outcomes_emitter.emit(o),
rng,
0,
);
},
projectile::Effect::Explode(e) => {

View File

@ -241,7 +241,7 @@ impl<'a> System<'a> for Sys {
shockwave.properties.attack.apply_attack(
attacker_info,
target_info,
&target_info,
dir,
attack_options,
1.0,
@ -254,6 +254,7 @@ impl<'a> System<'a> for Sys {
|e| server_emitter.emit(e),
|o| outcomes_emitter.emit(o),
&mut rng,
0,
);
shockwave_hit_list.hit_entities.push(*uid_b);

View File

@ -757,7 +757,7 @@ impl<'a> AgentData<'a> {
);
let attack_failed = if attempt_attack {
let context = AbilityContext::from(self.stance);
let contexts = AbilityContext::from(self.stance, Some(self.inventory));
let extract_ability = |input: AbilityInput| {
AbilityData::from_ability(
&self
@ -768,7 +768,7 @@ impl<'a> AgentData<'a> {
self.skill_set,
self.body,
Some(self.char_state),
context,
&contexts,
)
.unwrap_or_default()
.0,
@ -1437,7 +1437,7 @@ impl<'a> AgentData<'a> {
enum ActionStateConditions {
ConditionStaffCanShockwave = 0,
}
let context = AbilityContext::from(self.stance);
let contexts = AbilityContext::from(self.stance, Some(self.inventory));
let extract_ability = |input: AbilityInput| {
self.active_abilities
.activate_ability(
@ -1446,7 +1446,7 @@ impl<'a> AgentData<'a> {
self.skill_set,
self.body,
Some(self.char_state),
context,
&contexts,
)
.unwrap_or_default()
.0

View File

@ -1008,7 +1008,7 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
let time = server.state.ecs().read_resource::<Time>();
attack.apply_attack(
attacker_info,
target_info,
&target_info,
dir,
attack_options,
strength,
@ -1017,6 +1017,7 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
|e| emitter.emit(e),
|o| outcomes_emitter.emit(o),
&mut rng,
0,
);
}
}

View File

@ -181,7 +181,10 @@ impl Animation for ChargeswingAnimation {
.rotate_y(move2 * -0.3 - tension / 100.0);
next.foot_r.orientation.rotate_z(move3 * -1.5);
},
Some("common.abilities.sword.cleaving_spiral_slash") => {
Some(
"common.abilities.sword.cleaving_spiral_slash"
| "common.abilities.sword.cleaving_dual_spiral_slash",
) => {
let (move1, tension, move2, move3) = match stage_section {
Some(StageSection::Charge) => (
anim_time.powf(0.25).min(1.0),

View File

@ -714,6 +714,55 @@ impl Animation for ComboAnimation {
_ => {},
}
},
Some(
"common.abilities.sword.basic_dual_cross_cut"
| "common.abilities.sword.heavy_dual_cross_cut"
| "common.abilities.sword.agile_dual_cross_cut"
| "common.abilities.sword.defensive_dual_cross_cut"
| "common.abilities.sword.crippling_dual_cross_cut"
| "common.abilities.sword.cleaving_dual_cross_cut",
) => {
let (move1, move2) = if strike == current_strike {
match stage_section {
Some(StageSection::Buildup) => {
(((anim_time.max(0.4) - 0.4) * 1.5).powf(0.5), 0.0)
},
Some(StageSection::Action) => (1.0, (anim_time.min(0.4) * 2.5).powi(2)),
Some(StageSection::Recover) => (1.0, 1.0),
_ => (0.0, 0.0),
}
} else {
(1.0, 1.0)
};
let move1 = move1 * multi_strike_pullback;
let move2 = move2 * multi_strike_pullback;
next.hand_l.position = Vec3::new(s_a.shl.0, s_a.shl.1, s_a.shl.2);
next.hand_l.orientation =
Quaternion::rotation_x(s_a.shl.3) * Quaternion::rotation_y(s_a.shl.4);
next.hand_r.position = Vec3::new(-s_a.shl.0, s_a.shl.1, s_a.shl.2);
next.hand_r.orientation = Quaternion::rotation_x(s_a.shl.3);
next.control_l.position = Vec3::new(s_a.sc.0, s_a.sc.1, s_a.sc.2);
next.control_l.orientation = Quaternion::rotation_x(s_a.sc.3);
next.control_r.position = Vec3::new(-s_a.sc.0, s_a.sc.1, s_a.sc.2);
next.control_r.orientation = Quaternion::rotation_x(-s_a.sc.3);
next.control_l.position += Vec3::new(move1 * 3.0, move1 * 4.0, move1 * 8.0);
next.control_l.orientation.rotate_x(move1 * 1.0);
next.control_l.orientation.rotate_z(move1 * -0.5);
next.control_l.orientation.rotate_y(move1 * -0.3);
next.control_r.position += Vec3::new(move1 * -3.0, move1 * 4.0, move1 * 8.0);
next.control_r.orientation.rotate_x(move1 * -1.0);
next.control_r.orientation.rotate_z(move1 * 0.5);
next.control_r.orientation.rotate_y(move1 * 0.3);
next.control_l.orientation.rotate_x(move2 * -1.9);
next.control_l.orientation.rotate_z(move2 * -0.4);
next.control_l.position += Vec3::new(move2 * 8.0, move2 * 2.0, move2 * -9.0);
next.control_r.orientation.rotate_x(move2 * -1.5);
next.control_r.orientation.rotate_z(move2 * 0.4);
next.control_r.position += Vec3::new(move2 * -8.0, move2 * 2.0, move2 * -9.0);
},
Some("common.abilities.sword.crippling_bloody_gash") => {
let (move1, move2) = match stage_section {
Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0),

View File

@ -42,7 +42,9 @@ impl Animation for RapidMeleeAnimation {
match ability_id {
Some(
"common.abilities.sword.cleaving_whirlwind_slice"
| "common.abilities.sword.cleaving_bladestorm",
| "common.abilities.sword.cleaving_bladestorm"
| "common.abilities.sword.cleaving_dual_whirlwind_slice"
| "common.abilities.sword.cleaving_dual_bladestorm",
) => {
let (move1, move2, move3) = match stage_section {
Some(StageSection::Buildup) => (anim_time, 0.0, 0.0),
@ -134,6 +136,57 @@ impl Animation for RapidMeleeAnimation {
next.control.orientation.rotate_z(move2 * 1.2);
next.control.position += Vec3::new(0.0, move2 * 12.0, 0.0);
},
Some(
"common.abilities.sword.agile_dual_perforate"
| "common.abilities.sword.agile_dual_flurry",
) => {
let (move1, move2, move3) = match stage_section {
Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0),
Some(StageSection::Action) => (
1.0,
anim_time.min(0.5).mul(2.0).powi(2) - anim_time.max(0.5).sub(0.5).mul(2.0),
0.0,
),
Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)),
_ => (0.0, 0.0, 0.0),
};
let pullback = 1.0 - move3;
let move1 = move1 * pullback;
let move2 = move2 * pullback;
let dir = if current_strike % 2 == 1 { 1.0 } else { -1.0 };
next.hand_l.position = Vec3::new(s_a.shl.0, s_a.shl.1, s_a.shl.2);
next.hand_l.orientation =
Quaternion::rotation_x(s_a.shl.3) * Quaternion::rotation_y(s_a.shl.4);
next.hand_r.position = Vec3::new(-s_a.shl.0, s_a.shl.1, s_a.shl.2);
next.hand_r.orientation = Quaternion::rotation_x(s_a.shl.3);
next.control_l.position = Vec3::new(s_a.sc.0, s_a.sc.1, s_a.sc.2);
next.control_l.orientation = Quaternion::rotation_x(s_a.sc.3);
next.control_r.position = Vec3::new(-s_a.sc.0, s_a.sc.1, s_a.sc.2);
next.control_r.orientation = Quaternion::rotation_x(s_a.sc.3);
next.control_l.orientation.rotate_x(move1 * -1.1);
next.control_l.orientation.rotate_z(move1 * 0.7);
next.control_l.position += Vec3::new(move1 * 1.0, move1 * -1.0, move1 * 4.0);
next.control_r.orientation.rotate_x(move1 * -1.1);
next.control_r.orientation.rotate_z(move1 * -0.7);
next.control_r.position += Vec3::new(move1 * -1.0, move1 * -1.0, move1 * 4.0);
next.chest.orientation.rotate_z(move2 * -1.2 * dir);
next.head.orientation.rotate_z(move2 * 0.6 * dir);
next.belt.orientation.rotate_z(move2 * 0.3 * dir);
next.shorts.orientation.rotate_z(move2 * 0.7 * dir);
next.control_l
.orientation
.rotate_z(move2 * 1.2 * dir.max(0.0));
next.control_l.position += Vec3::new(0.0, move2 * 18.0 * dir.max(0.0), 0.0);
next.control_r
.orientation
.rotate_z(move2 * 1.2 * dir.min(0.0));
next.control_r.position += Vec3::new(0.0, move2 * 18.0 * -(dir.min(0.0)), 0.0);
next.control_l.orientation.rotate_z(move1 * -0.7);
next.control_r.orientation.rotate_z(move1 * 0.7);
},
Some("common.abilities.sword.agile_hundred_cuts") => {
let (move1, move2, move3) = match stage_section {
Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0),

View File

@ -87,6 +87,7 @@ fn maps_basic_melee() {
angle: 15.0,
damage_effect: None,
multi_target: None,
simultaneous_hits: 1,
},
ori_modifier: 1.0,
ability_info: empty_ability_info(),

View File

@ -241,7 +241,7 @@ pub struct Diary<'a> {
tooltip_manager: &'a mut TooltipManager,
slot_manager: &'a mut SlotManager,
pulse: f32,
context: AbilityContext,
contexts: &'a [AbilityContext],
#[conrod(common_builder)]
common: widget::CommonBuilder,
@ -287,7 +287,7 @@ impl<'a> Diary<'a> {
tooltip_manager: &'a mut TooltipManager,
slot_manager: &'a mut SlotManager,
pulse: f32,
context: AbilityContext,
contexts: &'a [AbilityContext],
) -> Self {
Self {
show,
@ -309,7 +309,7 @@ impl<'a> Diary<'a> {
tooltip_manager,
slot_manager,
pulse,
context,
contexts,
common: widget::CommonBuilder::default(),
created_btns_top_l: 0,
created_btns_top_r: 0,
@ -853,7 +853,7 @@ impl<'a> Widget for Diary<'a> {
self.active_abilities,
self.inventory,
self.skill_set,
self.context,
self.contexts,
),
image_source: self.imgs,
slot_manager: Some(self.slot_manager),
@ -868,7 +868,7 @@ impl<'a> Widget for Diary<'a> {
Some(self.inventory),
Some(self.skill_set),
)
.ability_id(Some(self.inventory), Some(self.skill_set), self.context);
.ability_id(Some(self.inventory), Some(self.skill_set), self.contexts);
let (ability_title, ability_desc) = if let Some(ability_id) = ability_id {
util::ability_description(ability_id, self.localized_strings)
} else {
@ -953,7 +953,7 @@ impl<'a> Widget for Diary<'a> {
Ability::from(a).ability_id(
Some(self.inventory),
Some(self.skill_set),
self.context,
self.contexts,
),
a,
)
@ -969,7 +969,7 @@ impl<'a> Widget for Diary<'a> {
Ability::from(a).ability_id(
Some(self.inventory),
Some(self.skill_set),
self.context,
self.contexts,
),
a,
)
@ -1080,7 +1080,7 @@ impl<'a> Widget for Diary<'a> {
self.active_abilities,
self.inventory,
self.skill_set,
self.context,
self.contexts,
),
image_source: self.imgs,
slot_manager: Some(self.slot_manager),

View File

@ -3018,7 +3018,7 @@ impl Hud {
bodies.get(entity),
) {
let stance = stances.get(entity);
let context = AbilityContext::from(stance);
let contexts = AbilityContext::from(stance, Some(inventory));
match Skillbar::new(
client,
&info,
@ -3044,7 +3044,7 @@ impl Hud {
i18n,
&msm,
self.floaters.combo_floater,
context,
&contexts,
combos.get(entity),
char_states.get(entity),
stance,
@ -3522,7 +3522,7 @@ impl Hud {
bodies.get(entity),
poises.get(entity),
) {
let context = AbilityContext::from(stances.get(entity));
let contexts = AbilityContext::from(stances.get(entity), Some(inventory));
for event in Diary::new(
&self.show,
client,
@ -3543,7 +3543,7 @@ impl Hud {
tooltip_manager,
&mut self.slot_manager,
self.pulse,
context,
&contexts,
)
.set(self.ids.diary, ui_widgets)
{

View File

@ -313,7 +313,7 @@ pub struct Skillbar<'a> {
common: widget::CommonBuilder,
msm: &'a MaterialStatManifest,
combo_floater: Option<ComboFloater>,
context: AbilityContext,
contexts: &'a [AbilityContext],
combo: Option<&'a Combo>,
char_state: Option<&'a CharacterState>,
stance: Option<&'a Stance>,
@ -346,7 +346,7 @@ impl<'a> Skillbar<'a> {
localized_strings: &'a Localization,
msm: &'a MaterialStatManifest,
combo_floater: Option<ComboFloater>,
context: AbilityContext,
contexts: &'a [AbilityContext],
combo: Option<&'a Combo>,
char_state: Option<&'a CharacterState>,
stance: Option<&'a Stance>,
@ -377,7 +377,7 @@ impl<'a> Skillbar<'a> {
localized_strings,
msm,
combo_floater,
context,
contexts,
combo,
char_state,
stance,
@ -940,7 +940,7 @@ impl<'a> Skillbar<'a> {
self.skillset,
self.active_abilities,
self.body,
self.context,
self.contexts,
self.combo,
self.char_state,
self.stance,
@ -1025,7 +1025,7 @@ impl<'a> Skillbar<'a> {
// Helper
let tooltip_text = |slot| {
let (hotbar, inventory, _, skill_set, active_abilities, _, context, _, _, _) =
let (hotbar, inventory, _, skill_set, active_abilities, _, contexts, _, _, _) =
content_source;
hotbar.get(slot).and_then(|content| match content {
hotbar::SlotContents::Inventory(i, _) => inventory
@ -1039,7 +1039,7 @@ impl<'a> Skillbar<'a> {
Ability::from(*a).ability_id(
Some(inventory),
Some(skill_set),
context,
contexts,
)
})
})
@ -1114,7 +1114,7 @@ impl<'a> Skillbar<'a> {
Ability::from(a.primary).ability_id(
Some(self.inventory),
Some(self.skillset),
self.context,
self.contexts,
)
});
@ -1144,7 +1144,7 @@ impl<'a> Skillbar<'a> {
Ability::from(a.secondary).ability_id(
Some(self.inventory),
Some(self.skillset),
self.context,
self.contexts,
)
});
@ -1166,7 +1166,7 @@ impl<'a> Skillbar<'a> {
self.skillset,
Some(self.body),
self.char_state,
self.context,
self.contexts,
)
})
.map_or(false, |(a, _)| {

View File

@ -129,7 +129,7 @@ type HotbarSource<'a> = (
&'a SkillSet,
Option<&'a ActiveAbilities>,
&'a Body,
AbilityContext,
&'a [AbilityContext],
Option<&'a Combo>,
Option<&'a CharacterState>,
Option<&'a Stance>,
@ -148,7 +148,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
skillset,
active_abilities,
body,
context,
contexts,
combo,
char_state,
stance,
@ -168,7 +168,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
a.auxiliary_set(Some(inventory), Some(skillset))
.get(i)
.and_then(|a| {
Ability::from(*a).ability_id(Some(inventory), Some(skillset), *context)
Ability::from(*a).ability_id(Some(inventory), Some(skillset), contexts)
})
});
@ -183,7 +183,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
skillset,
Some(body),
*char_state,
*context,
contexts,
)
})
.map(|(ability, _)| {
@ -240,7 +240,7 @@ type AbilitiesSource<'a> = (
&'a ActiveAbilities,
&'a Inventory,
&'a SkillSet,
AbilityContext,
&'a [AbilityContext],
);
impl<'a> SlotKey<AbilitiesSource<'a>, img_ids::Imgs> for AbilitySlot {
@ -248,7 +248,7 @@ impl<'a> SlotKey<AbilitiesSource<'a>, img_ids::Imgs> for AbilitySlot {
fn image_key(
&self,
(active_abilities, inventory, skillset, context): &AbilitiesSource<'a>,
(active_abilities, inventory, skillset, contexts): &AbilitiesSource<'a>,
) -> Option<(Self::ImageKey, Option<Color>)> {
let ability_id = match self {
Self::Slot(index) => active_abilities
@ -257,9 +257,9 @@ impl<'a> SlotKey<AbilitiesSource<'a>, img_ids::Imgs> for AbilitySlot {
Some(inventory),
Some(skillset),
)
.ability_id(Some(inventory), Some(skillset), *context),
.ability_id(Some(inventory), Some(skillset), contexts),
Self::Ability(ability) => {
Ability::from(*ability).ability_id(Some(inventory), Some(skillset), *context)
Ability::from(*ability).ability_id(Some(inventory), Some(skillset), contexts)
},
};

View File

@ -386,10 +386,11 @@ pub fn ability_image(imgs: &img_ids::Imgs, ability_id: &str) -> image::Id {
"veloren.core.pseudo_abilities.sword.secondary_ability" => imgs.sword_secondary_ability,
"common.abilities.sword.basic_thrust" => imgs.sword_basic_thrust,
"common.abilities.sword.heavy_slam" => imgs.sword_heavy_slam,
"common.abilities.sword.agile_perforate" => imgs.sword_agile_perforate,
"common.abilities.sword.agile_dual_perforate" => imgs.sword_agile_perforate,
"common.abilities.sword.defensive_vital_jab" => imgs.sword_defensive_vital_jab,
"common.abilities.sword.crippling_deep_rend" => imgs.sword_crippling_deep_rend,
"common.abilities.sword.cleaving_spiral_slash" => imgs.sword_cleaving_spiral_slash,
"common.abilities.sword.cleaving_dual_spiral_slash" => imgs.sword_cleaving_spiral_slash,
"veloren.core.pseudo_abilities.sword.crescent_slash" => imgs.sword_crescent_slash,
"common.abilities.sword.basic_crescent_slash" => imgs.sword_basic_crescent_slash,
"common.abilities.sword.heavy_crescent_slash" => imgs.sword_heavy_crescent_slash,
@ -425,6 +426,12 @@ pub fn ability_image(imgs: &img_ids::Imgs, ability_id: &str) -> image::Id {
"common.abilities.sword.defensive_cross_cut" => imgs.sword_defensive_cross_cut,
"common.abilities.sword.crippling_cross_cut" => imgs.sword_crippling_cross_cut,
"common.abilities.sword.cleaving_cross_cut" => imgs.sword_cleaving_cross_cut,
"common.abilities.sword.basic_dual_cross_cut" => imgs.sword_basic_cross_cut,
"common.abilities.sword.heavy_dual_cross_cut" => imgs.sword_heavy_cross_cut,
"common.abilities.sword.agile_dual_cross_cut" => imgs.sword_agile_cross_cut,
"common.abilities.sword.defensive_dual_cross_cut" => imgs.sword_defensive_cross_cut,
"common.abilities.sword.crippling_dual_cross_cut" => imgs.sword_crippling_cross_cut,
"common.abilities.sword.cleaving_dual_cross_cut" => imgs.sword_cleaving_cross_cut,
"veloren.core.pseudo_abilities.sword.finisher" => imgs.sword_finisher,
"common.abilities.sword.basic_mighty_strike" => imgs.sword_basic_mighty_strike,
"common.abilities.sword.heavy_guillotine" => imgs.sword_heavy_guillotine,
@ -432,6 +439,7 @@ pub fn ability_image(imgs: &img_ids::Imgs, ability_id: &str) -> image::Id {
"common.abilities.sword.defensive_counter" => imgs.sword_defensive_counter,
"common.abilities.sword.crippling_mutilate" => imgs.sword_crippling_mutilate,
"common.abilities.sword.cleaving_bladestorm" => imgs.sword_cleaving_bladestorm,
"common.abilities.sword.cleaving_dual_bladestorm" => imgs.sword_cleaving_bladestorm,
"common.abilities.sword.heavy_sweep" => imgs.sword_heavy_sweep,
"common.abilities.sword.heavy_pommel_strike" => imgs.sword_heavy_pommel_strike,
"common.abilities.sword.agile_quick_draw" => imgs.sword_agile_quick_draw,
@ -441,11 +449,15 @@ pub fn ability_image(imgs: &img_ids::Imgs, ability_id: &str) -> image::Id {
"common.abilities.sword.crippling_gouge" => imgs.sword_crippling_gouge,
"common.abilities.sword.crippling_hamstring" => imgs.sword_crippling_hamstring,
"common.abilities.sword.cleaving_whirlwind_slice" => imgs.sword_cleaving_whirlwind_slice,
"common.abilities.sword.cleaving_dual_whirlwind_slice" => {
imgs.sword_cleaving_whirlwind_slice
},
"common.abilities.sword.cleaving_earth_splitter" => imgs.sword_cleaving_earth_splitter,
"common.abilities.sword.heavy_fortitude" => imgs.sword_heavy_fortitude,
"common.abilities.sword.heavy_pillar_thrust" => imgs.sword_heavy_pillar_thrust,
"common.abilities.sword.agile_dancing_edge" => imgs.sword_agile_dancing_edge,
"common.abilities.sword.agile_flurry" => imgs.sword_agile_flurry,
"common.abilities.sword.agile_dual_flurry" => imgs.sword_agile_flurry,
"common.abilities.sword.defensive_stalwart_sword" => imgs.sword_defensive_stalwart_sword,
"common.abilities.sword.defensive_deflect" => imgs.sword_defensive_deflect,
"common.abilities.sword.crippling_eviscerate" => imgs.sword_crippling_eviscerate,

View File

@ -931,12 +931,12 @@ impl FigureMgr {
let second_tool_spec = second_tool_spec.as_deref();
let hands = (active_tool_hand, second_tool_hand);
let context = AbilityContext::from(stance);
let contexts = AbilityContext::from(stance, inventory);
let ability_id = character.and_then(|c| {
c.ability_info()
.and_then(|a| a.ability)
.and_then(|a| a.ability_id(inventory, skillset, context))
.and_then(|a| a.ability_id(inventory, skillset, &contexts))
});
let move_dir = {