mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'sam/mindflayer-rework' into 'master'
Mindflayer Rework See merge request veloren/veloren!1957
This commit is contained in:
commit
05ed391bb2
@ -17,11 +17,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Admin designated build areas
|
||||
|
||||
### Changed
|
||||
- Diamonds are now much more than twice as expensive as twigs.
|
||||
|
||||
- Admins can now grant normal players plots to place blocks within
|
||||
|
||||
- Seperated character randomization buttons into appearance and name.
|
||||
- Diamonds are now much more than twice as expensive as twigs.
|
||||
- Permission to build is no longer tied to being an admin
|
||||
- Separated character randomization buttons into appearance and name.
|
||||
- Reworked mindflayer to have unique attacks
|
||||
|
||||
### Removed
|
||||
|
||||
|
@ -3,7 +3,7 @@ ComboMelee(
|
||||
(
|
||||
stage: 1,
|
||||
base_damage: 90,
|
||||
base_poise_damage: 20,
|
||||
base_poise_damage: 15,
|
||||
damage_increase: 10,
|
||||
poise_damage_increase: 0,
|
||||
knockback: 8.0,
|
||||
|
@ -4,12 +4,15 @@ SpinMelee(
|
||||
recover_duration: 0.2,
|
||||
base_damage: 70,
|
||||
base_poise_damage: 55,
|
||||
knockback: 0.0,
|
||||
knockback: ( strength: 0.0, direction: Away),
|
||||
range: 3.5,
|
||||
damage_effect: None,
|
||||
energy_cost: 100,
|
||||
is_infinite: true,
|
||||
movement_behavior: AxeHover,
|
||||
is_interruptible: false,
|
||||
forward_speed: 0.0,
|
||||
num_spins: 1,
|
||||
specifier: None,
|
||||
target: Some(OutOfGroup),
|
||||
)
|
||||
|
@ -6,7 +6,7 @@ BasicBeam(
|
||||
tick_rate: 2.0,
|
||||
range: 25.0,
|
||||
max_angle: 1.0,
|
||||
lifesteal_eff: 0.15,
|
||||
damage_effect: Some(Lifesteal(0.15)),
|
||||
energy_regen: 50,
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Normal,
|
||||
|
@ -5,8 +5,8 @@ BasicBeam(
|
||||
damage: 50,
|
||||
tick_rate: 3.0,
|
||||
range: 20.0,
|
||||
max_angle: 10.0,
|
||||
lifesteal_eff: 0.0,
|
||||
max_angle: 15.0,
|
||||
damage_effect: None,
|
||||
energy_regen: 0,
|
||||
energy_drain: 350,
|
||||
orientation_behavior: Normal,
|
||||
|
@ -5,8 +5,8 @@ BasicBeam(
|
||||
damage: 50,
|
||||
tick_rate: 3.0,
|
||||
range: 20.0,
|
||||
max_angle: 0.1,
|
||||
lifesteal_eff: 0.0,
|
||||
max_angle: 15.0,
|
||||
damage_effect: None,
|
||||
energy_regen: 0,
|
||||
energy_drain: 350,
|
||||
orientation_behavior: Normal,
|
||||
|
@ -4,12 +4,15 @@ SpinMelee(
|
||||
recover_duration: 0.5,
|
||||
base_damage: 160,
|
||||
base_poise_damage: 25,
|
||||
knockback: 10.0,
|
||||
knockback: ( strength: 10.0, direction: Away),
|
||||
range: 3.5,
|
||||
damage_effect: None,
|
||||
energy_cost: 150,
|
||||
is_infinite: false,
|
||||
movement_behavior: ForwardGround,
|
||||
is_interruptible: true,
|
||||
forward_speed: 1.0,
|
||||
num_spins: 3,
|
||||
specifier: None,
|
||||
target: Some(OutOfGroup),
|
||||
)
|
||||
|
25
assets/common/abilities/unique/husk/singlestrike.ron
Normal file
25
assets/common/abilities/unique/husk/singlestrike.ron
Normal file
@ -0,0 +1,25 @@
|
||||
ComboMelee(
|
||||
stage_data: [
|
||||
(
|
||||
stage: 1,
|
||||
base_damage: 90,
|
||||
damage_increase: 0,
|
||||
base_poise_damage: 30,
|
||||
poise_damage_increase: 0,
|
||||
knockback: 5.0,
|
||||
range: 3.5,
|
||||
angle: 60.0,
|
||||
base_buildup_duration: 0.25,
|
||||
base_swing_duration: 0.07,
|
||||
base_recover_duration: 0.25,
|
||||
forward_movement: 0.5,
|
||||
),
|
||||
],
|
||||
initial_energy_gain: 0,
|
||||
max_energy_gain: 0,
|
||||
energy_increase: 0,
|
||||
speed_increase: 0.0,
|
||||
max_speed_increase: 0.0,
|
||||
scales_from_combo: 0,
|
||||
is_interruptible: false,
|
||||
)
|
53
assets/common/abilities/unique/husk/triplestrike.ron
Normal file
53
assets/common/abilities/unique/husk/triplestrike.ron
Normal file
@ -0,0 +1,53 @@
|
||||
ComboMelee(
|
||||
stage_data: [
|
||||
(
|
||||
stage: 1,
|
||||
base_damage: 120,
|
||||
damage_increase: 0,
|
||||
base_poise_damage: 35,
|
||||
poise_damage_increase: 0,
|
||||
knockback: 5.0,
|
||||
range: 3.5,
|
||||
angle: 30.0,
|
||||
base_buildup_duration: 0.2,
|
||||
base_swing_duration: 0.07,
|
||||
base_recover_duration: 0.2,
|
||||
forward_movement: 1.0,
|
||||
),
|
||||
(
|
||||
stage: 2,
|
||||
base_damage: 120,
|
||||
damage_increase: 0,
|
||||
base_poise_damage: 35,
|
||||
poise_damage_increase: 0,
|
||||
knockback: 5.0,
|
||||
range: 3.5,
|
||||
angle: 30.0,
|
||||
base_buildup_duration: 0.22,
|
||||
base_swing_duration: 0.07,
|
||||
base_recover_duration: 0.2,
|
||||
forward_movement: 0.0,
|
||||
),
|
||||
(
|
||||
stage: 3,
|
||||
base_damage: 120,
|
||||
damage_increase: 0,
|
||||
base_poise_damage: 35,
|
||||
poise_damage_increase: 0,
|
||||
knockback: 5.0,
|
||||
range: 3.5,
|
||||
angle: 30.0,
|
||||
base_buildup_duration: 0.2,
|
||||
base_swing_duration: 0.07,
|
||||
base_recover_duration: 0.2,
|
||||
forward_movement: 1.0,
|
||||
),
|
||||
],
|
||||
initial_energy_gain: 0,
|
||||
max_energy_gain: 0,
|
||||
energy_increase: 0,
|
||||
speed_increase: 0.0,
|
||||
max_speed_increase: 0.0,
|
||||
scales_from_combo: 0,
|
||||
is_interruptible: false,
|
||||
)
|
19
assets/common/abilities/unique/mindflayer/cursedflames.ron
Normal file
19
assets/common/abilities/unique/mindflayer/cursedflames.ron
Normal file
@ -0,0 +1,19 @@
|
||||
BasicBeam(
|
||||
buildup_duration: 0.40,
|
||||
recover_duration: 0.50,
|
||||
beam_duration: 1.0,
|
||||
damage: 350,
|
||||
tick_rate: 0.9,
|
||||
range: 22.0,
|
||||
max_angle: 15.0,
|
||||
damage_effect: Some(Buff((
|
||||
kind: Cursed,
|
||||
dur_secs: 30.0,
|
||||
strength: Value(0.5),
|
||||
chance: 1.0,
|
||||
))),
|
||||
energy_regen: 0,
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Normal,
|
||||
specifier: Cultist,
|
||||
)
|
@ -0,0 +1,5 @@
|
||||
Blink(
|
||||
buildup_duration: 0.5,
|
||||
recover_duration: 0.25,
|
||||
max_range: 100.0,
|
||||
)
|
18
assets/common/abilities/unique/mindflayer/necroticvortex.ron
Normal file
18
assets/common/abilities/unique/mindflayer/necroticvortex.ron
Normal file
@ -0,0 +1,18 @@
|
||||
SpinMelee(
|
||||
buildup_duration: 0.5,
|
||||
swing_duration: 0.2,
|
||||
recover_duration: 0.6,
|
||||
base_damage: 70.0,
|
||||
base_poise_damage: 0.0,
|
||||
knockback: ( strength: 8.0, direction: Towards),
|
||||
range: 17.5,
|
||||
damage_effect: Some(Lifesteal(1.0)),
|
||||
energy_cost: 0.0,
|
||||
is_infinite: true,
|
||||
movement_behavior: Stationary,
|
||||
is_interruptible: false,
|
||||
forward_speed: 0.0,
|
||||
num_spins: 1,
|
||||
specifier: Some(CultistVortex),
|
||||
target: None,
|
||||
)
|
16
assets/common/abilities/unique/mindflayer/summonminions.ron
Normal file
16
assets/common/abilities/unique/mindflayer/summonminions.ron
Normal file
@ -0,0 +1,16 @@
|
||||
BasicSummon(
|
||||
buildup_duration: 0.5,
|
||||
cast_duration: 1.0,
|
||||
recover_duration: 0.5,
|
||||
summon_amount: 6,
|
||||
summon_info: (
|
||||
body: BipedSmall((
|
||||
species: Husk,
|
||||
body_type: Male,
|
||||
)),
|
||||
scale: None,
|
||||
health_scaling: 80,
|
||||
loadout_config: Some(Husk),
|
||||
skillset_config: None,
|
||||
),
|
||||
)
|
@ -6,7 +6,7 @@ BasicBeam(
|
||||
tick_rate: 2.0,
|
||||
range: 25.0,
|
||||
max_angle: 1.0,
|
||||
lifesteal_eff: 0.15,
|
||||
damage_effect: Some(Lifesteal(0.15)),
|
||||
energy_regen: 25,
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Normal,
|
||||
|
@ -6,7 +6,7 @@ BasicBeam(
|
||||
tick_rate: 3.0,
|
||||
range: 15.0,
|
||||
max_angle: 22.5,
|
||||
lifesteal_eff: 0.0,
|
||||
damage_effect: None,
|
||||
energy_regen: 0,
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Normal,
|
||||
|
@ -4,12 +4,15 @@ SpinMelee(
|
||||
recover_duration: 0.1,
|
||||
base_damage: 500,
|
||||
base_poise_damage: 30,
|
||||
knockback: 0.0,
|
||||
knockback: ( strength: 0.0, direction: Away),
|
||||
range: 7.5,
|
||||
damage_effect: None,
|
||||
energy_cost: 0,
|
||||
is_infinite: false,
|
||||
movement_behavior: GolemHover,
|
||||
is_interruptible: false,
|
||||
forward_speed: 0.0,
|
||||
num_spins: 1,
|
||||
specifier: None,
|
||||
target: Some(OutOfGroup),
|
||||
)
|
||||
|
@ -7,7 +7,7 @@ BasicBeam(
|
||||
tick_rate: 3.0,
|
||||
range: 15.0,
|
||||
max_angle: 22.5,
|
||||
lifesteal_eff: 0.0,
|
||||
damage_effect: None,
|
||||
energy_regen: 0,
|
||||
energy_cost: 0,
|
||||
energy_drain: 0,
|
||||
|
@ -5,8 +5,8 @@ BasicBeam(
|
||||
damage: 3000,
|
||||
tick_rate: 3.0,
|
||||
range: 30.0,
|
||||
max_angle: 1.0,
|
||||
lifesteal_eff: 0.0,
|
||||
max_angle: 15.0,
|
||||
damage_effect: None,
|
||||
energy_regen: 0,
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Turret,
|
||||
|
@ -29,6 +29,11 @@
|
||||
(Some(Bow(UnlockRepeater)), "common.abilities.bow.repeater"),
|
||||
],
|
||||
),
|
||||
Unique(Husk): (
|
||||
primary: "common.abilities.unique.husk.singlestrike",
|
||||
secondary: "common.abilities.unique.husk.triplestrike",
|
||||
abilities: [],
|
||||
),
|
||||
Spear: (
|
||||
primary: "common.abilities.spear.doublestrike",
|
||||
secondary: "common.abilities.spear.dash",
|
||||
@ -166,7 +171,7 @@
|
||||
primary: "common.abilities.unique.quadlowbeam.healingbeam",
|
||||
secondary: "common.abilities.unique.quadlowbreathe.triplestrike",
|
||||
abilities: [
|
||||
(None, "common.abilities.unique.quadlowbreathe.dash"),
|
||||
(None, "common.abilities.unique.quadlowbreathe.dash"),
|
||||
],
|
||||
),
|
||||
Unique(QuadSmallBasic): (
|
||||
@ -194,6 +199,14 @@
|
||||
secondary: "common.abilities.unique.turret.arrows",
|
||||
abilities: [],
|
||||
),
|
||||
Unique(MindflayerStaff): (
|
||||
primary: "common.abilities.unique.mindflayer.cursedflames",
|
||||
secondary: "common.abilities.unique.mindflayer.necroticvortex",
|
||||
abilities: [
|
||||
(None, "common.abilities.unique.mindflayer.dimensionaldoor"),
|
||||
(None, "common.abilities.unique.mindflayer.summonminions"),
|
||||
],
|
||||
),
|
||||
Debug: (
|
||||
primary: "common.abilities.debug.forwardboost",
|
||||
secondary: "common.abilities.debug.upboost",
|
||||
|
@ -18,7 +18,7 @@ loot_tables: [
|
||||
(1,"common.loot_tables.loot_table_food"),
|
||||
(0.1,"common.loot_tables.loot_table_humanoids"),
|
||||
(1,"common.loot_tables.loot_table_maneater"),
|
||||
(0.0001,"common.loot_tables.loot_table_mindflayer"),
|
||||
(0.0001,"common.loot_tables.mindflayer"),
|
||||
(0.001,"common.loot_tables.loot_table_miniboss"),
|
||||
(0.05,"common.loot_tables.loot_table_raptor"),
|
||||
// loot_table_rocks is dropped by rock monsters, but is also the only source of stones to econsim until cave_scatter information is turned into a loot table
|
||||
|
@ -0,0 +1,13 @@
|
||||
ItemDef(
|
||||
name: "Husk",
|
||||
description: "Ceremonial attire used by members.",
|
||||
kind: Armor((
|
||||
kind: Chest("Husk"),
|
||||
stats: (
|
||||
protection: Normal(18.0),
|
||||
poise_resilience: Normal(1.0),
|
||||
),
|
||||
)),
|
||||
quality: Low,
|
||||
tags: [],
|
||||
)
|
13
assets/common/items/npc_armor/biped_small/husk/foot/husk.ron
Normal file
13
assets/common/items/npc_armor/biped_small/husk/foot/husk.ron
Normal file
@ -0,0 +1,13 @@
|
||||
ItemDef(
|
||||
name: "Husk",
|
||||
description: "Ceremonial attire used by members..",
|
||||
kind: Armor((
|
||||
kind: Foot("Husk"),
|
||||
stats: (
|
||||
protection: Normal(14.0),
|
||||
poise_resilience: Normal(1.0),
|
||||
),
|
||||
)),
|
||||
quality: Low,
|
||||
tags: [],
|
||||
)
|
13
assets/common/items/npc_armor/biped_small/husk/hand/husk.ron
Normal file
13
assets/common/items/npc_armor/biped_small/husk/hand/husk.ron
Normal file
@ -0,0 +1,13 @@
|
||||
ItemDef(
|
||||
name: "Husk",
|
||||
description: "Ceremonial attire used by members..",
|
||||
kind: Armor((
|
||||
kind: Hand("Husk"),
|
||||
stats: (
|
||||
protection: Normal(14.0),
|
||||
poise_resilience: Normal(1.0),
|
||||
),
|
||||
)),
|
||||
quality: Low,
|
||||
tags: [],
|
||||
)
|
13
assets/common/items/npc_armor/biped_small/husk/head/husk.ron
Normal file
13
assets/common/items/npc_armor/biped_small/husk/head/husk.ron
Normal file
@ -0,0 +1,13 @@
|
||||
ItemDef(
|
||||
name: "Husk",
|
||||
description: "Ceremonial attire used by members.",
|
||||
kind: Armor((
|
||||
kind: Head("Husk"),
|
||||
stats: (
|
||||
protection: Normal(14.0),
|
||||
poise_resilience: Normal(1.0),
|
||||
),
|
||||
)),
|
||||
quality: Low,
|
||||
tags: [],
|
||||
)
|
@ -0,0 +1,13 @@
|
||||
ItemDef(
|
||||
name: "Husk",
|
||||
description: "Ceremonial attire used by members..",
|
||||
kind: Armor((
|
||||
kind: Pants("Husk"),
|
||||
stats: (
|
||||
protection: Normal(14.0),
|
||||
poise_resilience: Normal(1.0),
|
||||
),
|
||||
)),
|
||||
quality: Low,
|
||||
tags: [],
|
||||
)
|
13
assets/common/items/npc_armor/biped_small/husk/tail/husk.ron
Normal file
13
assets/common/items/npc_armor/biped_small/husk/tail/husk.ron
Normal file
@ -0,0 +1,13 @@
|
||||
ItemDef(
|
||||
name: "Husk",
|
||||
description: "Ceremonial attire used by members..",
|
||||
kind: Armor((
|
||||
kind: Belt("Husk"),
|
||||
stats: (
|
||||
protection: Normal(14.0),
|
||||
poise_resilience: Normal(1.0),
|
||||
),
|
||||
)),
|
||||
quality: Low,
|
||||
tags: [],
|
||||
)
|
@ -2,15 +2,15 @@ ItemDef(
|
||||
name: "Mindflayer Staff",
|
||||
description: "Placeholder",
|
||||
kind: Tool((
|
||||
kind: StaffSimple,
|
||||
kind: Unique(MindflayerStaff),
|
||||
hands: Two,
|
||||
stats: Direct((
|
||||
equip_time_secs: 0.001,
|
||||
power: 3.0,
|
||||
equip_time_secs: 0.01,
|
||||
power: 1.0,
|
||||
poise_strength: 1.0,
|
||||
speed: 1.5,
|
||||
crit_chance: 0.15,
|
||||
crit_mult: 1.2539682,
|
||||
speed: 1.0,
|
||||
crit_chance: 0.0,
|
||||
crit_mult: 1.0,
|
||||
)),
|
||||
)),
|
||||
quality: Legendary,
|
||||
|
18
assets/common/items/npc_weapons/unique/husk.ron
Normal file
18
assets/common/items/npc_weapons/unique/husk.ron
Normal file
@ -0,0 +1,18 @@
|
||||
ItemDef(
|
||||
name: "Husk",
|
||||
description: "testing123",
|
||||
kind: Tool((
|
||||
kind: Unique(Husk),
|
||||
hands: Two,
|
||||
stats: Direct((
|
||||
equip_time_secs: 0.01,
|
||||
power: 1.0,
|
||||
poise_strength: 1.0,
|
||||
speed: 1.0,
|
||||
crit_chance: 0.0625,
|
||||
crit_mult: 1.9142857,
|
||||
)),
|
||||
)),
|
||||
quality: Low,
|
||||
tags: [],
|
||||
)
|
@ -13,7 +13,6 @@
|
||||
// Misc
|
||||
(4, "common.items.crafting_ing.empty_vial"),
|
||||
(0.25, "common.items.armor.misc.neck.plain_1"),
|
||||
(0.001, "common.items.glider.glider_purp"),
|
||||
(0.1, "common.items.glider.glider_blue"),
|
||||
(0.5, "common.items.utility.firework_purple"),
|
||||
(0.5, "common.items.utility.bomb"),
|
||||
@ -198,12 +197,10 @@
|
||||
(0.10, "common.items.weapons.sword.bloodsteel-0"),
|
||||
(0.10, "common.items.weapons.sword.bloodsteel-1"),
|
||||
(0.10, "common.items.weapons.sword.bloodsteel-2"),
|
||||
(0.001, "common.items.weapons.sword.caladbolg"),
|
||||
// axes
|
||||
(0.20, "common.items.weapons.axe.bloodsteel_axe-0"),
|
||||
(0.20, "common.items.weapons.axe.bloodsteel_axe-1"),
|
||||
(0.20, "common.items.weapons.axe.bloodsteel_axe-2"),
|
||||
(0.001, "common.items.weapons.axe.parashu"),
|
||||
// healing staff
|
||||
(0.25, "common.items.weapons.sceptre.staff_nature"),
|
||||
// staves
|
||||
@ -212,13 +209,11 @@
|
||||
(0.10, "common.items.weapons.staff.flamethrower_0"),
|
||||
(0.05, "common.items.weapons.staff.bent_fuse"),
|
||||
(0.05, "common.items.weapons.staff.orc_iron"),
|
||||
(0.001, "common.items.weapons.staff.phoenix"),
|
||||
// hammers
|
||||
(0.01, "common.items.weapons.hammer.cobalt_hammer-0"),
|
||||
(0.01, "common.items.weapons.hammer.cobalt_hammer-1"),
|
||||
(0.01, "common.items.weapons.hammer.runic_hammer"),
|
||||
(0.1, "common.items.weapons.hammer.ramshead_hammer"),
|
||||
(0.001, "common.items.weapons.hammer.mjolnir"),
|
||||
// bows
|
||||
(0.20, "common.items.weapons.bow.frostwood-0"),
|
||||
(0.20, "common.items.weapons.bow.frostwood-1"),
|
||||
@ -226,5 +221,4 @@
|
||||
(0.10, "common.items.weapons.bow.eldwood-0"),
|
||||
(0.10, "common.items.weapons.bow.eldwood-1"),
|
||||
(0.10, "common.items.weapons.bow.eldwood-2"),
|
||||
(0.001, "common.items.weapons.bow.sagitta"),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
[
|
||||
// armor
|
||||
(1, "common.items.armor.cultist.belt"),
|
||||
(1, "common.items.armor.cultist.chest"),
|
||||
(1, "common.items.armor.cultist.foot"),
|
||||
(1, "common.items.armor.cultist.hand"),
|
||||
(1, "common.items.armor.cultist.pants"),
|
||||
(1, "common.items.armor.cultist.shoulder"),
|
||||
(1, "common.items.armor.misc.back.dungeon_purple"),
|
||||
// weapons
|
||||
(1, "common.items.weapons.staff.cultist_staff"),
|
||||
(1, "common.items.weapons.hammer.cultist_purp_2h-0"),
|
||||
(1, "common.items.weapons.sword.cultist"),
|
||||
(0.25, "common.items.crafting_ing.mindflayer_bag_damaged"),
|
||||
(0.2, "common.items.weapons.sceptre.root_evil"),
|
||||
// misc
|
||||
(1, "common.items.boss_drops.lantern"),
|
||||
(0.25, "common.items.glider.glider_purp"),
|
||||
]
|
@ -12,6 +12,4 @@
|
||||
(1, "common.items.weapons.staff.cultist_staff"),
|
||||
(1, "common.items.weapons.hammer.cultist_purp_2h-0"),
|
||||
(1, "common.items.weapons.sword.cultist"),
|
||||
// misc
|
||||
(0.25, "common.items.boss_drops.lantern"),
|
||||
]
|
||||
|
@ -7,7 +7,6 @@
|
||||
(0.10, "common.items.weapons.sword.bloodsteel-0"),
|
||||
(0.10, "common.items.weapons.sword.bloodsteel-1"),
|
||||
(0.10, "common.items.weapons.sword.bloodsteel-2"),
|
||||
(0.01, "common.items.weapons.sword.caladbolg"),
|
||||
// axes
|
||||
(0.20, "common.items.weapons.axe.bloodsteel_axe-0"),
|
||||
(0.20, "common.items.weapons.axe.bloodsteel_axe-1"),
|
||||
@ -15,7 +14,6 @@
|
||||
(0.30, "common.items.weapons.axe.cobalt_axe-0"),
|
||||
(0.30, "common.items.weapons.axe.cobalt_axe-1"),
|
||||
(0.10, "common.items.weapons.axe.malachite_axe-0"),
|
||||
(0.01, "common.items.weapons.axe.parashu"),
|
||||
// healing staff
|
||||
(0.15, "common.items.weapons.sceptre.loops0"),
|
||||
(0.10, "common.items.weapons.sceptre.fork0"),
|
||||
@ -30,7 +28,6 @@
|
||||
(0.30, "common.items.weapons.hammer.cobalt_hammer-1"),
|
||||
(0.15, "common.items.weapons.hammer.runic_hammer"),
|
||||
(0.15, "common.items.weapons.hammer.ramshead_hammer"),
|
||||
(0.01, "common.items.weapons.hammer.mjolnir"),
|
||||
// bows
|
||||
(0.20, "common.items.weapons.bow.frostwood-0"),
|
||||
(0.20, "common.items.weapons.bow.frostwood-1"),
|
||||
@ -38,5 +35,4 @@
|
||||
(0.10, "common.items.weapons.bow.eldwood-0"),
|
||||
(0.10, "common.items.weapons.bow.eldwood-1"),
|
||||
(0.10, "common.items.weapons.bow.eldwood-2"),
|
||||
(0.01, "common.items.weapons.bow.sagitta"),
|
||||
]
|
14
assets/common/loot_tables/mindflayer.ron
Normal file
14
assets/common/loot_tables/mindflayer.ron
Normal file
@ -0,0 +1,14 @@
|
||||
[
|
||||
// Crafting material
|
||||
(2.0, "common.items.crafting_ing.mindflayer_bag_damaged"),
|
||||
// Legendary weapons
|
||||
(1.0, "common.items.weapons.sword.caladbolg"),
|
||||
(1.0, "common.items.weapons.axe.parashu"),
|
||||
(1.0, "common.items.weapons.hammer.mjolnir"),
|
||||
(1.0, "common.items.weapons.bow.sagitta"),
|
||||
(1.0, "common.items.weapons.staff.phoenix"),
|
||||
(1.0, "common.items.weapons.sceptre.root_evil"),
|
||||
// Rare misc items
|
||||
(0.5, "common.items.boss_drops.lantern"),
|
||||
(0.5, "common.items.glider.glider_purp"),
|
||||
]
|
@ -1025,6 +1025,10 @@
|
||||
myrmidon: (
|
||||
keyword: "myrmidon",
|
||||
generic: "Myrmidon"
|
||||
),
|
||||
husk: (
|
||||
keyword: "husk",
|
||||
generic: "Husk"
|
||||
)
|
||||
)
|
||||
),
|
||||
|
BIN
assets/voxygen/element/skillbar/decayed_bg.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/skillbar/decayed_bg.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -61,6 +61,7 @@ const int SNOW = 19;
|
||||
const int EXPLOSION = 20;
|
||||
const int ICE = 21;
|
||||
const int LIFESTEAL_BEAM = 22;
|
||||
const int CULTIST_FLAME = 23;
|
||||
|
||||
// meters per second squared (acceleration)
|
||||
const float earth_gravity = 9.807;
|
||||
@ -363,8 +364,8 @@ void main() {
|
||||
} else if (inst_mode == FLAMETHROWER) {
|
||||
f_reflect = 0.0; // Fire doesn't reflect light, it emits it
|
||||
attr = Attr(
|
||||
(inst_dir * slow_end(1.5)) + vec3(rand0, rand1, rand2) * (lifetime * 5 + 0.25),
|
||||
vec3((2.5 * (1 - slow_start(0.3)))),
|
||||
(inst_dir * slow_end(1.5)) + vec3(rand0, rand1, rand2) * (percent() + 2) * 0.1,
|
||||
vec3((2.5 * (1 - slow_start(0.2)))),
|
||||
vec4(3, 1.6 + rand5 * 0.3 - 0.4 * percent(), 0.2, 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
|
||||
);
|
||||
@ -392,6 +393,15 @@ void main() {
|
||||
vec4(3, 1.6 + rand5 * 0.3 - 0.4 * percent(), 0.2, 1),
|
||||
spin_in_axis(vec3(rand3, rand4, rand5), rand6)
|
||||
);
|
||||
} else if (inst_mode == CULTIST_FLAME) {
|
||||
f_reflect = 0.0; // Fire doesn't reflect light, it emits it
|
||||
float purp_color = 0.9 + 0.3 * rand3;
|
||||
attr = Attr(
|
||||
(inst_dir * slow_end(1.5)) + vec3(rand0, rand1, rand2) * (percent() + 2) * 0.1,
|
||||
vec3((3.5 * (1 - slow_start(0.2)))),
|
||||
vec4(purp_color, 0.0, purp_color, 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
|
||||
);
|
||||
} else {
|
||||
attr = Attr(
|
||||
linear_motion(
|
||||
|
@ -478,7 +478,7 @@
|
||||
central: ("npc.mindflayer.male.torso_upper"),
|
||||
),
|
||||
torso_lower: (
|
||||
offset: (-12.5, -10.0, -9.0),
|
||||
offset: (-12.5, -10.0, -18.0),
|
||||
central: ("npc.mindflayer.male.torso_lower"),
|
||||
),
|
||||
jaw: (
|
||||
@ -504,7 +504,7 @@
|
||||
central: ("npc.mindflayer.male.torso_upper"),
|
||||
),
|
||||
torso_lower: (
|
||||
offset: (-12.5, -10.0, -9.0),
|
||||
offset: (-12.5, -10.0, -18.0),
|
||||
central: ("npc.mindflayer.male.torso_lower"),
|
||||
),
|
||||
jaw: (
|
||||
|
@ -36,5 +36,8 @@
|
||||
"Myrmidon": (
|
||||
vox_spec: ("npc.myrmidon.male.chest", (-5.5, -4.0, -3.0)),
|
||||
),
|
||||
"Husk": (
|
||||
vox_spec: ("npc.husk.male.chest", (-6.0, -4.5, -4.0)),
|
||||
),
|
||||
},
|
||||
))
|
||||
|
@ -88,5 +88,13 @@
|
||||
vox_spec: ("npc.myrmidon.male.foot_r", (-1.5, -2.5, -7.0)),
|
||||
)
|
||||
),
|
||||
"Husk": (
|
||||
left: (
|
||||
vox_spec: ("npc.husk.male.foot_r", (-2.0, -3.5, -7.0)),
|
||||
),
|
||||
right: (
|
||||
vox_spec: ("npc.husk.male.foot_r", (-2.0, -3.5, -7.0)),
|
||||
)
|
||||
),
|
||||
},
|
||||
))
|
||||
|
@ -96,5 +96,13 @@
|
||||
vox_spec: ("npc.myrmidon.male.hand_r", (0.0, -2.0, -9.0)),
|
||||
)
|
||||
),
|
||||
"Husk": (
|
||||
left: (
|
||||
vox_spec: ("npc.husk.male.hand_r", (-7.0, -2.5, -10.0)),
|
||||
),
|
||||
right: (
|
||||
vox_spec: ("npc.husk.male.hand_r", (0.0, -2.5, -10.0)),
|
||||
)
|
||||
),
|
||||
},
|
||||
))
|
||||
|
@ -36,5 +36,8 @@
|
||||
"Myrmidon": (
|
||||
vox_spec: ("npc.myrmidon.male.head", (-4.5, -7.0, -6.5)),
|
||||
),
|
||||
"Husk": (
|
||||
vox_spec: ("npc.husk.male.head", (-6.0, -6.0, -5.5)),
|
||||
),
|
||||
},
|
||||
))
|
||||
|
@ -36,5 +36,8 @@
|
||||
"Myrmidon": (
|
||||
vox_spec: ("npc.myrmidon.male.pants", (-2.5, -4.5, -1.5)),
|
||||
),
|
||||
"Husk": (
|
||||
vox_spec: ("npc.husk.male.pants", (-5.0, -4.5, -6.0)),
|
||||
),
|
||||
},
|
||||
))
|
||||
|
@ -969,7 +969,7 @@
|
||||
vox_spec: ("armor.empty", (-3.0, -3.5, 1.0)),
|
||||
color: None
|
||||
),
|
||||
"common.items.npc_weapons.empty.empty": (
|
||||
"common.items.npc_weapons.unique.husk": (
|
||||
vox_spec: ("armor.empty", (-3.0, -3.5, 1.0)),
|
||||
color: None
|
||||
),
|
||||
|
BIN
assets/voxygen/voxel/npc/husk/male/chest.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/husk/male/chest.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/husk/male/foot_r.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/husk/male/foot_r.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/husk/male/hand_r.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/husk/male/hand_r.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/husk/male/head.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/husk/male/head.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/husk/male/pants.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/husk/male/pants.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -999,11 +999,17 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_input(&mut self, input: InputKind, pressed: bool, select_pos: Option<Vec3<f32>>) {
|
||||
pub fn handle_input(
|
||||
&mut self,
|
||||
input: InputKind,
|
||||
pressed: bool,
|
||||
select_pos: Option<Vec3<f32>>,
|
||||
target_entity: Option<EcsEntity>,
|
||||
) {
|
||||
if pressed {
|
||||
self.control_action(ControlAction::StartInput {
|
||||
input,
|
||||
target: None,
|
||||
target_entity: target_entity.and_then(|e| self.state.read_component_copied(e)),
|
||||
select_pos,
|
||||
});
|
||||
} else {
|
||||
|
@ -150,6 +150,7 @@ fn get_tool_kind(kind: &ToolKind) -> String {
|
||||
ToolKind::Spear => "Spear".to_string(),
|
||||
ToolKind::Debug => "Debug".to_string(),
|
||||
ToolKind::Farming => "Farming".to_string(),
|
||||
ToolKind::Pick => "Pick".to_string(),
|
||||
ToolKind::Unique(_) => "Unique".to_string(),
|
||||
ToolKind::Empty => "Empty".to_string(),
|
||||
}
|
||||
|
@ -411,7 +411,7 @@ impl AttackEffect {
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum CombatEffect {
|
||||
Heal(f32),
|
||||
Buff(CombatBuff),
|
||||
@ -647,7 +647,7 @@ impl Knockback {
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct CombatBuff {
|
||||
pub kind: BuffKind,
|
||||
pub dur_secs: f32,
|
||||
@ -656,7 +656,7 @@ pub struct CombatBuff {
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum CombatBuffStrength {
|
||||
DamageFraction(f32),
|
||||
Value(f32),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
assets::{self, Asset},
|
||||
combat,
|
||||
combat::{self, CombatEffect, Knockback},
|
||||
comp::{
|
||||
aura, beam, inventory::item::tool::ToolKind, projectile::ProjectileConstructor, skills,
|
||||
Body, CharacterState, EnergySource, Gravity, LightEmitter, StateUpdate,
|
||||
@ -10,7 +10,6 @@ use crate::{
|
||||
utils::{AbilityInfo, StageSection},
|
||||
*,
|
||||
},
|
||||
Knockback,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
@ -156,14 +155,17 @@ pub enum CharacterAbility {
|
||||
recover_duration: f32,
|
||||
base_damage: f32,
|
||||
base_poise_damage: f32,
|
||||
knockback: f32,
|
||||
knockback: Knockback,
|
||||
range: f32,
|
||||
damage_effect: Option<CombatEffect>,
|
||||
energy_cost: f32,
|
||||
is_infinite: bool,
|
||||
movement_behavior: spin_melee::MovementBehavior,
|
||||
is_interruptible: bool,
|
||||
forward_speed: f32,
|
||||
num_spins: u32,
|
||||
specifier: Option<spin_melee::FrontendSpecifier>,
|
||||
target: Option<combat::GroupTarget>,
|
||||
},
|
||||
ChargedMelee {
|
||||
energy_cost: f32,
|
||||
@ -223,7 +225,7 @@ pub enum CharacterAbility {
|
||||
tick_rate: f32,
|
||||
range: f32,
|
||||
max_angle: f32,
|
||||
lifesteal_eff: f32,
|
||||
damage_effect: Option<CombatEffect>,
|
||||
energy_regen: f32,
|
||||
energy_drain: f32,
|
||||
orientation_behavior: basic_beam::MovementBehavior,
|
||||
@ -250,6 +252,18 @@ pub enum CharacterAbility {
|
||||
energy_cost: f32,
|
||||
specifier: beam::FrontendSpecifier,
|
||||
},
|
||||
Blink {
|
||||
buildup_duration: f32,
|
||||
recover_duration: f32,
|
||||
max_range: f32,
|
||||
},
|
||||
BasicSummon {
|
||||
buildup_duration: f32,
|
||||
cast_duration: f32,
|
||||
recover_duration: f32,
|
||||
summon_amount: u32,
|
||||
summon_info: basic_summon::SummonInfo,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for CharacterAbility {
|
||||
@ -530,6 +544,25 @@ impl CharacterAbility {
|
||||
*heal *= power;
|
||||
*tick_rate *= speed;
|
||||
},
|
||||
Blink {
|
||||
ref mut buildup_duration,
|
||||
ref mut recover_duration,
|
||||
..
|
||||
} => {
|
||||
*buildup_duration /= speed;
|
||||
*recover_duration /= speed;
|
||||
},
|
||||
BasicSummon {
|
||||
ref mut buildup_duration,
|
||||
ref mut cast_duration,
|
||||
ref mut recover_duration,
|
||||
..
|
||||
} => {
|
||||
// TODO: Figure out how/if power should affect this
|
||||
*buildup_duration /= speed;
|
||||
*cast_duration /= speed;
|
||||
*recover_duration /= speed;
|
||||
},
|
||||
}
|
||||
self
|
||||
}
|
||||
@ -556,7 +589,7 @@ impl CharacterAbility {
|
||||
0
|
||||
}
|
||||
},
|
||||
BasicBlock | Boost { .. } | ComboMelee { .. } => 0,
|
||||
BasicBlock | Boost { .. } | ComboMelee { .. } | Blink { .. } | BasicSummon { .. } => 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1008,7 +1041,7 @@ impl CharacterAbility {
|
||||
ref mut damage,
|
||||
ref mut range,
|
||||
ref mut beam_duration,
|
||||
ref mut lifesteal_eff,
|
||||
ref mut damage_effect,
|
||||
ref mut energy_regen,
|
||||
..
|
||||
} => {
|
||||
@ -1024,8 +1057,10 @@ impl CharacterAbility {
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(LRegen)) {
|
||||
*energy_regen *= 1.25_f32.powi(level.into());
|
||||
}
|
||||
if let Ok(Some(level)) = skillset.skill_level(Sceptre(LLifesteal)) {
|
||||
*lifesteal_eff *= 1.3_f32.powi(level.into());
|
||||
if let (Ok(Some(level)), Some(CombatEffect::Lifesteal(ref mut lifesteal))) =
|
||||
(skillset.skill_level(Sceptre(LLifesteal)), damage_effect)
|
||||
{
|
||||
*lifesteal *= 1.3_f32.powi(level.into());
|
||||
}
|
||||
},
|
||||
HealingBeam {
|
||||
@ -1299,12 +1334,15 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
base_poise_damage,
|
||||
knockback,
|
||||
range,
|
||||
damage_effect,
|
||||
energy_cost,
|
||||
is_infinite,
|
||||
movement_behavior,
|
||||
is_interruptible,
|
||||
forward_speed,
|
||||
num_spins,
|
||||
specifier,
|
||||
target,
|
||||
} => CharacterState::SpinMelee(spin_melee::Data {
|
||||
static_data: spin_melee::StaticData {
|
||||
buildup_duration: Duration::from_secs_f32(*buildup_duration),
|
||||
@ -1314,13 +1352,16 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
base_poise_damage: *base_poise_damage,
|
||||
knockback: *knockback,
|
||||
range: *range,
|
||||
damage_effect: *damage_effect,
|
||||
energy_cost: *energy_cost,
|
||||
is_infinite: *is_infinite,
|
||||
movement_behavior: *movement_behavior,
|
||||
is_interruptible: *is_interruptible,
|
||||
forward_speed: *forward_speed,
|
||||
num_spins: *num_spins,
|
||||
target: *target,
|
||||
ability_info,
|
||||
specifier: *specifier,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
spins_remaining: *num_spins - 1,
|
||||
@ -1479,7 +1520,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
tick_rate,
|
||||
range,
|
||||
max_angle,
|
||||
lifesteal_eff,
|
||||
damage_effect,
|
||||
energy_regen,
|
||||
energy_drain,
|
||||
orientation_behavior,
|
||||
@ -1493,7 +1534,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
tick_rate: *tick_rate,
|
||||
range: *range,
|
||||
max_angle: *max_angle,
|
||||
lifesteal_eff: *lifesteal_eff,
|
||||
damage_effect: *damage_effect,
|
||||
energy_regen: *energy_regen,
|
||||
energy_drain: *energy_drain,
|
||||
ability_info,
|
||||
@ -1552,6 +1593,39 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
}),
|
||||
CharacterAbility::Blink {
|
||||
buildup_duration,
|
||||
recover_duration,
|
||||
max_range,
|
||||
} => CharacterState::Blink(blink::Data {
|
||||
static_data: blink::StaticData {
|
||||
buildup_duration: Duration::from_secs_f32(*buildup_duration),
|
||||
recover_duration: Duration::from_secs_f32(*recover_duration),
|
||||
max_range: *max_range,
|
||||
ability_info,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
}),
|
||||
CharacterAbility::BasicSummon {
|
||||
buildup_duration,
|
||||
cast_duration,
|
||||
recover_duration,
|
||||
summon_amount,
|
||||
summon_info,
|
||||
} => CharacterState::BasicSummon(basic_summon::Data {
|
||||
static_data: basic_summon::StaticData {
|
||||
buildup_duration: Duration::from_secs_f32(*buildup_duration),
|
||||
cast_duration: Duration::from_secs_f32(*cast_duration),
|
||||
recover_duration: Duration::from_secs_f32(*recover_duration),
|
||||
summon_amount: *summon_amount,
|
||||
summon_info: *summon_info,
|
||||
ability_info,
|
||||
},
|
||||
summon_count: 0,
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ pub enum Tactic {
|
||||
Turret,
|
||||
FixedTurret,
|
||||
RotatingTurret,
|
||||
Mindflayer,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
|
@ -51,4 +51,5 @@ pub enum FrontendSpecifier {
|
||||
Flamethrower,
|
||||
LifestealBeam,
|
||||
HealingBeam,
|
||||
Cultist,
|
||||
}
|
||||
|
@ -285,7 +285,7 @@ impl Body {
|
||||
biped_large::Species::Slysaurok => 3.4,
|
||||
biped_large::Species::Occultsaurok => 3.4,
|
||||
biped_large::Species::Mightysaurok => 3.4,
|
||||
biped_large::Species::Mindflayer => 6.3,
|
||||
biped_large::Species::Mindflayer => 8.0,
|
||||
biped_large::Species::Minotaur => 8.0,
|
||||
biped_large::Species::Dullahan => 5.5,
|
||||
biped_large::Species::Cyclops => 6.5,
|
||||
@ -393,6 +393,7 @@ impl Body {
|
||||
biped_small::Species::Sahagin => 500,
|
||||
biped_small::Species::Haniwa => 700,
|
||||
biped_small::Species::Myrmidon => 800,
|
||||
biped_small::Species::Husk => 200,
|
||||
_ => 600,
|
||||
},
|
||||
Body::Object(object) => match object {
|
||||
@ -540,10 +541,10 @@ impl Body {
|
||||
/// due to AI or not using an actual weapon
|
||||
// TODO: Match on species
|
||||
pub fn combat_multiplier(&self) -> f32 {
|
||||
if let Body::Object(_) | Body::Ship(_) = self {
|
||||
0.0
|
||||
} else {
|
||||
1.0
|
||||
match self {
|
||||
Body::Object(_) | Body::Ship(_) => 0.0,
|
||||
Body::BipedLarge(b) if matches!(b.species, biped_large::Species::Mindflayer) => 4.0,
|
||||
_ => 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,7 @@ make_case_elim!(
|
||||
Gnoll = 7,
|
||||
Haniwa = 8,
|
||||
Myrmidon = 9,
|
||||
Husk = 10,
|
||||
}
|
||||
);
|
||||
|
||||
@ -62,6 +63,7 @@ pub struct AllSpecies<SpeciesMeta> {
|
||||
pub gnoll: SpeciesMeta,
|
||||
pub haniwa: SpeciesMeta,
|
||||
pub myrmidon: SpeciesMeta,
|
||||
pub husk: SpeciesMeta,
|
||||
}
|
||||
|
||||
impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies<SpeciesMeta> {
|
||||
@ -80,11 +82,12 @@ impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies<SpeciesMeta>
|
||||
Species::Gnoll => &self.gnoll,
|
||||
Species::Haniwa => &self.haniwa,
|
||||
Species::Myrmidon => &self.myrmidon,
|
||||
Species::Husk => &self.husk,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const ALL_SPECIES: [Species; 10] = [
|
||||
pub const ALL_SPECIES: [Species; 11] = [
|
||||
Species::Gnome,
|
||||
Species::Sahagin,
|
||||
Species::Adlet,
|
||||
@ -95,6 +98,7 @@ pub const ALL_SPECIES: [Species; 10] = [
|
||||
Species::Gnoll,
|
||||
Species::Haniwa,
|
||||
Species::Myrmidon,
|
||||
Species::Husk,
|
||||
];
|
||||
|
||||
impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
|
||||
|
@ -20,8 +20,7 @@ pub enum BuffKind {
|
||||
Saturation,
|
||||
/// Lowers health over time for some duration
|
||||
Bleeding,
|
||||
/// Lower a creature's max health
|
||||
/// Currently placeholder buff to show other stuff is possible
|
||||
/// Lower a creature's max health over time
|
||||
Cursed,
|
||||
/// Applied when drinking a potion
|
||||
Potion,
|
||||
@ -109,6 +108,13 @@ pub enum BuffEffect {
|
||||
MaxEnergyModifier { value: f32, kind: ModifierKind },
|
||||
/// Reduces damage after armor is accounted for by this fraction
|
||||
DamageReduction(f32),
|
||||
/// Gradually changes an entities max health over time
|
||||
MaxHealthChangeOverTime {
|
||||
rate: f32,
|
||||
accumulated: f32,
|
||||
kind: ModifierKind,
|
||||
target_fraction: f32,
|
||||
},
|
||||
}
|
||||
|
||||
/// Actual de/buff.
|
||||
@ -191,10 +197,19 @@ impl Buff {
|
||||
data.duration,
|
||||
),
|
||||
BuffKind::Cursed => (
|
||||
vec![BuffEffect::MaxHealthModifier {
|
||||
value: -100. * data.strength,
|
||||
kind: ModifierKind::Additive,
|
||||
}],
|
||||
vec![
|
||||
BuffEffect::MaxHealthChangeOverTime {
|
||||
rate: -10.0,
|
||||
accumulated: 0.0,
|
||||
kind: ModifierKind::Additive,
|
||||
target_fraction: 1.0 - data.strength,
|
||||
},
|
||||
BuffEffect::HealthChangeOverTime {
|
||||
rate: -10.0,
|
||||
accumulated: 0.0,
|
||||
kind: ModifierKind::Additive,
|
||||
},
|
||||
],
|
||||
data.duration,
|
||||
),
|
||||
BuffKind::IncreaseMaxEnergy => (
|
||||
|
@ -93,6 +93,10 @@ pub enum CharacterState {
|
||||
/// specifically for the healing beam. There was also functionality present
|
||||
/// on basic beam which was unnecessary for the healing beam.
|
||||
HealingBeam(healing_beam::Data),
|
||||
/// A short teleport that targets either a position or entity
|
||||
Blink(blink::Data),
|
||||
/// Summons creatures that fight for the caster
|
||||
BasicSummon(basic_summon::Data),
|
||||
}
|
||||
|
||||
impl CharacterState {
|
||||
|
@ -110,7 +110,7 @@ pub enum ControlAction {
|
||||
Talk,
|
||||
StartInput {
|
||||
input: InputKind,
|
||||
target: Option<Uid>,
|
||||
target_entity: Option<Uid>,
|
||||
// Some inputs need a selected position, such as mining
|
||||
select_pos: Option<Vec3<f32>>,
|
||||
},
|
||||
@ -121,7 +121,7 @@ impl ControlAction {
|
||||
pub fn basic_input(input: InputKind) -> Self {
|
||||
ControlAction::StartInput {
|
||||
input,
|
||||
target: None,
|
||||
target_entity: None,
|
||||
select_pos: None,
|
||||
}
|
||||
}
|
||||
@ -144,9 +144,10 @@ impl InputKind {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct InputAttr {
|
||||
pub select_pos: Option<Vec3<f32>>,
|
||||
pub target_entity: Option<Uid>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
|
@ -34,12 +34,11 @@ pub enum HealthSource {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Health {
|
||||
base_max: u32,
|
||||
current: u32,
|
||||
base_max: u32,
|
||||
maximum: u32,
|
||||
last_max: u32,
|
||||
pub last_change: (f64, HealthChange),
|
||||
pub is_dead: bool,
|
||||
}
|
||||
@ -58,9 +57,8 @@ impl Health {
|
||||
pub fn empty() -> Self {
|
||||
Health {
|
||||
current: 0,
|
||||
maximum: 0,
|
||||
base_max: 0,
|
||||
last_max: 0,
|
||||
maximum: 0,
|
||||
last_change: (0.0, HealthChange {
|
||||
amount: 0,
|
||||
cause: HealthSource::Revive,
|
||||
@ -73,6 +71,8 @@ impl Health {
|
||||
|
||||
pub fn maximum(&self) -> u32 { self.maximum }
|
||||
|
||||
pub fn base_max(&self) -> u32 { self.base_max }
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn set_to(&mut self, amount: u32, cause: HealthSource) {
|
||||
let amount = amount.min(self.maximum);
|
||||
@ -97,6 +97,12 @@ impl Health {
|
||||
self.current = self.current.min(self.maximum);
|
||||
}
|
||||
|
||||
// Scales the temporary max health by a modifier.
|
||||
pub fn scale_maximum(&mut self, scaled: f32) {
|
||||
let scaled_max = (self.base_max as f32 * scaled) as u32;
|
||||
self.set_maximum(scaled_max);
|
||||
}
|
||||
|
||||
// This is private because max hp is based on the level
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn set_base_max(&mut self, amount: u32) {
|
||||
@ -125,25 +131,8 @@ impl Health {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn with_max_health(mut self, amount: u32) -> Self {
|
||||
self.maximum = amount;
|
||||
self.current = amount;
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn last_set(&mut self) { self.last_max = self.maximum }
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn reset_max(&mut self) {
|
||||
self.maximum = self.base_max;
|
||||
if self.current > self.last_max {
|
||||
self.current = self.last_max;
|
||||
|
||||
self.last_max = self.base_max;
|
||||
}
|
||||
}
|
||||
/// Returns the fraction of health an entity has remaining
|
||||
pub fn fraction(&self) -> f32 { self.current as f32 / self.maximum.max(1) as f32 }
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
@ -406,6 +406,7 @@ impl assets::Compound for AbilityMap {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum UniqueKind {
|
||||
StoneGolemFist,
|
||||
Husk,
|
||||
BeastClaws,
|
||||
WendigoMagic,
|
||||
TidalClaws,
|
||||
@ -426,4 +427,5 @@ pub enum UniqueKind {
|
||||
TheropodCharge,
|
||||
ObjectTurret,
|
||||
WoodenSpear,
|
||||
MindflayerStaff,
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ use crate::{
|
||||
trade::{Good, SiteInformation},
|
||||
};
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Builder for character Loadouts, containing weapon and armour items belonging
|
||||
/// to a character, along with some helper methods for loading Items and
|
||||
@ -34,13 +35,14 @@ use rand::Rng;
|
||||
#[derive(Clone)]
|
||||
pub struct LoadoutBuilder(Loadout);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, Debug)]
|
||||
pub enum LoadoutConfig {
|
||||
Adlet,
|
||||
Gnarling,
|
||||
Sahagin,
|
||||
Haniwa,
|
||||
Myrmidon,
|
||||
Husk,
|
||||
Guard,
|
||||
Villager,
|
||||
Merchant,
|
||||
@ -307,6 +309,11 @@ impl LoadoutBuilder {
|
||||
"common.items.npc_weapons.biped_small.adlet.gnoll_staff",
|
||||
));
|
||||
},
|
||||
(biped_small::Species::Husk, _) => {
|
||||
main_tool = Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_weapons.unique.husk",
|
||||
));
|
||||
},
|
||||
_ => {
|
||||
main_tool = Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_weapons.biped_small.adlet.wooden_spear",
|
||||
@ -502,6 +509,24 @@ impl LoadoutBuilder {
|
||||
"common.items.npc_armor.biped_small.myrmidon.tail.myrmidon",
|
||||
)))
|
||||
.build(),
|
||||
Husk => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.head(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.biped_small.husk.head.husk",
|
||||
)))
|
||||
.feet(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.biped_small.husk.foot.husk",
|
||||
)))
|
||||
.hands(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.biped_small.husk.hand.husk",
|
||||
)))
|
||||
.chest(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.biped_small.husk.chest.husk",
|
||||
)))
|
||||
.pants(Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_armor.biped_small.husk.pants.husk",
|
||||
)))
|
||||
.build(),
|
||||
Guard => LoadoutBuilder::new()
|
||||
.active_item(active_item)
|
||||
.shoulder(Some(Item::new_from_asset_expect(
|
||||
|
@ -154,7 +154,8 @@ impl Component for PhysicsState {
|
||||
type Storage = IdvStorage<Self>;
|
||||
}
|
||||
|
||||
// ForceUpdate
|
||||
/// Used to forcefully update the position, velocity, and orientation of the
|
||||
/// client
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ForceUpdate;
|
||||
|
||||
|
@ -28,6 +28,7 @@ pub struct Stats {
|
||||
// potentially be updated every tick (especially as more buffs are added)
|
||||
pub skill_set: SkillSet,
|
||||
pub damage_reduction: f32,
|
||||
pub max_health_modifier: f32,
|
||||
}
|
||||
|
||||
impl Stats {
|
||||
@ -36,6 +37,7 @@ impl Stats {
|
||||
name,
|
||||
skill_set: SkillSet::default(),
|
||||
damage_reduction: 0.0,
|
||||
max_health_modifier: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,8 +48,15 @@ impl Stats {
|
||||
name: "".to_owned(),
|
||||
skill_set: SkillSet::default(),
|
||||
damage_reduction: 0.0,
|
||||
max_health_modifier: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets temporary modifiers to default values
|
||||
pub fn reset_temp_modifiers(&mut self) {
|
||||
self.damage_reduction = 0.0;
|
||||
self.max_health_modifier = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Stats {
|
||||
|
@ -1,17 +1,18 @@
|
||||
use crate::{
|
||||
character::CharacterId,
|
||||
comp,
|
||||
comp::{
|
||||
self,
|
||||
invite::{InviteKind, InviteResponse},
|
||||
item::Item,
|
||||
Ori, Pos,
|
||||
},
|
||||
outcome::Outcome,
|
||||
rtsim::RtSimEntity,
|
||||
trade::{TradeAction, TradeId},
|
||||
uid::Uid,
|
||||
util::Dir,
|
||||
Explosion,
|
||||
};
|
||||
use comp::{
|
||||
invite::{InviteKind, InviteResponse},
|
||||
item::Item,
|
||||
Ori, Pos,
|
||||
};
|
||||
use specs::Entity as EcsEntity;
|
||||
use std::{collections::VecDeque, ops::DerefMut, sync::Mutex};
|
||||
use vek::*;
|
||||
@ -28,6 +29,8 @@ pub enum LocalEvent {
|
||||
},
|
||||
/// Applies `vel` velocity to `entity`
|
||||
Boost { entity: EcsEntity, vel: Vec3<f32> },
|
||||
/// Creates an outcome
|
||||
CreateOutcome(Outcome),
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)] // TODO: Pending review in #587
|
||||
@ -161,6 +164,11 @@ pub enum ServerEvent {
|
||||
pos: Vec3<i32>,
|
||||
tool: Option<comp::tool::ToolKind>,
|
||||
},
|
||||
TeleportTo {
|
||||
entity: EcsEntity,
|
||||
target: Uid,
|
||||
max_range: Option<f32>,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct EventBus<E> {
|
||||
|
@ -45,6 +45,10 @@ pub enum Outcome {
|
||||
pos: Vec3<i32>,
|
||||
color: Option<Rgb<u8>>,
|
||||
},
|
||||
SummonedCreature {
|
||||
pos: Vec3<f32>,
|
||||
body: comp::Body,
|
||||
},
|
||||
}
|
||||
|
||||
impl Outcome {
|
||||
@ -53,7 +57,8 @@ impl Outcome {
|
||||
Outcome::Explosion { pos, .. }
|
||||
| Outcome::ProjectileShot { pos, .. }
|
||||
| Outcome::Beam { pos, .. }
|
||||
| Outcome::SkillPointGain { pos, .. } => Some(*pos),
|
||||
| Outcome::SkillPointGain { pos, .. }
|
||||
| Outcome::SummonedCreature { pos, .. } => Some(*pos),
|
||||
Outcome::BreakBlock { pos, .. } => Some(pos.map(|e| e as f32 + 0.5)),
|
||||
Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => None,
|
||||
}
|
||||
|
@ -4,9 +4,10 @@ use crate::comp::{
|
||||
AxeSkill, BowSkill, HammerSkill, Skill, SkillGroupKind, SkillSet, StaffSkill, SwordSkill,
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::warn;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, Debug)]
|
||||
pub enum SkillSetConfig {
|
||||
Adlet,
|
||||
Gnarling,
|
||||
|
@ -32,9 +32,8 @@ pub struct StaticData {
|
||||
pub range: f32,
|
||||
/// Max angle (45.0 will give you a 90.0 angle window)
|
||||
pub max_angle: f32,
|
||||
/// Lifesteal efficiency (0 gives 0% conversion of damage to health, 1 gives
|
||||
/// 100% conversion of damage to health)
|
||||
pub lifesteal_eff: f32,
|
||||
/// Adds an effect onto the main damage of the attack
|
||||
pub damage_effect: Option<CombatEffect>,
|
||||
/// Energy regenerated per tick
|
||||
pub energy_regen: f32,
|
||||
/// Energy drained per second
|
||||
@ -111,15 +110,16 @@ impl CharacterBehavior for Data {
|
||||
CombatEffect::EnergyReward(self.static_data.energy_regen),
|
||||
)
|
||||
.with_requirement(CombatRequirement::AnyDamage);
|
||||
let lifesteal = CombatEffect::Lifesteal(self.static_data.lifesteal_eff);
|
||||
let damage = AttackDamage::new(
|
||||
let mut damage = AttackDamage::new(
|
||||
Damage {
|
||||
source: DamageSource::Energy,
|
||||
value: self.static_data.damage,
|
||||
},
|
||||
Some(GroupTarget::OutOfGroup),
|
||||
)
|
||||
.with_effect(lifesteal);
|
||||
);
|
||||
if let Some(effect) = self.static_data.damage_effect {
|
||||
damage = damage.with_effect(effect);
|
||||
}
|
||||
let (crit_chance, crit_mult) =
|
||||
get_crit_data(data, self.static_data.ability_info);
|
||||
let attack = Attack::default()
|
||||
@ -138,8 +138,8 @@ impl CharacterBehavior for Data {
|
||||
};
|
||||
// Gets offsets
|
||||
let body_offsets = Vec3::new(
|
||||
(data.body.radius() + 1.0) * data.inputs.look_dir.x,
|
||||
(data.body.radius() + 1.0) * data.inputs.look_dir.y,
|
||||
(data.body.radius() + 0.2) * data.inputs.look_dir.x,
|
||||
(data.body.radius() + 0.2) * data.inputs.look_dir.y,
|
||||
data.body.eye_height() * 0.6,
|
||||
);
|
||||
let pos = Pos(data.pos.0 + body_offsets);
|
||||
|
186
common/src/states/basic_summon.rs
Normal file
186
common/src/states/basic_summon.rs
Normal file
@ -0,0 +1,186 @@
|
||||
use crate::{
|
||||
comp::{
|
||||
self,
|
||||
inventory::loadout_builder::{LoadoutBuilder, LoadoutConfig},
|
||||
CharacterState, StateUpdate,
|
||||
},
|
||||
event::{LocalEvent, ServerEvent},
|
||||
outcome::Outcome,
|
||||
skillset_builder::{SkillSetBuilder, SkillSetConfig},
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
|
||||
/// Separated out to condense update portions of character state
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct StaticData {
|
||||
/// How long the state builds up for
|
||||
pub buildup_duration: Duration,
|
||||
/// How long the state is casting for
|
||||
pub cast_duration: Duration,
|
||||
/// How long the state recovers for
|
||||
pub recover_duration: Duration,
|
||||
/// How many creatures the state should summon
|
||||
pub summon_amount: u32,
|
||||
/// Information about the summoned creature
|
||||
pub summon_info: SummonInfo,
|
||||
/// Miscellaneous information about the ability
|
||||
pub ability_info: AbilityInfo,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Data {
|
||||
/// Struct containing data that does not change over the course of the
|
||||
/// character state
|
||||
pub static_data: StaticData,
|
||||
/// How many creatures have been summoned
|
||||
pub summon_count: u32,
|
||||
/// Timer for each stage
|
||||
pub timer: Duration,
|
||||
/// What section the character stage is in
|
||||
pub stage_section: StageSection,
|
||||
}
|
||||
|
||||
impl CharacterBehavior for Data {
|
||||
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
|
||||
match self.stage_section {
|
||||
StageSection::Buildup => {
|
||||
if self.timer < self.static_data.buildup_duration {
|
||||
// Build up
|
||||
update.character = CharacterState::BasicSummon(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Transitions to recover section of stage
|
||||
update.character = CharacterState::BasicSummon(Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Cast,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Cast => {
|
||||
if self.timer < self.static_data.cast_duration
|
||||
|| self.summon_count < self.static_data.summon_amount
|
||||
{
|
||||
if self.timer
|
||||
> self.static_data.cast_duration * self.summon_count
|
||||
/ self.static_data.summon_amount
|
||||
{
|
||||
let body = self.static_data.summon_info.body;
|
||||
|
||||
let loadout = LoadoutBuilder::build_loadout(
|
||||
body,
|
||||
None,
|
||||
self.static_data.summon_info.loadout_config,
|
||||
None,
|
||||
)
|
||||
.build();
|
||||
let mut stats = comp::Stats::new("Summon".to_string());
|
||||
stats.skill_set = SkillSetBuilder::build_skillset(
|
||||
&None,
|
||||
self.static_data.summon_info.skillset_config,
|
||||
)
|
||||
.build();
|
||||
|
||||
// Send server event to create npc
|
||||
update.server_events.push_front(ServerEvent::CreateNpc {
|
||||
pos: *data.pos,
|
||||
stats,
|
||||
health: comp::Health::new(
|
||||
body,
|
||||
self.static_data.summon_info.health_scaling,
|
||||
),
|
||||
poise: comp::Poise::new(body),
|
||||
loadout,
|
||||
body,
|
||||
agent: Some(comp::Agent::new(None, false, None, &body, true)),
|
||||
alignment: comp::Alignment::Owned(*data.uid),
|
||||
scale: self
|
||||
.static_data
|
||||
.summon_info
|
||||
.scale
|
||||
.unwrap_or(comp::Scale(1.0)),
|
||||
home_chunk: None,
|
||||
drop_item: None,
|
||||
rtsim_entity: None,
|
||||
});
|
||||
|
||||
// Send local event used for frontend shenanigans
|
||||
update.local_events.push_front(LocalEvent::CreateOutcome(
|
||||
Outcome::SummonedCreature {
|
||||
pos: data.pos.0,
|
||||
body,
|
||||
},
|
||||
));
|
||||
|
||||
update.character = CharacterState::BasicSummon(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
summon_count: self.summon_count + 1,
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Cast
|
||||
update.character = CharacterState::BasicSummon(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
..*self
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Transitions to recover section of stage
|
||||
update.character = CharacterState::BasicSummon(Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Recover,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Recover => {
|
||||
if self.timer < self.static_data.recover_duration {
|
||||
// Recovery
|
||||
update.character = CharacterState::BasicSummon(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
update.character = CharacterState::Wielding;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
update.character = CharacterState::Wielding;
|
||||
},
|
||||
}
|
||||
|
||||
update
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct SummonInfo {
|
||||
body: comp::Body,
|
||||
scale: Option<comp::Scale>,
|
||||
health_scaling: u16,
|
||||
loadout_config: Option<LoadoutConfig>,
|
||||
skillset_config: Option<SkillSetConfig>,
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
comp::{
|
||||
item::MaterialStatManifest, Beam, Body, CharacterState, Combo, ControlAction, Controller,
|
||||
ControllerInputs, Energy, Health, InputAttr, InputKind, Inventory, InventoryAction, Melee,
|
||||
Ori, PhysicsState, Pos, StateUpdate, Stats, Vel,
|
||||
self, item::MaterialStatManifest, Beam, Body, CharacterState, Combo, ControlAction,
|
||||
Controller, ControllerInputs, Energy, Health, InputAttr, InputKind, Inventory,
|
||||
InventoryAction, Melee, Ori, PhysicsState, Pos, StateUpdate, Stats, Vel,
|
||||
},
|
||||
resources::DeltaTime,
|
||||
uid::Uid,
|
||||
@ -34,11 +34,14 @@ pub trait CharacterBehavior {
|
||||
&self,
|
||||
data: &JoinData,
|
||||
input: InputKind,
|
||||
_target: Option<Uid>,
|
||||
target_entity: Option<Uid>,
|
||||
select_pos: Option<Vec3<f32>>,
|
||||
) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
update.queued_inputs.insert(input, InputAttr { select_pos });
|
||||
update.queued_inputs.insert(input, InputAttr {
|
||||
select_pos,
|
||||
target_entity,
|
||||
});
|
||||
update
|
||||
}
|
||||
fn cancel_input(&self, data: &JoinData, input: InputKind) -> StateUpdate {
|
||||
@ -60,9 +63,9 @@ pub trait CharacterBehavior {
|
||||
ControlAction::Talk => self.talk(data),
|
||||
ControlAction::StartInput {
|
||||
input,
|
||||
target,
|
||||
target_entity,
|
||||
select_pos,
|
||||
} => self.start_input(data, input, target, select_pos),
|
||||
} => self.start_input(data, input, target_entity, select_pos),
|
||||
ControlAction::CancelInput(input) => self.cancel_input(data, input),
|
||||
}
|
||||
}
|
||||
@ -90,6 +93,7 @@ pub struct JoinData<'a> {
|
||||
pub stats: &'a Stats,
|
||||
pub msm: &'a MaterialStatManifest,
|
||||
pub combo: &'a Combo,
|
||||
pub alignment: Option<&'a comp::Alignment>,
|
||||
}
|
||||
|
||||
type RestrictedMut<'a, C> = PairedStorage<
|
||||
@ -118,6 +122,7 @@ pub struct JoinStruct<'a> {
|
||||
pub beam: Option<&'a Beam>,
|
||||
pub stat: &'a Stats,
|
||||
pub combo: &'a Combo,
|
||||
pub alignment: Option<&'a comp::Alignment>,
|
||||
}
|
||||
|
||||
impl<'a> JoinData<'a> {
|
||||
@ -147,6 +152,7 @@ impl<'a> JoinData<'a> {
|
||||
dt,
|
||||
msm,
|
||||
combo: j.combo,
|
||||
alignment: j.alignment,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
98
common/src/states/blink.rs
Normal file
98
common/src/states/blink.rs
Normal file
@ -0,0 +1,98 @@
|
||||
use crate::{
|
||||
comp::{CharacterState, StateUpdate},
|
||||
event::ServerEvent,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
|
||||
/// Separated out to condense update portions of character state
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct StaticData {
|
||||
/// How long the state builds up for
|
||||
pub buildup_duration: Duration,
|
||||
/// How long the state recovers for
|
||||
pub recover_duration: Duration,
|
||||
/// What the max range of the teleport is
|
||||
pub max_range: f32,
|
||||
/// Miscellaneous information about the ability
|
||||
pub ability_info: AbilityInfo,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Data {
|
||||
/// Struct containing data that does not change over the course of the
|
||||
/// character state
|
||||
pub static_data: StaticData,
|
||||
/// Timer for each stage
|
||||
pub timer: Duration,
|
||||
/// What section the character stage is in
|
||||
pub stage_section: StageSection,
|
||||
}
|
||||
|
||||
impl CharacterBehavior for Data {
|
||||
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
|
||||
match self.stage_section {
|
||||
StageSection::Buildup => {
|
||||
if self.timer < self.static_data.buildup_duration {
|
||||
// Build up
|
||||
update.character = CharacterState::Blink(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Blinks to target location, defaults to 25 meters in front if no target
|
||||
// provided
|
||||
if let Some(input_attr) = self.static_data.ability_info.input_attr {
|
||||
if let Some(target) = input_attr.target_entity {
|
||||
update.server_events.push_front(ServerEvent::TeleportTo {
|
||||
entity: data.entity,
|
||||
target,
|
||||
max_range: Some(self.static_data.max_range),
|
||||
});
|
||||
} else if let Some(pos) = input_attr.select_pos {
|
||||
update.pos.0 = pos;
|
||||
} else {
|
||||
update.pos.0 += *data.inputs.look_dir * 25.0;
|
||||
}
|
||||
}
|
||||
// Transitions to recover section of stage
|
||||
update.character = CharacterState::Blink(Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Recover,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Recover => {
|
||||
if self.timer < self.static_data.recover_duration {
|
||||
// Recovery
|
||||
update.character = CharacterState::Blink(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
update.character = CharacterState::Wielding;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
update.character = CharacterState::Wielding;
|
||||
},
|
||||
}
|
||||
|
||||
update
|
||||
}
|
||||
}
|
@ -103,8 +103,8 @@ impl CharacterBehavior for Data {
|
||||
};
|
||||
// Gets offsets
|
||||
let body_offsets = Vec3::new(
|
||||
(data.body.radius() + 1.0) * data.inputs.look_dir.x,
|
||||
(data.body.radius() + 1.0) * data.inputs.look_dir.y,
|
||||
(data.body.radius() + 0.2) * data.inputs.look_dir.x,
|
||||
(data.body.radius() + 0.2) * data.inputs.look_dir.y,
|
||||
data.body.eye_height() * 0.6,
|
||||
);
|
||||
let pos = Pos(data.pos.0 + body_offsets);
|
||||
|
@ -3,7 +3,9 @@ pub mod basic_beam;
|
||||
pub mod basic_block;
|
||||
pub mod basic_melee;
|
||||
pub mod basic_ranged;
|
||||
pub mod basic_summon;
|
||||
pub mod behavior;
|
||||
pub mod blink;
|
||||
pub mod boost;
|
||||
pub mod charged_melee;
|
||||
pub mod charged_ranged;
|
||||
|
@ -1,12 +1,14 @@
|
||||
use crate::{
|
||||
combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement},
|
||||
combat::{
|
||||
Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement, Damage,
|
||||
DamageSource, GroupTarget, Knockback,
|
||||
},
|
||||
comp::{tool::ToolKind, CharacterState, EnergyChange, EnergySource, Melee, StateUpdate},
|
||||
consts::GRAVITY,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
},
|
||||
Damage, DamageSource, GroupTarget, Knockback, KnockbackDir,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
@ -26,9 +28,11 @@ pub struct StaticData {
|
||||
/// Base poise damage
|
||||
pub base_poise_damage: f32,
|
||||
/// Knockback
|
||||
pub knockback: f32,
|
||||
pub knockback: Knockback,
|
||||
/// Range
|
||||
pub range: f32,
|
||||
/// Adds an effect onto the main damage of the attack
|
||||
pub damage_effect: Option<CombatEffect>,
|
||||
/// Energy cost per attack
|
||||
pub energy_cost: f32,
|
||||
/// Whether spin state is infinite
|
||||
@ -41,8 +45,12 @@ pub struct StaticData {
|
||||
pub forward_speed: f32,
|
||||
/// Number of spins
|
||||
pub num_spins: u32,
|
||||
/// Used to determine targeting of attack
|
||||
pub target: Option<GroupTarget>,
|
||||
/// What key is used to press ability
|
||||
pub ability_info: AbilityInfo,
|
||||
/// Used to specify the melee attack to the frontend
|
||||
pub specifier: Option<FrontendSpecifier>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@ -65,7 +73,7 @@ impl CharacterBehavior for Data {
|
||||
let mut update = StateUpdate::from(data);
|
||||
|
||||
match self.static_data.movement_behavior {
|
||||
MovementBehavior::ForwardGround => {},
|
||||
MovementBehavior::ForwardGround | MovementBehavior::Stationary => {},
|
||||
MovementBehavior::AxeHover => {
|
||||
let new_vel_z = update.vel.0.z + GRAVITY * data.dt.0 * 0.5;
|
||||
update.vel.0 = Vec3::new(0.0, 0.0, new_vel_z) + data.inputs.move_dir * 5.0;
|
||||
@ -104,27 +112,29 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
|
||||
let poise = AttackEffect::new(
|
||||
Some(GroupTarget::OutOfGroup),
|
||||
self.static_data.target,
|
||||
CombatEffect::Poise(self.static_data.base_poise_damage as f32),
|
||||
)
|
||||
.with_requirement(CombatRequirement::AnyDamage);
|
||||
let knockback = AttackEffect::new(
|
||||
Some(GroupTarget::OutOfGroup),
|
||||
CombatEffect::Knockback(Knockback {
|
||||
strength: self.static_data.knockback,
|
||||
direction: KnockbackDir::Away,
|
||||
}),
|
||||
self.static_data.target,
|
||||
CombatEffect::Knockback(self.static_data.knockback),
|
||||
)
|
||||
.with_requirement(CombatRequirement::AnyDamage);
|
||||
let buff = CombatEffect::Buff(CombatBuff::default_physical());
|
||||
let damage = AttackDamage::new(
|
||||
let mut damage = AttackDamage::new(
|
||||
Damage {
|
||||
source: DamageSource::Melee,
|
||||
value: self.static_data.base_damage as f32,
|
||||
},
|
||||
Some(GroupTarget::OutOfGroup),
|
||||
)
|
||||
.with_effect(buff);
|
||||
self.static_data.target,
|
||||
);
|
||||
match self.static_data.damage_effect {
|
||||
Some(effect) => damage = damage.with_effect(effect),
|
||||
None => {
|
||||
let buff = CombatEffect::Buff(CombatBuff::default_physical());
|
||||
damage = damage.with_effect(buff);
|
||||
},
|
||||
}
|
||||
let (crit_chance, crit_mult) =
|
||||
get_crit_data(data, self.static_data.ability_info);
|
||||
let attack = Attack::default()
|
||||
@ -204,6 +214,8 @@ impl CharacterBehavior for Data {
|
||||
stage_section: StageSection::Recover,
|
||||
..*self
|
||||
});
|
||||
// Remove melee attack component
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
}
|
||||
},
|
||||
StageSection::Recover => {
|
||||
@ -242,7 +254,13 @@ impl CharacterBehavior for Data {
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum MovementBehavior {
|
||||
Stationary,
|
||||
ForwardGround,
|
||||
AxeHover,
|
||||
GolemHover,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum FrontendSpecifier {
|
||||
CultistVortex,
|
||||
}
|
||||
|
@ -5,7 +5,8 @@ use crate::{
|
||||
item::{Hands, ItemKind, Tool, ToolKind},
|
||||
quadruped_low, quadruped_medium, quadruped_small, ship,
|
||||
skills::{Skill, SwimSkill},
|
||||
theropod, Body, CharacterAbility, CharacterState, InputKind, InventoryAction, StateUpdate,
|
||||
theropod, Body, CharacterAbility, CharacterState, InputAttr, InputKind, InventoryAction,
|
||||
StateUpdate,
|
||||
},
|
||||
consts::{FRIC_GROUND, GRAVITY},
|
||||
event::{LocalEvent, ServerEvent},
|
||||
@ -523,11 +524,10 @@ fn handle_ability(data: &JoinData, update: &mut StateUpdate, input: InputKind) {
|
||||
})
|
||||
.filter(|ability| ability.requirements_paid(data, update))
|
||||
{
|
||||
update.character = (
|
||||
update.character = CharacterState::from((
|
||||
&ability,
|
||||
AbilityInfo::from_input(data, matches!(equip_slot, EquipSlot::Offhand), input),
|
||||
)
|
||||
.into();
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -578,40 +578,23 @@ pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) {
|
||||
})
|
||||
.filter(|ability| ability.requirements_paid(data, update))
|
||||
{
|
||||
update.character = CharacterState::from((
|
||||
&ability,
|
||||
AbilityInfo::from_input(data, false, InputKind::Roll),
|
||||
));
|
||||
if let CharacterState::ComboMelee(c) = data.character {
|
||||
update.character = (
|
||||
&ability,
|
||||
AbilityInfo::from_input(data, false, InputKind::Roll),
|
||||
)
|
||||
.into();
|
||||
if let CharacterState::Roll(roll) = &mut update.character {
|
||||
roll.was_combo = Some((c.static_data.ability_info.input, c.stage));
|
||||
roll.was_wielded = true;
|
||||
}
|
||||
} else if data.character.is_wield() {
|
||||
update.character = (
|
||||
&ability,
|
||||
AbilityInfo::from_input(data, false, InputKind::Roll),
|
||||
)
|
||||
.into();
|
||||
if let CharacterState::Roll(roll) = &mut update.character {
|
||||
roll.was_wielded = true;
|
||||
}
|
||||
} else if data.character.is_stealthy() {
|
||||
update.character = (
|
||||
&ability,
|
||||
AbilityInfo::from_input(data, false, InputKind::Roll),
|
||||
)
|
||||
.into();
|
||||
if let CharacterState::Roll(roll) = &mut update.character {
|
||||
roll.was_sneak = true;
|
||||
}
|
||||
} else {
|
||||
update.character = (
|
||||
&ability,
|
||||
AbilityInfo::from_input(data, false, InputKind::Roll),
|
||||
)
|
||||
.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -707,7 +690,7 @@ pub struct AbilityInfo {
|
||||
pub tool: Option<ToolKind>,
|
||||
pub hand: Option<HandInfo>,
|
||||
pub input: InputKind,
|
||||
pub select_pos: Option<Vec3<f32>>,
|
||||
pub input_attr: Option<InputAttr>,
|
||||
}
|
||||
|
||||
impl AbilityInfo {
|
||||
@ -730,13 +713,7 @@ impl AbilityInfo {
|
||||
tool,
|
||||
hand,
|
||||
input,
|
||||
select_pos: data
|
||||
.controller
|
||||
.queued_inputs
|
||||
.get(&input)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.select_pos,
|
||||
input_attr: data.controller.queued_inputs.get(&input).copied(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ pub struct ReadData<'a> {
|
||||
dt: Read<'a, DeltaTime>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
inventories: ReadStorage<'a, Inventory>,
|
||||
healths: ReadStorage<'a, Health>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -26,7 +27,6 @@ pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
ReadData<'a>,
|
||||
WriteStorage<'a, Health>,
|
||||
WriteStorage<'a, Energy>,
|
||||
WriteStorage<'a, Buffs>,
|
||||
WriteStorage<'a, Stats>,
|
||||
@ -38,22 +38,20 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
fn run(
|
||||
_job: &mut Job<Self>,
|
||||
(read_data, mut healths, mut energies, mut buffs, mut stats): Self::SystemData,
|
||||
(read_data, mut energies, mut buffs, mut stats): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
let dt = read_data.dt.0;
|
||||
// Set to false to avoid spamming server
|
||||
buffs.set_event_emission(false);
|
||||
healths.set_event_emission(false);
|
||||
energies.set_event_emission(false);
|
||||
healths.set_event_emission(false);
|
||||
stats.set_event_emission(false);
|
||||
for (entity, mut buff_comp, mut health, mut energy, mut stat) in (
|
||||
for (entity, mut buff_comp, mut energy, mut stat, health) in (
|
||||
&read_data.entities,
|
||||
&mut buffs,
|
||||
&mut healths,
|
||||
&mut energies,
|
||||
&mut stats,
|
||||
&read_data.healths,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
@ -89,12 +87,10 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
|
||||
// Call to reset health and energy to base values
|
||||
health.last_set();
|
||||
// Call to reset energy and stats to base values
|
||||
energy.last_set();
|
||||
health.reset_max();
|
||||
energy.reset_max();
|
||||
stat.damage_reduction = 0.0;
|
||||
stat.reset_temp_modifiers();
|
||||
|
||||
// Iterator over the lists of buffs by kind
|
||||
let buff_comp = &mut *buff_comp;
|
||||
@ -117,9 +113,9 @@ impl<'a> System<'a> for Sys {
|
||||
kind,
|
||||
} => {
|
||||
*accumulated += *rate * dt;
|
||||
// Apply health change only once a second or
|
||||
// Apply health change only once per second, per health, or
|
||||
// when a buff is removed
|
||||
if accumulated.abs() > rate.abs()
|
||||
if accumulated.abs() > rate.abs().min(10.0)
|
||||
|| buff.time.map_or(false, |dur| dur == Duration::default())
|
||||
{
|
||||
let cause = if *accumulated > 0.0 {
|
||||
@ -145,14 +141,10 @@ impl<'a> System<'a> for Sys {
|
||||
},
|
||||
BuffEffect::MaxHealthModifier { value, kind } => match kind {
|
||||
ModifierKind::Additive => {
|
||||
let health = &mut *health;
|
||||
let buffed_health_max =
|
||||
(health.maximum() as f32 + *value) as u32;
|
||||
health.set_maximum(buffed_health_max);
|
||||
stat.max_health_modifier += *value / (health.maximum() as f32);
|
||||
},
|
||||
ModifierKind::Fractional => {
|
||||
let health = &mut *health;
|
||||
health.set_maximum((health.maximum() as f32 * *value) as u32);
|
||||
stat.max_health_modifier *= *value;
|
||||
},
|
||||
},
|
||||
BuffEffect::MaxEnergyModifier { value, kind } => match kind {
|
||||
@ -168,6 +160,35 @@ impl<'a> System<'a> for Sys {
|
||||
BuffEffect::DamageReduction(dr) => {
|
||||
stat.damage_reduction = stat.damage_reduction.max(*dr).min(1.0);
|
||||
},
|
||||
BuffEffect::MaxHealthChangeOverTime {
|
||||
rate,
|
||||
accumulated,
|
||||
kind,
|
||||
target_fraction,
|
||||
} => {
|
||||
*accumulated += *rate * dt;
|
||||
let current_fraction = health.maximum() as f32
|
||||
/ (health.base_max() as f32 * stat.max_health_modifier);
|
||||
let progress = (1.0 - current_fraction) / (1.0 - *target_fraction);
|
||||
if progress > 1.0 {
|
||||
stat.max_health_modifier *= *target_fraction;
|
||||
} else if accumulated.abs() > rate.abs() {
|
||||
match kind {
|
||||
ModifierKind::Additive => {
|
||||
stat.max_health_modifier = stat.max_health_modifier
|
||||
* current_fraction
|
||||
+ *accumulated / health.maximum() as f32;
|
||||
},
|
||||
ModifierKind::Fractional => {
|
||||
stat.max_health_modifier *=
|
||||
current_fraction * (1.0 - *accumulated);
|
||||
},
|
||||
}
|
||||
*accumulated = 0.0;
|
||||
} else {
|
||||
stat.max_health_modifier *= current_fraction;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -195,7 +216,6 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
// Turned back to true
|
||||
buffs.set_event_emission(true);
|
||||
healths.set_event_emission(true);
|
||||
energies.set_event_emission(true);
|
||||
stats.set_event_emission(true);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ use specs::{
|
||||
|
||||
use common::{
|
||||
comp::{
|
||||
self,
|
||||
inventory::{
|
||||
item::MaterialStatManifest,
|
||||
slot::{EquipSlot, Slot},
|
||||
@ -71,6 +72,7 @@ pub struct ReadData<'a> {
|
||||
stats: ReadStorage<'a, Stats>,
|
||||
msm: Read<'a, MaterialStatManifest>,
|
||||
combos: ReadStorage<'a, Combo>,
|
||||
alignments: ReadStorage<'a, comp::Alignment>,
|
||||
}
|
||||
|
||||
/// ## Character Behavior System
|
||||
@ -255,6 +257,7 @@ impl<'a> System<'a> for Sys {
|
||||
beam: read_data.beams.get(entity),
|
||||
stat: &stat,
|
||||
combo: &combo,
|
||||
alignment: read_data.alignments.get(entity),
|
||||
};
|
||||
|
||||
for action in actions {
|
||||
@ -302,6 +305,8 @@ impl<'a> System<'a> for Sys {
|
||||
CharacterState::BasicBeam(data) => data.handle_event(&j, action),
|
||||
CharacterState::BasicAura(data) => data.handle_event(&j, action),
|
||||
CharacterState::HealingBeam(data) => data.handle_event(&j, action),
|
||||
CharacterState::Blink(data) => data.handle_event(&j, action),
|
||||
CharacterState::BasicSummon(data) => data.handle_event(&j, action),
|
||||
};
|
||||
local_emitter.append(&mut state_update.local_events);
|
||||
server_emitter.append(&mut state_update.server_events);
|
||||
@ -354,6 +359,8 @@ impl<'a> System<'a> for Sys {
|
||||
CharacterState::BasicBeam(data) => data.behavior(&j),
|
||||
CharacterState::BasicAura(data) => data.behavior(&j),
|
||||
CharacterState::HealingBeam(data) => data.behavior(&j),
|
||||
CharacterState::Blink(data) => data.behavior(&j),
|
||||
CharacterState::BasicSummon(data) => data.behavior(&j),
|
||||
};
|
||||
|
||||
local_emitter.append(&mut state_update.local_events);
|
||||
|
@ -8,6 +8,7 @@ use common::{
|
||||
comp,
|
||||
depot::{Depot, Id},
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
outcome::Outcome,
|
||||
region::RegionMap,
|
||||
resources::{DeltaTime, GameMode, PlayerEntity, Time, TimeOfDay},
|
||||
slowjob::SlowJobPool,
|
||||
@ -509,6 +510,9 @@ impl State {
|
||||
vel.0 += extra_vel;
|
||||
}
|
||||
},
|
||||
LocalEvent::CreateOutcome(outcome) => {
|
||||
self.ecs.write_resource::<Vec<Outcome>>().push(outcome);
|
||||
},
|
||||
}
|
||||
}
|
||||
drop(guard);
|
||||
|
@ -104,6 +104,18 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
let stat = stats.get_unchecked();
|
||||
|
||||
let update_max_hp = {
|
||||
let health = health.get_unchecked();
|
||||
(stat.max_health_modifier - 1.0).abs() > f32::EPSILON
|
||||
|| health.base_max() != health.maximum()
|
||||
};
|
||||
|
||||
if update_max_hp {
|
||||
let mut health = health.get_mut_unchecked();
|
||||
health.scale_maximum(stat.max_health_modifier);
|
||||
}
|
||||
|
||||
let skills_to_level = stat
|
||||
.skill_set
|
||||
.skill_groups
|
||||
@ -234,7 +246,9 @@ impl<'a> System<'a> for Sys {
|
||||
| CharacterState::Shockwave { .. }
|
||||
| CharacterState::BasicBeam { .. }
|
||||
| CharacterState::BasicAura { .. }
|
||||
| CharacterState::HealingBeam { .. } => {
|
||||
| CharacterState::HealingBeam { .. }
|
||||
| CharacterState::Blink { .. }
|
||||
| CharacterState::BasicSummon { .. } => {
|
||||
if energy.get_unchecked().regen_rate != 0.0 {
|
||||
energy.get_mut_unchecked().regen_rate = 0.0
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{sys, Server, StateExt};
|
||||
use crate::{client::Client, sys, Server, StateExt};
|
||||
use common::{
|
||||
character::CharacterId,
|
||||
comp::{
|
||||
@ -6,15 +6,16 @@ use common::{
|
||||
aura::{Aura, AuraKind, AuraTarget},
|
||||
beam,
|
||||
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
||||
group,
|
||||
inventory::loadout::Loadout,
|
||||
shockwave, Agent, Alignment, Body, Gravity, Health, HomeChunk, Inventory, Item, ItemDrop,
|
||||
LightEmitter, Object, Ori, Poise, Pos, Projectile, Scale, Stats, Vel, WaypointArea,
|
||||
},
|
||||
outcome::Outcome,
|
||||
rtsim::RtSimEntity,
|
||||
uid::Uid,
|
||||
util::Dir,
|
||||
};
|
||||
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
|
||||
use specs::{Builder, Entity as EcsEntity, WorldExt};
|
||||
use std::time::Duration;
|
||||
use vek::{Rgb, Vec3};
|
||||
@ -59,15 +60,6 @@ pub fn handle_create_npc(
|
||||
home_chunk: Option<HomeChunk>,
|
||||
rtsim_entity: Option<RtSimEntity>,
|
||||
) {
|
||||
let group = match alignment {
|
||||
Alignment::Wild => None,
|
||||
Alignment::Passive => None,
|
||||
Alignment::Enemy => Some(group::ENEMY),
|
||||
Alignment::Npc | Alignment::Tame => Some(group::NPC),
|
||||
// TODO: handle
|
||||
Alignment::Owned(_) => None,
|
||||
};
|
||||
|
||||
let inventory = Inventory::new_with_loadout(loadout);
|
||||
|
||||
let entity = server
|
||||
@ -76,12 +68,6 @@ pub fn handle_create_npc(
|
||||
.with(scale)
|
||||
.with(alignment);
|
||||
|
||||
let entity = if let Some(group) = group {
|
||||
entity.with(group)
|
||||
} else {
|
||||
entity
|
||||
};
|
||||
|
||||
let entity = if let Some(agent) = agent.into() {
|
||||
entity.with(agent)
|
||||
} else {
|
||||
@ -106,7 +92,45 @@ pub fn handle_create_npc(
|
||||
entity
|
||||
};
|
||||
|
||||
entity.build();
|
||||
let new_entity = entity.build();
|
||||
|
||||
// Add to group system if a pet
|
||||
if let comp::Alignment::Owned(owner_uid) = alignment {
|
||||
let state = server.state();
|
||||
let clients = state.ecs().read_storage::<Client>();
|
||||
let uids = state.ecs().read_storage::<Uid>();
|
||||
let mut group_manager = state.ecs().write_resource::<comp::group::GroupManager>();
|
||||
if let Some(owner) = state.ecs().entity_from_uid(owner_uid.into()) {
|
||||
group_manager.new_pet(
|
||||
new_entity,
|
||||
owner,
|
||||
&mut state.ecs().write_storage(),
|
||||
&state.ecs().entities(),
|
||||
&state.ecs().read_storage(),
|
||||
&uids,
|
||||
&mut |entity, group_change| {
|
||||
clients
|
||||
.get(entity)
|
||||
.and_then(|c| {
|
||||
group_change
|
||||
.try_map(|e| uids.get(e).copied())
|
||||
.map(|g| (g, c))
|
||||
})
|
||||
.map(|(g, c)| {
|
||||
c.send_fallible(ServerGeneral::GroupUpdate(g));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if let Some(group) = match alignment {
|
||||
comp::Alignment::Wild => None,
|
||||
comp::Alignment::Passive => None,
|
||||
comp::Alignment::Enemy => Some(comp::group::ENEMY),
|
||||
comp::Alignment::Npc | comp::Alignment::Tame => Some(comp::group::NPC),
|
||||
comp::Alignment::Owned(_) => unreachable!(),
|
||||
} {
|
||||
let _ = server.state.ecs().write_storage().insert(new_entity, group);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -329,7 +329,14 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
|
||||
.insert(entity, comp::CharacterState::default());
|
||||
|
||||
false
|
||||
} else if state.ecs().read_storage::<comp::Agent>().contains(entity) {
|
||||
} else if state.ecs().read_storage::<comp::Agent>().contains(entity)
|
||||
&& !matches!(
|
||||
state.ecs().read_storage::<comp::Alignment>().get(entity),
|
||||
Some(comp::Alignment::Owned(_))
|
||||
)
|
||||
{
|
||||
// Only drop loot if entity has agency (not a player), and if it is not owned by
|
||||
// another entity (not a pet)
|
||||
use specs::Builder;
|
||||
|
||||
// Decide for a loot drop before turning into a lootbag
|
||||
@ -941,3 +948,28 @@ pub fn handle_combo_change(server: &Server, entity: EcsEntity, change: i32) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_teleport_to(server: &Server, entity: EcsEntity, target: Uid, max_range: Option<f32>) {
|
||||
let ecs = &server.state.ecs();
|
||||
let mut positions = ecs.write_storage::<Pos>();
|
||||
|
||||
let target_pos = ecs
|
||||
.entity_from_uid(target.into())
|
||||
.and_then(|e| positions.get(e))
|
||||
.copied();
|
||||
|
||||
if let (Some(pos), Some(target_pos)) = (positions.get_mut(entity), target_pos) {
|
||||
if max_range.map_or(true, |r| pos.0.distance_squared(target_pos.0) < r.powi(2)) {
|
||||
*pos = target_pos;
|
||||
ecs.write_storage()
|
||||
.insert(entity, comp::ForceUpdate)
|
||||
.err()
|
||||
.map(|e| {
|
||||
error!(
|
||||
?e,
|
||||
"Error inserting ForceUpdate component when teleporting client"
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use entity_creation::{
|
||||
use entity_manipulation::{
|
||||
handle_aura, handle_buff, handle_combo_change, handle_damage, handle_delete, handle_destroy,
|
||||
handle_energy_change, handle_explosion, handle_knockback, handle_land_on_ground, handle_poise,
|
||||
handle_respawn,
|
||||
handle_respawn, handle_teleport_to,
|
||||
};
|
||||
use group_manip::handle_group;
|
||||
use information::handle_site_info;
|
||||
@ -199,6 +199,11 @@ impl Server {
|
||||
},
|
||||
ServerEvent::RequestSiteInfo { entity, id } => handle_site_info(&self, entity, id),
|
||||
ServerEvent::MineBlock { pos, tool } => handle_mine_block(self, pos, tool),
|
||||
ServerEvent::TeleportTo {
|
||||
entity,
|
||||
target,
|
||||
max_range,
|
||||
} => handle_teleport_to(&self, entity, target, max_range),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,8 @@ struct AgentData<'a> {
|
||||
light_emitter: Option<&'a LightEmitter>,
|
||||
glider_equipped: bool,
|
||||
is_gliding: bool,
|
||||
health: Option<&'a Health>,
|
||||
char_state: &'a CharacterState,
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
@ -145,14 +147,17 @@ impl<'a> System<'a> for Sys {
|
||||
read_data.light_emitter.maybe(),
|
||||
read_data.groups.maybe(),
|
||||
read_data.mount_states.maybe(),
|
||||
&read_data.char_states,
|
||||
)
|
||||
.par_join()
|
||||
.filter(|(_, _, _, _, _, _, _, _, _, _, _, _, _, _, mount_state)| {
|
||||
// Skip mounted entities
|
||||
mount_state
|
||||
.map(|ms| *ms == MountState::Unmounted)
|
||||
.unwrap_or(true)
|
||||
})
|
||||
.filter(
|
||||
|(_, _, _, _, _, _, _, _, _, _, _, _, _, _, mount_state, _)| {
|
||||
// Skip mounted entities
|
||||
mount_state
|
||||
.map(|ms| *ms == MountState::Unmounted)
|
||||
.unwrap_or(true)
|
||||
},
|
||||
)
|
||||
.for_each_init(
|
||||
|| {
|
||||
prof_span!(guard, "agent rayon job");
|
||||
@ -175,6 +180,7 @@ impl<'a> System<'a> for Sys {
|
||||
light_emitter,
|
||||
groups,
|
||||
_,
|
||||
char_state,
|
||||
)| {
|
||||
//// Hack, replace with better system when groups are more sophisticated
|
||||
//// Override alignment if in a group unless entity is owned already
|
||||
@ -269,6 +275,8 @@ impl<'a> System<'a> for Sys {
|
||||
light_emitter,
|
||||
glider_equipped,
|
||||
is_gliding,
|
||||
health: read_data.healths.get(entity),
|
||||
char_state,
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
@ -363,6 +371,7 @@ impl<'a> System<'a> for Sys {
|
||||
tgt_pos,
|
||||
read_data.bodies.get(attacker),
|
||||
&read_data.dt,
|
||||
&read_data,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -443,6 +452,7 @@ impl<'a> System<'a> for Sys {
|
||||
tgt_pos,
|
||||
read_data.bodies.get(attacker),
|
||||
&read_data.dt,
|
||||
&read_data,
|
||||
);
|
||||
// Remember this encounter if an RtSim entity
|
||||
if let Some(tgt_stats) =
|
||||
@ -604,6 +614,7 @@ impl<'a> AgentData<'a> {
|
||||
tgt_pos,
|
||||
read_data.bodies.get(target),
|
||||
&read_data.dt,
|
||||
&read_data,
|
||||
);
|
||||
} else {
|
||||
agent.target = None;
|
||||
@ -1104,7 +1115,7 @@ impl<'a> AgentData<'a> {
|
||||
|| e_pos.0.distance_squared(self.pos.0) < listen_dist.powi(2)) // TODO implement proper sound system for agents
|
||||
&& e != self.entity
|
||||
&& !e_health.is_dead
|
||||
&& (self.alignment.and_then(|a| e_alignment.map(|b| a.hostile_towards(*b))).unwrap_or(false) || (
|
||||
&& (try_owner_alignment(self.alignment, &read_data).and_then(|a| try_owner_alignment(*e_alignment, &read_data).map(|b| a.hostile_towards(*b))).unwrap_or(false) || (
|
||||
if let Some(rtsim_entity) = &self.rtsim_entity {
|
||||
if rtsim_entity.brain.remembers_fight_with_character(&e_stats.name) {
|
||||
agent.rtsim_controller.events.push(
|
||||
@ -1177,6 +1188,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn attack(
|
||||
&self,
|
||||
agent: &mut Agent,
|
||||
@ -1185,6 +1197,7 @@ impl<'a> AgentData<'a> {
|
||||
tgt_pos: &Pos,
|
||||
tgt_body: Option<&Body>,
|
||||
dt: &DeltaTime,
|
||||
read_data: &ReadData,
|
||||
) {
|
||||
let min_attack_dist = self.body.map_or(3.0, |b| b.radius() * self.scale + 2.0);
|
||||
let tactic = match self
|
||||
@ -1231,6 +1244,7 @@ impl<'a> AgentData<'a> {
|
||||
Some(ToolKind::Unique(UniqueKind::TheropodBasic)) => Tactic::Theropod,
|
||||
Some(ToolKind::Unique(UniqueKind::TheropodBird)) => Tactic::Theropod,
|
||||
Some(ToolKind::Unique(UniqueKind::ObjectTurret)) => Tactic::Turret,
|
||||
Some(ToolKind::Unique(UniqueKind::MindflayerStaff)) => Tactic::Mindflayer,
|
||||
_ => Tactic::Melee,
|
||||
};
|
||||
|
||||
@ -1288,7 +1302,7 @@ impl<'a> AgentData<'a> {
|
||||
// depending on the distance from the agent to the target
|
||||
match tactic {
|
||||
Tactic::Melee => {
|
||||
if dist_sqrd < (min_attack_dist * self.scale).powi(2) {
|
||||
if dist_sqrd < min_attack_dist.powi(2) {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
@ -1323,7 +1337,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
Tactic::Axe => {
|
||||
if dist_sqrd < (min_attack_dist * self.scale).powi(2) {
|
||||
if dist_sqrd < min_attack_dist.powi(2) {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
if agent.action_timer > 6.0 {
|
||||
controller
|
||||
@ -1381,7 +1395,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
Tactic::Hammer => {
|
||||
if dist_sqrd < (min_attack_dist * self.scale).powi(2) {
|
||||
if dist_sqrd < min_attack_dist.powi(2) {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
if agent.action_timer > 4.0 {
|
||||
controller
|
||||
@ -1457,7 +1471,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
Tactic::Sword => {
|
||||
if dist_sqrd < (min_attack_dist * self.scale).powi(2) {
|
||||
if dist_sqrd < min_attack_dist.powi(2) {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
if self
|
||||
.stats
|
||||
@ -1521,7 +1535,7 @@ impl<'a> AgentData<'a> {
|
||||
},
|
||||
Tactic::Bow => {
|
||||
if self.body.map(|b| b.is_humanoid()).unwrap_or(false)
|
||||
&& dist_sqrd < (2.0 * min_attack_dist * self.scale).powi(2)
|
||||
&& dist_sqrd < (2.0 * min_attack_dist).powi(2)
|
||||
{
|
||||
controller
|
||||
.actions
|
||||
@ -1614,12 +1628,12 @@ impl<'a> AgentData<'a> {
|
||||
},
|
||||
Tactic::Staff => {
|
||||
if self.body.map(|b| b.is_humanoid()).unwrap_or(false)
|
||||
&& dist_sqrd < (min_attack_dist * self.scale).powi(2)
|
||||
&& dist_sqrd < min_attack_dist.powi(2)
|
||||
{
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Roll));
|
||||
} else if dist_sqrd < (5.0 * min_attack_dist * self.scale).powi(2) {
|
||||
} else if dist_sqrd < (5.0 * min_attack_dist).powi(2) {
|
||||
if agent.action_timer < 1.5 {
|
||||
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
|
||||
.xy()
|
||||
@ -1713,7 +1727,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
Tactic::StoneGolemBoss => {
|
||||
if dist_sqrd < (min_attack_dist * self.scale).powi(2) {
|
||||
if dist_sqrd < min_attack_dist.powi(2) {
|
||||
// 2.0 is temporary correction factor to allow them to melee with their
|
||||
// large hitbox
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
@ -1763,19 +1777,18 @@ impl<'a> AgentData<'a> {
|
||||
radius,
|
||||
circle_time,
|
||||
} => {
|
||||
if dist_sqrd < (min_attack_dist * self.scale).powi(2) && thread_rng().gen_bool(0.5)
|
||||
{
|
||||
if dist_sqrd < min_attack_dist.powi(2) && thread_rng().gen_bool(0.5) {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
} else if dist_sqrd < (radius as f32 * min_attack_dist * self.scale).powi(2) {
|
||||
} else if dist_sqrd < (radius as f32 * min_attack_dist).powi(2) {
|
||||
controller.inputs.move_dir = (self.pos.0 - tgt_pos.0)
|
||||
.xy()
|
||||
.try_normalized()
|
||||
.unwrap_or_else(Vec2::unit_y);
|
||||
} else if dist_sqrd < ((radius as f32 + 1.0) * min_attack_dist * self.scale).powi(2)
|
||||
&& dist_sqrd > (radius as f32 * min_attack_dist * self.scale).powi(2)
|
||||
} else if dist_sqrd < ((radius as f32 + 1.0) * min_attack_dist).powi(2)
|
||||
&& dist_sqrd > (radius as f32 * min_attack_dist).powi(2)
|
||||
{
|
||||
if agent.action_timer < circle_time as f32 {
|
||||
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
|
||||
@ -1825,7 +1838,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
Tactic::QuadLowRanged => {
|
||||
if dist_sqrd < (3.0 * min_attack_dist * self.scale).powi(2) {
|
||||
if dist_sqrd < (3.0 * min_attack_dist).powi(2) {
|
||||
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
|
||||
.xy()
|
||||
.try_normalized()
|
||||
@ -1883,7 +1896,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
Tactic::TailSlap => {
|
||||
if dist_sqrd < (1.5 * min_attack_dist * self.scale).powi(2) {
|
||||
if dist_sqrd < (1.5 * min_attack_dist).powi(2) {
|
||||
if agent.action_timer > 4.0 {
|
||||
controller
|
||||
.actions
|
||||
@ -1926,13 +1939,13 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
Tactic::QuadLowQuick => {
|
||||
if dist_sqrd < (1.5 * min_attack_dist * self.scale).powi(2) {
|
||||
if dist_sqrd < (1.5 * min_attack_dist).powi(2) {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
} else if dist_sqrd < (3.0 * min_attack_dist * self.scale).powi(2)
|
||||
&& dist_sqrd > (2.0 * min_attack_dist * self.scale).powi(2)
|
||||
} else if dist_sqrd < (3.0 * min_attack_dist).powi(2)
|
||||
&& dist_sqrd > (2.0 * min_attack_dist).powi(2)
|
||||
{
|
||||
controller
|
||||
.actions
|
||||
@ -1963,7 +1976,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
Tactic::QuadLowBasic => {
|
||||
if dist_sqrd < (1.5 * min_attack_dist * self.scale).powi(2) {
|
||||
if dist_sqrd < (1.5 * min_attack_dist).powi(2) {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
if agent.action_timer > 5.0 {
|
||||
agent.action_timer = 0.0;
|
||||
@ -1999,12 +2012,12 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
Tactic::QuadMedJump => {
|
||||
if dist_sqrd < (1.5 * min_attack_dist * self.scale).powi(2) {
|
||||
if dist_sqrd < (1.5 * min_attack_dist).powi(2) {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
} else if dist_sqrd < (5.0 * min_attack_dist * self.scale).powi(2) {
|
||||
} else if dist_sqrd < (5.0 * min_attack_dist).powi(2) {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
@ -2037,7 +2050,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
Tactic::QuadMedBasic => {
|
||||
if dist_sqrd < (min_attack_dist * self.scale).powi(2) {
|
||||
if dist_sqrd < min_attack_dist.powi(2) {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
if agent.action_timer < 2.0 {
|
||||
controller
|
||||
@ -2073,12 +2086,12 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
Tactic::Lavadrake | Tactic::QuadLowBeam => {
|
||||
if dist_sqrd < (2.5 * min_attack_dist * self.scale).powi(2) {
|
||||
if dist_sqrd < (2.5 * min_attack_dist).powi(2) {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
} else if dist_sqrd < (7.0 * min_attack_dist * self.scale).powi(2) {
|
||||
} else if dist_sqrd < (7.0 * min_attack_dist).powi(2) {
|
||||
if agent.action_timer < 2.0 {
|
||||
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
|
||||
.xy()
|
||||
@ -2128,7 +2141,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
},
|
||||
Tactic::Theropod => {
|
||||
if dist_sqrd < (2.0 * min_attack_dist * self.scale).powi(2) {
|
||||
if dist_sqrd < (2.0 * min_attack_dist).powi(2) {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
controller
|
||||
.actions
|
||||
@ -2188,6 +2201,61 @@ impl<'a> AgentData<'a> {
|
||||
agent.target = None;
|
||||
}
|
||||
},
|
||||
Tactic::Mindflayer => {
|
||||
const MINDFLAYER_ATTACK_DIST: f32 = 17.5;
|
||||
const MINION_SUMMON_THRESHOLD: f32 = 0.20;
|
||||
let health_fraction = self.health.map_or(0.5, |h| h.fraction());
|
||||
// Extreme hack to set action_timer at start of combat
|
||||
if agent.action_timer < MINION_SUMMON_THRESHOLD
|
||||
&& health_fraction > MINION_SUMMON_THRESHOLD
|
||||
{
|
||||
agent.action_timer = health_fraction - MINION_SUMMON_THRESHOLD;
|
||||
}
|
||||
let mindflayer_is_far = dist_sqrd > MINDFLAYER_ATTACK_DIST.powi(2);
|
||||
if agent.action_timer > health_fraction {
|
||||
// Summon minions at particular thresholds of health
|
||||
if !self.char_state.is_attack() {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(1)));
|
||||
agent.action_timer -= MINION_SUMMON_THRESHOLD;
|
||||
}
|
||||
} else if mindflayer_is_far {
|
||||
// If too far from target, blink to them.
|
||||
controller.actions.push(ControlAction::StartInput {
|
||||
input: InputKind::Ability(0),
|
||||
target_entity: agent
|
||||
.target
|
||||
.as_ref()
|
||||
.and_then(|t| read_data.uids.get(t.target))
|
||||
.copied(),
|
||||
select_pos: None,
|
||||
});
|
||||
} else {
|
||||
// If close to target, use either primary or secondary ability
|
||||
if matches!(self.char_state, CharacterState::BasicBeam(_)) {
|
||||
// If already using primary, keep using primary
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
} else if matches!(self.char_state, CharacterState::SpinMelee(_)) {
|
||||
// If already using secondary, keep using secondary
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
} else if thread_rng().gen_bool(health_fraction.into()) {
|
||||
// Else if at high health, use primary
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
} else {
|
||||
// Else use secondary
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -2232,3 +2300,19 @@ fn should_stop_attacking(health: Option<&Health>, buffs: Option<&Buffs>) -> bool
|
||||
health.map_or(true, |a| a.is_dead)
|
||||
|| buffs.map_or(false, |b| b.kinds.contains_key(&BuffKind::Invulnerability))
|
||||
}
|
||||
|
||||
/// Attempts to get alignment of owner if entity has Owned alignment
|
||||
fn try_owner_alignment<'a>(
|
||||
alignment: Option<&'a Alignment>,
|
||||
read_data: &'a ReadData,
|
||||
) -> Option<&'a Alignment> {
|
||||
if let Some(Alignment::Owned(owner_uid)) = alignment {
|
||||
if let Some(owner) = read_data
|
||||
.uid_allocator
|
||||
.retrieve_entity_internal(owner_uid.id())
|
||||
{
|
||||
return read_data.alignments.get(owner);
|
||||
}
|
||||
}
|
||||
alignment
|
||||
}
|
||||
|
@ -76,8 +76,8 @@ impl Animation for AlphaAnimation {
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.hand_l.position = Vec3::new(0.0, 0.0, s_a.grip);
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, s_a.grip);
|
||||
next.hand_l.position = Vec3::new(0.0, 0.0, s_a.grip.0);
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, s_a.grip.0);
|
||||
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
@ -90,8 +90,8 @@ impl Animation for AlphaAnimation {
|
||||
* Quaternion::rotation_z(move1 * -0.2 + move2 * 0.6);
|
||||
next.control.position = Vec3::new(
|
||||
-3.0 + move1 * -4.0 + move2 * 5.0,
|
||||
5.0 + s_a.grip / 1.2 + move1 * -4.0 + move2 * 8.0,
|
||||
-4.0 + -s_a.grip / 2.0 + move2 * -5.0,
|
||||
5.0 + s_a.grip.0 / 1.2 + move1 * -4.0 + move2 * 8.0,
|
||||
-4.0 + -s_a.grip.0 / 2.0 + move2 * -5.0,
|
||||
);
|
||||
next.upper_torso.orientation = Quaternion::rotation_z(move1 * 0.5 + move2 * -0.7);
|
||||
next.lower_torso.orientation = Quaternion::rotation_z(move1 * -0.5 + move2 * 0.7);
|
||||
@ -113,8 +113,8 @@ impl Animation for AlphaAnimation {
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
4.0 + move1 * -12.0 + move2 * 20.0,
|
||||
(s_a.grip / 1.0) + move1 * -3.0 + move2 * 5.0,
|
||||
(-s_a.grip / 0.8) + move1 * -2.0 + move2 * 8.0,
|
||||
(s_a.grip.0 / 1.0) + move1 * -3.0 + move2 * 5.0,
|
||||
(-s_a.grip.0 / 0.8) + move1 * -2.0 + move2 * 8.0,
|
||||
);
|
||||
next.head.orientation = Quaternion::rotation_x(move1 * -0.25)
|
||||
* Quaternion::rotation_z(move1 * -0.2 + move2 * 0.6);
|
||||
@ -138,8 +138,8 @@ impl Animation for AlphaAnimation {
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
4.0 + move1 * -12.0 + move2 * 28.0,
|
||||
(s_a.grip / 1.0) + move1 * -3.0 + move2 * -5.0,
|
||||
(-s_a.grip / 0.8) + move1 * 2.0 + move2 * 8.0,
|
||||
(s_a.grip.0 / 1.0) + move1 * -3.0 + move2 * -5.0,
|
||||
(-s_a.grip.0 / 0.8) + move1 * 2.0 + move2 * 8.0,
|
||||
);
|
||||
next.head.orientation = Quaternion::rotation_x(move1 * -0.25)
|
||||
* Quaternion::rotation_z(move1 * -0.2 + move2 * 0.6);
|
||||
|
@ -2,7 +2,10 @@ use super::{
|
||||
super::{vek::*, Animation},
|
||||
BipedLargeSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::{comp::item::ToolKind, states::utils::StageSection};
|
||||
use common::{
|
||||
comp::item::{ToolKind, UniqueKind},
|
||||
states::utils::StageSection,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct BeamAnimation;
|
||||
@ -53,23 +56,25 @@ impl Animation for BeamAnimation {
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.hand_l.position = Vec3::new(0.0, 0.0, s_a.grip);
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, s_a.grip);
|
||||
next.hand_l.position = Vec3::new(s_a.grip.1, 0.0, s_a.grip.0);
|
||||
next.hand_r.position = Vec3::new(-s_a.grip.1, 0.0, s_a.grip.0);
|
||||
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
match active_tool_kind {
|
||||
Some(ToolKind::StaffSimple) | Some(ToolKind::Sceptre) => {
|
||||
Some(ToolKind::StaffSimple)
|
||||
| Some(ToolKind::Sceptre)
|
||||
| Some(ToolKind::Unique(UniqueKind::MindflayerStaff)) => {
|
||||
let (move1base, move2shake, _move2base, move3) = match stage_section {
|
||||
Some(StageSection::Buildup) => (
|
||||
(anim_time.powf(0.25)).min(1.0),
|
||||
(anim_time * 10.0 + PI).sin(),
|
||||
(anim_time * 15.0 + PI).sin(),
|
||||
(anim_time * 10.0 + PI).sin(),
|
||||
0.0,
|
||||
),
|
||||
Some(StageSection::Cast) => (
|
||||
1.0,
|
||||
(anim_time * 10.0 + PI).sin(),
|
||||
(anim_time * 15.0 + PI).sin(),
|
||||
anim_time.powf(0.25),
|
||||
0.0,
|
||||
),
|
||||
@ -80,14 +85,15 @@ impl Animation for BeamAnimation {
|
||||
let move1 = move1base * pullback;
|
||||
next.control_l.position = Vec3::new(-1.0, 3.0, 12.0);
|
||||
next.control_r.position =
|
||||
Vec3::new(1.0 + move1 * 5.0, 2.0 + move1 * 1.0, 2.0 + move1 * 8.0);
|
||||
Vec3::new(1.0 + move1 * 5.0, 2.0 + move1 * 1.0, 2.0 + move1 * 14.0);
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
-3.0 + move1 * 5.0,
|
||||
3.0 + s_a.grip / 1.2 + move1 * 5.0 + move2shake * 1.0,
|
||||
-11.0 + -s_a.grip / 2.0 + move1 * -4.0,
|
||||
-3.0 + move1 * -5.0,
|
||||
3.0 + s_a.grip.0 / 1.2 + move1 * 3.0 + move2shake * 1.0,
|
||||
-11.0 + -s_a.grip.0 / 2.0 + move1 * -2.0,
|
||||
);
|
||||
next.head.orientation = Quaternion::rotation_x(move1 * -0.2);
|
||||
next.head.orientation =
|
||||
Quaternion::rotation_x(move1 * -0.2) * Quaternion::rotation_y(move1 * 0.2);
|
||||
next.jaw.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.control_l.orientation =
|
||||
@ -96,8 +102,8 @@ impl Animation for BeamAnimation {
|
||||
* Quaternion::rotation_y(0.5)
|
||||
* Quaternion::rotation_z(move1 * 1.2 + move2shake * 0.5);
|
||||
|
||||
next.control.orientation = Quaternion::rotation_x(-0.2 + move1 * -0.2)
|
||||
* Quaternion::rotation_y(-0.1 + move1 * -0.4);
|
||||
next.control.orientation = Quaternion::rotation_x(-0.2 + move1 * -0.1)
|
||||
* Quaternion::rotation_y(-0.1 + move1 * 0.6);
|
||||
next.shoulder_l.position = Vec3::new(
|
||||
-s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
|
@ -75,8 +75,8 @@ impl Animation for BetaAnimation {
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.hand_l.position = Vec3::new(0.0, 0.0, s_a.grip);
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, s_a.grip);
|
||||
next.hand_l.position = Vec3::new(0.0, 0.0, s_a.grip.0);
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, s_a.grip.0);
|
||||
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
@ -89,8 +89,8 @@ impl Animation for BetaAnimation {
|
||||
* Quaternion::rotation_z(move1 * -0.2 + move2 * 0.6);
|
||||
next.control.position = Vec3::new(
|
||||
-3.0 + move1 * -4.0 + move2 * 5.0,
|
||||
5.0 + s_a.grip / 1.2 + move1 * -4.0 + move2 * 8.0,
|
||||
-4.0 + -s_a.grip / 2.0 + move2 * -5.0,
|
||||
5.0 + s_a.grip.0 / 1.2 + move1 * -4.0 + move2 * 8.0,
|
||||
-4.0 + -s_a.grip.0 / 2.0 + move2 * -5.0,
|
||||
);
|
||||
next.upper_torso.orientation =
|
||||
Quaternion::rotation_z(move1base * 0.5 + move2 * -0.7);
|
||||
@ -114,8 +114,8 @@ impl Animation for BetaAnimation {
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
4.0 + move1 * -12.0 + move2 * 20.0,
|
||||
(s_a.grip / 1.0) + move1 * -3.0 + move2 * 5.0,
|
||||
(-s_a.grip / 0.8) + move1 * 6.0 + move2 * 8.0,
|
||||
(s_a.grip.0 / 1.0) + move1 * -3.0 + move2 * 5.0,
|
||||
(-s_a.grip.0 / 0.8) + move1 * 6.0 + move2 * 8.0,
|
||||
);
|
||||
next.head.orientation = Quaternion::rotation_x(move1 * -0.25)
|
||||
* Quaternion::rotation_z(move1 * -0.2 + move2 * 0.6);
|
||||
@ -139,8 +139,8 @@ impl Animation for BetaAnimation {
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
4.0 + move1 * -18.0 + move2 * 20.0,
|
||||
(s_a.grip / 1.0) + move1 * -3.0 + move2 * 12.0,
|
||||
(-s_a.grip / 0.8) + move1 * -2.0 + move2 * 4.0,
|
||||
(s_a.grip.0 / 1.0) + move1 * -3.0 + move2 * 12.0,
|
||||
(-s_a.grip.0 / 0.8) + move1 * -2.0 + move2 * 4.0,
|
||||
);
|
||||
next.head.orientation = Quaternion::rotation_x(move1 * -0.25)
|
||||
* Quaternion::rotation_z(move1 * -0.9 + move2 * 0.6);
|
||||
|
112
voxygen/anim/src/biped_large/blink.rs
Normal file
112
voxygen/anim/src/biped_large/blink.rs
Normal file
@ -0,0 +1,112 @@
|
||||
use super::{
|
||||
super::{vek::*, Animation},
|
||||
BipedLargeSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::{
|
||||
comp::item::{ToolKind, UniqueKind},
|
||||
states::utils::StageSection,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct BlinkAnimation;
|
||||
|
||||
impl Animation for BlinkAnimation {
|
||||
type Dependency = (
|
||||
Option<ToolKind>,
|
||||
Option<ToolKind>,
|
||||
Vec3<f32>,
|
||||
f32,
|
||||
Option<StageSection>,
|
||||
f32,
|
||||
);
|
||||
type Skeleton = BipedLargeSkeleton;
|
||||
|
||||
#[cfg(feature = "use-dyn-lib")]
|
||||
const UPDATE_FN: &'static [u8] = b"biped_large_blink\0";
|
||||
|
||||
#[cfg_attr(feature = "be-dyn-lib", export_name = "biped_large_blink")]
|
||||
#[allow(clippy::approx_constant)] // TODO: Pending review in #587
|
||||
fn update_skeleton_inner(
|
||||
skeleton: &Self::Skeleton,
|
||||
(active_tool_kind, _second_tool_kind, velocity, _global_time, stage_section, acc_vel): Self::Dependency,
|
||||
anim_time: f32,
|
||||
rate: &mut f32,
|
||||
s_a: &SkeletonAttr,
|
||||
) -> Self::Skeleton {
|
||||
*rate = 1.0;
|
||||
let mut next = (*skeleton).clone();
|
||||
let speed = Vec2::<f32>::from(velocity).magnitude();
|
||||
|
||||
let lab: f32 = 0.65 * s_a.tempo;
|
||||
let speednorm = (speed / 12.0).powf(0.4);
|
||||
let foothoril = (acc_vel * lab + PI * 1.45).sin() * speednorm;
|
||||
let foothorir = (acc_vel * lab + PI * (0.45)).sin() * speednorm;
|
||||
let footrotl = ((1.0 / (0.5 + (0.5) * ((acc_vel * lab + PI * 1.4).sin()).powi(2))).sqrt())
|
||||
* ((acc_vel * lab + PI * 1.4).sin());
|
||||
|
||||
let footrotr = ((1.0 / (0.5 + (0.5) * ((acc_vel * lab + PI * 0.4).sin()).powi(2))).sqrt())
|
||||
* ((acc_vel * lab + PI * 0.4).sin());
|
||||
let (move1base, move2) = match stage_section {
|
||||
Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0),
|
||||
Some(StageSection::Recover) => (1.0, anim_time.powi(4)),
|
||||
_ => (0.0, 0.0),
|
||||
};
|
||||
let pullback = 1.0 - move2;
|
||||
let move1 = move1base * pullback;
|
||||
|
||||
next.shoulder_l.position = Vec3::new(
|
||||
-s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
s_a.shoulder.2 - foothorir * 1.0,
|
||||
);
|
||||
next.shoulder_l.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + 0.6 * speednorm + (footrotr * -0.2) * speednorm);
|
||||
|
||||
next.shoulder_r.position = Vec3::new(
|
||||
s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
s_a.shoulder.2 - foothoril * 1.0,
|
||||
);
|
||||
next.shoulder_r.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + 0.6 * speednorm + (footrotl * -0.2) * speednorm);
|
||||
next.torso.orientation = Quaternion::rotation_z(0.0);
|
||||
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.hand_l.position = Vec3::new(s_a.grip.1, 0.0, s_a.grip.0);
|
||||
next.hand_r.position = Vec3::new(-s_a.grip.1, 0.0, s_a.grip.0);
|
||||
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
match active_tool_kind {
|
||||
Some(ToolKind::StaffSimple) | Some(ToolKind::Unique(UniqueKind::MindflayerStaff)) => {
|
||||
next.head.orientation =
|
||||
Quaternion::rotation_x(move1 * -0.3) * Quaternion::rotation_y(move1 * -0.1);
|
||||
next.control_l.position = Vec3::new(-1.0, 3.0, 12.0);
|
||||
next.control_r.position =
|
||||
Vec3::new(1.0 + move1 * 12.0, 2.0 + move1 * 8.0, 2.0 + move1 * 18.0);
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
-3.0 + move1 * -4.0,
|
||||
3.0 + s_a.grip.0 / 1.2 + move1 * -8.0,
|
||||
-11.0 + -s_a.grip.0 / 2.0,
|
||||
);
|
||||
|
||||
next.control_l.orientation = Quaternion::rotation_x(PI / 2.0 - move1 * -0.8)
|
||||
* Quaternion::rotation_y(-0.5 + move1 * -0.0)
|
||||
* Quaternion::rotation_z(move1 * 0.0);
|
||||
next.control_r.orientation = Quaternion::rotation_x(PI / 2.5 + move1 * 0.7)
|
||||
* Quaternion::rotation_y(0.5 + move1 * -0.6)
|
||||
* Quaternion::rotation_z(move1 * 1.5);
|
||||
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(-0.2 + move1 * -0.4) * Quaternion::rotation_y(-0.1);
|
||||
},
|
||||
|
||||
_ => {},
|
||||
}
|
||||
next
|
||||
}
|
||||
}
|
@ -47,8 +47,8 @@ impl Animation for DashAnimation {
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.hand_l.position = Vec3::new(0.0, 0.0, s_a.grip);
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, s_a.grip);
|
||||
next.hand_l.position = Vec3::new(0.0, 0.0, s_a.grip.0);
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, s_a.grip.0);
|
||||
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
@ -88,8 +88,8 @@ impl Animation for DashAnimation {
|
||||
* Quaternion::rotation_z(move1 * -0.2 + move2 * 0.6);
|
||||
next.control.position = Vec3::new(
|
||||
-3.0 + move1 * -2.0 + move2 * 2.0,
|
||||
5.0 + s_a.grip / 1.2 + move1 * -4.0 + move2 * 2.0 + move3 * 8.0,
|
||||
-4.0 + -s_a.grip / 2.0 + move2 * -5.0 + move3 * 5.0,
|
||||
5.0 + s_a.grip.0 / 1.2 + move1 * -4.0 + move2 * 2.0 + move3 * 8.0,
|
||||
-4.0 + -s_a.grip.0 / 2.0 + move2 * -5.0 + move3 * 5.0,
|
||||
);
|
||||
next.upper_torso.orientation = Quaternion::rotation_x(move2 * -0.2 + move3 * 0.2)
|
||||
* Quaternion::rotation_z(move1 * 0.8 + move3 * -0.7);
|
||||
@ -114,8 +114,8 @@ impl Animation for DashAnimation {
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
4.0 + move1 * -3.0 + move3 * -5.0,
|
||||
(s_a.grip / 1.0) + move1 * -1.0 + move3 * 1.0 + footrotl * 2.0,
|
||||
(-s_a.grip / 0.8) + move1 * 2.0 + move3 * -3.0,
|
||||
(s_a.grip.0 / 1.0) + move1 * -1.0 + move3 * 1.0 + footrotl * 2.0,
|
||||
(-s_a.grip.0 / 0.8) + move1 * 2.0 + move3 * -3.0,
|
||||
);
|
||||
next.head.orientation = Quaternion::rotation_x(move1 * -0.5 + move3 * 0.5)
|
||||
* Quaternion::rotation_z(move1 * 0.3 + move3 * 0.3);
|
||||
|
@ -1,6 +1,7 @@
|
||||
pub mod alpha;
|
||||
pub mod beam;
|
||||
pub mod beta;
|
||||
pub mod blink;
|
||||
pub mod charge;
|
||||
pub mod dash;
|
||||
pub mod equip;
|
||||
@ -12,15 +13,16 @@ pub mod shockwave;
|
||||
pub mod shoot;
|
||||
pub mod spin;
|
||||
pub mod spinmelee;
|
||||
pub mod summon;
|
||||
pub mod wield;
|
||||
|
||||
// Reexports
|
||||
pub use self::{
|
||||
alpha::AlphaAnimation, beam::BeamAnimation, beta::BetaAnimation, charge::ChargeAnimation,
|
||||
dash::DashAnimation, equip::EquipAnimation, idle::IdleAnimation, jump::JumpAnimation,
|
||||
leapmelee::LeapAnimation, run::RunAnimation, shockwave::ShockwaveAnimation,
|
||||
shoot::ShootAnimation, spin::SpinAnimation, spinmelee::SpinMeleeAnimation,
|
||||
wield::WieldAnimation,
|
||||
alpha::AlphaAnimation, beam::BeamAnimation, beta::BetaAnimation, blink::BlinkAnimation,
|
||||
charge::ChargeAnimation, dash::DashAnimation, equip::EquipAnimation, idle::IdleAnimation,
|
||||
jump::JumpAnimation, leapmelee::LeapAnimation, run::RunAnimation,
|
||||
shockwave::ShockwaveAnimation, shoot::ShootAnimation, spin::SpinAnimation,
|
||||
spinmelee::SpinMeleeAnimation, summon::SummonAnimation, wield::WieldAnimation,
|
||||
};
|
||||
|
||||
use super::{make_bone, vek::*, FigureBoneData, Skeleton};
|
||||
@ -127,7 +129,7 @@ pub struct SkeletonAttr {
|
||||
foot: (f32, f32, f32),
|
||||
scaler: f32,
|
||||
tempo: f32,
|
||||
grip: f32,
|
||||
grip: (f32, f32),
|
||||
shl: (f32, f32, f32, f32, f32, f32),
|
||||
shr: (f32, f32, f32, f32, f32, f32),
|
||||
sc: (f32, f32, f32, f32, f32, f32),
|
||||
@ -169,7 +171,7 @@ impl Default for SkeletonAttr {
|
||||
foot: (0.0, 0.0, 0.0),
|
||||
scaler: 0.0,
|
||||
tempo: 0.0,
|
||||
grip: 0.0,
|
||||
grip: (0.0, 0.0),
|
||||
shl: (0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
|
||||
shr: (0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
|
||||
sc: (0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
|
||||
@ -259,7 +261,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
|
||||
(Occultsaurok, _) => (0.0, -5.0),
|
||||
(Mightysaurok, _) => (0.0, -5.0),
|
||||
(Slysaurok, _) => (0.0, -6.0),
|
||||
(Mindflayer, _) => (3.5, -19.5),
|
||||
(Mindflayer, _) => (3.5, -10.0),
|
||||
(Minotaur, _) => (1.5, -8.5),
|
||||
(Tidalwarrior, _) => (0.0, -9.5),
|
||||
(Yeti, _) => (0.0, -6.5),
|
||||
@ -343,7 +345,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
|
||||
(Occultsaurok, _) => (3.5, 3.5, -10.0),
|
||||
(Mightysaurok, _) => (3.5, 3.5, -10.0),
|
||||
(Slysaurok, _) => (3.5, 3.5, -10.0),
|
||||
(Mindflayer, _) => (4.5, 1.5, -7.0),
|
||||
(Mindflayer, _) => (4.5, 1.5, -16.0),
|
||||
(Minotaur, _) => (6.0, 4.5, -17.5),
|
||||
(Tidalwarrior, _) => (3.5, 0.5, -10.5),
|
||||
(Yeti, _) => (4.5, 0.5, -12.5),
|
||||
@ -380,23 +382,23 @@ impl<'a> From<&'a Body> for SkeletonAttr {
|
||||
_ => 1.0,
|
||||
},
|
||||
grip: match (body.species, body.body_type) {
|
||||
(Ogre, Male) => 13.0,
|
||||
(Ogre, Female) => 8.0,
|
||||
(Cyclops, _) => 12.0,
|
||||
(Wendigo, _) => 15.0,
|
||||
(Troll, _) => 12.0,
|
||||
(Dullahan, _) => 15.0,
|
||||
(Werewolf, _) => 13.0,
|
||||
(Occultsaurok, _) => 10.0,
|
||||
(Mightysaurok, _) => 10.0,
|
||||
(Slysaurok, _) => 10.0,
|
||||
(Mindflayer, _) => 12.0,
|
||||
(Minotaur, _) => 14.0,
|
||||
(Tidalwarrior, _) => 14.0,
|
||||
(Yeti, _) => 12.5,
|
||||
(Harvester, _) => 7.5,
|
||||
(Blueoni, _) => 12.5,
|
||||
(Redoni, _) => 12.5,
|
||||
(Ogre, Male) => (13.0, 0.0),
|
||||
(Ogre, Female) => (8.0, 0.0),
|
||||
(Cyclops, _) => (12.0, 0.0),
|
||||
(Wendigo, _) => (15.0, 0.0),
|
||||
(Troll, _) => (12.0, 0.0),
|
||||
(Dullahan, _) => (15.0, 0.0),
|
||||
(Werewolf, _) => (13.0, 0.0),
|
||||
(Occultsaurok, _) => (10.0, 0.0),
|
||||
(Mightysaurok, _) => (10.0, 0.0),
|
||||
(Slysaurok, _) => (10.0, 0.0),
|
||||
(Mindflayer, _) => (12.0, 2.5),
|
||||
(Minotaur, _) => (14.0, 0.0),
|
||||
(Tidalwarrior, _) => (14.0, 0.0),
|
||||
(Yeti, _) => (12.5, 0.0),
|
||||
(Harvester, _) => (7.5, 0.0),
|
||||
(Blueoni, _) => (12.5, 0.0),
|
||||
(Redoni, _) => (12.5, 0.0),
|
||||
},
|
||||
shl: match (body.species, body.body_type) {
|
||||
(Dullahan, _) => (-4.75, -11.0, 8.5, 1.47, -0.2, 0.0),
|
||||
|
@ -82,8 +82,8 @@ impl Animation for ShootAnimation {
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.hand_l.position = Vec3::new(0.0, 0.0, s_a.grip);
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, s_a.grip);
|
||||
next.hand_l.position = Vec3::new(0.0, 0.0, s_a.grip.0);
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, s_a.grip.0);
|
||||
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
@ -105,8 +105,8 @@ impl Animation for ShootAnimation {
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
-3.0,
|
||||
3.0 + s_a.grip / 1.2 + move1 * 4.0 + move2 + move1shake * 2.0 + move2 * -2.0,
|
||||
-11.0 + -s_a.grip / 2.0 + move1 * 3.0,
|
||||
3.0 + s_a.grip.0 / 1.2 + move1 * 4.0 + move2 + move1shake * 2.0 + move2 * -2.0,
|
||||
-11.0 + -s_a.grip.0 / 2.0 + move1 * 3.0,
|
||||
);
|
||||
next.head.orientation = Quaternion::rotation_x(move1 * -0.15)
|
||||
* Quaternion::rotation_y(move1 * 0.25)
|
||||
@ -154,8 +154,8 @@ impl Animation for ShootAnimation {
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
-1.0 + move1 * 2.0,
|
||||
6.0 + s_a.grip / 1.2 + move1 * 7.0,
|
||||
-5.0 + -s_a.grip / 2.0 + move1 * 8.0,
|
||||
6.0 + s_a.grip.0 / 1.2 + move1 * 7.0,
|
||||
-5.0 + -s_a.grip.0 / 2.0 + move1 * 8.0,
|
||||
);
|
||||
|
||||
next.control_l.orientation =
|
||||
|
@ -2,7 +2,10 @@ use super::{
|
||||
super::{vek::*, Animation},
|
||||
BipedLargeSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::{comp::item::ToolKind, states::utils::StageSection};
|
||||
use common::{
|
||||
comp::item::{ToolKind, UniqueKind},
|
||||
states::utils::StageSection,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct SpinMeleeAnimation;
|
||||
@ -14,6 +17,7 @@ impl Animation for SpinMeleeAnimation {
|
||||
Vec3<f32>,
|
||||
f32,
|
||||
Option<StageSection>,
|
||||
f32,
|
||||
);
|
||||
type Skeleton = BipedLargeSkeleton;
|
||||
|
||||
@ -24,122 +28,97 @@ impl Animation for SpinMeleeAnimation {
|
||||
#[allow(clippy::approx_constant)] // TODO: Pending review in #587
|
||||
fn update_skeleton_inner(
|
||||
skeleton: &Self::Skeleton,
|
||||
(active_tool_kind, _second_tool_kind, velocity, _global_time, stage_section): Self::Dependency,
|
||||
(active_tool_kind, _second_tool_kind, velocity, _global_time, stage_section, acc_vel): Self::Dependency,
|
||||
anim_time: f32,
|
||||
rate: &mut f32,
|
||||
s_a: &SkeletonAttr,
|
||||
) -> Self::Skeleton {
|
||||
*rate = 1.0;
|
||||
let lab: f32 = 1.0;
|
||||
let (_movement1, movement2, movement3) = match stage_section {
|
||||
Some(StageSection::Buildup) => (anim_time, 0.0, 0.0),
|
||||
Some(StageSection::Swing) => (1.0, anim_time, 0.0),
|
||||
Some(StageSection::Recover) => (1.0, 1.0, anim_time),
|
||||
let mut next = (*skeleton).clone();
|
||||
|
||||
let speed = Vec2::<f32>::from(velocity).magnitude();
|
||||
|
||||
let lab: f32 = 0.65 * s_a.tempo;
|
||||
let speednorm = (speed / 12.0).powf(0.4);
|
||||
let foothoril = (acc_vel * lab + PI * 1.45).sin() * speednorm;
|
||||
let foothorir = (acc_vel * lab + PI * (0.45)).sin() * speednorm;
|
||||
let footrotl = ((1.0 / (0.5 + (0.5) * ((acc_vel * lab + PI * 1.4).sin()).powi(2))).sqrt())
|
||||
* ((acc_vel * lab + PI * 1.4).sin());
|
||||
|
||||
let footrotr = ((1.0 / (0.5 + (0.5) * ((acc_vel * lab + PI * 0.4).sin()).powi(2))).sqrt())
|
||||
* ((acc_vel * lab + PI * 0.4).sin());
|
||||
|
||||
let (move1base, move2, move3) = match stage_section {
|
||||
Some(StageSection::Buildup) => ((anim_time.powf(0.25)), 0.0, 0.0),
|
||||
Some(StageSection::Swing) => (1.0, (anim_time * 0.05).sin() - 0.05, 0.0),
|
||||
Some(StageSection::Recover) => (1.0, 0.0, anim_time),
|
||||
_ => (0.0, 0.0, 0.0),
|
||||
};
|
||||
let speed = Vec2::<f32>::from(velocity).magnitude();
|
||||
let mut next = (*skeleton).clone();
|
||||
//torso movement
|
||||
let xshift = if velocity.z.abs() < 0.1 {
|
||||
((anim_time - 1.1) * lab * 3.0).sin()
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let yshift = if velocity.z.abs() < 0.1 {
|
||||
((anim_time - 1.1) * lab * 3.0 + PI / 2.0).sin()
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let pullback = 1.0 - move3;
|
||||
let move1 = move1base * pullback;
|
||||
|
||||
let spin = if anim_time < 1.1 && velocity.z.abs() < 0.1 {
|
||||
0.5 * (anim_time.powi(2))
|
||||
} else {
|
||||
lab * anim_time * 0.9
|
||||
};
|
||||
next.shoulder_l.position = Vec3::new(
|
||||
-s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
s_a.shoulder.2 - foothorir * 1.0,
|
||||
);
|
||||
next.shoulder_l.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + 0.6 * speednorm + (footrotr * -0.2) * speednorm);
|
||||
|
||||
//feet
|
||||
let slowersmooth = (anim_time * lab * 4.0).sin();
|
||||
let quick = (anim_time * lab * 8.0).sin();
|
||||
next.shoulder_r.position = Vec3::new(
|
||||
s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
s_a.shoulder.2 - foothoril * 1.0,
|
||||
);
|
||||
next.shoulder_r.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + 0.6 * speednorm + (footrotl * -0.2) * speednorm);
|
||||
next.torso.orientation = Quaternion::rotation_z(0.0);
|
||||
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.hand_l.position = Vec3::new(s_a.grip.1, 0.0, s_a.grip.0);
|
||||
next.hand_r.position = Vec3::new(-s_a.grip.1, 0.0, s_a.grip.0);
|
||||
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
match active_tool_kind {
|
||||
Some(ToolKind::Sword) => {
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.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.shr.0, s_a.shr.1, s_a.shr.2);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(s_a.shr.3) * Quaternion::rotation_y(s_a.shr.4);
|
||||
|
||||
next.control.position = Vec3::new(s_a.sc.0, s_a.sc.1, s_a.sc.2);
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(s_a.sc.3 - PI / 2.0 + movement3 * PI / 2.0)
|
||||
* Quaternion::rotation_z(s_a.sc.5 - PI / 2.0 + movement3 * PI / 2.0);
|
||||
next.torso.orientation = Quaternion::rotation_z(movement2 * PI * 2.0);
|
||||
|
||||
next.upper_torso.position = Vec3::new(0.0, s_a.upper_torso.0, s_a.upper_torso.1);
|
||||
next.upper_torso.orientation = Quaternion::rotation_y(0.3 + movement3 * -0.3);
|
||||
next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1);
|
||||
next.head.orientation = Quaternion::rotation_x(-0.15 + movement3 * 0.15);
|
||||
next.lower_torso.orientation = Quaternion::rotation_x(0.2);
|
||||
},
|
||||
Some(ToolKind::Axe) => {
|
||||
next.hand_l.position = Vec3::new(-0.5, 0.0, 4.0);
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_y(PI);
|
||||
next.hand_r.position = Vec3::new(0.5, 0.0, -2.5);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(PI / 2.0);
|
||||
next.main.position = Vec3::new(-0.0, -2.0, -1.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.control.position = Vec3::new(0.0, 16.0, 3.0);
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(-1.4) * Quaternion::rotation_z(1.4);
|
||||
|
||||
next.head.orientation =
|
||||
Quaternion::rotation_x(-0.15) * Quaternion::rotation_y(0.08);
|
||||
next.upper_torso.position =
|
||||
Vec3::new(0.0, s_a.upper_torso.0 - 3.0, s_a.upper_torso.1 - 2.0);
|
||||
next.upper_torso.orientation = Quaternion::rotation_z(0.0)
|
||||
* Quaternion::rotation_x(-0.1)
|
||||
* Quaternion::rotation_y(0.3);
|
||||
|
||||
next.lower_torso.position = Vec3::new(0.0, 3.0, -2.5);
|
||||
next.lower_torso.orientation = Quaternion::rotation_z(0.0)
|
||||
* Quaternion::rotation_x(0.7)
|
||||
* Quaternion::rotation_y(0.0);
|
||||
next.torso.position = Vec3::new(
|
||||
-xshift * anim_time.min(0.6),
|
||||
-yshift * anim_time.min(0.6),
|
||||
0.0,
|
||||
Some(ToolKind::StaffSimple) | Some(ToolKind::Unique(UniqueKind::MindflayerStaff)) => {
|
||||
next.head.orientation = Quaternion::rotation_x(move1 * -0.3 + move2 * 0.5);
|
||||
next.control_l.position = Vec3::new(
|
||||
-1.0 + move1 * -10.0 + move2 * -10.0,
|
||||
3.0,
|
||||
12.0 + move1 * 7.0,
|
||||
);
|
||||
next.control_r.position = Vec3::new(
|
||||
1.0 + move1 * 10.0 + move2 * -10.0,
|
||||
2.0 + move1 * -0.0,
|
||||
2.0 + move1 * 15.0,
|
||||
);
|
||||
next.torso.orientation = Quaternion::rotation_z(spin * -16.0)
|
||||
* Quaternion::rotation_x(0.0)
|
||||
* Quaternion::rotation_y(0.0);
|
||||
if velocity.z.abs() > 0.1 {
|
||||
next.foot_l.position = Vec3::new(-s_a.foot.0, 8.0, s_a.foot.2 + 2.0);
|
||||
next.foot_l.orientation =
|
||||
Quaternion::rotation_x(1.0) * Quaternion::rotation_z(0.0);
|
||||
|
||||
next.foot_r.position = Vec3::new(s_a.foot.0, 8.0, s_a.foot.2 + 2.0);
|
||||
next.foot_r.orientation = Quaternion::rotation_x(1.0);
|
||||
} else if speed < 0.5 {
|
||||
next.foot_l.position = Vec3::new(-s_a.foot.0, 2.0 + quick * -6.0, s_a.foot.2);
|
||||
next.foot_l.orientation = Quaternion::rotation_x(0.5 + slowersmooth * 0.2);
|
||||
next.control.position = Vec3::new(
|
||||
-3.0 + move1 * 3.0,
|
||||
3.0 + s_a.grip.0 / 1.2 + move1 * 18.0,
|
||||
-11.0 + -s_a.grip.0 / 2.0 + move1 * 8.0,
|
||||
);
|
||||
|
||||
next.foot_r.position = Vec3::new(s_a.foot.0, 4.0, s_a.foot.2);
|
||||
next.foot_r.orientation = Quaternion::rotation_x(0.5 - slowersmooth * 0.2)
|
||||
* Quaternion::rotation_y(-0.4);
|
||||
} else {
|
||||
next.foot_l.position = Vec3::new(-s_a.foot.0, 2.0 + quick * -6.0, s_a.foot.2);
|
||||
next.foot_l.orientation = Quaternion::rotation_x(0.5 + slowersmooth * 0.2);
|
||||
next.control_l.orientation = Quaternion::rotation_x(PI / 2.0 - move1 * 0.2)
|
||||
* Quaternion::rotation_y(-0.5 + move1 * 0.3)
|
||||
* Quaternion::rotation_z(move1 * 0.0);
|
||||
next.control_r.orientation = Quaternion::rotation_x(PI / 2.5 + move1 * 0.0)
|
||||
* Quaternion::rotation_y(0.5 + move1 * -0.3)
|
||||
* Quaternion::rotation_z(move2 * 1.0);
|
||||
|
||||
next.foot_r.position = Vec3::new(s_a.foot.0, 2.0 + quick * 6.0, s_a.foot.2);
|
||||
next.foot_r.orientation = Quaternion::rotation_x(0.5 - slowersmooth * 0.2);
|
||||
};
|
||||
next.control.orientation = Quaternion::rotation_x(-0.2 + move1 * 0.8)
|
||||
* Quaternion::rotation_y(-0.1 + move1 * 0.1 + move2 * 1.0);
|
||||
|
||||
next.upper_torso.orientation = Quaternion::rotation_x(move1 * -0.5);
|
||||
|
||||
next.lower_torso.orientation = Quaternion::rotation_x(move1 * 0.8);
|
||||
next.torso.position = Vec3::new(0.0, 0.0, move1 * 0.8);
|
||||
},
|
||||
|
||||
_ => {},
|
||||
}
|
||||
|
||||
|
119
voxygen/anim/src/biped_large/summon.rs
Normal file
119
voxygen/anim/src/biped_large/summon.rs
Normal file
@ -0,0 +1,119 @@
|
||||
use super::{
|
||||
super::{vek::*, Animation},
|
||||
BipedLargeSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::{
|
||||
comp::item::{ToolKind, UniqueKind},
|
||||
states::utils::StageSection,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct SummonAnimation;
|
||||
|
||||
impl Animation for SummonAnimation {
|
||||
type Dependency = (
|
||||
Option<ToolKind>,
|
||||
Option<ToolKind>,
|
||||
Vec3<f32>,
|
||||
f32,
|
||||
Option<StageSection>,
|
||||
f32,
|
||||
);
|
||||
type Skeleton = BipedLargeSkeleton;
|
||||
|
||||
#[cfg(feature = "use-dyn-lib")]
|
||||
const UPDATE_FN: &'static [u8] = b"biped_large_summon\0";
|
||||
|
||||
#[cfg_attr(feature = "be-dyn-lib", export_name = "biped_large_summon")]
|
||||
#[allow(clippy::approx_constant)] // TODO: Pending review in #587
|
||||
fn update_skeleton_inner(
|
||||
skeleton: &Self::Skeleton,
|
||||
(active_tool_kind, _second_tool_kind, velocity, _global_time, stage_section, acc_vel): Self::Dependency,
|
||||
anim_time: f32,
|
||||
rate: &mut f32,
|
||||
s_a: &SkeletonAttr,
|
||||
) -> Self::Skeleton {
|
||||
*rate = 1.0;
|
||||
let mut next = (*skeleton).clone();
|
||||
|
||||
let speed = Vec2::<f32>::from(velocity).magnitude();
|
||||
|
||||
let lab: f32 = 0.65 * s_a.tempo;
|
||||
let speednorm = (speed / 12.0).powf(0.4);
|
||||
let foothoril = (acc_vel * lab + PI * 1.45).sin() * speednorm;
|
||||
let foothorir = (acc_vel * lab + PI * (0.45)).sin() * speednorm;
|
||||
let footrotl = ((1.0 / (0.5 + (0.5) * ((acc_vel * lab + PI * 1.4).sin()).powi(2))).sqrt())
|
||||
* ((acc_vel * lab + PI * 1.4).sin());
|
||||
|
||||
let footrotr = ((1.0 / (0.5 + (0.5) * ((acc_vel * lab + PI * 0.4).sin()).powi(2))).sqrt())
|
||||
* ((acc_vel * lab + PI * 0.4).sin());
|
||||
|
||||
let (move1base, move2base, move3) = match stage_section {
|
||||
Some(StageSection::Buildup) => ((anim_time.powf(0.5)), 0.0, 0.0),
|
||||
Some(StageSection::Cast) => (1.0, (anim_time.powi(2)), 0.0),
|
||||
Some(StageSection::Recover) => (1.0, 1.0, anim_time),
|
||||
_ => (0.0, 0.0, 0.0),
|
||||
};
|
||||
let pullback = 1.0 - move3;
|
||||
let move1 = move1base * pullback;
|
||||
let move2 = move2base * pullback;
|
||||
|
||||
next.shoulder_l.position = Vec3::new(
|
||||
-s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
s_a.shoulder.2 - foothorir * 1.0,
|
||||
);
|
||||
next.shoulder_l.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + 0.6 * speednorm + (footrotr * -0.2) * speednorm);
|
||||
|
||||
next.shoulder_r.position = Vec3::new(
|
||||
s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
s_a.shoulder.2 - foothoril * 1.0,
|
||||
);
|
||||
next.shoulder_r.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + 0.6 * speednorm + (footrotl * -0.2) * speednorm);
|
||||
next.torso.orientation = Quaternion::rotation_z(0.0);
|
||||
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.hand_l.position = Vec3::new(s_a.grip.1, 0.0, s_a.grip.0);
|
||||
next.hand_r.position = Vec3::new(-s_a.grip.1, 0.0, s_a.grip.0);
|
||||
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
match active_tool_kind {
|
||||
Some(ToolKind::StaffSimple) | Some(ToolKind::Unique(UniqueKind::MindflayerStaff)) => {
|
||||
next.head.orientation = Quaternion::rotation_x(0.0);
|
||||
next.control_l.position = Vec3::new(-1.0, 3.0, 12.0);
|
||||
next.control_r.position = Vec3::new(
|
||||
1.0 + move1 * 3.0 + move2 * 20.0,
|
||||
2.0 + move1 * -5.0 + move2 * 5.0,
|
||||
2.0 + move1 * 15.0 + move2 * 0.0,
|
||||
);
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
-3.0 + move2 * 9.0,
|
||||
3.0 + s_a.grip.0 / 1.2 + move1 * 15.0 + move2 * 2.0,
|
||||
-11.0 + -s_a.grip.0 / 2.0 + move1 * 15.0 + move2 * -12.0,
|
||||
);
|
||||
|
||||
next.control_l.orientation = Quaternion::rotation_x(PI / 2.0 - move1 * 0.2)
|
||||
* Quaternion::rotation_y(-0.5 + move2 * -0.4)
|
||||
* Quaternion::rotation_z(move1 * 0.0);
|
||||
next.control_r.orientation = Quaternion::rotation_x(PI / 2.5 + move1 * 0.2)
|
||||
* Quaternion::rotation_y(0.5 + move1 * 0.5 + move2 * 0.0)
|
||||
* Quaternion::rotation_z(move1 * 0.5 + move2 * 0.8);
|
||||
|
||||
next.control.orientation = Quaternion::rotation_x(-0.2 + move1 * 1.0)
|
||||
* Quaternion::rotation_y(-0.1 + move2 * -0.8);
|
||||
},
|
||||
|
||||
_ => {},
|
||||
}
|
||||
|
||||
next
|
||||
}
|
||||
}
|
@ -95,8 +95,8 @@ impl Animation for WieldAnimation {
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.hand_l.position = Vec3::new(0.0, 0.0, s_a.grip);
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, s_a.grip);
|
||||
next.hand_l.position = Vec3::new(s_a.grip.1, 0.0, s_a.grip.0);
|
||||
next.hand_r.position = Vec3::new(-s_a.grip.1, 0.0, s_a.grip.0);
|
||||
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
@ -158,8 +158,8 @@ impl Animation for WieldAnimation {
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
-3.0,
|
||||
5.0 + s_a.grip / 1.2,
|
||||
-4.0 + -s_a.grip / 2.0 + short * -1.5,
|
||||
5.0 + s_a.grip.0 / 1.2,
|
||||
-4.0 + -s_a.grip.0 / 2.0 + short * -1.5,
|
||||
);
|
||||
|
||||
next.control_l.orientation =
|
||||
@ -177,8 +177,8 @@ impl Animation for WieldAnimation {
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
-1.0,
|
||||
6.0 + s_a.grip / 1.2,
|
||||
-5.0 + -s_a.grip / 2.0 + short * -1.5,
|
||||
6.0 + s_a.grip.0 / 1.2,
|
||||
-5.0 + -s_a.grip.0 / 2.0 + short * -1.5,
|
||||
);
|
||||
|
||||
next.control_l.orientation =
|
||||
@ -195,8 +195,11 @@ impl Animation for WieldAnimation {
|
||||
next.control_l.position = Vec3::new(-1.0, 2.0, 12.0);
|
||||
next.control_r.position = Vec3::new(1.0, 2.0, -2.0);
|
||||
|
||||
next.control.position =
|
||||
Vec3::new(4.0, 0.0 + s_a.grip / 1.0, -s_a.grip / 0.8 + short * -1.5);
|
||||
next.control.position = Vec3::new(
|
||||
4.0,
|
||||
0.0 + s_a.grip.0 / 1.0,
|
||||
-s_a.grip.0 / 0.8 + short * -1.5,
|
||||
);
|
||||
|
||||
next.control_l.orientation =
|
||||
Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_y(-0.0);
|
||||
@ -207,14 +210,15 @@ impl Animation for WieldAnimation {
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(-1.0 + short * 0.2) * Quaternion::rotation_y(-1.8);
|
||||
},
|
||||
Some(ToolKind::StaffSimple) => {
|
||||
Some(ToolKind::StaffSimple)
|
||||
| Some(ToolKind::Unique(UniqueKind::MindflayerStaff)) => {
|
||||
next.control_l.position = Vec3::new(-1.0, 3.0, 12.0);
|
||||
next.control_r.position = Vec3::new(1.0, 2.0, 2.0);
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
-3.0,
|
||||
3.0 + s_a.grip / 1.2,
|
||||
-11.0 + -s_a.grip / 2.0 + short * -1.5,
|
||||
3.0 + s_a.grip.0 / 1.2,
|
||||
-11.0 + -s_a.grip.0 / 2.0 + short * -1.5,
|
||||
);
|
||||
|
||||
next.control_l.orientation =
|
||||
|
@ -2,12 +2,16 @@ use super::{
|
||||
super::{vek::*, Animation},
|
||||
BipedSmallSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::states::utils::StageSection;
|
||||
use common::{
|
||||
comp::item::{ToolKind, UniqueKind},
|
||||
states::utils::StageSection,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct AlphaAnimation;
|
||||
|
||||
type AlphaAnimationDependency = (
|
||||
Option<ToolKind>,
|
||||
Vec3<f32>,
|
||||
Vec3<f32>,
|
||||
Vec3<f32>,
|
||||
@ -30,14 +34,15 @@ impl Animation for AlphaAnimation {
|
||||
fn update_skeleton_inner(
|
||||
skeleton: &Self::Skeleton,
|
||||
(
|
||||
active_tool_kind,
|
||||
velocity,
|
||||
_orientation,
|
||||
_last_ori,
|
||||
_global_time,
|
||||
global_time,
|
||||
_avg_vel,
|
||||
_acc_vel,
|
||||
stage_section,
|
||||
_timer,
|
||||
timer,
|
||||
): Self::Dependency,
|
||||
anim_time: f32,
|
||||
_rate: &mut f32,
|
||||
@ -59,54 +64,82 @@ impl Animation for AlphaAnimation {
|
||||
_ => (0.0, 0.0, 0.0),
|
||||
};
|
||||
let pullback = 1.0 - move3;
|
||||
let subtract = global_time - timer;
|
||||
let check = subtract - subtract.trunc();
|
||||
let mirror = (check - 0.5).signum();
|
||||
let move1 = move1base * pullback * mirror;
|
||||
let move2 = move2base * pullback * mirror;
|
||||
let move1abs = move1base * pullback;
|
||||
let move2abs = move2base * pullback;
|
||||
match active_tool_kind {
|
||||
Some(ToolKind::Spear) => {
|
||||
next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1);
|
||||
next.head.orientation = Quaternion::rotation_x(move1abs * 0.2 + move2abs * 0.3)
|
||||
* Quaternion::rotation_z(move1abs * -0.2 + move2abs * 0.6)
|
||||
* Quaternion::rotation_y(move1abs * 0.3 + move2abs * -0.5);
|
||||
next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1) / 11.0;
|
||||
next.chest.orientation = Quaternion::rotation_x(move1abs * -0.2 + move2abs * 0.3)
|
||||
* Quaternion::rotation_z(move1abs * 0.5 + move2abs * -0.6);
|
||||
|
||||
next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1);
|
||||
next.head.orientation = Quaternion::rotation_x(move1abs * 0.2 + move2abs * 0.3)
|
||||
* Quaternion::rotation_z(move1abs * -0.2 + move2abs * 0.6)
|
||||
* Quaternion::rotation_y(move1abs * 0.3 + move2abs * -0.5);
|
||||
next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1) / 11.0;
|
||||
next.chest.orientation = Quaternion::rotation_x(move1abs * -0.2 + move2abs * 0.3)
|
||||
* Quaternion::rotation_z(move1abs * 0.5 + move2abs * -0.6);
|
||||
next.pants.position = Vec3::new(0.0, s_a.pants.0, s_a.pants.1);
|
||||
next.pants.orientation = Quaternion::rotation_x(move1abs * 0.2 + move2abs * -0.3)
|
||||
* Quaternion::rotation_z(move1abs * -0.2 + move2abs * 0.2);
|
||||
|
||||
next.pants.position = Vec3::new(0.0, s_a.pants.0, s_a.pants.1);
|
||||
next.pants.orientation = Quaternion::rotation_x(move1abs * 0.2 + move2abs * -0.3)
|
||||
* Quaternion::rotation_z(move1abs * -0.2 + move2abs * 0.2);
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_l.position = Vec3::new(s_a.grip.0 * 4.0, 0.0, s_a.grip.2);
|
||||
next.hand_r.position = Vec3::new(-s_a.grip.0 * 4.0, 0.0, s_a.grip.2);
|
||||
|
||||
next.hand_l.position = Vec3::new(s_a.grip.0 * 4.0, 0.0, s_a.grip.2);
|
||||
next.hand_r.position = Vec3::new(-s_a.grip.0 * 4.0, 0.0, s_a.grip.2);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
next.control_l.position = Vec3::new(1.0 - s_a.grip.0 * 2.0, 2.0, -2.0);
|
||||
next.control_r.position = Vec3::new(-1.0 + s_a.grip.0 * 2.0, 2.0, 2.0);
|
||||
|
||||
next.control_l.position = Vec3::new(1.0 - s_a.grip.0 * 2.0, 2.0, -2.0);
|
||||
next.control_r.position = Vec3::new(-1.0 + s_a.grip.0 * 2.0, 2.0, 2.0);
|
||||
next.control.position = Vec3::new(
|
||||
-3.0 + move1abs * -3.0 + move2abs * 5.0,
|
||||
s_a.grip.2 + move1abs * -12.0 + move2abs * 17.0,
|
||||
-s_a.grip.2 / 2.5 + s_a.grip.0 * -2.0 + move2abs * 5.0,
|
||||
);
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
-3.0 + move1abs * -3.0 + move2abs * 5.0,
|
||||
s_a.grip.2 + move1abs * -12.0 + move2abs * 17.0,
|
||||
-s_a.grip.2 / 2.5 + s_a.grip.0 * -2.0 + move2abs * 5.0,
|
||||
);
|
||||
next.control_l.orientation =
|
||||
Quaternion::rotation_x(PI / 1.5 + move1abs * -1.5 + move2abs * 2.5)
|
||||
* Quaternion::rotation_y(-0.3);
|
||||
next.control_r.orientation = Quaternion::rotation_x(
|
||||
PI / 1.5 + s_a.grip.0 * 0.2 + move1abs * -1.5 + move2abs * 2.5,
|
||||
) * Quaternion::rotation_y(0.5 + s_a.grip.0 * 0.2);
|
||||
|
||||
next.control_l.orientation =
|
||||
Quaternion::rotation_x(PI / 1.5 + move1abs * -1.5 + move2abs * 2.5)
|
||||
* Quaternion::rotation_y(-0.3);
|
||||
next.control_r.orientation =
|
||||
Quaternion::rotation_x(PI / 1.5 + s_a.grip.0 * 0.2 + move1abs * -1.5 + move2abs * 2.5)
|
||||
* Quaternion::rotation_y(0.5 + s_a.grip.0 * 0.2);
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(-1.35 + move1abs * -0.3 + move2abs * 0.5)
|
||||
* Quaternion::rotation_z(move1abs * 1.0 + move2abs * -1.0)
|
||||
* Quaternion::rotation_y(move2abs * 0.0);
|
||||
|
||||
next.control.orientation = Quaternion::rotation_x(-1.35 + move1abs * -0.3 + move2abs * 0.5)
|
||||
* Quaternion::rotation_z(move1abs * 1.0 + move2abs * -1.0)
|
||||
* Quaternion::rotation_y(move2abs * 0.0);
|
||||
|
||||
next.tail.position = Vec3::new(0.0, s_a.tail.0, s_a.tail.1);
|
||||
next.tail.orientation = Quaternion::rotation_x(0.05 * fastalt * speednormcancel)
|
||||
* Quaternion::rotation_z(fast * 0.15 * speednormcancel);
|
||||
next.tail.position = Vec3::new(0.0, s_a.tail.0, s_a.tail.1);
|
||||
next.tail.orientation = Quaternion::rotation_x(0.05 * fastalt * speednormcancel)
|
||||
* Quaternion::rotation_z(fast * 0.15 * speednormcancel);
|
||||
},
|
||||
Some(ToolKind::Unique(UniqueKind::Husk)) => {
|
||||
next.hand_l.position = Vec3::new(
|
||||
-s_a.hand.0,
|
||||
s_a.hand.1 + move1abs * 3.0 + move2abs * 3.0,
|
||||
s_a.hand.2 + move1abs * 1.5,
|
||||
);
|
||||
next.hand_r.position = Vec3::new(
|
||||
s_a.hand.0,
|
||||
s_a.hand.1 + move1abs * 3.0 + move2abs * 3.0,
|
||||
s_a.hand.2 + move1abs * 1.5,
|
||||
);
|
||||
|
||||
next.hand_l.orientation = Quaternion::rotation_x(1.7 + move1 * 1.3 + move2 * -2.4)
|
||||
* Quaternion::rotation_y(-0.3 + move1abs * -0.35 + move2abs * 0.6);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(1.7 - move1 * 1.3 + move2 * 2.4)
|
||||
* Quaternion::rotation_y(0.3 + move1abs * -0.35 + move2abs * 0.6);
|
||||
next.head.orientation = Quaternion::rotation_x(move1abs * 0.1 + move2abs * -0.2)
|
||||
* Quaternion::rotation_y(move1 * 0.3 + move2 * -0.4);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
next
|
||||
}
|
||||
}
|
||||
|
@ -120,6 +120,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
|
||||
(Gnoll, _) => (5.5, -1.0),
|
||||
(Haniwa, _) => (0.0, 7.0),
|
||||
(Myrmidon, _) => (0.0, 8.0),
|
||||
(Husk, _) => (0.5, 8.5),
|
||||
},
|
||||
chest: match (body.species, body.body_type) {
|
||||
(Gnome, _) => (0.0, 9.0),
|
||||
@ -132,6 +133,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
|
||||
(Gnoll, _) => (0.0, 15.5),
|
||||
(Haniwa, _) => (0.0, 11.0),
|
||||
(Myrmidon, _) => (0.0, 11.0),
|
||||
(Husk, _) => (0.0, 13.0),
|
||||
},
|
||||
pants: match (body.species, body.body_type) {
|
||||
(Gnome, _) => (0.0, -3.0),
|
||||
@ -144,6 +146,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
|
||||
(Gnoll, _) => (0.5, -7.5),
|
||||
(Haniwa, _) => (0.0, -3.5),
|
||||
(Myrmidon, _) => (0.0, -3.0),
|
||||
(Husk, _) => (-1.0, -3.0),
|
||||
},
|
||||
tail: match (body.species, body.body_type) {
|
||||
(Gnome, _) => (0.0, 0.0),
|
||||
@ -156,6 +159,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
|
||||
(Gnoll, _) => (-2.5, -2.0),
|
||||
(Haniwa, _) => (-4.5, -2.0),
|
||||
(Myrmidon, _) => (-2.5, -1.0),
|
||||
(Husk, _) => (0.0, 0.0),
|
||||
},
|
||||
hand: match (body.species, body.body_type) {
|
||||
(Gnome, _) => (4.0, 0.5, -1.0),
|
||||
@ -168,6 +172,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
|
||||
(Gnoll, _) => (3.5, 0.5, -1.0),
|
||||
(Haniwa, _) => (4.25, -1.0, 1.5),
|
||||
(Myrmidon, _) => (3.5, 1.5, 2.0),
|
||||
(Husk, _) => (4.0, 0.0, 1.0),
|
||||
},
|
||||
foot: match (body.species, body.body_type) {
|
||||
(Gnome, _) => (3.0, 0.0, 4.0),
|
||||
@ -180,6 +185,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
|
||||
(Gnoll, _) => (3.0, 1.0, 7.0),
|
||||
(Haniwa, _) => (3.0, 0.5, 8.0),
|
||||
(Myrmidon, _) => (3.0, 0.5, 7.0),
|
||||
(Husk, _) => (4.0, 0.5, 7.0),
|
||||
},
|
||||
grip: match (body.species, body.body_type) {
|
||||
(Gnome, _) => (0.0, 0.0, 5.0),
|
||||
@ -192,6 +198,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
|
||||
(Gnoll, _) => (1.0, 0.0, 9.0),
|
||||
(Haniwa, _) => (0.0, 0.5, 8.0),
|
||||
(Myrmidon, _) => (0.0, 0.0, 8.0),
|
||||
(Husk, _) => (0.0, 0.0, 8.0),
|
||||
},
|
||||
scaler: match (body.species, body.body_type) {
|
||||
(Gnome, _) => 1.0,
|
||||
@ -204,6 +211,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
|
||||
(Gnoll, _) => 1.0,
|
||||
(Haniwa, _) => 1.4,
|
||||
(Myrmidon, _) => 1.5,
|
||||
(Husk, _) => 1.4,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,10 @@ use super::{
|
||||
super::{vek::*, Animation},
|
||||
BipedSmallSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::{comp::item::ToolKind, states::utils::StageSection};
|
||||
use common::{
|
||||
comp::item::{ToolKind, UniqueKind},
|
||||
states::utils::StageSection,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct StunnedAnimation;
|
||||
@ -150,6 +153,15 @@ impl Animation for StunnedAnimation {
|
||||
* Quaternion::rotation_y(-0.2 * speednorm)
|
||||
* Quaternion::rotation_z(0.5);
|
||||
},
|
||||
Some(ToolKind::Unique(UniqueKind::Husk)) => {
|
||||
next.hand_l.position = Vec3::new(-s_a.hand.0, s_a.hand.1, s_a.hand.2);
|
||||
next.hand_r.position = Vec3::new(s_a.hand.0, s_a.hand.1, s_a.hand.2);
|
||||
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(1.7) * Quaternion::rotation_y(-0.3);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(1.7) * Quaternion::rotation_y(0.3);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
} else {
|
||||
|
@ -2,7 +2,7 @@ use super::{
|
||||
super::{vek::*, Animation},
|
||||
BipedSmallSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::comp::item::ToolKind;
|
||||
use common::comp::item::{ToolKind, UniqueKind};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct WieldAnimation;
|
||||
@ -147,6 +147,14 @@ impl Animation for WieldAnimation {
|
||||
* Quaternion::rotation_y(-0.2 * speednorm)
|
||||
* Quaternion::rotation_z(0.5);
|
||||
},
|
||||
Some(ToolKind::Unique(UniqueKind::Husk)) => {
|
||||
next.hand_l.position = Vec3::new(-s_a.hand.0, s_a.hand.1, s_a.hand.2);
|
||||
next.hand_r.position = Vec3::new(s_a.hand.0, s_a.hand.1, s_a.hand.2);
|
||||
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(1.7) * Quaternion::rotation_y(-0.3);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(1.7) * Quaternion::rotation_y(0.3);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
|
@ -217,6 +217,6 @@ fn empty_ability_info() -> states::utils::AbilityInfo {
|
||||
tool: None,
|
||||
hand: None,
|
||||
input: InputKind::Primary,
|
||||
select_pos: None,
|
||||
input_attr: None,
|
||||
}
|
||||
}
|
||||
|
@ -353,6 +353,6 @@ fn empty_ability_info() -> states::utils::AbilityInfo {
|
||||
tool: None,
|
||||
hand: None,
|
||||
input: InputKind::Primary,
|
||||
select_pos: None,
|
||||
input_attr: None,
|
||||
}
|
||||
}
|
||||
|
@ -359,16 +359,18 @@ impl SfxMgr {
|
||||
let file_ref = "voxygen.audio.sfx.abilities.staff_channeling";
|
||||
audio.play_sfx(file_ref, *pos, None);
|
||||
},
|
||||
beam::FrontendSpecifier::Flamethrower => {
|
||||
beam::FrontendSpecifier::Flamethrower | beam::FrontendSpecifier::Cultist => {
|
||||
let file_ref = "voxygen.audio.sfx.abilities.flame_thrower";
|
||||
audio.play_sfx(file_ref, *pos, None);
|
||||
},
|
||||
},
|
||||
Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => {},
|
||||
Outcome::BreakBlock { pos, .. } => {
|
||||
let file_ref = "voxygen.audio.sfx.footsteps.stone_step_1";
|
||||
audio.play_sfx(file_ref, pos.map(|e| e as f32 + 0.5), Some(3.0));
|
||||
},
|
||||
Outcome::ExpChange { .. }
|
||||
| Outcome::ComboChange { .. }
|
||||
| Outcome::SummonedCreature { .. } => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user