mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'sam/tidal-warrior' into 'master'
Tidal Warrior Rework See merge request veloren/veloren!2340
This commit is contained in:
commit
e984ebc446
@ -119,6 +119,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Reworked sprite rendering to vastly reduce the CPU work. Large sprite view distances are now much more performant.
|
||||
- Optimized rendering of quads (most of the graphics in the game) using an index buffer, decreasing the number of vertices that need to be processed by 33%.
|
||||
- Moved the rest of screenshot work into the background. Screenshoting no longer induces large pauses.
|
||||
- Reworked tidal warrior to have unique attacks
|
||||
|
||||
### Removed
|
||||
|
||||
|
@ -108,9 +108,17 @@
|
||||
secondary: "common.abilities.custom.wendigomagic.singlestrike",
|
||||
abilities: [],
|
||||
),
|
||||
Custom("Tidal Claws"): (
|
||||
primary: "common.abilities.staff.flamethrower",
|
||||
secondary: "common.abilities.custom.wendigomagic.singlestrike",
|
||||
Custom("Tidal Warrior"): (
|
||||
primary: "common.abilities.custom.tidalwarrior.pincer",
|
||||
secondary: "common.abilities.custom.tidalwarrior.scuttle",
|
||||
abilities: [
|
||||
(None, "common.abilities.custom.tidalwarrior.bubbles"),
|
||||
(None, "common.abilities.custom.tidalwarrior.totem"),
|
||||
],
|
||||
),
|
||||
Custom("Tidal Totem"): (
|
||||
primary: "common.abilities.custom.tidalwarrior.totem_wave",
|
||||
secondary: "common.abilities.custom.tidalwarrior.totem_wave",
|
||||
abilities: [],
|
||||
),
|
||||
Custom("Quad Med Quick"): (
|
||||
|
@ -3,7 +3,7 @@ BasicMelee(
|
||||
buildup_duration: 0.25,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 0.25,
|
||||
knockback: 25.0,
|
||||
knockback: ( strength: 25.0, direction: Away),
|
||||
base_damage: 200,
|
||||
base_poise_damage: 40,
|
||||
range: 5.0,
|
||||
|
@ -13,4 +13,5 @@ Shockwave(
|
||||
requires_ground: false,
|
||||
move_efficiency: 0.1,
|
||||
damage_kind: Energy,
|
||||
specifier: Fire,
|
||||
)
|
||||
|
@ -13,4 +13,5 @@ Shockwave(
|
||||
requires_ground: true,
|
||||
move_efficiency: 0.0,
|
||||
damage_kind: Crushing,
|
||||
specifier: Ground,
|
||||
)
|
@ -5,7 +5,7 @@ BasicMelee(
|
||||
recover_duration: 0.5,
|
||||
base_damage: 200,
|
||||
base_poise_damage: 50,
|
||||
knockback: 10.0,
|
||||
knockback: ( strength: 10.0, direction: Away),
|
||||
range: 4.0,
|
||||
max_angle: 45.0,
|
||||
damage_effect: None,
|
||||
|
@ -5,7 +5,7 @@ BasicMelee(
|
||||
recover_duration: 0.6,
|
||||
base_damage: 150.0,
|
||||
base_poise_damage: 60.0,
|
||||
knockback: 15.0,
|
||||
knockback: ( strength: 15.0, direction: Away),
|
||||
range: 5.0,
|
||||
max_angle: 60.0,
|
||||
damage_effect: Some(Buff((
|
||||
|
@ -5,7 +5,7 @@ BasicMelee(
|
||||
recover_duration: 0.35,
|
||||
base_damage: 100,
|
||||
base_poise_damage: 28,
|
||||
knockback: 25.0,
|
||||
knockback: ( strength: 25.0, direction: Away),
|
||||
range: 1.2,
|
||||
max_angle: 50.0,
|
||||
damage_effect: None,
|
||||
|
@ -13,4 +13,5 @@ Shockwave(
|
||||
requires_ground: true,
|
||||
move_efficiency: 0.05,
|
||||
damage_kind: Crushing,
|
||||
specifier: Ground,
|
||||
)
|
||||
|
19
assets/common/abilities/custom/tidalwarrior/bubbles.ron
Normal file
19
assets/common/abilities/custom/tidalwarrior/bubbles.ron
Normal file
@ -0,0 +1,19 @@
|
||||
BasicBeam(
|
||||
buildup_duration: 0.5,
|
||||
recover_duration: 0.5,
|
||||
beam_duration: 2.5,
|
||||
damage: 100,
|
||||
tick_rate: 2.0,
|
||||
range: 25.0,
|
||||
max_angle: 15.0,
|
||||
damage_effect: Some(Buff((
|
||||
kind: Wet,
|
||||
dur_secs: 15.0,
|
||||
strength: Value(4.5),
|
||||
chance: 1.0,
|
||||
))),
|
||||
energy_regen: 0,
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Normal,
|
||||
specifier: Bubbles,
|
||||
)
|
13
assets/common/abilities/custom/tidalwarrior/pincer.ron
Normal file
13
assets/common/abilities/custom/tidalwarrior/pincer.ron
Normal file
@ -0,0 +1,13 @@
|
||||
BasicMelee(
|
||||
energy_cost: 0,
|
||||
buildup_duration: 0.3,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 0.6,
|
||||
base_damage: 50.0,
|
||||
base_poise_damage: 0.0,
|
||||
knockback: ( strength: 100.0, direction: Towards),
|
||||
range: 5.0,
|
||||
max_angle: 60.0,
|
||||
damage_effect: None,
|
||||
damage_kind: Crushing,
|
||||
)
|
20
assets/common/abilities/custom/tidalwarrior/scuttle.ron
Normal file
20
assets/common/abilities/custom/tidalwarrior/scuttle.ron
Normal file
@ -0,0 +1,20 @@
|
||||
DashMelee(
|
||||
energy_cost: 0,
|
||||
base_damage: 50,
|
||||
scaled_damage: 250,
|
||||
base_poise_damage: 10,
|
||||
scaled_poise_damage: 40,
|
||||
base_knockback: 10.0,
|
||||
scaled_knockback: 30.0,
|
||||
range: 5.0,
|
||||
angle: 90.0,
|
||||
energy_drain: 0,
|
||||
forward_speed: 10.0,
|
||||
buildup_duration: 0.4,
|
||||
charge_duration: 2.0,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 0.5,
|
||||
charge_through: true,
|
||||
is_interruptible: false,
|
||||
damage_kind: Crushing,
|
||||
)
|
13
assets/common/abilities/custom/tidalwarrior/totem.ron
Normal file
13
assets/common/abilities/custom/tidalwarrior/totem.ron
Normal file
@ -0,0 +1,13 @@
|
||||
BasicSummon(
|
||||
buildup_duration: 0.5,
|
||||
cast_duration: 1.0,
|
||||
recover_duration: 0.5,
|
||||
summon_amount: 1,
|
||||
summon_info: (
|
||||
body: Object(SeaLantern),
|
||||
scale: None,
|
||||
health_scaling: 0,
|
||||
loadout_config: None,
|
||||
skillset_config: None,
|
||||
),
|
||||
)
|
17
assets/common/abilities/custom/tidalwarrior/totem_wave.ron
Normal file
17
assets/common/abilities/custom/tidalwarrior/totem_wave.ron
Normal file
@ -0,0 +1,17 @@
|
||||
Shockwave(
|
||||
energy_cost: 0,
|
||||
buildup_duration: 1.4,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 0.5,
|
||||
damage: 10,
|
||||
poise_damage: 0,
|
||||
knockback: ( strength: 100.0, direction: Up),
|
||||
shockwave_angle: 360.0,
|
||||
shockwave_vertical_angle: 30.0,
|
||||
shockwave_speed: 10.0,
|
||||
shockwave_duration: 5.0,
|
||||
requires_ground: true,
|
||||
move_efficiency: 0.0,
|
||||
damage_kind: Crushing,
|
||||
specifier: Water,
|
||||
)
|
@ -5,7 +5,7 @@ BasicMelee(
|
||||
recover_duration: 0.3,
|
||||
base_damage: 50,
|
||||
base_poise_damage: 0,
|
||||
knockback: 0.0,
|
||||
knockback: ( strength: 0.0, direction: Away),
|
||||
range: 3.5,
|
||||
max_angle: 20.0,
|
||||
damage_effect: None,
|
||||
|
@ -5,7 +5,7 @@ BasicMelee(
|
||||
recover_duration: 0.9,
|
||||
base_damage: 20,
|
||||
base_poise_damage: 0,
|
||||
knockback: 0.0,
|
||||
knockback: ( strength: 0.0, direction: Away),
|
||||
range: 3.5,
|
||||
max_angle: 15.0,
|
||||
damage_effect: None,
|
||||
|
@ -5,7 +5,7 @@ BasicMelee(
|
||||
recover_duration: 0.15,
|
||||
base_damage: 50,
|
||||
base_poise_damage: 0,
|
||||
knockback: 0.0,
|
||||
knockback: ( strength: 0.0, direction: Away),
|
||||
range: 3.5,
|
||||
max_angle: 20.0,
|
||||
damage_effect: None,
|
||||
|
@ -5,7 +5,7 @@ BasicMelee(
|
||||
recover_duration: 0.15,
|
||||
base_damage: 50,
|
||||
base_poise_damage: 0,
|
||||
knockback: 0.0,
|
||||
knockback: ( strength: 0.0, direction: Away),
|
||||
range: 3.5,
|
||||
max_angle: 20.0,
|
||||
damage_effect: None,
|
||||
|
@ -5,7 +5,7 @@ BasicMelee(
|
||||
recover_duration: 0.3,
|
||||
base_damage: 40,
|
||||
base_poise_damage: 0,
|
||||
knockback: 0.0,
|
||||
knockback: ( strength: 0.0, direction: Away),
|
||||
range: 3.0,
|
||||
max_angle: 120.0,
|
||||
damage_effect: None,
|
||||
|
@ -13,4 +13,5 @@ Shockwave(
|
||||
requires_ground: false,
|
||||
move_efficiency: 0.1,
|
||||
damage_kind: Energy,
|
||||
specifier: Fire,
|
||||
)
|
||||
|
@ -4,7 +4,7 @@ ItemDef(
|
||||
kind: Armor((
|
||||
kind: Chest("Tidal Warrior"),
|
||||
stats: (
|
||||
protection: Normal(0.0),
|
||||
protection: Normal(20.0),
|
||||
poise_resilience: Normal(0.0),
|
||||
),
|
||||
)),
|
||||
|
@ -5,7 +5,7 @@ ItemDef(
|
||||
kind: Natural,
|
||||
hands: Two,
|
||||
stats: Direct((
|
||||
equip_time_secs: 0.5,
|
||||
equip_time_secs: 0.0,
|
||||
power: 1.0,
|
||||
poise_strength: 1.0,
|
||||
speed: 1.0,
|
||||
@ -15,5 +15,5 @@ ItemDef(
|
||||
)),
|
||||
quality: Low,
|
||||
tags: [],
|
||||
ability_spec: Some(Custom("Tidal Claws")),
|
||||
ability_spec: Some(Custom("Tidal Warrior")),
|
||||
)
|
19
assets/common/items/npc_weapons/unique/tidal_totem.ron
Normal file
19
assets/common/items/npc_weapons/unique/tidal_totem.ron
Normal file
@ -0,0 +1,19 @@
|
||||
ItemDef(
|
||||
name: "Tidal Totem",
|
||||
description: "Yeet",
|
||||
kind: Tool((
|
||||
kind: Natural,
|
||||
hands: Two,
|
||||
stats: Direct((
|
||||
equip_time_secs: 0.01,
|
||||
power: 1.0,
|
||||
poise_strength: 1.0,
|
||||
speed: 1.0,
|
||||
crit_chance: 0.0625,
|
||||
crit_mult: 1.9142857,
|
||||
)),
|
||||
)),
|
||||
quality: Low,
|
||||
tags: [],
|
||||
ability_spec: Some(Custom("Tidal Totem")),
|
||||
)
|
@ -40,9 +40,9 @@
|
||||
("common.items.armor.ferocious.hand",1),
|
||||
("common.items.armor.ferocious.foot",1),
|
||||
("common.items.armor.ferocious.shoulder",1),
|
||||
("common.items.armor.ferocious.belt",1),
|
||||
("common.items.armor.ferocious.back",1),
|
||||
("common.items.weapons.sword.bloodsteel-1",1),
|
||||
("common.items.armor.ferocious.belt",1),
|
||||
("common.items.armor.ferocious.back",1),
|
||||
("common.items.weapons.sword.bloodsteel-1",1),
|
||||
],
|
||||
"consumables": [
|
||||
("common.items.consumable.potion_minor", 100),
|
||||
@ -80,6 +80,36 @@
|
||||
("common.items.weapons.sceptre.coralline_cane", 1),
|
||||
("common.items.consumable.potion_med", 100),
|
||||
],
|
||||
"tier-2": [
|
||||
("common.items.armor.twigs.belt", 1),
|
||||
("common.items.armor.twigs.chest", 1),
|
||||
("common.items.armor.twigs.foot", 1),
|
||||
("common.items.armor.twigs.hand", 1),
|
||||
("common.items.armor.twigs.pants", 1),
|
||||
("common.items.armor.twigs.shoulder", 1),
|
||||
("common.items.weapons.sword.iron-0", 1),
|
||||
("common.items.weapons.axe.iron_axe-0", 1),
|
||||
("common.items.weapons.hammer.iron_hammer-0", 1),
|
||||
("common.items.weapons.bow.hardwood-0", 1),
|
||||
("common.items.weapons.staff.heated_arm", 1),
|
||||
("common.items.weapons.sceptre.druids_arbor", 1),
|
||||
("common.items.consumable.potion_med", 100),
|
||||
],
|
||||
"tier-1": [
|
||||
("common.items.armor.agile.belt", 1),
|
||||
("common.items.armor.agile.chest", 1),
|
||||
("common.items.armor.agile.foot", 1),
|
||||
("common.items.armor.agile.hand", 1),
|
||||
("common.items.armor.agile.pants", 1),
|
||||
("common.items.armor.agile.shoulder", 1),
|
||||
("common.items.weapons.sword.bronze-0", 1),
|
||||
("common.items.weapons.axe.bronze_axe-0", 1),
|
||||
("common.items.weapons.hammer.bronze_hammer-0", 1),
|
||||
("common.items.weapons.bow.bone-0", 1),
|
||||
("common.items.weapons.staff.bone_staff", 1),
|
||||
("common.items.weapons.sceptre.divine_gohei", 1),
|
||||
("common.items.consumable.potion_minor", 100),
|
||||
],
|
||||
"tier-0": [
|
||||
("common.items.armor.cloth_purple.belt", 1),
|
||||
("common.items.armor.cloth_purple.chest", 1),
|
||||
|
BIN
assets/voxygen/element/de_buffs/debuff_wet_0.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/de_buffs/debuff_wet_0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -32,6 +32,8 @@
|
||||
"buff.desc.crippled": "Your movement is crippled as your legs are heavily injured.",
|
||||
"buff.title.frozen": "Frozen",
|
||||
"buff.desc.frozen": "Your movements and attacks are slowed.",
|
||||
"buff.title.wet": "Wet",
|
||||
"buff.desc.wet": "The ground rejects your feet, making it hard to stop.",
|
||||
// Buffs stats
|
||||
"buff.stat.health": "Restores {str_total} Health",
|
||||
"buff.stat.increase_max_stamina": "Raises Maximum Stamina by {strength}",
|
||||
|
@ -67,6 +67,8 @@ const int BLOOD = 25;
|
||||
const int ENRAGED = 26;
|
||||
const int BIG_SHRAPNEL = 27;
|
||||
const int LASER = 28;
|
||||
const int BUBBLES = 29;
|
||||
const int WATER = 30;
|
||||
|
||||
// meters per second squared (acceleration)
|
||||
const float earth_gravity = 9.807;
|
||||
@ -181,7 +183,7 @@ void main() {
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 0.5)
|
||||
);
|
||||
break;
|
||||
case FIRE:
|
||||
case FIRE:
|
||||
f_reflect = 0.0; // Fire doesn't reflect light, it emits it
|
||||
attr = Attr(
|
||||
linear_motion(
|
||||
@ -489,6 +491,28 @@ void main() {
|
||||
spin_in_axis(perp_axis, asin(inst_dir.z / length(inst_dir)) + PI / 2.0)
|
||||
);
|
||||
break;
|
||||
case BUBBLES:
|
||||
f_reflect = 0.0; // Magic water doesn't reflect light, it emits it
|
||||
float blue_color = 1.5 + 0.2 * rand3 + 1.5 * max(floor(rand4 + 0.3), 0.0);
|
||||
float size = 8.0 * (1 - slow_start(0.1)) * slow_end(0.15);
|
||||
attr = Attr(
|
||||
(inst_dir * slow_end(1.5)) + vec3(rand0, rand1, rand2) * (percent() + 2) * 0.1,
|
||||
vec3(size),
|
||||
vec4(0.5 * blue_color, 0.75 * blue_color, blue_color, 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
|
||||
);
|
||||
break;
|
||||
case WATER:
|
||||
f_reflect = 0.0; // Magic water doesn't reflect light, it emits it
|
||||
blue_color = 1.25 + 0.2 * rand3 + 1.75 * max(floor(rand4 + 0.15), 0.0);
|
||||
size = 8.0 * (1 - slow_start(0.1)) * slow_end(0.15);
|
||||
attr = Attr(
|
||||
(inst_dir * slow_end(0.2)) + vec3(rand0, rand1, rand2) * 0.5,
|
||||
vec3(size),
|
||||
vec4(0.5 * blue_color, 0.9 * blue_color, blue_color, 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 5 + 3 * rand9)
|
||||
);
|
||||
break;
|
||||
default:
|
||||
attr = Attr(
|
||||
linear_motion(
|
||||
|
BIN
assets/voxygen/voxel/object/sea_lantern.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/object/sea_lantern.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -709,4 +709,14 @@
|
||||
central: ("object.haniwa_sentry.bone1"),
|
||||
)
|
||||
),
|
||||
SeaLantern: (
|
||||
bone0: (
|
||||
offset: (-4.5, -4.5, 0.0),
|
||||
central: ("object.sea_lantern"),
|
||||
),
|
||||
bone1: (
|
||||
offset: (0.0, 0.0, 0.0),
|
||||
central: ("armor.empty"),
|
||||
)
|
||||
),
|
||||
})
|
||||
|
@ -271,6 +271,7 @@ lazy_static! {
|
||||
BuffKind::Frenzied => "frenzied",
|
||||
BuffKind::Crippled => "crippled",
|
||||
BuffKind::Frozen => "frozen",
|
||||
BuffKind::Wet => "wet",
|
||||
};
|
||||
let mut buff_parser = HashMap::new();
|
||||
BuffKind::iter().for_each(|kind| {buff_parser.insert(string_from_buff(kind).to_string(), kind);});
|
||||
|
@ -2,7 +2,7 @@ use crate::{
|
||||
assets::{self, Asset},
|
||||
combat::{self, CombatEffect, DamageKind, Knockback},
|
||||
comp::{
|
||||
aura, beam, buff, inventory::item::tool::ToolKind, projectile::ProjectileConstructor,
|
||||
self, aura, beam, buff, inventory::item::tool::ToolKind, projectile::ProjectileConstructor,
|
||||
skills, Body, CharacterState, EnergySource, LightEmitter, StateUpdate,
|
||||
},
|
||||
states::{
|
||||
@ -65,7 +65,7 @@ pub enum CharacterAbility {
|
||||
recover_duration: f32,
|
||||
base_damage: f32,
|
||||
base_poise_damage: f32,
|
||||
knockback: f32,
|
||||
knockback: Knockback,
|
||||
range: f32,
|
||||
max_angle: f32,
|
||||
damage_effect: Option<CombatEffect>,
|
||||
@ -233,6 +233,7 @@ pub enum CharacterAbility {
|
||||
requires_ground: bool,
|
||||
move_efficiency: f32,
|
||||
damage_kind: DamageKind,
|
||||
specifier: comp::shockwave::FrontendSpecifier,
|
||||
},
|
||||
BasicBeam {
|
||||
buildup_duration: f32,
|
||||
@ -301,7 +302,10 @@ impl Default for CharacterAbility {
|
||||
recover_duration: 0.5,
|
||||
base_damage: 10.0,
|
||||
base_poise_damage: 0.0,
|
||||
knockback: 0.0,
|
||||
knockback: Knockback {
|
||||
strength: 0.0,
|
||||
direction: combat::KnockbackDir::Away,
|
||||
},
|
||||
range: 3.5,
|
||||
max_angle: 15.0,
|
||||
damage_effect: None,
|
||||
@ -1593,6 +1597,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
requires_ground,
|
||||
move_efficiency,
|
||||
damage_kind,
|
||||
specifier,
|
||||
} => CharacterState::Shockwave(shockwave::Data {
|
||||
static_data: shockwave::StaticData {
|
||||
buildup_duration: Duration::from_secs_f32(*buildup_duration),
|
||||
@ -1609,6 +1614,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
move_efficiency: *move_efficiency,
|
||||
ability_info,
|
||||
damage_kind: *damage_kind,
|
||||
specifier: *specifier,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
|
@ -53,4 +53,5 @@ pub enum FrontendSpecifier {
|
||||
HealingBeam,
|
||||
Cultist,
|
||||
ClayGolem,
|
||||
Bubbles,
|
||||
}
|
||||
|
@ -470,7 +470,7 @@ impl Body {
|
||||
biped_large::Species::Troll => 2400,
|
||||
biped_large::Species::Dullahan => 3000,
|
||||
biped_large::Species::Mindflayer => 12500,
|
||||
biped_large::Species::Tidalwarrior => 2500,
|
||||
biped_large::Species::Tidalwarrior => 16000,
|
||||
biped_large::Species::Yeti => 4000,
|
||||
biped_large::Species::Minotaur => 30000,
|
||||
biped_large::Species::Harvester => 3000,
|
||||
@ -491,6 +491,7 @@ impl Body {
|
||||
object::Body::TrainingDummy => 10000,
|
||||
object::Body::Crossbow => 800,
|
||||
object::Body::HaniwaSentry => 600,
|
||||
object::Body::SeaLantern => 1000,
|
||||
_ => 10000,
|
||||
},
|
||||
Body::Golem(golem) => match golem.species {
|
||||
@ -585,12 +586,12 @@ impl Body {
|
||||
biped_large::Species::Wendigo => 80,
|
||||
biped_large::Species::Troll => 60,
|
||||
biped_large::Species::Dullahan => 120,
|
||||
biped_large::Species::Tidalwarrior => 90,
|
||||
biped_large::Species::Yeti => 80,
|
||||
biped_large::Species::Harvester => 80,
|
||||
// Boss enemies have their health set, not adjusted by level.
|
||||
biped_large::Species::Mindflayer => 0,
|
||||
biped_large::Species::Minotaur => 0,
|
||||
biped_large::Species::Tidalwarrior => 0,
|
||||
_ => 100,
|
||||
},
|
||||
Body::BipedSmall(_) => 10,
|
||||
@ -648,6 +649,7 @@ impl Body {
|
||||
Body::BipedLarge(b) => match b.species {
|
||||
biped_large::Species::Mindflayer => 4.8,
|
||||
biped_large::Species::Minotaur => 3.2,
|
||||
biped_large::Species::Tidalwarrior => 2.25,
|
||||
_ => 1.0,
|
||||
},
|
||||
Body::Golem(g) => match g.species {
|
||||
|
@ -83,6 +83,7 @@ make_case_elim!(
|
||||
SilverOre = 68,
|
||||
ClayRocket = 69,
|
||||
HaniwaSentry = 70,
|
||||
SeaLantern = 71,
|
||||
}
|
||||
);
|
||||
|
||||
@ -93,7 +94,7 @@ impl Body {
|
||||
}
|
||||
}
|
||||
|
||||
pub const ALL_OBJECTS: [Body; 71] = [
|
||||
pub const ALL_OBJECTS: [Body; 72] = [
|
||||
Body::Arrow,
|
||||
Body::Bomb,
|
||||
Body::Scarecrow,
|
||||
@ -165,6 +166,7 @@ pub const ALL_OBJECTS: [Body; 71] = [
|
||||
Body::GoldOre,
|
||||
Body::ClayRocket,
|
||||
Body::HaniwaSentry,
|
||||
Body::SeaLantern,
|
||||
];
|
||||
|
||||
impl From<Body> for super::Body {
|
||||
@ -245,6 +247,7 @@ impl Body {
|
||||
Body::GoldOre => "gold_ore",
|
||||
Body::ClayRocket => "clay_rocket",
|
||||
Body::HaniwaSentry => "haniwa_sentry",
|
||||
Body::SeaLantern => "sea_lantern",
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,6 +339,7 @@ impl Body {
|
||||
Body::GoldOre => 1000.0,
|
||||
Body::ClayRocket => 50.0,
|
||||
Body::HaniwaSentry => 300.0,
|
||||
Body::SeaLantern => 1000.0,
|
||||
};
|
||||
|
||||
Mass(m)
|
||||
@ -349,6 +353,7 @@ impl Body {
|
||||
Body::BoltFire => Vec3::new(0.1, 0.1, 0.1),
|
||||
Body::Crossbow => Vec3::new(3.0, 3.0, 1.5),
|
||||
Body::HaniwaSentry => Vec3::new(0.8, 0.8, 1.4),
|
||||
Body::SeaLantern => Vec3::new(0.5, 0.5, 1.0),
|
||||
_ => Vec3::broadcast(0.5),
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,10 @@ pub enum BuffKind {
|
||||
/// speed, 1.0 is 33% speed. Movement speed debuff is scaled to be slightly
|
||||
/// smaller than attack speed debuff.
|
||||
Frozen,
|
||||
/// Makes you wet and causes you to have reduced friction on the ground.
|
||||
/// Strength scales the friction you ignore non-linearly. 0.5 is 50% ground
|
||||
/// friction, 1.0 is 33% ground friction.
|
||||
Wet,
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@ -88,6 +92,7 @@ impl BuffKind {
|
||||
BuffKind::Crippled => false,
|
||||
BuffKind::Frenzied => true,
|
||||
BuffKind::Frozen => false,
|
||||
BuffKind::Wet => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,6 +161,8 @@ pub enum BuffEffect {
|
||||
MovementSpeed(f32),
|
||||
/// Modifies attack speed of target
|
||||
AttackSpeed(f32),
|
||||
/// Modifies ground friction of target
|
||||
GroundFriction(f32),
|
||||
}
|
||||
|
||||
/// Actual de/buff.
|
||||
@ -316,6 +323,10 @@ impl Buff {
|
||||
],
|
||||
data.duration,
|
||||
),
|
||||
BuffKind::Wet => (
|
||||
vec![BuffEffect::GroundFriction(1.0 - nn_scaling(data.strength))],
|
||||
data.duration,
|
||||
),
|
||||
};
|
||||
Buff {
|
||||
kind,
|
||||
|
@ -308,6 +308,9 @@ pub fn default_main_tool(body: &Body) -> Option<Item> {
|
||||
object::Body::HaniwaSentry => Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_weapons.unique.haniwa_sentry",
|
||||
)),
|
||||
object::Body::SeaLantern => Some(Item::new_from_asset_expect(
|
||||
"common.items.npc_weapons.unique.tidal_totem",
|
||||
)),
|
||||
_ => None,
|
||||
},
|
||||
Body::BipedSmall(biped_small) => match (biped_small.species, biped_small.body_type) {
|
||||
|
@ -13,6 +13,7 @@ pub struct Properties {
|
||||
pub requires_ground: bool,
|
||||
pub duration: Duration,
|
||||
pub owner: Option<Uid>,
|
||||
pub specifier: FrontendSpecifier,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
@ -43,3 +44,10 @@ pub struct ShockwaveHitEntities {
|
||||
impl Component for ShockwaveHitEntities {
|
||||
type Storage = IdvStorage<Self>;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum FrontendSpecifier {
|
||||
Ground,
|
||||
Fire,
|
||||
Water,
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ pub struct Stats {
|
||||
pub max_health_modifier: f32,
|
||||
pub move_speed_modifier: f32,
|
||||
pub attack_speed_modifier: f32,
|
||||
pub friction_modifier: f32,
|
||||
}
|
||||
|
||||
impl Stats {
|
||||
@ -37,6 +38,7 @@ impl Stats {
|
||||
max_health_modifier: 1.0,
|
||||
move_speed_modifier: 1.0,
|
||||
attack_speed_modifier: 1.0,
|
||||
friction_modifier: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,6 +51,7 @@ impl Stats {
|
||||
max_health_modifier: 1.0,
|
||||
move_speed_modifier: 1.0,
|
||||
attack_speed_modifier: 1.0,
|
||||
friction_modifier: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,6 +61,7 @@ impl Stats {
|
||||
self.max_health_modifier = 1.0;
|
||||
self.move_speed_modifier = 1.0;
|
||||
self.attack_speed_modifier = 1.0;
|
||||
self.friction_modifier = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,13 @@
|
||||
use crate::{
|
||||
combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement},
|
||||
combat::{
|
||||
Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement, Damage,
|
||||
DamageKind, DamageSource, GroupTarget, Knockback,
|
||||
},
|
||||
comp::{tool::ToolKind, CharacterState, Melee, StateUpdate},
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
},
|
||||
Damage, DamageKind, DamageSource, GroupTarget, Knockback, KnockbackDir,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
@ -24,7 +26,7 @@ pub struct StaticData {
|
||||
/// Base poise reduction
|
||||
pub base_poise_damage: f32,
|
||||
/// Knockback
|
||||
pub knockback: f32,
|
||||
pub knockback: Knockback,
|
||||
/// Max range
|
||||
pub range: f32,
|
||||
/// Max angle (45.0 will give you a 90.0 angle window)
|
||||
@ -91,10 +93,7 @@ impl CharacterBehavior for Data {
|
||||
.with_requirement(CombatRequirement::AnyDamage);
|
||||
let knockback = AttackEffect::new(
|
||||
Some(GroupTarget::OutOfGroup),
|
||||
CombatEffect::Knockback(Knockback {
|
||||
strength: self.static_data.knockback,
|
||||
direction: KnockbackDir::Away,
|
||||
}),
|
||||
CombatEffect::Knockback(self.static_data.knockback),
|
||||
)
|
||||
.with_requirement(CombatRequirement::AnyDamage);
|
||||
let energy = AttackEffect::new(None, CombatEffect::EnergyReward(50.0))
|
||||
|
@ -235,14 +235,9 @@ impl CharacterBehavior for Data {
|
||||
handle_orientation(data, &mut update, 0.4 * self.static_data.ori_modifier);
|
||||
|
||||
// Forward movement
|
||||
handle_forced_movement(
|
||||
data,
|
||||
&mut update,
|
||||
ForcedMovement::Forward {
|
||||
strength: self.static_data.stage_data[stage_index].forward_movement,
|
||||
},
|
||||
0.3,
|
||||
);
|
||||
handle_forced_movement(data, &mut update, ForcedMovement::Forward {
|
||||
strength: self.static_data.stage_data[stage_index].forward_movement,
|
||||
});
|
||||
|
||||
// Swings
|
||||
update.character = CharacterState::ComboMelee(Data {
|
||||
|
@ -106,14 +106,9 @@ impl CharacterBehavior for Data {
|
||||
.min(1.0);
|
||||
|
||||
handle_orientation(data, &mut update, 0.6);
|
||||
handle_forced_movement(
|
||||
data,
|
||||
&mut update,
|
||||
ForcedMovement::Forward {
|
||||
strength: self.static_data.forward_speed * charge_frac.sqrt(),
|
||||
},
|
||||
0.1,
|
||||
);
|
||||
handle_forced_movement(data, &mut update, ForcedMovement::Forward {
|
||||
strength: self.static_data.forward_speed * charge_frac.sqrt(),
|
||||
});
|
||||
|
||||
// This logic basically just decides if a charge should end, and prevents the
|
||||
// character state spamming attacks while checking if it has hit something
|
||||
|
@ -86,17 +86,12 @@ impl CharacterBehavior for Data {
|
||||
let progress = 1.0
|
||||
- self.timer.as_secs_f32()
|
||||
/ self.static_data.movement_duration.as_secs_f32();
|
||||
handle_forced_movement(
|
||||
data,
|
||||
&mut update,
|
||||
ForcedMovement::Leap {
|
||||
vertical: self.static_data.vertical_leap_strength,
|
||||
forward: self.static_data.forward_leap_strength,
|
||||
progress,
|
||||
direction: MovementDirection::Look,
|
||||
},
|
||||
0.15,
|
||||
);
|
||||
handle_forced_movement(data, &mut update, ForcedMovement::Leap {
|
||||
vertical: self.static_data.vertical_leap_strength,
|
||||
forward: self.static_data.forward_leap_strength,
|
||||
progress,
|
||||
direction: MovementDirection::Look,
|
||||
});
|
||||
|
||||
// Increment duration
|
||||
// If we were to set a timeout for state, this would be
|
||||
|
@ -78,19 +78,14 @@ impl CharacterBehavior for Data {
|
||||
},
|
||||
StageSection::Movement => {
|
||||
// Update velocity
|
||||
handle_forced_movement(
|
||||
data,
|
||||
&mut update,
|
||||
ForcedMovement::Forward {
|
||||
strength: self.static_data.roll_strength
|
||||
* ((1.0
|
||||
- self.timer.as_secs_f32()
|
||||
/ self.static_data.movement_duration.as_secs_f32())
|
||||
/ 2.0
|
||||
+ 0.5),
|
||||
},
|
||||
0.0,
|
||||
);
|
||||
handle_forced_movement(data, &mut update, ForcedMovement::Forward {
|
||||
strength: self.static_data.roll_strength
|
||||
* ((1.0
|
||||
- self.timer.as_secs_f32()
|
||||
/ self.static_data.movement_duration.as_secs_f32())
|
||||
/ 2.0
|
||||
+ 0.5),
|
||||
});
|
||||
|
||||
if self.timer < self.static_data.movement_duration {
|
||||
// Movement
|
||||
|
@ -44,6 +44,8 @@ pub struct StaticData {
|
||||
pub ability_info: AbilityInfo,
|
||||
/// What kind of damage the attack does
|
||||
pub damage_kind: DamageKind,
|
||||
/// Used to specify the shockwave to the frontend
|
||||
pub specifier: shockwave::FrontendSpecifier,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@ -108,6 +110,7 @@ impl CharacterBehavior for Data {
|
||||
attack,
|
||||
requires_ground: self.static_data.requires_ground,
|
||||
owner: Some(*data.uid),
|
||||
specifier: self.static_data.specifier,
|
||||
};
|
||||
update.server_events.push_front(ServerEvent::Shockwave {
|
||||
properties,
|
||||
|
@ -167,14 +167,9 @@ impl CharacterBehavior for Data {
|
||||
self.static_data.movement_behavior,
|
||||
MovementBehavior::ForwardGround
|
||||
) {
|
||||
handle_forced_movement(
|
||||
data,
|
||||
&mut update,
|
||||
ForcedMovement::Forward {
|
||||
strength: self.static_data.forward_speed,
|
||||
},
|
||||
0.1,
|
||||
);
|
||||
handle_forced_movement(data, &mut update, ForcedMovement::Forward {
|
||||
strength: self.static_data.forward_speed,
|
||||
});
|
||||
}
|
||||
|
||||
// Swings
|
||||
|
@ -243,7 +243,7 @@ pub fn handle_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
||||
/// Updates components to move player as if theyre on ground or in air
|
||||
#[allow(clippy::assign_op_pattern)] // TODO: Pending review in #587
|
||||
fn basic_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
||||
let efficiency = efficiency * data.stats.move_speed_modifier;
|
||||
let efficiency = efficiency * data.stats.move_speed_modifier * data.stats.friction_modifier;
|
||||
|
||||
let accel = if data.physics.on_ground {
|
||||
data.body.base_accel()
|
||||
@ -263,20 +263,15 @@ fn basic_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
||||
}
|
||||
|
||||
/// Handles forced movement
|
||||
pub fn handle_forced_movement(
|
||||
data: &JoinData,
|
||||
update: &mut StateUpdate,
|
||||
movement: ForcedMovement,
|
||||
efficiency: f32,
|
||||
) {
|
||||
let efficiency = efficiency * data.stats.move_speed_modifier;
|
||||
|
||||
pub fn handle_forced_movement(data: &JoinData, update: &mut StateUpdate, movement: ForcedMovement) {
|
||||
match movement {
|
||||
ForcedMovement::Forward { strength } => {
|
||||
let strength = strength * data.stats.move_speed_modifier * data.stats.friction_modifier;
|
||||
if let Some(accel) = data.physics.on_ground.then_some(data.body.base_accel()) {
|
||||
update.vel.0 += Vec2::broadcast(data.dt.0)
|
||||
* accel
|
||||
* (data.inputs.move_dir * efficiency + Vec2::from(update.ori) * strength);
|
||||
* (data.inputs.move_dir + Vec2::from(update.ori))
|
||||
* strength;
|
||||
}
|
||||
},
|
||||
ForcedMovement::Leap {
|
||||
@ -328,7 +323,7 @@ pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, efficiency:
|
||||
|
||||
/// Updates components to move player as if theyre swimming
|
||||
fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32, submersion: f32) -> bool {
|
||||
let efficiency = efficiency * data.stats.move_speed_modifier;
|
||||
let efficiency = efficiency * data.stats.move_speed_modifier * data.stats.friction_modifier;
|
||||
if let Some(force) = data.body.swim_thrust() {
|
||||
let force = efficiency * force;
|
||||
let mut water_accel = force / data.mass.0;
|
||||
@ -366,7 +361,7 @@ fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32, submers
|
||||
|
||||
/// Updates components to move entity as if it's flying
|
||||
pub fn fly_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) -> bool {
|
||||
let efficiency = efficiency * data.stats.move_speed_modifier;
|
||||
let efficiency = efficiency * data.stats.move_speed_modifier * data.stats.friction_modifier;
|
||||
|
||||
let glider = match data.character {
|
||||
CharacterState::Glide(data) => Some(data),
|
||||
|
@ -214,6 +214,9 @@ impl<'a> System<'a> for Sys {
|
||||
BuffEffect::AttackSpeed(speed) => {
|
||||
stat.attack_speed_modifier *= *speed;
|
||||
},
|
||||
BuffEffect::GroundFriction(gf) => {
|
||||
stat.friction_modifier *= *gf;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use common::{
|
||||
body::ship::figuredata::{VoxelCollider, VOXEL_COLLIDER_MANIFEST},
|
||||
fluid_dynamics::{Fluid, Wings},
|
||||
BeamSegment, Body, CharacterState, Collider, Density, Mass, Mounting, Ori, PhysicsState,
|
||||
Pos, PosVelDefer, PreviousPhysCache, Projectile, Scale, Shockwave, Sticky, Vel,
|
||||
Pos, PosVelDefer, PreviousPhysCache, Projectile, Scale, Shockwave, Stats, Sticky, Vel,
|
||||
},
|
||||
consts::{AIR_DENSITY, FRIC_GROUND, GRAVITY},
|
||||
event::{EventBus, ServerEvent},
|
||||
@ -124,6 +124,7 @@ pub struct PhysicsRead<'a> {
|
||||
bodies: ReadStorage<'a, Body>,
|
||||
character_states: ReadStorage<'a, CharacterState>,
|
||||
densities: ReadStorage<'a, Density>,
|
||||
stats: ReadStorage<'a, Stats>,
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
@ -779,6 +780,7 @@ impl<'a> PhysicsData<'a> {
|
||||
block_snap,
|
||||
climbing,
|
||||
|entity, vel| land_on_ground = Some((entity, vel)),
|
||||
read,
|
||||
);
|
||||
tgt_pos = cpos.0;
|
||||
},
|
||||
@ -808,6 +810,7 @@ impl<'a> PhysicsData<'a> {
|
||||
block_snap,
|
||||
climbing,
|
||||
|entity, vel| land_on_ground = Some((entity, vel)),
|
||||
read,
|
||||
);
|
||||
|
||||
// Sticky things shouldn't move when on a surface
|
||||
@ -1048,6 +1051,7 @@ impl<'a> PhysicsData<'a> {
|
||||
land_on_ground =
|
||||
Some((entity, Vel(ori_from.mul_direction(vel.0))));
|
||||
},
|
||||
read,
|
||||
);
|
||||
|
||||
cpos.0 = transform_from.mul_point(cpos.0) + wpos;
|
||||
@ -1242,6 +1246,7 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
block_snap: bool,
|
||||
climbing: bool,
|
||||
mut land_on_ground: impl FnMut(Entity, Vel),
|
||||
read: &PhysicsRead,
|
||||
) {
|
||||
let (radius, z_min, z_max) = cylinder;
|
||||
|
||||
@ -1567,8 +1572,9 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
}
|
||||
}
|
||||
physics_state.on_wall = on_wall;
|
||||
let fric_mod = read.stats.get(entity).map_or(1.0, |s| s.friction_modifier);
|
||||
if physics_state.on_ground || (physics_state.on_wall.is_some() && climbing) {
|
||||
vel.0 *= (1.0 - FRIC_GROUND.min(1.0)).powf(dt.0 * 60.0);
|
||||
vel.0 *= (1.0 - FRIC_GROUND.min(1.0) * fric_mod).powf(dt.0 * 60.0);
|
||||
physics_state.ground_vel = ground_vel;
|
||||
}
|
||||
|
||||
|
@ -87,6 +87,10 @@ struct AttackData {
|
||||
angle: f32,
|
||||
}
|
||||
|
||||
impl AttackData {
|
||||
fn in_min_range(&self) -> bool { self.dist_sqrd < self.min_attack_dist.powi(2) }
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub enum Tactic {
|
||||
Melee,
|
||||
@ -108,11 +112,13 @@ pub enum Tactic {
|
||||
Turret,
|
||||
FixedTurret,
|
||||
RotatingTurret,
|
||||
RadialTurret,
|
||||
Mindflayer,
|
||||
BirdLargeBreathe,
|
||||
BirdLargeFire,
|
||||
Minotaur,
|
||||
ClayGolem,
|
||||
TidalWarrior,
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
@ -1601,6 +1607,8 @@ impl<'a> AgentData<'a> {
|
||||
"Mindflayer" => Tactic::Mindflayer,
|
||||
"Minotaur" => Tactic::Minotaur,
|
||||
"Clay Golem" => Tactic::ClayGolem,
|
||||
"Tidal Warrior" => Tactic::TidalWarrior,
|
||||
"Tidal Totem" => Tactic::RadialTurret,
|
||||
_ => Tactic::Melee,
|
||||
},
|
||||
AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind),
|
||||
@ -1843,6 +1851,20 @@ impl<'a> AgentData<'a> {
|
||||
&tgt_data,
|
||||
&read_data,
|
||||
),
|
||||
Tactic::TidalWarrior => self.handle_tidal_warrior_attack(
|
||||
agent,
|
||||
controller,
|
||||
&attack_data,
|
||||
&tgt_data,
|
||||
&read_data,
|
||||
),
|
||||
Tactic::RadialTurret => self.handle_radial_turret_attack(
|
||||
agent,
|
||||
controller,
|
||||
&attack_data,
|
||||
&tgt_data,
|
||||
&read_data,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1854,7 +1876,7 @@ impl<'a> AgentData<'a> {
|
||||
tgt_data: &TargetData,
|
||||
read_data: &ReadData,
|
||||
) {
|
||||
if attack_data.dist_sqrd < attack_data.min_attack_dist.powi(2) && attack_data.angle < 45.0 {
|
||||
if attack_data.in_min_range() && attack_data.angle < 45.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
@ -1883,7 +1905,7 @@ impl<'a> AgentData<'a> {
|
||||
tgt_data: &TargetData,
|
||||
read_data: &ReadData,
|
||||
) {
|
||||
if attack_data.dist_sqrd < attack_data.min_attack_dist.powi(2) && attack_data.angle < 45.0 {
|
||||
if attack_data.in_min_range() && attack_data.angle < 45.0 {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
if agent.action_state.timer > 6.0 {
|
||||
controller
|
||||
@ -1932,7 +1954,7 @@ impl<'a> AgentData<'a> {
|
||||
tgt_data: &TargetData,
|
||||
read_data: &ReadData,
|
||||
) {
|
||||
if attack_data.dist_sqrd < attack_data.min_attack_dist.powi(2) && attack_data.angle < 45.0 {
|
||||
if attack_data.in_min_range() && attack_data.angle < 45.0 {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
if agent.action_state.timer > 4.0 {
|
||||
controller
|
||||
@ -2004,7 +2026,7 @@ impl<'a> AgentData<'a> {
|
||||
tgt_data: &TargetData,
|
||||
read_data: &ReadData,
|
||||
) {
|
||||
if attack_data.dist_sqrd < attack_data.min_attack_dist.powi(2) && attack_data.angle < 45.0 {
|
||||
if attack_data.in_min_range() && attack_data.angle < 45.0 {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
if self
|
||||
.skill_set
|
||||
@ -2226,9 +2248,7 @@ impl<'a> AgentData<'a> {
|
||||
tgt_data: &TargetData,
|
||||
read_data: &ReadData,
|
||||
) {
|
||||
if self.body.map(|b| b.is_humanoid()).unwrap_or(false)
|
||||
&& attack_data.dist_sqrd < attack_data.min_attack_dist.powi(2)
|
||||
{
|
||||
if self.body.map(|b| b.is_humanoid()).unwrap_or(false) && attack_data.in_min_range() {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Roll));
|
||||
@ -2325,7 +2345,7 @@ impl<'a> AgentData<'a> {
|
||||
tgt_data: &TargetData,
|
||||
read_data: &ReadData,
|
||||
) {
|
||||
if attack_data.dist_sqrd < attack_data.min_attack_dist.powi(2) && attack_data.angle < 90.0 {
|
||||
if attack_data.in_min_range() && attack_data.angle < 90.0 {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
controller
|
||||
.actions
|
||||
@ -2371,8 +2391,7 @@ impl<'a> AgentData<'a> {
|
||||
radius: u32,
|
||||
circle_time: u32,
|
||||
) {
|
||||
if attack_data.dist_sqrd < attack_data.min_attack_dist.powi(2) && thread_rng().gen_bool(0.5)
|
||||
{
|
||||
if attack_data.in_min_range() && thread_rng().gen_bool(0.5) {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
controller
|
||||
.actions
|
||||
@ -2679,7 +2698,7 @@ impl<'a> AgentData<'a> {
|
||||
tgt_data: &TargetData,
|
||||
read_data: &ReadData,
|
||||
) {
|
||||
if attack_data.angle < 90.0 && attack_data.dist_sqrd < attack_data.min_attack_dist.powi(2) {
|
||||
if attack_data.angle < 90.0 && attack_data.in_min_range() {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
if agent.action_state.timer < 2.0 {
|
||||
controller
|
||||
@ -2762,7 +2781,7 @@ impl<'a> AgentData<'a> {
|
||||
tgt_data: &TargetData,
|
||||
read_data: &ReadData,
|
||||
) {
|
||||
if attack_data.angle < 90.0 && attack_data.dist_sqrd < attack_data.min_attack_dist.powi(2) {
|
||||
if attack_data.angle < 90.0 && attack_data.in_min_range() {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
controller
|
||||
.actions
|
||||
@ -2850,6 +2869,26 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_radial_turret_attack(
|
||||
&self,
|
||||
_agent: &mut Agent,
|
||||
controller: &mut Controller,
|
||||
attack_data: &AttackData,
|
||||
tgt_data: &TargetData,
|
||||
read_data: &ReadData,
|
||||
) {
|
||||
if can_see_tgt(
|
||||
&*read_data.terrain,
|
||||
self.pos,
|
||||
tgt_data.pos,
|
||||
attack_data.dist_sqrd,
|
||||
) {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_mindflayer_attack(
|
||||
&self,
|
||||
agent: &mut Agent,
|
||||
@ -3190,7 +3229,7 @@ impl<'a> AgentData<'a> {
|
||||
agent.action_state.timer += read_data.dt.0;
|
||||
} else if agent.action_state.timer < 6.0
|
||||
&& attack_data.angle < 90.0
|
||||
&& attack_data.dist_sqrd < attack_data.min_attack_dist.powi(2)
|
||||
&& attack_data.in_min_range()
|
||||
{
|
||||
// Triplestrike
|
||||
controller
|
||||
@ -3355,6 +3394,88 @@ impl<'a> AgentData<'a> {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, true, None);
|
||||
}
|
||||
|
||||
fn handle_tidal_warrior_attack(
|
||||
&self,
|
||||
agent: &mut Agent,
|
||||
controller: &mut Controller,
|
||||
attack_data: &AttackData,
|
||||
tgt_data: &TargetData,
|
||||
read_data: &ReadData,
|
||||
) {
|
||||
const SCUTTLE_RANGE: f32 = 40.0;
|
||||
const BUBBLE_RANGE: f32 = 20.0;
|
||||
const MINION_SUMMON_THRESHOLD: f32 = 0.20;
|
||||
let health_fraction = self.health.map_or(0.5, |h| h.fraction());
|
||||
// Sets counter at start of combat, using `condition` to keep track of whether
|
||||
// it was already intitialized
|
||||
if !agent.action_state.condition {
|
||||
agent.action_state.counter = 1.0 - MINION_SUMMON_THRESHOLD;
|
||||
agent.action_state.condition = true;
|
||||
}
|
||||
|
||||
if agent.action_state.counter > health_fraction {
|
||||
// Summon minions at particular thresholds of health
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(1)));
|
||||
|
||||
if matches!(self.char_state, CharacterState::BasicSummon(c) if matches!(c.stage_section, StageSection::Recover))
|
||||
{
|
||||
agent.action_state.counter -= MINION_SUMMON_THRESHOLD;
|
||||
}
|
||||
} else if attack_data.dist_sqrd < SCUTTLE_RANGE.powi(2) {
|
||||
if matches!(self.char_state, CharacterState::DashMelee(c) if !matches!(c.stage_section, StageSection::Recover))
|
||||
{
|
||||
// Keep scuttling if already in dash melee and not in recover
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
} else if attack_data.dist_sqrd < BUBBLE_RANGE.powi(2) {
|
||||
if matches!(self.char_state, CharacterState::BasicBeam(c) if !matches!(c.stage_section, StageSection::Recover) && c.timer < Duration::from_secs(10))
|
||||
{
|
||||
// Keep shooting bubbles at them if already in basic beam and not in recover and
|
||||
// have not been bubbling too long
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
} else if attack_data.in_min_range() && attack_data.angle < 60.0 {
|
||||
// Pincer them if they're in range and angle
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
} else if attack_data.angle < 30.0
|
||||
&& can_see_tgt(
|
||||
&*read_data.terrain,
|
||||
self.pos,
|
||||
tgt_data.pos,
|
||||
attack_data.dist_sqrd,
|
||||
)
|
||||
{
|
||||
// Start bubbling them if not close enough to do something else and in angle and
|
||||
// can see target
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
}
|
||||
} else if attack_data.angle < 90.0
|
||||
&& can_see_tgt(
|
||||
&*read_data.terrain,
|
||||
self.pos,
|
||||
tgt_data.pos,
|
||||
attack_data.dist_sqrd,
|
||||
)
|
||||
{
|
||||
// Start scuttling if not close enough to do something else and in angle and can
|
||||
// see target
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
}
|
||||
}
|
||||
// Always attempt to path towards target
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
}
|
||||
|
||||
fn follow(
|
||||
&self,
|
||||
agent: &mut Agent,
|
||||
|
@ -19,6 +19,7 @@ impl Animation for AlphaAnimation {
|
||||
f32,
|
||||
Option<StageSection>,
|
||||
f32,
|
||||
f32,
|
||||
);
|
||||
type Skeleton = BipedLargeSkeleton;
|
||||
|
||||
@ -33,9 +34,10 @@ impl Animation for AlphaAnimation {
|
||||
(active_tool_kind, active_tool_spec),
|
||||
_second_tool,
|
||||
velocity,
|
||||
_global_time,
|
||||
global_time,
|
||||
stage_section,
|
||||
acc_vel,
|
||||
timer,
|
||||
): Self::Dependency<'a>,
|
||||
anim_time: f32,
|
||||
rate: &mut f32,
|
||||
@ -63,6 +65,9 @@ impl Animation for AlphaAnimation {
|
||||
let pullback = 1.0 - move3;
|
||||
let move1 = move1base * pullback;
|
||||
let move2 = move2base * pullback;
|
||||
let subtract = global_time - timer;
|
||||
let check = subtract - subtract.trunc();
|
||||
let mirror = (check - 0.5).signum();
|
||||
next.second.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.second.orientation = Quaternion::rotation_x(0.0);
|
||||
next.shoulder_l.position = Vec3::new(
|
||||
@ -196,40 +201,69 @@ impl Animation for AlphaAnimation {
|
||||
* Quaternion::rotation_z(move1 * -0.5 + move2 * 0.6);
|
||||
next.head.orientation = Quaternion::rotation_x(move1 * 0.3);
|
||||
},
|
||||
"Tidal Claws" => {
|
||||
next.torso.position = Vec3::new(0.0, 0.0, move1 * -0.3);
|
||||
next.upper_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * -0.5 + move2 * -0.4);
|
||||
next.lower_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.5 + move2 * 0.4);
|
||||
"Tidal Warrior" => {
|
||||
if mirror > 0.0 {
|
||||
next.head.orientation = Quaternion::rotation_z(move1 * 0.75);
|
||||
next.upper_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.2 + move2 * 0.7)
|
||||
* Quaternion::rotation_z(move1 * -1.0 + move2 * 1.3);
|
||||
next.lower_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.2 + move2 * -0.7)
|
||||
* Quaternion::rotation_y(move1 * -0.5 + move2 * 0.7)
|
||||
* Quaternion::rotation_z(move1 * 1.0 + move2 * -1.2);
|
||||
|
||||
next.shoulder_l.orientation = Quaternion::rotation_x(
|
||||
move1 * 0.4 + 0.4 * speednorm + (footrotl * -0.2) * speednorm,
|
||||
);
|
||||
next.shoulder_r.orientation = Quaternion::rotation_x(
|
||||
move1 * 0.4 + 0.4 * speednorm + (footrotl * -0.2) * speednorm,
|
||||
);
|
||||
next.shoulder_l.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.3 + move2 * 0.8)
|
||||
* Quaternion::rotation_y(move1 * -0.3 + move2 * -0.5);
|
||||
next.hand_l.position = Vec3::new(
|
||||
-14.0 + move1 * -2.0 + move2 * 4.0,
|
||||
2.0 + move2 * 4.0,
|
||||
-4.0 + move2 * 3.0,
|
||||
);
|
||||
|
||||
next.control_l.position = Vec3::new(
|
||||
-14.0 + move2 * 9.0,
|
||||
12.0 + move1 * 6.0,
|
||||
-12.0 + move1 * 9.0,
|
||||
);
|
||||
next.control_r.position = Vec3::new(
|
||||
14.0 + move2 * -9.0,
|
||||
12.0 + move1 * 6.0,
|
||||
-12.0 + move1 * 9.0,
|
||||
);
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0 + move2 * 1.5)
|
||||
* Quaternion::rotation_y(move2 * 0.5)
|
||||
* Quaternion::rotation_z(
|
||||
-0.35 + move1 * -0.5 + move2 * 1.0,
|
||||
);
|
||||
next.hand_r.position = Vec3::new(14.0, 2.0, -4.0);
|
||||
|
||||
next.control_l.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0 + move1 * 0.5)
|
||||
* Quaternion::rotation_y(-0.15)
|
||||
* Quaternion::rotation_z(move1 * 0.5 + move2 * -0.6);
|
||||
next.control_r.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0 + move1 * 0.5)
|
||||
* Quaternion::rotation_y(0.15)
|
||||
* Quaternion::rotation_z(move1 * -0.5 + move2 * 0.6);
|
||||
next.head.orientation = Quaternion::rotation_x(move1 * 0.3);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0) * Quaternion::rotation_z(0.35);
|
||||
|
||||
next.shoulder_r.orientation = Quaternion::rotation_x(0.0);
|
||||
} else {
|
||||
next.head.orientation = Quaternion::rotation_z(move1 * -0.75);
|
||||
next.upper_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.2 + move2 * 0.7)
|
||||
* Quaternion::rotation_z(move1 * 1.0 + move2 * -1.3);
|
||||
next.lower_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.2 + move2 * -0.7)
|
||||
* Quaternion::rotation_y(move1 * 0.5 + move2 * -0.7)
|
||||
* Quaternion::rotation_z(move1 * -1.0 + move2 * 1.2);
|
||||
|
||||
next.shoulder_r.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.3 + move2 * 0.8)
|
||||
* Quaternion::rotation_y(move1 * 0.3 + move2 * 0.5);
|
||||
next.hand_r.position = Vec3::new(
|
||||
14.0 + move1 * 2.0 + move2 * -4.0,
|
||||
2.0 + move2 * 4.0,
|
||||
-4.0 + move2 * 3.0,
|
||||
);
|
||||
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0 + move2 * 1.5)
|
||||
* Quaternion::rotation_y(move2 * -0.5)
|
||||
* Quaternion::rotation_z(0.35 + move1 * 0.5 + move2 * -1.0);
|
||||
next.hand_l.position = Vec3::new(-14.0, 2.0, -4.0);
|
||||
|
||||
next.hand_l.orientation = Quaternion::rotation_x(PI / 3.0)
|
||||
* Quaternion::rotation_z(-0.35);
|
||||
|
||||
next.shoulder_l.orientation = Quaternion::rotation_x(0.0);
|
||||
};
|
||||
next.torso.position = Vec3::new(0.0, move2 * -2.2, move2 * -1.0);
|
||||
},
|
||||
"Minotaur" => {
|
||||
next.control_l.position = Vec3::new(0.0, 4.0, 5.0);
|
||||
|
@ -2,19 +2,24 @@ use super::{
|
||||
super::{vek::*, Animation},
|
||||
BipedLargeSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::{comp::item::ToolKind, states::utils::StageSection};
|
||||
use common::{
|
||||
comp::item::{AbilitySpec, ToolKind},
|
||||
states::utils::StageSection,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct BeamAnimation;
|
||||
|
||||
impl Animation for BeamAnimation {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type Dependency<'a> = (
|
||||
Option<ToolKind>,
|
||||
Option<ToolKind>,
|
||||
(Option<ToolKind>, Option<&'a AbilitySpec>),
|
||||
(Option<ToolKind>, Option<&'a AbilitySpec>),
|
||||
f32,
|
||||
Vec3<f32>,
|
||||
Option<StageSection>,
|
||||
f32,
|
||||
f32,
|
||||
);
|
||||
type Skeleton = BipedLargeSkeleton;
|
||||
|
||||
@ -25,7 +30,15 @@ impl Animation for BeamAnimation {
|
||||
#[allow(clippy::single_match)] // TODO: Pending review in #587
|
||||
fn update_skeleton_inner<'a>(
|
||||
skeleton: &Self::Skeleton,
|
||||
(active_tool_kind, _second_tool_kind, _global_time, velocity, stage_section, acc_vel): Self::Dependency<'a>,
|
||||
(
|
||||
(active_tool_kind, active_tool_spec),
|
||||
_second_tool_kind,
|
||||
global_time,
|
||||
velocity,
|
||||
stage_section,
|
||||
acc_vel,
|
||||
timer,
|
||||
): Self::Dependency<'a>,
|
||||
anim_time: f32,
|
||||
rate: &mut f32,
|
||||
s_a: &SkeletonAttr,
|
||||
@ -46,7 +59,9 @@ impl Animation for BeamAnimation {
|
||||
let footrotr = ((1.0 / (0.5 + (0.5) * ((acc_vel * lab + PI * 0.4).sin()).powi(2))).sqrt())
|
||||
* ((acc_vel * lab + PI * 0.4).sin())
|
||||
* speednorm;
|
||||
|
||||
let subtract = global_time - timer;
|
||||
let check = subtract - subtract.trunc();
|
||||
let mirror = (check - 0.5).signum();
|
||||
next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1);
|
||||
next.jaw.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
@ -58,26 +73,26 @@ impl Animation for BeamAnimation {
|
||||
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
let (move1base, move2shake, _move2base, move3) = match stage_section {
|
||||
Some(StageSection::Buildup) => (
|
||||
(anim_time.powf(0.25)).min(1.0),
|
||||
(anim_time * 15.0 + PI).sin(),
|
||||
(anim_time * 10.0 + PI).sin(),
|
||||
0.0,
|
||||
),
|
||||
Some(StageSection::Cast) => (
|
||||
1.0,
|
||||
(anim_time * 15.0 + PI).sin(),
|
||||
anim_time.powf(0.25),
|
||||
0.0,
|
||||
),
|
||||
Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time),
|
||||
_ => (0.0, 0.0, 0.0, 0.0),
|
||||
};
|
||||
let pullback = 1.0 - move3;
|
||||
let move1 = move1base * pullback;
|
||||
match active_tool_kind {
|
||||
Some(ToolKind::Sceptre) | Some(ToolKind::Staff) => {
|
||||
let (move1base, move2shake, _move2base, move3) = match stage_section {
|
||||
Some(StageSection::Buildup) => (
|
||||
(anim_time.powf(0.25)).min(1.0),
|
||||
(anim_time * 15.0 + PI).sin(),
|
||||
(anim_time * 10.0 + PI).sin(),
|
||||
0.0,
|
||||
),
|
||||
Some(StageSection::Cast) => (
|
||||
1.0,
|
||||
(anim_time * 15.0 + PI).sin(),
|
||||
anim_time.powf(0.25),
|
||||
0.0,
|
||||
),
|
||||
Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time),
|
||||
_ => (0.0, 0.0, 0.0, 0.0),
|
||||
};
|
||||
let pullback = 1.0 - move3;
|
||||
let move1 = move1base * pullback;
|
||||
next.control_l.position = Vec3::new(-1.0, 3.0, 12.0);
|
||||
next.control_r.position =
|
||||
Vec3::new(1.0 + move1 * 5.0, 2.0 + move1 * 1.0, 2.0 + move1 * 14.0);
|
||||
@ -116,6 +131,56 @@ impl Animation for BeamAnimation {
|
||||
next.torso.orientation = Quaternion::rotation_x(move1 * -0.1);
|
||||
next.torso.position = Vec3::new(0.0, 0.0, move1 * 1.0);
|
||||
},
|
||||
Some(ToolKind::Natural) => {
|
||||
if let Some(AbilitySpec::Custom(spec)) = active_tool_spec {
|
||||
match spec.as_str() {
|
||||
"Tidal Warrior" => {
|
||||
if mirror > 0.0 {
|
||||
next.head.orientation = Quaternion::rotation_z(move1 * -0.6);
|
||||
next.upper_torso.orientation = Quaternion::rotation_z(move1 * 0.6);
|
||||
next.lower_torso.orientation = Quaternion::rotation_z(move1 * -0.6);
|
||||
|
||||
next.shoulder_l.orientation = Quaternion::rotation_z(move1 * 0.3);
|
||||
next.hand_l.position = Vec3::new(-14.0 + move1 * 3.0, 2.0, -4.0);
|
||||
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0 + move2shake * -0.07)
|
||||
* Quaternion::rotation_y(move1 * -0.5)
|
||||
* Quaternion::rotation_z(-0.35 + move2shake * 0.07);
|
||||
next.hand_r.position = Vec3::new(14.0 + move1 - 3.0, 2.0, -4.0);
|
||||
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0 + move2shake * 0.07)
|
||||
* Quaternion::rotation_y(move1 * -0.5)
|
||||
* Quaternion::rotation_z(0.35 - move2shake * 0.07);
|
||||
|
||||
next.shoulder_r.orientation = Quaternion::rotation_z(move1 * -0.3);
|
||||
} else {
|
||||
next.head.orientation = Quaternion::rotation_z(move1 * 0.6);
|
||||
next.upper_torso.orientation = Quaternion::rotation_z(move1 * -0.6);
|
||||
next.lower_torso.orientation = Quaternion::rotation_z(move1 * 0.6);
|
||||
|
||||
next.shoulder_l.orientation = Quaternion::rotation_z(move1 * -0.3);
|
||||
next.hand_l.position = Vec3::new(-14.0 + move1 * 3.0, 2.0, -4.0);
|
||||
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0 + move2shake * 0.07)
|
||||
* Quaternion::rotation_y(move1 * 0.5)
|
||||
* Quaternion::rotation_z(-0.35 + move2shake * 0.07);
|
||||
next.hand_r.position = Vec3::new(14.0 + move1 - 3.0, 2.0, -4.0);
|
||||
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0 + move2shake * -0.07)
|
||||
* Quaternion::rotation_y(move1 * 0.5)
|
||||
* Quaternion::rotation_z(0.35 - move2shake * -0.07);
|
||||
|
||||
next.shoulder_r.orientation = Quaternion::rotation_z(move1 * 0.3);
|
||||
};
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ impl Animation for DashAnimation {
|
||||
let lab: f32 = 0.65 * s_a.tempo;
|
||||
let speed = Vec2::<f32>::from(velocity).magnitude();
|
||||
|
||||
let speednorm = (speed / 12.0).powf(0.4);
|
||||
let speednorm = (speed.min(16.0) / 12.0).powf(0.4);
|
||||
let foothoril = (acc_vel * lab + PI * 1.45).sin() * speednorm;
|
||||
let foothorir = (acc_vel * lab + PI * (0.45)).sin() * speednorm;
|
||||
let footrotl = ((1.0 / (0.5 + (0.5) * ((acc_vel * lab + PI * 1.4).sin()).powi(2))).sqrt())
|
||||
@ -64,12 +64,18 @@ impl Animation for DashAnimation {
|
||||
next.second.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
let (move1base, move2base, move3base, move4) = match stage_section {
|
||||
Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0, 0.0),
|
||||
Some(StageSection::Charge) => (1.0, (anim_time.powf(4.0)).min(1.0), 0.0, 0.0),
|
||||
Some(StageSection::Swing) => (1.0, 1.0, anim_time.powf(4.0), 0.0),
|
||||
Some(StageSection::Recover) => (1.1, 1.0, 1.0, anim_time.powf(4.0)),
|
||||
_ => (0.0, 0.0, 0.0, 0.0),
|
||||
let (move1base, motion, move2base, move3base, move4) = match stage_section {
|
||||
Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0, 0.0, 0.0),
|
||||
Some(StageSection::Charge) => (
|
||||
1.0,
|
||||
(acc_vel * lab).sin(),
|
||||
(anim_time.powf(4.0)).min(1.0),
|
||||
0.0,
|
||||
0.0,
|
||||
),
|
||||
Some(StageSection::Swing) => (1.0, 1.0, 1.0, anim_time.powf(4.0), 0.0),
|
||||
Some(StageSection::Recover) => (1.1, 1.0, 1.0, 1.0, anim_time.powf(4.0)),
|
||||
_ => (0.0, 0.0, 0.0, 0.0, 0.0),
|
||||
};
|
||||
let pullback = 1.0 - move4;
|
||||
let move1 = move1base * pullback;
|
||||
@ -189,6 +195,32 @@ impl Animation for DashAnimation {
|
||||
|
||||
next.shoulder_r.orientation = Quaternion::rotation_x(-0.3);
|
||||
},
|
||||
"Tidal Warrior" => {
|
||||
next.head.orientation =
|
||||
Quaternion::rotation_x(0.0) * Quaternion::rotation_z(move1 * -0.3);
|
||||
next.upper_torso.orientation = Quaternion::rotation_x(move1 * -0.1)
|
||||
* Quaternion::rotation_z(move1 * 1.57);
|
||||
next.lower_torso.orientation = Quaternion::rotation_x(move1 * 0.1)
|
||||
* Quaternion::rotation_x(move1 * -0.1)
|
||||
* Quaternion::rotation_z(move1 * -0.2);
|
||||
|
||||
next.hand_l.position = Vec3::new(-14.0, 2.0 + motion * 1.5, -4.0);
|
||||
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0 + move1 * 1.0)
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(-0.35 + motion * -0.6);
|
||||
next.hand_r.position = Vec3::new(14.0, 2.0 + motion * -1.5, -4.0);
|
||||
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0 + move1 * 1.0)
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.35 + motion * 0.6);
|
||||
|
||||
next.shoulder_l.orientation = Quaternion::rotation_x(move1 * 0.8);
|
||||
|
||||
next.shoulder_r.orientation = Quaternion::rotation_x(move1 * 0.8);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
@ -48,8 +48,8 @@ impl Animation for RunAnimation {
|
||||
*rate = 1.0;
|
||||
|
||||
let lab: f32 = 0.65 * s_a.tempo;
|
||||
let speednorm = (speed / 12.0).powf(0.6);
|
||||
let speednormlow = (speed / 12.0).powf(4.0);
|
||||
let speednorm = (speed.min(16.0) / 12.0).powf(0.6);
|
||||
let speednormlow = (speed.min(16.0) / 12.0).powf(4.0);
|
||||
|
||||
let footvertl = (acc_vel * lab + PI * -0.2).sin() * speednorm;
|
||||
let footvertr = (acc_vel * lab + PI * -1.2).sin() * speednorm;
|
||||
@ -119,16 +119,17 @@ impl Animation for RunAnimation {
|
||||
|
||||
let foothoril = (acc_vel * lab + PI * 1.45).sin() * speednorm;
|
||||
let foothorir = (acc_vel * lab + PI * (0.45)).sin() * speednorm;
|
||||
let footstrafel = (acc_vel * lab + PI * 1.45).sin();
|
||||
let footstrafer = (acc_vel * lab + PI * (0.95)).sin();
|
||||
let footvertsl = (acc_vel * lab).sin();
|
||||
let footvertsr = (acc_vel * lab + PI * 0.5).sin();
|
||||
let footstrafel = (acc_vel * lab + PI * 1.45).sin() * speednorm;
|
||||
let footstrafer = (acc_vel * lab + PI * (0.95)).sin() * speednorm;
|
||||
let footvertsl = (acc_vel * lab).sin() * speednorm;
|
||||
let footvertsr = (acc_vel * lab + PI * 0.5).sin() * speednorm;
|
||||
let direction = velocity.y * -0.098 * orientation.y + velocity.x * -0.098 * orientation.x;
|
||||
|
||||
let side =
|
||||
(velocity.x * -0.098 * orientation.y + velocity.y * 0.098 * orientation.x) * -1.0;
|
||||
let side = ((velocity.x * -0.098 * orientation.y + velocity.y * 0.098 * orientation.x)
|
||||
* -1.0)
|
||||
.min(1.0)
|
||||
.max(-1.0);
|
||||
let sideabs = side.abs();
|
||||
|
||||
let x_tilt = avg_vel.z.atan2(avg_vel.xy().magnitude());
|
||||
|
||||
next.jaw.scale = Vec3::one() * 1.02;
|
||||
|
@ -212,14 +212,19 @@ impl Animation for StunnedAnimation {
|
||||
next.control_r.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0) * Quaternion::rotation_y(0.15);
|
||||
},
|
||||
"Tidal Claws" => {
|
||||
next.control_l.position = Vec3::new(-14.0, 12.0, -12.0);
|
||||
next.control_r.position = Vec3::new(14.0, 12.0, -12.0);
|
||||
"Tidal Warrior" => {
|
||||
next.head.orientation = Quaternion::rotation_x(movement1 * -2.0);
|
||||
next.upper_torso.orientation =
|
||||
Quaternion::rotation_z(movement1 * 1.0);
|
||||
next.lower_torso.orientation =
|
||||
Quaternion::rotation_z(movement1 * -1.0);
|
||||
next.hand_l.position = Vec3::new(-14.0, 2.0, -4.0);
|
||||
next.hand_r.position = Vec3::new(14.0, 2.0, -4.0);
|
||||
|
||||
next.control_l.orientation = Quaternion::rotation_x(PI / 3.0)
|
||||
* Quaternion::rotation_y(-0.15);
|
||||
next.control_r.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0) * Quaternion::rotation_y(0.15);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(PI / 3.0)
|
||||
* Quaternion::rotation_z(-0.35);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0) * Quaternion::rotation_z(0.35);
|
||||
},
|
||||
"Beast Claws" => {
|
||||
next.shoulder_l.position =
|
||||
|
@ -2,15 +2,19 @@ use super::{
|
||||
super::{vek::*, Animation},
|
||||
BipedLargeSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::{comp::item::ToolKind, states::utils::StageSection};
|
||||
use common::{
|
||||
comp::item::{AbilitySpec, ToolKind},
|
||||
states::utils::StageSection,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct SummonAnimation;
|
||||
|
||||
impl Animation for SummonAnimation {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type Dependency<'a> = (
|
||||
Option<ToolKind>,
|
||||
Option<ToolKind>,
|
||||
(Option<ToolKind>, Option<&'a AbilitySpec>),
|
||||
(Option<ToolKind>, Option<&'a AbilitySpec>),
|
||||
Vec3<f32>,
|
||||
f32,
|
||||
Option<StageSection>,
|
||||
@ -25,7 +29,14 @@ impl Animation for SummonAnimation {
|
||||
#[allow(clippy::approx_constant)] // TODO: Pending review in #587
|
||||
fn update_skeleton_inner<'a>(
|
||||
skeleton: &Self::Skeleton,
|
||||
(active_tool_kind, _second_tool_kind, velocity, _global_time, stage_section, acc_vel): Self::Dependency<'a>,
|
||||
(
|
||||
(active_tool_kind, active_tool_spec),
|
||||
_second_tool_kind,
|
||||
velocity,
|
||||
_global_time,
|
||||
stage_section,
|
||||
acc_vel,
|
||||
): Self::Dependency<'a>,
|
||||
anim_time: f32,
|
||||
rate: &mut f32,
|
||||
s_a: &SkeletonAttr,
|
||||
@ -55,21 +66,6 @@ impl Animation for SummonAnimation {
|
||||
let move1 = move1base * pullback;
|
||||
let move2 = move2base * pullback;
|
||||
|
||||
next.shoulder_l.position = Vec3::new(
|
||||
-s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
s_a.shoulder.2 - foothorir * 1.0,
|
||||
);
|
||||
next.shoulder_l.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + 0.6 * speednorm + (footrotr * -0.2) * speednorm);
|
||||
|
||||
next.shoulder_r.position = Vec3::new(
|
||||
s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
s_a.shoulder.2 - foothoril * 1.0,
|
||||
);
|
||||
next.shoulder_r.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + 0.6 * speednorm + (footrotl * -0.2) * speednorm);
|
||||
next.torso.orientation = Quaternion::rotation_z(0.0);
|
||||
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
@ -84,6 +80,23 @@ impl Animation for SummonAnimation {
|
||||
#[allow(clippy::single_match)]
|
||||
match active_tool_kind {
|
||||
Some(ToolKind::Staff) => {
|
||||
next.shoulder_l.position = Vec3::new(
|
||||
-s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
s_a.shoulder.2 - foothorir * 1.0,
|
||||
);
|
||||
next.shoulder_l.orientation = Quaternion::rotation_x(
|
||||
move1 * 0.8 + 0.6 * speednorm + (footrotr * -0.2) * speednorm,
|
||||
);
|
||||
|
||||
next.shoulder_r.position = Vec3::new(
|
||||
s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
s_a.shoulder.2 - foothoril * 1.0,
|
||||
);
|
||||
next.shoulder_r.orientation = Quaternion::rotation_x(
|
||||
move1 * 0.8 + 0.6 * speednorm + (footrotl * -0.2) * speednorm,
|
||||
);
|
||||
next.head.orientation = Quaternion::rotation_x(0.0);
|
||||
next.control_l.position = Vec3::new(-1.0, 3.0, 12.0);
|
||||
next.control_r.position = Vec3::new(
|
||||
@ -108,7 +121,77 @@ impl Animation for SummonAnimation {
|
||||
next.control.orientation = Quaternion::rotation_x(-0.2 + move1 * 1.0)
|
||||
* Quaternion::rotation_y(-0.1 + move2 * -0.8);
|
||||
},
|
||||
Some(ToolKind::Natural) => {
|
||||
if let Some(AbilitySpec::Custom(spec)) = active_tool_spec {
|
||||
match spec.as_str() {
|
||||
"Tidal Warrior" => {
|
||||
let (move1base, move2base, move3) = match stage_section {
|
||||
Some(StageSection::Buildup) => ((anim_time.powi(2)), 0.0, 0.0),
|
||||
Some(StageSection::Cast) => (1.0, (anim_time * 30.0).sin(), 0.0),
|
||||
Some(StageSection::Recover) => (1.0, 1.0, anim_time),
|
||||
_ => (0.0, 0.0, 0.0),
|
||||
};
|
||||
let pullback = 1.0 - move3;
|
||||
let move1 = move1base * pullback;
|
||||
let move2 = move2base * pullback;
|
||||
next.torso.position = Vec3::new(0.0, 0.0 + move1 * 1.0, move1 * -4.0);
|
||||
next.upper_torso.position =
|
||||
Vec3::new(0.0, s_a.upper_torso.0, s_a.upper_torso.1);
|
||||
|
||||
next.lower_torso.position =
|
||||
Vec3::new(0.0, s_a.lower_torso.0, s_a.lower_torso.1);
|
||||
|
||||
next.head.position =
|
||||
Vec3::new(0.0, s_a.head.0 + move1 * -8.0, s_a.head.1 + move1 * 6.0);
|
||||
next.shoulder_l.orientation = Quaternion::rotation_x(move1 * 2.5)
|
||||
* Quaternion::rotation_y(move1 * 0.4 + move2 * 0.05);
|
||||
next.shoulder_r.orientation = Quaternion::rotation_x(move1 * 2.5)
|
||||
* Quaternion::rotation_y(move1 * -0.4 + move2 * -0.05);
|
||||
next.head.orientation = Quaternion::rotation_x(move1 * 1.4)
|
||||
* Quaternion::rotation_y(move2 * 0.02);
|
||||
next.upper_torso.orientation = Quaternion::rotation_x(move1 * -1.5)
|
||||
* Quaternion::rotation_y(move2 * -0.02);
|
||||
next.lower_torso.orientation = Quaternion::rotation_x(move1 * 0.2)
|
||||
* Quaternion::rotation_y(move2 * 0.02);
|
||||
next.hand_l.position = Vec3::new(
|
||||
-14.0 + move1 * -5.0,
|
||||
2.0 + move1 * -2.0,
|
||||
-4.0 + move1 * 12.0,
|
||||
);
|
||||
next.hand_r.position = Vec3::new(
|
||||
14.0 + move1 * 5.0,
|
||||
2.0 + move1 * -2.0,
|
||||
-4.0 + move1 * 12.0,
|
||||
);
|
||||
|
||||
next.hand_l.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0 + move1 * 1.5)
|
||||
* Quaternion::rotation_y(-move1 * 0.7 + move2 * 0.2)
|
||||
* Quaternion::rotation_z(-0.35);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0 + move1 * 1.5)
|
||||
* Quaternion::rotation_y(move1 * 0.7 + move2 * 0.2)
|
||||
* Quaternion::rotation_z(0.35);
|
||||
next.leg_l.position = Vec3::new(-s_a.leg.0, s_a.leg.1, s_a.leg.2);
|
||||
next.leg_l.orientation =
|
||||
Quaternion::rotation_z(0.0) * Quaternion::rotation_x(move1 * -0.8);
|
||||
|
||||
next.leg_r.position = Vec3::new(s_a.leg.0, s_a.leg.1, s_a.leg.2);
|
||||
next.foot_l.position =
|
||||
Vec3::new(-s_a.foot.0, s_a.foot.1 + move1 * -3.0, s_a.foot.2);
|
||||
next.foot_r.position =
|
||||
Vec3::new(s_a.foot.0, s_a.foot.1 + move1 * -3.0, s_a.foot.2);
|
||||
next.leg_r.orientation =
|
||||
Quaternion::rotation_z(0.0) * Quaternion::rotation_x(move1 * -0.8);
|
||||
next.foot_l.orientation =
|
||||
Quaternion::rotation_z(0.0) * Quaternion::rotation_x(move1 * 0.8);
|
||||
next.foot_r.orientation =
|
||||
Quaternion::rotation_z(0.0) * Quaternion::rotation_x(move1 * 0.8);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
|
@ -251,14 +251,14 @@ impl Animation for WieldAnimation {
|
||||
next.control_r.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0) * Quaternion::rotation_y(0.15);
|
||||
},
|
||||
"Tidal Claws" => {
|
||||
next.control_l.position = Vec3::new(-14.0, 12.0, -12.0);
|
||||
next.control_r.position = Vec3::new(14.0, 12.0, -12.0);
|
||||
"Tidal Warrior" => {
|
||||
next.hand_l.position = Vec3::new(-14.0, 2.0, -4.0);
|
||||
next.hand_r.position = Vec3::new(14.0, 2.0, -4.0);
|
||||
|
||||
next.control_l.orientation = Quaternion::rotation_x(PI / 3.0)
|
||||
* Quaternion::rotation_y(-0.15);
|
||||
next.control_r.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0) * Quaternion::rotation_y(0.15);
|
||||
next.hand_l.orientation = Quaternion::rotation_x(PI / 3.0)
|
||||
* Quaternion::rotation_z(-0.35);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(PI / 3.0) * Quaternion::rotation_z(0.35);
|
||||
},
|
||||
"Beast Claws" => {
|
||||
next.shoulder_l.position =
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::*;
|
||||
use crate::audio::sfx::SfxEvent;
|
||||
use common::{
|
||||
combat::DamageKind,
|
||||
combat::{self, DamageKind},
|
||||
comp::{
|
||||
inventory::loadout_builder::LoadoutBuilder, item::tool::ToolKind, CharacterAbilityType,
|
||||
CharacterState, InputKind, Item,
|
||||
@ -76,7 +76,10 @@ fn maps_basic_melee() {
|
||||
recover_duration: Duration::default(),
|
||||
base_damage: 10.0,
|
||||
base_poise_damage: 10.0,
|
||||
knockback: 0.0,
|
||||
knockback: combat::Knockback {
|
||||
strength: 0.0,
|
||||
direction: combat::KnockbackDir::Away,
|
||||
},
|
||||
range: 1.0,
|
||||
max_angle: 1.0,
|
||||
ability_info: empty_ability_info(),
|
||||
|
@ -403,7 +403,7 @@ impl SfxMgr {
|
||||
audio.emit_sfx(sfx_trigger_item, *pos, None, false);
|
||||
}
|
||||
},
|
||||
beam::FrontendSpecifier::ClayGolem => {},
|
||||
beam::FrontendSpecifier::ClayGolem | beam::FrontendSpecifier::Bubbles => {},
|
||||
},
|
||||
Outcome::BreakBlock { pos, .. } => {
|
||||
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::BreakBlock);
|
||||
|
@ -756,6 +756,10 @@ fn insert_killing_buff(buff: BuffKind, localized_strings: &Localization, templat
|
||||
tracing::error!("Player was killed by a positive buff!");
|
||||
localized_strings.get("hud.outcome.mysterious")
|
||||
},
|
||||
BuffKind::Wet => {
|
||||
tracing::error!("Player was killed by a debuff that doesn't do damage!");
|
||||
localized_strings.get("hud.outcome.mysterious")
|
||||
},
|
||||
};
|
||||
|
||||
template.replace("{died_of_buff}", buff_outcome)
|
||||
|
@ -3777,6 +3777,8 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id {
|
||||
BuffKind::Burning { .. } => imgs.debuff_burning_0,
|
||||
BuffKind::Crippled { .. } => imgs.debuff_crippled_0,
|
||||
BuffKind::Frozen { .. } => imgs.debuff_frozen_0,
|
||||
// TODO: Get icon for this before merging. Anyone doing code review open a comment here.
|
||||
BuffKind::Wet { .. } => imgs.debuff_burning_0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -3798,6 +3800,7 @@ pub fn get_buff_title(buff: BuffKind, localized_strings: &Localization) -> &str
|
||||
BuffKind::Burning { .. } => localized_strings.get("buff.title.burn"),
|
||||
BuffKind::Crippled { .. } => localized_strings.get("buff.title.crippled"),
|
||||
BuffKind::Frozen { .. } => localized_strings.get("buff.title.frozen"),
|
||||
BuffKind::Wet { .. } => localized_strings.get("buff.title.wet"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -3831,6 +3834,7 @@ pub fn get_buff_desc(buff: BuffKind, data: BuffData, localized_strings: &Localiz
|
||||
BuffKind::Burning { .. } => Cow::Borrowed(localized_strings.get("buff.desc.burn")),
|
||||
BuffKind::Crippled { .. } => Cow::Borrowed(localized_strings.get("buff.desc.crippled")),
|
||||
BuffKind::Frozen { .. } => Cow::Borrowed(localized_strings.get("buff.desc.frozen")),
|
||||
BuffKind::Wet { .. } => Cow::Borrowed(localized_strings.get("buff.desc.wet")),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,8 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> String {
|
||||
| BuffKind::ProtectingWard
|
||||
| BuffKind::Crippled
|
||||
| BuffKind::Frenzied
|
||||
| BuffKind::Frozen => continue,
|
||||
| BuffKind::Frozen
|
||||
| BuffKind::Wet => continue,
|
||||
};
|
||||
|
||||
write!(&mut description, "{}", buff_desc).unwrap();
|
||||
@ -144,7 +145,8 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> String {
|
||||
| BuffKind::ProtectingWard
|
||||
| BuffKind::Crippled
|
||||
| BuffKind::Frenzied
|
||||
| BuffKind::Frozen => continue,
|
||||
| BuffKind::Frozen
|
||||
| BuffKind::Wet => continue,
|
||||
}
|
||||
} else if let BuffKind::Saturation | BuffKind::Regeneration = buff.kind {
|
||||
i18n.get("buff.text.every_second").to_string()
|
||||
|
@ -79,6 +79,8 @@ pub enum ParticleMode {
|
||||
Enraged = 26,
|
||||
BigShrapnel = 27,
|
||||
Laser = 28,
|
||||
Bubbles = 29,
|
||||
Water = 30,
|
||||
}
|
||||
|
||||
impl ParticleMode {
|
||||
|
@ -3807,6 +3807,7 @@ impl FigureMgr {
|
||||
time,
|
||||
Some(s.stage_section),
|
||||
state.acc_vel,
|
||||
state.state_time,
|
||||
),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
@ -4005,6 +4006,7 @@ impl FigureMgr {
|
||||
time,
|
||||
Some(s.stage_section),
|
||||
state.acc_vel,
|
||||
state.state_time,
|
||||
),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
@ -4090,8 +4092,8 @@ impl FigureMgr {
|
||||
anim::biped_large::SummonAnimation::update_skeleton(
|
||||
&target_base,
|
||||
(
|
||||
active_tool_kind,
|
||||
second_tool_kind,
|
||||
(active_tool_kind, active_tool_spec),
|
||||
(second_tool_kind, second_tool_spec),
|
||||
rel_vel,
|
||||
time,
|
||||
Some(s.stage_section),
|
||||
@ -4185,12 +4187,13 @@ impl FigureMgr {
|
||||
anim::biped_large::BeamAnimation::update_skeleton(
|
||||
&target_base,
|
||||
(
|
||||
active_tool_kind,
|
||||
second_tool_kind,
|
||||
(active_tool_kind, active_tool_spec),
|
||||
(second_tool_kind, second_tool_spec),
|
||||
time,
|
||||
rel_vel,
|
||||
Some(s.stage_section),
|
||||
state.acc_vel,
|
||||
state.state_time,
|
||||
),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
|
@ -9,8 +9,8 @@ use crate::{
|
||||
use common::{
|
||||
assets::{AssetExt, DotVoxAsset},
|
||||
comp::{
|
||||
self, aura, beam, body, buff, item::Reagent, object, BeamSegment, Body, CharacterState,
|
||||
Ori, Pos, Shockwave, Vel,
|
||||
self, aura, beam, body, buff, item::Reagent, object, shockwave, BeamSegment, Body,
|
||||
CharacterState, Ori, Pos, Shockwave, Vel,
|
||||
},
|
||||
figure::Segment,
|
||||
outcome::Outcome,
|
||||
@ -830,6 +830,31 @@ impl ParticleMgr {
|
||||
)
|
||||
})
|
||||
},
|
||||
beam::FrontendSpecifier::Bubbles => {
|
||||
let mut rng = thread_rng();
|
||||
let (from, to) = (Vec3::<f32>::unit_z(), *ori.look_dir());
|
||||
let m = Mat3::<f32>::rotation_from_to_3d(from, to);
|
||||
self.particles.resize_with(
|
||||
self.particles.len() + usize::from(beam_tick_count) / 15,
|
||||
|| {
|
||||
let phi: f32 = rng.gen_range(0.0..beam.properties.angle);
|
||||
let theta: f32 = rng.gen_range(0.0..2.0 * PI);
|
||||
let offset_z = Vec3::new(
|
||||
phi.sin() * theta.cos(),
|
||||
phi.sin() * theta.sin(),
|
||||
phi.cos(),
|
||||
);
|
||||
let random_ori = offset_z * m * Vec3::new(-1.0, -1.0, 1.0);
|
||||
Particle::new_directed(
|
||||
beam.properties.duration,
|
||||
time,
|
||||
ParticleMode::Bubbles,
|
||||
pos.0,
|
||||
pos.0 + random_ori * range,
|
||||
)
|
||||
},
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1093,6 +1118,7 @@ impl ParticleMgr {
|
||||
let state = scene_data.state;
|
||||
let ecs = state.ecs();
|
||||
let time = state.get_time();
|
||||
let dt = scene_data.state.ecs().fetch::<DeltaTime>().0;
|
||||
|
||||
for (_entity, pos, ori, shockwave) in (
|
||||
&ecs.entities(),
|
||||
@ -1102,9 +1128,10 @@ impl ParticleMgr {
|
||||
)
|
||||
.join()
|
||||
{
|
||||
let elapsed = time - shockwave.creation.unwrap_or_default();
|
||||
let elapsed = time - shockwave.creation.unwrap_or(time);
|
||||
let speed = shockwave.properties.speed;
|
||||
|
||||
let distance = shockwave.properties.speed * elapsed as f32;
|
||||
let distance = speed * elapsed as f32;
|
||||
|
||||
let radians = shockwave.properties.angle.to_radians();
|
||||
|
||||
@ -1112,55 +1139,99 @@ impl ParticleMgr {
|
||||
let theta = ori_vec.y.atan2(ori_vec.x);
|
||||
let dtheta = radians / distance;
|
||||
|
||||
let heartbeats = self.scheduler.heartbeats(Duration::from_millis(2));
|
||||
// Number of particles derived from arc length (for new particles at least, old
|
||||
// can be converted later)
|
||||
let arc_length = distance * radians;
|
||||
|
||||
for heartbeat in 0..heartbeats {
|
||||
if shockwave.properties.requires_ground {
|
||||
// 1 / 3 the size of terrain voxel
|
||||
let scale = 1.0 / 3.0;
|
||||
use shockwave::FrontendSpecifier;
|
||||
match shockwave.properties.specifier {
|
||||
FrontendSpecifier::Ground => {
|
||||
let heartbeats = self.scheduler.heartbeats(Duration::from_millis(2));
|
||||
for heartbeat in 0..heartbeats {
|
||||
// 1 / 3 the size of terrain voxel
|
||||
let scale = 1.0 / 3.0;
|
||||
|
||||
let scaled_speed = shockwave.properties.speed * scale;
|
||||
let scaled_speed = speed * scale;
|
||||
|
||||
let sub_tick_interpolation = scaled_speed * 1000.0 * heartbeat as f32;
|
||||
let sub_tick_interpolation = scaled_speed * 1000.0 * heartbeat as f32;
|
||||
|
||||
let distance =
|
||||
shockwave.properties.speed * (elapsed as f32 - sub_tick_interpolation);
|
||||
let distance = speed * (elapsed as f32 - sub_tick_interpolation);
|
||||
|
||||
let particle_count_factor = radians / (3.0 * scale);
|
||||
let new_particle_count = distance * particle_count_factor;
|
||||
self.particles.reserve(new_particle_count as usize);
|
||||
let particle_count_factor = radians / (3.0 * scale);
|
||||
let new_particle_count = distance * particle_count_factor;
|
||||
self.particles.reserve(new_particle_count as usize);
|
||||
|
||||
for d in 0..(new_particle_count as i32) {
|
||||
let arc_position =
|
||||
theta - radians / 2.0 + dtheta * d as f32 / particle_count_factor;
|
||||
for d in 0..(new_particle_count as i32) {
|
||||
let arc_position =
|
||||
theta - radians / 2.0 + dtheta * d as f32 / particle_count_factor;
|
||||
|
||||
let position = pos.0
|
||||
+ distance * Vec3::new(arc_position.cos(), arc_position.sin(), 0.0);
|
||||
let position = pos.0
|
||||
+ distance * Vec3::new(arc_position.cos(), arc_position.sin(), 0.0);
|
||||
|
||||
let position_snapped = ((position / scale).floor() + 0.5) * scale;
|
||||
let position_snapped = ((position / scale).floor() + 0.5) * scale;
|
||||
|
||||
self.particles.push(Particle::new(
|
||||
Duration::from_millis(250),
|
||||
time,
|
||||
ParticleMode::GroundShockwave,
|
||||
position_snapped,
|
||||
));
|
||||
self.particles.push(Particle::new(
|
||||
Duration::from_millis(250),
|
||||
time,
|
||||
ParticleMode::GroundShockwave,
|
||||
position_snapped,
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for d in 0..3 * distance as i32 {
|
||||
let arc_position = theta - radians / 2.0 + dtheta * d as f32 / 3.0;
|
||||
},
|
||||
FrontendSpecifier::Fire => {
|
||||
let heartbeats = self.scheduler.heartbeats(Duration::from_millis(2));
|
||||
for _ in 0..heartbeats {
|
||||
for d in 0..3 * distance as i32 {
|
||||
let arc_position = theta - radians / 2.0 + dtheta * d as f32 / 3.0;
|
||||
|
||||
let position = pos.0
|
||||
+ distance * Vec3::new(arc_position.cos(), arc_position.sin(), 0.0);
|
||||
let position = pos.0
|
||||
+ distance * Vec3::new(arc_position.cos(), arc_position.sin(), 0.0);
|
||||
|
||||
self.particles.push(Particle::new(
|
||||
Duration::from_secs_f32((distance + 10.0) / 50.0),
|
||||
time,
|
||||
ParticleMode::FireShockwave,
|
||||
position,
|
||||
));
|
||||
self.particles.push(Particle::new(
|
||||
Duration::from_secs_f32((distance + 10.0) / 50.0),
|
||||
time,
|
||||
ParticleMode::FireShockwave,
|
||||
position,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
FrontendSpecifier::Water => {
|
||||
// 4 particles per unit length of arc
|
||||
let particles_per_length = (arc_length) as usize;
|
||||
let dtheta = radians / particles_per_length as f32;
|
||||
// Scales number of desired heartbeats from speed - thicker arc = higher speed =
|
||||
// lower duration = more particles
|
||||
let heartbeats = self
|
||||
.scheduler
|
||||
.heartbeats(Duration::from_secs_f32(1.0 / speed));
|
||||
|
||||
// Reserves capacity for new particles
|
||||
let new_particle_count = particles_per_length * heartbeats as usize;
|
||||
self.particles.reserve(new_particle_count);
|
||||
|
||||
for i in 0..particles_per_length {
|
||||
let angle = dtheta * i as f32;
|
||||
let direction = Vec3::new(angle.cos(), angle.sin(), 0.0);
|
||||
for j in 0..heartbeats {
|
||||
// Sub tick dt
|
||||
let dt = (j as f32 / heartbeats as f32) * dt;
|
||||
let distance = distance + speed * dt;
|
||||
let pos1 = pos.0 + distance * direction - Vec3::unit_z();
|
||||
let pos2 = pos1 + (Vec3::unit_z() + direction) * 3.0;
|
||||
let time = time + dt as f64;
|
||||
|
||||
self.particles.push(Particle::new_directed(
|
||||
Duration::from_secs_f32(0.5),
|
||||
time,
|
||||
ParticleMode::Water,
|
||||
pos1,
|
||||
pos2,
|
||||
));
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user