Merge branch 'sam/mindflayer-rework' into 'master'

Mindflayer Rework

See merge request veloren/veloren!1957
This commit is contained in:
Samuel Keiffer 2021-03-28 02:10:51 +00:00
commit 05ed391bb2
110 changed files with 2195 additions and 617 deletions

View File

@ -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

View File

@ -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,

View File

@ -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),
)

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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),
)

View 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,
)

View 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,
)

View 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,
)

View File

@ -0,0 +1,5 @@
Blink(
buildup_duration: 0.5,
recover_duration: 0.25,
max_range: 100.0,
)

View 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,
)

View 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,
),
)

View File

@ -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,

View File

@ -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,

View File

@ -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),
)

View File

@ -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,

View File

@ -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,

View File

@ -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",

View File

@ -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

View File

@ -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: [],
)

View 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: [],
)

View 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: [],
)

View 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: [],
)

View File

@ -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: [],
)

View 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: [],
)

View File

@ -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,

View 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: [],
)

View File

@ -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"),
]

View File

@ -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"),
]

View File

@ -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"),
]

View File

@ -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"),
]

View 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"),
]

View File

@ -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

Binary file not shown.

View File

@ -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(

View File

@ -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: (

View File

@ -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)),
),
},
))

View File

@ -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)),
)
),
},
))

View File

@ -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)),
)
),
},
))

View File

@ -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)),
),
},
))

View File

@ -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)),
),
},
))

View File

@ -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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

View File

@ -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 {

View File

@ -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(),
}

View File

@ -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),

View File

@ -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,
}),
}
}
}

View File

@ -35,6 +35,7 @@ pub enum Tactic {
Turret,
FixedTurret,
RotatingTurret,
Mindflayer,
}
#[derive(Copy, Clone, Debug, PartialEq)]

View File

@ -51,4 +51,5 @@ pub enum FrontendSpecifier {
Flamethrower,
LifestealBeam,
HealingBeam,
Cultist,
}

View File

@ -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,
}
}

View File

@ -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> {

View File

@ -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 => (

View File

@ -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 {

View File

@ -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)]

View File

@ -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"))]

View File

@ -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,
}

View File

@ -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(

View File

@ -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;

View File

@ -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 {

View File

@ -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> {

View File

@ -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,
}

View File

@ -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,

View File

@ -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);

View 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>,
}

View File

@ -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,
}
}
}

View 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
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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,
}

View File

@ -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(),
}
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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
}

View File

@ -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)]

View File

@ -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"
)
});
}
}
}

View File

@ -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),
}
}

View File

@ -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
}

View File

@ -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);

View File

@ -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,

View File

@ -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);

View 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
}
}

View File

@ -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);

View File

@ -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),

View File

@ -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 =

View File

@ -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);
},
_ => {},
}

View 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
}
}

View File

@ -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 =

View File

@ -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
}
}

View File

@ -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,
},
}
}

View File

@ -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 {

View File

@ -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);
},
_ => {},
}

View File

@ -217,6 +217,6 @@ fn empty_ability_info() -> states::utils::AbilityInfo {
tool: None,
hand: None,
input: InputKind::Primary,
select_pos: None,
input_attr: None,
}
}

View File

@ -353,6 +353,6 @@ fn empty_ability_info() -> states::utils::AbilityInfo {
tool: None,
hand: None,
input: InputKind::Primary,
select_pos: None,
input_attr: None,
}
}

View File

@ -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