mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'sam/harvester' into 'master'
Harvester See merge request veloren/veloren!2413
This commit is contained in:
commit
e3bf77298b
@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Clay Golem uses shockwave only after specific fraction of health and other difficulty adjustments.
|
||||
- Made strafing slightly slower
|
||||
- Food now has limited regeneration strength but longer duration.
|
||||
- Harvester boss now has new abilities and AI
|
||||
|
||||
### Removed
|
||||
|
||||
|
@ -257,6 +257,14 @@
|
||||
(None, "common.abilities.custom.yeti.snowball"),
|
||||
],
|
||||
),
|
||||
Custom("Harvester"): (
|
||||
primary: "common.abilities.custom.harvester.scythe",
|
||||
secondary: "common.abilities.custom.harvester.firebreath",
|
||||
abilities: [
|
||||
(None, "common.abilities.custom.harvester.ensnaringvines"),
|
||||
(None, "common.abilities.custom.harvester.explodingpumpkin"),
|
||||
],
|
||||
),
|
||||
Custom("Bird Large Breathe"): (
|
||||
primary: "common.abilities.custom.birdlargebreathe.firebomb",
|
||||
secondary: "common.abilities.custom.birdlargebreathe.triplestrike",
|
||||
|
@ -6,7 +6,12 @@ BasicBeam(
|
||||
tick_rate: 3.0,
|
||||
range: 15.0,
|
||||
max_angle: 22.5,
|
||||
damage_effect: None,
|
||||
damage_effect: Some(Buff((
|
||||
kind: Burning,
|
||||
dur_secs: 10.0,
|
||||
strength: DamageFraction(0.5),
|
||||
chance: 0.25,
|
||||
))),
|
||||
energy_regen: 0,
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Normal,
|
||||
|
@ -6,7 +6,12 @@ BasicBeam(
|
||||
tick_rate: 3.0,
|
||||
range: 15.0,
|
||||
max_angle: 22.5,
|
||||
damage_effect: None,
|
||||
damage_effect: Some(Buff((
|
||||
kind: Burning,
|
||||
dur_secs: 10.0,
|
||||
strength: DamageFraction(0.5),
|
||||
chance: 0.25,
|
||||
))),
|
||||
energy_regen: 0,
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Normal,
|
||||
|
@ -0,0 +1,8 @@
|
||||
SpriteSummon(
|
||||
buildup_duration: 0.6,
|
||||
cast_duration: 0.4,
|
||||
recover_duration: 0.3,
|
||||
sprite: EnsnaringVines,
|
||||
summon_distance: (0, 25),
|
||||
sparseness: 0.67,
|
||||
)
|
@ -0,0 +1,15 @@
|
||||
BasicRanged(
|
||||
energy_cost: 0,
|
||||
buildup_duration: 0.5,
|
||||
recover_duration: 0.8,
|
||||
projectile: ExplodingPumpkin(
|
||||
damage: 200.0,
|
||||
knockback: 25.0,
|
||||
radius: 5.0,
|
||||
),
|
||||
projectile_body: Object(Pumpkin),
|
||||
projectile_light: None,
|
||||
projectile_speed: 30.0,
|
||||
num_projectiles: 1,
|
||||
projectile_spread: 0.0,
|
||||
)
|
20
assets/common/abilities/custom/harvester/firebreath.ron
Normal file
20
assets/common/abilities/custom/harvester/firebreath.ron
Normal file
@ -0,0 +1,20 @@
|
||||
BasicBeam(
|
||||
buildup_duration: 0.5,
|
||||
recover_duration: 0.5,
|
||||
beam_duration: 1.0,
|
||||
damage: 40,
|
||||
tick_rate: 1.5,
|
||||
range: 20.0,
|
||||
max_angle: 15.0,
|
||||
damage_effect: Some(Buff((
|
||||
kind: Burning,
|
||||
dur_secs: 10.0,
|
||||
strength: DamageFraction(0.5),
|
||||
chance: 0.25,
|
||||
))),
|
||||
energy_regen: 0,
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Normal,
|
||||
ori_rate: 0.2,
|
||||
specifier: Flamethrower,
|
||||
)
|
13
assets/common/abilities/custom/harvester/scythe.ron
Normal file
13
assets/common/abilities/custom/harvester/scythe.ron
Normal file
@ -0,0 +1,13 @@
|
||||
BasicMelee(
|
||||
energy_cost: 0,
|
||||
buildup_duration: 0.7,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 0.6,
|
||||
base_damage: 70,
|
||||
base_poise_damage: 10,
|
||||
knockback: ( strength: 10.0, direction: Away),
|
||||
range: 4.0,
|
||||
max_angle: 60.0,
|
||||
damage_effect: None,
|
||||
damage_kind: Slashing,
|
||||
)
|
@ -6,7 +6,12 @@ BasicBeam(
|
||||
tick_rate: 3.0,
|
||||
range: 15.0,
|
||||
max_angle: 22.5,
|
||||
damage_effect: None,
|
||||
damage_effect: Some(Buff((
|
||||
kind: Burning,
|
||||
dur_secs: 10.0,
|
||||
strength: DamageFraction(0.5),
|
||||
chance: 0.25,
|
||||
))),
|
||||
energy_regen: 0,
|
||||
energy_drain: 0,
|
||||
orientation_behavior: Normal,
|
||||
|
@ -6,7 +6,12 @@ BasicBeam(
|
||||
tick_rate: 3.0,
|
||||
range: 20.0,
|
||||
max_angle: 15.0,
|
||||
damage_effect: None,
|
||||
damage_effect: Some(Buff((
|
||||
kind: Burning,
|
||||
dur_secs: 10.0,
|
||||
strength: DamageFraction(0.5),
|
||||
chance: 0.25,
|
||||
))),
|
||||
energy_regen: 0,
|
||||
energy_drain: 350,
|
||||
orientation_behavior: Normal,
|
||||
|
@ -2,11 +2,11 @@ ItemDef(
|
||||
name: "Harvester Sythe",
|
||||
description: "Placeholder",
|
||||
kind: Tool((
|
||||
kind: Hammer,
|
||||
kind: Natural,
|
||||
hands: Two,
|
||||
stats: Direct((
|
||||
equip_time_secs: 0.001,
|
||||
power: 1.6,
|
||||
power: 1.0,
|
||||
poise_strength: 1.0,
|
||||
speed: 1.0,
|
||||
crit_chance: 0.05078125,
|
||||
@ -14,5 +14,5 @@ ItemDef(
|
||||
)),
|
||||
quality: Low,
|
||||
tags: [],
|
||||
ability_spec: Some(Custom("Hammer Simple")),
|
||||
ability_spec: Some(Custom("Harvester")),
|
||||
)
|
BIN
assets/voxygen/element/de_buffs/debuff_ensnared_0.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/de_buffs/debuff_ensnared_0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -34,6 +34,8 @@
|
||||
"buff.desc.frozen": "Your movements and attacks are slowed.",
|
||||
"buff.title.wet": "Wet",
|
||||
"buff.desc.wet": "The ground rejects your feet, making it hard to stop.",
|
||||
"buff.title.ensnared": "Ensnared",
|
||||
"buff.desc.ensnared": "Vines grasp at your legs, impeding your movement.",
|
||||
// Buffs stats
|
||||
"buff.stat.health": "Restores {str_total} Health",
|
||||
"buff.stat.increase_max_stamina": "Raises Maximum Stamina by {strength}",
|
||||
|
BIN
assets/voxygen/voxel/sprite/misc/ensnaring_vines.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/misc/ensnaring_vines.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -3247,4 +3247,14 @@ CookingPot: Some((
|
||||
],
|
||||
wind_sway: 0.0,
|
||||
)),
|
||||
EnsnaringVines: Some((
|
||||
variations: [
|
||||
(
|
||||
model: "voxygen.voxel.sprite.misc.ensnaring_vines",
|
||||
offset: (-5.0, -6.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
],
|
||||
wind_sway: 0.0,
|
||||
)),
|
||||
)
|
||||
|
@ -197,6 +197,7 @@ lazy_static! {
|
||||
BuffKind::Crippled => "crippled",
|
||||
BuffKind::Frozen => "frozen",
|
||||
BuffKind::Wet => "wet",
|
||||
BuffKind::Ensnared => "ensnared",
|
||||
};
|
||||
let mut buff_parser = HashMap::new();
|
||||
BuffKind::iter().for_each(|kind| {buff_parser.insert(string_from_buff(kind).to_string(), kind);});
|
||||
|
@ -10,6 +10,7 @@ use crate::{
|
||||
utils::{AbilityInfo, StageSection},
|
||||
*,
|
||||
},
|
||||
terrain::SpriteKind,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
@ -294,6 +295,14 @@ pub enum CharacterAbility {
|
||||
buff_duration: Option<f32>,
|
||||
energy_cost: f32,
|
||||
},
|
||||
SpriteSummon {
|
||||
buildup_duration: f32,
|
||||
cast_duration: f32,
|
||||
recover_duration: f32,
|
||||
sprite: SpriteKind,
|
||||
summon_distance: (f32, f32),
|
||||
sparseness: f64,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for CharacterAbility {
|
||||
@ -329,7 +338,7 @@ impl CharacterAbility {
|
||||
pub fn requirements_paid(&self, data: &JoinData, update: &mut StateUpdate) -> bool {
|
||||
match self {
|
||||
CharacterAbility::Roll { energy_cost, .. } => {
|
||||
data.physics.on_ground
|
||||
data.physics.on_ground.is_some()
|
||||
&& data.vel.0.xy().magnitude_squared() > 0.5
|
||||
&& update
|
||||
.energy
|
||||
@ -365,7 +374,8 @@ impl CharacterAbility {
|
||||
| CharacterAbility::Boost { .. }
|
||||
| CharacterAbility::BasicBeam { .. }
|
||||
| CharacterAbility::Blink { .. }
|
||||
| CharacterAbility::BasicSummon { .. } => true,
|
||||
| CharacterAbility::BasicSummon { .. }
|
||||
| CharacterAbility::SpriteSummon { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,6 +637,17 @@ impl CharacterAbility {
|
||||
*cast_duration /= speed;
|
||||
*recover_duration /= speed;
|
||||
},
|
||||
SpriteSummon {
|
||||
ref mut buildup_duration,
|
||||
ref mut cast_duration,
|
||||
ref mut recover_duration,
|
||||
..
|
||||
} => {
|
||||
// TODO: Figure out how/if power should affect this
|
||||
*buildup_duration /= speed;
|
||||
*cast_duration /= speed;
|
||||
*recover_duration /= speed;
|
||||
},
|
||||
}
|
||||
self
|
||||
}
|
||||
@ -655,7 +676,11 @@ impl CharacterAbility {
|
||||
0
|
||||
}
|
||||
},
|
||||
Boost { .. } | ComboMelee { .. } | Blink { .. } | BasicSummon { .. } => 0,
|
||||
Boost { .. }
|
||||
| ComboMelee { .. }
|
||||
| Blink { .. }
|
||||
| BasicSummon { .. }
|
||||
| SpriteSummon { .. } => 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1781,6 +1806,27 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
}),
|
||||
CharacterAbility::SpriteSummon {
|
||||
buildup_duration,
|
||||
cast_duration,
|
||||
recover_duration,
|
||||
sprite,
|
||||
summon_distance,
|
||||
sparseness,
|
||||
} => CharacterState::SpriteSummon(sprite_summon::Data {
|
||||
static_data: sprite_summon::StaticData {
|
||||
buildup_duration: Duration::from_secs_f32(*buildup_duration),
|
||||
cast_duration: Duration::from_secs_f32(*cast_duration),
|
||||
recover_duration: Duration::from_secs_f32(*recover_duration),
|
||||
sprite: *sprite,
|
||||
summon_distance: *summon_distance,
|
||||
sparseness: *sparseness,
|
||||
ability_info,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
achieved_radius: summon_distance.0.floor() as i32 - 1,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -305,6 +305,7 @@ impl Body {
|
||||
biped_large::Species::Occultsaurok => Vec3::new(4.0, 3.0, 3.4),
|
||||
biped_large::Species::Slysaurok => Vec3::new(4.0, 3.0, 3.4),
|
||||
biped_large::Species::Werewolf => Vec3::new(4.0, 3.0, 3.5),
|
||||
biped_large::Species::Harvester => Vec3::new(4.6, 3.0, 5.4),
|
||||
|
||||
_ => Vec3::new(4.6, 3.0, 6.0),
|
||||
},
|
||||
@ -489,7 +490,7 @@ impl Body {
|
||||
biped_large::Species::Tidalwarrior => 16000,
|
||||
biped_large::Species::Yeti => 12000,
|
||||
biped_large::Species::Minotaur => 30000,
|
||||
biped_large::Species::Harvester => 3000,
|
||||
biped_large::Species::Harvester => 5000,
|
||||
biped_large::Species::Blueoni => 2400,
|
||||
biped_large::Species::Redoni => 2400,
|
||||
_ => 1200,
|
||||
@ -608,12 +609,12 @@ impl Body {
|
||||
biped_large::Species::Mountaintroll => 60,
|
||||
biped_large::Species::Swamptroll => 60,
|
||||
biped_large::Species::Dullahan => 120,
|
||||
biped_large::Species::Yeti => 0,
|
||||
biped_large::Species::Harvester => 80,
|
||||
// Boss enemies have their health set, not adjusted by level.
|
||||
biped_large::Species::Mindflayer => 0,
|
||||
biped_large::Species::Minotaur => 0,
|
||||
biped_large::Species::Tidalwarrior => 0,
|
||||
biped_large::Species::Yeti => 0,
|
||||
biped_large::Species::Harvester => 0,
|
||||
_ => 100,
|
||||
},
|
||||
Body::BipedSmall(_) => 10,
|
||||
@ -658,6 +659,9 @@ impl Body {
|
||||
Body::Object(object::Body::HaniwaSentry) => true,
|
||||
_ => false,
|
||||
},
|
||||
BuffKind::Ensnared => {
|
||||
matches!(self, Body::BipedLarge(b) if matches!(b.species, biped_large::Species::Harvester))
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -673,6 +677,7 @@ impl Body {
|
||||
biped_large::Species::Minotaur => 3.2,
|
||||
biped_large::Species::Tidalwarrior => 2.25,
|
||||
biped_large::Species::Yeti => 2.0,
|
||||
biped_large::Species::Harvester => 2.4,
|
||||
_ => 1.0,
|
||||
},
|
||||
Body::Golem(g) => match g.species {
|
||||
|
@ -69,6 +69,10 @@ pub enum BuffKind {
|
||||
/// Strength scales the friction you ignore non-linearly. 0.5 is 50% ground
|
||||
/// friction, 1.0 is 33% ground friction.
|
||||
Wet,
|
||||
/// Makes you move slower.
|
||||
/// Strength scales the movement speed debuff non-linearly. 0.5 is 50%
|
||||
/// speed, 1.0 is 33% speed.
|
||||
Ensnared,
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@ -91,6 +95,7 @@ impl BuffKind {
|
||||
BuffKind::Frenzied => true,
|
||||
BuffKind::Frozen => false,
|
||||
BuffKind::Wet => false,
|
||||
BuffKind::Ensnared => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -319,6 +324,10 @@ impl Buff {
|
||||
vec![BuffEffect::GroundFriction(1.0 - nn_scaling(data.strength))],
|
||||
data.duration,
|
||||
),
|
||||
BuffKind::Ensnared => (
|
||||
vec![BuffEffect::MovementSpeed(1.0 - nn_scaling(data.strength))],
|
||||
data.duration,
|
||||
),
|
||||
};
|
||||
Buff {
|
||||
kind,
|
||||
|
@ -103,6 +103,8 @@ pub enum CharacterState {
|
||||
BasicSummon(basic_summon::Data),
|
||||
/// Inserts a buff on the caster
|
||||
SelfBuff(self_buff::Data),
|
||||
/// Creates sprites around the caster
|
||||
SpriteSummon(sprite_summon::Data),
|
||||
}
|
||||
|
||||
impl CharacterState {
|
||||
@ -125,6 +127,9 @@ impl CharacterState {
|
||||
| CharacterState::BasicAura(_)
|
||||
| CharacterState::HealingBeam(_)
|
||||
| CharacterState::SelfBuff(_)
|
||||
| CharacterState::Blink(_)
|
||||
| CharacterState::BasicSummon(_)
|
||||
| CharacterState::SpriteSummon(_)
|
||||
)
|
||||
}
|
||||
|
||||
@ -149,6 +154,9 @@ impl CharacterState {
|
||||
| CharacterState::BasicAura(_)
|
||||
| CharacterState::HealingBeam(_)
|
||||
| CharacterState::SelfBuff(_)
|
||||
| CharacterState::Blink(_)
|
||||
| CharacterState::BasicSummon(_)
|
||||
| CharacterState::SpriteSummon(_)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::Fluid;
|
||||
use crate::{consts::WATER_DENSITY, uid::Uid};
|
||||
use crate::{consts::WATER_DENSITY, terrain::Block, uid::Uid};
|
||||
use hashbrown::HashSet;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::{Component, DerefFlaggedStorage, NullStorage};
|
||||
@ -142,7 +142,7 @@ impl Component for Sticky {
|
||||
// PhysicsState
|
||||
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PhysicsState {
|
||||
pub on_ground: bool,
|
||||
pub on_ground: Option<Block>,
|
||||
pub on_ceiling: bool,
|
||||
pub on_wall: Option<Vec3<f32>>,
|
||||
pub touch_entities: HashSet<Uid>,
|
||||
@ -165,7 +165,7 @@ impl PhysicsState {
|
||||
|
||||
pub fn on_surface(&self) -> Option<Vec3<f32>> {
|
||||
self.on_ground
|
||||
.then_some(-Vec3::unit_z())
|
||||
.map(|_| -Vec3::unit_z())
|
||||
.or_else(|| self.on_ceiling.then_some(Vec3::unit_z()))
|
||||
.or(self.on_wall)
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::{
|
||||
combat::{
|
||||
Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement, Damage,
|
||||
DamageKind, DamageSource, GroupTarget, Knockback, KnockbackDir,
|
||||
Attack, AttackDamage, AttackEffect, CombatBuff, CombatBuffStrength, CombatEffect,
|
||||
CombatRequirement, Damage, DamageKind, DamageSource, GroupTarget, Knockback, KnockbackDir,
|
||||
},
|
||||
comp::item::Reagent,
|
||||
comp::{buff::BuffKind, item::Reagent},
|
||||
uid::Uid,
|
||||
Explosion, RadiusEffect,
|
||||
};
|
||||
@ -72,6 +72,11 @@ pub enum ProjectileConstructor {
|
||||
damage: f32,
|
||||
radius: f32,
|
||||
},
|
||||
ExplodingPumpkin {
|
||||
damage: f32,
|
||||
radius: f32,
|
||||
knockback: f32,
|
||||
},
|
||||
}
|
||||
|
||||
impl ProjectileConstructor {
|
||||
@ -298,6 +303,60 @@ impl ProjectileConstructor {
|
||||
is_point: false,
|
||||
}
|
||||
},
|
||||
ExplodingPumpkin {
|
||||
damage,
|
||||
radius,
|
||||
knockback,
|
||||
} => {
|
||||
let knockback = AttackEffect::new(
|
||||
Some(GroupTarget::OutOfGroup),
|
||||
CombatEffect::Knockback(Knockback {
|
||||
strength: knockback,
|
||||
direction: KnockbackDir::Away,
|
||||
}),
|
||||
)
|
||||
.with_requirement(CombatRequirement::AnyDamage);
|
||||
let buff = AttackEffect::new(
|
||||
Some(GroupTarget::OutOfGroup),
|
||||
CombatEffect::Buff(CombatBuff {
|
||||
kind: BuffKind::Burning,
|
||||
dur_secs: 5.0,
|
||||
strength: CombatBuffStrength::DamageFraction(0.2),
|
||||
chance: 1.0,
|
||||
}),
|
||||
)
|
||||
.with_requirement(CombatRequirement::AnyDamage);
|
||||
let damage = AttackDamage::new(
|
||||
Damage {
|
||||
source: DamageSource::Explosion,
|
||||
kind: DamageKind::Energy,
|
||||
value: damage,
|
||||
},
|
||||
Some(GroupTarget::OutOfGroup),
|
||||
);
|
||||
let attack = Attack::default()
|
||||
.with_damage(damage)
|
||||
.with_crit(crit_chance, crit_mult)
|
||||
.with_effect(knockback)
|
||||
.with_effect(buff);
|
||||
let explosion = Explosion {
|
||||
effects: vec![
|
||||
RadiusEffect::Attack(attack),
|
||||
RadiusEffect::TerrainDestruction(5.0),
|
||||
],
|
||||
radius,
|
||||
reagent: Some(Reagent::Red),
|
||||
};
|
||||
Projectile {
|
||||
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
|
||||
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
|
||||
time_left: Duration::from_secs(10),
|
||||
owner,
|
||||
ignore_group: true,
|
||||
is_sticky: true,
|
||||
is_point: true,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,6 +413,14 @@ impl ProjectileConstructor {
|
||||
*damage *= power;
|
||||
*radius *= range;
|
||||
},
|
||||
ExplodingPumpkin {
|
||||
ref mut damage,
|
||||
ref mut radius,
|
||||
..
|
||||
} => {
|
||||
*damage *= power;
|
||||
*radius *= range;
|
||||
},
|
||||
}
|
||||
self
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ use crate::{
|
||||
},
|
||||
outcome::Outcome,
|
||||
rtsim::RtSimEntity,
|
||||
terrain::SpriteKind,
|
||||
trade::{TradeAction, TradeId},
|
||||
uid::Uid,
|
||||
util::Dir,
|
||||
@ -181,6 +182,10 @@ pub enum ServerEvent {
|
||||
Sound {
|
||||
sound: Sound,
|
||||
},
|
||||
CreateSprite {
|
||||
pos: Vec3<i32>,
|
||||
sprite: SpriteKind,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct EventBus<E> {
|
||||
|
@ -10,12 +10,22 @@ pub struct Spiral2d {
|
||||
|
||||
impl Spiral2d {
|
||||
#[allow(clippy::new_without_default)] // TODO: Pending review in #587
|
||||
/// Creates a new spiral starting at the origin
|
||||
pub fn new() -> Self { Self { layer: 0, i: 0 } }
|
||||
|
||||
/// Creates an iterator over points in a spiral starting at the origin and
|
||||
/// going out to some radius
|
||||
pub fn radius(self, radius: i32) -> impl Iterator<Item = Vec2<i32>> {
|
||||
self.take((radius * 2 + 1).pow(2) as usize)
|
||||
.filter(move |pos| pos.magnitude_squared() < (radius + 1).pow(2))
|
||||
}
|
||||
|
||||
/// Creates an iterator over points in the edge of a circle of some radius
|
||||
pub fn edge_radius(self, radius: i32) -> impl Iterator<Item = Vec2<i32>> {
|
||||
self.take((radius * 2 + 1).pow(2) as usize)
|
||||
.filter(move |pos| pos.magnitude_squared() < (radius + 1).pow(2))
|
||||
.filter(move |pos| pos.magnitude_squared() >= radius.pow(2))
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Spiral2d {
|
||||
|
@ -57,7 +57,7 @@ impl CharacterBehavior for Data {
|
||||
let mut update = StateUpdate::from(data);
|
||||
|
||||
// If no wall is in front of character or we stopped climbing;
|
||||
let (wall_dir, climb) = if let (Some(wall_dir), Some(climb), false) = (
|
||||
let (wall_dir, climb) = if let (Some(wall_dir), Some(climb), None) = (
|
||||
data.physics.on_wall,
|
||||
data.inputs.climb,
|
||||
data.physics.on_ground,
|
||||
@ -105,7 +105,11 @@ impl CharacterBehavior for Data {
|
||||
// Smooth orientation
|
||||
update.ori = update.ori.slerped_towards(
|
||||
Ori::from(ori_dir),
|
||||
if data.physics.on_ground { 9.0 } else { 2.0 } * data.dt.0,
|
||||
if data.physics.on_ground.is_some() {
|
||||
9.0
|
||||
} else {
|
||||
2.0
|
||||
} * data.dt.0,
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -16,7 +16,7 @@ impl CharacterBehavior for Data {
|
||||
handle_jump(data, &mut update, 1.0);
|
||||
|
||||
// Try to Fall/Stand up/Move
|
||||
if !data.physics.on_ground || data.inputs.move_dir.magnitude_squared() > 0.0 {
|
||||
if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 {
|
||||
update.character = CharacterState::Idle;
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ impl CharacterBehavior for Data {
|
||||
let mut update = StateUpdate::from(data);
|
||||
|
||||
// If player is on ground, end glide
|
||||
if data.physics.on_ground
|
||||
if data.physics.on_ground.is_some()
|
||||
&& (data.vel.0 - data.physics.ground_vel).magnitude_squared() < 2_f32.powi(2)
|
||||
{
|
||||
update.character = CharacterState::GlideWield;
|
||||
@ -163,7 +163,13 @@ impl CharacterBehavior for Data {
|
||||
let accel_factor = accel.magnitude_squared().min(1.0) / 1.0;
|
||||
|
||||
Quaternion::rotation_3d(
|
||||
PI / 2.0 * accel_factor * if data.physics.on_ground { -1.0 } else { 1.0 },
|
||||
PI / 2.0
|
||||
* accel_factor
|
||||
* if data.physics.on_ground.is_some() {
|
||||
-1.0
|
||||
} else {
|
||||
1.0
|
||||
},
|
||||
ori.up()
|
||||
.cross(accel)
|
||||
.try_normalized()
|
||||
|
@ -20,7 +20,7 @@ impl CharacterBehavior for Data {
|
||||
handle_wield(data, &mut update);
|
||||
|
||||
// If not on the ground while wielding glider enter gliding state
|
||||
if !data.physics.on_ground {
|
||||
if data.physics.on_ground.is_none() {
|
||||
update.character = CharacterState::Glide(glide::Data::new(10.0, 0.6, *data.ori));
|
||||
}
|
||||
if data
|
||||
|
@ -101,7 +101,7 @@ impl CharacterBehavior for Data {
|
||||
timer: tick_attack_or_default(data, self.timer, None),
|
||||
..*self
|
||||
});
|
||||
} else if data.physics.on_ground {
|
||||
} else if data.physics.on_ground.is_some() {
|
||||
// Transitions to swing portion of state upon hitting ground
|
||||
update.character = CharacterState::LeapMelee(Data {
|
||||
timer: Duration::default(),
|
||||
|
@ -26,6 +26,7 @@ pub mod shockwave;
|
||||
pub mod sit;
|
||||
pub mod sneak;
|
||||
pub mod spin_melee;
|
||||
pub mod sprite_summon;
|
||||
pub mod stunned;
|
||||
pub mod talk;
|
||||
pub mod utils;
|
||||
|
@ -16,7 +16,7 @@ impl CharacterBehavior for Data {
|
||||
handle_jump(data, &mut update, 1.0);
|
||||
|
||||
// Try to Fall/Stand up/Move
|
||||
if !data.physics.on_ground || data.inputs.move_dir.magnitude_squared() > 0.0 {
|
||||
if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 {
|
||||
update.character = CharacterState::Idle;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ impl CharacterBehavior for Data {
|
||||
handle_dodge_input(data, &mut update);
|
||||
|
||||
// Try to Fall/Stand up/Move
|
||||
if !data.physics.on_ground {
|
||||
if data.physics.on_ground.is_none() {
|
||||
update.character = CharacterState::Idle;
|
||||
}
|
||||
|
||||
|
164
common/src/states/sprite_summon.rs
Normal file
164
common/src/states/sprite_summon.rs
Normal file
@ -0,0 +1,164 @@
|
||||
use crate::{
|
||||
comp::{CharacterState, StateUpdate},
|
||||
event::ServerEvent,
|
||||
spiral::Spiral2d,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
},
|
||||
terrain::{Block, SpriteKind},
|
||||
vol::ReadVol,
|
||||
};
|
||||
use rand::{thread_rng, Rng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use vek::*;
|
||||
|
||||
/// Separated out to condense update portions of character state
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct StaticData {
|
||||
/// How long the state builds up for
|
||||
pub buildup_duration: Duration,
|
||||
/// How long the state is casting for
|
||||
pub cast_duration: Duration,
|
||||
/// How long the state recovers for
|
||||
pub recover_duration: Duration,
|
||||
/// What kind of sprite is created by this state
|
||||
pub sprite: SpriteKind,
|
||||
/// Range that sprites are created relative to the summonner
|
||||
pub summon_distance: (f32, f32),
|
||||
/// Chance that sprite is not created on a particular square
|
||||
pub sparseness: f64,
|
||||
/// Miscellaneous information about the ability
|
||||
pub ability_info: AbilityInfo,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Data {
|
||||
/// Struct containing data that does not change over the course of the
|
||||
/// character state
|
||||
pub static_data: StaticData,
|
||||
/// Timer for each stage
|
||||
pub timer: Duration,
|
||||
/// What section the character stage is in
|
||||
pub stage_section: StageSection,
|
||||
/// What radius of sprites have already been summoned
|
||||
pub achieved_radius: i32,
|
||||
}
|
||||
|
||||
impl CharacterBehavior for Data {
|
||||
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
|
||||
match self.stage_section {
|
||||
StageSection::Buildup => {
|
||||
if self.timer < self.static_data.buildup_duration {
|
||||
// Build up
|
||||
update.character = CharacterState::SpriteSummon(Data {
|
||||
timer: tick_attack_or_default(data, self.timer, None),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Transitions to recover section of stage
|
||||
update.character = CharacterState::SpriteSummon(Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Cast,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Cast => {
|
||||
if self.timer < self.static_data.cast_duration {
|
||||
let timer_frac =
|
||||
self.timer.as_secs_f32() / self.static_data.cast_duration.as_secs_f32();
|
||||
|
||||
// Determines distance from summoner sprites should be created. Goes outward
|
||||
// with time.
|
||||
let summon_distance = timer_frac
|
||||
* (self.static_data.summon_distance.1 - self.static_data.summon_distance.0)
|
||||
+ self.static_data.summon_distance.0;
|
||||
let summon_distance = summon_distance.round() as i32;
|
||||
|
||||
// Only summons sprites if summon distance is greater than achieved radius
|
||||
for radius in self.achieved_radius..=summon_distance {
|
||||
// 1 added to make range correct, too lazy to add 1 to both variables above
|
||||
let radius = radius + 1;
|
||||
// Creates a spiral iterator for the newly achieved radius
|
||||
let spiral = Spiral2d::new().edge_radius(radius);
|
||||
for point in spiral {
|
||||
// If square is not sparse, generate sprite
|
||||
if !thread_rng().gen_bool(self.static_data.sparseness) {
|
||||
// The coordinates of where the sprite is created
|
||||
let sprite_pos = Vec3::new(
|
||||
data.pos.0.x.floor() as i32 + point.x,
|
||||
data.pos.0.y.floor() as i32 + point.y,
|
||||
data.pos.0.z.floor() as i32,
|
||||
);
|
||||
|
||||
// Check for collision in z up to 10 blocks up or down
|
||||
let obstacle_z = data
|
||||
.terrain
|
||||
.ray(
|
||||
sprite_pos.map(|x| x as f32 + 0.5) + Vec3::unit_z() * 10.0,
|
||||
sprite_pos.map(|x| x as f32 + 0.5) - Vec3::unit_z() * 10.0,
|
||||
)
|
||||
.until(|b| {
|
||||
// Until reaching a solid block that is not the created
|
||||
// sprite
|
||||
Block::is_solid(b)
|
||||
&& b.get_sprite() != Some(self.static_data.sprite)
|
||||
})
|
||||
.cast()
|
||||
.0;
|
||||
|
||||
// z height relative to caster
|
||||
let z = sprite_pos.z + (10.5 - obstacle_z).ceil() as i32;
|
||||
|
||||
// Location sprite will be created
|
||||
let sprite_pos =
|
||||
Vec3::new(sprite_pos.x as i32, sprite_pos.y as i32, z);
|
||||
|
||||
// Send server event to create sprite
|
||||
update.server_events.push_front(ServerEvent::CreateSprite {
|
||||
pos: sprite_pos,
|
||||
sprite: self.static_data.sprite,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update.character = CharacterState::SpriteSummon(Data {
|
||||
timer: tick_attack_or_default(data, self.timer, None),
|
||||
achieved_radius: summon_distance,
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Transitions to recover section of stage
|
||||
update.character = CharacterState::SpriteSummon(Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Recover,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Recover => {
|
||||
if self.timer < self.static_data.recover_duration {
|
||||
// Recovery
|
||||
update.character = CharacterState::SpriteSummon(Data {
|
||||
timer: tick_attack_or_default(data, self.timer, None),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
update.character = CharacterState::Wielding;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
update.character = CharacterState::Wielding;
|
||||
},
|
||||
}
|
||||
|
||||
update
|
||||
}
|
||||
}
|
@ -233,13 +233,14 @@ pub fn handle_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
||||
|
||||
if input_is_pressed(data, InputKind::Fly)
|
||||
&& submersion.map_or(true, |sub| sub < 1.0)
|
||||
&& (!data.physics.on_ground || data.body.jump_impulse().is_none())
|
||||
&& (data.physics.on_ground.is_none() || data.body.jump_impulse().is_none())
|
||||
&& data.body.fly_thrust().is_some()
|
||||
{
|
||||
fly_move(data, update, efficiency);
|
||||
} else if let Some(submersion) = (!data.physics.on_ground && data.body.swim_thrust().is_some())
|
||||
.then_some(submersion)
|
||||
.flatten()
|
||||
} else if let Some(submersion) = (data.physics.on_ground.is_none()
|
||||
&& data.body.swim_thrust().is_some())
|
||||
.then_some(submersion)
|
||||
.flatten()
|
||||
{
|
||||
swim_move(data, update, efficiency, submersion);
|
||||
} else {
|
||||
@ -252,7 +253,7 @@ pub fn handle_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
||||
fn basic_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
||||
let efficiency = efficiency * data.stats.move_speed_modifier * data.stats.friction_modifier;
|
||||
|
||||
let accel = if data.physics.on_ground {
|
||||
let accel = if data.physics.on_ground.is_some() {
|
||||
data.body.base_accel()
|
||||
} else {
|
||||
data.body.air_accel()
|
||||
@ -294,7 +295,7 @@ pub fn handle_forced_movement(data: &JoinData, update: &mut StateUpdate, movemen
|
||||
match movement {
|
||||
ForcedMovement::Forward { strength } => {
|
||||
let strength = strength * data.stats.move_speed_modifier * data.stats.friction_modifier;
|
||||
if let Some(accel) = data.physics.on_ground.then_some(data.body.base_accel()) {
|
||||
if let Some(accel) = data.physics.on_ground.map(|_| data.body.base_accel()) {
|
||||
update.vel.0 += Vec2::broadcast(data.dt.0)
|
||||
* accel
|
||||
* (data.inputs.move_dir + Vec2::from(update.ori))
|
||||
@ -499,25 +500,25 @@ pub fn attempt_wield(data: &JoinData, update: &mut StateUpdate) {
|
||||
|
||||
/// Checks that player can `Sit` and updates `CharacterState` if so
|
||||
pub fn attempt_sit(data: &JoinData, update: &mut StateUpdate) {
|
||||
if data.physics.on_ground {
|
||||
if data.physics.on_ground.is_some() {
|
||||
update.character = CharacterState::Sit;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attempt_dance(data: &JoinData, update: &mut StateUpdate) {
|
||||
if data.physics.on_ground && data.body.is_humanoid() {
|
||||
if data.physics.on_ground.is_some() && data.body.is_humanoid() {
|
||||
update.character = CharacterState::Dance;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attempt_talk(data: &JoinData, update: &mut StateUpdate) {
|
||||
if data.physics.on_ground {
|
||||
if data.physics.on_ground.is_some() {
|
||||
update.character = CharacterState::Talk;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn attempt_sneak(data: &JoinData, update: &mut StateUpdate) {
|
||||
if data.physics.on_ground && data.body.is_humanoid() {
|
||||
if data.physics.on_ground.is_some() && data.body.is_humanoid() {
|
||||
update.character = CharacterState::Sneak;
|
||||
}
|
||||
}
|
||||
@ -526,7 +527,7 @@ pub fn attempt_sneak(data: &JoinData, update: &mut StateUpdate) {
|
||||
pub fn handle_climb(data: &JoinData, update: &mut StateUpdate) -> bool {
|
||||
if data.inputs.climb.is_some()
|
||||
&& data.physics.on_wall.is_some()
|
||||
&& !data.physics.on_ground
|
||||
&& data.physics.on_ground.is_none()
|
||||
&& !data
|
||||
.physics
|
||||
.in_liquid()
|
||||
@ -585,13 +586,13 @@ pub fn attempt_glide_wield(data: &JoinData, update: &mut StateUpdate) {
|
||||
|
||||
/// Checks that player can jump and sends jump event if so
|
||||
pub fn handle_jump(data: &JoinData, update: &mut StateUpdate, strength: f32) -> bool {
|
||||
(input_is_pressed(data, InputKind::Jump) && data.physics.on_ground)
|
||||
(input_is_pressed(data, InputKind::Jump) && data.physics.on_ground.is_some())
|
||||
.then(|| data.body.jump_impulse())
|
||||
.flatten()
|
||||
.map(|impulse| {
|
||||
update.local_events.push_front(LocalEvent::Jump(
|
||||
data.entity,
|
||||
strength * impulse / data.mass.0,
|
||||
strength * impulse / data.mass.0 * data.stats.move_speed_modifier,
|
||||
));
|
||||
})
|
||||
.is_some()
|
||||
|
@ -173,6 +173,7 @@ make_case_elim!(
|
||||
CrystalLow = 0x92,
|
||||
CeilingMushroom = 0x93,
|
||||
Orb = 0x94,
|
||||
EnsnaringVines = 0x95,
|
||||
}
|
||||
);
|
||||
|
||||
@ -256,6 +257,7 @@ impl SpriteKind {
|
||||
| SpriteKind::Tin
|
||||
| SpriteKind::Silver
|
||||
| SpriteKind::Gold => 0.6,
|
||||
SpriteKind::EnsnaringVines => 0.1,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
@ -1,11 +1,15 @@
|
||||
use common::{
|
||||
comp::{
|
||||
buff::{
|
||||
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource,
|
||||
Buffs,
|
||||
},
|
||||
fluid_dynamics::{Fluid, LiquidKind},
|
||||
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs,
|
||||
Energy, Health, HealthChange, HealthSource, Inventory, ModifierKind, PhysicsState, Stats,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
resources::DeltaTime,
|
||||
terrain::SpriteKind,
|
||||
Damage, DamageSource,
|
||||
};
|
||||
use common_ecs::{Job, Origin, Phase, System};
|
||||
@ -55,25 +59,54 @@ impl<'a> System<'a> for Sys {
|
||||
)
|
||||
.join()
|
||||
{
|
||||
let in_fluid = physics_state.and_then(|p| p.in_fluid);
|
||||
|
||||
if matches!(
|
||||
in_fluid,
|
||||
Some(Fluid::Liquid {
|
||||
kind: LiquidKind::Lava,
|
||||
..
|
||||
})
|
||||
) && !buff_comp.contains(BuffKind::Burning)
|
||||
{
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
entity,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
BuffKind::Burning,
|
||||
BuffData::new(200.0, None),
|
||||
vec![BuffCategory::Natural],
|
||||
BuffSource::World,
|
||||
)),
|
||||
});
|
||||
// Apply buffs to entity based off of their current physics_state
|
||||
if let Some(physics_state) = physics_state {
|
||||
if matches!(
|
||||
physics_state.on_ground.and_then(|b| b.get_sprite()),
|
||||
Some(SpriteKind::EnsnaringVines)
|
||||
) {
|
||||
// If on ensnaring vines, apply ensnared debuff
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
entity,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
BuffKind::Ensnared,
|
||||
BuffData::new(1.0, Some(Duration::from_secs_f32(1.0))),
|
||||
Vec::new(),
|
||||
BuffSource::World,
|
||||
)),
|
||||
});
|
||||
}
|
||||
if matches!(
|
||||
physics_state.in_fluid,
|
||||
Some(Fluid::Liquid {
|
||||
kind: LiquidKind::Lava,
|
||||
..
|
||||
})
|
||||
) {
|
||||
// If in lava fluid, apply burning debuff
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
entity,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
BuffKind::Burning,
|
||||
BuffData::new(200.0, None),
|
||||
vec![BuffCategory::Natural],
|
||||
BuffSource::World,
|
||||
)),
|
||||
});
|
||||
} else if matches!(
|
||||
physics_state.in_fluid,
|
||||
Some(Fluid::Liquid {
|
||||
kind: LiquidKind::Water,
|
||||
..
|
||||
})
|
||||
) && buff_comp.kinds.contains_key(&BuffKind::Burning)
|
||||
{
|
||||
// If in water fluid and currently burning, remove burning debuffs
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
entity,
|
||||
buff_change: BuffChange::RemoveByKind(BuffKind::Burning),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let (buff_comp_kinds, buff_comp_buffs): (
|
||||
@ -90,14 +123,14 @@ impl<'a> System<'a> for Sys {
|
||||
if let Some((Some(buff), id)) =
|
||||
ids.get(0).map(|id| (buff_comp_buffs.get_mut(id), id))
|
||||
{
|
||||
tick_buff(*id, buff, dt, in_fluid, |id| expired_buffs.push(id));
|
||||
tick_buff(*id, buff, dt, |id| expired_buffs.push(id));
|
||||
}
|
||||
} else {
|
||||
for (id, buff) in buff_comp_buffs
|
||||
.iter_mut()
|
||||
.filter(|(i, _)| ids.iter().any(|id| id == *i))
|
||||
{
|
||||
tick_buff(*id, buff, dt, in_fluid, |id| expired_buffs.push(id));
|
||||
tick_buff(*id, buff, dt, |id| expired_buffs.push(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -253,13 +286,7 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
|
||||
fn tick_buff(
|
||||
id: u64,
|
||||
buff: &mut Buff,
|
||||
dt: f32,
|
||||
in_fluid: Option<Fluid>,
|
||||
mut expire_buff: impl FnMut(u64),
|
||||
) {
|
||||
fn tick_buff(id: u64, buff: &mut Buff, dt: f32, mut expire_buff: impl FnMut(u64)) {
|
||||
// If a buff is recently applied from an aura, do not tick duration
|
||||
if buff
|
||||
.cat_ids
|
||||
@ -269,19 +296,6 @@ fn tick_buff(
|
||||
return;
|
||||
}
|
||||
if let Some(remaining_time) = &mut buff.time {
|
||||
// Extinguish Burning buff when in water
|
||||
if matches!(buff.kind, BuffKind::Burning)
|
||||
&& matches!(
|
||||
in_fluid,
|
||||
Some(Fluid::Liquid {
|
||||
kind: LiquidKind::Water,
|
||||
..
|
||||
})
|
||||
)
|
||||
{
|
||||
*remaining_time = Duration::default();
|
||||
}
|
||||
|
||||
if let Some(new_duration) = remaining_time.checked_sub(Duration::from_secs_f32(dt)) {
|
||||
// The buff still continues.
|
||||
*remaining_time = new_duration;
|
||||
|
@ -331,6 +331,7 @@ impl<'a> System<'a> for Sys {
|
||||
CharacterState::Blink(data) => data.handle_event(&j, action),
|
||||
CharacterState::BasicSummon(data) => data.handle_event(&j, action),
|
||||
CharacterState::SelfBuff(data) => data.handle_event(&j, action),
|
||||
CharacterState::SpriteSummon(data) => data.handle_event(&j, action),
|
||||
};
|
||||
local_emitter.append(&mut state_update.local_events);
|
||||
server_emitter.append(&mut state_update.server_events);
|
||||
@ -386,6 +387,7 @@ impl<'a> System<'a> for Sys {
|
||||
CharacterState::Blink(data) => data.behavior(&j),
|
||||
CharacterState::BasicSummon(data) => data.behavior(&j),
|
||||
CharacterState::SelfBuff(data) => data.behavior(&j),
|
||||
CharacterState::SpriteSummon(data) => data.behavior(&j),
|
||||
};
|
||||
|
||||
local_emitter.append(&mut state_update.local_events);
|
||||
|
@ -319,7 +319,8 @@ impl<'a> PhysicsData<'a> {
|
||||
char_state_maybe,
|
||||
)| {
|
||||
let is_sticky = sticky.is_some();
|
||||
let is_mid_air = physics.on_wall.is_none() && physics.on_ground;
|
||||
// Code reviewers: remind me to check why on_ground was true instead of false here?
|
||||
let is_mid_air = physics.on_wall.is_none() && physics.on_ground.is_some();
|
||||
let mut entity_entity_collision_checks = 0;
|
||||
let mut entity_entity_collisions = 0;
|
||||
|
||||
@ -753,7 +754,7 @@ impl<'a> PhysicsData<'a> {
|
||||
// velocities or entirely broken position snapping.
|
||||
let mut tgt_pos = pos.0 + pos_delta;
|
||||
|
||||
let was_on_ground = physics_state.on_ground;
|
||||
let was_on_ground = physics_state.on_ground.is_some();
|
||||
let block_snap =
|
||||
body.map_or(false, |b| !matches!(b, Body::Object(_) | Body::Ship(_)));
|
||||
let climbing =
|
||||
@ -879,7 +880,7 @@ impl<'a> PhysicsData<'a> {
|
||||
> block_rpos.xy().map(|e| e.abs()).reduce_partial_max()
|
||||
{
|
||||
if block_rpos.z > 0.0 {
|
||||
physics_state.on_ground = true;
|
||||
physics_state.on_ground = block.copied();
|
||||
} else {
|
||||
physics_state.on_ceiling = true;
|
||||
}
|
||||
@ -1068,10 +1069,11 @@ impl<'a> PhysicsData<'a> {
|
||||
// union in the state updates, so that the state isn't just
|
||||
// based on the most
|
||||
// recent terrain that collision was attempted with
|
||||
if physics_state_delta.on_ground {
|
||||
if physics_state_delta.on_ground.is_some() {
|
||||
physics_state.ground_vel = vel_other;
|
||||
}
|
||||
physics_state.on_ground |= physics_state_delta.on_ground;
|
||||
physics_state.on_ground =
|
||||
physics_state.on_ground.or(physics_state_delta.on_ground);
|
||||
physics_state.on_ceiling |= physics_state_delta.on_ceiling;
|
||||
physics_state.on_wall = physics_state.on_wall.or_else(|| {
|
||||
physics_state_delta
|
||||
@ -1339,10 +1341,10 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
.is_some()
|
||||
}
|
||||
|
||||
physics_state.on_ground = false;
|
||||
physics_state.on_ground = None;
|
||||
physics_state.on_ceiling = false;
|
||||
|
||||
let mut on_ground = false;
|
||||
let mut on_ground = None;
|
||||
let mut on_ceiling = false;
|
||||
let mut attempts = 0; // Don't loop infinitely here
|
||||
|
||||
@ -1360,7 +1362,7 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
const MAX_ATTEMPTS: usize = 16;
|
||||
|
||||
// While the player is colliding with the terrain...
|
||||
while let Some((_block_pos, block_aabb, block_height)) =
|
||||
while let Some((_block_pos, block_aabb, block_height, block)) =
|
||||
(attempts < MAX_ATTEMPTS).then(|| {
|
||||
// Calculate the player's AABB
|
||||
let player_aabb = Aabb {
|
||||
@ -1391,12 +1393,13 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
max: block_pos.map(|e| e as f32) + Vec3::new(1.0, 1.0, block.solid_height()),
|
||||
},
|
||||
block.solid_height(),
|
||||
block,
|
||||
)
|
||||
})
|
||||
// Determine whether the block's AABB collides with the player's AABB
|
||||
.filter(|(_, block_aabb, _)| block_aabb.collides_with_aabb(player_aabb))
|
||||
.filter(|(_, block_aabb, _, _)| block_aabb.collides_with_aabb(player_aabb))
|
||||
// Find the maximum of the minimum collision axes (this bit is weird, trust me that it works)
|
||||
.min_by_key(|(_, block_aabb, _)| {
|
||||
.min_by_key(|(_, block_aabb, _, _)| {
|
||||
ordered_float::OrderedFloat((block_aabb.center() - player_aabb.center() - Vec3::unit_z() * 0.5)
|
||||
.map(f32::abs)
|
||||
.sum())
|
||||
@ -1428,7 +1431,7 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
if resolve_dir.z > 0.0
|
||||
/* && vel.0.z <= 0.0 */
|
||||
{
|
||||
on_ground = true;
|
||||
on_ground = Some(block).copied();
|
||||
|
||||
if !was_on_ground {
|
||||
land_on_ground(entity, *vel);
|
||||
@ -1466,7 +1469,7 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
if (vel.0 * resolve_dir).xy().magnitude_squared() < 1.0f32.powi(2) {
|
||||
pos.0 -= resolve_dir.normalized() * 0.05;
|
||||
}
|
||||
on_ground = true;
|
||||
on_ground = Some(block).copied();
|
||||
break;
|
||||
} else {
|
||||
// Correct the velocity
|
||||
@ -1497,8 +1500,8 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
physics_state.on_ceiling = true;
|
||||
}
|
||||
|
||||
if on_ground {
|
||||
physics_state.on_ground = true;
|
||||
if on_ground.is_some() {
|
||||
physics_state.on_ground = on_ground;
|
||||
// If the space below us is free, then "snap" to the ground
|
||||
} else if collision_with(
|
||||
pos.0 - Vec3::unit_z() * 1.1,
|
||||
@ -1519,7 +1522,7 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
.unwrap_or(0.0);
|
||||
vel.0.z = 0.0;
|
||||
pos.0.z = (pos.0.z - 0.1).floor() + snap_height;
|
||||
physics_state.on_ground = true;
|
||||
physics_state.on_ground = on_ground;
|
||||
}
|
||||
|
||||
let player_aabb = Aabb {
|
||||
@ -1594,7 +1597,7 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
}
|
||||
physics_state.on_wall = on_wall;
|
||||
let fric_mod = read.stats.get(entity).map_or(1.0, |s| s.friction_modifier);
|
||||
if physics_state.on_ground || (physics_state.on_wall.is_some() && climbing) {
|
||||
if physics_state.on_ground.is_some() || (physics_state.on_wall.is_some() && climbing) {
|
||||
vel.0 *= (1.0 - FRIC_GROUND.min(1.0) * fric_mod).powf(dt.0 * 60.0);
|
||||
physics_state.ground_vel = ground_vel;
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ impl<'a> System<'a> for Sys {
|
||||
arc_strip.collides_with_circle(Disk::new(pos_b2, rad_b))
|
||||
}
|
||||
&& (pos_b_ground - pos.0).angle_between(pos_b.0 - pos.0) < max_angle
|
||||
&& (!shockwave.requires_ground || physics_state_b.on_ground);
|
||||
&& (!shockwave.requires_ground || physics_state_b.on_ground.is_some());
|
||||
|
||||
if hit {
|
||||
let dir = Dir::from_unnormalized(pos_b.0 - pos.0).unwrap_or(look_dir);
|
||||
|
@ -279,7 +279,8 @@ impl<'a> System<'a> for Sys {
|
||||
| CharacterState::HealingBeam { .. }
|
||||
| CharacterState::Blink { .. }
|
||||
| CharacterState::BasicSummon { .. }
|
||||
| CharacterState::SelfBuff { .. } => {
|
||||
| CharacterState::SelfBuff { .. }
|
||||
| CharacterState::SpriteSummon { .. } => {
|
||||
if energy.get_unchecked().regen_rate != 0.0 {
|
||||
energy.get_mut_unchecked().regen_rate = 0.0
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ use common::{
|
||||
},
|
||||
consts::{MAX_MOUNT_RANGE, SOUND_TRAVEL_DIST_PER_VOLUME},
|
||||
outcome::Outcome,
|
||||
terrain::{Block, SpriteKind},
|
||||
uid::Uid,
|
||||
vol::ReadVol,
|
||||
};
|
||||
@ -418,3 +419,17 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) {
|
||||
ecs.write_resource::<Vec<Outcome>>().push(outcome);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_create_sprite(server: &mut Server, pos: Vec3<i32>, sprite: SpriteKind) {
|
||||
let state = server.state_mut();
|
||||
if state.can_set_block(pos) {
|
||||
let block = state.terrain().get(pos).ok().copied();
|
||||
if block.map_or(false, |b| (*b).is_air()) {
|
||||
let new_block = state
|
||||
.get_block(pos)
|
||||
.unwrap_or_else(|| Block::air(SpriteKind::Empty))
|
||||
.with_sprite(sprite);
|
||||
server.state.set_block(pos, new_block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ use entity_manipulation::{
|
||||
use group_manip::handle_group;
|
||||
use information::handle_site_info;
|
||||
use interaction::{
|
||||
handle_lantern, handle_mine_block, handle_mount, handle_npc_interaction, handle_possess,
|
||||
handle_sound, handle_unmount,
|
||||
handle_create_sprite, handle_lantern, handle_mine_block, handle_mount, handle_npc_interaction,
|
||||
handle_possess, handle_sound, handle_unmount,
|
||||
};
|
||||
use inventory_manip::handle_inventory;
|
||||
use invite::{handle_invite, handle_invite_response};
|
||||
@ -221,6 +221,9 @@ impl Server {
|
||||
self.state.create_safezone(range, pos).build();
|
||||
},
|
||||
ServerEvent::Sound { sound } => handle_sound(self, &sound),
|
||||
ServerEvent::CreateSprite { pos, sprite } => {
|
||||
handle_create_sprite(self, pos, sprite)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,6 +122,7 @@ pub enum Tactic {
|
||||
TidalWarrior,
|
||||
Yeti,
|
||||
Tornado,
|
||||
Harvester,
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
@ -280,7 +281,7 @@ impl<'a> System<'a> for Sys {
|
||||
let is_gliding = matches!(
|
||||
read_data.char_states.get(entity),
|
||||
Some(CharacterState::GlideWield) | Some(CharacterState::Glide(_))
|
||||
) && !physics_state.on_ground;
|
||||
) && physics_state.on_ground.is_none();
|
||||
|
||||
if let Some(pid) = agent.position_pid_controller.as_mut() {
|
||||
pid.add_measurement(read_data.time.0, pos.0);
|
||||
@ -295,7 +296,7 @@ impl<'a> System<'a> for Sys {
|
||||
let traversal_config = TraversalConfig {
|
||||
node_tolerance,
|
||||
slow_factor,
|
||||
on_ground: physics_state.on_ground,
|
||||
on_ground: physics_state.on_ground.is_some(),
|
||||
in_liquid: physics_state.in_liquid().is_some(),
|
||||
min_tgt_dist: 1.0,
|
||||
can_climb: body.map(|b| b.can_climb()).unwrap_or(false),
|
||||
@ -365,7 +366,7 @@ impl<'a> System<'a> for Sys {
|
||||
// inputs.
|
||||
|
||||
// If falling fast and can glide, save yourself!
|
||||
if data.glider_equipped && !data.physics_state.on_ground {
|
||||
if data.glider_equipped && data.physics_state.on_ground.is_none() {
|
||||
// toggle glider when vertical velocity is above some threshold (here ~
|
||||
// glider fall vertical speed)
|
||||
data.glider_fall(agent, controller, &read_data);
|
||||
@ -1663,6 +1664,7 @@ impl<'a> AgentData<'a> {
|
||||
"Tidal Warrior" => Tactic::TidalWarrior,
|
||||
"Tidal Totem" => Tactic::RadialTurret,
|
||||
"Yeti" => Tactic::Yeti,
|
||||
"Harvester" => Tactic::Harvester,
|
||||
_ => Tactic::Melee,
|
||||
},
|
||||
AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind),
|
||||
@ -1763,6 +1765,18 @@ impl<'a> AgentData<'a> {
|
||||
),
|
||||
)
|
||||
},
|
||||
Tactic::Harvester if matches!(self.char_state, CharacterState::BasicRanged(_)) => {
|
||||
const PUMPKIN_SPEED: f32 = 30.0;
|
||||
aim_projectile(
|
||||
PUMPKIN_SPEED,
|
||||
Vec3::new(self.pos.0.x, self.pos.0.y, self.pos.0.z + eye_offset),
|
||||
Vec3::new(
|
||||
tgt_data.pos.0.x,
|
||||
tgt_data.pos.0.y,
|
||||
tgt_data.pos.0.z + tgt_eye_offset,
|
||||
),
|
||||
)
|
||||
},
|
||||
_ => Dir::from_unnormalized(
|
||||
Vec3::new(
|
||||
tgt_data.pos.0.x,
|
||||
@ -1942,6 +1956,9 @@ impl<'a> AgentData<'a> {
|
||||
Tactic::Yeti => {
|
||||
self.handle_yeti_attack(agent, controller, &attack_data, &tgt_data, &read_data)
|
||||
},
|
||||
Tactic::Harvester => {
|
||||
self.handle_harvester_attack(agent, controller, &attack_data, &tgt_data, &read_data)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -3693,6 +3710,78 @@ impl<'a> AgentData<'a> {
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
}
|
||||
|
||||
fn handle_harvester_attack(
|
||||
&self,
|
||||
agent: &mut Agent,
|
||||
controller: &mut Controller,
|
||||
attack_data: &AttackData,
|
||||
tgt_data: &TargetData,
|
||||
read_data: &ReadData,
|
||||
) {
|
||||
const VINE_CREATION_THRESHOLD: f32 = 0.50;
|
||||
const FIRE_BREATH_RANGE: f32 = 20.0;
|
||||
const MAX_PUMPKIN_RANGE: f32 = 50.0;
|
||||
let health_fraction = self.health.map_or(0.5, |h| h.fraction());
|
||||
|
||||
if health_fraction < VINE_CREATION_THRESHOLD && !agent.action_state.condition {
|
||||
// Summon vines when reach threshold of health
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(0)));
|
||||
|
||||
if matches!(self.char_state, CharacterState::SpriteSummon(c) if matches!(c.stage_section, StageSection::Recover))
|
||||
{
|
||||
agent.action_state.condition = true;
|
||||
}
|
||||
} else if attack_data.dist_sqrd < FIRE_BREATH_RANGE.powi(2) {
|
||||
if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(5))
|
||||
&& can_see_tgt(
|
||||
&*read_data.terrain,
|
||||
self.pos,
|
||||
tgt_data.pos,
|
||||
attack_data.dist_sqrd,
|
||||
)
|
||||
{
|
||||
// Keep breathing fire if close enough, can see target, and have not been
|
||||
// breathing for more than 5 seconds
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
} else if attack_data.in_min_range() && attack_data.angle < 60.0 {
|
||||
// Scythe them if they're in range and angle
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Primary));
|
||||
} else if attack_data.angle < 30.0
|
||||
&& can_see_tgt(
|
||||
&*read_data.terrain,
|
||||
self.pos,
|
||||
tgt_data.pos,
|
||||
attack_data.dist_sqrd,
|
||||
)
|
||||
{
|
||||
// Start breathing fire at them if close enough, in angle, and can see target
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Secondary));
|
||||
}
|
||||
} else if attack_data.dist_sqrd < MAX_PUMPKIN_RANGE.powi(2)
|
||||
&& can_see_tgt(
|
||||
&*read_data.terrain,
|
||||
self.pos,
|
||||
tgt_data.pos,
|
||||
attack_data.dist_sqrd,
|
||||
)
|
||||
{
|
||||
// Throw a pumpkin at them if close enough and can see them
|
||||
controller
|
||||
.actions
|
||||
.push(ControlAction::basic_input(InputKind::Ability(1)));
|
||||
}
|
||||
// Always attempt to path towards target
|
||||
self.path_toward_target(agent, controller, tgt_data, read_data, false, None);
|
||||
}
|
||||
|
||||
fn follow(
|
||||
&self,
|
||||
agent: &mut Agent,
|
||||
|
@ -327,6 +327,63 @@ impl Animation for AlphaAnimation {
|
||||
* Quaternion::rotation_y(-1.8 + move1 * -0.8 + move2 * 3.0)
|
||||
* Quaternion::rotation_z(move1 * -0.8 + move2 * -0.8);
|
||||
},
|
||||
"Harvester" => {
|
||||
next.head.orientation =
|
||||
Quaternion::rotation_x(move1 * -0.25 + move2 * 0.25)
|
||||
* Quaternion::rotation_z(move1 * -0.3 + move2 * 0.4);
|
||||
|
||||
next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1 + move2 * -0.5);
|
||||
next.jaw.orientation = Quaternion::rotation_x(move2 * -0.15);
|
||||
|
||||
next.upper_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.2 + move2 * -0.2)
|
||||
* Quaternion::rotation_z(move1 * -1.0 + move2 * 1.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(-0.4 + move1 * 1.0 + move2 * -1.0)
|
||||
* Quaternion::rotation_y(move1 * -0.2);
|
||||
next.shoulder_r.orientation =
|
||||
Quaternion::rotation_y(0.4 + move1 * -0.8 + move2 * 0.8)
|
||||
* Quaternion::rotation_x(0.4 + move1 * -0.4 + move2 * 0.8);
|
||||
|
||||
if speed == 0.0 {
|
||||
next.leg_l.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.4 + move2 * -0.4);
|
||||
|
||||
next.foot_l.position = Vec3::new(
|
||||
-s_a.foot.0,
|
||||
s_a.foot.1,
|
||||
s_a.foot.2 + move1 * 2.0 + move2 * -2.0,
|
||||
);
|
||||
next.foot_l.orientation =
|
||||
Quaternion::rotation_x(move1 * -0.6 + move2 * 0.6);
|
||||
}
|
||||
|
||||
next.control_l.position = Vec3::new(1.0, 2.0, 8.0);
|
||||
next.control_r.position = Vec3::new(1.0, 1.0, -2.0);
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
-7.0 + move1 * 26.0 - move2 * 32.0,
|
||||
0.0 + s_a.grip.0 / 1.0 - move1 * 4.0,
|
||||
-s_a.grip.0 / 0.8,
|
||||
);
|
||||
|
||||
next.control_l.orientation = Quaternion::rotation_x(PI / 2.0)
|
||||
* Quaternion::rotation_y(-0.0)
|
||||
* Quaternion::rotation_z(PI);
|
||||
next.control_r.orientation = Quaternion::rotation_x(PI / 2.0 + 0.2)
|
||||
* Quaternion::rotation_y(-1.0 + move1 * 1.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
|
||||
next.control.orientation = Quaternion::rotation_x(-1.4 + move1 * -0.4)
|
||||
* Quaternion::rotation_y(-2.8 + move1 * 3.0 + move2 * -3.0)
|
||||
* Quaternion::rotation_z(move1 * -1.5);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
@ -215,6 +215,69 @@ impl Animation for BeamAnimation {
|
||||
move1 * -0.8 + move2 * 1.1 + move2shake * 0.02,
|
||||
);
|
||||
},
|
||||
"Harvester" => {
|
||||
next.head.orientation = Quaternion::rotation_x(
|
||||
move1 * 0.5 + move2 * -0.4 + move2shake * -0.02,
|
||||
);
|
||||
|
||||
next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1);
|
||||
next.jaw.orientation =
|
||||
Quaternion::rotation_x(move2 * -0.5 + move2shake * -0.1);
|
||||
|
||||
next.upper_torso.position = Vec3::new(
|
||||
0.0,
|
||||
s_a.upper_torso.0 + move1 * -3.0 + move2 * 3.0,
|
||||
s_a.upper_torso.1 + move1 * -0.4,
|
||||
);
|
||||
next.upper_torso.orientation = Quaternion::rotation_x(
|
||||
move1 * 0.8 + move2 * -1.1 + move2shake * -0.02,
|
||||
);
|
||||
next.lower_torso.position =
|
||||
Vec3::new(0.0, s_a.lower_torso.0, s_a.lower_torso.1);
|
||||
next.lower_torso.orientation = Quaternion::rotation_x(
|
||||
move1 * -0.8 + move2 * 1.1 + move2shake * 0.02,
|
||||
);
|
||||
|
||||
next.control_l.position = Vec3::new(1.0, 2.0, 8.0);
|
||||
next.control_r.position = Vec3::new(1.0, 1.0, -2.0);
|
||||
|
||||
next.control.position =
|
||||
Vec3::new(-6.0, 0.0 + s_a.grip.0 / 1.0, -s_a.grip.0 / 0.8);
|
||||
|
||||
next.control_l.orientation =
|
||||
Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(PI);
|
||||
next.control_r.orientation = Quaternion::rotation_x(PI / 2.0 + 0.2)
|
||||
* Quaternion::rotation_y(-1.0);
|
||||
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(-1.4) * Quaternion::rotation_y(-2.8);
|
||||
|
||||
next.weapon_l.position =
|
||||
Vec3::new(move1 * 8.0, move1 * 1.0, move1 * 6.0);
|
||||
next.weapon_l.orientation = Quaternion::rotation_x(move1 * 0.5)
|
||||
* Quaternion::rotation_y(move1 * -0.8);
|
||||
|
||||
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_y(-0.4 + move1 * 0.8)
|
||||
* Quaternion::rotation_x(-0.4 + move1 * -0.2);
|
||||
next.shoulder_r.orientation =
|
||||
Quaternion::rotation_y(0.4 + move1 * -0.8)
|
||||
* Quaternion::rotation_x(0.4 + move1 * -0.8);
|
||||
|
||||
next.hand_r.position = Vec3::new(
|
||||
-s_a.grip.1 + move1 * -5.0,
|
||||
0.0 + move1 * 6.0,
|
||||
s_a.grip.0 + move1 * 13.0,
|
||||
);
|
||||
next.hand_r.orientation = Quaternion::rotation_x(move1 * -3.0)
|
||||
* Quaternion::rotation_y(move1 * 1.5)
|
||||
* Quaternion::rotation_z(move1 * -1.5);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ pub mod shockwave;
|
||||
pub mod shoot;
|
||||
pub mod spin;
|
||||
pub mod spinmelee;
|
||||
pub mod spritesummon;
|
||||
pub mod stunned;
|
||||
pub mod summon;
|
||||
pub mod wield;
|
||||
@ -26,7 +27,8 @@ pub use self::{
|
||||
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,
|
||||
spritesummon::SpriteSummonAnimation, stunned::StunnedAnimation, summon::SummonAnimation,
|
||||
wield::WieldAnimation,
|
||||
};
|
||||
|
||||
use super::{make_bone, vek::*, FigureBoneData, Skeleton};
|
||||
|
@ -262,6 +262,87 @@ impl Animation for ShootAnimation {
|
||||
next.arm_control_r.position =
|
||||
Vec3::new(0.0, move1 * 10.0 + move2 * -10.0, 0.0);
|
||||
},
|
||||
"Harvester" => {
|
||||
let (move1, move2, move3) = match stage_section {
|
||||
Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0),
|
||||
Some(StageSection::Swing) => (1.0, anim_time, 0.0),
|
||||
Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)),
|
||||
_ => (0.0, 0.0, 0.0),
|
||||
};
|
||||
next.control_l.position = Vec3::new(1.0, 2.0, 8.0);
|
||||
next.control_r.position = Vec3::new(1.0, 1.0, -2.0);
|
||||
|
||||
next.control.position =
|
||||
Vec3::new(-7.0, 0.0 + s_a.grip.0 / 1.0, -s_a.grip.0 / 0.8);
|
||||
|
||||
next.control_l.orientation =
|
||||
Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(PI);
|
||||
next.control_r.orientation = Quaternion::rotation_x(PI / 2.0 + 0.2)
|
||||
* Quaternion::rotation_y(-1.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(-1.4) * Quaternion::rotation_y(-2.8);
|
||||
|
||||
next.head.orientation = Quaternion::rotation_x(move1 * 0.2);
|
||||
next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1);
|
||||
next.jaw.orientation = Quaternion::rotation_x(move2 * -0.3);
|
||||
|
||||
let twist = move1 * 0.8 + move3 * -0.8;
|
||||
next.upper_torso.position = Vec3::new(
|
||||
0.0,
|
||||
s_a.upper_torso.0,
|
||||
s_a.upper_torso.1 + move1 * 1.0 + move2 * -1.0,
|
||||
);
|
||||
next.upper_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + move2 * -1.1)
|
||||
* Quaternion::rotation_z(
|
||||
twist * -0.2 + move1 * -0.1 + move2 * 0.3,
|
||||
);
|
||||
|
||||
next.lower_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * -0.8 + move2 * 1.1)
|
||||
* Quaternion::rotation_z(-twist + move1 * 0.4);
|
||||
|
||||
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(-0.4);
|
||||
|
||||
next.shoulder_r.position = Vec3::new(
|
||||
s_a.shoulder.0 + move2 * -2.0,
|
||||
s_a.shoulder.1,
|
||||
s_a.shoulder.2,
|
||||
);
|
||||
next.shoulder_r.orientation = Quaternion::rotation_y(move1 * -PI / 2.0)
|
||||
* Quaternion::rotation_x(move2 * 2.0)
|
||||
* Quaternion::rotation_z(move1 * -PI / 2.0);
|
||||
|
||||
next.hand_r.position = Vec3::new(
|
||||
-s_a.grip.1 + move1 * -2.0 + move2 * 8.0,
|
||||
0.0 + move1 * 6.0,
|
||||
s_a.grip.0 + move1 * 18.0 + move2 * -19.0,
|
||||
);
|
||||
next.hand_r.orientation =
|
||||
Quaternion::rotation_x(move1 * -3.0 + move2 * 3.0)
|
||||
* Quaternion::rotation_y(move1 * 0.5 + move2 * -1.5)
|
||||
* Quaternion::rotation_z(move1 * -1.5);
|
||||
|
||||
if speed == 0.0 {
|
||||
next.leg_l.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + move2 * -0.8);
|
||||
|
||||
next.foot_l.position = Vec3::new(
|
||||
-s_a.foot.0,
|
||||
s_a.foot.1,
|
||||
s_a.foot.2 + move1 * 4.0 + move2 * -4.0,
|
||||
);
|
||||
next.foot_l.orientation =
|
||||
Quaternion::rotation_x(move1 * -0.6 + move2 * 0.6);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
144
voxygen/anim/src/biped_large/spritesummon.rs
Normal file
144
voxygen/anim/src/biped_large/spritesummon.rs
Normal file
@ -0,0 +1,144 @@
|
||||
use super::{
|
||||
super::{vek::*, Animation},
|
||||
BipedLargeSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::{
|
||||
comp::item::{AbilitySpec, ToolKind},
|
||||
states::utils::StageSection,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct SpriteSummonAnimation;
|
||||
|
||||
type SpriteSummonAnimationDependency<'a> = (
|
||||
(Option<ToolKind>, Option<&'a AbilitySpec>),
|
||||
(Option<ToolKind>, Option<&'a AbilitySpec>),
|
||||
f32,
|
||||
f32,
|
||||
Option<StageSection>,
|
||||
);
|
||||
impl Animation for SpriteSummonAnimation {
|
||||
type Dependency<'a> = SpriteSummonAnimationDependency<'a>;
|
||||
type Skeleton = BipedLargeSkeleton;
|
||||
|
||||
#[cfg(feature = "use-dyn-lib")]
|
||||
const UPDATE_FN: &'static [u8] = b"biped_large_sprite_summon\0";
|
||||
|
||||
#[cfg_attr(feature = "be-dyn-lib", export_name = "biped_large_sprite_summon")]
|
||||
#[allow(clippy::single_match)] // TODO: Pending review in #587
|
||||
fn update_skeleton_inner<'a>(
|
||||
skeleton: &Self::Skeleton,
|
||||
(
|
||||
(active_tool_kind, active_tool_spec),
|
||||
_second_tool_kind,
|
||||
_global_time,
|
||||
velocity,
|
||||
stage_section,
|
||||
): Self::Dependency<'a>,
|
||||
anim_time: f32,
|
||||
rate: &mut f32,
|
||||
s_a: &SkeletonAttr,
|
||||
) -> Self::Skeleton {
|
||||
*rate = 1.0;
|
||||
let mut next = (*skeleton).clone();
|
||||
|
||||
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);
|
||||
|
||||
match active_tool_kind {
|
||||
Some(ToolKind::Natural) => {
|
||||
if let Some(AbilitySpec::Custom(spec)) = active_tool_spec {
|
||||
match spec.as_str() {
|
||||
"Harvester" => {
|
||||
let (move1, move1pow, move2, move3) = match stage_section {
|
||||
Some(StageSection::Buildup) => {
|
||||
(anim_time, anim_time.powf(0.1), 0.0, 0.0)
|
||||
},
|
||||
Some(StageSection::Cast) => {
|
||||
(1.0, 1.0, (anim_time.powf(4.0) * 80.0).min(1.0), 0.0)
|
||||
},
|
||||
Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time),
|
||||
_ => (0.0, 0.0, 0.0, 0.0),
|
||||
};
|
||||
|
||||
let speed = Vec2::<f32>::from(velocity).magnitude();
|
||||
|
||||
let pullback = 1.0 - move3;
|
||||
let move1 = move1 * pullback;
|
||||
let move1pow = move1pow * pullback;
|
||||
let move2 = move2 * pullback;
|
||||
|
||||
next.head.orientation = Quaternion::rotation_x(move1 * 0.2);
|
||||
next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1);
|
||||
next.jaw.orientation = Quaternion::rotation_x(move2 * -0.3);
|
||||
|
||||
let twist = move1 * 0.8 + move3 * -0.8;
|
||||
next.upper_torso.position = Vec3::new(
|
||||
0.0,
|
||||
s_a.upper_torso.0,
|
||||
s_a.upper_torso.1 + move1 * 1.0 + move2 * -1.0,
|
||||
);
|
||||
next.upper_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + move2 * -1.1)
|
||||
* Quaternion::rotation_z(
|
||||
twist * -0.2 + move1 * -0.1 + move2 * 0.3,
|
||||
);
|
||||
|
||||
next.lower_torso.orientation =
|
||||
Quaternion::rotation_x(move1 * -0.8 + move2 * 1.1)
|
||||
* Quaternion::rotation_z(-twist + move1 * 0.4);
|
||||
|
||||
next.control_l.position = Vec3::new(1.0, 2.0, 8.0);
|
||||
next.control_r.position = Vec3::new(1.0, 1.0, -2.0);
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
-7.0 + move1pow * 7.0,
|
||||
0.0 + s_a.grip.0 / 1.0 + move1pow * 12.0,
|
||||
-s_a.grip.0 / 0.8 + move1pow * 20.0 + move2 * -3.0,
|
||||
);
|
||||
|
||||
next.control_l.orientation =
|
||||
Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(PI);
|
||||
next.control_r.orientation = Quaternion::rotation_x(PI / 2.0 + 0.2)
|
||||
* Quaternion::rotation_y(-1.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(-1.4 + move1pow * 2.2 + move2 * -0.6)
|
||||
* Quaternion::rotation_y(-PI);
|
||||
|
||||
next.shoulder_l.position =
|
||||
Vec3::new(-s_a.shoulder.0, s_a.shoulder.1, s_a.shoulder.2);
|
||||
next.shoulder_l.orientation =
|
||||
Quaternion::rotation_x(-0.4 + move1pow * 1.6);
|
||||
next.shoulder_r.orientation = Quaternion::rotation_y(0.4)
|
||||
* Quaternion::rotation_x(0.4 + move1pow * 1.0);
|
||||
|
||||
if speed == 0.0 {
|
||||
next.leg_l.orientation =
|
||||
Quaternion::rotation_x(move1 * 0.8 + move2 * -0.8);
|
||||
|
||||
next.foot_l.position = Vec3::new(
|
||||
-s_a.foot.0,
|
||||
s_a.foot.1,
|
||||
s_a.foot.2 + move1 * 4.0 + move2 * -4.0,
|
||||
);
|
||||
next.foot_l.orientation =
|
||||
Quaternion::rotation_x(move1 * -0.6 + move2 * 0.6);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
next
|
||||
}
|
||||
}
|
@ -470,6 +470,34 @@ impl Animation for WieldAnimation {
|
||||
|
||||
next.shoulder_r.orientation = Quaternion::rotation_x(-0.3);
|
||||
},
|
||||
"Harvester" => {
|
||||
next.control_l.position = Vec3::new(1.0, 2.0, 8.0);
|
||||
next.control_r.position = Vec3::new(1.0, 1.0, -2.0);
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
-7.0,
|
||||
0.0 + s_a.grip.0 / 1.0,
|
||||
-s_a.grip.0 / 0.8 + short * -1.5,
|
||||
);
|
||||
|
||||
next.control_l.orientation =
|
||||
Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_z(PI);
|
||||
next.control_r.orientation = Quaternion::rotation_x(PI / 2.0 + 0.2)
|
||||
* Quaternion::rotation_y(-1.0)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(-1.4) * Quaternion::rotation_y(-2.8);
|
||||
|
||||
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(-0.4);
|
||||
next.shoulder_r.orientation =
|
||||
Quaternion::rotation_y(0.4) * Quaternion::rotation_x(0.4);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ impl EventMapper for MovementEventMapper {
|
||||
// update state to determine the next event. We only record the time (above) if
|
||||
// it was dispatched
|
||||
internal_state.event = mapped_event;
|
||||
internal_state.on_ground = physics.on_ground;
|
||||
internal_state.on_ground = physics.on_ground.is_some();
|
||||
internal_state.in_water = physics.in_liquid().is_some();
|
||||
let dt = ecs.fetch::<DeltaTime>().0;
|
||||
internal_state.distance_travelled += vel.0.magnitude() * dt;
|
||||
@ -197,8 +197,8 @@ impl MovementEventMapper {
|
||||
|| !previous_state.in_water && physics_state.in_liquid().is_some()
|
||||
{
|
||||
return SfxEvent::Swim;
|
||||
} else if physics_state.on_ground && vel.magnitude() > 0.1
|
||||
|| !previous_state.on_ground && physics_state.on_ground
|
||||
} else if physics_state.on_ground.is_some() && vel.magnitude() > 0.1
|
||||
|| !previous_state.on_ground && physics_state.on_ground.is_some()
|
||||
{
|
||||
return if matches!(character_state, CharacterState::Roll(_)) {
|
||||
SfxEvent::Roll
|
||||
@ -238,7 +238,7 @@ impl MovementEventMapper {
|
||||
) -> SfxEvent {
|
||||
if physics_state.in_liquid().is_some() && vel.magnitude() > 0.1 {
|
||||
SfxEvent::Swim
|
||||
} else if physics_state.on_ground && vel.magnitude() > 0.1 {
|
||||
} else if physics_state.on_ground.is_some() && vel.magnitude() > 0.1 {
|
||||
match underfoot_block_kind {
|
||||
BlockKind::Snow => SfxEvent::Run(BlockKind::Snow),
|
||||
BlockKind::Rock | BlockKind::WeakRock => SfxEvent::Run(BlockKind::Rock),
|
||||
@ -259,7 +259,7 @@ impl MovementEventMapper {
|
||||
) -> SfxEvent {
|
||||
if physics_state.in_liquid().is_some() && vel.magnitude() > 0.1 {
|
||||
SfxEvent::Swim
|
||||
} else if physics_state.on_ground && vel.magnitude() > 0.1 {
|
||||
} else if physics_state.on_ground.is_some() && vel.magnitude() > 0.1 {
|
||||
match underfoot_block_kind {
|
||||
BlockKind::Snow => SfxEvent::QuadRun(BlockKind::Snow),
|
||||
BlockKind::Rock | BlockKind::WeakRock => SfxEvent::QuadRun(BlockKind::Rock),
|
||||
|
@ -6,7 +6,7 @@ use common::{
|
||||
Ori, PhysicsState,
|
||||
},
|
||||
states,
|
||||
terrain::BlockKind,
|
||||
terrain::{Block, BlockKind},
|
||||
};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
@ -95,7 +95,7 @@ fn maps_idle() {
|
||||
let result = MovementEventMapper::map_movement_event(
|
||||
&CharacterState::Idle {},
|
||||
&PhysicsState {
|
||||
on_ground: true,
|
||||
on_ground: Some(Block::empty()),
|
||||
..Default::default()
|
||||
},
|
||||
&PreviousEntityState {
|
||||
@ -117,7 +117,7 @@ fn maps_run_with_sufficient_velocity() {
|
||||
let result = MovementEventMapper::map_movement_event(
|
||||
&CharacterState::Idle {},
|
||||
&PhysicsState {
|
||||
on_ground: true,
|
||||
on_ground: Some(Block::empty()),
|
||||
..Default::default()
|
||||
},
|
||||
&PreviousEntityState {
|
||||
@ -139,7 +139,7 @@ fn does_not_map_run_with_insufficient_velocity() {
|
||||
let result = MovementEventMapper::map_movement_event(
|
||||
&CharacterState::Idle {},
|
||||
&PhysicsState {
|
||||
on_ground: true,
|
||||
on_ground: Some(Block::empty()),
|
||||
..Default::default()
|
||||
},
|
||||
&PreviousEntityState {
|
||||
@ -194,7 +194,7 @@ fn maps_roll() {
|
||||
was_combo: None,
|
||||
}),
|
||||
&PhysicsState {
|
||||
on_ground: true,
|
||||
on_ground: Some(Block::empty()),
|
||||
..Default::default()
|
||||
},
|
||||
&PreviousEntityState {
|
||||
@ -216,7 +216,7 @@ fn maps_land_on_ground_to_run() {
|
||||
let result = MovementEventMapper::map_movement_event(
|
||||
&CharacterState::Idle {},
|
||||
&PhysicsState {
|
||||
on_ground: true,
|
||||
on_ground: Some(Block::empty()),
|
||||
..Default::default()
|
||||
},
|
||||
&PreviousEntityState {
|
||||
@ -296,7 +296,7 @@ fn maps_glider_close_when_landing() {
|
||||
let result = MovementEventMapper::map_movement_event(
|
||||
&CharacterState::Idle {},
|
||||
&PhysicsState {
|
||||
on_ground: true,
|
||||
on_ground: Some(Block::empty()),
|
||||
..Default::default()
|
||||
},
|
||||
&PreviousEntityState {
|
||||
@ -317,7 +317,7 @@ fn maps_glider_close_when_landing() {
|
||||
fn maps_quadrupeds_running() {
|
||||
let result = MovementEventMapper::map_non_humanoid_movement_event(
|
||||
&PhysicsState {
|
||||
on_ground: true,
|
||||
on_ground: Some(Block::empty()),
|
||||
..Default::default()
|
||||
},
|
||||
Vec3::new(0.5, 0.8, 0.0),
|
||||
|
@ -769,7 +769,7 @@ fn insert_killing_buff(buff: BuffKind, localized_strings: &Localization, templat
|
||||
tracing::error!("Player was killed by a positive buff!");
|
||||
localized_strings.get("hud.outcome.mysterious")
|
||||
},
|
||||
BuffKind::Wet => {
|
||||
BuffKind::Wet | BuffKind::Ensnared => {
|
||||
tracing::error!("Player was killed by a debuff that doesn't do damage!");
|
||||
localized_strings.get("hud.outcome.mysterious")
|
||||
},
|
||||
|
@ -596,6 +596,7 @@ image_ids! {
|
||||
debuff_crippled_0: "voxygen.element.de_buffs.debuff_cripple_0",
|
||||
debuff_frozen_0: "voxygen.element.de_buffs.debuff_frozen_0",
|
||||
debuff_wet_0: "voxygen.element.de_buffs.debuff_wet_0",
|
||||
debuff_ensnared_0: "voxygen.element.de_buffs.debuff_ensnared_0",
|
||||
|
||||
// Animation Frames
|
||||
// Buff Frame
|
||||
|
@ -3822,6 +3822,7 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id {
|
||||
BuffKind::Crippled { .. } => imgs.debuff_crippled_0,
|
||||
BuffKind::Frozen { .. } => imgs.debuff_frozen_0,
|
||||
BuffKind::Wet { .. } => imgs.debuff_wet_0,
|
||||
BuffKind::Ensnared { .. } => imgs.debuff_ensnared_0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -3844,6 +3845,7 @@ pub fn get_buff_title(buff: BuffKind, localized_strings: &Localization) -> &str
|
||||
BuffKind::Crippled { .. } => localized_strings.get("buff.title.crippled"),
|
||||
BuffKind::Frozen { .. } => localized_strings.get("buff.title.frozen"),
|
||||
BuffKind::Wet { .. } => localized_strings.get("buff.title.wet"),
|
||||
BuffKind::Ensnared { .. } => localized_strings.get("buff.title.ensnared"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -3878,6 +3880,7 @@ pub fn get_buff_desc(buff: BuffKind, data: BuffData, localized_strings: &Localiz
|
||||
BuffKind::Crippled { .. } => Cow::Borrowed(localized_strings.get("buff.desc.crippled")),
|
||||
BuffKind::Frozen { .. } => Cow::Borrowed(localized_strings.get("buff.desc.frozen")),
|
||||
BuffKind::Wet { .. } => Cow::Borrowed(localized_strings.get("buff.desc.wet")),
|
||||
BuffKind::Ensnared { .. } => Cow::Borrowed(localized_strings.get("buff.desc.ensnared")),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,8 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> String {
|
||||
| BuffKind::Crippled
|
||||
| BuffKind::Frenzied
|
||||
| BuffKind::Frozen
|
||||
| BuffKind::Wet => continue,
|
||||
| BuffKind::Wet
|
||||
| BuffKind::Ensnared => continue,
|
||||
};
|
||||
|
||||
write!(&mut description, "{}", buff_desc).unwrap();
|
||||
@ -156,7 +157,8 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> String {
|
||||
| BuffKind::Crippled
|
||||
| BuffKind::Frenzied
|
||||
| BuffKind::Frozen
|
||||
| BuffKind::Wet => continue,
|
||||
| BuffKind::Wet
|
||||
| BuffKind::Ensnared => continue,
|
||||
}
|
||||
} else if let BuffKind::Saturation | BuffKind::Regeneration = buff.kind {
|
||||
i18n.get("buff.text.every_second").to_string()
|
||||
|
@ -800,7 +800,7 @@ impl FigureMgr {
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
) {
|
||||
@ -1596,7 +1596,7 @@ impl FigureMgr {
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
) {
|
||||
@ -1798,7 +1798,7 @@ impl FigureMgr {
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > 0.25, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
) {
|
||||
@ -2125,7 +2125,7 @@ impl FigureMgr {
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
) {
|
||||
@ -2484,7 +2484,7 @@ impl FigureMgr {
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
) {
|
||||
@ -2594,7 +2594,7 @@ impl FigureMgr {
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
) {
|
||||
@ -2683,7 +2683,7 @@ impl FigureMgr {
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
) {
|
||||
@ -3028,7 +3028,7 @@ impl FigureMgr {
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
) {
|
||||
@ -3123,7 +3123,7 @@ impl FigureMgr {
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
) {
|
||||
@ -3309,7 +3309,7 @@ impl FigureMgr {
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
) {
|
||||
@ -3396,7 +3396,7 @@ impl FigureMgr {
|
||||
Some(s.stage_section),
|
||||
state.state_time,
|
||||
look_dir,
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
@ -3434,7 +3434,7 @@ impl FigureMgr {
|
||||
Some(s.stage_section),
|
||||
ori * anim::vek::Vec3::<f32>::unit_y(),
|
||||
state.last_ori * anim::vek::Vec3::<f32>::unit_y(),
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
@ -3461,7 +3461,7 @@ impl FigureMgr {
|
||||
Some(s.stage_section),
|
||||
state.state_time,
|
||||
look_dir,
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
@ -3484,7 +3484,7 @@ impl FigureMgr {
|
||||
};
|
||||
anim::bird_large::ShockwaveAnimation::update_skeleton(
|
||||
&target_base,
|
||||
(Some(s.stage_section), physics.on_ground),
|
||||
(Some(s.stage_section), physics.on_ground.is_some()),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
skeleton_attr,
|
||||
@ -3513,7 +3513,7 @@ impl FigureMgr {
|
||||
Some(s.stage_section),
|
||||
state.state_time,
|
||||
look_dir,
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
@ -3637,7 +3637,7 @@ impl FigureMgr {
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
) {
|
||||
@ -3726,7 +3726,7 @@ impl FigureMgr {
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
) {
|
||||
@ -4274,6 +4274,34 @@ impl FigureMgr {
|
||||
skeleton_attr,
|
||||
)
|
||||
},
|
||||
CharacterState::SpriteSummon(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::SpriteSummonAnimation::update_skeleton(
|
||||
&target_base,
|
||||
(
|
||||
(active_tool_kind, active_tool_spec),
|
||||
(second_tool_kind, second_tool_spec),
|
||||
time,
|
||||
rel_vel.magnitude(),
|
||||
Some(s.stage_section),
|
||||
),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
skeleton_attr,
|
||||
)
|
||||
},
|
||||
// TODO!
|
||||
_ => target_base,
|
||||
};
|
||||
@ -4327,7 +4355,7 @@ impl FigureMgr {
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
) {
|
||||
@ -4581,7 +4609,7 @@ impl FigureMgr {
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
) {
|
||||
@ -4710,7 +4738,7 @@ impl FigureMgr {
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
physics.on_ground.is_some(),
|
||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid().is_some(), // In water
|
||||
) {
|
||||
|
@ -469,7 +469,7 @@ impl Scene {
|
||||
let on_ground = ecs
|
||||
.read_storage::<comp::PhysicsState>()
|
||||
.get(scene_data.player_entity)
|
||||
.map(|p| p.on_ground);
|
||||
.map(|p| p.on_ground.is_some());
|
||||
|
||||
let (player_height, player_eye_height) = scene_data
|
||||
.state
|
||||
|
Loading…
Reference in New Issue
Block a user