mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'sam/minotaur' into 'master'
Overhauled Minotaur See merge request veloren/veloren!2193
This commit is contained in:
commit
fb940ad27a
@ -92,6 +92,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Debug Kit is split to "admin_cosmetics" and "debug"
|
||||
- Potion Kit is renamed to "consumables" and gives potions and mushroom curry
|
||||
- Cultist Kit gives cape, rings and necklace in addition to armour and weapons.
|
||||
- Reworked minotaur to have unique attacks.
|
||||
|
||||
### Removed
|
||||
|
||||
|
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -5542,6 +5542,8 @@ dependencies = [
|
||||
"specs-idvs",
|
||||
"spin_sleep",
|
||||
"structopt",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"uuid",
|
||||
|
@ -207,6 +207,14 @@
|
||||
(None, "common.abilities.custom.mindflayer.summonminions"),
|
||||
],
|
||||
),
|
||||
Custom("Minotaur"): (
|
||||
primary: "common.abilities.custom.minotaur.cleave",
|
||||
secondary: "common.abilities.custom.minotaur.cripplingstrike",
|
||||
abilities: [
|
||||
(None, "common.abilities.custom.minotaur.charge"),
|
||||
(None, "common.abilities.custom.minotaur.frenzy"),
|
||||
],
|
||||
),
|
||||
Custom("Bird Large Breathe"): (
|
||||
primary: "common.abilities.custom.birdlargebreathe.firebomb",
|
||||
secondary: "common.abilities.custom.birdlargebreathe.triplestrike",
|
||||
|
@ -8,4 +8,5 @@ BasicMelee(
|
||||
base_poise_damage: 40,
|
||||
range: 5.0,
|
||||
max_angle: 120.0,
|
||||
damage_effect: None,
|
||||
)
|
||||
|
@ -2,7 +2,7 @@ BasicBeam(
|
||||
buildup_duration: 0.40,
|
||||
recover_duration: 0.50,
|
||||
beam_duration: 1.0,
|
||||
damage: 350,
|
||||
damage: 500,
|
||||
tick_rate: 0.9,
|
||||
range: 22.0,
|
||||
max_angle: 15.0,
|
||||
|
@ -2,7 +2,7 @@ SpinMelee(
|
||||
buildup_duration: 0.5,
|
||||
swing_duration: 0.2,
|
||||
recover_duration: 0.6,
|
||||
base_damage: 80.0,
|
||||
base_damage: 100.0,
|
||||
base_poise_damage: 1.0,
|
||||
knockback: ( strength: 7.0, direction: Towards),
|
||||
range: 16.0,
|
||||
|
19
assets/common/abilities/custom/minotaur/charge.ron
Normal file
19
assets/common/abilities/custom/minotaur/charge.ron
Normal file
@ -0,0 +1,19 @@
|
||||
DashMelee(
|
||||
energy_cost: 0,
|
||||
base_damage: 150,
|
||||
scaled_damage: 600,
|
||||
base_poise_damage: 25,
|
||||
scaled_poise_damage: 100,
|
||||
base_knockback: 10.0,
|
||||
scaled_knockback: 30.0,
|
||||
range: 5.0,
|
||||
angle: 90.0,
|
||||
energy_drain: 0,
|
||||
forward_speed: 5.0,
|
||||
buildup_duration: 0.4,
|
||||
charge_duration: 4.0,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 0.5,
|
||||
charge_through: false,
|
||||
is_interruptible: false,
|
||||
)
|
18
assets/common/abilities/custom/minotaur/cleave.ron
Normal file
18
assets/common/abilities/custom/minotaur/cleave.ron
Normal file
@ -0,0 +1,18 @@
|
||||
ChargedMelee(
|
||||
energy_cost: 0,
|
||||
energy_drain: 0,
|
||||
initial_damage: 0,
|
||||
scaled_damage: 500,
|
||||
initial_poise_damage: 50,
|
||||
scaled_poise_damage: 150,
|
||||
initial_knockback: 0.0,
|
||||
scaled_knockback: 0.0,
|
||||
range: 5.0,
|
||||
max_angle: 45.0,
|
||||
speed: 1.0,
|
||||
charge_duration: 1.5,
|
||||
swing_duration: 0.1,
|
||||
hit_timing: 0.8,
|
||||
recover_duration: 0.5,
|
||||
specifier: Some(GroundCleave),
|
||||
)
|
17
assets/common/abilities/custom/minotaur/cripplingstrike.ron
Normal file
17
assets/common/abilities/custom/minotaur/cripplingstrike.ron
Normal file
@ -0,0 +1,17 @@
|
||||
BasicMelee(
|
||||
energy_cost: 0,
|
||||
buildup_duration: 0.3,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 0.6,
|
||||
base_damage: 150.0,
|
||||
base_poise_damage: 60.0,
|
||||
knockback: 15.0,
|
||||
range: 5.0,
|
||||
max_angle: 60.0,
|
||||
damage_effect: Some(Buff((
|
||||
kind: Crippled,
|
||||
dur_secs: 15.0,
|
||||
strength: Value(0.5),
|
||||
chance: 1.0,
|
||||
))),
|
||||
)
|
9
assets/common/abilities/custom/minotaur/frenzy.ron
Normal file
9
assets/common/abilities/custom/minotaur/frenzy.ron
Normal file
@ -0,0 +1,9 @@
|
||||
SelfBuff(
|
||||
buildup_duration: 0.25,
|
||||
cast_duration: 0.8,
|
||||
recover_duration: 0.25,
|
||||
buff_kind: Frenzied,
|
||||
buff_strength: 0.5,
|
||||
buff_duration: Some(300.0),
|
||||
energy_cost: 0,
|
||||
)
|
@ -8,4 +8,5 @@ BasicMelee(
|
||||
knockback: 25.0,
|
||||
range: 1.2,
|
||||
max_angle: 50.0,
|
||||
damage_effect: None,
|
||||
)
|
||||
|
@ -8,4 +8,5 @@ BasicMelee(
|
||||
knockback: 0.0,
|
||||
range: 3.5,
|
||||
max_angle: 20.0,
|
||||
damage_effect: None,
|
||||
)
|
||||
|
@ -8,4 +8,5 @@ BasicMelee(
|
||||
knockback: 0.0,
|
||||
range: 3.5,
|
||||
max_angle: 15.0,
|
||||
damage_effect: None,
|
||||
)
|
||||
|
@ -8,4 +8,5 @@ BasicMelee(
|
||||
knockback: 0.0,
|
||||
range: 3.5,
|
||||
max_angle: 20.0,
|
||||
damage_effect: None,
|
||||
)
|
||||
|
@ -8,4 +8,5 @@ BasicMelee(
|
||||
knockback: 0.0,
|
||||
range: 3.5,
|
||||
max_angle: 20.0,
|
||||
damage_effect: None,
|
||||
)
|
||||
|
@ -8,4 +8,5 @@ BasicMelee(
|
||||
knockback: 0.0,
|
||||
range: 3.0,
|
||||
max_angle: 120.0,
|
||||
damage_effect: None,
|
||||
)
|
||||
|
@ -6,14 +6,14 @@ ItemDef(
|
||||
hands: Two,
|
||||
stats: Direct((
|
||||
equip_time_secs: 0.5,
|
||||
power: 1.8,
|
||||
power: 1.0,
|
||||
poise_strength: 1.0,
|
||||
speed: 1.0,
|
||||
crit_chance: 0.048611112,
|
||||
crit_mult: 1.6530613,
|
||||
crit_chance: 0.1,
|
||||
crit_mult: 1.5,
|
||||
)),
|
||||
)),
|
||||
quality: Low,
|
||||
quality: Legendary,
|
||||
tags: [],
|
||||
ability_spec: Some(Custom("Axe Simple")),
|
||||
ability_spec: Some(Custom("Minotaur")),
|
||||
)
|
@ -6,9 +6,9 @@ ItemDef(
|
||||
hands: Two,
|
||||
stats: Direct((
|
||||
equip_time_secs: 0.5,
|
||||
power: 4.0,
|
||||
power: 2.5,
|
||||
poise_strength: 1.0,
|
||||
speed: 0.5,
|
||||
speed: 0.8,
|
||||
crit_chance: 0.078125,
|
||||
crit_mult: 1.3657143,
|
||||
)),
|
||||
|
@ -6,9 +6,9 @@ ItemDef(
|
||||
hands: Two,
|
||||
stats: Direct((
|
||||
equip_time_secs: 0.4,
|
||||
power: 2.3,
|
||||
power: 1.67,
|
||||
poise_strength: 1.5,
|
||||
speed: 1.0,
|
||||
speed: 1.2,
|
||||
crit_chance: 0.078125,
|
||||
crit_mult: 1.3657143,
|
||||
)),
|
||||
|
@ -6,9 +6,9 @@ ItemDef(
|
||||
hands: Two,
|
||||
stats: Direct((
|
||||
equip_time_secs: 0.4,
|
||||
power: 4.0,
|
||||
poise_strength: 1.5,
|
||||
speed: 0.5,
|
||||
power: 2.5,
|
||||
poise_strength: 1.0,
|
||||
speed: 0.8,
|
||||
crit_chance: 0.078125,
|
||||
crit_mult: 1.3657143,
|
||||
)),
|
||||
|
@ -6,9 +6,9 @@ ItemDef(
|
||||
hands: Two,
|
||||
stats: Direct((
|
||||
equip_time_secs: 0.4,
|
||||
power: 1.8,
|
||||
power: 2.0,
|
||||
poise_strength: 1.5,
|
||||
speed: 1.2,
|
||||
speed: 1.0,
|
||||
crit_chance: 0.21153846,
|
||||
crit_mult: 1.4502164,
|
||||
)),
|
||||
|
@ -6,9 +6,9 @@ ItemDef(
|
||||
hands: Two,
|
||||
stats: Direct((
|
||||
equip_time_secs: 0.6,
|
||||
power: 1.8,
|
||||
power: 1.67,
|
||||
poise_strength: 1.0,
|
||||
speed: 1.6,
|
||||
speed: 1.2,
|
||||
crit_chance: 0.2002994,
|
||||
crit_mult: 1.3798152,
|
||||
)),
|
||||
|
@ -6,9 +6,9 @@ ItemDef(
|
||||
hands: Two,
|
||||
stats: Direct((
|
||||
equip_time_secs: 0.6,
|
||||
power: 3.0,
|
||||
power: 2.5,
|
||||
poise_strength: 1.0,
|
||||
speed: 0.67,
|
||||
speed: 0.8,
|
||||
crit_chance: 0.1002994,
|
||||
crit_mult: 1.3798152,
|
||||
)),
|
||||
|
@ -39,5 +39,20 @@
|
||||
("common.items.consumable.potion_med", 100),
|
||||
("common.items.consumable.potion_big", 100),
|
||||
("common.items.food.apple_mushroom_curry", 100),
|
||||
]
|
||||
],
|
||||
"tier-4": [
|
||||
("common.items.armor.steel.belt", 1),
|
||||
("common.items.armor.steel.chest", 1),
|
||||
("common.items.armor.steel.foot", 1),
|
||||
("common.items.armor.steel.hand", 1),
|
||||
("common.items.armor.steel.pants", 1),
|
||||
("common.items.armor.steel.shoulder", 1),
|
||||
("common.items.weapons.sword.cobalt-0", 1),
|
||||
("common.items.weapons.axe.cobalt_axe-0", 1),
|
||||
("common.items.weapons.hammer.cobalt_hammer-0", 1),
|
||||
("common.items.weapons.bow.frostwood-0", 1),
|
||||
("common.items.weapons.staff.frostwood_torch", 1),
|
||||
("common.items.weapons.sceptre.fork0", 1),
|
||||
("common.items.consumable.potion_med", 100),
|
||||
],
|
||||
})
|
||||
|
@ -824,5 +824,12 @@
|
||||
],
|
||||
threshold: 1.25,
|
||||
),
|
||||
GroundSlam: (
|
||||
files: [
|
||||
"voxygen.audio.sfx.abilities.minotaur_smash_1",
|
||||
"voxygen.audio.sfx.abilities.minotaur_smash_2",
|
||||
],
|
||||
threshold: 0.2,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
BIN
assets/voxygen/audio/sfx/abilities/minotaur_smash_1.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/abilities/minotaur_smash_1.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/abilities/minotaur_smash_2.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/abilities/minotaur_smash_2.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/de_buffs/buff_frenzy_0.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/de_buffs/buff_frenzy_0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/de_buffs/debuff_cripple_0.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/de_buffs/debuff_cripple_0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -19,6 +19,8 @@
|
||||
"buff.desc.invulnerability": "You cannot be damaged by any attack.",
|
||||
"buff.title.protectingward": "Protecting Ward",
|
||||
"buff.desc.protectingward": "You are protected, somewhat, from attacks.",
|
||||
"buff.title.frenzied": "Frenzied",
|
||||
"buff.desc.frenzied": "You are imbued with unnatural speed and can ignore minor injuries.",
|
||||
// Debuffs
|
||||
"buff.title.bleed": "Bleeding",
|
||||
"buff.desc.bleed": "Inflicts regular damage.",
|
||||
@ -26,6 +28,8 @@
|
||||
"buff.desc.cursed": "You are cursed.",
|
||||
"buff.title.burn": "On Fire",
|
||||
"buff.desc.burn": "You are burning alive",
|
||||
"buff.title.crippled": "Crippled",
|
||||
"buff.desc.crippled": "Your movement is crippled as your legs are heavily injured.",
|
||||
// Buffs stats
|
||||
"buff.stat.health": "Restores {str_total} Health",
|
||||
"buff.stat.increase_max_stamina": "Raises Maximum Stamina by {strength}",
|
||||
|
@ -7,6 +7,7 @@
|
||||
"hud.outcome.burning": "died of: burning",
|
||||
"hud.outcome.curse": "died of: curse",
|
||||
"hud.outcome.bleeding": "died of: bleeding",
|
||||
"hud.outcome.crippled": "died of: crippled",
|
||||
|
||||
// Chat outputs
|
||||
"hud.chat.online_msg": "[{name}] is online now",
|
||||
|
@ -64,6 +64,8 @@ const int LIFESTEAL_BEAM = 22;
|
||||
const int CULTIST_FLAME = 23;
|
||||
const int STATIC_SMOKE = 24;
|
||||
const int BLOOD = 25;
|
||||
const int ENRAGED = 26;
|
||||
const int BIG_SHRAPNEL = 27;
|
||||
|
||||
// meters per second squared (acceleration)
|
||||
const float earth_gravity = 9.807;
|
||||
@ -218,6 +220,17 @@ void main() {
|
||||
vec4(vec3(0.25), 1),
|
||||
spin_in_axis(vec3(1,0,0),0)
|
||||
);
|
||||
} else if (inst_mode == BIG_SHRAPNEL) {
|
||||
float brown_color = 0.05 + 0.1 * rand1;
|
||||
attr = Attr(
|
||||
linear_motion(
|
||||
vec3(0),
|
||||
normalize(vec3(rand4, rand5, rand6)) * 15.0 + grav_vel(earth_gravity)
|
||||
),
|
||||
vec3(5 * (1 - percent())),
|
||||
vec4(vec3(brown_color, brown_color / 2, 0), 1),
|
||||
spin_in_axis(vec3(1,0,0),0)
|
||||
);
|
||||
} else if (inst_mode == FIREWORK_BLUE) {
|
||||
f_reflect = 0.0; // Fire doesn't reflect light, it emits it
|
||||
attr = Attr(
|
||||
@ -421,6 +434,15 @@ void main() {
|
||||
vec4(1, 0, 0, 1),
|
||||
spin_in_axis(vec3(1,0,0),0)
|
||||
);
|
||||
} else if (inst_mode == ENRAGED) {
|
||||
f_reflect = 0.0;
|
||||
float red_color = 1.2 + 0.3 * rand3;
|
||||
attr = Attr(
|
||||
(inst_dir * slow_end(1.5)) + vec3(rand0, rand1, rand2) * (percent() + 2) * 0.1,
|
||||
vec3((3.5 * (1 - slow_start(0.2)))),
|
||||
vec4(red_color, 0.0, 0.0, 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9)
|
||||
);
|
||||
} else {
|
||||
attr = Attr(
|
||||
linear_motion(
|
||||
|
@ -1043,7 +1043,7 @@
|
||||
color: None
|
||||
),
|
||||
"common.items.npc_weapons.axe.minotaur_axe": (
|
||||
vox_spec: ("weapon.axe.2haxe_minotaur", (-2.5, -9.0, -6.0)),
|
||||
vox_spec: ("weapon.axe.2haxe_minotaur", (-2.5, -9.0, -8.0)),
|
||||
color: None
|
||||
),
|
||||
"common.items.npc_weapons.hammer.yeti_hammer": (
|
||||
|
BIN
assets/voxygen/voxel/weapon/axe/2haxe_minotaur.vox
(Stored with Git LFS)
BIN
assets/voxygen/voxel/weapon/axe/2haxe_minotaur.vox
(Stored with Git LFS)
Binary file not shown.
@ -49,7 +49,7 @@ ron = { version = "0.6", default-features = false }
|
||||
serde_json = "1.0.50"
|
||||
serde_repr = "0.1.6"
|
||||
|
||||
# esv export
|
||||
# csv export
|
||||
csv = { version = "1.1.3", optional = true }
|
||||
structopt = { version = "0.3.13", optional = true }
|
||||
|
||||
@ -59,6 +59,10 @@ slotmap = { version = "1.0", features = ["serde"] }
|
||||
indexmap = "1.3.0"
|
||||
slab = "0.4.2"
|
||||
|
||||
# Strum
|
||||
strum = "0.20"
|
||||
strum_macros = "0.20"
|
||||
|
||||
# ECS
|
||||
specs = { git = "https://github.com/amethyst/specs.git", features = ["serde", "storage-event-control", "nightly"], rev = "5a9b71035007be0e3574f35184acac1cd4530496" }
|
||||
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", rev = "b65fb220e94f5d3c9bc30074a076149763795556" }
|
||||
|
@ -1,4 +1,8 @@
|
||||
use crate::{assets, comp, npc, terrain};
|
||||
use crate::{
|
||||
assets,
|
||||
comp::{self, buff::BuffKind},
|
||||
npc, terrain,
|
||||
};
|
||||
use assets::AssetExt;
|
||||
use hashbrown::HashMap;
|
||||
use lazy_static::lazy_static;
|
||||
@ -8,6 +12,7 @@ use std::{
|
||||
path::Path,
|
||||
str::FromStr,
|
||||
};
|
||||
use strum::IntoEnumIterator;
|
||||
use tracing::warn;
|
||||
|
||||
/// Struct representing a command that a user can run from server chat.
|
||||
@ -210,21 +215,40 @@ lazy_static! {
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
|
||||
static ref BUFFS: Vec<String> = vec![
|
||||
// Debuffs
|
||||
"burning", "bleeding", "curse",
|
||||
// Heal
|
||||
"regeneration", "saturation", "potion", "campfire_heal",
|
||||
// Outmaxing stats
|
||||
"increase_max_energy", "increase_max_health",
|
||||
// Defensive buffs
|
||||
"invulnerability", "protecting_ward",
|
||||
// One command to rule them all
|
||||
"all",
|
||||
]
|
||||
.iter()
|
||||
.map(|b| b.to_string())
|
||||
.collect();
|
||||
pub static ref BUFF_PARSER: HashMap<String, BuffKind> = {
|
||||
let string_from_buff = |kind| match kind {
|
||||
BuffKind::Burning => "burning",
|
||||
BuffKind::Regeneration => "regeration",
|
||||
BuffKind::Saturation => "saturation",
|
||||
BuffKind::Bleeding => "bleeding",
|
||||
BuffKind::Cursed => "cursed",
|
||||
BuffKind::Potion => "potion",
|
||||
BuffKind::CampfireHeal => "campfire_heal",
|
||||
BuffKind::IncreaseMaxEnergy => "increase_max_energy",
|
||||
BuffKind::IncreaseMaxHealth => "increase_max_health",
|
||||
BuffKind::Invulnerability => "invulnerability",
|
||||
BuffKind::ProtectingWard => "protecting_ward",
|
||||
BuffKind::Frenzied => "frenzied",
|
||||
BuffKind::Crippled => "crippled",
|
||||
};
|
||||
let mut buff_parser = HashMap::new();
|
||||
BuffKind::iter().for_each(|kind| {buff_parser.insert(string_from_buff(kind).to_string(), kind);});
|
||||
buff_parser
|
||||
};
|
||||
|
||||
pub static ref BUFF_PACK: Vec<String> = {
|
||||
let mut buff_pack: Vec<_> = BUFF_PARSER.keys().cloned().collect();
|
||||
// Remove invulnerability as it removes debuffs
|
||||
buff_pack.retain(|kind| kind != "invulnerability");
|
||||
buff_pack
|
||||
};
|
||||
|
||||
static ref BUFFS: Vec<String> = {
|
||||
let mut buff_pack: Vec<_> = BUFF_PARSER.keys().cloned().collect();
|
||||
// Add all as valid command
|
||||
buff_pack.push("all".to_string());
|
||||
buff_pack
|
||||
};
|
||||
|
||||
static ref BLOCK_KINDS: Vec<String> = terrain::block::BLOCK_KINDS
|
||||
.keys()
|
||||
|
@ -2,8 +2,8 @@ use crate::{
|
||||
assets::{self, Asset},
|
||||
combat::{self, CombatEffect, Knockback},
|
||||
comp::{
|
||||
aura, beam, inventory::item::tool::ToolKind, projectile::ProjectileConstructor, skills,
|
||||
Body, CharacterState, EnergySource, LightEmitter, StateUpdate,
|
||||
aura, beam, buff, inventory::item::tool::ToolKind, projectile::ProjectileConstructor,
|
||||
skills, Body, CharacterState, EnergySource, LightEmitter, StateUpdate,
|
||||
},
|
||||
states::{
|
||||
behavior::JoinData,
|
||||
@ -30,6 +30,7 @@ pub enum CharacterAbilityType {
|
||||
BasicBeam,
|
||||
RepeaterRanged,
|
||||
BasicAura,
|
||||
SelfBuff,
|
||||
}
|
||||
|
||||
impl From<&CharacterState> for CharacterAbilityType {
|
||||
@ -49,6 +50,7 @@ impl From<&CharacterState> for CharacterAbilityType {
|
||||
CharacterState::BasicBeam(_) => Self::BasicBeam,
|
||||
CharacterState::RepeaterRanged(_) => Self::RepeaterRanged,
|
||||
CharacterState::BasicAura(_) => Self::BasicAura,
|
||||
CharacterState::SelfBuff(_) => Self::SelfBuff,
|
||||
_ => Self::BasicMelee,
|
||||
}
|
||||
}
|
||||
@ -66,6 +68,7 @@ pub enum CharacterAbility {
|
||||
knockback: f32,
|
||||
range: f32,
|
||||
max_angle: f32,
|
||||
damage_effect: Option<CombatEffect>,
|
||||
},
|
||||
BasicRanged {
|
||||
energy_cost: f32,
|
||||
@ -188,6 +191,7 @@ pub enum CharacterAbility {
|
||||
swing_duration: f32,
|
||||
hit_timing: f32,
|
||||
recover_duration: f32,
|
||||
specifier: Option<charged_melee::FrontendSpecifier>,
|
||||
},
|
||||
ChargedRanged {
|
||||
energy_cost: f32,
|
||||
@ -268,6 +272,15 @@ pub enum CharacterAbility {
|
||||
summon_amount: u32,
|
||||
summon_info: basic_summon::SummonInfo,
|
||||
},
|
||||
SelfBuff {
|
||||
buildup_duration: f32,
|
||||
cast_duration: f32,
|
||||
recover_duration: f32,
|
||||
buff_kind: buff::BuffKind,
|
||||
buff_strength: f32,
|
||||
buff_duration: Option<f32>,
|
||||
energy_cost: f32,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for CharacterAbility {
|
||||
@ -282,6 +295,7 @@ impl Default for CharacterAbility {
|
||||
knockback: 0.0,
|
||||
range: 3.5,
|
||||
max_angle: 15.0,
|
||||
damage_effect: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -313,7 +327,8 @@ impl CharacterAbility {
|
||||
| CharacterAbility::ChargedMelee { energy_cost, .. }
|
||||
| CharacterAbility::Shockwave { energy_cost, .. }
|
||||
| CharacterAbility::BasicAura { energy_cost, .. }
|
||||
| CharacterAbility::BasicBlock { energy_cost, .. } => update
|
||||
| CharacterAbility::BasicBlock { energy_cost, .. }
|
||||
| CharacterAbility::SelfBuff { energy_cost, .. } => update
|
||||
.energy
|
||||
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
|
||||
.is_ok(),
|
||||
@ -334,7 +349,11 @@ impl CharacterAbility {
|
||||
.is_ok()
|
||||
},
|
||||
CharacterAbility::HealingBeam { .. } => data.combo.counter() > 0,
|
||||
_ => true,
|
||||
CharacterAbility::ComboMelee { .. }
|
||||
| CharacterAbility::Boost { .. }
|
||||
| CharacterAbility::BasicBeam { .. }
|
||||
| CharacterAbility::Blink { .. }
|
||||
| CharacterAbility::BasicSummon { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
@ -586,6 +605,18 @@ impl CharacterAbility {
|
||||
*cast_duration /= speed;
|
||||
*recover_duration /= speed;
|
||||
},
|
||||
SelfBuff {
|
||||
ref mut buff_strength,
|
||||
ref mut buildup_duration,
|
||||
ref mut cast_duration,
|
||||
ref mut recover_duration,
|
||||
..
|
||||
} => {
|
||||
*buff_strength *= power;
|
||||
*buildup_duration /= speed;
|
||||
*cast_duration /= speed;
|
||||
*recover_duration /= speed;
|
||||
},
|
||||
}
|
||||
self
|
||||
}
|
||||
@ -605,7 +636,8 @@ impl CharacterAbility {
|
||||
| Shockwave { energy_cost, .. }
|
||||
| HealingBeam { energy_cost, .. }
|
||||
| BasicAura { energy_cost, .. }
|
||||
| BasicBlock { energy_cost, .. } => *energy_cost as u32,
|
||||
| BasicBlock { energy_cost, .. }
|
||||
| SelfBuff { energy_cost, .. } => *energy_cost as u32,
|
||||
BasicBeam { energy_drain, .. } => {
|
||||
if *energy_drain > f32::EPSILON {
|
||||
1
|
||||
@ -1164,6 +1196,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
knockback,
|
||||
range,
|
||||
max_angle,
|
||||
damage_effect,
|
||||
energy_cost: _,
|
||||
} => CharacterState::BasicMelee(basic_melee::Data {
|
||||
static_data: basic_melee::StaticData {
|
||||
@ -1175,6 +1208,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
knockback: *knockback,
|
||||
range: *range,
|
||||
max_angle: *max_angle,
|
||||
damage_effect: *damage_effect,
|
||||
ability_info,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
@ -1419,6 +1453,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
recover_duration,
|
||||
range,
|
||||
max_angle,
|
||||
specifier,
|
||||
} => CharacterState::ChargedMelee(charged_melee::Data {
|
||||
static_data: charged_melee::StaticData {
|
||||
energy_cost: *energy_cost,
|
||||
@ -1437,6 +1472,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
hit_timing: *hit_timing,
|
||||
recover_duration: Duration::from_secs_f32(*recover_duration),
|
||||
ability_info,
|
||||
specifier: *specifier,
|
||||
},
|
||||
stage_section: StageSection::Charge,
|
||||
timer: Duration::default(),
|
||||
@ -1657,6 +1693,27 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
}),
|
||||
CharacterAbility::SelfBuff {
|
||||
buildup_duration,
|
||||
cast_duration,
|
||||
recover_duration,
|
||||
buff_kind,
|
||||
buff_strength,
|
||||
buff_duration,
|
||||
energy_cost: _,
|
||||
} => CharacterState::SelfBuff(self_buff::Data {
|
||||
static_data: self_buff::StaticData {
|
||||
buildup_duration: Duration::from_secs_f32(*buildup_duration),
|
||||
cast_duration: Duration::from_secs_f32(*cast_duration),
|
||||
recover_duration: Duration::from_secs_f32(*recover_duration),
|
||||
buff_kind: *buff_kind,
|
||||
buff_strength: *buff_strength,
|
||||
buff_duration: buff_duration.map(Duration::from_secs_f32),
|
||||
ability_info,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ pub enum Tactic {
|
||||
Mindflayer,
|
||||
BirdLargeBreathe,
|
||||
BirdLargeFire,
|
||||
Minotaur,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
@ -299,10 +300,17 @@ pub struct Agent {
|
||||
pub behavior: Behavior,
|
||||
pub psyche: Psyche,
|
||||
pub inbox: VecDeque<AgentEvent>,
|
||||
pub action_timer: f32,
|
||||
pub action_state: ActionState,
|
||||
pub bearing: Vec2<f32>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ActionState {
|
||||
pub timer: f32,
|
||||
pub counter: f32,
|
||||
pub condition: bool,
|
||||
}
|
||||
|
||||
impl Agent {
|
||||
pub fn with_patrol_origin(mut self, origin: Vec3<f32>) -> Self {
|
||||
self.patrol_origin = Some(origin);
|
||||
|
@ -441,10 +441,10 @@ impl Body {
|
||||
biped_large::Species::Wendigo => 2800,
|
||||
biped_large::Species::Troll => 2400,
|
||||
biped_large::Species::Dullahan => 3000,
|
||||
biped_large::Species::Mindflayer => 8000,
|
||||
biped_large::Species::Mindflayer => 25000,
|
||||
biped_large::Species::Tidalwarrior => 2500,
|
||||
biped_large::Species::Yeti => 4000,
|
||||
biped_large::Species::Minotaur => 5000,
|
||||
biped_large::Species::Minotaur => 30000,
|
||||
biped_large::Species::Harvester => 3000,
|
||||
biped_large::Species::Blueoni => 2400,
|
||||
biped_large::Species::Redoni => 2400,
|
||||
@ -553,10 +553,12 @@ impl Body {
|
||||
biped_large::Species::Wendigo => 80,
|
||||
biped_large::Species::Troll => 60,
|
||||
biped_large::Species::Dullahan => 120,
|
||||
biped_large::Species::Mindflayer => 250,
|
||||
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,
|
||||
_ => 100,
|
||||
},
|
||||
Body::BipedSmall(_) => 10,
|
||||
@ -605,7 +607,11 @@ impl Body {
|
||||
pub fn combat_multiplier(&self) -> f32 {
|
||||
match self {
|
||||
Body::Object(_) | Body::Ship(_) => 0.0,
|
||||
Body::BipedLarge(b) if matches!(b.species, biped_large::Species::Mindflayer) => 4.0,
|
||||
Body::BipedLarge(b) => match b.species {
|
||||
biped_large::Species::Mindflayer => 4.8,
|
||||
biped_large::Species::Minotaur => 3.2,
|
||||
_ => 1.0,
|
||||
},
|
||||
_ => 1.0,
|
||||
}
|
||||
}
|
||||
|
@ -9,33 +9,59 @@ use specs::{Component, DerefFlaggedStorage};
|
||||
use specs_idvs::IdvStorage;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::{cmp::Ordering, time::Duration};
|
||||
use strum_macros::EnumIter;
|
||||
|
||||
/// De/buff Kind.
|
||||
/// This is used to determine what effects a buff will have
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, PartialOrd, Ord)]
|
||||
#[derive(
|
||||
Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, PartialOrd, Ord, EnumIter,
|
||||
)]
|
||||
pub enum BuffKind {
|
||||
/// Does damage to a creature over time
|
||||
Burning,
|
||||
// Buffs
|
||||
/// Restores health/time for some period
|
||||
/// Strength should be 10x the healing per second
|
||||
Regeneration,
|
||||
/// Restores health/time for some period for consumables
|
||||
/// Strength should be 10x the healing per second
|
||||
Saturation,
|
||||
/// Lowers health over time for some duration
|
||||
Bleeding,
|
||||
/// Lower a creature's max health over time
|
||||
Cursed,
|
||||
/// Applied when drinking a potion
|
||||
/// Strength should be 10x the healing per second
|
||||
Potion,
|
||||
/// Applied when sitting at a campfire
|
||||
/// Strength is fraction of health resotred per second
|
||||
CampfireHeal,
|
||||
/// Raises maximum stamina
|
||||
/// Strength should be 10x the effect to max energy
|
||||
IncreaseMaxEnergy,
|
||||
/// Raises maximum health
|
||||
/// Strength should be 10x the effect to max health
|
||||
IncreaseMaxHealth,
|
||||
/// Makes you immune to attacks
|
||||
/// Strength does not affect this buff
|
||||
Invulnerability,
|
||||
/// Reduces incoming damage
|
||||
/// Strength scales the damage reduction non-linearly. 0.5 provides 50% DR,
|
||||
/// 1.0 provides 67% DR
|
||||
ProtectingWard,
|
||||
/// Increases movement speed and gives health regeneration
|
||||
/// Strength scales the movement speed linearly. 0.5 is 150% speed, 1.0 is
|
||||
/// 200% speed. Provides regeneration at 10x the value of the strength
|
||||
Frenzied,
|
||||
// Debuffs
|
||||
/// Does damage to a creature over time
|
||||
/// Strength should be 10x the DPS of the debuff
|
||||
Burning,
|
||||
/// Lowers health over time for some duration
|
||||
/// Strength should be 10x the DPS of the debuff
|
||||
Bleeding,
|
||||
/// Lower a creature's max health over time
|
||||
/// Strength only affects the target max health, 0.5 targets 50% of base
|
||||
/// max, 1.0 targets 100% of base max
|
||||
Cursed,
|
||||
/// Reduces movement speed and causes bleeding damage
|
||||
/// Strength scales the movement speed debuff non-linearly. 0.5 is 50%
|
||||
/// speed, 1.0 is 33% speed. Bleeding is at 10x the value of the strength.
|
||||
Crippled,
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@ -54,6 +80,8 @@ impl BuffKind {
|
||||
BuffKind::Invulnerability => true,
|
||||
BuffKind::ProtectingWard => true,
|
||||
BuffKind::Burning => false,
|
||||
BuffKind::Crippled => false,
|
||||
BuffKind::Frenzied => true,
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,6 +146,8 @@ pub enum BuffEffect {
|
||||
kind: ModifierKind,
|
||||
target_fraction: f32,
|
||||
},
|
||||
/// Modifies move speed of target
|
||||
MovementSpeed(f32),
|
||||
}
|
||||
|
||||
/// Actual de/buff.
|
||||
@ -174,6 +204,8 @@ impl Buff {
|
||||
cat_ids: Vec<BuffCategory>,
|
||||
source: BuffSource,
|
||||
) -> Self {
|
||||
// Normalized nonlinear scaling
|
||||
let nn_scaling = |a| a / (a + 0.5);
|
||||
let (effects, time) = match kind {
|
||||
BuffKind::Bleeding => (
|
||||
vec![BuffEffect::HealthChangeOverTime {
|
||||
@ -235,7 +267,7 @@ impl Buff {
|
||||
// Causes non-linearity in effect strength, but necessary to allow for tool
|
||||
// power and other things to affect the strength. 0.5 also still provides 50%
|
||||
// damage reduction.
|
||||
data.strength / (0.5 + data.strength),
|
||||
nn_scaling(data.strength),
|
||||
)],
|
||||
data.duration,
|
||||
),
|
||||
@ -247,6 +279,28 @@ impl Buff {
|
||||
}],
|
||||
data.duration,
|
||||
),
|
||||
BuffKind::Crippled => (
|
||||
vec![
|
||||
BuffEffect::MovementSpeed(1.0 - nn_scaling(data.strength)),
|
||||
BuffEffect::HealthChangeOverTime {
|
||||
rate: -data.strength * 40.0,
|
||||
accumulated: 0.0,
|
||||
kind: ModifierKind::Additive,
|
||||
},
|
||||
],
|
||||
data.duration,
|
||||
),
|
||||
BuffKind::Frenzied => (
|
||||
vec![
|
||||
BuffEffect::MovementSpeed(1.0 + data.strength),
|
||||
BuffEffect::HealthChangeOverTime {
|
||||
rate: data.strength * 100.0,
|
||||
accumulated: 0.0,
|
||||
kind: ModifierKind::Additive,
|
||||
},
|
||||
],
|
||||
data.duration,
|
||||
),
|
||||
};
|
||||
Buff {
|
||||
kind,
|
||||
|
@ -99,6 +99,8 @@ pub enum CharacterState {
|
||||
Blink(blink::Data),
|
||||
/// Summons creatures that fight for the caster
|
||||
BasicSummon(basic_summon::Data),
|
||||
/// Inserts a buff on the caster
|
||||
SelfBuff(self_buff::Data),
|
||||
}
|
||||
|
||||
impl CharacterState {
|
||||
@ -120,6 +122,7 @@ impl CharacterState {
|
||||
| CharacterState::BasicBeam(_)
|
||||
| CharacterState::BasicAura(_)
|
||||
| CharacterState::HealingBeam(_)
|
||||
| CharacterState::SelfBuff(_)
|
||||
)
|
||||
}
|
||||
|
||||
@ -143,6 +146,7 @@ impl CharacterState {
|
||||
| CharacterState::BasicBeam(_)
|
||||
| CharacterState::BasicAura(_)
|
||||
| CharacterState::HealingBeam(_)
|
||||
| CharacterState::SelfBuff(_)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ pub struct Stats {
|
||||
pub name: String,
|
||||
pub damage_reduction: f32,
|
||||
pub max_health_modifier: f32,
|
||||
pub move_speed_modifier: f32,
|
||||
}
|
||||
|
||||
impl Stats {
|
||||
@ -33,6 +34,7 @@ impl Stats {
|
||||
name,
|
||||
damage_reduction: 0.0,
|
||||
max_health_modifier: 1.0,
|
||||
move_speed_modifier: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,6 +45,7 @@ impl Stats {
|
||||
name: "".to_owned(),
|
||||
damage_reduction: 0.0,
|
||||
max_health_modifier: 1.0,
|
||||
move_speed_modifier: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,6 +53,7 @@ impl Stats {
|
||||
pub fn reset_temp_modifiers(&mut self) {
|
||||
self.damage_reduction = 0.0;
|
||||
self.max_health_modifier = 1.0;
|
||||
self.move_speed_modifier = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,9 @@ pub enum Outcome {
|
||||
pos: Vec3<f32>,
|
||||
state: PoiseState,
|
||||
},
|
||||
GroundSlam {
|
||||
pos: Vec3<f32>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Outcome {
|
||||
@ -81,7 +84,8 @@ impl Outcome {
|
||||
| Outcome::SummonedCreature { pos, .. }
|
||||
| Outcome::Damage { pos, .. }
|
||||
| Outcome::Block { pos, .. }
|
||||
| Outcome::PoiseChange { pos, .. } => Some(*pos),
|
||||
| Outcome::PoiseChange { pos, .. }
|
||||
| Outcome::GroundSlam { pos } => Some(*pos),
|
||||
Outcome::BreakBlock { pos, .. } => Some(pos.map(|e| e as f32 + 0.5)),
|
||||
Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => None,
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ pub struct StaticData {
|
||||
pub range: f32,
|
||||
/// Max angle (45.0 will give you a 90.0 angle window)
|
||||
pub max_angle: f32,
|
||||
/// Adds an effect onto the main damage of the attack
|
||||
pub damage_effect: Option<CombatEffect>,
|
||||
/// What key is used to press ability
|
||||
pub ability_info: AbilityInfo,
|
||||
}
|
||||
@ -50,6 +52,7 @@ impl CharacterBehavior for Data {
|
||||
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
|
||||
handle_orientation(data, &mut update, 1.0);
|
||||
handle_move(data, &mut update, 0.7);
|
||||
handle_jump(data, &mut update, 1.0);
|
||||
handle_orientation(data, &mut update, 0.35);
|
||||
@ -97,15 +100,20 @@ impl CharacterBehavior for Data {
|
||||
.with_requirement(CombatRequirement::AnyDamage);
|
||||
let energy = AttackEffect::new(None, CombatEffect::EnergyReward(50.0))
|
||||
.with_requirement(CombatRequirement::AnyDamage);
|
||||
let buff = CombatEffect::Buff(CombatBuff::default_physical());
|
||||
let damage = AttackDamage::new(
|
||||
let mut damage = AttackDamage::new(
|
||||
Damage {
|
||||
source: DamageSource::Melee,
|
||||
value: self.static_data.base_damage as f32,
|
||||
},
|
||||
Some(GroupTarget::OutOfGroup),
|
||||
)
|
||||
.with_effect(buff);
|
||||
);
|
||||
match self.static_data.damage_effect {
|
||||
Some(effect) => damage = damage.with_effect(effect),
|
||||
None => {
|
||||
let buff = CombatEffect::Buff(CombatBuff::default_physical());
|
||||
damage = damage.with_effect(buff);
|
||||
},
|
||||
}
|
||||
let (crit_chance, crit_mult) =
|
||||
get_crit_data(data, self.static_data.ability_info);
|
||||
let attack = Attack::default()
|
||||
|
@ -1,6 +1,8 @@
|
||||
use crate::{
|
||||
combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement},
|
||||
comp::{tool::ToolKind, CharacterState, EnergyChange, EnergySource, Melee, StateUpdate},
|
||||
event::LocalEvent,
|
||||
outcome::Outcome,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::{StageSection, *},
|
||||
@ -45,6 +47,8 @@ pub struct StaticData {
|
||||
pub recover_duration: Duration,
|
||||
/// What key is used to press ability
|
||||
pub ability_info: AbilityInfo,
|
||||
/// Used to specify the melee attack to the frontend
|
||||
pub specifier: Option<FrontendSpecifier>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@ -199,6 +203,17 @@ impl CharacterBehavior for Data {
|
||||
})
|
||||
.filter(|(_, tool)| tool == &Some(ToolKind::Pick)),
|
||||
});
|
||||
|
||||
if let Some(FrontendSpecifier::GroundCleave) = self.static_data.specifier {
|
||||
// Send local event used for frontend shenanigans
|
||||
update.local_events.push_front(LocalEvent::CreateOutcome(
|
||||
Outcome::GroundSlam {
|
||||
pos: data.pos.0
|
||||
+ *data.ori.look_dir()
|
||||
* (data.body.radius() + self.static_data.range),
|
||||
},
|
||||
));
|
||||
}
|
||||
} else if self.timer < self.static_data.swing_duration {
|
||||
// Swings
|
||||
update.character = CharacterState::ChargedMelee(Data {
|
||||
@ -250,3 +265,9 @@ impl CharacterBehavior for Data {
|
||||
update
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to specify a particular effect for frontend purposes
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum FrontendSpecifier {
|
||||
GroundCleave,
|
||||
}
|
||||
|
@ -71,12 +71,12 @@ impl CharacterBehavior for Data {
|
||||
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
|
||||
handle_orientation(data, &mut update, 1.0);
|
||||
handle_move(data, &mut update, 0.1);
|
||||
|
||||
match self.stage_section {
|
||||
StageSection::Buildup => {
|
||||
if self.timer < self.static_data.buildup_duration {
|
||||
handle_orientation(data, &mut update, 1.0);
|
||||
// Build up
|
||||
update.character = CharacterState::DashMelee(Data {
|
||||
timer: self
|
||||
@ -106,6 +106,7 @@ impl CharacterBehavior for Data {
|
||||
/ self.static_data.charge_duration.as_secs_f32())
|
||||
.min(1.0);
|
||||
|
||||
handle_orientation(data, &mut update, 0.6);
|
||||
handle_forced_movement(
|
||||
data,
|
||||
&mut update,
|
||||
|
@ -21,6 +21,7 @@ pub mod idle;
|
||||
pub mod leap_melee;
|
||||
pub mod repeater_ranged;
|
||||
pub mod roll;
|
||||
pub mod self_buff;
|
||||
pub mod shockwave;
|
||||
pub mod sit;
|
||||
pub mod sneak;
|
||||
|
131
common/src/states/self_buff.rs
Normal file
131
common/src/states/self_buff.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use crate::{
|
||||
comp::{
|
||||
buff::{Buff, BuffChange, BuffData, BuffKind, BuffSource},
|
||||
CharacterState, StateUpdate,
|
||||
},
|
||||
event::ServerEvent,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
|
||||
/// Separated out to condense update portions of character state
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct StaticData {
|
||||
/// How long until state should create the aura
|
||||
pub buildup_duration: Duration,
|
||||
/// How long the state is creating an aura
|
||||
pub cast_duration: Duration,
|
||||
/// How long the state has until exiting
|
||||
pub recover_duration: Duration,
|
||||
/// What kind of buff is created
|
||||
pub buff_kind: BuffKind,
|
||||
/// Strength of the created buff
|
||||
pub buff_strength: f32,
|
||||
/// How long buff lasts
|
||||
pub buff_duration: Option<Duration>,
|
||||
/// What key is used to press ability
|
||||
pub ability_info: AbilityInfo,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Data {
|
||||
/// Struct containing data that does not change over the course of the
|
||||
/// character state
|
||||
pub static_data: StaticData,
|
||||
/// Timer for each stage
|
||||
pub timer: Duration,
|
||||
/// What section the character stage is in
|
||||
pub stage_section: StageSection,
|
||||
}
|
||||
|
||||
impl CharacterBehavior for Data {
|
||||
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
|
||||
handle_move(data, &mut update, 0.8);
|
||||
handle_jump(data, &mut update, 1.0);
|
||||
|
||||
match self.stage_section {
|
||||
StageSection::Buildup => {
|
||||
if self.timer < self.static_data.buildup_duration {
|
||||
// Build up
|
||||
update.character = CharacterState::SelfBuff(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Creates buff
|
||||
let buff = Buff::new(
|
||||
self.static_data.buff_kind,
|
||||
BuffData {
|
||||
strength: self.static_data.buff_strength,
|
||||
duration: self.static_data.buff_duration,
|
||||
},
|
||||
Vec::new(),
|
||||
BuffSource::Character { by: *data.uid },
|
||||
);
|
||||
update.server_events.push_front(ServerEvent::Buff {
|
||||
entity: data.entity,
|
||||
buff_change: BuffChange::Add(buff),
|
||||
});
|
||||
// Build up
|
||||
update.character = CharacterState::SelfBuff(Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Cast,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Cast => {
|
||||
if self.timer < self.static_data.cast_duration {
|
||||
// Cast
|
||||
update.character = CharacterState::SelfBuff(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
update.character = CharacterState::SelfBuff(Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Recover,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Recover => {
|
||||
if self.timer < self.static_data.recover_duration {
|
||||
update.character = CharacterState::SelfBuff(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
update.character = CharacterState::Wielding;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
update.character = CharacterState::Wielding;
|
||||
},
|
||||
}
|
||||
|
||||
// At end of state logic so an interrupt isn't overwritten
|
||||
if !input_is_pressed(data, self.static_data.ability_info.input) {
|
||||
handle_state_interrupt(data, &mut update, false);
|
||||
}
|
||||
|
||||
update
|
||||
}
|
||||
}
|
@ -175,7 +175,6 @@ impl CharacterBehavior for Data {
|
||||
},
|
||||
0.1,
|
||||
);
|
||||
handle_orientation(data, &mut update, 1.0);
|
||||
}
|
||||
|
||||
// Swings
|
||||
|
@ -71,7 +71,7 @@ impl Body {
|
||||
biped_large::Species::Occultsaurok => 100.0,
|
||||
biped_large::Species::Mightysaurok => 100.0,
|
||||
biped_large::Species::Mindflayer => 90.0,
|
||||
biped_large::Species::Minotaur => 90.0,
|
||||
biped_large::Species::Minotaur => 60.0,
|
||||
_ => 80.0,
|
||||
},
|
||||
Body::BirdMedium(_) => 80.0,
|
||||
@ -243,6 +243,8 @@ 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 accel = if data.physics.on_ground {
|
||||
data.body.base_accel()
|
||||
} else {
|
||||
@ -267,6 +269,8 @@ pub fn handle_forced_movement(
|
||||
movement: ForcedMovement,
|
||||
efficiency: f32,
|
||||
) {
|
||||
let efficiency = efficiency * data.stats.move_speed_modifier;
|
||||
|
||||
match movement {
|
||||
ForcedMovement::Forward { strength } => {
|
||||
if let Some(accel) = data.physics.on_ground.then_some(data.body.base_accel()) {
|
||||
@ -324,6 +328,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;
|
||||
if let Some(force) = data.body.swim_thrust() {
|
||||
let force = efficiency * force;
|
||||
let mut water_accel = force / data.mass.0;
|
||||
@ -361,6 +366,8 @@ 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 glider = match data.character {
|
||||
CharacterState::Glide(data) => Some(data),
|
||||
_ => None,
|
||||
|
@ -189,6 +189,9 @@ impl<'a> System<'a> for Sys {
|
||||
stat.max_health_modifier *= current_fraction;
|
||||
}
|
||||
},
|
||||
BuffEffect::MovementSpeed(ms) => {
|
||||
stat.move_speed_modifier *= *ms;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -341,6 +341,7 @@ impl<'a> System<'a> for Sys {
|
||||
CharacterState::HealingBeam(data) => data.handle_event(&j, action),
|
||||
CharacterState::Blink(data) => data.handle_event(&j, action),
|
||||
CharacterState::BasicSummon(data) => data.handle_event(&j, action),
|
||||
CharacterState::SelfBuff(data) => data.handle_event(&j, action),
|
||||
};
|
||||
local_emitter.append(&mut state_update.local_events);
|
||||
server_emitter.append(&mut state_update.server_events);
|
||||
@ -395,6 +396,7 @@ impl<'a> System<'a> for Sys {
|
||||
CharacterState::HealingBeam(data) => data.behavior(&j),
|
||||
CharacterState::Blink(data) => data.behavior(&j),
|
||||
CharacterState::BasicSummon(data) => data.behavior(&j),
|
||||
CharacterState::SelfBuff(data) => data.behavior(&j),
|
||||
};
|
||||
|
||||
local_emitter.append(&mut state_update.local_events);
|
||||
|
@ -248,7 +248,8 @@ impl<'a> System<'a> for Sys {
|
||||
| CharacterState::BasicAura { .. }
|
||||
| CharacterState::HealingBeam { .. }
|
||||
| CharacterState::Blink { .. }
|
||||
| CharacterState::BasicSummon { .. } => {
|
||||
| CharacterState::BasicSummon { .. }
|
||||
| CharacterState::SelfBuff { .. } => {
|
||||
if energy.get_unchecked().regen_rate != 0.0 {
|
||||
energy.get_mut_unchecked().regen_rate = 0.0
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ use authc::Uuid;
|
||||
use chrono::{NaiveTime, Timelike};
|
||||
use common::{
|
||||
assets,
|
||||
cmd::{ChatCommand, CHAT_COMMANDS, CHAT_SHORTCUTS},
|
||||
cmd::{ChatCommand, BUFF_PACK, BUFF_PARSER, CHAT_COMMANDS, CHAT_SHORTCUTS},
|
||||
comp::{
|
||||
self,
|
||||
aura::{Aura, AuraKind, AuraTarget},
|
||||
@ -36,13 +36,11 @@ use common_net::{
|
||||
};
|
||||
use common_state::{BuildAreaError, BuildAreas};
|
||||
use core::{convert::TryFrom, ops::Not, time::Duration};
|
||||
use hashbrown::HashSet;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use rand::Rng;
|
||||
use specs::{Builder, Entity as EcsEntity, Join, WorldExt};
|
||||
use wiring::{Circuit, Wire, WiringAction, WiringActionEffect, WiringElement};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use vek::*;
|
||||
use wiring::{Circuit, Wire, WiringAction, WiringActionEffect, WiringElement};
|
||||
use world::util::Sampler;
|
||||
|
||||
use crate::{client::Client, login_provider::LoginProvider, wiring};
|
||||
@ -2615,22 +2613,6 @@ fn handle_apply_buff(
|
||||
args: String,
|
||||
action: &ChatCommand,
|
||||
) -> CmdResult<()> {
|
||||
const BUFF_PACK: &[&str] = &[
|
||||
// Debuffs
|
||||
"burning",
|
||||
"bleeding",
|
||||
"curse",
|
||||
// Healing
|
||||
"regeneration",
|
||||
"saturation",
|
||||
"potion",
|
||||
"campfire_heal",
|
||||
// Outmaxing stats
|
||||
"increase_max_energy",
|
||||
"increase_max_health",
|
||||
// Defensive buffs (invulnerability is skipped because it ruins all debuffs)
|
||||
"protecting_ward",
|
||||
];
|
||||
if let (Some(buff), strength, duration) =
|
||||
scan_fmt_some!(&args, &action.arg_fmt(), String, f32, f64)
|
||||
{
|
||||
@ -2640,7 +2622,7 @@ fn handle_apply_buff(
|
||||
if buff != "all" {
|
||||
cast_buff(&buff, buffdata, server, target)
|
||||
} else {
|
||||
for kind in BUFF_PACK {
|
||||
for kind in BUFF_PACK.iter() {
|
||||
cast_buff(kind, buffdata, server, target)?;
|
||||
}
|
||||
Ok(())
|
||||
@ -2663,19 +2645,4 @@ fn cast_buff(kind: &str, data: BuffData, server: &mut Server, target: EcsEntity)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_buffkind(buff: &str) -> Option<BuffKind> {
|
||||
match buff {
|
||||
"burning" => Some(BuffKind::Burning),
|
||||
"regeneration" => Some(BuffKind::Regeneration),
|
||||
"saturation" => Some(BuffKind::Saturation),
|
||||
"bleeding" => Some(BuffKind::Bleeding),
|
||||
"curse" => Some(BuffKind::Cursed),
|
||||
"potion" => Some(BuffKind::Potion),
|
||||
"campfire_heal" => Some(BuffKind::CampfireHeal),
|
||||
"increase_max_energy" => Some(BuffKind::IncreaseMaxEnergy),
|
||||
"increase_max_health" => Some(BuffKind::IncreaseMaxHealth),
|
||||
"invulnerability" => Some(BuffKind::Invulnerability),
|
||||
"protecting_ward" => Some(BuffKind::ProtectingWard),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn parse_buffkind(buff: &str) -> Option<BuffKind> { BUFF_PARSER.get(buff).copied() }
|
||||
|
@ -550,10 +550,10 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
// Interact if incoming messages
|
||||
if !agent.inbox.is_empty() {
|
||||
agent.action_timer = 0.1;
|
||||
agent.action_state.timer = 0.1;
|
||||
}
|
||||
if agent.action_timer > 0.0 {
|
||||
if agent.action_timer
|
||||
if agent.action_state.timer > 0.0 {
|
||||
if agent.action_state.timer
|
||||
< (if agent.behavior.is(BehaviorState::TRADING) {
|
||||
TRADE_INTERACTION_TIME
|
||||
} else {
|
||||
@ -562,7 +562,7 @@ impl<'a> AgentData<'a> {
|
||||
{
|
||||
self.interact(agent, controller, &read_data, event_emitter);
|
||||
} else {
|
||||
agent.action_timer = 0.0;
|
||||
agent.action_state.timer = 0.0;
|
||||
agent.target = None;
|
||||
controller.actions.push(ControlAction::Stand);
|
||||
self.idle(agent, controller, &read_data);
|
||||
@ -582,7 +582,7 @@ impl<'a> AgentData<'a> {
|
||||
event_emitter: &mut Emitter<'_, ServerEvent>,
|
||||
) {
|
||||
if self.damage < HEALING_ITEM_THRESHOLD && self.heal_self(agent, controller) {
|
||||
agent.action_timer = 0.01;
|
||||
agent.action_state.timer = 0.01;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -596,12 +596,15 @@ impl<'a> AgentData<'a> {
|
||||
let dist_sqrd = self.pos.0.distance_squared(tgt_pos.0);
|
||||
// Should the agent flee?
|
||||
if 1.0 - agent.psyche.aggro > self.damage && self.flees {
|
||||
if agent.action_timer == 0.0 && agent.behavior.can(BehaviorCapability::SPEAK) {
|
||||
if agent.action_state.timer == 0.0
|
||||
&& agent.behavior.can(BehaviorCapability::SPEAK)
|
||||
{
|
||||
let msg = "npc.speech.villager_under_attack".to_string();
|
||||
event_emitter
|
||||
.emit(ServerEvent::Chat(UnresolvedChatMsg::npc(*self.uid, msg)));
|
||||
agent.action_timer = 0.01;
|
||||
} else if agent.action_timer < FLEE_DURATION || dist_sqrd < MAX_FLEE_DIST {
|
||||
agent.action_state.timer = 0.01;
|
||||
} else if agent.action_state.timer < FLEE_DURATION || dist_sqrd < MAX_FLEE_DIST
|
||||
{
|
||||
self.flee(
|
||||
agent,
|
||||
controller,
|
||||
@ -610,7 +613,7 @@ impl<'a> AgentData<'a> {
|
||||
&read_data.dt,
|
||||
);
|
||||
} else {
|
||||
agent.action_timer = 0.0;
|
||||
agent.action_state.timer = 0.0;
|
||||
agent.target = None;
|
||||
self.idle(agent, controller, &read_data);
|
||||
}
|
||||
@ -705,11 +708,11 @@ impl<'a> AgentData<'a> {
|
||||
};
|
||||
|
||||
if self.damage < HEALING_ITEM_THRESHOLD && self.heal_self(agent, controller) {
|
||||
agent.action_timer = 0.01;
|
||||
agent.action_state.timer = 0.01;
|
||||
return;
|
||||
}
|
||||
|
||||
agent.action_timer = 0.0;
|
||||
agent.action_state.timer = 0.0;
|
||||
if let Some((travel_to, _destination)) = &agent.rtsim_controller.travel_to {
|
||||
// if it has an rtsim destination and can fly then it should
|
||||
// if it is flying and bumps something above it then it should move down
|
||||
@ -901,7 +904,7 @@ impl<'a> AgentData<'a> {
|
||||
// .events
|
||||
// .push(ControlEvent::InviteResponse(InviteResponse::Decline));
|
||||
// }
|
||||
agent.action_timer += read_data.dt.0;
|
||||
agent.action_state.timer += read_data.dt.0;
|
||||
let msg = agent.inbox.pop_back();
|
||||
match msg {
|
||||
Some(AgentEvent::Talk(by, subject)) => {
|
||||
@ -1250,7 +1253,7 @@ impl<'a> AgentData<'a> {
|
||||
if let Some(Target { target, .. }) = &agent.target {
|
||||
self.look_toward(controller, read_data, target);
|
||||
} else {
|
||||
agent.action_timer = 0.0;
|
||||
agent.action_state.timer = 0.0;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1314,7 +1317,7 @@ impl<'a> AgentData<'a> {
|
||||
self.jump_if(controller, bearing.z > 1.5);
|
||||
controller.inputs.move_z = bearing.z;
|
||||
}
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
}
|
||||
|
||||
/// Attempt to consume a healing item, and return whether any healing items
|
||||
@ -1379,7 +1382,7 @@ impl<'a> AgentData<'a> {
|
||||
read_data: &ReadData,
|
||||
event_emitter: &mut Emitter<'_, ServerEvent>,
|
||||
) {
|
||||
agent.action_timer = 0.0;
|
||||
agent.action_state.timer = 0.0;
|
||||
|
||||
// Search area
|
||||
let target = self.cached_spatial_grid.0
|
||||
@ -1538,6 +1541,7 @@ impl<'a> AgentData<'a> {
|
||||
"Bird Large Breathe" => Tactic::BirdLargeBreathe,
|
||||
"Bird Large Fire" => Tactic::BirdLargeFire,
|
||||
"Mindflayer" => Tactic::Mindflayer,
|
||||
"Minotaur" => Tactic::Minotaur,
|
||||
_ => Tactic::Melee,
|
||||
},
|
||||
AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind),
|
||||
@ -1644,16 +1648,16 @@ impl<'a> AgentData<'a> {
|
||||
Tactic::Axe => {
|
||||
if dist_sqrd < min_attack_dist.powi(2) && angle < 45.0 {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
if agent.action_timer > 6.0 {
|
||||
if agent.action_state.timer > 6.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::CancelInput(InputKind::Secondary));
|
||||
agent.action_timer = 0.0;
|
||||
} else if agent.action_timer > 4.0 && self.energy.current() > 10 {
|
||||
agent.action_state.timer = 0.0;
|
||||
} else if agent.action_state.timer > 4.0 && self.energy.current() > 10 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else if self.skill_set.has_skill(Skill::Axe(AxeSkill::UnlockLeap))
|
||||
&& self.energy.current() > 800
|
||||
&& thread_rng().gen_bool(0.5)
|
||||
@ -1661,12 +1665,12 @@ impl<'a> AgentData<'a> {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
}
|
||||
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
|
||||
if let Some((bearing, speed)) = agent.chaser.chase(
|
||||
@ -1699,16 +1703,16 @@ impl<'a> AgentData<'a> {
|
||||
Tactic::Hammer => {
|
||||
if dist_sqrd < min_attack_dist.powi(2) && angle < 45.0 {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
if agent.action_timer > 4.0 {
|
||||
if agent.action_state.timer > 4.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::CancelInput(InputKind::Secondary));
|
||||
agent.action_timer = 0.0;
|
||||
} else if agent.action_timer > 2.0 {
|
||||
agent.action_state.timer = 0.0;
|
||||
} else if agent.action_state.timer > 2.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else if self
|
||||
.skill_set
|
||||
.has_skill(Skill::Hammer(HammerSkill::UnlockLeap))
|
||||
@ -1718,12 +1722,12 @@ impl<'a> AgentData<'a> {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
}
|
||||
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
|
||||
if let Some((bearing, speed)) = agent.chaser.chase(
|
||||
@ -1742,14 +1746,14 @@ impl<'a> AgentData<'a> {
|
||||
if self
|
||||
.skill_set
|
||||
.has_skill(Skill::Hammer(HammerSkill::UnlockLeap))
|
||||
&& agent.action_timer > 5.0
|
||||
&& agent.action_state.timer > 5.0
|
||||
{
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
agent.action_timer = 0.0;
|
||||
agent.action_state.timer = 0.0;
|
||||
} else {
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
}
|
||||
} else {
|
||||
controller.inputs.move_dir =
|
||||
@ -1776,20 +1780,20 @@ impl<'a> AgentData<'a> {
|
||||
if self
|
||||
.skill_set
|
||||
.has_skill(Skill::Sword(SwordSkill::UnlockSpin))
|
||||
&& agent.action_timer < 2.0
|
||||
&& agent.action_state.timer < 2.0
|
||||
&& self.energy.current() > 600
|
||||
{
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
agent.action_timer += dt.0;
|
||||
} else if agent.action_timer > 2.0 {
|
||||
agent.action_timer = 0.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else if agent.action_state.timer > 2.0 {
|
||||
agent.action_state.timer = 0.0;
|
||||
} else {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
}
|
||||
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
|
||||
if let Some((bearing, speed)) = agent.chaser.chase(
|
||||
@ -1805,13 +1809,13 @@ impl<'a> AgentData<'a> {
|
||||
if can_see_tgt(&*terrain, self.pos, tgt_pos, dist_sqrd) {
|
||||
controller.inputs.move_dir =
|
||||
bearing.xy().try_normalized().unwrap_or_else(Vec2::zero) * speed;
|
||||
if agent.action_timer > 4.0 && angle < 45.0 {
|
||||
if agent.action_state.timer > 4.0 && angle < 45.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
agent.action_timer = 0.0;
|
||||
agent.action_state.timer = 0.0;
|
||||
} else {
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
}
|
||||
} else {
|
||||
controller.inputs.move_dir =
|
||||
@ -1886,16 +1890,17 @@ impl<'a> AgentData<'a> {
|
||||
.try_normalized()
|
||||
.unwrap_or_else(Vec2::zero)
|
||||
* speed;
|
||||
if agent.action_timer > 4.0 {
|
||||
if agent.action_state.timer > 4.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::CancelInput(InputKind::Secondary));
|
||||
agent.action_timer = 0.0;
|
||||
} else if agent.action_timer > 2.0 && self.energy.current() > 300 {
|
||||
agent.action_state.timer = 0.0;
|
||||
} else if agent.action_state.timer > 2.0 && self.energy.current() > 300
|
||||
{
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else if self
|
||||
.skill_set
|
||||
.has_skill(Skill::Bow(BowSkill::UnlockRepeater))
|
||||
@ -1908,7 +1913,7 @@ impl<'a> AgentData<'a> {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else {
|
||||
controller
|
||||
.actions
|
||||
@ -1916,7 +1921,7 @@ impl<'a> AgentData<'a> {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
}
|
||||
} else {
|
||||
controller.inputs.move_dir =
|
||||
@ -1961,22 +1966,22 @@ impl<'a> AgentData<'a> {
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Roll));
|
||||
} else if dist_sqrd < (5.0 * min_attack_dist).powi(2) && angle < 15.0 {
|
||||
if agent.action_timer < 1.5 {
|
||||
if agent.action_state.timer < 1.5 {
|
||||
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
|
||||
.xy()
|
||||
.rotated_z(0.47 * PI)
|
||||
.try_normalized()
|
||||
.unwrap_or_else(Vec2::unit_y);
|
||||
agent.action_timer += dt.0;
|
||||
} else if agent.action_timer < 3.0 {
|
||||
agent.action_state.timer += dt.0;
|
||||
} else if agent.action_state.timer < 3.0 {
|
||||
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
|
||||
.xy()
|
||||
.rotated_z(-0.47 * PI)
|
||||
.try_normalized()
|
||||
.unwrap_or_else(Vec2::unit_y);
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else {
|
||||
agent.action_timer = 0.0;
|
||||
agent.action_state.timer = 0.0;
|
||||
}
|
||||
if self
|
||||
.skill_set
|
||||
@ -2080,13 +2085,13 @@ impl<'a> AgentData<'a> {
|
||||
if can_see_tgt(&*terrain, self.pos, tgt_pos, dist_sqrd) && angle < 90.0 {
|
||||
controller.inputs.move_dir =
|
||||
bearing.xy().try_normalized().unwrap_or_else(Vec2::zero) * speed;
|
||||
if agent.action_timer > 5.0 {
|
||||
if agent.action_state.timer > 5.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
agent.action_timer = 0.0;
|
||||
agent.action_state.timer = 0.0;
|
||||
} else {
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
}
|
||||
} else {
|
||||
controller.inputs.move_dir =
|
||||
@ -2116,7 +2121,7 @@ impl<'a> AgentData<'a> {
|
||||
} else if dist_sqrd < ((radius as f32 + 1.0) * min_attack_dist).powi(2)
|
||||
&& dist_sqrd > (radius as f32 * min_attack_dist).powi(2)
|
||||
{
|
||||
if agent.action_timer < circle_time as f32 {
|
||||
if agent.action_state.timer < circle_time as f32 {
|
||||
let move_dir = (tgt_pos.0 - self.pos.0)
|
||||
.xy()
|
||||
.rotated_z(0.47 * PI)
|
||||
@ -2133,16 +2138,16 @@ impl<'a> AgentData<'a> {
|
||||
.1
|
||||
.map_or(true, |b| b.is_some());
|
||||
if obstacle_left {
|
||||
agent.action_timer = circle_time as f32;
|
||||
agent.action_state.timer = circle_time as f32;
|
||||
}
|
||||
controller.inputs.move_dir = move_dir;
|
||||
agent.action_timer += dt.0;
|
||||
} else if agent.action_timer < circle_time as f32 + 0.5 {
|
||||
agent.action_state.timer += dt.0;
|
||||
} else if agent.action_state.timer < circle_time as f32 + 0.5 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
agent.action_timer += dt.0;
|
||||
} else if agent.action_timer < 2.0 * circle_time as f32 + 0.5 {
|
||||
agent.action_state.timer += dt.0;
|
||||
} else if agent.action_state.timer < 2.0 * circle_time as f32 + 0.5 {
|
||||
let move_dir = (tgt_pos.0 - self.pos.0)
|
||||
.xy()
|
||||
.rotated_z(-0.47 * PI)
|
||||
@ -2159,20 +2164,20 @@ impl<'a> AgentData<'a> {
|
||||
.1
|
||||
.map_or(true, |b| b.is_some());
|
||||
if obstacle_right {
|
||||
agent.action_timer = 2.0 * circle_time as f32 + 0.5;
|
||||
agent.action_state.timer = 2.0 * circle_time as f32 + 0.5;
|
||||
}
|
||||
controller.inputs.move_dir = move_dir;
|
||||
agent.action_timer += dt.0;
|
||||
} else if agent.action_timer < 2.0 * circle_time as f32 + 1.0 {
|
||||
if agent.action_timer < 2.0 * circle_time as f32 {
|
||||
agent.action_timer = 2.0 * circle_time as f32;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else if agent.action_state.timer < 2.0 * circle_time as f32 + 1.0 {
|
||||
if agent.action_state.timer < 2.0 * circle_time as f32 {
|
||||
agent.action_state.timer = 2.0 * circle_time as f32;
|
||||
}
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else {
|
||||
agent.action_timer = 0.0;
|
||||
agent.action_state.timer = 0.0;
|
||||
}
|
||||
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
|
||||
if let Some((bearing, speed)) = agent.chaser.chase(
|
||||
@ -2215,16 +2220,16 @@ impl<'a> AgentData<'a> {
|
||||
},
|
||||
) {
|
||||
if can_see_tgt(&*terrain, self.pos, tgt_pos, dist_sqrd) && angle < 15.0 {
|
||||
if agent.action_timer > 5.0 {
|
||||
agent.action_timer = 0.0;
|
||||
} else if agent.action_timer > 2.5 {
|
||||
if agent.action_state.timer > 5.0 {
|
||||
agent.action_state.timer = 0.0;
|
||||
} else if agent.action_state.timer > 2.5 {
|
||||
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
|
||||
.xy()
|
||||
.rotated_z(1.75 * PI)
|
||||
.try_normalized()
|
||||
.unwrap_or_else(Vec2::zero)
|
||||
* speed;
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else {
|
||||
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
|
||||
.xy()
|
||||
@ -2232,7 +2237,7 @@ impl<'a> AgentData<'a> {
|
||||
.try_normalized()
|
||||
.unwrap_or_else(Vec2::zero)
|
||||
* speed;
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
}
|
||||
controller
|
||||
.actions
|
||||
@ -2254,21 +2259,21 @@ impl<'a> AgentData<'a> {
|
||||
},
|
||||
Tactic::TailSlap => {
|
||||
if dist_sqrd < (1.5 * min_attack_dist).powi(2) && angle < 90.0 {
|
||||
if agent.action_timer > 4.0 {
|
||||
if agent.action_state.timer > 4.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::CancelInput(InputKind::Primary));
|
||||
agent.action_timer = 0.0;
|
||||
} else if agent.action_timer > 1.0 {
|
||||
agent.action_state.timer = 0.0;
|
||||
} else if agent.action_state.timer > 1.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
}
|
||||
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
|
||||
.xy()
|
||||
@ -2336,18 +2341,18 @@ impl<'a> AgentData<'a> {
|
||||
Tactic::QuadLowBasic => {
|
||||
if dist_sqrd < (1.5 * min_attack_dist).powi(2) {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
if agent.action_timer > 5.0 {
|
||||
agent.action_timer = 0.0;
|
||||
} else if agent.action_timer > 2.0 && angle < 90.0 {
|
||||
if agent.action_state.timer > 5.0 {
|
||||
agent.action_state.timer = 0.0;
|
||||
} else if agent.action_state.timer > 2.0 && angle < 90.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else if angle < 90.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
}
|
||||
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
|
||||
if let Some((bearing, speed)) = agent.chaser.chase(
|
||||
@ -2410,18 +2415,18 @@ impl<'a> AgentData<'a> {
|
||||
Tactic::QuadMedBasic => {
|
||||
if dist_sqrd < min_attack_dist.powi(2) && angle < 90.0 {
|
||||
controller.inputs.move_dir = Vec2::zero();
|
||||
if agent.action_timer < 2.0 {
|
||||
if agent.action_state.timer < 2.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
agent.action_timer += dt.0;
|
||||
} else if agent.action_timer < 3.0 {
|
||||
agent.action_state.timer += dt.0;
|
||||
} else if agent.action_state.timer < 3.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else {
|
||||
agent.action_timer = 0.0;
|
||||
agent.action_state.timer = 0.0;
|
||||
}
|
||||
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
|
||||
if let Some((bearing, speed)) = agent.chaser.chase(
|
||||
@ -2450,7 +2455,7 @@ impl<'a> AgentData<'a> {
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
} else if dist_sqrd < (7.0 * min_attack_dist).powi(2) && angle < 15.0 {
|
||||
if agent.action_timer < 2.0 {
|
||||
if agent.action_state.timer < 2.0 {
|
||||
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
|
||||
.xy()
|
||||
.rotated_z(0.47 * PI)
|
||||
@ -2459,8 +2464,8 @@ impl<'a> AgentData<'a> {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
agent.action_timer += dt.0;
|
||||
} else if agent.action_timer < 4.0 && angle < 15.0 {
|
||||
agent.action_state.timer += dt.0;
|
||||
} else if agent.action_state.timer < 4.0 && angle < 15.0 {
|
||||
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
|
||||
.xy()
|
||||
.rotated_z(-0.47 * PI)
|
||||
@ -2469,14 +2474,14 @@ impl<'a> AgentData<'a> {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
agent.action_timer += dt.0;
|
||||
} else if agent.action_timer < 6.0 && angle < 15.0 {
|
||||
agent.action_state.timer += dt.0;
|
||||
} else if agent.action_state.timer < 6.0 && angle < 15.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else {
|
||||
agent.action_timer = 0.0;
|
||||
agent.action_state.timer = 0.0;
|
||||
}
|
||||
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
|
||||
if let Some((bearing, speed)) = agent.chaser.chase(
|
||||
@ -2563,21 +2568,20 @@ impl<'a> AgentData<'a> {
|
||||
const MINDFLAYER_ATTACK_DIST: f32 = 16.0;
|
||||
const MINION_SUMMON_THRESHOLD: f32 = 0.20;
|
||||
let health_fraction = self.health.map_or(0.5, |h| h.fraction());
|
||||
// Extreme hack to set action_timer at start of combat
|
||||
if agent.action_timer < MINION_SUMMON_THRESHOLD
|
||||
&& health_fraction > MINION_SUMMON_THRESHOLD
|
||||
{
|
||||
agent.action_timer = health_fraction - MINION_SUMMON_THRESHOLD;
|
||||
// Sets counter at start of combat
|
||||
if agent.action_state.condition {
|
||||
agent.action_state.counter = 1.0 - MINION_SUMMON_THRESHOLD;
|
||||
agent.action_state.condition = true;
|
||||
}
|
||||
let mindflayer_is_far = dist_sqrd > MINDFLAYER_ATTACK_DIST.powi(2);
|
||||
if agent.action_timer > health_fraction {
|
||||
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_timer -= MINION_SUMMON_THRESHOLD;
|
||||
agent.action_state.counter -= MINION_SUMMON_THRESHOLD;
|
||||
}
|
||||
} else if mindflayer_is_far {
|
||||
// If too far from target, blink to them.
|
||||
@ -2605,7 +2609,7 @@ impl<'a> AgentData<'a> {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
} else if thread_rng().gen_bool(health_fraction.into()) && angle < 30.0 {
|
||||
} else if thread_rng().gen_bool(health_fraction.into()) {
|
||||
// Else if at high health, use primary
|
||||
controller
|
||||
.actions
|
||||
@ -2866,18 +2870,92 @@ impl<'a> AgentData<'a> {
|
||||
self.jump_if(controller, bearing.z > 1.5);
|
||||
controller.inputs.move_z = bearing.z;
|
||||
}
|
||||
} else if self.energy.current() > 600 && agent.action_timer < 3.0 && angle < 15.0 {
|
||||
} else if self.energy.current() > 600
|
||||
&& agent.action_state.timer < 3.0
|
||||
&& angle < 15.0
|
||||
{
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
agent.action_timer += dt.0;
|
||||
} else if agent.action_timer < 6.0 && angle < 90.0 {
|
||||
agent.action_state.timer += dt.0;
|
||||
} else if agent.action_state.timer < 6.0 && angle < 90.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
agent.action_timer += dt.0;
|
||||
agent.action_state.timer += dt.0;
|
||||
} else {
|
||||
agent.action_timer = 0.0;
|
||||
agent.action_state.timer = 0.0;
|
||||
}
|
||||
},
|
||||
Tactic::Minotaur => {
|
||||
const MINOTAUR_FRENZY_THRESHOLD: f32 = 0.5;
|
||||
const MINOTAUR_ATTACK_RANGE: f32 = 5.0;
|
||||
const MINOTAUR_CHARGE_DISTANCE: f32 = 15.0;
|
||||
let minotaur_attack_distance =
|
||||
self.body.map_or(0.0, |b| b.radius()) + MINOTAUR_ATTACK_RANGE;
|
||||
let health_fraction = self.health.map_or(1.0, |h| h.fraction());
|
||||
// Sets action float at start of combat
|
||||
if agent.action_state.counter < MINOTAUR_FRENZY_THRESHOLD
|
||||
&& health_fraction > MINOTAUR_FRENZY_THRESHOLD
|
||||
{
|
||||
agent.action_state.counter = MINOTAUR_FRENZY_THRESHOLD;
|
||||
}
|
||||
if health_fraction < agent.action_state.counter {
|
||||
// Makes minotaur buff itself with frenzy
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(1)));
|
||||
if matches!(self.char_state, CharacterState::SelfBuff(c) if matches!(c.stage_section, StageSection::Recover))
|
||||
{
|
||||
agent.action_state.counter = 0.0;
|
||||
}
|
||||
} else if matches!(self.char_state, CharacterState::DashMelee(c) if !matches!(c.stage_section, StageSection::Recover))
|
||||
{
|
||||
// If already charging, keep charging if not in recover
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
} else if matches!(self.char_state, CharacterState::ChargedMelee(c) if matches!(c.stage_section, StageSection::Charge) && c.timer < c.static_data.charge_duration)
|
||||
{
|
||||
// If already charging a melee attack, keep charging it if charging
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
} else if dist_sqrd > MINOTAUR_CHARGE_DISTANCE.powi(2) {
|
||||
// Charges at target if they are far enough away
|
||||
if angle < 60.0 {
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
}
|
||||
} else if dist_sqrd < minotaur_attack_distance.powi(2) {
|
||||
if agent.action_state.condition && !self.char_state.is_attack() {
|
||||
// Cripple target if not just used cripple
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
agent.action_state.condition = false;
|
||||
} else if !self.char_state.is_attack() {
|
||||
// Cleave target if not just used cleave
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
agent.action_state.condition = true;
|
||||
}
|
||||
}
|
||||
// Make minotaur move towards target
|
||||
if let Some((bearing, speed)) = agent.chaser.chase(
|
||||
&*terrain,
|
||||
self.pos.0,
|
||||
self.vel.0,
|
||||
tgt_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;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -63,7 +63,8 @@ impl Animation for AlphaAnimation {
|
||||
let pullback = 1.0 - move3;
|
||||
let move1 = move1base * pullback;
|
||||
let move2 = move2base * pullback;
|
||||
|
||||
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(
|
||||
-s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
@ -79,7 +80,6 @@ impl Animation for AlphaAnimation {
|
||||
);
|
||||
next.shoulder_r.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + 0.6 * speednorm + (footrotl * -0.2) * speednorm);
|
||||
next.torso.orientation = Quaternion::rotation_z(0.0);
|
||||
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
@ -231,6 +231,38 @@ impl Animation for AlphaAnimation {
|
||||
* Quaternion::rotation_z(move1 * -0.5 + move2 * 0.6);
|
||||
next.head.orientation = Quaternion::rotation_x(move1 * 0.3);
|
||||
},
|
||||
"Minotaur" => {
|
||||
next.control_l.position = Vec3::new(0.0, 4.0, 5.0);
|
||||
next.control_r.position = Vec3::new(0.0, 4.0, 5.0);
|
||||
next.weapon_l.position = Vec3::new(
|
||||
-12.0 + move1 * -9.0 + move2 * 16.0,
|
||||
-6.0 + move2 * 8.0,
|
||||
-18.0 + move1 * 8.0 + move2 * -4.0,
|
||||
);
|
||||
next.weapon_r.position = Vec3::new(
|
||||
12.0 + move1 * 9.0 + move2 * -16.0,
|
||||
-6.0 + move2 * 8.0,
|
||||
-18.0 + move1 * 8.0 + move2 * -8.0,
|
||||
);
|
||||
|
||||
next.weapon_l.orientation = Quaternion::rotation_x(-1.67)
|
||||
* Quaternion::rotation_y(move1 * -0.3 + move2 * 1.0)
|
||||
* Quaternion::rotation_z(move1 * 0.8 + move2 * -1.8);
|
||||
next.weapon_r.orientation = Quaternion::rotation_x(-1.67)
|
||||
* Quaternion::rotation_y(move1 * 0.3 + move2 * -0.6)
|
||||
* Quaternion::rotation_z(move1 * -0.8 + move2 * 1.8);
|
||||
|
||||
next.control_l.orientation = Quaternion::rotation_x(1.57 + move2 * 1.0);
|
||||
next.control_r.orientation = Quaternion::rotation_x(1.57 + move2 * 1.0);
|
||||
|
||||
next.shoulder_l.orientation = Quaternion::rotation_x(-0.3)
|
||||
* Quaternion::rotation_y(move1 * 0.7 + move2 * -0.7);
|
||||
|
||||
next.shoulder_r.orientation = Quaternion::rotation_x(-0.3)
|
||||
* Quaternion::rotation_y(move1 * -0.7 + move2 * 0.7);
|
||||
next.head.orientation =
|
||||
Quaternion::rotation_x(move1 * -0.6 + move2 * 0.4)
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
159
voxygen/anim/src/biped_large/chargemelee.rs
Normal file
159
voxygen/anim/src/biped_large/chargemelee.rs
Normal file
@ -0,0 +1,159 @@
|
||||
use super::{
|
||||
super::{vek::*, Animation},
|
||||
BipedLargeSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::{
|
||||
comp::item::tool::{AbilitySpec, ToolKind},
|
||||
states::utils::StageSection,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct ChargeMeleeAnimation;
|
||||
|
||||
impl Animation for ChargeMeleeAnimation {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type Dependency<'a> = (
|
||||
(Option<ToolKind>, Option<&'a AbilitySpec>),
|
||||
(Option<ToolKind>, Option<&'a AbilitySpec>),
|
||||
Vec3<f32>,
|
||||
f32,
|
||||
Option<StageSection>,
|
||||
f32,
|
||||
);
|
||||
type Skeleton = BipedLargeSkeleton;
|
||||
|
||||
#[cfg(feature = "use-dyn-lib")]
|
||||
const UPDATE_FN: &'static [u8] = b"biped_large_chargemelee\0";
|
||||
|
||||
#[cfg_attr(feature = "be-dyn-lib", export_name = "biped_large_chargemelee")]
|
||||
#[allow(clippy::approx_constant)] // TODO: Pending review in #587
|
||||
fn update_skeleton_inner<'a>(
|
||||
skeleton: &Self::Skeleton,
|
||||
(
|
||||
(active_tool_kind, active_tool_spec),
|
||||
_second_tool,
|
||||
velocity,
|
||||
_global_time,
|
||||
stage_section,
|
||||
acc_vel,
|
||||
): Self::Dependency<'a>,
|
||||
anim_time: f32,
|
||||
rate: &mut f32,
|
||||
s_a: &SkeletonAttr,
|
||||
) -> Self::Skeleton {
|
||||
*rate = 1.0;
|
||||
let mut next = (*skeleton).clone();
|
||||
let speed = Vec2::<f32>::from(velocity).magnitude();
|
||||
|
||||
let lab: f32 = 0.65 * s_a.tempo;
|
||||
let speednorm = (speed / 12.0).powf(0.4);
|
||||
let foothoril = (acc_vel * lab + PI * 1.45).sin() * speednorm;
|
||||
let foothorir = (acc_vel * lab + PI * (0.45)).sin() * speednorm;
|
||||
let footrotl = ((1.0 / (0.5 + (0.5) * ((acc_vel * lab + PI * 1.4).sin()).powi(2))).sqrt())
|
||||
* ((acc_vel * lab + PI * 1.4).sin());
|
||||
|
||||
let footrotr = ((1.0 / (0.5 + (0.5) * ((acc_vel * lab + PI * 0.4).sin()).powi(2))).sqrt())
|
||||
* ((acc_vel * lab + PI * 0.4).sin());
|
||||
let (move1base, move2base, movement3, tension) = match stage_section {
|
||||
Some(StageSection::Charge) => (
|
||||
(anim_time.powf(0.25)).min(1.0),
|
||||
0.0,
|
||||
0.0,
|
||||
(anim_time * 100.0).sin(),
|
||||
),
|
||||
Some(StageSection::Swing) => (1.0, anim_time.powf(0.25), 0.0, 0.0),
|
||||
Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4), 0.0),
|
||||
_ => (0.0, 0.0, 0.0, 0.0),
|
||||
};
|
||||
|
||||
let pullback = 1.0 - movement3;
|
||||
let move1 = move1base * pullback;
|
||||
let move2 = move2base * pullback;
|
||||
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(
|
||||
-s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
s_a.shoulder.2 - foothorir * 1.0,
|
||||
);
|
||||
next.shoulder_l.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + 0.6 * speednorm + (footrotr * -0.2) * speednorm);
|
||||
|
||||
next.shoulder_r.position = Vec3::new(
|
||||
s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
s_a.shoulder.2 - foothoril * 1.0,
|
||||
);
|
||||
next.shoulder_r.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + 0.6 * speednorm + (footrotl * -0.2) * speednorm);
|
||||
next.torso.orientation = Quaternion::rotation_z(0.0);
|
||||
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.hand_l.position = Vec3::new(0.0, 0.0, s_a.grip.0);
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, s_a.grip.0);
|
||||
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
#[allow(clippy::single_match)]
|
||||
match active_tool_kind {
|
||||
Some(ToolKind::Natural) => {
|
||||
if let Some(AbilitySpec::Custom(spec)) = active_tool_spec {
|
||||
match spec.as_str() {
|
||||
"Minotaur" => {
|
||||
next.upper_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.3 + move2 * -0.9);
|
||||
next.lower_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * -0.3 + move2 * 0.9);
|
||||
next.head.orientation =
|
||||
Quaternion::rotation_x(move1 * -0.5 + move2 * 0.5);
|
||||
|
||||
next.control_l.position = Vec3::new(0.0, 4.0, 5.0);
|
||||
next.control_r.position = Vec3::new(0.0, 4.0, 5.0);
|
||||
next.weapon_l.position = Vec3::new(
|
||||
-12.0 + move2 * 5.0,
|
||||
-6.0 + move1 * 22.0 + move2 * 8.0,
|
||||
-18.0 + move1 * 16.0 + move2 * -19.0,
|
||||
);
|
||||
next.weapon_r.position = Vec3::new(
|
||||
12.0 + move2 * -5.0,
|
||||
-6.0 + move1 * 22.0 + move2 * 8.0,
|
||||
-18.0 + move1 * 14.0 + move2 * -19.0,
|
||||
);
|
||||
next.torso.position = Vec3::new(0.0, move2 * 1.5, 0.0);
|
||||
|
||||
next.weapon_l.orientation =
|
||||
Quaternion::rotation_x(
|
||||
-1.67 + move1 * 2.8 + tension * 0.03 + move2 * -2.3,
|
||||
) * Quaternion::rotation_y(move1 * 0.3 + move2 * 0.5);
|
||||
next.weapon_r.orientation = Quaternion::rotation_x(
|
||||
-1.67 + move1 * 1.6 + tension * -0.03 + move2 * -0.7,
|
||||
) * Quaternion::rotation_y(
|
||||
move1 * -0.3 + move2 * -0.5,
|
||||
) * Quaternion::rotation_z(0.0);
|
||||
|
||||
next.control_l.orientation =
|
||||
Quaternion::rotation_x(1.57 + move1 * 0.2 + move2 * 0.1);
|
||||
next.control_r.orientation =
|
||||
Quaternion::rotation_x(1.57 + move1 * 0.4 + move2 * -0.4);
|
||||
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0);
|
||||
next.shoulder_l.orientation =
|
||||
Quaternion::rotation_x(-0.3 + move1 * 1.0);
|
||||
|
||||
next.shoulder_r.orientation =
|
||||
Quaternion::rotation_x(-0.3 + move1 * 1.0);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
next
|
||||
}
|
||||
}
|
@ -2,15 +2,19 @@ use super::{
|
||||
super::{vek::*, Animation},
|
||||
BipedLargeSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::{comp::item::ToolKind, states::utils::StageSection};
|
||||
use common::{
|
||||
comp::item::tool::{AbilitySpec, ToolKind},
|
||||
states::utils::StageSection,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct DashAnimation;
|
||||
|
||||
impl Animation for DashAnimation {
|
||||
#[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 DashAnimation {
|
||||
#[allow(clippy::single_match)] // 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,
|
||||
velocity,
|
||||
_global_time,
|
||||
stage_section,
|
||||
acc_vel,
|
||||
): Self::Dependency<'a>,
|
||||
anim_time: f32,
|
||||
rate: &mut f32,
|
||||
s_a: &SkeletonAttr,
|
||||
@ -49,7 +60,8 @@ impl Animation for DashAnimation {
|
||||
|
||||
next.hand_l.position = Vec3::new(0.0, 0.0, s_a.grip.0);
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, s_a.grip.0);
|
||||
|
||||
next.second.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
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 {
|
||||
@ -136,6 +148,51 @@ impl Animation for DashAnimation {
|
||||
* Quaternion::rotation_y(-1.8 + move1 * -0.2 + move3 * -0.2)
|
||||
* Quaternion::rotation_z(move1 * -0.8 + move3 * -0.1);
|
||||
},
|
||||
Some(ToolKind::Natural) => {
|
||||
if let Some(AbilitySpec::Custom(spec)) = active_tool_spec {
|
||||
match spec.as_str() {
|
||||
"Minotaur" => {
|
||||
next.head.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.4 + move3 * 0.5)
|
||||
* Quaternion::rotation_z(move1 * -0.3 + move3 * -0.3);
|
||||
next.upper_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * -0.4 + move3 * 0.9)
|
||||
* Quaternion::rotation_z(move1 * 0.6 + move3 * -1.5);
|
||||
next.lower_torso.orientation =
|
||||
Quaternion::rotation_y(move1 * -0.2 + move3 * -0.1)
|
||||
* Quaternion::rotation_x(
|
||||
move1 * 0.4 + move3 * -0.7 + footrotr * 0.1,
|
||||
)
|
||||
* Quaternion::rotation_z(move1 * -0.6 + move3 * 1.6);
|
||||
next.control_l.position = Vec3::new(0.0, 4.0, 5.0);
|
||||
next.control_r.position = Vec3::new(0.0, 4.0, 5.0);
|
||||
next.weapon_l.position = Vec3::new(-12.0 + move1 * -3.0, -6.0, -18.0);
|
||||
next.weapon_r.position = Vec3::new(
|
||||
12.0 + move1 * -3.0,
|
||||
-6.0 + move1 * 2.0,
|
||||
-18.0 + move1 * 2.0,
|
||||
);
|
||||
|
||||
next.weapon_l.orientation = Quaternion::rotation_x(-1.67 + move1 * 0.4)
|
||||
* Quaternion::rotation_y(move1 * 0.4 + move2 * 0.2)
|
||||
* Quaternion::rotation_z(move3 * -0.5);
|
||||
next.weapon_r.orientation = Quaternion::rotation_x(-1.67 + move1 * 0.3)
|
||||
* Quaternion::rotation_y(move1 * 0.6 + move2 * -0.6)
|
||||
* Quaternion::rotation_z(move3 * -0.5);
|
||||
|
||||
next.control_l.orientation = Quaternion::rotation_x(1.57);
|
||||
next.control_r.orientation = Quaternion::rotation_x(1.57);
|
||||
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0);
|
||||
next.shoulder_l.orientation = Quaternion::rotation_x(-0.3);
|
||||
|
||||
next.shoulder_r.orientation = Quaternion::rotation_x(-0.3);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
|
@ -101,8 +101,11 @@ impl Animation for IdleAnimation {
|
||||
next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57);
|
||||
},
|
||||
Some(ToolKind::Hammer) | Some(ToolKind::Axe) => {
|
||||
next.main.position = Vec3::new(-10.0, -8.0, 12.0);
|
||||
next.main.position = Vec3::new(-6.0, -8.0, 8.0);
|
||||
next.main.orientation = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57);
|
||||
next.second.position = Vec3::new(6.0, -8.0, 8.0);
|
||||
next.second.orientation =
|
||||
Quaternion::rotation_y(-2.5) * Quaternion::rotation_z(1.57);
|
||||
},
|
||||
_ => {
|
||||
next.main.position = Vec3::new(-2.0, -5.0, -6.0);
|
||||
|
@ -28,6 +28,7 @@ impl Animation for JumpAnimation {
|
||||
let torso = (anim_time * lab + 1.5 * PI).sin();
|
||||
|
||||
let wave_slow = (anim_time * 0.8).sin();
|
||||
next.hold.scale = Vec3::one() * 0.0;
|
||||
|
||||
next.head.scale = Vec3::one() * 1.02;
|
||||
|
||||
@ -57,7 +58,7 @@ impl Animation for JumpAnimation {
|
||||
next.second.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.second.orientation =
|
||||
Quaternion::rotation_x(PI) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0);
|
||||
next.second.scale = Vec3::one() * 0.0;
|
||||
next.second.scale = Vec3::one() * 1.0;
|
||||
|
||||
match active_tool_kind {
|
||||
Some(ToolKind::Bow) => {
|
||||
|
@ -3,12 +3,14 @@ pub mod beam;
|
||||
pub mod beta;
|
||||
pub mod blink;
|
||||
pub mod charge;
|
||||
pub mod chargemelee;
|
||||
pub mod dash;
|
||||
pub mod equip;
|
||||
pub mod idle;
|
||||
pub mod jump;
|
||||
pub mod leapmelee;
|
||||
pub mod run;
|
||||
pub mod selfbuff;
|
||||
pub mod shockwave;
|
||||
pub mod shoot;
|
||||
pub mod spin;
|
||||
@ -20,11 +22,11 @@ pub mod wield;
|
||||
// Reexports
|
||||
pub use self::{
|
||||
alpha::AlphaAnimation, beam::BeamAnimation, beta::BetaAnimation, blink::BlinkAnimation,
|
||||
charge::ChargeAnimation, dash::DashAnimation, equip::EquipAnimation, idle::IdleAnimation,
|
||||
jump::JumpAnimation, leapmelee::LeapAnimation, run::RunAnimation,
|
||||
shockwave::ShockwaveAnimation, shoot::ShootAnimation, spin::SpinAnimation,
|
||||
spinmelee::SpinMeleeAnimation, stunned::StunnedAnimation, summon::SummonAnimation,
|
||||
wield::WieldAnimation,
|
||||
charge::ChargeAnimation, chargemelee::ChargeMeleeAnimation, dash::DashAnimation,
|
||||
equip::EquipAnimation, idle::IdleAnimation, jump::JumpAnimation, leapmelee::LeapAnimation,
|
||||
run::RunAnimation, selfbuff::SelfBuffAnimation, shockwave::ShockwaveAnimation,
|
||||
shoot::ShootAnimation, spin::SpinAnimation, spinmelee::SpinMeleeAnimation,
|
||||
stunned::StunnedAnimation, summon::SummonAnimation, wield::WieldAnimation,
|
||||
};
|
||||
|
||||
use super::{make_bone, vek::*, FigureBoneData, Skeleton};
|
||||
@ -54,6 +56,8 @@ skeleton_impls!(struct BipedLargeSkeleton {
|
||||
control,
|
||||
control_l,
|
||||
control_r,
|
||||
weapon_l,
|
||||
weapon_r,
|
||||
leg_control_l,
|
||||
leg_control_r,
|
||||
arm_control_l,
|
||||
@ -78,8 +82,11 @@ impl Skeleton for BipedLargeSkeleton {
|
||||
|
||||
let torso_mat = base_mat * Mat4::<f32>::from(self.torso);
|
||||
let upper_torso_mat = torso_mat * upper_torso;
|
||||
let control_mat = Mat4::<f32>::from(self.control);
|
||||
let control_l_mat = Mat4::<f32>::from(self.control_l);
|
||||
let control_r_mat = Mat4::<f32>::from(self.control_r);
|
||||
let weapon_l_mat = control_mat * Mat4::<f32>::from(self.weapon_l);
|
||||
let weapon_r_mat = control_mat * Mat4::<f32>::from(self.weapon_r);
|
||||
let lower_torso_mat = upper_torso_mat * Mat4::<f32>::from(self.lower_torso);
|
||||
|
||||
let leg_l = Mat4::<f32>::from(self.leg_l);
|
||||
@ -92,8 +99,6 @@ impl Skeleton for BipedLargeSkeleton {
|
||||
let arm_control_r = upper_torso_mat * Mat4::<f32>::from(self.arm_control_r);
|
||||
|
||||
let head_mat = upper_torso_mat * Mat4::<f32>::from(self.head);
|
||||
let control_mat = Mat4::<f32>::from(self.control);
|
||||
|
||||
let hand_l_mat = Mat4::<f32>::from(self.hand_l);
|
||||
|
||||
*(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [
|
||||
@ -102,12 +107,16 @@ impl Skeleton for BipedLargeSkeleton {
|
||||
make_bone(upper_torso_mat),
|
||||
make_bone(lower_torso_mat),
|
||||
make_bone(lower_torso_mat * Mat4::<f32>::from(self.tail)),
|
||||
make_bone(upper_torso_mat * control_mat * Mat4::<f32>::from(self.main)),
|
||||
make_bone(upper_torso_mat * control_mat * Mat4::<f32>::from(self.second)),
|
||||
make_bone(upper_torso_mat * weapon_l_mat * Mat4::<f32>::from(self.main)),
|
||||
make_bone(upper_torso_mat * weapon_r_mat * Mat4::<f32>::from(self.second)),
|
||||
make_bone(arm_control_l * Mat4::<f32>::from(self.shoulder_l)),
|
||||
make_bone(arm_control_r * Mat4::<f32>::from(self.shoulder_r)),
|
||||
make_bone(arm_control_l * control_mat * control_l_mat * Mat4::<f32>::from(self.hand_l)),
|
||||
make_bone(arm_control_r * control_mat * control_r_mat * Mat4::<f32>::from(self.hand_r)),
|
||||
make_bone(
|
||||
arm_control_l * weapon_l_mat * control_l_mat * Mat4::<f32>::from(self.hand_l),
|
||||
),
|
||||
make_bone(
|
||||
arm_control_r * weapon_r_mat * control_r_mat * Mat4::<f32>::from(self.hand_r),
|
||||
),
|
||||
make_bone(leg_control_l * leg_l),
|
||||
make_bone(leg_control_r * leg_r),
|
||||
make_bone(leg_control_l * Mat4::<f32>::from(self.foot_l)),
|
||||
|
@ -271,12 +271,12 @@ impl Animation for RunAnimation {
|
||||
s_a.upper_torso.1 + shortalt * -1.5 * speednorm,
|
||||
);
|
||||
next.upper_torso.orientation =
|
||||
Quaternion::rotation_z(short * 0.18 * speednorm + tilt * -1.0)
|
||||
Quaternion::rotation_z(short * 0.07 * speednorm + tilt * -1.0)
|
||||
* Quaternion::rotation_y(tilt);
|
||||
|
||||
next.lower_torso.position = Vec3::new(0.0, s_a.lower_torso.0, s_a.lower_torso.1);
|
||||
next.lower_torso.orientation =
|
||||
Quaternion::rotation_z(short * 0.15 * speednorm + tilt * 0.5)
|
||||
Quaternion::rotation_z(short * 0.05 * speednorm + tilt * 0.5)
|
||||
* Quaternion::rotation_y(tilt * -0.5)
|
||||
* Quaternion::rotation_x(0.14 * speednorm);
|
||||
|
||||
@ -287,7 +287,7 @@ impl Animation for RunAnimation {
|
||||
next.tail.orientation = Quaternion::rotation_x(shortalt * 0.3 * speednorm);
|
||||
|
||||
next.second.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.second.orientation = Quaternion::rotation_x(PI)
|
||||
next.second.orientation = Quaternion::rotation_x(0.0)
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
|
||||
@ -308,9 +308,12 @@ impl Animation for RunAnimation {
|
||||
Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57);
|
||||
},
|
||||
Some(ToolKind::Hammer) | Some(ToolKind::Axe) => {
|
||||
next.main.position = Vec3::new(-10.0, -8.0, 12.0);
|
||||
next.main.position = Vec3::new(-6.0, -8.0, 8.0);
|
||||
next.main.orientation =
|
||||
Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57);
|
||||
next.second.position = Vec3::new(6.0, -8.0, 8.0);
|
||||
next.second.orientation =
|
||||
Quaternion::rotation_y(-2.5) * Quaternion::rotation_z(1.57);
|
||||
},
|
||||
_ => {
|
||||
next.main.position = Vec3::new(-2.0, -5.0, -6.0);
|
||||
|
163
voxygen/anim/src/biped_large/selfbuff.rs
Normal file
163
voxygen/anim/src/biped_large/selfbuff.rs
Normal file
@ -0,0 +1,163 @@
|
||||
use super::{
|
||||
super::{vek::*, Animation},
|
||||
BipedLargeSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::{
|
||||
comp::item::tool::{AbilitySpec, ToolKind},
|
||||
states::utils::StageSection,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct SelfBuffAnimation;
|
||||
|
||||
impl Animation for SelfBuffAnimation {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type Dependency<'a> = (
|
||||
(Option<ToolKind>, Option<&'a AbilitySpec>),
|
||||
(Option<ToolKind>, Option<&'a AbilitySpec>),
|
||||
Vec3<f32>,
|
||||
f32,
|
||||
Option<StageSection>,
|
||||
f32,
|
||||
);
|
||||
type Skeleton = BipedLargeSkeleton;
|
||||
|
||||
#[cfg(feature = "use-dyn-lib")]
|
||||
const UPDATE_FN: &'static [u8] = b"biped_large_selfbuff\0";
|
||||
|
||||
#[cfg_attr(feature = "be-dyn-lib", export_name = "biped_large_selfbuff")]
|
||||
fn update_skeleton_inner<'a>(
|
||||
skeleton: &Self::Skeleton,
|
||||
(
|
||||
(active_tool_kind, active_tool_spec),
|
||||
_second_tool,
|
||||
velocity,
|
||||
_global_time,
|
||||
stage_section,
|
||||
acc_vel,
|
||||
): Self::Dependency<'a>,
|
||||
anim_time: f32,
|
||||
rate: &mut f32,
|
||||
s_a: &SkeletonAttr,
|
||||
) -> Self::Skeleton {
|
||||
*rate = 1.0;
|
||||
let mut next = (*skeleton).clone();
|
||||
let speed = Vec2::<f32>::from(velocity).magnitude();
|
||||
|
||||
let lab: f32 = 0.65 * s_a.tempo;
|
||||
let speednorm = (speed / 12.0).powf(0.4);
|
||||
let foothoril = (acc_vel * lab + PI * 1.45).sin() * speednorm;
|
||||
let foothorir = (acc_vel * lab + PI * (0.45)).sin() * speednorm;
|
||||
let footrotl = ((1.0 / (0.5 + (0.5) * ((acc_vel * lab + PI * 1.4).sin()).powi(2))).sqrt())
|
||||
* ((acc_vel * lab + PI * 1.4).sin());
|
||||
|
||||
let footrotr = ((1.0 / (0.5 + (0.5) * ((acc_vel * lab + PI * 0.4).sin()).powi(2))).sqrt())
|
||||
* ((acc_vel * lab + PI * 0.4).sin());
|
||||
let (move1base, movement3, tensionbase, tension2base) = match stage_section {
|
||||
Some(StageSection::Buildup) => (
|
||||
(anim_time.powf(0.25)).min(1.0),
|
||||
0.0,
|
||||
(anim_time * 10.0).sin(),
|
||||
0.0,
|
||||
),
|
||||
Some(StageSection::Cast) => {
|
||||
(1.0, 0.0, (anim_time * 30.0).sin(), (anim_time * 12.0).sin())
|
||||
},
|
||||
Some(StageSection::Recover) => (1.0, anim_time.powi(4), 1.0, 1.0),
|
||||
_ => (0.0, 0.0, 0.0, 0.0),
|
||||
};
|
||||
|
||||
let pullback = 1.0 - movement3;
|
||||
let move1 = move1base * pullback;
|
||||
let tension = tensionbase * pullback;
|
||||
let tension2 = tension2base * pullback;
|
||||
|
||||
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(
|
||||
-s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
s_a.shoulder.2 - foothorir * 1.0,
|
||||
);
|
||||
next.shoulder_l.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + 0.6 * speednorm + (footrotr * -0.2) * speednorm);
|
||||
|
||||
next.shoulder_r.position = Vec3::new(
|
||||
s_a.shoulder.0,
|
||||
s_a.shoulder.1,
|
||||
s_a.shoulder.2 - foothoril * 1.0,
|
||||
);
|
||||
next.shoulder_r.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + 0.6 * speednorm + (footrotl * -0.2) * speednorm);
|
||||
next.torso.orientation = Quaternion::rotation_z(0.0);
|
||||
|
||||
next.main.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.main.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
next.hand_l.position = Vec3::new(0.0, 0.0, s_a.grip.0);
|
||||
next.hand_r.position = Vec3::new(0.0, 0.0, s_a.grip.0);
|
||||
|
||||
next.hand_l.orientation = Quaternion::rotation_x(0.0);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
// TODO: Remove clippy allow when second species is added
|
||||
#[allow(clippy::single_match)]
|
||||
match active_tool_kind {
|
||||
Some(ToolKind::Natural) => {
|
||||
if let Some(AbilitySpec::Custom(spec)) = active_tool_spec {
|
||||
match spec.as_str() {
|
||||
"Minotaur" => {
|
||||
next.upper_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * -0.1 + tension2 * 0.05);
|
||||
next.lower_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.1 + tension2 * -0.05);
|
||||
|
||||
next.head.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + tension2 * -0.1)
|
||||
* Quaternion::rotation_y(tension2 * -0.1);
|
||||
|
||||
next.control_l.position = Vec3::new(0.0, 4.0, 5.0);
|
||||
next.control_r.position = Vec3::new(0.0, 4.0, 5.0);
|
||||
next.weapon_l.position = Vec3::new(
|
||||
-12.0 + move1 * -15.0,
|
||||
-6.0 + move1 * 13.0,
|
||||
-18.0 + move1 * 16.0 + tension2 * 3.0,
|
||||
);
|
||||
next.weapon_r.position = Vec3::new(
|
||||
12.0 + move1 * 1.0,
|
||||
-6.0 + move1 * 7.0 + tension * 0.3,
|
||||
-18.0 + move1 * -2.0,
|
||||
);
|
||||
|
||||
next.weapon_l.orientation = Quaternion::rotation_x(-1.67 + move1 * 1.9)
|
||||
* Quaternion::rotation_y(move1 * 0.25 + tension2 * 0.06)
|
||||
* Quaternion::rotation_z(move1 * 1.3);
|
||||
next.weapon_r.orientation = Quaternion::rotation_x(-1.67 + move1 * 0.8)
|
||||
* Quaternion::rotation_y(move1 * -0.85 + tension * 0.12)
|
||||
* Quaternion::rotation_z(move1 * 0.7);
|
||||
|
||||
next.control_l.orientation = Quaternion::rotation_x(1.57 + move1 * 0.1)
|
||||
* Quaternion::rotation_y(0.0);
|
||||
next.control_r.orientation = Quaternion::rotation_x(1.57 + move1 * 0.1)
|
||||
* Quaternion::rotation_y(0.0);
|
||||
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0);
|
||||
next.shoulder_l.orientation =
|
||||
Quaternion::rotation_x(-0.3 + move1 * 2.2 + tension2 * 0.17)
|
||||
* Quaternion::rotation_y(move1 * 0.95);
|
||||
|
||||
next.shoulder_r.orientation =
|
||||
Quaternion::rotation_x(-0.3 + move1 * 0.1)
|
||||
* Quaternion::rotation_y(move1 * -0.35);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
next
|
||||
}
|
||||
}
|
@ -65,7 +65,8 @@ impl Animation for StunnedAnimation {
|
||||
let short = (acc_vel * lab).sin() * speednorm;
|
||||
|
||||
let shortalt = (anim_time * lab * 16.0 + PI / 2.0).sin();
|
||||
|
||||
next.second.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.second.orientation = Quaternion::rotation_x(0.0);
|
||||
if s_a.beast {
|
||||
next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1);
|
||||
} else {
|
||||
@ -391,6 +392,24 @@ impl Animation for StunnedAnimation {
|
||||
next.torso.orientation = Quaternion::rotation_x(-0.25);
|
||||
}
|
||||
},
|
||||
"Minotaur" => {
|
||||
next.control_l.position = Vec3::new(0.0, 4.0, 5.0);
|
||||
next.control_r.position = Vec3::new(0.0, 4.0, 5.0);
|
||||
next.weapon_l.position = Vec3::new(-12.0, -6.0, -18.0);
|
||||
next.weapon_r.position = Vec3::new(12.0, -6.0, -18.0);
|
||||
|
||||
next.weapon_l.orientation = Quaternion::rotation_x(-1.57 - 0.1);
|
||||
next.weapon_r.orientation = Quaternion::rotation_x(-1.57 - 0.1);
|
||||
|
||||
next.control_l.orientation = Quaternion::rotation_x(1.57);
|
||||
next.control_r.orientation = Quaternion::rotation_x(1.57);
|
||||
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0);
|
||||
next.shoulder_l.orientation = Quaternion::rotation_x(-0.3);
|
||||
|
||||
next.shoulder_r.orientation = Quaternion::rotation_x(-0.3);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +91,8 @@ impl Animation for WieldAnimation {
|
||||
let short = (acc_vel * lab).sin() * speednorm;
|
||||
|
||||
let shortalt = (anim_time * lab * 16.0 + PI / 2.0).sin();
|
||||
next.second.position = Vec3::new(0.0, 0.0, 0.0);
|
||||
next.second.orientation = Quaternion::rotation_x(0.0);
|
||||
|
||||
if s_a.beast {
|
||||
next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1);
|
||||
@ -169,12 +171,12 @@ impl Animation for WieldAnimation {
|
||||
5.0 + s_a.grip.0 / 1.2,
|
||||
-4.0 + -s_a.grip.0 / 2.0 + short * -1.5,
|
||||
);
|
||||
next.second.scale = Vec3::one() * 0.0;
|
||||
|
||||
next.control_l.orientation =
|
||||
Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_y(-0.2);
|
||||
next.control_r.orientation = Quaternion::rotation_x(PI / 2.2)
|
||||
* Quaternion::rotation_y(0.2)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.control_r.orientation =
|
||||
Quaternion::rotation_x(PI / 2.2) * Quaternion::rotation_y(0.2);
|
||||
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(-0.2 + short * 0.2) * Quaternion::rotation_y(-0.1);
|
||||
@ -430,6 +432,24 @@ impl Animation for WieldAnimation {
|
||||
next.torso.orientation = Quaternion::rotation_x(-0.25);
|
||||
}
|
||||
},
|
||||
"Minotaur" => {
|
||||
next.control_l.position = Vec3::new(0.0, 4.0, 5.0);
|
||||
next.control_r.position = Vec3::new(0.0, 4.0, 5.0);
|
||||
next.weapon_l.position = Vec3::new(-12.0, -6.0, -18.0);
|
||||
next.weapon_r.position = Vec3::new(12.0, -6.0, -18.0);
|
||||
|
||||
next.weapon_l.orientation = Quaternion::rotation_x(-1.57 - 0.1);
|
||||
next.weapon_r.orientation = Quaternion::rotation_x(-1.57 - 0.1);
|
||||
|
||||
next.control_l.orientation = Quaternion::rotation_x(1.57);
|
||||
next.control_r.orientation = Quaternion::rotation_x(1.57);
|
||||
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0);
|
||||
next.shoulder_l.orientation = Quaternion::rotation_x(-0.3);
|
||||
|
||||
next.shoulder_r.orientation = Quaternion::rotation_x(-0.3);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,7 @@ fn maps_basic_melee() {
|
||||
range: 1.0,
|
||||
max_angle: 1.0,
|
||||
ability_info: empty_ability_info(),
|
||||
damage_effect: None,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: states::utils::StageSection::Buildup,
|
||||
|
@ -181,6 +181,7 @@ pub enum SfxEvent {
|
||||
FireShot,
|
||||
FlameThrower,
|
||||
PoiseChange(PoiseState),
|
||||
GroundSlam,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
|
||||
@ -325,6 +326,10 @@ impl SfxMgr {
|
||||
false,
|
||||
);
|
||||
},
|
||||
Outcome::GroundSlam { pos, .. } => {
|
||||
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::GroundSlam);
|
||||
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.0), false);
|
||||
},
|
||||
Outcome::ProjectileShot { pos, body, .. } => {
|
||||
match body {
|
||||
Body::Object(
|
||||
|
@ -628,6 +628,7 @@ fn insert_killing_buff(buff: BuffKind, localized_strings: &Localization, templat
|
||||
BuffKind::Burning => localized_strings.get("hud.outcome.burning"),
|
||||
BuffKind::Bleeding => localized_strings.get("hud.outcome.bleeding"),
|
||||
BuffKind::Cursed => localized_strings.get("hud.outcome.curse"),
|
||||
BuffKind::Crippled => localized_strings.get("hud.outcome.crippled"),
|
||||
BuffKind::Regeneration
|
||||
| BuffKind::Saturation
|
||||
| BuffKind::Potion
|
||||
@ -635,7 +636,8 @@ fn insert_killing_buff(buff: BuffKind, localized_strings: &Localization, templat
|
||||
| BuffKind::IncreaseMaxEnergy
|
||||
| BuffKind::IncreaseMaxHealth
|
||||
| BuffKind::Invulnerability
|
||||
| BuffKind::ProtectingWard => {
|
||||
| BuffKind::ProtectingWard
|
||||
| BuffKind::Frenzied => {
|
||||
tracing::error!("Player was killed by a positive buff!");
|
||||
localized_strings.get("hud.outcome.mysterious")
|
||||
},
|
||||
|
@ -569,11 +569,13 @@ image_ids! {
|
||||
buff_healthplus_0: "voxygen.element.de_buffs.buff_healthplus_0",
|
||||
buff_invincibility_0: "voxygen.element.de_buffs.buff_invincibility_0",
|
||||
buff_dmg_red_0: "voxygen.element.de_buffs.buff_damage_reduce_0",
|
||||
buff_frenzy_0: "voxygen.element.de_buffs.buff_frenzy_0",
|
||||
|
||||
// Debuffs
|
||||
debuff_skull_0: "voxygen.element.de_buffs.debuff_skull_0",
|
||||
debuff_bleed_0: "voxygen.element.de_buffs.debuff_bleed_0",
|
||||
debuff_burning_0: "voxygen.element.de_buffs.debuff_burning_0",
|
||||
debuff_crippled_0: "voxygen.element.de_buffs.debuff_cripple_0",
|
||||
|
||||
// Animation Frames
|
||||
// Buff Frame
|
||||
|
@ -3622,10 +3622,12 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id {
|
||||
BuffKind::IncreaseMaxHealth { .. } => imgs.buff_healthplus_0,
|
||||
BuffKind::Invulnerability => imgs.buff_invincibility_0,
|
||||
BuffKind::ProtectingWard => imgs.buff_dmg_red_0,
|
||||
BuffKind::Frenzied { .. } => imgs.buff_frenzy_0,
|
||||
// Debuffs
|
||||
BuffKind::Bleeding { .. } => imgs.debuff_bleed_0,
|
||||
BuffKind::Cursed { .. } => imgs.debuff_skull_0,
|
||||
BuffKind::Burning { .. } => imgs.debuff_burning_0,
|
||||
BuffKind::Crippled { .. } => imgs.debuff_crippled_0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -3640,10 +3642,12 @@ pub fn get_buff_title(buff: BuffKind, localized_strings: &Localization) -> &str
|
||||
BuffKind::IncreaseMaxEnergy { .. } => localized_strings.get("buff.title.staminaup"),
|
||||
BuffKind::Invulnerability => localized_strings.get("buff.title.invulnerability"),
|
||||
BuffKind::ProtectingWard => localized_strings.get("buff.title.protectingward"),
|
||||
BuffKind::Frenzied => localized_strings.get("buff.title.frenzied"),
|
||||
// Debuffs
|
||||
BuffKind::Bleeding { .. } => localized_strings.get("buff.title.bleed"),
|
||||
BuffKind::Cursed { .. } => localized_strings.get("buff.title.cursed"),
|
||||
BuffKind::Burning { .. } => localized_strings.get("buff.title.burn"),
|
||||
BuffKind::Crippled { .. } => localized_strings.get("buff.title.crippled"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -3670,10 +3674,12 @@ pub fn get_buff_desc(buff: BuffKind, data: BuffData, localized_strings: &Localiz
|
||||
BuffKind::ProtectingWard => {
|
||||
Cow::Borrowed(localized_strings.get("buff.desc.protectingward"))
|
||||
},
|
||||
BuffKind::Frenzied => Cow::Borrowed(localized_strings.get("buff.desc.frenzied")),
|
||||
// Debuffs
|
||||
BuffKind::Bleeding { .. } => Cow::Borrowed(localized_strings.get("buff.desc.bleed")),
|
||||
BuffKind::Cursed { .. } => Cow::Borrowed(localized_strings.get("buff.desc.cursed")),
|
||||
BuffKind::Burning { .. } => Cow::Borrowed(localized_strings.get("buff.desc.burn")),
|
||||
BuffKind::Crippled { .. } => Cow::Borrowed(localized_strings.get("buff.desc.crippled")),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,9 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> String {
|
||||
| BuffKind::Burning
|
||||
| BuffKind::CampfireHeal
|
||||
| BuffKind::Cursed
|
||||
| BuffKind::ProtectingWard => continue,
|
||||
| BuffKind::ProtectingWard
|
||||
| BuffKind::Crippled
|
||||
| BuffKind::Frenzied => continue,
|
||||
};
|
||||
|
||||
write!(&mut description, "{}", buff_desc).unwrap();
|
||||
@ -138,7 +140,9 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> String {
|
||||
| BuffKind::Potion
|
||||
| BuffKind::CampfireHeal
|
||||
| BuffKind::Cursed
|
||||
| BuffKind::ProtectingWard => continue,
|
||||
| BuffKind::ProtectingWard
|
||||
| BuffKind::Crippled
|
||||
| BuffKind::Frenzied => continue,
|
||||
}
|
||||
} else if let BuffKind::Saturation | BuffKind::Regeneration = buff.kind {
|
||||
i18n.get("buff.text.every_second").to_string()
|
||||
|
@ -122,6 +122,8 @@ pub enum ParticleMode {
|
||||
CultistFlame = 23,
|
||||
StaticSmoke = 24,
|
||||
Blood = 25,
|
||||
Enraged = 26,
|
||||
BigShrapnel = 27,
|
||||
}
|
||||
|
||||
impl ParticleMode {
|
||||
|
@ -3495,7 +3495,6 @@ struct SidedBLCentralVoxSpec {
|
||||
torso_upper: BipedLargeCentralSubSpec,
|
||||
torso_lower: BipedLargeCentralSubSpec,
|
||||
tail: BipedLargeCentralSubSpec,
|
||||
second: BipedLargeCentralSubSpec,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
struct BipedLargeCentralSubSpec {
|
||||
@ -3523,13 +3522,16 @@ struct BipedLargeLateralSubSpec {
|
||||
lateral: VoxSimple,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
struct BipedLargeWeaponSpec(HashMap<String, ArmorVoxSpec>);
|
||||
struct BipedLargeMainSpec(HashMap<String, ArmorVoxSpec>);
|
||||
#[derive(Deserialize)]
|
||||
struct BipedLargeSecondSpec(HashMap<String, ArmorVoxSpec>);
|
||||
make_vox_spec!(
|
||||
biped_large::Body,
|
||||
struct BipedLargeSpec {
|
||||
central: BipedLargeCentralSpec = "voxygen.voxel.biped_large_central_manifest",
|
||||
lateral: BipedLargeLateralSpec = "voxygen.voxel.biped_large_lateral_manifest",
|
||||
weapon: BipedLargeWeaponSpec = "voxygen.voxel.biped_weapon_manifest",
|
||||
main: BipedLargeMainSpec = "voxygen.voxel.biped_weapon_manifest",
|
||||
second: BipedLargeSecondSpec = "voxygen.voxel.biped_weapon_manifest",
|
||||
},
|
||||
|FigureKey { body, extra }, spec| {
|
||||
const DEFAULT_LOADOUT: super::cache::CharacterCacheKey = super::cache::CharacterCacheKey {
|
||||
@ -3567,15 +3569,17 @@ make_vox_spec!(
|
||||
body.body_type,
|
||||
)),
|
||||
tool.and_then(|tool| tool.active.as_ref()).map(|tool| {
|
||||
spec.weapon.read().0.mesh_main(
|
||||
spec.main.read().0.mesh_main(
|
||||
&tool.name,
|
||||
false,
|
||||
)
|
||||
}),
|
||||
tool.and_then(|tool| tool.active.as_ref()).map(|tool| {
|
||||
spec.second.read().0.mesh_second(
|
||||
&tool.name,
|
||||
false,
|
||||
)
|
||||
}),
|
||||
Some(spec.central.read().0.mesh_second(
|
||||
body.species,
|
||||
body.body_type,
|
||||
)),
|
||||
Some(spec.lateral.read().0.mesh_shoulder_l(
|
||||
body.species,
|
||||
body.body_type,
|
||||
@ -3693,22 +3697,6 @@ impl BipedLargeCentralSpec {
|
||||
|
||||
(central, Vec3::from(spec.tail.offset))
|
||||
}
|
||||
|
||||
fn mesh_second(&self, species: BLSpecies, body_type: BLBodyType) -> BoneMeshes {
|
||||
let spec = match self.0.get(&(species, body_type)) {
|
||||
Some(spec) => spec,
|
||||
None => {
|
||||
error!(
|
||||
"No second weapon specification exists for the combination of {:?} and {:?}",
|
||||
species, body_type
|
||||
);
|
||||
return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5));
|
||||
},
|
||||
};
|
||||
let central = graceful_load_segment(&spec.second.central.0);
|
||||
|
||||
(central, Vec3::from(spec.second.offset))
|
||||
}
|
||||
}
|
||||
impl BipedLargeLateralSpec {
|
||||
fn mesh_shoulder_l(&self, species: BLSpecies, body_type: BLBodyType) -> BoneMeshes {
|
||||
@ -3839,7 +3827,7 @@ impl BipedLargeLateralSpec {
|
||||
(lateral, Vec3::from(spec.foot_r.offset))
|
||||
}
|
||||
}
|
||||
impl BipedLargeWeaponSpec {
|
||||
impl BipedLargeMainSpec {
|
||||
fn mesh_main(&self, item_definition_id: &str, flipped: bool) -> BoneMeshes {
|
||||
let spec = match self.0.get(item_definition_id) {
|
||||
Some(spec) => spec,
|
||||
@ -3868,6 +3856,35 @@ impl BipedLargeWeaponSpec {
|
||||
(tool_kind_segment, offset)
|
||||
}
|
||||
}
|
||||
impl BipedLargeSecondSpec {
|
||||
fn mesh_second(&self, item_definition_id: &str, flipped: bool) -> BoneMeshes {
|
||||
let spec = match self.0.get(item_definition_id) {
|
||||
Some(spec) => spec,
|
||||
None => {
|
||||
error!(?item_definition_id, "No tool/weapon specification exists");
|
||||
return load_mesh("not_found", Vec3::new(-1.5, -1.5, -7.0));
|
||||
},
|
||||
};
|
||||
|
||||
let tool_kind_segment = if flipped {
|
||||
graceful_load_segment_flipped(&spec.vox_spec.0, true)
|
||||
} else {
|
||||
graceful_load_segment(&spec.vox_spec.0)
|
||||
};
|
||||
|
||||
let offset = Vec3::new(
|
||||
if flipped {
|
||||
0.0 - spec.vox_spec.1[0] - (tool_kind_segment.sz.x as f32)
|
||||
} else {
|
||||
spec.vox_spec.1[0]
|
||||
},
|
||||
spec.vox_spec.1[1],
|
||||
spec.vox_spec.1[2],
|
||||
);
|
||||
|
||||
(tool_kind_segment, offset)
|
||||
}
|
||||
}
|
||||
////
|
||||
#[derive(Deserialize)]
|
||||
struct GolemCentralSpec(HashMap<(GSpecies, GBodyType), SidedGCentralVoxSpec>);
|
||||
|
@ -3713,7 +3713,84 @@ impl FigureMgr {
|
||||
skeleton_attr,
|
||||
)
|
||||
},
|
||||
CharacterState::BasicMelee(_) => {
|
||||
CharacterState::ChargedMelee(s) => {
|
||||
let stage_time = s.timer.as_secs_f32();
|
||||
|
||||
let stage_progress = match s.stage_section {
|
||||
StageSection::Charge => {
|
||||
stage_time / s.static_data.charge_duration.as_secs_f32()
|
||||
},
|
||||
StageSection::Swing => {
|
||||
stage_time / s.static_data.swing_duration.as_secs_f32()
|
||||
},
|
||||
StageSection::Recover => {
|
||||
stage_time / s.static_data.recover_duration.as_secs_f32()
|
||||
},
|
||||
_ => 0.0,
|
||||
};
|
||||
|
||||
anim::biped_large::ChargeMeleeAnimation::update_skeleton(
|
||||
&target_base,
|
||||
(
|
||||
(active_tool_kind, active_tool_spec),
|
||||
(second_tool_kind, second_tool_spec),
|
||||
rel_vel,
|
||||
time,
|
||||
Some(s.stage_section),
|
||||
state.acc_vel,
|
||||
),
|
||||
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::Cast => {
|
||||
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::biped_large::SelfBuffAnimation::update_skeleton(
|
||||
&target_base,
|
||||
(
|
||||
(active_tool_kind, active_tool_spec),
|
||||
(second_tool_kind, second_tool_spec),
|
||||
rel_vel,
|
||||
time,
|
||||
Some(s.stage_section),
|
||||
state.acc_vel,
|
||||
),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
skeleton_attr,
|
||||
)
|
||||
},
|
||||
CharacterState::BasicMelee(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::Swing => {
|
||||
stage_time / s.static_data.swing_duration.as_secs_f32()
|
||||
},
|
||||
StageSection::Recover => {
|
||||
stage_time / s.static_data.recover_duration.as_secs_f32()
|
||||
},
|
||||
_ => 0.0,
|
||||
};
|
||||
|
||||
anim::biped_large::AlphaAnimation::update_skeleton(
|
||||
&target_base,
|
||||
(
|
||||
@ -3721,10 +3798,10 @@ impl FigureMgr {
|
||||
(second_tool_kind, second_tool_spec),
|
||||
rel_vel,
|
||||
time,
|
||||
None,
|
||||
Some(s.stage_section),
|
||||
state.acc_vel,
|
||||
),
|
||||
state.state_time,
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
skeleton_attr,
|
||||
)
|
||||
@ -3875,8 +3952,8 @@ impl FigureMgr {
|
||||
anim::biped_large::DashAnimation::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),
|
||||
|
@ -215,6 +215,16 @@ impl ParticleMgr {
|
||||
});
|
||||
}
|
||||
},
|
||||
Outcome::GroundSlam { pos, .. } => {
|
||||
self.particles.resize_with(self.particles.len() + 100, || {
|
||||
Particle::new(
|
||||
Duration::from_millis(1000),
|
||||
time,
|
||||
ParticleMode::BigShrapnel,
|
||||
*pos,
|
||||
)
|
||||
});
|
||||
},
|
||||
Outcome::ProjectileShot { .. }
|
||||
| Outcome::Beam { .. }
|
||||
| Outcome::ExpChange { .. }
|
||||
@ -643,6 +653,36 @@ impl ParticleMgr {
|
||||
},
|
||||
);
|
||||
},
|
||||
CharacterState::SelfBuff(c) => {
|
||||
use buff::BuffKind;
|
||||
if let BuffKind::Frenzied = c.static_data.buff_kind {
|
||||
if matches!(c.stage_section, StageSection::Cast) {
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ usize::from(
|
||||
self.scheduler.heartbeats(Duration::from_millis(5)),
|
||||
),
|
||||
|| {
|
||||
let start_pos = pos.0
|
||||
+ Vec3::new(
|
||||
body.radius(),
|
||||
body.radius(),
|
||||
body.height() / 2.0,
|
||||
)
|
||||
.map(|d| d * rng.gen_range(-1.0..1.0));
|
||||
let end_pos = pos.0 + (start_pos - pos.0) * 6.0;
|
||||
Particle::new_directed(
|
||||
Duration::from_secs(1),
|
||||
time,
|
||||
ParticleMode::Enraged,
|
||||
start_pos,
|
||||
end_pos,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
@ -818,9 +858,9 @@ impl ParticleMgr {
|
||||
.join()
|
||||
{
|
||||
for (buff_kind, _) in buffs.kinds.iter() {
|
||||
#[allow(clippy::single_match)]
|
||||
use buff::BuffKind;
|
||||
match buff_kind {
|
||||
buff::BuffKind::Cursed | buff::BuffKind::Burning => {
|
||||
BuffKind::Cursed | BuffKind::Burning => {
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ usize::from(self.scheduler.heartbeats(Duration::from_millis(15))),
|
||||
@ -850,6 +890,29 @@ impl ParticleMgr {
|
||||
},
|
||||
);
|
||||
},
|
||||
BuffKind::Frenzied => {
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ usize::from(self.scheduler.heartbeats(Duration::from_millis(15))),
|
||||
|| {
|
||||
let start_pos = pos.0
|
||||
+ Vec3::new(body.radius(), body.radius(), body.height() / 2.0)
|
||||
.map(|d| d * rng.gen_range(-1.0..1.0));
|
||||
let end_pos = start_pos
|
||||
+ Vec3::unit_z() * body.height()
|
||||
+ Vec3::<f32>::zero()
|
||||
.map(|_| rng.gen_range(-1.0..1.0))
|
||||
.normalized();
|
||||
Particle::new_directed(
|
||||
Duration::from_secs(1),
|
||||
time,
|
||||
ParticleMode::Enraged,
|
||||
start_pos,
|
||||
end_pos,
|
||||
)
|
||||
},
|
||||
);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user