mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'qsto/phoenix' into 'master'
Phoenix overhaul See merge request veloren/veloren!4073
This commit is contained in:
commit
233f1d6bac
@ -716,10 +716,14 @@
|
||||
],
|
||||
),
|
||||
Custom("Bird Large Fire"): (
|
||||
primary: Simple(None, "common.abilities.custom.birdlargefire.firebomb"),
|
||||
secondary: Simple(None, "common.abilities.custom.birdlargefire.triplestrike"),
|
||||
primary: Simple(None, "common.abilities.custom.birdlargefire.longstrike"),
|
||||
secondary: Simple(None, "common.abilities.custom.birdlargefire.shortstrike"),
|
||||
abilities: [
|
||||
Simple(None, "common.abilities.custom.birdlargefire.fireshockwave"),
|
||||
Simple(None, "common.abilities.custom.birdlargefire.legstrike"),
|
||||
Simple(None, "common.abilities.custom.birdlargefire.summontornadoes"),
|
||||
Simple(None, "common.abilities.custom.birdlargefire.firerain"),
|
||||
Simple(None, "common.abilities.custom.birdlargefire.heat_laser"),
|
||||
Simple(None, "common.abilities.custom.birdlargefire.from_the_ashes"),
|
||||
],
|
||||
),
|
||||
Custom("Flame Wyvern"): (
|
||||
@ -807,6 +811,11 @@
|
||||
secondary: Simple(None, "common.abilities.empty.basic"),
|
||||
abilities: [],
|
||||
),
|
||||
Custom("FieryTornado"): (
|
||||
primary: Simple(None, "common.abilities.custom.fiery_tornado.fiery_spin"),
|
||||
secondary: Simple(None, "common.abilities.custom.fiery_tornado.fiery_aura"),
|
||||
abilities: [],
|
||||
),
|
||||
Custom("Golf Club"): (
|
||||
primary: Simple(None, "common.abilities.hammer.singlestrike"),
|
||||
secondary: Simple(None, "common.abilities.tool.golf_club.charged"),
|
||||
|
@ -1,20 +0,0 @@
|
||||
BasicRanged(
|
||||
energy_cost: 0,
|
||||
buildup_duration: 1.0,
|
||||
recover_duration: 0.7,
|
||||
projectile: Fireball(
|
||||
damage: 20.0,
|
||||
radius: 5.0,
|
||||
energy_regen: 5.0,
|
||||
min_falloff: 0.5,
|
||||
),
|
||||
projectile_body: Object(BoltFire),
|
||||
/*projectile_light: Some(LightEmitter {
|
||||
col: (1.0, 0.75, 0.11).into(),
|
||||
..Default::default()
|
||||
}),*/
|
||||
projectile_speed: 60.0,
|
||||
num_projectiles: 1,
|
||||
projectile_spread: 0.0,
|
||||
move_efficiency: 0.3,
|
||||
)
|
32
assets/common/abilities/custom/birdlargefire/firerain.ron
Normal file
32
assets/common/abilities/custom/birdlargefire/firerain.ron
Normal file
@ -0,0 +1,32 @@
|
||||
RepeaterRanged(
|
||||
energy_cost: 0.0,
|
||||
buildup_duration: 0.2,
|
||||
shoot_duration: 0.3,
|
||||
recover_duration: 0.5,
|
||||
max_speed: 8.0,
|
||||
half_speed_at: 5,
|
||||
projectile: FireDroplet(
|
||||
damage: 30.0,
|
||||
radius: 12.0,
|
||||
min_falloff: 0.5,
|
||||
energy_regen: 0,
|
||||
reagent: Some(Phoenix),
|
||||
),
|
||||
projectile_body: Object(FireRainDrop),
|
||||
projectile_light: Some(LightEmitter(
|
||||
col: Rgb(
|
||||
r: 1.0,
|
||||
g: 0.8,
|
||||
b: 0.3,
|
||||
),
|
||||
strength: 10.0,
|
||||
flicker: 5.0,
|
||||
animated: true,
|
||||
)),
|
||||
projectile_speed: 0.0,
|
||||
properties_of_aoe: Some(ProjectileOffset(
|
||||
radius: 30,
|
||||
height: 20,
|
||||
)),
|
||||
specifier: Some(FireRain),
|
||||
)
|
@ -1,19 +0,0 @@
|
||||
BasicBeam(
|
||||
buildup_duration: 0.8,
|
||||
recover_duration: 0.5,
|
||||
beam_duration: 0.5,
|
||||
damage: 10.0,
|
||||
tick_rate: 3.0,
|
||||
range: 15.0,
|
||||
max_angle: 22.5,
|
||||
damage_effect: Some(Buff((
|
||||
kind: Burning,
|
||||
dur_secs: 10.0,
|
||||
strength: DamageFraction(0.5),
|
||||
chance: 0.25,
|
||||
))),
|
||||
energy_regen: 0,
|
||||
energy_drain: 0,
|
||||
ori_rate: 0.3,
|
||||
specifier: Flamethrower,
|
||||
)
|
@ -0,0 +1,10 @@
|
||||
SelfBuff(
|
||||
buildup_duration: 1.25,
|
||||
cast_duration: 1.8,
|
||||
recover_duration: 1.25,
|
||||
buff_kind: Regeneration,
|
||||
buff_strength: 1500.0,
|
||||
buff_duration: Some(3.0),
|
||||
energy_cost: 0,
|
||||
specifier: Some(FromTheAshes),
|
||||
)
|
19
assets/common/abilities/custom/birdlargefire/heat_laser.ron
Normal file
19
assets/common/abilities/custom/birdlargefire/heat_laser.ron
Normal file
@ -0,0 +1,19 @@
|
||||
BasicBeam(
|
||||
buildup_duration: 0.5,
|
||||
recover_duration: 0.3,
|
||||
beam_duration: 1.0,
|
||||
damage: 30.0,
|
||||
tick_rate: 1.0,
|
||||
range: 120.0,
|
||||
max_angle: 1.0,
|
||||
damage_effect: Some(Buff((
|
||||
kind: Burning,
|
||||
dur_secs: 5.0,
|
||||
strength: DamageFraction(0.75),
|
||||
chance: 0.75,
|
||||
))),
|
||||
energy_regen: 0,
|
||||
energy_drain: 0,
|
||||
ori_rate: 0.07,
|
||||
specifier: PhoenixLaser,
|
||||
)
|
26
assets/common/abilities/custom/birdlargefire/legstrike.ron
Normal file
26
assets/common/abilities/custom/birdlargefire/legstrike.ron
Normal file
@ -0,0 +1,26 @@
|
||||
ComboMelee2(
|
||||
strikes: [
|
||||
(
|
||||
melee_constructor: (
|
||||
kind: Slash(
|
||||
damage: 35,
|
||||
poise: 0,
|
||||
knockback: 15,
|
||||
energy_regen: 0,
|
||||
),
|
||||
range: 6.0,
|
||||
angle: 90.0,
|
||||
),
|
||||
buildup_duration: 0.1,
|
||||
swing_duration: 0.1,
|
||||
hit_timing: 0.5,
|
||||
recover_duration: 0.1,
|
||||
movement: (
|
||||
swing: Some(Forward(0.5)),
|
||||
),
|
||||
ori_modifier: 0.7,
|
||||
),
|
||||
],
|
||||
energy_cost_per_strike: 0,
|
||||
auto_progress: false,
|
||||
)
|
26
assets/common/abilities/custom/birdlargefire/longstrike.ron
Normal file
26
assets/common/abilities/custom/birdlargefire/longstrike.ron
Normal file
@ -0,0 +1,26 @@
|
||||
ComboMelee2(
|
||||
strikes: [
|
||||
(
|
||||
melee_constructor: (
|
||||
kind: Slash(
|
||||
damage: 30,
|
||||
poise: 0,
|
||||
knockback: 10,
|
||||
energy_regen: 0,
|
||||
),
|
||||
range: 6.0,
|
||||
angle: 90.0,
|
||||
),
|
||||
buildup_duration: 0.6,
|
||||
swing_duration: 0.3,
|
||||
hit_timing: 0.5,
|
||||
recover_duration: 0.1,
|
||||
movement: (
|
||||
swing: Some(Forward(0.5)),
|
||||
),
|
||||
ori_modifier: 0.7,
|
||||
),
|
||||
],
|
||||
energy_cost_per_strike: 0,
|
||||
auto_progress: true,
|
||||
)
|
26
assets/common/abilities/custom/birdlargefire/shortstrike.ron
Normal file
26
assets/common/abilities/custom/birdlargefire/shortstrike.ron
Normal file
@ -0,0 +1,26 @@
|
||||
ComboMelee2(
|
||||
strikes: [
|
||||
(
|
||||
melee_constructor: (
|
||||
kind: Slash(
|
||||
damage: 20,
|
||||
poise: 0,
|
||||
knockback: 5,
|
||||
energy_regen: 0,
|
||||
),
|
||||
range: 6.0,
|
||||
angle: 90.0,
|
||||
),
|
||||
buildup_duration: 0.2,
|
||||
swing_duration: 0.1,
|
||||
hit_timing: 0.5,
|
||||
recover_duration: 0.1,
|
||||
movement: (
|
||||
swing: Some(Forward(0.5)),
|
||||
),
|
||||
ori_modifier: 0.7,
|
||||
),
|
||||
],
|
||||
energy_cost_per_strike: 0,
|
||||
auto_progress: true,
|
||||
)
|
@ -0,0 +1,18 @@
|
||||
BasicSummon(
|
||||
buildup_duration: 0.75,
|
||||
cast_duration: 0.25,
|
||||
recover_duration: 0.5,
|
||||
summon_amount: 12,
|
||||
summon_distance: (8, 10),
|
||||
summon_info: (
|
||||
body: Object(FieryTornado),
|
||||
scale: None,
|
||||
has_health: false,
|
||||
loadout_config: None,
|
||||
skillset_config: None,
|
||||
),
|
||||
duration: Some((
|
||||
secs: 10,
|
||||
nanos: 0,
|
||||
)),
|
||||
)
|
@ -11,7 +11,7 @@ BasicAura(
|
||||
category: Magical,
|
||||
),
|
||||
],
|
||||
aura_duration: 2.0,
|
||||
aura_duration: Some(2.0),
|
||||
range: 10.0,
|
||||
energy_cost: 0.0,
|
||||
scales_with_combo: false,
|
||||
|
@ -11,7 +11,7 @@ BasicAura(
|
||||
category: Magical,
|
||||
),
|
||||
],
|
||||
aura_duration: 2.0,
|
||||
aura_duration: Some(2.0),
|
||||
range: 10.0,
|
||||
energy_cost: 0.0,
|
||||
scales_with_combo: false,
|
||||
|
19
assets/common/abilities/custom/fiery_tornado/fiery_aura.ron
Normal file
19
assets/common/abilities/custom/fiery_tornado/fiery_aura.ron
Normal file
@ -0,0 +1,19 @@
|
||||
BasicAura(
|
||||
buildup_duration: 0.0,
|
||||
cast_duration: 0.0,
|
||||
recover_duration: 0.0,
|
||||
targets: OutOfGroup,
|
||||
auras: [
|
||||
(
|
||||
kind: Heatstroke,
|
||||
strength: 1.0,
|
||||
duration: Some(5.0),
|
||||
category: Magical,
|
||||
),
|
||||
],
|
||||
aura_duration: None,
|
||||
range: 20.0,
|
||||
energy_cost: 0.0,
|
||||
scales_with_combo: false,
|
||||
specifier: Some(FieryAura),
|
||||
)
|
25
assets/common/abilities/custom/fiery_tornado/fiery_spin.ron
Normal file
25
assets/common/abilities/custom/fiery_tornado/fiery_spin.ron
Normal file
@ -0,0 +1,25 @@
|
||||
RapidMelee(
|
||||
buildup_duration: 0.0,
|
||||
swing_duration: 0.5,
|
||||
recover_duration: 0.0,
|
||||
melee_constructor: (
|
||||
kind: Slash(
|
||||
damage: 80,
|
||||
poise: 0,
|
||||
knockback: 50,
|
||||
energy_regen: 0,
|
||||
),
|
||||
range: 3.5,
|
||||
angle: 360,
|
||||
multi_target: Some(Normal),
|
||||
damage_effect: Some(Buff((
|
||||
kind: Burning,
|
||||
dur_secs: 8.0,
|
||||
strength: Value(0.3),
|
||||
chance: 1.0,
|
||||
))),
|
||||
),
|
||||
energy_cost: 0,
|
||||
ori_modifier: 1.0,
|
||||
move_modifier: 1.0,
|
||||
)
|
@ -11,7 +11,7 @@ BasicAura(
|
||||
category: Magical,
|
||||
),
|
||||
],
|
||||
aura_duration: 34.75,
|
||||
aura_duration: Some(34.75),
|
||||
range: 18.0,
|
||||
energy_cost: 0.0,
|
||||
scales_with_combo: false,
|
||||
|
@ -17,7 +17,7 @@ BasicAura(
|
||||
category: Magical,
|
||||
),
|
||||
],
|
||||
aura_duration: 2,
|
||||
aura_duration: Some(2),
|
||||
range: 50,
|
||||
energy_cost: 0,
|
||||
scales_with_combo: false,
|
||||
|
@ -11,7 +11,7 @@ BasicAura(
|
||||
category: Magical,
|
||||
),
|
||||
],
|
||||
aura_duration: 2,
|
||||
aura_duration: Some(2),
|
||||
range: 50,
|
||||
energy_cost: 0,
|
||||
scales_with_combo: false,
|
||||
|
@ -11,7 +11,7 @@ BasicAura(
|
||||
category: Magical,
|
||||
),
|
||||
],
|
||||
aura_duration: 2,
|
||||
aura_duration: Some(2),
|
||||
range: 50,
|
||||
energy_cost: 0,
|
||||
scales_with_combo: false,
|
||||
|
@ -11,7 +11,7 @@ BasicAura(
|
||||
category: Magical,
|
||||
),
|
||||
],
|
||||
aura_duration: 1.0,
|
||||
aura_duration: Some(1.0),
|
||||
range: 25.0,
|
||||
energy_cost: 20.0,
|
||||
scales_with_combo: true,
|
||||
|
@ -11,7 +11,7 @@ BasicAura(
|
||||
category: Magical,
|
||||
),
|
||||
],
|
||||
aura_duration: 1.0,
|
||||
aura_duration: Some(1.0),
|
||||
range: 25.0,
|
||||
energy_cost: 35.0,
|
||||
scales_with_combo: false,
|
||||
|
@ -3,7 +3,7 @@
|
||||
name: Automatic,
|
||||
body: RandomWith("oni_blue"),
|
||||
alignment: Alignment(Enemy),
|
||||
loot: LootTable("common.loot_tables.creature.biped_large.default"),
|
||||
loot: LootTable("common.loot_tables.creature.biped_large.blue_oni"),
|
||||
inventory: (
|
||||
loadout: FromBody,
|
||||
),
|
||||
|
@ -3,7 +3,7 @@
|
||||
name: Automatic,
|
||||
body: RandomWith("oni_red"),
|
||||
alignment: Alignment(Enemy),
|
||||
loot: LootTable("common.loot_tables.creature.biped_large.default"),
|
||||
loot: LootTable("common.loot_tables.creature.biped_large.red_oni"),
|
||||
inventory: (
|
||||
loadout: FromBody,
|
||||
),
|
||||
|
13
assets/common/items/npc_armor/bird_large/phoenix.ron
Normal file
13
assets/common/items/npc_armor/bird_large/phoenix.ron
Normal file
@ -0,0 +1,13 @@
|
||||
ItemDef(
|
||||
name: "Phoenix Armor",
|
||||
description: "The thickest feather you have ever seen!",
|
||||
kind: Armor((
|
||||
kind: Chest,
|
||||
stats: Direct((
|
||||
protection: Some(Normal(60.0)),
|
||||
poise_resilience: Some(Normal(0.0)),
|
||||
)),
|
||||
)),
|
||||
quality: Epic,
|
||||
tags: [],
|
||||
)
|
@ -1,6 +1,6 @@
|
||||
ItemDef(
|
||||
name: "Bird Large Fire",
|
||||
description: "testing123",
|
||||
description: "Fiery touch of a mighty aerial beast",
|
||||
kind: Tool((
|
||||
kind: Natural,
|
||||
hands: Two,
|
||||
@ -14,7 +14,7 @@ ItemDef(
|
||||
buff_strength: 1.0,
|
||||
),
|
||||
)),
|
||||
quality: Low,
|
||||
quality: Epic,
|
||||
tags: [],
|
||||
ability_spec: Some(Custom("Bird Large Fire")),
|
||||
)
|
20
assets/common/items/npc_weapons/unique/fiery_tornado.ron
Normal file
20
assets/common/items/npc_weapons/unique/fiery_tornado.ron
Normal file
@ -0,0 +1,20 @@
|
||||
ItemDef(
|
||||
name: "FieryTornado",
|
||||
description: "Fiery Tornado weapon",
|
||||
kind: Tool((
|
||||
kind: Natural,
|
||||
hands: Two,
|
||||
stats: (
|
||||
equip_time_secs: 0.01,
|
||||
power: 1.0,
|
||||
effect_power: 1.0,
|
||||
speed: 1.0,
|
||||
range: 1.0,
|
||||
energy_efficiency: 1.0,
|
||||
buff_strength: 1.0,
|
||||
),
|
||||
)),
|
||||
quality: Low,
|
||||
tags: [],
|
||||
ability_spec: Some(Custom("FieryTornado")),
|
||||
)
|
@ -7,7 +7,7 @@ ItemDef(
|
||||
stats: (
|
||||
equip_time_secs: 0.01,
|
||||
power: 1.0,
|
||||
effect_power: 0.0,
|
||||
effect_power: 1.0,
|
||||
speed: 1.0,
|
||||
range: 1.0,
|
||||
energy_efficiency: 1.0,
|
||||
|
@ -0,0 +1,5 @@
|
||||
[
|
||||
(1.0, LootTable("common.loot_tables.food.prepared")),
|
||||
(2.0, LootTable("common.loot_tables.cave_large")),
|
||||
(0.4, Item("common.items.glider.morpho")),
|
||||
]
|
@ -1,9 +1,8 @@
|
||||
[
|
||||
(1.8, All([
|
||||
(1.9, All([
|
||||
MultiDrop(Item("common.items.utility.coins"), 200, 500),
|
||||
MultiDrop(Item("common.items.mineral.ingot.bloodsteel"), 2, 4),
|
||||
MultiDrop(Item("common.items.mineral.ingot.silver"), 0, 2),
|
||||
])),
|
||||
(0.1, Item("common.items.glider.morpho")),
|
||||
(0.1, Item("common.items.weapons.sword.caladbolg")),
|
||||
]
|
@ -0,0 +1,5 @@
|
||||
[
|
||||
(1.0, LootTable("common.loot_tables.food.prepared")),
|
||||
(2.0, LootTable("common.loot_tables.cave_large")),
|
||||
(0.4, Item("common.items.glider.monarch")),
|
||||
]
|
@ -1,5 +1,4 @@
|
||||
[
|
||||
(1.2, Nothing),
|
||||
(0.4, Item("common.items.glider.monarch")),
|
||||
(1.6, Nothing),
|
||||
(0.4, Item("common.items.weapons.bow.sagitta")),
|
||||
]
|
@ -1517,6 +1517,13 @@
|
||||
threshold: 0.8,
|
||||
subtitle: "subtitle-portal-teleported",
|
||||
),
|
||||
FromTheAshes: (
|
||||
files: [
|
||||
"voxygen.audio.sfx.abilities.heal",
|
||||
],
|
||||
threshold: 1.8,
|
||||
subtitle: "subtitle-attack-from-the-ashes",
|
||||
),
|
||||
|
||||
// Utterances (NPCs)
|
||||
|
||||
@ -1557,6 +1564,14 @@
|
||||
threshold: 1.3,
|
||||
subtitle: "subtitle-utterance-wyvern-angry",
|
||||
),
|
||||
Utterance(Angry, Phoenix): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.utterance.phoenix_angry_0",
|
||||
"voxygen.audio.sfx.utterance.phoenix_angry_1",
|
||||
],
|
||||
threshold: 3.1,
|
||||
subtitle: "subtitle-utterance-phoenix-angry",
|
||||
),
|
||||
Utterance(Angry, Adlet): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.utterance.adlet_angry1",
|
||||
@ -1765,6 +1780,14 @@
|
||||
threshold: 1.2,
|
||||
subtitle: "subtitle-utterance-wyvern-hurt",
|
||||
),
|
||||
Utterance(Hurt, Phoenix): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.utterance.phoenix_hurt_0",
|
||||
"voxygen.audio.sfx.utterance.phoenix_hurt_1",
|
||||
],
|
||||
threshold: 1.5,
|
||||
subtitle: "subtitle-utterance-phoenix-hurt",
|
||||
),
|
||||
Utterance(Angry, Wendigo): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.utterance.wendigo_angry1",
|
||||
|
BIN
assets/voxygen/audio/sfx/abilities/heal.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/abilities/heal.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/utterance/phoenix_angry_0.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/phoenix_angry_0.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/utterance/phoenix_angry_1.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/phoenix_angry_1.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/utterance/phoenix_hurt_0.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/phoenix_hurt_0.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/utterance/phoenix_hurt_1.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/utterance/phoenix_hurt_1.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/de_buffs/debuff_heatstroke_0.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/de_buffs/debuff_heatstroke_0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -109,6 +109,9 @@ buff-desc-bloodfeast = You restore life on attacks against bleeding enemies
|
||||
## Berserk
|
||||
buff-title-berserk = Berserk
|
||||
buff-desc-berserk = You are in a berserking rage, causing your attacks to be more powerful and swift, and increasing your speed. However, as a result your defensive capability is less.
|
||||
## Heatstroke
|
||||
buff-title-heatstroke = Heatstroke
|
||||
buff-desc-heatstroke = You were exposed to heat and now suffer from heatstroke, your energy reward and movement speed are cut down. Chill.
|
||||
## Util
|
||||
buff-text-over_seconds = over { $dur_secs } seconds
|
||||
buff-text-for_seconds = for { $dur_secs } seconds
|
||||
|
@ -111,6 +111,7 @@ subtitle-attack-icy_spikes = Icy spikes
|
||||
subtitle-attack-ice_crack = Ice crack
|
||||
subtitle-attack-steam = Steam
|
||||
subtitle-attack-shovel = Shovel digging
|
||||
subtitle-attack-from-the-ashes = Heal Sound
|
||||
|
||||
subtitle-consume_potion = Drinking potion
|
||||
subtitle-consume_apple = Eating apple
|
||||
@ -154,3 +155,5 @@ subtitle-utterance-wolf-angry = Wolf growling
|
||||
subtitle-utterance-wolf-hurt = Wolf whining
|
||||
subtitle-utterance-wyvern-angry = Wyvern roaring
|
||||
subtitle-utterance-wyvern-hurt = Wyvern hurting
|
||||
subtitle-utterance-phoenix-angry = Phoenix screaming
|
||||
subtitle-utterance-phoenix-hurt = Phoenix hurting
|
@ -84,6 +84,16 @@ const int CYCLOPS_CHARGE = 43;
|
||||
const int PORTAL_FIZZ = 45;
|
||||
const int INK = 46;
|
||||
const int WHIRLWIND = 47;
|
||||
const int FIERY_BURST = 48;
|
||||
const int FIERY_BURST_VORTEX = 49;
|
||||
const int FIERY_BURST_SPARKS = 50;
|
||||
const int FIERY_BURST_ASH = 51;
|
||||
const int FIERY_TORNADO = 52;
|
||||
const int PHOENIX_CLOUD = 53;
|
||||
const int FIERY_DROPLET_TRACE = 54;
|
||||
const int ENERGY_PHOENIX = 55;
|
||||
const int PHOENIX_BEAM = 56;
|
||||
const int PHOENIX_BUILD_UP_AIM = 57;
|
||||
|
||||
// meters per second squared (acceleration)
|
||||
const float earth_gravity = 9.807;
|
||||
@ -727,6 +737,240 @@ void main() {
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
|
||||
);
|
||||
break;
|
||||
case FIERY_BURST:
|
||||
f_reflect = 0.0;
|
||||
float fiery_radius = start_end(1.0 - pow(abs(rand5), 5.0), 1.0) * length(inst_dir);
|
||||
float fiery_color1 = (7.0 + 1.0 * percent()) * min(1.0, percent() * 4.0) * 1.5;
|
||||
float fiery_color2 = (4.0 - 2.0 * percent() + 1.3 * rand5 * slow_end(0.0)) * min(1.0, percent() * 4.0) * 1.3;
|
||||
float fiery_color3 = 1.0 + 0.3 * percent();
|
||||
attr = Attr(
|
||||
spiral_motion(
|
||||
vec3(
|
||||
0.0,
|
||||
0.0,
|
||||
(rand3 + 1.0)
|
||||
* max(
|
||||
((percent() * 8.0) * (1.0 - step(0.2, percent()))),
|
||||
((2.0 * (1.0 - percent())) * (step(0.2, percent())))
|
||||
)
|
||||
),
|
||||
fiery_radius,
|
||||
lifetime,
|
||||
max(0.1, step(0.6, percent())) * 3.0 * abs(rand0),
|
||||
rand1 * 2.0 * PI) + vec3(0.0, 0.0, rand2),
|
||||
vec3(6.0 * abs(rand4) * (1.0 - slow_start(2.0)) * pow(fiery_radius / length(inst_dir), 0.5)),
|
||||
vec4(fiery_color1, fiery_color2, fiery_color3, slow_end(0.4)),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3.0)
|
||||
);
|
||||
break;
|
||||
case FIERY_BURST_VORTEX:
|
||||
f_reflect = 0.0;
|
||||
float fiery_vortex_color1 = (min(1, percent() * 2) * (5 + 1 * percent() + 1 * slow_end(0)) * 1.5);
|
||||
float fiery_vortex_color2 = (min(1, percent() * 2) * (4 - 2.4 * percent() + 1.3 * rand5 * slow_end(0)) * 1.3);
|
||||
float fiery_vortex_color3 = 0;
|
||||
attr = Attr(
|
||||
spiral_motion(
|
||||
vec3(
|
||||
0,
|
||||
0,
|
||||
(0 + 0.5 * rand4 ) + 4.0
|
||||
* max(
|
||||
((percent() * 8) * (1 - step(0.2, percent()))), // first 20% of lifetime particle moves up, then goes down
|
||||
((2 * (1 - percent())) * (step(0.2, percent())))// to avoid tearing multi should have same proportion as edge(here: 8 before, 2 after)
|
||||
)
|
||||
),
|
||||
abs(rand0) + 0.5 * 10 * percent(),
|
||||
percent(),
|
||||
10.0 * abs(rand2),
|
||||
rand3),
|
||||
vec3((2.5 * (1 - slow_start(0.05)))),
|
||||
vec4(fiery_vortex_color1, fiery_vortex_color2, fiery_vortex_color3, start_end(0.5, 1.5) * abs(rand2)),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
|
||||
);
|
||||
break;
|
||||
case FIERY_BURST_SPARKS:
|
||||
f_reflect = 0.0;
|
||||
// sparks should flicker, so it stops glowing for 18% of time 4 times per second, same thing used in 4th float of RGBA vector
|
||||
float fiery_sparks_color1 = 2 + 1 * rand2 + 2 * step(0.18, fract(tick.x*4));
|
||||
float fiery_sparks_color2 = 4 + 1 * rand2 + 4 * step(0.18, fract(tick.x*4));
|
||||
float fiery_sparks_color3 = 4 + 6 * step(0.18, fract(tick.x*4));
|
||||
attr = Attr(
|
||||
spiral_motion(vec3(0, 0, 5), abs(rand0) + abs(rand1) * percent() * 4.0, percent(), 8.0 * abs(rand2), rand3),
|
||||
vec3((2.5 * (1 - slow_start(0.05)))),
|
||||
vec4(fiery_sparks_color1, fiery_sparks_color2, fiery_sparks_color3, 0.5 + 0.5 * step(0.18, fract(tick.x*4))),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
|
||||
);
|
||||
break;
|
||||
case FIERY_BURST_ASH:
|
||||
f_reflect = 0.0;
|
||||
/// inst_dir holds info about:
|
||||
/// .x: radius of random spawn
|
||||
float fiery_ash_rand_rad = inst_dir.x;
|
||||
/// .y:
|
||||
/// in fract: relative time of "setting on fire"
|
||||
/// in int: radius of curve
|
||||
float fiery_ash_radius = floor(inst_dir.y);
|
||||
float fiery_ash_edge = inst_dir.y - fiery_ash_radius;
|
||||
/// .z: height of the flight
|
||||
float fiery_ash_height = inst_dir.z;
|
||||
// {FOR PHOENIX "from the ashes"}sets ash on fire at 0.4 of lifetime, then makes it lose glow, representing losing heat
|
||||
float fiery_ash_color1 = (2 + 1 * percent() * slow_end(0))
|
||||
* (max(
|
||||
1,
|
||||
8 * step(fiery_ash_edge, percent()) * (1.4 - percent()))
|
||||
);
|
||||
float fiery_ash_color2 = (2 - 1 * percent() + 0.3 * abs(rand5) * slow_end(0.5))
|
||||
* (max(
|
||||
1,
|
||||
6.5 * step(fiery_ash_edge, percent()) * (1.4 - percent()))
|
||||
);
|
||||
float fiery_ash_color3 = 1.5;
|
||||
attr = Attr(
|
||||
spiral_motion(
|
||||
vec3(
|
||||
0.0,
|
||||
0.0,
|
||||
fiery_ash_height// {FOR PHOENIX "from the ashes"} 8.58
|
||||
),
|
||||
abs(rand0 / 2.0 + 1.0)
|
||||
* max(1.0, ((percent() * fiery_ash_radius * 0.8) * (1.0 - step(0.2, percent())))) // part of lifetime particle moves to periphery
|
||||
* max(1.0, (fiery_ash_radius * 0.2 * (1.0 - percent()) * (step(0.2, percent())))),// then back to center
|
||||
percent(),
|
||||
6.0 * abs(rand2),
|
||||
rand3 * 5.0
|
||||
)
|
||||
+ vec3((rand6 + rand5) * fiery_ash_rand_rad, (rand8 + rand3) * fiery_ash_rand_rad, abs(rand0)),//makes it apear randomly above base animation (Fiery Burst)
|
||||
vec3((2.5 * (1 - slow_start(0.0)))),
|
||||
vec4(fiery_ash_color1, fiery_ash_color2, fiery_ash_color3, abs(rand2) * slow_end(0.3)),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
|
||||
);
|
||||
break;
|
||||
case FIERY_TORNADO:
|
||||
f_reflect = 0.0;
|
||||
float fiery_tornado_color1 = (2.6 + 0.5 * percent())
|
||||
* 4.0 * max(0.5, percent() * 1.2);
|
||||
float fiery_tornado_color2 = (1.7 - 0.6 * pow(1.0 - percent(), 2.0) + 0.3 * abs(rand5))
|
||||
* 2.0 * max(0.45, percent() * 1.2);
|
||||
float fiery_tornado_color3 = 1.5 * max(0.6, percent());
|
||||
attr = Attr(
|
||||
spiral_motion(vec3(0, 0, 6.0 + rand3 * 1.5), abs(rand0) + abs(rand1) * percent() * 3.0, percent(), 15.0 * abs(rand2), -inst_time),
|
||||
vec3((2.5 * (1 - slow_start(0.05)))),
|
||||
vec4(fiery_tornado_color1, fiery_tornado_color2, fiery_tornado_color3, 0.5),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
|
||||
);
|
||||
break;
|
||||
case PHOENIX_CLOUD:
|
||||
float PC_spin = floor(inst_dir.x);
|
||||
float refl = floor(inst_dir.y);
|
||||
float PC_size = floor(inst_dir.z);
|
||||
//best is 0.4 - reflects some light but only part as
|
||||
f_reflect = refl * 0.1;
|
||||
// modifies by + 5% to -15%, if color is less than 0.5 it will get from +10% to +25% to it's value
|
||||
float PC_rand_color_factor = rand0 * 0.05;
|
||||
float PC_R = inst_dir.x - PC_spin;
|
||||
PC_R += PC_R * PC_rand_color_factor * step(0.05, PC_R) * -abs(PC_rand_color_factor * 2.0)
|
||||
+ PC_R * (1.0 - step(0.05, PC_R)) * max(abs(PC_rand_color_factor), 0.02) * 5.0;
|
||||
float PC_G = inst_dir.y - refl;
|
||||
PC_G += PC_G * PC_rand_color_factor * step(0.05, PC_G) * -abs(PC_rand_color_factor * 2.0)
|
||||
+ PC_G * (1.0 - step(0.05, PC_G)) * max(abs(PC_rand_color_factor), 0.02) * 5.0;
|
||||
float PC_B = inst_dir.z - PC_size;
|
||||
PC_B += PC_B * PC_rand_color_factor * step(0.05, PC_B) * -abs(PC_rand_color_factor * 2.0)
|
||||
+ PC_B * (1.0 - step(0.05, PC_B)) * max(abs(PC_rand_color_factor), 0.02) * 5.0;
|
||||
attr = Attr(
|
||||
linear_motion(
|
||||
vec3(0.0, 0.0, 0.0),
|
||||
vec3(rand4, rand5, rand6 * 2.5)
|
||||
),
|
||||
vec3(8.0 * min(percent() * 3.0, 1.0) * min((1.0 - percent()) * 2.0, 1.0)),
|
||||
vec4(
|
||||
PC_R,
|
||||
PC_G,
|
||||
PC_B,
|
||||
PC_size * 1.2) * 10.0,
|
||||
spin_in_axis(vec3(rand6 + rand5, rand7 + rand9, rand8 + rand2), percent() * PC_spin)
|
||||
);
|
||||
break;
|
||||
case FIERY_DROPLET_TRACE:
|
||||
float m_r = 4.0;
|
||||
f_reflect = 0.0; // Fire doesn't reflect light, it emits it
|
||||
float prcnt = percent(); //idk if compiler would optimize it or not but as we have a lot of those particles... i'll just try
|
||||
float droplet_color1 = 1 * (5 + 1 * prcnt + 1 * slow_end(0)) * 1.5;
|
||||
float droplet_color2 = 1 * (4 - 2.4 * prcnt + 1.3 * rand5 * slow_end(0)) * 1.3;
|
||||
float droplet_color3 = 0;
|
||||
attr = Attr(
|
||||
quadratic_bezier_motion(
|
||||
vec3(0.0),
|
||||
vec3(m_r * rand0, m_r * rand1, 0.0),
|
||||
vec3(m_r * rand0, m_r * rand1, 4.0)
|
||||
),
|
||||
vec3(1),
|
||||
vec4(droplet_color1,
|
||||
droplet_color2,
|
||||
droplet_color3,
|
||||
1 * prcnt * (1 - step(0.5, prcnt)) + (1 - prcnt) * (step(0.5, prcnt))),
|
||||
spin_in_axis(vec3(1,0,0),0)
|
||||
);
|
||||
break;
|
||||
case ENERGY_PHOENIX:
|
||||
f_reflect = 0.0;
|
||||
float fiery_r = (2 + 1 * percent() * slow_end(0))
|
||||
* 6 * (1.4 - percent());
|
||||
float fiery_g = (2 - 1 * percent() + 0.3 * abs(rand5) * slow_end(0.5))
|
||||
* 4.5 * (1.4 - percent());
|
||||
float fiery_b = 1.5;
|
||||
spiral_radius = length(inst_dir);
|
||||
attr = Attr(
|
||||
spiral_motion(vec3(0.0, 0.0, 0.01), spiral_radius + abs(rand1), lifetime / 0.5, abs(rand0), rand1 * 2.0 * PI) + vec3(0.0, 0.0, rand2),
|
||||
vec3(6.0 * abs(rand4) * (1 - slow_start(2.0))),
|
||||
vec4(vec3(fiery_r, fiery_g, fiery_b), 1.0),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3.0)
|
||||
);
|
||||
break;
|
||||
case PHOENIX_BEAM:
|
||||
f_reflect = 0.0; // Fire doesn't reflect light, it emits it
|
||||
float beam_r = 6.0 - (4.0 * percent()) + 15.0 * fract(percent() * 4 + rand0 * rand0) * (1 - percent());
|
||||
float beam_g = 2.0 + 6.6 * fract(percent() * 4 + rand0 * rand0) * (1 - percent());
|
||||
float beam_b = 1.4;
|
||||
|
||||
vec3 factor_rand = vec3((rand0 * 0.2) * (rand5 * 0.1) + rand6 * 0.9, (rand1 * 0.2) * (rand4 * 0.1) + rand7 * 0.9, (rand2 * 0.2) * (rand3 * 0.1) + rand8 * 0.9);
|
||||
start_pos += factor_rand + normalize(inst_dir) * 0.6;
|
||||
attr = Attr(
|
||||
spiral_motion(inst_dir - factor_rand * 0.4, 0.3 * ((rand2 + 0.5) * 5.5) * (1.0 - min(linear_scale(1.5), 1.0)), lifetime / inst_lifespan, 24.0, -inst_time * 8.0),
|
||||
vec3((2.5 * (1 - slow_start(0.2)))),
|
||||
vec4(beam_r, beam_g, beam_b, 1.0),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10.0 + 3.0 * rand9)
|
||||
);
|
||||
break;
|
||||
case PHOENIX_BUILD_UP_AIM:
|
||||
|
||||
f_reflect = 0.0; // Fire doesn't reflect light, it emits it
|
||||
|
||||
float perc_t = percent(); // in case compiler wont optimize, idk
|
||||
|
||||
float aim_r = rand0 * 0.25 + 3.0 + 4.5 * perc_t * (1 - step(0.79, perc_t)) + 8.0 * step(0.81, perc_t) * perc_t;
|
||||
float aim_g = rand0 * 0.25 + 2.0 - 1.0 * perc_t * (1 - step(0.79, perc_t)) + 2.0 * step(0.81, perc_t) * perc_t;
|
||||
float aim_b = 1.4 * ((1 - perc_t) + step(0.74, perc_t));
|
||||
|
||||
|
||||
vec3 dir_aim = inst_dir * 1.0;
|
||||
vec3 rand_pos_aim = (cross(
|
||||
(1.0 - 2.0 * step(0.0, rand2)) * normalize(inst_dir),
|
||||
vec3(0.0, 0.0, 1.0)));
|
||||
|
||||
vec3 rand_fact = vec3(rand1 * 1, rand0 * 1, rand2 * 1);
|
||||
start_pos += vec3(0.0, 0.0, 5.0) + rand_fact;
|
||||
attr = Attr(
|
||||
spiral_motion(
|
||||
inst_dir + vec3(0.0, 0.0, -(6.0 - 3.0 * pow(perc_t, 2.5))) - rand_fact,
|
||||
1.2 * rand9 * max(1.0 - perc_t, 0.0),
|
||||
perc_t,
|
||||
6.0,
|
||||
inst_time * 8.0),
|
||||
vec3((1.9 * (1 - slow_start(0.2)))),
|
||||
vec4(aim_r, aim_g, aim_b, 1.0),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), perc_t * 10.0 + 3.0 * rand9)
|
||||
);
|
||||
break;
|
||||
default:
|
||||
attr = Attr(
|
||||
linear_motion(
|
||||
|
@ -629,6 +629,16 @@
|
||||
central: ("armor.empty"),
|
||||
)
|
||||
),
|
||||
FireRainDrop: (
|
||||
bone0: (
|
||||
offset: (-2.0, -10.0, -2.0),
|
||||
central: ("weapon.projectile.firerain_droplet1"),
|
||||
),
|
||||
bone1: (
|
||||
offset: (0.0, 0.0, 0.0),
|
||||
central: ("armor.empty"),
|
||||
)
|
||||
),
|
||||
TrainingDummy: (
|
||||
bone0: (
|
||||
offset: (-7.0, -5.0, 0.0),
|
||||
@ -809,6 +819,16 @@
|
||||
central: ("armor.empty"),
|
||||
)
|
||||
),
|
||||
FieryTornado: (
|
||||
bone0: (
|
||||
offset: (0.0, 0.0, 0.0),
|
||||
central: ("armor.empty"),
|
||||
),
|
||||
bone1: (
|
||||
offset: (0.0, 0.0, 0.0),
|
||||
central: ("armor.empty"),
|
||||
)
|
||||
),
|
||||
Apple: (
|
||||
bone0: (
|
||||
offset: (-3.5, -3.5, 0.0),
|
||||
|
BIN
assets/voxygen/voxel/weapon/projectile/firerain_droplet1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/weapon/projectile/firerain_droplet1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -183,6 +183,7 @@ lazy_static! {
|
||||
BuffKind::Defiance => "defiance",
|
||||
BuffKind::Bloodfeast => "bloodfeast",
|
||||
BuffKind::Berserk => "berserk",
|
||||
BuffKind::Heatstroke => "heatstroke"
|
||||
};
|
||||
let mut buff_parser = HashMap::new();
|
||||
for kind in BuffKind::iter() {
|
||||
|
@ -721,6 +721,8 @@ pub enum CharacterAbility {
|
||||
projectile_light: Option<LightEmitter>,
|
||||
projectile_speed: f32,
|
||||
damage_effect: Option<CombatEffect>,
|
||||
properties_of_aoe: Option<repeater_ranged::ProjectileOffset>,
|
||||
specifier: Option<repeater_ranged::FrontendSpecifier>,
|
||||
#[serde(default)]
|
||||
meta: AbilityMeta,
|
||||
},
|
||||
@ -906,7 +908,7 @@ pub enum CharacterAbility {
|
||||
recover_duration: f32,
|
||||
targets: combat::GroupTarget,
|
||||
auras: Vec<aura::AuraBuffConstructor>,
|
||||
aura_duration: Secs,
|
||||
aura_duration: Option<Secs>,
|
||||
range: f32,
|
||||
energy_cost: f32,
|
||||
scales_with_combo: bool,
|
||||
@ -947,6 +949,7 @@ pub enum CharacterAbility {
|
||||
combo_scaling: Option<ScalingKind>,
|
||||
#[serde(default)]
|
||||
meta: AbilityMeta,
|
||||
specifier: Option<self_buff::FrontendSpecifier>,
|
||||
},
|
||||
SpriteSummon {
|
||||
buildup_duration: f32,
|
||||
@ -1228,6 +1231,8 @@ impl CharacterAbility {
|
||||
projectile_light: _,
|
||||
ref mut projectile_speed,
|
||||
damage_effect: _,
|
||||
properties_of_aoe: _,
|
||||
specifier: _,
|
||||
meta: _,
|
||||
} => {
|
||||
*buildup_duration /= stats.speed;
|
||||
@ -1578,6 +1583,7 @@ impl CharacterAbility {
|
||||
combo_cost: _,
|
||||
combo_scaling: _,
|
||||
meta: _,
|
||||
specifier: _,
|
||||
} => {
|
||||
*buff_strength *= stats.diminished_buff_strength();
|
||||
*buildup_duration /= stats.speed;
|
||||
@ -2597,6 +2603,8 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
|
||||
projectile_light,
|
||||
projectile_speed,
|
||||
damage_effect,
|
||||
properties_of_aoe,
|
||||
specifier,
|
||||
meta: _,
|
||||
} => CharacterState::RepeaterRanged(repeater_ranged::Data {
|
||||
static_data: repeater_ranged::StaticData {
|
||||
@ -2613,6 +2621,8 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
|
||||
projectile_speed: *projectile_speed,
|
||||
ability_info,
|
||||
damage_effect: *damage_effect,
|
||||
properties_of_aoe: *properties_of_aoe,
|
||||
specifier: *specifier,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
@ -2776,6 +2786,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
|
||||
combo_scaling,
|
||||
enforced_limit,
|
||||
meta: _,
|
||||
specifier,
|
||||
} => CharacterState::SelfBuff(self_buff::Data {
|
||||
static_data: self_buff::StaticData {
|
||||
buildup_duration: Duration::from_secs_f32(*buildup_duration),
|
||||
@ -2789,6 +2800,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
|
||||
combo_on_use: data.combo.map_or(0, |c| c.counter()),
|
||||
enforced_limit: *enforced_limit,
|
||||
ability_info,
|
||||
specifier: *specifier,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
|
@ -78,6 +78,7 @@ pub enum Specifier {
|
||||
WardingAura,
|
||||
HealingAura,
|
||||
Frozen,
|
||||
FieryAura,
|
||||
}
|
||||
|
||||
impl From<(Option<GroupTarget>, Option<&Uid>)> for AuraTarget {
|
||||
|
@ -44,4 +44,5 @@ pub enum FrontendSpecifier {
|
||||
Poison,
|
||||
Ink,
|
||||
Lightning,
|
||||
PhoenixLaser,
|
||||
}
|
||||
|
@ -519,7 +519,7 @@ impl Body {
|
||||
| bird_large::Species::CloudWyvern
|
||||
| bird_large::Species::SeaWyvern
|
||||
| bird_large::Species::WealdWyvern => Vec3::new(2.5, 9.0, 4.5),
|
||||
_ => Vec3::new(2.0, 6.0, 3.5),
|
||||
_ => Vec3::new(2.0, 6.0, 4.4),
|
||||
},
|
||||
Body::Dragon(_) => Vec3::new(16.0, 10.0, 16.0),
|
||||
Body::FishMedium(_) => Vec3::new(0.5, 2.0, 0.8),
|
||||
@ -842,6 +842,7 @@ impl Body {
|
||||
| bird_large::Species::FrostWyvern
|
||||
| bird_large::Species::SeaWyvern
|
||||
| bird_large::Species::WealdWyvern => 1000,
|
||||
bird_large::Species::Phoenix => 2000,
|
||||
_ => 300,
|
||||
},
|
||||
Body::BirdMedium(bird_medium) => match bird_medium.species {
|
||||
@ -940,7 +941,7 @@ impl Body {
|
||||
quadruped_low::Species::Maneater => 130,
|
||||
quadruped_low::Species::Sandshark => 110,
|
||||
quadruped_low::Species::Hakulaq => 120,
|
||||
quadruped_low::Species::Dagon => 1200,
|
||||
quadruped_low::Species::Dagon => 1500,
|
||||
quadruped_low::Species::Lavadrake => 160,
|
||||
quadruped_low::Species::Basilisk => 200,
|
||||
quadruped_low::Species::Deadwood => 120,
|
||||
|
@ -112,6 +112,8 @@ make_case_elim!(
|
||||
SpearIcicle = 97,
|
||||
Portal = 98,
|
||||
PortalActive = 99,
|
||||
FieryTornado = 100,
|
||||
FireRainDrop = 101,
|
||||
}
|
||||
);
|
||||
|
||||
@ -122,7 +124,7 @@ impl Body {
|
||||
}
|
||||
}
|
||||
|
||||
pub const ALL_OBJECTS: [Body; 100] = [
|
||||
pub const ALL_OBJECTS: [Body; 102] = [
|
||||
Body::Arrow,
|
||||
Body::Bomb,
|
||||
Body::Scarecrow,
|
||||
@ -223,6 +225,8 @@ pub const ALL_OBJECTS: [Body; 100] = [
|
||||
Body::SpearIcicle,
|
||||
Body::Portal,
|
||||
Body::PortalActive,
|
||||
Body::FieryTornado,
|
||||
Body::FireRainDrop,
|
||||
];
|
||||
|
||||
impl From<Body> for super::Body {
|
||||
@ -332,6 +336,8 @@ impl Body {
|
||||
Body::SpearIcicle => "spear_icicle",
|
||||
Body::Portal => "portal",
|
||||
Body::PortalActive => "portal_active",
|
||||
Body::FieryTornado => "fiery_tornado",
|
||||
Body::FireRainDrop => "fire_rain_drop",
|
||||
}
|
||||
}
|
||||
|
||||
@ -343,6 +349,7 @@ impl Body {
|
||||
Reagent::Red => Body::FireworkRed,
|
||||
Reagent::White => Body::FireworkWhite,
|
||||
Reagent::Yellow => Body::FireworkYellow,
|
||||
Reagent::Phoenix => Body::FireRainDrop,
|
||||
}
|
||||
}
|
||||
|
||||
@ -384,7 +391,11 @@ impl Body {
|
||||
Body::BedBlue => 50.0,
|
||||
Body::Bedroll => 3.0,
|
||||
Body::Bench => 100.0,
|
||||
Body::BoltFire | Body::BoltFireBig | Body::BoltNature | Body::BoltIcicle => 1.0,
|
||||
Body::BoltFire
|
||||
| Body::BoltFireBig
|
||||
| Body::BoltNature
|
||||
| Body::BoltIcicle
|
||||
| Body::FireRainDrop => 1.0,
|
||||
Body::SpitPoison => 100.0,
|
||||
Body::Bomb | Body::DagonBomb => {
|
||||
0.5 * IRON_DENSITY * std::f32::consts::PI / 6.0 * self.dimensions().x.powi(3)
|
||||
@ -445,7 +456,7 @@ impl Body {
|
||||
Body::FishMeat => 10.0,
|
||||
Body::BirdMeat => 10.0,
|
||||
Body::SmallMeat => 10.0,
|
||||
Body::Tornado => 50.0,
|
||||
Body::Tornado | Body::FieryTornado => 50.0,
|
||||
Body::Apple => 2.0,
|
||||
Body::Hive => 2.0,
|
||||
Body::Coconut => 2.0,
|
||||
@ -479,7 +490,7 @@ impl Body {
|
||||
Body::HaniwaSentry => Vec3::new(0.8, 0.8, 1.4),
|
||||
Body::SeaLantern => Vec3::new(0.8, 0.8, 1.4),
|
||||
Body::Snowball => Vec3::broadcast(2.5),
|
||||
Body::Tornado => Vec3::new(2.0, 2.0, 3.4),
|
||||
Body::Tornado | Body::FieryTornado => Vec3::new(2.0, 2.0, 3.4),
|
||||
Body::TrainingDummy => Vec3::new(1.5, 1.5, 3.0),
|
||||
Body::GnarlingTotemRed | Body::GnarlingTotemGreen | Body::GnarlingTotemWhite => {
|
||||
Vec3::new(0.8, 0.8, 1.4)
|
||||
@ -489,6 +500,7 @@ impl Body {
|
||||
Body::LaserBeam => Vec3::new(8.0, 8.0, 8.0),
|
||||
Body::Mine => Vec3::new(0.8, 0.8, 0.5),
|
||||
Body::LightningBolt | Body::SpearIcicle => Vec3::new(1.0, 1.0, 1.0),
|
||||
Body::FireRainDrop => Vec3::new(0.01, 0.01, 0.02),
|
||||
// FIXME: this *must* be exhaustive match
|
||||
_ => Vec3::broadcast(0.5),
|
||||
}
|
||||
|
@ -162,6 +162,12 @@ pub enum BuffKind {
|
||||
PotionSickness,
|
||||
/// Changed into another body.
|
||||
Polymorphed,
|
||||
/// Slows movement speed and reduces energy reward.
|
||||
/// Both scales non-linearly to strength, 0.5 lead to movespeed reduction
|
||||
/// by 25% and energy reward reduced by 150%, 1.0 lead to MS reduction by
|
||||
/// 33.3% and energy reward reduced by 200%. Energy reward can't be
|
||||
/// reduced by more than 200%, to a minimum value of -100%.
|
||||
Heatstroke,
|
||||
}
|
||||
|
||||
impl BuffKind {
|
||||
@ -201,7 +207,8 @@ impl BuffKind {
|
||||
| BuffKind::Poisoned
|
||||
| BuffKind::Parried
|
||||
| BuffKind::PotionSickness
|
||||
| BuffKind::Polymorphed => false,
|
||||
| BuffKind::Polymorphed
|
||||
| BuffKind::Heatstroke => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,6 +416,10 @@ impl BuffKind {
|
||||
BuffEffect::AttackSpeed(1.0 + nn_scaling(data.strength) / 2.0),
|
||||
BuffEffect::MovementSpeed(1.0 + nn_scaling(data.strength) / 4.0),
|
||||
],
|
||||
BuffKind::Heatstroke => vec![
|
||||
BuffEffect::MovementSpeed(1.0 - nn_scaling(data.strength) * 0.5),
|
||||
BuffEffect::EnergyReward((1.0 - nn_scaling(data.strength) * 3.0).max(-1.0)),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,7 @@ pub enum Reagent {
|
||||
Red,
|
||||
White,
|
||||
Yellow,
|
||||
Phoenix,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
|
@ -780,6 +780,9 @@ fn default_main_tool(body: &Body) -> Item {
|
||||
object::Body::Tornado => Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_weapons.unique.tornado",
|
||||
)),
|
||||
object::Body::FieryTornado => Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_weapons.unique.fiery_tornado",
|
||||
)),
|
||||
object::Body::GnarlingTotemRed => Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_weapons.biped_small.gnarling.redtotem",
|
||||
)),
|
||||
@ -984,6 +987,7 @@ impl LoadoutBuilder {
|
||||
| bird_large::Species::WealdWyvern => {
|
||||
Some("common.items.npc_armor.bird_large.wyvern")
|
||||
},
|
||||
bird_large::Species::Phoenix => Some("common.items.npc_armor.bird_large.phoenix"),
|
||||
_ => None,
|
||||
},
|
||||
Body::Golem(body) => match body.species {
|
||||
|
@ -59,6 +59,13 @@ pub enum ProjectileConstructor {
|
||||
knockback: f32,
|
||||
energy_regen: f32,
|
||||
},
|
||||
FireDroplet {
|
||||
damage: f32,
|
||||
radius: f32,
|
||||
energy_regen: f32,
|
||||
min_falloff: f32,
|
||||
reagent: Option<Reagent>,
|
||||
},
|
||||
Fireball {
|
||||
damage: f32,
|
||||
radius: f32,
|
||||
@ -253,6 +260,56 @@ impl ProjectileConstructor {
|
||||
is_point: true,
|
||||
}
|
||||
},
|
||||
FireDroplet {
|
||||
damage,
|
||||
radius,
|
||||
energy_regen,
|
||||
min_falloff,
|
||||
reagent,
|
||||
} => {
|
||||
let energy = AttackEffect::new(None, CombatEffect::EnergyReward(energy_regen))
|
||||
.with_requirement(CombatRequirement::AnyDamage);
|
||||
let buff = CombatEffect::Buff(CombatBuff {
|
||||
kind: BuffKind::Burning,
|
||||
dur_secs: 4.0,
|
||||
strength: CombatBuffStrength::DamageFraction(1.0),
|
||||
chance: 1.0,
|
||||
})
|
||||
.adjusted_by_stats(tool_stats);
|
||||
let damage = AttackDamage::new(
|
||||
Damage {
|
||||
source: DamageSource::Explosion,
|
||||
kind: DamageKind::Energy,
|
||||
value: damage,
|
||||
},
|
||||
Some(GroupTarget::OutOfGroup),
|
||||
instance,
|
||||
)
|
||||
.with_effect(buff);
|
||||
let attack = Attack::default()
|
||||
.with_damage(damage)
|
||||
.with_precision(precision_mult)
|
||||
.with_effect(energy)
|
||||
.with_combo_increment();
|
||||
let explosion = Explosion {
|
||||
effects: vec![
|
||||
RadiusEffect::Attack(attack),
|
||||
RadiusEffect::TerrainDestruction(2.0, Rgb::black()),
|
||||
],
|
||||
radius,
|
||||
reagent,
|
||||
min_falloff,
|
||||
};
|
||||
Projectile {
|
||||
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
||||
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
||||
time_left: Duration::from_secs(10),
|
||||
owner,
|
||||
ignore_group: true,
|
||||
is_sticky: true,
|
||||
is_point: true,
|
||||
}
|
||||
},
|
||||
Fireball {
|
||||
damage,
|
||||
radius,
|
||||
@ -915,6 +972,16 @@ impl ProjectileConstructor {
|
||||
*damage *= power;
|
||||
*energy_regen *= regen;
|
||||
},
|
||||
FireDroplet {
|
||||
ref mut damage,
|
||||
ref mut energy_regen,
|
||||
ref mut radius,
|
||||
..
|
||||
} => {
|
||||
*damage *= power;
|
||||
*energy_regen *= regen;
|
||||
*radius *= range;
|
||||
},
|
||||
Fireball {
|
||||
ref mut damage,
|
||||
ref mut energy_regen,
|
||||
@ -1034,6 +1101,7 @@ impl ProjectileConstructor {
|
||||
match self {
|
||||
Arrow { .. } => false,
|
||||
Knife { .. } => false,
|
||||
FireDroplet { .. } => true,
|
||||
Fireball { .. } => true,
|
||||
Frostball { .. } => true,
|
||||
Poisonball { .. } => true,
|
||||
|
@ -146,6 +146,9 @@ pub enum Outcome {
|
||||
TeleportedByPortal {
|
||||
pos: Vec3<f32>,
|
||||
},
|
||||
FromTheAshes {
|
||||
pos: Vec3<f32>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Outcome {
|
||||
@ -177,6 +180,7 @@ impl Outcome {
|
||||
| Outcome::GroundDig { pos }
|
||||
| Outcome::PortalActivated { pos }
|
||||
| Outcome::TeleportedByPortal { pos}
|
||||
| Outcome::FromTheAshes { pos }
|
||||
| Outcome::Glider { pos, .. } => Some(*pos),
|
||||
Outcome::BreakBlock { pos, .. }
|
||||
| Outcome::SpriteUnlocked { pos }
|
||||
|
@ -29,7 +29,7 @@ pub struct StaticData {
|
||||
/// Has information used to construct the auras
|
||||
pub auras: Vec<AuraBuffConstructor>,
|
||||
/// How long aura lasts
|
||||
pub aura_duration: Secs,
|
||||
pub aura_duration: Option<Secs>,
|
||||
/// Radius of aura
|
||||
pub range: f32,
|
||||
/// What key is used to press ability
|
||||
@ -78,7 +78,8 @@ impl CharacterBehavior for Data {
|
||||
let mut aura = aura_data.to_aura(
|
||||
data.uid,
|
||||
self.static_data.range,
|
||||
Some(self.static_data.aura_duration),
|
||||
// check for indefinite aura
|
||||
self.static_data.aura_duration,
|
||||
targets,
|
||||
*data.time,
|
||||
);
|
||||
|
@ -4,8 +4,11 @@ use crate::{
|
||||
DamageKind, DamageSource, GroupTarget,
|
||||
},
|
||||
comp::{
|
||||
beam, body::biped_large, character_state::OutputEvents, object::Body::Flamethrower, Body,
|
||||
CharacterState, Ori, StateUpdate,
|
||||
beam,
|
||||
body::{biped_large, bird_large},
|
||||
character_state::OutputEvents,
|
||||
object::Body::Flamethrower,
|
||||
Body, CharacterState, Ori, StateUpdate,
|
||||
},
|
||||
event::LocalEvent,
|
||||
outcome::Outcome,
|
||||
@ -242,8 +245,13 @@ impl CharacterBehavior for Data {
|
||||
fn height_offset(body: &Body, look_dir: Dir, velocity: Vec3<f32>, on_ground: Option<Block>) -> f32 {
|
||||
match body {
|
||||
// Hack to make the beam offset correspond to the animation
|
||||
Body::BirdLarge(_) => {
|
||||
body.height() * 0.3
|
||||
Body::BirdLarge(b) => {
|
||||
let height_factor = match b.species {
|
||||
bird_large::Species::Phoenix => 0.5,
|
||||
bird_large::Species::Cockatrice => 0.4,
|
||||
_ => 0.3,
|
||||
};
|
||||
body.height() * height_factor
|
||||
+ if on_ground.is_none() {
|
||||
(2.0 - velocity.xy().magnitude() * 0.25).max(-1.0)
|
||||
} else {
|
||||
|
@ -3,8 +3,11 @@ use crate::{
|
||||
self,
|
||||
character_state::OutputEvents,
|
||||
inventory::loadout_builder::{self, LoadoutBuilder},
|
||||
object::Body::FieryTornado,
|
||||
skillset::skills,
|
||||
Behavior, BehaviorCapability, CharacterState, Projectile, StateUpdate,
|
||||
Behavior, BehaviorCapability,
|
||||
Body::Object,
|
||||
CharacterState, Projectile, StateUpdate,
|
||||
},
|
||||
event::{LocalEvent, NpcBuilder, ServerEvent},
|
||||
npc::NPC_NAMES,
|
||||
@ -185,11 +188,19 @@ impl CharacterBehavior for Data {
|
||||
is_sticky: false,
|
||||
is_point: false,
|
||||
});
|
||||
let extra_height =
|
||||
if self.static_data.summon_info.body == Object(FieryTornado) {
|
||||
5.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
// Send server event to create npc
|
||||
output_events.emit_server(ServerEvent::CreateNpc {
|
||||
pos: comp::Pos(collision_vector - Vec3::unit_z() * obstacle_z),
|
||||
pos: comp::Pos(
|
||||
collision_vector - Vec3::unit_z() * obstacle_z + extra_height,
|
||||
),
|
||||
ori: comp::Ori::from(Dir::random_2d(&mut rng)),
|
||||
npc: NpcBuilder::new(stats, body, comp::Alignment::Owned(*data.uid))
|
||||
.with_skill_set(skill_set)
|
||||
|
@ -9,9 +9,11 @@ use crate::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::{StageSection, *},
|
||||
},
|
||||
util::Dir,
|
||||
};
|
||||
use rand::{thread_rng, Rng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use std::{f32::consts::TAU, time::Duration};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
/// Separated out to condense update portions of character state
|
||||
@ -37,6 +39,19 @@ pub struct StaticData {
|
||||
pub ability_info: AbilityInfo,
|
||||
/// Adds an effect onto the main damage of the attack
|
||||
pub damage_effect: Option<CombatEffect>,
|
||||
/// Whether ablity should be casted from above as aoe or shoot projectiles
|
||||
/// as normal
|
||||
pub properties_of_aoe: Option<ProjectileOffset>,
|
||||
/// Used to specify the attack to the frontend
|
||||
pub specifier: Option<FrontendSpecifier>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ProjectileOffset {
|
||||
/// Radius of AOE
|
||||
pub radius: f32,
|
||||
/// Height of shooting point for AOE's projectiles
|
||||
pub height: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@ -94,10 +109,35 @@ impl CharacterBehavior for Data {
|
||||
let precision_mult = combat::compute_precision_mult(data.inventory, data.msm);
|
||||
let tool_stats = get_tool_stats(data, self.static_data.ability_info);
|
||||
// Gets offsets
|
||||
let body_offsets = data
|
||||
.body
|
||||
.projectile_offsets(update.ori.look_vec(), data.scale.map_or(1.0, |s| s.0));
|
||||
let pos = Pos(data.pos.0 + body_offsets);
|
||||
let pos: Pos = self.static_data.properties_of_aoe.as_ref().map_or_else(
|
||||
|| {
|
||||
// Default position
|
||||
let body_offsets = data.body.projectile_offsets(
|
||||
update.ori.look_vec(),
|
||||
data.scale.map_or(1.0, |s| s.0),
|
||||
);
|
||||
Pos(data.pos.0 + body_offsets)
|
||||
},
|
||||
|aoe_data| {
|
||||
// Position calculated from aoe_data
|
||||
let rand_pos = {
|
||||
let mut rng = thread_rng();
|
||||
let theta = rng.gen::<f32>() * TAU;
|
||||
let radius = aoe_data.radius * rng.gen::<f32>().sqrt();
|
||||
let x = radius * theta.sin();
|
||||
let y = radius * theta.cos();
|
||||
vek::Vec2::new(x, y)
|
||||
};
|
||||
Pos(data.pos.0 + rand_pos.with_z(aoe_data.height))
|
||||
},
|
||||
);
|
||||
|
||||
let direction: Dir = if self.static_data.properties_of_aoe.is_some() {
|
||||
Dir::down()
|
||||
} else {
|
||||
data.inputs.look_dir
|
||||
};
|
||||
|
||||
let projectile = self.static_data.projectile.create_projectile(
|
||||
Some(*data.uid),
|
||||
precision_mult,
|
||||
@ -107,7 +147,7 @@ impl CharacterBehavior for Data {
|
||||
output_events.emit_server(ServerEvent::Shoot {
|
||||
entity: data.entity,
|
||||
pos,
|
||||
dir: data.inputs.look_dir,
|
||||
dir: direction,
|
||||
body: self.static_data.projectile_body,
|
||||
projectile,
|
||||
light: self.static_data.projectile_light,
|
||||
@ -167,3 +207,8 @@ impl CharacterBehavior for Data {
|
||||
update
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum FrontendSpecifier {
|
||||
FireRain,
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ use crate::{
|
||||
character_state::OutputEvents,
|
||||
CharacterState, StateUpdate,
|
||||
},
|
||||
event::ServerEvent,
|
||||
event::{LocalEvent, ServerEvent},
|
||||
outcome::Outcome,
|
||||
resources::Secs,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
@ -41,6 +42,8 @@ pub struct StaticData {
|
||||
pub enforced_limit: bool,
|
||||
/// What key is used to press ability
|
||||
pub ability_info: AbilityInfo,
|
||||
/// Used to specify an outcome for the buff
|
||||
pub specifier: Option<FrontendSpecifier>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@ -145,6 +148,12 @@ impl CharacterBehavior for Data {
|
||||
timer: tick_attack_or_default(data, self.timer, None),
|
||||
..*self
|
||||
});
|
||||
if let Some(FrontendSpecifier::FromTheAshes) = self.static_data.specifier {
|
||||
// Send local event used for frontend shenanigans
|
||||
output_events.emit_local(LocalEvent::CreateOutcome(
|
||||
Outcome::FromTheAshes { pos: data.pos.0 },
|
||||
));
|
||||
}
|
||||
} else {
|
||||
update.character = CharacterState::SelfBuff(Data {
|
||||
timer: Duration::default(),
|
||||
@ -176,3 +185,8 @@ impl CharacterBehavior for Data {
|
||||
update
|
||||
}
|
||||
}
|
||||
/// Used to specify a particular effect for frontend purposes
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum FrontendSpecifier {
|
||||
FromTheAshes,
|
||||
}
|
||||
|
@ -211,35 +211,52 @@ impl Data {
|
||||
}
|
||||
}
|
||||
|
||||
for (site_id, site) in this.sites.iter()
|
||||
// TODO: Stupid
|
||||
.filter(|(_, site)| site.world_site.map_or(false, |ws|
|
||||
matches!(&index.sites.get(ws).kind, SiteKind::Dungeon(_))))
|
||||
{
|
||||
for (site_id, site) in this.sites.iter() {
|
||||
let rand_wpos = |rng: &mut SmallRng| {
|
||||
let wpos2d = site.wpos.map(|e| e + rng.gen_range(-10..10));
|
||||
// don't spawn in buildings
|
||||
let spread_factor = rng.gen_range(-3..3) * 50;
|
||||
let spread = if spread_factor == 0 {
|
||||
100
|
||||
} else {
|
||||
spread_factor
|
||||
};
|
||||
let wpos2d = site.wpos.map(|e| e + spread);
|
||||
wpos2d
|
||||
.map(|e| e as f32 + 0.5)
|
||||
.with_z(world.sim().get_alt_approx(wpos2d).unwrap_or(0.0))
|
||||
};
|
||||
|
||||
let species = [
|
||||
comp::body::bird_large::Species::Phoenix,
|
||||
comp::body::bird_large::Species::Cockatrice,
|
||||
comp::body::bird_large::Species::Roc,
|
||||
comp::body::bird_large::Species::FlameWyvern,
|
||||
comp::body::bird_large::Species::CloudWyvern,
|
||||
comp::body::bird_large::Species::FrostWyvern,
|
||||
comp::body::bird_large::Species::SeaWyvern,
|
||||
comp::body::bird_large::Species::WealdWyvern,
|
||||
let site_kind = site.world_site.map(|ws| &index.sites.get(ws).kind);
|
||||
let Some(species) = [
|
||||
Some(comp::body::bird_large::Species::Phoenix)
|
||||
.filter(|_| matches!(site_kind, Some(SiteKind::Dungeon(_)))),
|
||||
Some(comp::body::bird_large::Species::Cockatrice)
|
||||
.filter(|_| matches!(site_kind, Some(SiteKind::Dungeon(_)))),
|
||||
Some(comp::body::bird_large::Species::Roc)
|
||||
.filter(|_| matches!(site_kind, Some(SiteKind::Dungeon(_)))),
|
||||
Some(comp::body::bird_large::Species::FlameWyvern)
|
||||
.filter(|_| matches!(site_kind, Some(SiteKind::Dungeon(_)))),
|
||||
Some(comp::body::bird_large::Species::CloudWyvern)
|
||||
.filter(|_| matches!(site_kind, Some(SiteKind::Dungeon(_)))),
|
||||
Some(comp::body::bird_large::Species::FrostWyvern)
|
||||
.filter(|_| matches!(site_kind, Some(SiteKind::Adlet(_)))),
|
||||
Some(comp::body::bird_large::Species::SeaWyvern)
|
||||
.filter(|_| matches!(site_kind, Some(SiteKind::ChapelSite(_)))),
|
||||
Some(comp::body::bird_large::Species::WealdWyvern)
|
||||
.filter(|_| matches!(site_kind, Some(SiteKind::GiantTree(_)))),
|
||||
]
|
||||
.choose(&mut rng)
|
||||
.unwrap();
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.choose(&mut rng) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
this.npcs.create_npc(
|
||||
Npc::new(
|
||||
rng.gen(),
|
||||
rand_wpos(&mut rng),
|
||||
Body::BirdLarge(comp::body::bird_large::Body::random_with(&mut rng, species)),
|
||||
Body::BirdLarge(comp::body::bird_large::Body::random_with(
|
||||
&mut rng, &species,
|
||||
)),
|
||||
Role::Wild,
|
||||
)
|
||||
.with_home(site_id),
|
||||
|
@ -16,7 +16,7 @@ use crate::{
|
||||
use common::{
|
||||
astar::{Astar, PathResult},
|
||||
comp::{
|
||||
self,
|
||||
self, bird_large,
|
||||
compass::{Direction, Distance},
|
||||
dialogue::Subject,
|
||||
Content,
|
||||
@ -1210,15 +1210,21 @@ fn bird_large() -> impl Action<DefaultState> {
|
||||
.map(|e| e + ctx.rng.gen_range(-0.1..0.1))
|
||||
.try_normalized()
|
||||
.unwrap_or_default();
|
||||
let bearing_dist = 24.0;
|
||||
let bearing_dist = 15.0;
|
||||
let mut pos = ctx.npc.wpos.xy() + *bearing * bearing_dist;
|
||||
let is_deep_water = ctx
|
||||
let is_deep_water =
|
||||
matches!(ctx.npc.body, common::comp::Body::BirdLarge(b) if matches!(b.species, bird_large::Species::SeaWyvern))
|
||||
|| ctx
|
||||
.world
|
||||
.sim()
|
||||
.get(pos.as_().wpos_to_cpos())
|
||||
.map_or(true, |c| {
|
||||
c.alt - c.water_alt < -120.0 && (c.river.is_ocean() || c.river.is_lake())
|
||||
});
|
||||
if is_deep_water {
|
||||
*bearing *= -1.0;
|
||||
pos = ctx.npc.wpos.xy() + *bearing * bearing_dist;
|
||||
};
|
||||
// when high tree_density fly high, otherwise fly low-mid
|
||||
let npc_pos = ctx.npc.wpos.xy();
|
||||
let trees = ctx
|
||||
@ -1231,35 +1237,56 @@ fn bird_large() -> impl Action<DefaultState> {
|
||||
} else {
|
||||
ctx.rng.gen_range(0.4..0.9)
|
||||
};
|
||||
if !is_deep_water {
|
||||
goto_2d_flying(
|
||||
pos,
|
||||
0.1,
|
||||
bearing_dist,
|
||||
8.0,
|
||||
8.0,
|
||||
ctx.npc.body.flying_height() * height_factor,
|
||||
)
|
||||
|
||||
} else {
|
||||
*bearing *= -1.0;
|
||||
|
||||
pos = ctx.npc.wpos.xy() + *bearing * 24.0;
|
||||
|
||||
goto_2d_flying(
|
||||
pos,
|
||||
0.1,
|
||||
bearing_dist,
|
||||
8.0,
|
||||
8.0,
|
||||
ctx.npc.body.flying_height() * height_factor,
|
||||
)
|
||||
let data = ctx.state.data();
|
||||
// without destination site fly to next waypoint
|
||||
let mut dest_site = pos;
|
||||
if let Some(home) = ctx.npc.home {
|
||||
let is_home = ctx.npc.current_site.map_or(false, |site| home == site);
|
||||
if is_home {
|
||||
if let Some((id, _)) = data
|
||||
.sites
|
||||
.iter()
|
||||
.filter(|(id, site)| {
|
||||
*id != home
|
||||
&& site.world_site.map_or(false, |site| {
|
||||
match ctx.npc.body {
|
||||
common::comp::Body::BirdLarge(b) => match b.species {
|
||||
bird_large::Species::SeaWyvern => matches!(&ctx.index.sites.get(site).kind, SiteKind::ChapelSite(_)),
|
||||
bird_large::Species::FrostWyvern => matches!(&ctx.index.sites.get(site).kind, SiteKind::Adlet(_)),
|
||||
bird_large::Species::WealdWyvern => matches!(&ctx.index.sites.get(site).kind, SiteKind::GiantTree(_)),
|
||||
_ => matches!(&ctx.index.sites.get(site).kind, SiteKind::Dungeon(_)),
|
||||
},
|
||||
_ => matches!(&ctx.index.sites.get(site).kind, SiteKind::Dungeon(_)),
|
||||
}
|
||||
// If we are too far away from our goal position we can stop since we aren't going to a specific place.
|
||||
})
|
||||
})
|
||||
/*choose closest destination:
|
||||
.min_by_key(|(_, site)| site.wpos.as_().distance(npc_pos) as i32)*/
|
||||
//choose random destination:
|
||||
.choose(&mut ctx.rng)
|
||||
{
|
||||
ctx.controller.set_new_home(id)
|
||||
}
|
||||
} else if let Some(site) = data.sites.get(home) {
|
||||
dest_site = site.wpos.as_::<f32>()
|
||||
}
|
||||
}
|
||||
goto_2d_flying(
|
||||
pos,
|
||||
0.2,
|
||||
bearing_dist,
|
||||
8.0,
|
||||
8.0,
|
||||
ctx.npc.body.flying_height() * height_factor,
|
||||
)
|
||||
// If we are too far away from our waypoint position we can stop since we aren't going to a specific place.
|
||||
// If waypoint position is further away from destination site find a new waypoint
|
||||
.stop_if(move |ctx: &mut NpcCtx| {
|
||||
ctx.npc.wpos.xy().distance_squared(pos) > (bearing_dist + 5.0).powi(2)
|
||||
|| dest_site.distance_squared(pos) > dest_site.distance_squared(npc_pos)
|
||||
})
|
||||
// If goal position wasn't reached within 20 seconds we're probably stuck and need to find a new goal position.
|
||||
// If waypoint position wasn't reached within 10 seconds we're probably stuck and need to find a new waypoint.
|
||||
.stop_if(timeout(10.0))
|
||||
.debug({
|
||||
let bearing = *bearing;
|
||||
|
@ -1075,6 +1075,7 @@ impl<'a> AgentData<'a> {
|
||||
| "Gnarling Totem Red"
|
||||
| "Gnarling Totem Green"
|
||||
| "Gnarling Totem White" => Tactic::RadialTurret,
|
||||
"FieryTornado" => Tactic::FieryTornado,
|
||||
"Yeti" => Tactic::Yeti,
|
||||
"Harvester" => Tactic::Harvester,
|
||||
"Cardinal" => Tactic::Cardinal,
|
||||
@ -1500,6 +1501,7 @@ impl<'a> AgentData<'a> {
|
||||
read_data,
|
||||
),
|
||||
Tactic::RadialTurret => self.handle_radial_turret_attack(controller),
|
||||
Tactic::FieryTornado => self.handle_fiery_tornado_attack(agent, controller),
|
||||
Tactic::Yeti => {
|
||||
self.handle_yeti_attack(agent, controller, &attack_data, tgt_data, read_data)
|
||||
},
|
||||
|
@ -2678,6 +2678,23 @@ impl<'a> AgentData<'a> {
|
||||
controller.push_basic_input(InputKind::Primary);
|
||||
}
|
||||
|
||||
pub fn handle_fiery_tornado_attack(&self, agent: &mut Agent, controller: &mut Controller) {
|
||||
enum Conditions {
|
||||
AuraEmited = 0,
|
||||
}
|
||||
if matches!(self.char_state, CharacterState::BasicAura(c) if matches!(c.stage_section, StageSection::Recover))
|
||||
{
|
||||
agent.combat_state.conditions[Conditions::AuraEmited as usize] = true;
|
||||
}
|
||||
// 1 time use of aura
|
||||
if !agent.combat_state.conditions[Conditions::AuraEmited as usize] {
|
||||
controller.push_basic_input(InputKind::Secondary);
|
||||
} else {
|
||||
// Spin
|
||||
controller.push_basic_input(InputKind::Primary);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_mindflayer_attack(
|
||||
&self,
|
||||
agent: &mut Agent,
|
||||
@ -2975,106 +2992,141 @@ impl<'a> AgentData<'a> {
|
||||
read_data: &ReadData,
|
||||
_rng: &mut impl Rng,
|
||||
) {
|
||||
const PHOENIX_HEAL_THRESHOLD: f32 = 0.20;
|
||||
|
||||
enum Conditions {
|
||||
Healed = 0,
|
||||
}
|
||||
enum ActionStateTimers {
|
||||
AttackTimer = 0,
|
||||
AttackTimer1,
|
||||
AttackTimer2,
|
||||
}
|
||||
// Set fly to false
|
||||
controller.push_cancel_input(InputKind::Fly);
|
||||
if attack_data.dist_sqrd > 30.0_f32.powi(2) {
|
||||
if entities_have_line_of_sight(
|
||||
self.pos,
|
||||
self.body,
|
||||
self.scale,
|
||||
tgt_data.pos,
|
||||
tgt_data.body,
|
||||
tgt_data.scale,
|
||||
read_data,
|
||||
) && attack_data.angle < 15.0
|
||||
{
|
||||
controller.push_basic_input(InputKind::Primary);
|
||||
|
||||
let attack_timer_1 =
|
||||
if agent.combat_state.timers[ActionStateTimers::AttackTimer1 as usize] < 2.0 {
|
||||
0
|
||||
} else if agent.combat_state.timers[ActionStateTimers::AttackTimer1 as usize] < 4.0 {
|
||||
1
|
||||
} else if agent.combat_state.timers[ActionStateTimers::AttackTimer1 as usize] < 6.0 {
|
||||
2
|
||||
} else {
|
||||
3
|
||||
};
|
||||
agent.combat_state.timers[ActionStateTimers::AttackTimer1 as usize] += read_data.dt.0;
|
||||
if agent.combat_state.timers[ActionStateTimers::AttackTimer1 as usize] > 8.0 {
|
||||
// Reset timer
|
||||
agent.combat_state.timers[ActionStateTimers::AttackTimer1 as usize] = 0.0;
|
||||
}
|
||||
if let Some((bearing, speed)) = agent.chaser.chase(
|
||||
&*read_data.terrain,
|
||||
self.pos.0,
|
||||
self.vel.0,
|
||||
tgt_data.pos.0,
|
||||
TraversalConfig {
|
||||
min_tgt_dist: 1.25,
|
||||
..self.traversal_config
|
||||
},
|
||||
) {
|
||||
controller.inputs.move_dir =
|
||||
bearing.xy().try_normalized().unwrap_or_else(Vec2::zero) * speed;
|
||||
if (self.pos.0.z - tgt_data.pos.0.z) < 20.0 {
|
||||
let (attack_timer_2, speed) =
|
||||
if agent.combat_state.timers[ActionStateTimers::AttackTimer2 as usize] < 3.0 {
|
||||
// fly high
|
||||
(0, 2.0)
|
||||
} else if agent.combat_state.timers[ActionStateTimers::AttackTimer2 as usize] < 6.0 {
|
||||
// attack_mid_1
|
||||
(1, 2.0)
|
||||
} else if agent.combat_state.timers[ActionStateTimers::AttackTimer2 as usize] < 9.0 {
|
||||
// fly high
|
||||
(0, 3.0)
|
||||
} else if agent.combat_state.timers[ActionStateTimers::AttackTimer2 as usize] < 16.0 {
|
||||
// attack_mid_2
|
||||
(2, 1.0)
|
||||
} else if agent.combat_state.timers[ActionStateTimers::AttackTimer2 as usize] < 20.0 {
|
||||
// fly low
|
||||
(5, 20.0)
|
||||
} else {
|
||||
// attack_close
|
||||
(3, 1.0)
|
||||
};
|
||||
agent.combat_state.timers[ActionStateTimers::AttackTimer2 as usize] += read_data.dt.0;
|
||||
if agent.combat_state.timers[ActionStateTimers::AttackTimer2 as usize] > 28.0 {
|
||||
// Reset timer
|
||||
agent.combat_state.timers[ActionStateTimers::AttackTimer2 as usize] = 0.0;
|
||||
}
|
||||
// Fly to target
|
||||
let dir_to_target = ((tgt_data.pos.0 + Vec3::unit_z() * 1.5) - self.pos.0)
|
||||
.try_normalized()
|
||||
.unwrap_or_else(Vec3::zero);
|
||||
controller.inputs.move_dir = dir_to_target.xy() * speed;
|
||||
|
||||
// Always fly! If the floor can't touch you, it can't hurt you...
|
||||
controller.push_basic_input(InputKind::Fly);
|
||||
controller.inputs.move_z = 1.0;
|
||||
}
|
||||
}
|
||||
} else if !read_data
|
||||
// Flee from the ground! The internet told me it was lava!
|
||||
// If on the ground, jump with every last ounce of energy, holding onto
|
||||
// all that is dear in life and straining for the wide open skies.
|
||||
if self.physics_state.on_ground.is_some() {
|
||||
controller.push_basic_input(InputKind::Jump);
|
||||
} else {
|
||||
// Use a proportional controller with a coefficient of 1.0 to
|
||||
// maintain altidude at the the provided set point
|
||||
let mut maintain_altitude = |set_point| {
|
||||
let alt = read_data
|
||||
.terrain
|
||||
.ray(self.pos.0, self.pos.0 - (Vec3::unit_z() * 2.0))
|
||||
.ray(self.pos.0, self.pos.0 - (Vec3::unit_z() * 7.0))
|
||||
.until(Block::is_solid)
|
||||
.cast()
|
||||
.1
|
||||
.map_or(true, |b| b.is_some())
|
||||
.0;
|
||||
let error = set_point - alt;
|
||||
controller.inputs.move_z = error;
|
||||
};
|
||||
// heal once - from_the_ashes
|
||||
let health_fraction = self.health.map_or(0.5, |h| h.fraction());
|
||||
if matches!(self.char_state, CharacterState::SelfBuff(c) if matches!(c.stage_section, StageSection::Recover))
|
||||
{
|
||||
// Do not increment the timer during this movement
|
||||
// The next stage shouldn't trigger until the entity
|
||||
// is on the ground
|
||||
controller.push_basic_input(InputKind::Fly);
|
||||
let move_dir = tgt_data.pos.0 - self.pos.0;
|
||||
controller.inputs.move_dir =
|
||||
move_dir.xy().try_normalized().unwrap_or_else(Vec2::zero) * 2.0;
|
||||
controller.inputs.move_z = move_dir.z - 0.5;
|
||||
if attack_data.dist_sqrd > (4.0 * attack_data.min_attack_dist).powi(2)
|
||||
&& attack_data.angle < 15.0
|
||||
{
|
||||
controller.push_basic_input(InputKind::Primary);
|
||||
agent.combat_state.conditions[Conditions::Healed as usize] = true;
|
||||
}
|
||||
} else if attack_data.dist_sqrd > (3.0 * attack_data.min_attack_dist).powi(2) {
|
||||
self.path_toward_target(
|
||||
agent,
|
||||
controller,
|
||||
tgt_data.pos.0,
|
||||
read_data,
|
||||
Path::Separate,
|
||||
None,
|
||||
);
|
||||
} else if self.energy.current() > 60.0
|
||||
&& agent.combat_state.timers[ActionStateTimers::AttackTimer as usize] < 3.0
|
||||
&& attack_data.angle < 15.0
|
||||
if !agent.combat_state.conditions[Conditions::Healed as usize]
|
||||
&& PHOENIX_HEAL_THRESHOLD > health_fraction
|
||||
{
|
||||
// Shockwave
|
||||
controller.push_basic_input(InputKind::Ability(0));
|
||||
// Move towards the target slowly
|
||||
self.path_toward_target(
|
||||
agent,
|
||||
controller,
|
||||
tgt_data.pos.0,
|
||||
read_data,
|
||||
Path::Separate,
|
||||
Some(0.5),
|
||||
);
|
||||
agent.combat_state.timers[ActionStateTimers::AttackTimer as usize] += read_data.dt.0;
|
||||
} else if agent.combat_state.timers[ActionStateTimers::AttackTimer as usize] < 6.0
|
||||
&& attack_data.angle < 90.0
|
||||
&& attack_data.in_min_range()
|
||||
{
|
||||
// Triple strike
|
||||
controller.push_basic_input(InputKind::Secondary);
|
||||
agent.combat_state.timers[ActionStateTimers::AttackTimer as usize] += read_data.dt.0;
|
||||
controller.push_basic_input(InputKind::Ability(4));
|
||||
} else if (tgt_data.pos.0 - self.pos.0).xy().magnitude_squared() > (35.0_f32).powi(2) {
|
||||
// heat laser
|
||||
maintain_altitude(2.0);
|
||||
controller.push_basic_input(InputKind::Ability(3))
|
||||
} else {
|
||||
// Reset timer
|
||||
agent.combat_state.timers[ActionStateTimers::AttackTimer as usize] = 0.0;
|
||||
// Target is behind us or the timer needs to be reset. Chase target
|
||||
self.path_toward_target(
|
||||
agent,
|
||||
controller,
|
||||
tgt_data.pos.0,
|
||||
read_data,
|
||||
Path::Separate,
|
||||
None,
|
||||
);
|
||||
match attack_timer_2 {
|
||||
0 => maintain_altitude(3.0),
|
||||
1 => {
|
||||
//summontornados
|
||||
controller.push_basic_input(InputKind::Ability(1));
|
||||
},
|
||||
2 => {
|
||||
// firerain
|
||||
controller.push_basic_input(InputKind::Ability(2));
|
||||
},
|
||||
3 => {
|
||||
if attack_data.dist_sqrd < 4.0_f32.powi(2) && attack_data.angle < 150.0 {
|
||||
// close range attack
|
||||
match attack_timer_1 {
|
||||
1 => {
|
||||
// short strike
|
||||
controller.push_basic_input(InputKind::Primary);
|
||||
},
|
||||
3 => {
|
||||
// long strike
|
||||
controller.push_basic_input(InputKind::Secondary)
|
||||
},
|
||||
_ => {
|
||||
// leg strike
|
||||
controller.push_basic_input(InputKind::Ability(0))
|
||||
},
|
||||
}
|
||||
} else {
|
||||
match attack_timer_1 {
|
||||
0 | 2 => {
|
||||
maintain_altitude(2.0);
|
||||
},
|
||||
_ => {
|
||||
// heat laser
|
||||
controller.push_basic_input(InputKind::Ability(3))
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
maintain_altitude(2.0);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,6 +176,7 @@ pub enum Tactic {
|
||||
FixedTurret,
|
||||
RotatingTurret,
|
||||
RadialTurret,
|
||||
FieryTornado,
|
||||
SimpleDouble,
|
||||
// u8s are weights that each ability gets used, if it can be used
|
||||
RandomAbilities {
|
||||
|
@ -162,7 +162,7 @@ fn get_npc_entity_info(npc: &Npc, sites: &Sites, index: IndexRef) -> EntityInfo
|
||||
} else {
|
||||
let config_asset = match npc.body {
|
||||
Body::BirdLarge(body) => match body.species {
|
||||
comp::bird_large::Species::Phoenix => "common.entity.wild.peaceful.phoenix",
|
||||
comp::bird_large::Species::Phoenix => "common.entity.wild.aggressive.phoenix",
|
||||
comp::bird_large::Species::Cockatrice => "common.entity.wild.aggressive.cockatrice",
|
||||
comp::bird_large::Species::Roc => "common.entity.wild.aggressive.roc",
|
||||
comp::bird_large::Species::CloudWyvern => {
|
||||
|
93
voxygen/anim/src/bird_large/aura.rs
Normal file
93
voxygen/anim/src/bird_large/aura.rs
Normal file
@ -0,0 +1,93 @@
|
||||
use super::{
|
||||
super::{vek::*, Animation},
|
||||
BirdLargeSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::states::utils::StageSection;
|
||||
|
||||
pub struct AuraAnimation;
|
||||
|
||||
impl Animation for AuraAnimation {
|
||||
type Dependency<'a> = (Option<StageSection>, bool);
|
||||
type Skeleton = BirdLargeSkeleton;
|
||||
|
||||
#[cfg(feature = "use-dyn-lib")]
|
||||
const UPDATE_FN: &'static [u8] = b"bird_large_aura\0";
|
||||
|
||||
#[cfg_attr(feature = "be-dyn-lib", export_name = "bird_large_aura")]
|
||||
fn update_skeleton_inner(
|
||||
skeleton: &Self::Skeleton,
|
||||
(stage_section, on_ground): Self::Dependency<'_>,
|
||||
anim_time: f32,
|
||||
_rate: &mut f32,
|
||||
s_a: &SkeletonAttr,
|
||||
) -> Self::Skeleton {
|
||||
let mut next = (*skeleton).clone();
|
||||
|
||||
let (movement1base, movement2base, movement3, _twitch) = match stage_section {
|
||||
Some(StageSection::Buildup) => (anim_time.powf(0.5), 0.0, 0.0, 0.0),
|
||||
Some(StageSection::Action) => (1.0, anim_time.min(1.0).powf(0.1), 0.0, anim_time),
|
||||
Some(StageSection::Recover) => (1.0, 1.0, anim_time, 1.0),
|
||||
_ => (0.0, 0.0, 0.0, 0.0),
|
||||
};
|
||||
|
||||
let pullback = 1.0 - movement3;
|
||||
|
||||
let movement1abs = movement1base * pullback;
|
||||
let _movement2abs = movement2base * pullback;
|
||||
|
||||
let wave_slow_cos = (anim_time * 4.5).cos();
|
||||
|
||||
next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1 + movement1abs * 1.5);
|
||||
next.chest.orientation = Quaternion::rotation_x(movement1abs * 1.0);
|
||||
|
||||
next.neck.position = Vec3::new(0.0, s_a.neck.0, s_a.neck.1);
|
||||
next.neck.orientation = Quaternion::rotation_x(-0.2);
|
||||
|
||||
next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1);
|
||||
next.head.orientation = Quaternion::rotation_x(wave_slow_cos * 0.01);
|
||||
|
||||
next.beak.position = Vec3::new(0.0, s_a.beak.0, s_a.beak.1);
|
||||
next.beak.orientation = Quaternion::rotation_x(wave_slow_cos * -0.02 - 0.02);
|
||||
|
||||
next.tail_front.position = Vec3::new(0.0, s_a.tail_front.0, s_a.tail_front.1);
|
||||
next.tail_front.orientation = Quaternion::rotation_x(0.6);
|
||||
next.tail_rear.position = Vec3::new(0.0, s_a.tail_rear.0, s_a.tail_rear.1);
|
||||
next.tail_rear.orientation = Quaternion::rotation_x(-0.2);
|
||||
|
||||
if on_ground {
|
||||
next.wing_in_l.position = Vec3::new(-s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2);
|
||||
next.wing_in_r.position = Vec3::new(s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2);
|
||||
|
||||
next.wing_in_l.orientation = Quaternion::rotation_y(-0.8 + movement1abs * 1.5)
|
||||
* Quaternion::rotation_z(0.2 + movement1abs * -0.8);
|
||||
next.wing_in_r.orientation = Quaternion::rotation_y(0.8 + movement1abs * -1.5)
|
||||
* Quaternion::rotation_z(-0.2 + movement1abs * 0.8);
|
||||
|
||||
next.wing_mid_l.position = Vec3::new(-s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2);
|
||||
next.wing_mid_r.position = Vec3::new(s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2);
|
||||
next.wing_mid_l.orientation =
|
||||
Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(0.7);
|
||||
next.wing_mid_r.orientation =
|
||||
Quaternion::rotation_y(0.1) * Quaternion::rotation_z(-0.7);
|
||||
|
||||
next.wing_out_l.position = Vec3::new(-s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2);
|
||||
next.wing_out_r.position = Vec3::new(s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2);
|
||||
next.wing_out_l.orientation =
|
||||
Quaternion::rotation_y(-0.4) * Quaternion::rotation_z(0.2);
|
||||
next.wing_out_r.orientation =
|
||||
Quaternion::rotation_y(0.4) * Quaternion::rotation_z(-0.2);
|
||||
|
||||
next.leg_l.position = Vec3::new(-s_a.leg.0, s_a.leg.1, s_a.leg.2);
|
||||
next.leg_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.leg_r.position = Vec3::new(s_a.leg.0, s_a.leg.1, s_a.leg.2);
|
||||
next.leg_r.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.foot_l.position = Vec3::new(-s_a.foot.0, s_a.foot.1, s_a.foot.2);
|
||||
next.foot_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.foot_r.position = Vec3::new(s_a.foot.0, s_a.foot.1, s_a.foot.2);
|
||||
next.foot_r.orientation = Quaternion::rotation_x(0.0);
|
||||
}
|
||||
|
||||
next
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ use common::{states::utils::StageSection, util::Dir};
|
||||
|
||||
pub struct BreatheAnimation;
|
||||
|
||||
type BreatheAnimationDependency = (
|
||||
type BreatheAnimationDependency<'a> = (
|
||||
Vec3<f32>,
|
||||
f32,
|
||||
Vec3<f32>,
|
||||
@ -15,10 +15,11 @@ type BreatheAnimationDependency = (
|
||||
f32,
|
||||
Dir,
|
||||
bool,
|
||||
Option<&'a str>,
|
||||
);
|
||||
|
||||
impl Animation for BreatheAnimation {
|
||||
type Dependency<'a> = BreatheAnimationDependency;
|
||||
type Dependency<'a> = BreatheAnimationDependency<'a>;
|
||||
type Skeleton = BirdLargeSkeleton;
|
||||
|
||||
#[cfg(feature = "use-dyn-lib")]
|
||||
@ -27,7 +28,17 @@ impl Animation for BreatheAnimation {
|
||||
#[cfg_attr(feature = "be-dyn-lib", export_name = "bird_large_breathe")]
|
||||
fn update_skeleton_inner(
|
||||
skeleton: &Self::Skeleton,
|
||||
(velocity,global_time, _orientation, _last_ori, stage_section, timer, look_dir, on_ground): Self::Dependency<'_>,
|
||||
(
|
||||
velocity,
|
||||
global_time,
|
||||
_orientation,
|
||||
_last_ori,
|
||||
stage_section,
|
||||
timer,
|
||||
look_dir,
|
||||
on_ground,
|
||||
ability_id,
|
||||
): Self::Dependency<'_>,
|
||||
anim_time: f32,
|
||||
_rate: &mut f32,
|
||||
s_a: &SkeletonAttr,
|
||||
@ -106,6 +117,13 @@ impl Animation for BreatheAnimation {
|
||||
next.tail_rear.orientation =
|
||||
Quaternion::rotation_x(-movement1abs * 0.1 + movement2abs * 0.1 + twitch2 * -0.2);
|
||||
} else {
|
||||
match ability_id {
|
||||
Some("common.abilities.custom.birdlargefire.heat_laser") => {
|
||||
next.chest.orientation = Quaternion::rotation_x(
|
||||
movement1abs * 0.2 - movement2abs * 0.5 + twitch2 * 0.03,
|
||||
);
|
||||
next.chest.position =
|
||||
Vec3::new(0.0, s_a.chest.0, s_a.chest.1 + movement2abs * -3.0);
|
||||
next.neck.orientation = Quaternion::rotation_x(
|
||||
movement1abs * -0.4
|
||||
+ movement2abs * (-0.5 + velocity.xy().magnitude() * 0.2).min(0.0),
|
||||
@ -116,8 +134,21 @@ impl Animation for BreatheAnimation {
|
||||
+ movement2abs * (-0.5 + velocity.xy().magnitude() * 0.2).min(0.0)
|
||||
+ look_dir.z * 0.4,
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
next.neck.orientation = Quaternion::rotation_x(
|
||||
movement1abs * -0.4
|
||||
+ movement2abs * (-0.5 + velocity.xy().magnitude() * 0.2).min(0.0),
|
||||
);
|
||||
|
||||
next.head.orientation = Quaternion::rotation_x(
|
||||
movement1abs * 0.5
|
||||
+ movement2abs * (-0.5 + velocity.xy().magnitude() * 0.2).min(0.0)
|
||||
+ look_dir.z * 0.4,
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
next
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ impl Animation for ComboAnimation {
|
||||
fn update_skeleton_inner(
|
||||
skeleton: &Self::Skeleton,
|
||||
(
|
||||
_ability_id,
|
||||
ability_id,
|
||||
stage_section,
|
||||
current_strike,
|
||||
global_time,
|
||||
@ -79,7 +79,21 @@ impl Animation for ComboAnimation {
|
||||
} * 1.3;
|
||||
|
||||
for strike in 0..=current_strike {
|
||||
match strike {
|
||||
match ability_id {
|
||||
Some("common.abilities.custom.birdlargefire.legstrike") => match strike {
|
||||
0..=2 => {
|
||||
next.chest.position = Vec3::new(
|
||||
0.0,
|
||||
s_a.chest.0,
|
||||
s_a.chest.1 + wave_slow_cos * 0.06 + move2 * -6.0,
|
||||
);
|
||||
next.chest.orientation = Quaternion::rotation_x(move1 * 0.5 - move2 * 0.8);
|
||||
next.leg_r.orientation = Quaternion::rotation_x(move1 * 1.5 + move2 * -2.5);
|
||||
next.leg_l.orientation = Quaternion::rotation_x(move1 * 1.5 + move2 * -2.5);
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
_ => match strike {
|
||||
0..=2 => {
|
||||
next.chest.position = Vec3::new(
|
||||
0.0,
|
||||
@ -98,14 +112,16 @@ impl Animation for ComboAnimation {
|
||||
* Quaternion::rotation_y(move1mirror * 0.5);
|
||||
|
||||
next.beak.position = Vec3::new(0.0, s_a.beak.0, s_a.beak.1);
|
||||
next.beak.orientation =
|
||||
Quaternion::rotation_x(wave_slow_cos * -0.02 + move1 * -0.5 + move2 * 0.5);
|
||||
next.beak.orientation = Quaternion::rotation_x(
|
||||
wave_slow_cos * -0.02 + move1 * -0.5 + move2 * 0.5,
|
||||
);
|
||||
|
||||
if on_ground {
|
||||
next.tail_front.position =
|
||||
Vec3::new(0.0, s_a.tail_front.0, s_a.tail_front.1);
|
||||
next.tail_front.orientation = Quaternion::rotation_x(-move1 * 0.2);
|
||||
next.tail_rear.position = Vec3::new(0.0, s_a.tail_rear.0, s_a.tail_rear.1);
|
||||
next.tail_rear.position =
|
||||
Vec3::new(0.0, s_a.tail_rear.0, s_a.tail_rear.1);
|
||||
next.tail_rear.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.wing_in_l.position =
|
||||
@ -124,7 +140,8 @@ impl Animation for ComboAnimation {
|
||||
Vec3::new(-s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2);
|
||||
next.wing_mid_r.position =
|
||||
Vec3::new(s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2);
|
||||
next.wing_mid_l.orientation = Quaternion::rotation_y(-0.1 + move1 * -0.5)
|
||||
next.wing_mid_l.orientation =
|
||||
Quaternion::rotation_y(-0.1 + move1 * -0.5)
|
||||
* Quaternion::rotation_z(0.7 + move1 * -0.7);
|
||||
next.wing_mid_r.orientation = Quaternion::rotation_y(0.1 + move1 * 0.5)
|
||||
* Quaternion::rotation_z(-0.7 + move1 * 0.7);
|
||||
@ -133,13 +150,15 @@ impl Animation for ComboAnimation {
|
||||
Vec3::new(-s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2);
|
||||
next.wing_out_r.position =
|
||||
Vec3::new(s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2);
|
||||
next.wing_out_l.orientation = Quaternion::rotation_y(-0.2 + move1 * -0.3)
|
||||
next.wing_out_l.orientation =
|
||||
Quaternion::rotation_y(-0.2 + move1 * -0.3)
|
||||
* Quaternion::rotation_z(0.2);
|
||||
next.wing_out_r.orientation = Quaternion::rotation_y(0.2 + move1 * 0.3)
|
||||
* Quaternion::rotation_z(-0.2);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub mod alpha;
|
||||
pub mod aura;
|
||||
pub mod breathe;
|
||||
pub mod combomelee;
|
||||
pub mod dash;
|
||||
@ -6,6 +7,7 @@ pub mod feed;
|
||||
pub mod fly;
|
||||
pub mod idle;
|
||||
pub mod run;
|
||||
pub mod selfbuff;
|
||||
pub mod shockwave;
|
||||
pub mod shoot;
|
||||
pub mod stunned;
|
||||
@ -14,10 +16,11 @@ pub mod swim;
|
||||
|
||||
// Reexports
|
||||
pub use self::{
|
||||
alpha::AlphaAnimation, breathe::BreatheAnimation, combomelee::ComboAnimation,
|
||||
dash::DashAnimation, feed::FeedAnimation, fly::FlyAnimation, idle::IdleAnimation,
|
||||
run::RunAnimation, shockwave::ShockwaveAnimation, shoot::ShootAnimation,
|
||||
stunned::StunnedAnimation, summon::SummonAnimation, swim::SwimAnimation,
|
||||
alpha::AlphaAnimation, aura::AuraAnimation, breathe::BreatheAnimation,
|
||||
combomelee::ComboAnimation, dash::DashAnimation, feed::FeedAnimation, fly::FlyAnimation,
|
||||
idle::IdleAnimation, run::RunAnimation, selfbuff::SelfBuffAnimation,
|
||||
shockwave::ShockwaveAnimation, shoot::ShootAnimation, stunned::StunnedAnimation,
|
||||
summon::SummonAnimation, swim::SwimAnimation,
|
||||
};
|
||||
|
||||
use super::{make_bone, vek::*, FigureBoneData, Offsets, Skeleton};
|
||||
|
99
voxygen/anim/src/bird_large/selfbuff.rs
Normal file
99
voxygen/anim/src/bird_large/selfbuff.rs
Normal file
@ -0,0 +1,99 @@
|
||||
use super::{
|
||||
super::{vek::*, Animation},
|
||||
BirdLargeSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::states::utils::StageSection;
|
||||
|
||||
pub struct SelfBuffAnimation;
|
||||
|
||||
impl Animation for SelfBuffAnimation {
|
||||
type Dependency<'a> = (Option<StageSection>, bool);
|
||||
type Skeleton = BirdLargeSkeleton;
|
||||
|
||||
#[cfg(feature = "use-dyn-lib")]
|
||||
const UPDATE_FN: &'static [u8] = b"bird_large_selfbuff\0";
|
||||
|
||||
#[cfg_attr(feature = "be-dyn-lib", export_name = "bird_large_selfbuff")]
|
||||
fn update_skeleton_inner(
|
||||
skeleton: &Self::Skeleton,
|
||||
(stage_section, on_ground): Self::Dependency<'_>,
|
||||
anim_time: f32,
|
||||
_rate: &mut f32,
|
||||
s_a: &SkeletonAttr,
|
||||
) -> Self::Skeleton {
|
||||
let mut next = (*skeleton).clone();
|
||||
|
||||
let (movement1base, movement2base, movement3, _twitch) = match stage_section {
|
||||
Some(StageSection::Buildup) => (anim_time.powf(0.5), 0.0, 0.0, 0.0),
|
||||
Some(StageSection::Action) => (1.0, anim_time.min(1.0).powf(0.1), 0.0, anim_time),
|
||||
Some(StageSection::Recover) => (1.0, 1.0, anim_time, 1.0),
|
||||
_ => (0.0, 0.0, 0.0, 0.0),
|
||||
};
|
||||
|
||||
let pullback = 1.0 - movement3;
|
||||
|
||||
let movement1abs = movement1base * pullback;
|
||||
let movement2abs = movement2base * pullback;
|
||||
|
||||
let wave_slow_cos = (anim_time * 4.5).cos();
|
||||
|
||||
next.chest.position = Vec3::new(
|
||||
0.0,
|
||||
s_a.chest.0,
|
||||
s_a.chest.1 + movement1abs * 2.5 + movement2abs * 2.5,
|
||||
);
|
||||
next.chest.orientation = Quaternion::rotation_x(movement1abs * 1.0 + movement2abs * 1.0);
|
||||
|
||||
next.neck.position = Vec3::new(0.0, s_a.neck.0, s_a.neck.1);
|
||||
next.neck.orientation = Quaternion::rotation_x(-0.2);
|
||||
|
||||
next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1);
|
||||
next.head.orientation = Quaternion::rotation_x(wave_slow_cos * 0.01);
|
||||
|
||||
next.beak.position = Vec3::new(0.0, s_a.beak.0, s_a.beak.1);
|
||||
next.beak.orientation = Quaternion::rotation_x(wave_slow_cos * -0.02 - 0.02);
|
||||
|
||||
next.tail_front.position = Vec3::new(0.0, s_a.tail_front.0, s_a.tail_front.1);
|
||||
next.tail_front.orientation = Quaternion::rotation_x(0.6);
|
||||
next.tail_rear.position = Vec3::new(0.0, s_a.tail_rear.0, s_a.tail_rear.1);
|
||||
next.tail_rear.orientation = Quaternion::rotation_x(-0.2);
|
||||
|
||||
if on_ground {
|
||||
next.wing_in_l.position = Vec3::new(-s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2);
|
||||
next.wing_in_r.position = Vec3::new(s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2);
|
||||
|
||||
next.wing_in_l.orientation =
|
||||
Quaternion::rotation_y(-0.8 + movement1abs * 1.6 + movement2abs * 1.6)
|
||||
* Quaternion::rotation_z(0.2 + movement1abs * -0.8);
|
||||
next.wing_in_r.orientation =
|
||||
Quaternion::rotation_y(0.8 + movement1abs * -1.6 + movement2abs * -1.6)
|
||||
* Quaternion::rotation_z(-0.2 + movement1abs * 0.8);
|
||||
|
||||
next.wing_mid_l.position = Vec3::new(-s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2);
|
||||
next.wing_mid_r.position = Vec3::new(s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2);
|
||||
next.wing_mid_l.orientation =
|
||||
Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(0.7);
|
||||
next.wing_mid_r.orientation =
|
||||
Quaternion::rotation_y(0.1) * Quaternion::rotation_z(-0.7);
|
||||
|
||||
next.wing_out_l.position = Vec3::new(-s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2);
|
||||
next.wing_out_r.position = Vec3::new(s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2);
|
||||
next.wing_out_l.orientation =
|
||||
Quaternion::rotation_y(-0.4) * Quaternion::rotation_z(0.2);
|
||||
next.wing_out_r.orientation =
|
||||
Quaternion::rotation_y(0.4) * Quaternion::rotation_z(-0.2);
|
||||
|
||||
next.leg_l.position = Vec3::new(-s_a.leg.0, s_a.leg.1, s_a.leg.2);
|
||||
next.leg_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.leg_r.position = Vec3::new(s_a.leg.0, s_a.leg.1, s_a.leg.2);
|
||||
next.leg_r.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.foot_l.position = Vec3::new(-s_a.foot.0, s_a.foot.1, s_a.foot.2);
|
||||
next.foot_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.foot_r.position = Vec3::new(s_a.foot.0, s_a.foot.1, s_a.foot.2);
|
||||
next.foot_r.orientation = Quaternion::rotation_x(0.0);
|
||||
}
|
||||
|
||||
next
|
||||
}
|
||||
}
|
@ -6,10 +6,18 @@ use common::{states::utils::StageSection, util::Dir};
|
||||
|
||||
pub struct ShootAnimation;
|
||||
|
||||
type ShootAnimationDependency = (Vec3<f32>, f32, Option<StageSection>, f32, Dir, bool);
|
||||
type ShootAnimationDependency<'a> = (
|
||||
Vec3<f32>,
|
||||
f32,
|
||||
Option<StageSection>,
|
||||
f32,
|
||||
Dir,
|
||||
bool,
|
||||
Option<&'a str>,
|
||||
);
|
||||
|
||||
impl Animation for ShootAnimation {
|
||||
type Dependency<'a> = ShootAnimationDependency;
|
||||
type Dependency<'a> = ShootAnimationDependency<'a>;
|
||||
type Skeleton = BirdLargeSkeleton;
|
||||
|
||||
#[cfg(feature = "use-dyn-lib")]
|
||||
@ -18,13 +26,88 @@ impl Animation for ShootAnimation {
|
||||
#[cfg_attr(feature = "be-dyn-lib", export_name = "bird_large_shoot")]
|
||||
fn update_skeleton_inner(
|
||||
skeleton: &Self::Skeleton,
|
||||
(velocity, global_time, stage_section, timer, look_dir, on_ground): Self::Dependency<'_>,
|
||||
(velocity, global_time, stage_section, timer, look_dir, on_ground, ability_id,
|
||||
): Self::Dependency<'_>,
|
||||
anim_time: f32,
|
||||
_rate: &mut f32,
|
||||
s_a: &SkeletonAttr,
|
||||
) -> Self::Skeleton {
|
||||
let mut next = (*skeleton).clone();
|
||||
|
||||
match ability_id {
|
||||
Some("common.abilities.custom.birdlargefire.firerain") => {
|
||||
let (movement1base, movement2base, movement3, _twitch) = match stage_section {
|
||||
Some(StageSection::Buildup) => (anim_time.powf(0.5), 0.0, 0.0, 0.0),
|
||||
Some(StageSection::Action) => {
|
||||
(1.0, anim_time.min(1.0).powf(0.1), 0.0, anim_time)
|
||||
},
|
||||
Some(StageSection::Recover) => (1.0, 1.0, anim_time, 1.0),
|
||||
_ => (0.0, 0.0, 0.0, 0.0),
|
||||
};
|
||||
|
||||
let pullback = 1.0 - movement3;
|
||||
let movement1abs = movement1base * pullback;
|
||||
let _movement2abs = movement2base * pullback;
|
||||
let wave_slow_cos = (anim_time * 4.5).cos();
|
||||
|
||||
next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1 + movement1abs * 2.5);
|
||||
next.chest.orientation = Quaternion::rotation_x(movement1abs * 1.0);
|
||||
|
||||
next.neck.position = Vec3::new(0.0, s_a.neck.0, s_a.neck.1);
|
||||
next.neck.orientation = Quaternion::rotation_x(-0.2);
|
||||
|
||||
next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1);
|
||||
next.head.orientation = Quaternion::rotation_x(wave_slow_cos * 0.01);
|
||||
|
||||
next.beak.position = Vec3::new(0.0, s_a.beak.0, s_a.beak.1);
|
||||
next.beak.orientation = Quaternion::rotation_x(wave_slow_cos * -0.02 - 0.02);
|
||||
|
||||
next.tail_front.position = Vec3::new(0.0, s_a.tail_front.0, s_a.tail_front.1);
|
||||
next.tail_front.orientation = Quaternion::rotation_x(0.6);
|
||||
next.tail_rear.position = Vec3::new(0.0, s_a.tail_rear.0, s_a.tail_rear.1);
|
||||
next.tail_rear.orientation = Quaternion::rotation_x(-0.2);
|
||||
|
||||
if on_ground {
|
||||
next.wing_in_l.position =
|
||||
Vec3::new(-s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2);
|
||||
next.wing_in_r.position =
|
||||
Vec3::new(s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2);
|
||||
|
||||
next.wing_in_l.orientation = Quaternion::rotation_y(-0.8 + movement1abs * 1.6)
|
||||
* Quaternion::rotation_z(0.2 + movement1abs * -0.8);
|
||||
next.wing_in_r.orientation = Quaternion::rotation_y(0.8 + movement1abs * -1.6)
|
||||
* Quaternion::rotation_z(-0.2 + movement1abs * 0.8);
|
||||
|
||||
next.wing_mid_l.position =
|
||||
Vec3::new(-s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2);
|
||||
next.wing_mid_r.position =
|
||||
Vec3::new(s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2);
|
||||
next.wing_mid_l.orientation =
|
||||
Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(0.7);
|
||||
next.wing_mid_r.orientation =
|
||||
Quaternion::rotation_y(0.1) * Quaternion::rotation_z(-0.7);
|
||||
|
||||
next.wing_out_l.position =
|
||||
Vec3::new(-s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2);
|
||||
next.wing_out_r.position =
|
||||
Vec3::new(s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2);
|
||||
next.wing_out_l.orientation =
|
||||
Quaternion::rotation_y(-0.4) * Quaternion::rotation_z(0.2);
|
||||
next.wing_out_r.orientation =
|
||||
Quaternion::rotation_y(0.4) * Quaternion::rotation_z(-0.2);
|
||||
|
||||
next.leg_l.position = Vec3::new(-s_a.leg.0, s_a.leg.1, s_a.leg.2);
|
||||
next.leg_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.leg_r.position = Vec3::new(s_a.leg.0, s_a.leg.1, s_a.leg.2);
|
||||
next.leg_r.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.foot_l.position = Vec3::new(-s_a.foot.0, s_a.foot.1, s_a.foot.2);
|
||||
next.foot_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.foot_r.position = Vec3::new(s_a.foot.0, s_a.foot.1, s_a.foot.2);
|
||||
next.foot_r.orientation = Quaternion::rotation_x(0.0);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
let (movement1base, movement3, twitch) = match stage_section {
|
||||
Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0),
|
||||
Some(StageSection::Recover) => (1.0, anim_time.powf(0.25), anim_time),
|
||||
@ -40,7 +123,6 @@ impl Animation for ShootAnimation {
|
||||
let movement1mirror = movement1abs * mirror;
|
||||
|
||||
let wave_slow_cos = (anim_time * 4.5).cos();
|
||||
|
||||
next.chest.position = Vec3::new(
|
||||
0.0,
|
||||
s_a.chest.0,
|
||||
@ -85,6 +167,8 @@ impl Animation for ShootAnimation {
|
||||
* Quaternion::rotation_z(0.2 - movement1abs * 0.8);
|
||||
next.wing_in_r.orientation = Quaternion::rotation_y(1.0 - movement1abs * 0.8)
|
||||
* Quaternion::rotation_z(-0.2 + movement1abs * 0.8);
|
||||
}
|
||||
},
|
||||
};
|
||||
next
|
||||
}
|
||||
|
@ -137,7 +137,8 @@ pub fn localize_chat_message(
|
||||
| BuffKind::Poisoned
|
||||
| BuffKind::Parried
|
||||
| BuffKind::PotionSickness
|
||||
| BuffKind::Polymorphed => {
|
||||
| BuffKind::Polymorphed
|
||||
| BuffKind::Heatstroke => {
|
||||
tracing::error!("Player was killed by a debuff that doesn't do damage!");
|
||||
"mysterious"
|
||||
},
|
||||
|
@ -186,6 +186,7 @@ pub enum SfxEvent {
|
||||
GroundDig,
|
||||
PortalActivated,
|
||||
TeleportedByPortal,
|
||||
FromTheAshes,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
|
||||
@ -219,6 +220,7 @@ pub enum VoiceKind {
|
||||
Truffler,
|
||||
Wolf,
|
||||
Wyvern,
|
||||
Phoenix,
|
||||
}
|
||||
|
||||
fn body_to_voice(body: &Body) -> Option<VoiceKind> {
|
||||
@ -274,6 +276,7 @@ fn body_to_voice(body: &Body) -> Option<VoiceKind> {
|
||||
| bird_large::Species::FrostWyvern
|
||||
| bird_large::Species::SeaWyvern
|
||||
| bird_large::Species::WealdWyvern => VoiceKind::Wyvern,
|
||||
bird_large::Species::Phoenix => VoiceKind::Phoenix,
|
||||
_ => VoiceKind::Bird,
|
||||
},
|
||||
Body::BipedSmall(body) => match body.species {
|
||||
@ -492,6 +495,11 @@ impl SfxMgr {
|
||||
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::DeepLaugh);
|
||||
audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), underwater);
|
||||
},
|
||||
Body::Object(object::Body::Tornado)
|
||||
| Body::Object(object::Body::FieryTornado) => {
|
||||
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Swoosh);
|
||||
audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), underwater);
|
||||
},
|
||||
_ => { // not mapped to sfx file
|
||||
},
|
||||
}
|
||||
@ -524,6 +532,10 @@ impl SfxMgr {
|
||||
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::FlameThrower);
|
||||
audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), underwater);
|
||||
},
|
||||
Outcome::FromTheAshes { pos, .. } => {
|
||||
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::FromTheAshes);
|
||||
audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), underwater);
|
||||
},
|
||||
Outcome::ProjectileShot { pos, body, .. } => {
|
||||
match body {
|
||||
Body::Object(
|
||||
@ -633,7 +645,9 @@ impl SfxMgr {
|
||||
audio.emit_sfx(sfx_trigger_item, *pos, None, underwater);
|
||||
};
|
||||
},
|
||||
beam::FrontendSpecifier::Flamethrower | beam::FrontendSpecifier::Cultist => {
|
||||
beam::FrontendSpecifier::Flamethrower
|
||||
| beam::FrontendSpecifier::Cultist
|
||||
| beam::FrontendSpecifier::PhoenixLaser => {
|
||||
if thread_rng().gen_bool(0.5) {
|
||||
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::FlameThrower);
|
||||
audio.emit_sfx(sfx_trigger_item, *pos, None, underwater);
|
||||
|
@ -795,6 +795,7 @@ image_ids! {
|
||||
debuff_parried_0: "voxygen.element.de_buffs.debuff_parried_0",
|
||||
debuff_potionsickness_0: "voxygen.element.de_buffs.debuff_potionsickness_0",
|
||||
debuff_polymorphed: "voxygen.element.de_buffs.debuff_polymorphed",
|
||||
debuff_heatstroke_0: "voxygen.element.de_buffs.debuff_heatstroke_0",
|
||||
|
||||
// Animation Frames
|
||||
// Buff Frame
|
||||
|
@ -5193,6 +5193,7 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id {
|
||||
BuffKind::Parried => imgs.debuff_parried_0,
|
||||
BuffKind::PotionSickness => imgs.debuff_potionsickness_0,
|
||||
BuffKind::Polymorphed => imgs.debuff_polymorphed,
|
||||
BuffKind::Heatstroke => imgs.debuff_heatstroke_0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -5234,6 +5235,7 @@ pub fn get_buff_title(buff: BuffKind, localized_strings: &Localization) -> Cow<s
|
||||
BuffKind::Parried => localized_strings.get_msg("buff-title-parried"),
|
||||
BuffKind::PotionSickness => localized_strings.get_msg("buff-title-potionsickness"),
|
||||
BuffKind::Polymorphed => localized_strings.get_msg("buff-title-polymorphed"),
|
||||
BuffKind::Heatstroke => localized_strings.get_msg("buff-title-heatstroke"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -5279,6 +5281,7 @@ pub fn get_buff_desc(buff: BuffKind, data: BuffData, localized_strings: &Localiz
|
||||
BuffKind::Parried => localized_strings.get_msg("buff-desc-parried"),
|
||||
BuffKind::PotionSickness => localized_strings.get_msg("buff-desc-potionsickness"),
|
||||
BuffKind::Polymorphed => localized_strings.get_msg("buff-desc-polymorphed"),
|
||||
BuffKind::Heatstroke => localized_strings.get_msg("buff-desc-heatstroke"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,7 +215,8 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
|
||||
| BuffKind::Sunderer
|
||||
| BuffKind::Defiance
|
||||
| BuffKind::Bloodfeast
|
||||
| BuffKind::Berserk => Cow::Borrowed(""),
|
||||
| BuffKind::Berserk
|
||||
| BuffKind::Heatstroke => Cow::Borrowed(""),
|
||||
};
|
||||
|
||||
write!(&mut description, "{}", buff_desc).unwrap();
|
||||
@ -263,7 +264,8 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
|
||||
| BuffKind::Sunderer
|
||||
| BuffKind::Defiance
|
||||
| BuffKind::Bloodfeast
|
||||
| BuffKind::Berserk => Cow::Borrowed(""),
|
||||
| BuffKind::Berserk
|
||||
| BuffKind::Heatstroke => Cow::Borrowed(""),
|
||||
}
|
||||
} else if let BuffKind::Saturation
|
||||
| BuffKind::Regeneration
|
||||
|
@ -99,6 +99,16 @@ pub enum ParticleMode {
|
||||
PortalFizz = 45,
|
||||
Ink = 46,
|
||||
Whirlwind = 47,
|
||||
FieryBurst = 48,
|
||||
FieryBurstVortex = 49,
|
||||
FieryBurstSparks = 50,
|
||||
FieryBurstAsh = 51,
|
||||
FieryTornado = 52,
|
||||
PhoenixCloud = 53,
|
||||
FieryDropletTrace = 54,
|
||||
EnergyPhoenix = 55,
|
||||
PhoenixBeam = 56,
|
||||
PhoenixBuildUpAim = 57,
|
||||
}
|
||||
|
||||
impl ParticleMode {
|
||||
|
@ -4931,6 +4931,7 @@ impl FigureMgr {
|
||||
state.state_time,
|
||||
look_dir,
|
||||
physics.on_ground.is_some(),
|
||||
ability_id,
|
||||
),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
@ -4993,6 +4994,36 @@ impl FigureMgr {
|
||||
state.state_time,
|
||||
look_dir,
|
||||
physics.on_ground.is_some(),
|
||||
ability_id,
|
||||
),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
skeleton_attr,
|
||||
)
|
||||
},
|
||||
CharacterState::RepeaterRanged(s) => {
|
||||
let stage_time = s.timer.as_secs_f32();
|
||||
|
||||
let stage_progress = match s.stage_section {
|
||||
StageSection::Buildup => {
|
||||
stage_time / s.static_data.buildup_duration.as_secs_f32()
|
||||
},
|
||||
StageSection::Recover => {
|
||||
stage_time / s.static_data.recover_duration.as_secs_f32()
|
||||
},
|
||||
|
||||
_ => 0.0,
|
||||
};
|
||||
anim::bird_large::ShootAnimation::update_skeleton(
|
||||
&target_base,
|
||||
(
|
||||
rel_vel,
|
||||
time,
|
||||
Some(s.stage_section),
|
||||
state.state_time,
|
||||
look_dir,
|
||||
physics.on_ground.is_some(),
|
||||
ability_id,
|
||||
),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
@ -5021,6 +5052,50 @@ impl FigureMgr {
|
||||
skeleton_attr,
|
||||
)
|
||||
},
|
||||
CharacterState::BasicAura(s) => {
|
||||
let stage_time = s.timer.as_secs_f32();
|
||||
let stage_progress = match s.stage_section {
|
||||
StageSection::Buildup => {
|
||||
stage_time / s.static_data.buildup_duration.as_secs_f32()
|
||||
},
|
||||
StageSection::Action => {
|
||||
stage_time / s.static_data.cast_duration.as_secs_f32()
|
||||
},
|
||||
StageSection::Recover => {
|
||||
stage_time / s.static_data.recover_duration.as_secs_f32()
|
||||
},
|
||||
_ => 0.0,
|
||||
};
|
||||
anim::bird_large::AuraAnimation::update_skeleton(
|
||||
&target_base,
|
||||
(Some(s.stage_section), physics.on_ground.is_some()),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
skeleton_attr,
|
||||
)
|
||||
},
|
||||
CharacterState::SelfBuff(s) => {
|
||||
let stage_time = s.timer.as_secs_f32();
|
||||
let stage_progress = match s.stage_section {
|
||||
StageSection::Buildup => {
|
||||
stage_time / s.static_data.buildup_duration.as_secs_f32()
|
||||
},
|
||||
StageSection::Action => {
|
||||
stage_time / s.static_data.cast_duration.as_secs_f32()
|
||||
},
|
||||
StageSection::Recover => {
|
||||
stage_time / s.static_data.recover_duration.as_secs_f32()
|
||||
},
|
||||
_ => 0.0,
|
||||
};
|
||||
anim::bird_large::SelfBuffAnimation::update_skeleton(
|
||||
&target_base,
|
||||
(Some(s.stage_section), physics.on_ground.is_some()),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
skeleton_attr,
|
||||
)
|
||||
},
|
||||
CharacterState::BasicSummon(s) => {
|
||||
let stage_time = s.timer.as_secs_f32();
|
||||
let stage_progress = match s.stage_section {
|
||||
|
@ -492,6 +492,7 @@ impl Scene {
|
||||
Rgb::new(1.0, 0.0, 0.0)
|
||||
}
|
||||
},
|
||||
Some(Reagent::Phoenix) => Rgb::new(1.0, 0.8, 0.3),
|
||||
Some(Reagent::White) => Rgb::new(1.0, 1.0, 1.0),
|
||||
Some(Reagent::Yellow) => Rgb::new(1.0, 1.0, 0.0),
|
||||
None => Rgb::new(1.0, 0.5, 0.0),
|
||||
|
@ -15,7 +15,7 @@ use common::{
|
||||
item::Reagent,
|
||||
object,
|
||||
shockwave::{self, ShockwaveDodgeable},
|
||||
Beam, Body, CharacterState, Ori, Pos, Scale, Shockwave, Vel,
|
||||
Beam, Body, CharacterActivity, CharacterState, Ori, Pos, Scale, Shockwave, Vel,
|
||||
},
|
||||
figure::Segment,
|
||||
outcome::Outcome,
|
||||
@ -170,6 +170,23 @@ impl ParticleMgr {
|
||||
},
|
||||
);
|
||||
},
|
||||
Some(Reagent::Phoenix) => {
|
||||
self.particles.resize_with(
|
||||
self.particles.len() + (5.0 * power.abs()) as usize,
|
||||
|| {
|
||||
Particle::new_directed(
|
||||
Duration::from_millis(300),
|
||||
time,
|
||||
ParticleMode::Explosion,
|
||||
*pos,
|
||||
*pos + Vec3::<f32>::zero()
|
||||
.map(|_| rng.gen_range(-1.0..1.0))
|
||||
.normalized()
|
||||
* *radius,
|
||||
)
|
||||
},
|
||||
);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
} else {
|
||||
@ -186,6 +203,7 @@ impl ParticleMgr {
|
||||
Some(Reagent::Red) => ParticleMode::FireworkRed,
|
||||
Some(Reagent::White) => ParticleMode::FireworkWhite,
|
||||
Some(Reagent::Yellow) => ParticleMode::FireworkYellow,
|
||||
Some(Reagent::Phoenix) => ParticleMode::FireworkYellow,
|
||||
None => ParticleMode::Shrapnel,
|
||||
},
|
||||
*pos,
|
||||
@ -431,6 +449,7 @@ impl ParticleMgr {
|
||||
| Outcome::Steam { .. }
|
||||
| Outcome::FireShockwave { .. }
|
||||
| Outcome::PortalActivated { .. }
|
||||
| Outcome::FromTheAshes { .. }
|
||||
| Outcome::LaserBeam { .. } => {},
|
||||
}
|
||||
}
|
||||
@ -501,12 +520,18 @@ impl ParticleMgr {
|
||||
Body::Object(object::Body::BoltFireBig) => {
|
||||
self.maintain_boltfirebig_particles(scene_data, interpolated.pos, vel)
|
||||
},
|
||||
Body::Object(object::Body::FireRainDrop) => {
|
||||
self.maintain_fireraindrop_particles(scene_data, interpolated.pos, vel)
|
||||
},
|
||||
Body::Object(object::Body::BoltNature) => {
|
||||
self.maintain_boltnature_particles(scene_data, interpolated.pos, vel)
|
||||
},
|
||||
Body::Object(object::Body::Tornado) => {
|
||||
self.maintain_tornado_particles(scene_data, interpolated.pos)
|
||||
},
|
||||
Body::Object(object::Body::FieryTornado) => {
|
||||
self.maintain_fiery_tornado_particles(scene_data, interpolated.pos)
|
||||
},
|
||||
Body::Object(object::Body::Mine) => {
|
||||
self.maintain_mine_particles(scene_data, interpolated.pos)
|
||||
},
|
||||
@ -672,6 +697,38 @@ impl ParticleMgr {
|
||||
);
|
||||
}
|
||||
|
||||
fn maintain_fireraindrop_particles(
|
||||
&mut self,
|
||||
scene_data: &SceneData,
|
||||
pos: Vec3<f32>,
|
||||
vel: Option<&Vel>,
|
||||
) {
|
||||
span!(
|
||||
_guard,
|
||||
"fireraindrop_particles",
|
||||
"ParticleMgr::maintain_fireraindrop_particles"
|
||||
);
|
||||
let time = scene_data.state.get_time();
|
||||
let dt = scene_data.state.get_delta_time();
|
||||
let mut rng = thread_rng();
|
||||
|
||||
// trace
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ usize::from(self.scheduler.heartbeats(Duration::from_millis(100))),
|
||||
|| {
|
||||
Particle::new(
|
||||
Duration::from_millis(300),
|
||||
time,
|
||||
ParticleMode::FieryDropletTrace,
|
||||
pos.map(|e| e + rng.gen_range(-0.25..0.25))
|
||||
+ Vec3::new(0.0, 0.0, 0.5)
|
||||
+ vel.map_or(Vec3::zero(), |v| -v.0 * dt * rng.gen::<f32>()),
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn maintain_boltnature_particles(
|
||||
&mut self,
|
||||
scene_data: &SceneData,
|
||||
@ -715,6 +772,24 @@ impl ParticleMgr {
|
||||
);
|
||||
}
|
||||
|
||||
fn maintain_fiery_tornado_particles(&mut self, scene_data: &SceneData, pos: Vec3<f32>) {
|
||||
let time = scene_data.state.get_time();
|
||||
let mut rng = thread_rng();
|
||||
|
||||
// air particles
|
||||
self.particles.resize_with(
|
||||
self.particles.len() + usize::from(self.scheduler.heartbeats(Duration::from_millis(5))),
|
||||
|| {
|
||||
Particle::new(
|
||||
Duration::from_millis(1000),
|
||||
time,
|
||||
ParticleMode::FieryTornado,
|
||||
pos.map(|e| e + rng.gen_range(-0.25..0.25)),
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn maintain_bomb_particles(
|
||||
&mut self,
|
||||
scene_data: &SceneData,
|
||||
@ -830,12 +905,14 @@ impl ParticleMgr {
|
||||
let dt = scene_data.state.get_delta_time();
|
||||
let mut rng = thread_rng();
|
||||
|
||||
for (entity, interpolated, vel, character_state, body) in (
|
||||
for (entity, interpolated, vel, character_state, body, ori, character_activity) in (
|
||||
&ecs.entities(),
|
||||
&ecs.read_storage::<Interpolated>(),
|
||||
ecs.read_storage::<Vel>().maybe(),
|
||||
&ecs.read_storage::<CharacterState>(),
|
||||
&ecs.read_storage::<Body>(),
|
||||
&ecs.read_storage::<Ori>(),
|
||||
&ecs.read_storage::<CharacterActivity>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
@ -957,6 +1034,87 @@ impl ParticleMgr {
|
||||
}
|
||||
}
|
||||
},
|
||||
CharacterState::RepeaterRanged(repeater) => {
|
||||
if let Some(specifier) = repeater.static_data.specifier {
|
||||
match specifier {
|
||||
states::repeater_ranged::FrontendSpecifier::FireRain => {
|
||||
// base, dark clouds
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ 2 * usize::from(
|
||||
self.scheduler.heartbeats(Duration::from_millis(25)),
|
||||
),
|
||||
|| {
|
||||
let rand_pos = {
|
||||
let theta = rng.gen::<f32>() * TAU;
|
||||
let radius = repeater
|
||||
.static_data
|
||||
.properties_of_aoe
|
||||
.map(|aoe| aoe.radius)
|
||||
.unwrap_or_default()
|
||||
* rng.gen::<f32>().sqrt();
|
||||
let x = radius * theta.sin();
|
||||
let y = radius * theta.cos();
|
||||
Vec2::new(x, y) + interpolated.pos.xy()
|
||||
};
|
||||
let pos1 = rand_pos.with_z(
|
||||
repeater
|
||||
.static_data
|
||||
.properties_of_aoe
|
||||
.map(|aoe| aoe.height)
|
||||
.unwrap_or_default()
|
||||
+ interpolated.pos.z
|
||||
+ 2.0 * rng.gen::<f32>(),
|
||||
);
|
||||
Particle::new_directed(
|
||||
Duration::from_secs_f32(3.0),
|
||||
time,
|
||||
ParticleMode::PhoenixCloud,
|
||||
pos1,
|
||||
pos1 + Vec3::new(7.09, 4.09, 18.09),
|
||||
)
|
||||
},
|
||||
);
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ 2 * usize::from(
|
||||
self.scheduler.heartbeats(Duration::from_millis(25)),
|
||||
),
|
||||
|| {
|
||||
let rand_pos = {
|
||||
let theta = rng.gen::<f32>() * TAU;
|
||||
let radius = repeater
|
||||
.static_data
|
||||
.properties_of_aoe
|
||||
.map(|aoe| aoe.radius)
|
||||
.unwrap_or_default()
|
||||
* rng.gen::<f32>().sqrt();
|
||||
let x = radius * theta.sin();
|
||||
let y = radius * theta.cos();
|
||||
Vec2::new(x, y) + interpolated.pos.xy()
|
||||
};
|
||||
let pos1 = rand_pos.with_z(
|
||||
repeater
|
||||
.static_data
|
||||
.properties_of_aoe
|
||||
.map(|aoe| aoe.height)
|
||||
.unwrap_or_default()
|
||||
+ interpolated.pos.z
|
||||
+ 1.5 * rng.gen::<f32>(),
|
||||
);
|
||||
Particle::new_directed(
|
||||
Duration::from_secs_f32(2.5),
|
||||
time,
|
||||
ParticleMode::PhoenixCloud,
|
||||
pos1,
|
||||
pos1 + Vec3::new(10.025, 4.025, 17.025),
|
||||
)
|
||||
},
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
CharacterState::Blink(c) => {
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
@ -992,6 +1150,94 @@ impl ParticleMgr {
|
||||
);
|
||||
},
|
||||
CharacterState::SelfBuff(c) => {
|
||||
if let Some(specifier) = c.static_data.specifier {
|
||||
match specifier {
|
||||
states::self_buff::FrontendSpecifier::FromTheAshes => {
|
||||
if matches!(c.stage_section, StageSection::Action) {
|
||||
let pos = interpolated.pos;
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ 2 * usize::from(
|
||||
self.scheduler.heartbeats(Duration::from_millis(1)),
|
||||
),
|
||||
|| {
|
||||
let start_pos = pos + Vec3::unit_z() - 1.0;
|
||||
let end_pos = pos
|
||||
+ Vec3::new(
|
||||
4.0 * rng.gen::<f32>() - 1.0,
|
||||
4.0 * rng.gen::<f32>() - 1.0,
|
||||
0.0,
|
||||
)
|
||||
.normalized()
|
||||
* 1.5
|
||||
+ Vec3::unit_z()
|
||||
+ 5.0 * rng.gen::<f32>();
|
||||
|
||||
Particle::new_directed(
|
||||
Duration::from_secs_f32(0.5),
|
||||
time,
|
||||
ParticleMode::FieryBurst,
|
||||
start_pos,
|
||||
end_pos,
|
||||
)
|
||||
},
|
||||
);
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ usize::from(
|
||||
self.scheduler
|
||||
.heartbeats(Duration::from_millis(10)),
|
||||
),
|
||||
|| {
|
||||
Particle::new(
|
||||
Duration::from_millis(650),
|
||||
time,
|
||||
ParticleMode::FieryBurstVortex,
|
||||
pos.map(|e| e + rng.gen_range(-0.25..0.25))
|
||||
+ Vec3::new(0.0, 0.0, 1.0),
|
||||
)
|
||||
},
|
||||
);
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ usize::from(
|
||||
self.scheduler
|
||||
.heartbeats(Duration::from_millis(40)),
|
||||
),
|
||||
|| {
|
||||
Particle::new(
|
||||
Duration::from_millis(1000),
|
||||
time,
|
||||
ParticleMode::FieryBurstSparks,
|
||||
pos.map(|e| e + rng.gen_range(-0.25..0.25)),
|
||||
)
|
||||
},
|
||||
);
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ usize::from(
|
||||
self.scheduler
|
||||
.heartbeats(Duration::from_millis(14)),
|
||||
),
|
||||
|| {
|
||||
let pos1 = pos.map(|e| e + rng.gen_range(-0.25..0.25));
|
||||
Particle::new_directed(
|
||||
Duration::from_millis(1000),
|
||||
time,
|
||||
ParticleMode::FieryBurstAsh,
|
||||
pos1,
|
||||
Vec3::new(
|
||||
4.5, // radius of rand spawn
|
||||
20.4, // integer part - radius of the curve part, fractional part - relative time of setting particle on fire
|
||||
8.58) // height of the flight
|
||||
+ pos1,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
use buff::BuffKind;
|
||||
if let BuffKind::Frenzied = c.static_data.buff_kind {
|
||||
if matches!(c.stage_section, StageSection::Action) {
|
||||
@ -1022,6 +1268,44 @@ impl ParticleMgr {
|
||||
}
|
||||
}
|
||||
},
|
||||
CharacterState::BasicBeam(beam) => {
|
||||
let ori = *ori;
|
||||
let _look_dir = *character_activity.look_dir.unwrap_or(ori.look_dir());
|
||||
let dir = ori.look_dir(); //.with_z(look_dir.z);
|
||||
let specifier = beam.static_data.specifier;
|
||||
if specifier == beam::FrontendSpecifier::PhoenixLaser
|
||||
&& matches!(beam.stage_section, StageSection::Buildup)
|
||||
{
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ 2 * usize::from(
|
||||
self.scheduler.heartbeats(Duration::from_millis(2)),
|
||||
),
|
||||
|| {
|
||||
let mut left_right_alignment =
|
||||
dir.cross(Vec3::new(0.0, 0.0, 1.0)).normalized();
|
||||
if rng.gen_bool(0.5) {
|
||||
left_right_alignment *= -1.0;
|
||||
}
|
||||
let start = interpolated.pos
|
||||
+ left_right_alignment * 4.0
|
||||
+ dir.normalized() * 6.0;
|
||||
let lifespan = Duration::from_secs_f32(0.5);
|
||||
Particle::new_directed(
|
||||
lifespan,
|
||||
time,
|
||||
ParticleMode::PhoenixBuildUpAim,
|
||||
start,
|
||||
interpolated.pos
|
||||
+ dir.normalized() * 3.0
|
||||
+ left_right_alignment * 0.4
|
||||
+ vel
|
||||
.map_or(Vec3::zero(), |v| v.0 * lifespan.as_secs_f32()),
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
@ -1285,6 +1569,20 @@ impl ParticleMgr {
|
||||
},
|
||||
);
|
||||
},
|
||||
beam::FrontendSpecifier::PhoenixLaser => {
|
||||
self.particles.resize_with(
|
||||
self.particles.len() + usize::from(beam_tick_count) / 2,
|
||||
|| {
|
||||
Particle::new_directed(
|
||||
Duration::from_secs_f64(beam.duration.0),
|
||||
time,
|
||||
ParticleMode::PhoenixBeam,
|
||||
beam.bezier.start,
|
||||
beam.bezier.start + beam_dir * beam.range,
|
||||
)
|
||||
},
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1458,6 +1756,62 @@ impl ParticleMgr {
|
||||
);
|
||||
}
|
||||
},
|
||||
aura::AuraKind::Buff {
|
||||
kind: buff::BuffKind::Heatstroke,
|
||||
..
|
||||
} => {
|
||||
let heartbeats = self.scheduler.heartbeats(Duration::from_millis(5));
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ aura.radius.powi(2) as usize * usize::from(heartbeats) / 900,
|
||||
|| {
|
||||
let rand_dist = aura.radius * (1.0 - rng.gen::<f32>().powi(100));
|
||||
let init_pos = Vec3::new(rand_dist, 0_f32, 0_f32);
|
||||
let duration = Duration::from_secs_f64(
|
||||
aura.end_time
|
||||
.map_or(1.0, |end| end.0 - time)
|
||||
.clamp(0.0, 1.0),
|
||||
);
|
||||
Particle::new_directed(
|
||||
duration,
|
||||
time,
|
||||
ParticleMode::EnergyPhoenix,
|
||||
pos,
|
||||
pos + init_pos,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
let num_particles = aura.radius.powi(2) * dt / 50.0;
|
||||
let num_particles = num_particles.floor() as usize
|
||||
+ usize::from(rng.gen_bool(f64::from(num_particles % 1.0)));
|
||||
self.particles
|
||||
.resize_with(self.particles.len() + num_particles, || {
|
||||
let rand_pos = {
|
||||
let theta = rng.gen::<f32>() * TAU;
|
||||
let radius = aura.radius * rng.gen::<f32>().sqrt();
|
||||
let x = radius * theta.sin();
|
||||
let y = radius * theta.cos();
|
||||
Vec2::new(x, y) + pos.xy()
|
||||
};
|
||||
let duration = Duration::from_secs_f64(
|
||||
aura.end_time
|
||||
.map_or(1.0, |end| end.0 - time)
|
||||
.clamp(0.0, 1.0),
|
||||
);
|
||||
Particle::new_directed(
|
||||
duration,
|
||||
time,
|
||||
ParticleMode::FieryBurstAsh,
|
||||
pos,
|
||||
Vec3::new(
|
||||
0.0, // radius of rand spawn
|
||||
20.0, // integer part - radius of the curve part, fractional part - relative time of setting particle on fire
|
||||
5.5) // height of the flight
|
||||
+ rand_pos.with_z(pos.z),
|
||||
)
|
||||
});
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user