mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'sam/staff-overhaul' into 'master'
Staff Rework Closes #767 and #762 See merge request veloren/veloren!1429
This commit is contained in:
commit
b5a969862a
@ -50,6 +50,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Split out the sections of the server settings that can be edited and saved by the server.
|
||||
- Revamped structure of where settings, logs, and game saves are stored so that almost everything is in one place.
|
||||
- Moved hammer leap attack to skillbar
|
||||
- Reworked fire staff
|
||||
|
||||
### Removed
|
||||
|
||||
|
BIN
assets/voxygen/element/icons/skill_fire_aoe.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/skill_fire_aoe.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/skill_flamethrower.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/skill_flamethrower.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/icons/staff_m2.png
(Stored with Git LFS)
BIN
assets/voxygen/element/icons/staff_m2.png
(Stored with Git LFS)
Binary file not shown.
@ -51,6 +51,8 @@ const int BEE = 11;
|
||||
const int GROUND_SHOCKWAVE = 12;
|
||||
const int HEALING_BEAM = 13;
|
||||
const int ENERGY_NATURE = 14;
|
||||
const int FLAMETHROWER = 15;
|
||||
const int FIRE_SHOCKWAVE = 16;
|
||||
|
||||
// meters per second squared (acceleration)
|
||||
const float earth_gravity = 9.807;
|
||||
@ -172,9 +174,9 @@ void main() {
|
||||
attr = Attr(
|
||||
linear_motion(
|
||||
vec3(0),
|
||||
vec3(rand4, rand5, rand6) * 40.0 + grav_vel(earth_gravity)
|
||||
vec3(rand4, rand5, rand6) * 30.0 + grav_vel(earth_gravity)
|
||||
),
|
||||
vec3(3.0 + rand0),
|
||||
vec3(2.0 + rand0),
|
||||
vec4(vec3(0.6 + rand7 * 0.4), 1),
|
||||
spin_in_axis(vec3(1,0,0),0)
|
||||
);
|
||||
@ -286,6 +288,20 @@ void main() {
|
||||
vec4(vec3(0, 1, 0), 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3)
|
||||
);
|
||||
} else if (inst_mode == FLAMETHROWER) {
|
||||
attr = Attr(
|
||||
(inst_dir * lifetime / inst_lifespan) + vec3(rand0, rand1, rand2) * (lifetime * 5 + 0.25),
|
||||
vec3(0.6 + rand3 * 0.5 + lifetime / inst_lifespan * 5),
|
||||
vec4(1, 0.6 + rand5 * 0.3 - 0.6 * lifetime / inst_lifespan, 0, 0.8 - 0.6 * lifetime / inst_lifespan),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), lifetime / inst_lifespan * 10 + 3 * rand9)
|
||||
);
|
||||
} else if (inst_mode == FIRE_SHOCKWAVE) {
|
||||
attr = Attr(
|
||||
vec3(rand0, rand1, lifetime * 10 + rand2),
|
||||
vec3(1.6 + rand3 * 1.5 + 10 * (lifetime + inst_lifespan)),
|
||||
vec4(1, 0.6 + rand7 * 0.3 - 5 * inst_lifespan + 2 * lifetime, 0, 0.8 - 3.5 * inst_lifespan),
|
||||
spin_in_axis(vec3(rand3, rand4, rand5), rand6)
|
||||
);
|
||||
} else {
|
||||
attr = Attr(
|
||||
linear_motion(
|
||||
|
@ -531,24 +531,24 @@
|
||||
),
|
||||
// Staves
|
||||
Staff("BasicStaff"): (
|
||||
vox_spec: ("weapon.staff.firestaff_starter", (-1.0, -6.0, -3.0)),
|
||||
vox_spec: ("weapon.staff.firestaff_starter", (-1.5, -3.0, -3.0)),
|
||||
color: None
|
||||
),
|
||||
Staff("BoneStaff"): (
|
||||
vox_spec: ("weapon.staff.firestaff_bone", (-1.0, -6.0, -3.0)),
|
||||
vox_spec: ("weapon.staff.firestaff_bone", (-1.5, -2.5, -3.0)),
|
||||
color: None
|
||||
),
|
||||
Staff("AmethystStaff"): (
|
||||
vox_spec: ("weapon.staff.firestaff_amethyst", (-1.0, -8.0, -4.0)),
|
||||
vox_spec: ("weapon.staff.firestaff_amethyst", (-1.5, -4.0, -4.0)),
|
||||
color: None
|
||||
),
|
||||
Staff("CultistStaff"): (
|
||||
vox_spec: ("weapon.staff.firestaff_cultist", (-2.0, -6.0, -4.0)),
|
||||
vox_spec: ("weapon.staff.firestaff_cultist", (-2.5, -2.5, -4.0)),
|
||||
color: None
|
||||
),
|
||||
// Healing staff
|
||||
Sceptre("StarterSceptre"): (
|
||||
vox_spec: ("weapon.sceptre.wood-simple", (-1.0, -6.0, -6.0)),
|
||||
vox_spec: ("weapon.sceptre.wood-simple", (-1.5, -2.5, -6.0)),
|
||||
color: None
|
||||
),
|
||||
Sceptre("Sceptre"): (
|
||||
|
BIN
assets/voxygen/voxel/weapon/staff/orc-0.vox
(Stored with Git LFS)
BIN
assets/voxygen/voxel/weapon/staff/orc-0.vox
(Stored with Git LFS)
Binary file not shown.
@ -3,7 +3,10 @@ use crate::{
|
||||
item::{armor::Protection, Item, ItemKind},
|
||||
Body, CharacterState, EnergySource, Gravity, LightEmitter, Projectile, StateUpdate,
|
||||
},
|
||||
states::{utils::StageSection, *},
|
||||
states::{
|
||||
utils::{AbilityKey, StageSection},
|
||||
*,
|
||||
},
|
||||
sys::character_behavior::JoinData,
|
||||
};
|
||||
use arraygen::Arraygen;
|
||||
@ -25,7 +28,7 @@ pub enum CharacterAbilityType {
|
||||
ComboMelee(StageSection, u32),
|
||||
LeapMelee(StageSection),
|
||||
SpinMelee(StageSection),
|
||||
GroundShockwave,
|
||||
Shockwave,
|
||||
BasicBeam,
|
||||
RepeaterRanged,
|
||||
}
|
||||
@ -43,7 +46,7 @@ impl From<&CharacterState> for CharacterAbilityType {
|
||||
CharacterState::SpinMelee(data) => Self::SpinMelee(data.stage_section),
|
||||
CharacterState::ChargedMelee(data) => Self::ChargedMelee(data.stage_section),
|
||||
CharacterState::ChargedRanged(_) => Self::ChargedRanged,
|
||||
CharacterState::GroundShockwave(_) => Self::ChargedRanged,
|
||||
CharacterState::Shockwave(_) => Self::ChargedRanged,
|
||||
CharacterState::BasicBeam(_) => Self::BasicBeam,
|
||||
CharacterState::RepeaterRanged(_) => Self::RepeaterRanged,
|
||||
_ => Self::BasicMelee,
|
||||
@ -175,19 +178,21 @@ pub enum CharacterAbility {
|
||||
initial_projectile_speed: f32,
|
||||
max_projectile_speed: f32,
|
||||
},
|
||||
GroundShockwave {
|
||||
Shockwave {
|
||||
energy_cost: u32,
|
||||
buildup_duration: Duration,
|
||||
swing_duration: Duration,
|
||||
recover_duration: Duration,
|
||||
damage: u32,
|
||||
knockback: f32,
|
||||
shockwave_angle: f32,
|
||||
shockwave_vertical_angle: f32,
|
||||
shockwave_speed: f32,
|
||||
shockwave_duration: Duration,
|
||||
requires_ground: bool,
|
||||
move_efficiency: f32,
|
||||
},
|
||||
BasicBeam {
|
||||
energy_cost: u32,
|
||||
buildup_duration: Duration,
|
||||
recover_duration: Duration,
|
||||
beam_duration: Duration,
|
||||
@ -198,6 +203,7 @@ pub enum CharacterAbility {
|
||||
max_angle: f32,
|
||||
lifesteal_eff: f32,
|
||||
energy_regen: u32,
|
||||
energy_cost: u32,
|
||||
energy_drain: u32,
|
||||
},
|
||||
}
|
||||
@ -248,11 +254,7 @@ impl CharacterAbility {
|
||||
.energy
|
||||
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
|
||||
.is_ok(),
|
||||
CharacterAbility::GroundShockwave { energy_cost, .. } => update
|
||||
.energy
|
||||
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
|
||||
.is_ok(),
|
||||
CharacterAbility::BasicBeam { energy_cost, .. } => update
|
||||
CharacterAbility::Shockwave { energy_cost, .. } => update
|
||||
.energy
|
||||
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
|
||||
.is_ok(),
|
||||
@ -349,8 +351,8 @@ impl Loadout {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&CharacterAbility> for CharacterState {
|
||||
fn from(ability: &CharacterAbility) -> Self {
|
||||
impl From<(&CharacterAbility, AbilityKey)> for CharacterState {
|
||||
fn from((ability, key): (&CharacterAbility, AbilityKey)) -> Self {
|
||||
match ability {
|
||||
CharacterAbility::BasicMelee {
|
||||
buildup_duration,
|
||||
@ -390,6 +392,7 @@ impl From<&CharacterAbility> for CharacterState {
|
||||
projectile_light: *projectile_light,
|
||||
projectile_gravity: *projectile_gravity,
|
||||
projectile_speed: *projectile_speed,
|
||||
ability_key: key,
|
||||
}),
|
||||
CharacterAbility::Boost { duration, only_up } => CharacterState::Boost(boost::Data {
|
||||
duration: *duration,
|
||||
@ -618,29 +621,37 @@ impl From<&CharacterAbility> for CharacterState {
|
||||
stage_section: StageSection::Movement,
|
||||
reps_remaining: *reps_remaining,
|
||||
}),
|
||||
CharacterAbility::GroundShockwave {
|
||||
CharacterAbility::Shockwave {
|
||||
energy_cost: _,
|
||||
buildup_duration,
|
||||
swing_duration,
|
||||
recover_duration,
|
||||
damage,
|
||||
knockback,
|
||||
shockwave_angle,
|
||||
shockwave_vertical_angle,
|
||||
shockwave_speed,
|
||||
shockwave_duration,
|
||||
requires_ground,
|
||||
} => CharacterState::GroundShockwave(ground_shockwave::Data {
|
||||
exhausted: false,
|
||||
buildup_duration: *buildup_duration,
|
||||
recover_duration: *recover_duration,
|
||||
damage: *damage,
|
||||
knockback: *knockback,
|
||||
shockwave_angle: *shockwave_angle,
|
||||
shockwave_speed: *shockwave_speed,
|
||||
shockwave_duration: *shockwave_duration,
|
||||
requires_ground: *requires_ground,
|
||||
move_efficiency,
|
||||
} => CharacterState::Shockwave(shockwave::Data {
|
||||
static_data: shockwave::StaticData {
|
||||
buildup_duration: *buildup_duration,
|
||||
swing_duration: *swing_duration,
|
||||
recover_duration: *recover_duration,
|
||||
damage: *damage,
|
||||
knockback: *knockback,
|
||||
shockwave_angle: *shockwave_angle,
|
||||
shockwave_vertical_angle: *shockwave_vertical_angle,
|
||||
shockwave_speed: *shockwave_speed,
|
||||
shockwave_duration: *shockwave_duration,
|
||||
requires_ground: *requires_ground,
|
||||
move_efficiency: *move_efficiency,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
}),
|
||||
CharacterAbility::BasicBeam {
|
||||
energy_cost: _,
|
||||
buildup_duration,
|
||||
recover_duration,
|
||||
beam_duration,
|
||||
@ -651,6 +662,7 @@ impl From<&CharacterAbility> for CharacterState {
|
||||
max_angle,
|
||||
lifesteal_eff,
|
||||
energy_regen,
|
||||
energy_cost,
|
||||
energy_drain,
|
||||
} => CharacterState::BasicBeam(basic_beam::Data {
|
||||
static_data: basic_beam::StaticData {
|
||||
@ -664,7 +676,9 @@ impl From<&CharacterAbility> for CharacterState {
|
||||
max_angle: *max_angle,
|
||||
lifesteal_eff: *lifesteal_eff,
|
||||
energy_regen: *energy_regen,
|
||||
energy_cost: *energy_cost,
|
||||
energy_drain: *energy_drain,
|
||||
ability_key: key,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
|
@ -12,7 +12,7 @@ pub struct Properties {
|
||||
pub heal: u32,
|
||||
pub lifesteal_eff: f32,
|
||||
pub energy_regen: u32,
|
||||
pub energy_drain: u32,
|
||||
pub energy_cost: u32,
|
||||
pub duration: Duration,
|
||||
pub owner: Option<Uid>,
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ pub enum CharacterState {
|
||||
/// A repeating ranged attack
|
||||
RepeaterRanged(repeater_ranged::Data),
|
||||
/// A ground shockwave attack
|
||||
GroundShockwave(ground_shockwave::Data),
|
||||
Shockwave(shockwave::Data),
|
||||
/// A continuous attack that affects all creatures in a cone originating
|
||||
/// from the source
|
||||
BasicBeam(basic_beam::Data),
|
||||
@ -94,7 +94,7 @@ impl CharacterState {
|
||||
| CharacterState::ChargedMelee(_)
|
||||
| CharacterState::ChargedRanged(_)
|
||||
| CharacterState::RepeaterRanged(_)
|
||||
| CharacterState::GroundShockwave(_)
|
||||
| CharacterState::Shockwave(_)
|
||||
| CharacterState::BasicBeam(_)
|
||||
)
|
||||
}
|
||||
@ -110,7 +110,7 @@ impl CharacterState {
|
||||
| CharacterState::ChargedMelee(_)
|
||||
| CharacterState::ChargedRanged(_)
|
||||
| CharacterState::RepeaterRanged(_)
|
||||
| CharacterState::GroundShockwave(_)
|
||||
| CharacterState::Shockwave(_)
|
||||
| CharacterState::BasicBeam(_)
|
||||
)
|
||||
}
|
||||
@ -126,7 +126,7 @@ impl CharacterState {
|
||||
| CharacterState::ChargedMelee(_)
|
||||
| CharacterState::ChargedRanged(_)
|
||||
| CharacterState::RepeaterRanged(_)
|
||||
| CharacterState::GroundShockwave(_)
|
||||
| CharacterState::Shockwave(_)
|
||||
| CharacterState::BasicBeam(_)
|
||||
)
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
use crate::{
|
||||
comp::{body::object, projectile, Body, CharacterAbility, Gravity, LightEmitter, Projectile},
|
||||
states::combo_melee,
|
||||
Explosion,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
@ -362,18 +363,18 @@ impl Tool {
|
||||
}],
|
||||
Sceptre(_) => vec![
|
||||
BasicBeam {
|
||||
energy_cost: 0,
|
||||
buildup_duration: Duration::from_millis(250),
|
||||
recover_duration: Duration::from_millis(250),
|
||||
beam_duration: Duration::from_secs(1),
|
||||
base_hps: (60.0 * self.base_power()) as u32,
|
||||
base_dps: (40.0 * self.base_power()) as u32,
|
||||
base_dps: (60.0 * self.base_power()) as u32,
|
||||
tick_rate: 2.0,
|
||||
range: 25.0,
|
||||
max_angle: 1.0,
|
||||
lifesteal_eff: 0.25,
|
||||
lifesteal_eff: 0.20,
|
||||
energy_regen: 50,
|
||||
energy_drain: 100,
|
||||
energy_cost: 100,
|
||||
energy_drain: 0,
|
||||
},
|
||||
BasicRanged {
|
||||
energy_cost: 800,
|
||||
@ -382,17 +383,27 @@ impl Tool {
|
||||
recover_duration: Duration::from_millis(50),
|
||||
projectile: Projectile {
|
||||
hit_solid: vec![
|
||||
projectile::Effect::Explode {
|
||||
power: 1.4 * self.base_power(),
|
||||
percent_damage: 0.2,
|
||||
},
|
||||
projectile::Effect::Explode(Explosion {
|
||||
radius: 3.0 + 2.5 * self.base_power(),
|
||||
max_damage: (50.0 * self.base_power()) as u32,
|
||||
min_damage: (20.0 * self.base_power()) as u32,
|
||||
max_heal: (140.0 * self.base_power()) as u32,
|
||||
min_heal: (50.0 * self.base_power()) as u32,
|
||||
terrain_destruction_power: 0.0,
|
||||
energy_regen: 0,
|
||||
}),
|
||||
projectile::Effect::Vanish,
|
||||
],
|
||||
hit_entity: vec![
|
||||
projectile::Effect::Explode {
|
||||
power: 1.4 * self.base_power(),
|
||||
percent_damage: 0.2,
|
||||
},
|
||||
projectile::Effect::Explode(Explosion {
|
||||
radius: 3.0 + 2.5 * self.base_power(),
|
||||
max_damage: (50.0 * self.base_power()) as u32,
|
||||
min_damage: (20.0 * self.base_power()) as u32,
|
||||
max_heal: (140.0 * self.base_power()) as u32,
|
||||
min_heal: (50.0 * self.base_power()) as u32,
|
||||
terrain_destruction_power: 0.0,
|
||||
energy_regen: 0,
|
||||
}),
|
||||
projectile::Effect::Vanish,
|
||||
],
|
||||
time_left: Duration::from_secs(20),
|
||||
@ -409,25 +420,34 @@ impl Tool {
|
||||
},
|
||||
],
|
||||
Staff(_) => vec![
|
||||
BasicMelee {
|
||||
energy_cost: 0,
|
||||
buildup_duration: Duration::from_millis(100),
|
||||
recover_duration: Duration::from_millis(300),
|
||||
base_healthchange: (-40.0 * self.base_power()) as i32,
|
||||
knockback: 0.0,
|
||||
range: 3.5,
|
||||
max_angle: 20.0,
|
||||
},
|
||||
BasicRanged {
|
||||
energy_cost: 0,
|
||||
holdable: false,
|
||||
prepare_duration: Duration::from_millis(250),
|
||||
recover_duration: Duration::from_millis(600),
|
||||
prepare_duration: Duration::from_millis(500),
|
||||
recover_duration: Duration::from_millis(350),
|
||||
projectile: Projectile {
|
||||
hit_solid: vec![projectile::Effect::Vanish],
|
||||
hit_solid: vec![
|
||||
projectile::Effect::Explode(Explosion {
|
||||
radius: 5.0,
|
||||
max_damage: (100.0 * self.base_power()) as u32,
|
||||
min_damage: 0,
|
||||
max_heal: 0,
|
||||
min_heal: 0,
|
||||
terrain_destruction_power: 0.0,
|
||||
energy_regen: 50,
|
||||
}),
|
||||
projectile::Effect::Vanish,
|
||||
],
|
||||
hit_entity: vec![
|
||||
projectile::Effect::Damage((-40.0 * self.base_power()) as i32),
|
||||
projectile::Effect::RewardEnergy(150),
|
||||
projectile::Effect::Explode(Explosion {
|
||||
radius: 5.0,
|
||||
max_damage: (100.0 * self.base_power()) as u32,
|
||||
min_damage: 0,
|
||||
max_heal: 0,
|
||||
min_heal: 0,
|
||||
terrain_destruction_power: 0.0,
|
||||
energy_regen: 50,
|
||||
}),
|
||||
projectile::Effect::Vanish,
|
||||
],
|
||||
time_left: Duration::from_secs(20),
|
||||
@ -435,44 +455,40 @@ impl Tool {
|
||||
ignore_group: true,
|
||||
},
|
||||
projectile_body: Body::Object(object::Body::BoltFire),
|
||||
projectile_light: Some(LightEmitter {
|
||||
col: (0.85, 0.5, 0.11).into(),
|
||||
..Default::default()
|
||||
}),
|
||||
projectile_gravity: None,
|
||||
projectile_speed: 100.0,
|
||||
},
|
||||
BasicRanged {
|
||||
energy_cost: 400,
|
||||
holdable: true,
|
||||
prepare_duration: Duration::from_millis(800),
|
||||
recover_duration: Duration::from_millis(50),
|
||||
projectile: Projectile {
|
||||
hit_solid: vec![
|
||||
projectile::Effect::Explode {
|
||||
power: 1.4 * self.base_power(),
|
||||
percent_damage: 1.0,
|
||||
},
|
||||
projectile::Effect::Vanish,
|
||||
],
|
||||
hit_entity: vec![
|
||||
projectile::Effect::Explode {
|
||||
power: 1.4 * self.base_power(),
|
||||
percent_damage: 1.0,
|
||||
},
|
||||
projectile::Effect::Vanish,
|
||||
],
|
||||
time_left: Duration::from_secs(20),
|
||||
owner: None,
|
||||
ignore_group: true,
|
||||
},
|
||||
projectile_body: Body::Object(object::Body::BoltFireBig),
|
||||
projectile_light: Some(LightEmitter {
|
||||
col: (1.0, 0.75, 0.11).into(),
|
||||
..Default::default()
|
||||
}),
|
||||
projectile_gravity: None,
|
||||
projectile_speed: 100.0,
|
||||
projectile_gravity: Some(Gravity(0.3)),
|
||||
projectile_speed: 60.0,
|
||||
},
|
||||
BasicBeam {
|
||||
buildup_duration: Duration::from_millis(250),
|
||||
recover_duration: Duration::from_millis(250),
|
||||
beam_duration: Duration::from_millis(500),
|
||||
base_hps: 0,
|
||||
base_dps: (150.0 * self.base_power()) as u32,
|
||||
tick_rate: 3.0,
|
||||
range: 15.0,
|
||||
max_angle: 22.5,
|
||||
lifesteal_eff: 0.0,
|
||||
energy_regen: 0,
|
||||
energy_cost: 0,
|
||||
energy_drain: 350,
|
||||
},
|
||||
Shockwave {
|
||||
energy_cost: 600,
|
||||
buildup_duration: Duration::from_millis(700),
|
||||
swing_duration: Duration::from_millis(100),
|
||||
recover_duration: Duration::from_millis(300),
|
||||
damage: (200.0 * self.base_power()) as u32,
|
||||
knockback: 25.0,
|
||||
shockwave_angle: 360.0,
|
||||
shockwave_vertical_angle: 90.0,
|
||||
shockwave_speed: 20.0,
|
||||
shockwave_duration: Duration::from_millis(500),
|
||||
requires_ground: false,
|
||||
move_efficiency: 0.1,
|
||||
},
|
||||
],
|
||||
Shield(_) => vec![
|
||||
@ -499,16 +515,19 @@ impl Tool {
|
||||
range: 5.0,
|
||||
max_angle: 120.0,
|
||||
},
|
||||
GroundShockwave {
|
||||
Shockwave {
|
||||
energy_cost: 0,
|
||||
buildup_duration: Duration::from_millis(500),
|
||||
recover_duration: Duration::from_millis(1000),
|
||||
swing_duration: Duration::from_millis(200),
|
||||
recover_duration: Duration::from_millis(800),
|
||||
damage: 500,
|
||||
knockback: -40.0,
|
||||
shockwave_angle: 90.0,
|
||||
shockwave_vertical_angle: 15.0,
|
||||
shockwave_speed: 20.0,
|
||||
shockwave_duration: Duration::from_millis(2000),
|
||||
requires_ground: true,
|
||||
move_efficiency: 0.05,
|
||||
},
|
||||
]
|
||||
} else {
|
||||
|
@ -54,7 +54,7 @@ pub use misc::Object;
|
||||
pub use phys::{Collider, ForceUpdate, Gravity, Mass, Ori, PhysicsState, Pos, Scale, Sticky, Vel};
|
||||
pub use player::{Player, MAX_MOUNT_RANGE_SQR};
|
||||
pub use projectile::Projectile;
|
||||
pub use shockwave::Shockwave;
|
||||
pub use shockwave::{Shockwave, ShockwaveHitEntities};
|
||||
pub use skills::{Skill, SkillGroup, SkillGroupType, SkillSet};
|
||||
pub use stats::{Exp, HealthChange, HealthSource, Level, Stats};
|
||||
pub use visual::{LightAnimation, LightEmitter};
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::sync::Uid;
|
||||
use crate::{sync::Uid, Explosion};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::{Component, FlaggedStorage};
|
||||
use specs_idvs::IdvStorage;
|
||||
@ -9,7 +9,7 @@ pub enum Effect {
|
||||
Damage(i32),
|
||||
Knockback(f32),
|
||||
RewardEnergy(u32),
|
||||
Explode { power: f32, percent_damage: f32 },
|
||||
Explode(Explosion),
|
||||
Vanish,
|
||||
Stick,
|
||||
Possess,
|
||||
|
@ -7,6 +7,7 @@ use std::time::Duration;
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Properties {
|
||||
pub angle: f32,
|
||||
pub vertical_angle: f32,
|
||||
pub speed: f32,
|
||||
pub damage: u32,
|
||||
pub knockback: f32,
|
||||
@ -34,3 +35,12 @@ impl std::ops::Deref for Shockwave {
|
||||
|
||||
fn deref(&self) -> &Properties { &self.properties }
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ShockwaveHitEntities {
|
||||
pub hit_entities: Vec<Uid>,
|
||||
}
|
||||
|
||||
impl Component for ShockwaveHitEntities {
|
||||
type Storage = IdvStorage<Self>;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{character::CharacterId, comp, sync::Uid, util::Dir};
|
||||
use crate::{character::CharacterId, comp, sync::Uid, util::Dir, Explosion};
|
||||
use comp::{
|
||||
item::{Item, Reagent},
|
||||
Ori, Pos,
|
||||
@ -29,11 +29,10 @@ pub enum LocalEvent {
|
||||
pub enum ServerEvent {
|
||||
Explosion {
|
||||
pos: Vec3<f32>,
|
||||
power: f32,
|
||||
explosion: Explosion,
|
||||
owner: Option<Uid>,
|
||||
friendly_damage: bool,
|
||||
reagent: Option<Reagent>,
|
||||
percent_damage: f32,
|
||||
},
|
||||
Damage {
|
||||
uid: Uid,
|
||||
|
12
common/src/explosion.rs
Normal file
12
common/src/explosion.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Explosion {
|
||||
pub radius: f32,
|
||||
pub max_damage: u32,
|
||||
pub min_damage: u32,
|
||||
pub max_heal: u32,
|
||||
pub min_heal: u32,
|
||||
pub terrain_destruction_power: f32,
|
||||
pub energy_regen: u32,
|
||||
}
|
@ -25,6 +25,7 @@ pub mod cmd;
|
||||
pub mod comp;
|
||||
pub mod effect;
|
||||
pub mod event;
|
||||
pub mod explosion;
|
||||
pub mod figure;
|
||||
pub mod generation;
|
||||
pub mod loadout_builder;
|
||||
@ -50,4 +51,5 @@ pub mod util;
|
||||
pub mod vol;
|
||||
pub mod volumes;
|
||||
|
||||
pub use explosion::Explosion;
|
||||
pub use loadout_builder::LoadoutBuilder;
|
||||
|
@ -13,8 +13,9 @@ pub enum Outcome {
|
||||
Explosion {
|
||||
pos: Vec3<f32>,
|
||||
power: f32,
|
||||
radius: f32,
|
||||
is_attack: bool,
|
||||
reagent: Option<Reagent>, // How can we better define this?
|
||||
percent_damage: f32,
|
||||
},
|
||||
ProjectileShot {
|
||||
pos: Vec3<f32>,
|
||||
|
@ -127,6 +127,7 @@ impl State {
|
||||
ecs.register::<comp::Object>();
|
||||
ecs.register::<comp::Group>();
|
||||
ecs.register::<comp::Shockwave>();
|
||||
ecs.register::<comp::ShockwaveHitEntities>();
|
||||
ecs.register::<comp::BeamSegment>();
|
||||
|
||||
// Register components send from clients -> server
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
comp::{beam, humanoid, Body, CharacterState, Ori, Pos, StateUpdate},
|
||||
comp::{beam, humanoid, Body, CharacterState, EnergySource, Ori, Pos, StateUpdate},
|
||||
event::ServerEvent,
|
||||
states::utils::{StageSection, *},
|
||||
states::utils::*,
|
||||
sync::Uid,
|
||||
sys::character_behavior::{CharacterBehavior, JoinData},
|
||||
};
|
||||
@ -34,7 +34,11 @@ pub struct StaticData {
|
||||
/// Energy regened per second for damage ticks
|
||||
pub energy_regen: u32,
|
||||
/// Energy consumed per second for heal ticks
|
||||
pub energy_cost: u32,
|
||||
/// Energy drained per
|
||||
pub energy_drain: u32,
|
||||
/// What key is used to press ability
|
||||
pub ability_key: AbilityKey,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@ -96,20 +100,22 @@ impl CharacterBehavior for Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Cast,
|
||||
particle_ori: Some(*data.inputs.look_dir),
|
||||
offset: eye_height * 0.9,
|
||||
offset: eye_height * 0.55,
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Cast => {
|
||||
if data.inputs.primary.is_pressed() {
|
||||
if ability_key_is_pressed(data, self.static_data.ability_key)
|
||||
&& (self.static_data.energy_drain == 0 || update.energy.current() > 0)
|
||||
{
|
||||
let damage =
|
||||
(self.static_data.base_dps as f32 / self.static_data.tick_rate) as u32;
|
||||
let heal =
|
||||
(self.static_data.base_hps as f32 / self.static_data.tick_rate) as u32;
|
||||
let energy_regen =
|
||||
(self.static_data.energy_regen as f32 / self.static_data.tick_rate) as u32;
|
||||
let energy_drain =
|
||||
(self.static_data.energy_drain as f32 / self.static_data.tick_rate) as u32;
|
||||
let energy_cost =
|
||||
(self.static_data.energy_cost as f32 / self.static_data.tick_rate) as u32;
|
||||
let speed =
|
||||
self.static_data.range / self.static_data.beam_duration.as_secs_f32();
|
||||
let properties = beam::Properties {
|
||||
@ -119,7 +125,7 @@ impl CharacterBehavior for Data {
|
||||
heal,
|
||||
lifesteal_eff: self.static_data.lifesteal_eff,
|
||||
energy_regen,
|
||||
energy_drain,
|
||||
energy_cost,
|
||||
duration: self.static_data.beam_duration,
|
||||
owner: Some(*data.uid),
|
||||
};
|
||||
@ -132,11 +138,20 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
update.character = CharacterState::BasicBeam(Data {
|
||||
static_data: self.static_data,
|
||||
timer: self.timer,
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
stage_section: self.stage_section,
|
||||
particle_ori: Some(*data.inputs.look_dir),
|
||||
offset: self.offset,
|
||||
});
|
||||
|
||||
// Consumes energy if there's enough left and ability key is held down
|
||||
update.energy.change_by(
|
||||
-(self.static_data.energy_drain as f32 * data.dt.0) as i32,
|
||||
EnergySource::Ability,
|
||||
);
|
||||
} else {
|
||||
update.character = CharacterState::BasicBeam(Data {
|
||||
static_data: self.static_data,
|
||||
|
@ -24,6 +24,8 @@ pub struct Data {
|
||||
pub projectile_speed: f32,
|
||||
/// Whether the attack fired already
|
||||
pub exhausted: bool,
|
||||
/// What key is used to press ability
|
||||
pub ability_key: AbilityKey,
|
||||
}
|
||||
|
||||
impl CharacterBehavior for Data {
|
||||
@ -35,7 +37,8 @@ impl CharacterBehavior for Data {
|
||||
|
||||
if !self.exhausted
|
||||
&& if self.holdable {
|
||||
data.inputs.holding_ability_key() || self.prepare_timer < self.prepare_duration
|
||||
ability_key_is_pressed(data, self.ability_key)
|
||||
|| self.prepare_timer < self.prepare_duration
|
||||
} else {
|
||||
self.prepare_timer < self.prepare_duration
|
||||
}
|
||||
@ -52,6 +55,7 @@ impl CharacterBehavior for Data {
|
||||
projectile_gravity: self.projectile_gravity,
|
||||
projectile_speed: self.projectile_speed,
|
||||
exhausted: false,
|
||||
ability_key: self.ability_key,
|
||||
});
|
||||
} else if !self.exhausted {
|
||||
// Fire
|
||||
@ -78,6 +82,7 @@ impl CharacterBehavior for Data {
|
||||
projectile_gravity: self.projectile_gravity,
|
||||
projectile_speed: self.projectile_speed,
|
||||
exhausted: true,
|
||||
ability_key: self.ability_key,
|
||||
});
|
||||
} else if self.recover_duration != Duration::default() {
|
||||
// Recovery
|
||||
@ -95,6 +100,7 @@ impl CharacterBehavior for Data {
|
||||
projectile_gravity: self.projectile_gravity,
|
||||
projectile_speed: self.projectile_speed,
|
||||
exhausted: true,
|
||||
ability_key: self.ability_key,
|
||||
});
|
||||
return update;
|
||||
} else {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
comp::{Attacking, CharacterState, EnergySource, StateUpdate},
|
||||
states::utils::{StageSection, *},
|
||||
states::utils::*,
|
||||
sys::character_behavior::{CharacterBehavior, JoinData},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
comp::{Attacking, CharacterState, EnergySource, StateUpdate},
|
||||
states::utils::{StageSection, *},
|
||||
states::utils::*,
|
||||
sys::character_behavior::{CharacterBehavior, JoinData},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -1,107 +0,0 @@
|
||||
use crate::{
|
||||
comp::{shockwave, Attacking, CharacterState, StateUpdate},
|
||||
event::ServerEvent,
|
||||
states::utils::*,
|
||||
sys::character_behavior::{CharacterBehavior, JoinData},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Data {
|
||||
/// Whether the attack can deal more damage
|
||||
pub exhausted: bool,
|
||||
/// How long until state should deal damage
|
||||
pub buildup_duration: Duration,
|
||||
/// How long the state has until exiting
|
||||
pub recover_duration: Duration,
|
||||
/// Base damage
|
||||
pub damage: u32,
|
||||
/// Knockback
|
||||
pub knockback: f32,
|
||||
/// Angle of the shockwave
|
||||
pub shockwave_angle: f32,
|
||||
/// Speed of the shockwave
|
||||
pub shockwave_speed: f32,
|
||||
/// How long the shockwave travels for
|
||||
pub shockwave_duration: Duration,
|
||||
/// Whether the shockwave requires the target to be on the ground
|
||||
pub requires_ground: bool,
|
||||
}
|
||||
|
||||
impl CharacterBehavior for Data {
|
||||
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
|
||||
handle_move(data, &mut update, 0.05);
|
||||
|
||||
if self.buildup_duration != Duration::default() {
|
||||
// Build up
|
||||
update.character = CharacterState::GroundShockwave(Data {
|
||||
exhausted: self.exhausted,
|
||||
buildup_duration: self
|
||||
.buildup_duration
|
||||
.checked_sub(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
recover_duration: self.recover_duration,
|
||||
damage: self.damage,
|
||||
knockback: self.knockback,
|
||||
shockwave_angle: self.shockwave_angle,
|
||||
shockwave_speed: self.shockwave_speed,
|
||||
shockwave_duration: self.shockwave_duration,
|
||||
requires_ground: self.requires_ground,
|
||||
});
|
||||
} else if !self.exhausted {
|
||||
// Attack
|
||||
let properties = shockwave::Properties {
|
||||
angle: self.shockwave_angle,
|
||||
speed: self.shockwave_speed,
|
||||
duration: self.shockwave_duration,
|
||||
damage: self.damage,
|
||||
knockback: self.knockback,
|
||||
requires_ground: self.requires_ground,
|
||||
owner: Some(*data.uid),
|
||||
};
|
||||
update.server_events.push_front(ServerEvent::Shockwave {
|
||||
properties,
|
||||
pos: *data.pos,
|
||||
ori: *data.ori,
|
||||
});
|
||||
|
||||
update.character = CharacterState::GroundShockwave(Data {
|
||||
exhausted: true,
|
||||
buildup_duration: self.buildup_duration,
|
||||
recover_duration: self.recover_duration,
|
||||
damage: self.damage,
|
||||
knockback: self.knockback,
|
||||
shockwave_angle: self.shockwave_angle,
|
||||
shockwave_speed: self.shockwave_speed,
|
||||
shockwave_duration: self.shockwave_duration,
|
||||
requires_ground: self.requires_ground,
|
||||
});
|
||||
} else if self.recover_duration != Duration::default() {
|
||||
// Recovery
|
||||
update.character = CharacterState::GroundShockwave(Data {
|
||||
exhausted: self.exhausted,
|
||||
buildup_duration: self.buildup_duration,
|
||||
recover_duration: self
|
||||
.recover_duration
|
||||
.checked_sub(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
damage: self.damage,
|
||||
knockback: self.knockback,
|
||||
shockwave_angle: self.shockwave_angle,
|
||||
shockwave_speed: self.shockwave_speed,
|
||||
shockwave_duration: self.shockwave_duration,
|
||||
requires_ground: self.requires_ground,
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
update.character = CharacterState::Wielding;
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Attacking>(data.entity);
|
||||
}
|
||||
|
||||
update
|
||||
}
|
||||
}
|
@ -12,11 +12,11 @@ pub mod dash_melee;
|
||||
pub mod equipping;
|
||||
pub mod glide;
|
||||
pub mod glide_wield;
|
||||
pub mod ground_shockwave;
|
||||
pub mod idle;
|
||||
pub mod leap_melee;
|
||||
pub mod repeater_ranged;
|
||||
pub mod roll;
|
||||
pub mod shockwave;
|
||||
pub mod sit;
|
||||
pub mod sneak;
|
||||
pub mod spin_melee;
|
||||
|
136
common/src/states/shockwave.rs
Normal file
136
common/src/states/shockwave.rs
Normal file
@ -0,0 +1,136 @@
|
||||
use crate::{
|
||||
comp::{shockwave, CharacterState, StateUpdate},
|
||||
event::ServerEvent,
|
||||
states::utils::*,
|
||||
sys::character_behavior::{CharacterBehavior, JoinData},
|
||||
};
|
||||
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 deal damage
|
||||
pub buildup_duration: Duration,
|
||||
/// How long the state is swinging for
|
||||
pub swing_duration: Duration,
|
||||
/// How long the state has until exiting
|
||||
pub recover_duration: Duration,
|
||||
/// Base damage
|
||||
pub damage: u32,
|
||||
/// Knockback
|
||||
pub knockback: f32,
|
||||
/// Angle of the shockwave
|
||||
pub shockwave_angle: f32,
|
||||
/// Vertical angle of the shockwave
|
||||
pub shockwave_vertical_angle: f32,
|
||||
/// Speed of the shockwave
|
||||
pub shockwave_speed: f32,
|
||||
/// How long the shockwave travels for
|
||||
pub shockwave_duration: Duration,
|
||||
/// Whether the shockwave requires the target to be on the ground
|
||||
pub requires_ground: bool,
|
||||
/// Movement speed efficiency
|
||||
pub move_efficiency: f32,
|
||||
}
|
||||
|
||||
#[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, self.static_data.move_efficiency);
|
||||
|
||||
match self.stage_section {
|
||||
StageSection::Buildup => {
|
||||
if self.timer < self.static_data.buildup_duration {
|
||||
// Build up
|
||||
update.character = CharacterState::Shockwave(Data {
|
||||
static_data: self.static_data,
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
stage_section: self.stage_section,
|
||||
});
|
||||
} else {
|
||||
// Attack
|
||||
let properties = shockwave::Properties {
|
||||
angle: self.static_data.shockwave_angle,
|
||||
vertical_angle: self.static_data.shockwave_vertical_angle,
|
||||
speed: self.static_data.shockwave_speed,
|
||||
duration: self.static_data.shockwave_duration,
|
||||
damage: self.static_data.damage,
|
||||
knockback: self.static_data.knockback,
|
||||
requires_ground: self.static_data.requires_ground,
|
||||
owner: Some(*data.uid),
|
||||
};
|
||||
update.server_events.push_front(ServerEvent::Shockwave {
|
||||
properties,
|
||||
pos: *data.pos,
|
||||
ori: *data.ori,
|
||||
});
|
||||
|
||||
// Transitions to swing
|
||||
update.character = CharacterState::Shockwave(Data {
|
||||
static_data: self.static_data,
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Swing,
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Swing => {
|
||||
if self.timer < self.static_data.swing_duration {
|
||||
// Swings
|
||||
update.character = CharacterState::Shockwave(Data {
|
||||
static_data: self.static_data,
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
stage_section: self.stage_section,
|
||||
});
|
||||
} else {
|
||||
// Transitions to recover
|
||||
update.character = CharacterState::Shockwave(Data {
|
||||
static_data: self.static_data,
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Recover,
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Recover => {
|
||||
if self.timer < self.static_data.swing_duration {
|
||||
// Recovers
|
||||
update.character = CharacterState::Shockwave(Data {
|
||||
static_data: self.static_data,
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
stage_section: self.stage_section,
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
update.character = CharacterState::Wielding;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
update.character = CharacterState::Wielding;
|
||||
},
|
||||
}
|
||||
|
||||
update
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
comp::{Attacking, CharacterState, EnergySource, StateUpdate},
|
||||
states::utils::{StageSection, *},
|
||||
states::utils::*,
|
||||
sys::character_behavior::{CharacterBehavior, JoinData},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -253,7 +253,7 @@ pub fn handle_ability1_input(data: &JoinData, update: &mut StateUpdate) {
|
||||
.and_then(|i| i.ability1.as_ref())
|
||||
.filter(|ability| ability.requirements_paid(data, update))
|
||||
{
|
||||
update.character = ability.into();
|
||||
update.character = (ability, AbilityKey::Mouse1).into();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -283,7 +283,7 @@ pub fn handle_ability2_input(data: &JoinData, update: &mut StateUpdate) {
|
||||
.and_then(|i| i.ability2.as_ref())
|
||||
.filter(|ability| ability.requirements_paid(data, update))
|
||||
{
|
||||
update.character = ability.into();
|
||||
update.character = (ability, AbilityKey::Mouse2).into();
|
||||
}
|
||||
},
|
||||
(_, Some(Hands::OneHand)) => {
|
||||
@ -294,7 +294,7 @@ pub fn handle_ability2_input(data: &JoinData, update: &mut StateUpdate) {
|
||||
.and_then(|i| i.ability2.as_ref())
|
||||
.filter(|ability| ability.requirements_paid(data, update))
|
||||
{
|
||||
update.character = ability.into();
|
||||
update.character = (ability, AbilityKey::Mouse2).into();
|
||||
}
|
||||
},
|
||||
(_, _) => {},
|
||||
@ -312,7 +312,7 @@ pub fn handle_ability3_input(data: &JoinData, update: &mut StateUpdate) {
|
||||
.and_then(|i| i.ability3.as_ref())
|
||||
.filter(|ability| ability.requirements_paid(data, update))
|
||||
{
|
||||
update.character = ability.into();
|
||||
update.character = (ability, AbilityKey::Skill1).into();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -329,12 +329,12 @@ pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) {
|
||||
.filter(|ability| ability.requirements_paid(data, update))
|
||||
{
|
||||
if data.character.is_wield() {
|
||||
update.character = ability.into();
|
||||
update.character = (ability, AbilityKey::Dodge).into();
|
||||
if let CharacterState::Roll(roll) = &mut update.character {
|
||||
roll.was_wielded = true;
|
||||
}
|
||||
} else {
|
||||
update.character = ability.into();
|
||||
update.character = (ability, AbilityKey::Dodge).into();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -355,6 +355,15 @@ pub fn handle_interrupt(data: &JoinData, update: &mut StateUpdate) {
|
||||
handle_dodge_input(data, update);
|
||||
}
|
||||
|
||||
pub fn ability_key_is_pressed(data: &JoinData, ability_key: AbilityKey) -> bool {
|
||||
match ability_key {
|
||||
AbilityKey::Mouse1 => data.inputs.primary.is_pressed(),
|
||||
AbilityKey::Mouse2 => data.inputs.secondary.is_pressed(),
|
||||
AbilityKey::Skill1 => data.inputs.ability3.is_pressed(),
|
||||
AbilityKey::Dodge => data.inputs.roll.is_pressed(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines what portion a state is in. Used in all attacks (eventually). Is
|
||||
/// used to control aspects of animation code, as well as logic within the
|
||||
/// character states.
|
||||
@ -368,3 +377,11 @@ pub enum StageSection {
|
||||
Shoot,
|
||||
Movement,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub enum AbilityKey {
|
||||
Mouse1,
|
||||
Mouse2,
|
||||
Skill1,
|
||||
Dodge,
|
||||
}
|
||||
|
@ -5,9 +5,9 @@ use crate::{
|
||||
group,
|
||||
group::Invite,
|
||||
item::{tool::ToolKind, ItemKind},
|
||||
Agent, Alignment, Body, CharacterState, ControlAction, ControlEvent, Controller,
|
||||
GroupManip, LightEmitter, Loadout, MountState, Ori, PhysicsState, Pos, Scale, Stats,
|
||||
UnresolvedChatMsg, Vel,
|
||||
Agent, Alignment, Body, ControlAction, ControlEvent, Controller, Energy, GroupManip,
|
||||
LightEmitter, Loadout, MountState, Ori, PhysicsState, Pos, Scale, Stats, UnresolvedChatMsg,
|
||||
Vel,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
metrics::SysMetrics,
|
||||
@ -45,7 +45,6 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Scale>,
|
||||
ReadStorage<'a, Stats>,
|
||||
ReadStorage<'a, Loadout>,
|
||||
ReadStorage<'a, CharacterState>,
|
||||
ReadStorage<'a, PhysicsState>,
|
||||
ReadStorage<'a, Uid>,
|
||||
ReadStorage<'a, group::Group>,
|
||||
@ -58,6 +57,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Invite>,
|
||||
Read<'a, TimeOfDay>,
|
||||
ReadStorage<'a, LightEmitter>,
|
||||
ReadStorage<'a, Energy>,
|
||||
);
|
||||
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
@ -77,7 +77,6 @@ impl<'a> System<'a> for Sys {
|
||||
scales,
|
||||
stats,
|
||||
loadouts,
|
||||
character_states,
|
||||
physics_states,
|
||||
uids,
|
||||
groups,
|
||||
@ -90,6 +89,7 @@ impl<'a> System<'a> for Sys {
|
||||
invites,
|
||||
time_of_day,
|
||||
light_emitter,
|
||||
energies,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let start_time = std::time::Instant::now();
|
||||
@ -101,7 +101,6 @@ impl<'a> System<'a> for Sys {
|
||||
ori,
|
||||
alignment,
|
||||
loadout,
|
||||
character_state,
|
||||
physics_state,
|
||||
body,
|
||||
uid,
|
||||
@ -110,6 +109,7 @@ impl<'a> System<'a> for Sys {
|
||||
mount_state,
|
||||
group,
|
||||
light_emitter,
|
||||
energy,
|
||||
) in (
|
||||
&entities,
|
||||
&positions,
|
||||
@ -117,7 +117,6 @@ impl<'a> System<'a> for Sys {
|
||||
&orientations,
|
||||
alignments.maybe(),
|
||||
&loadouts,
|
||||
&character_states,
|
||||
&physics_states,
|
||||
bodies.maybe(),
|
||||
&uids,
|
||||
@ -126,6 +125,7 @@ impl<'a> System<'a> for Sys {
|
||||
mount_states.maybe(),
|
||||
groups.maybe(),
|
||||
light_emitter.maybe(),
|
||||
&energies,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
@ -298,6 +298,7 @@ impl<'a> System<'a> for Sys {
|
||||
powerup,
|
||||
..
|
||||
} => {
|
||||
#[derive(Eq, PartialEq)]
|
||||
enum Tactic {
|
||||
Melee,
|
||||
RangedPowerup,
|
||||
@ -389,7 +390,10 @@ impl<'a> System<'a> for Sys {
|
||||
} else {
|
||||
do_idle = true;
|
||||
}
|
||||
} else if dist_sqrd < (MIN_ATTACK_DIST * scale).powf(2.0) {
|
||||
} else if (tactic == Tactic::Staff
|
||||
&& dist_sqrd < (5.0 * MIN_ATTACK_DIST * scale).powf(2.0))
|
||||
|| dist_sqrd < (MIN_ATTACK_DIST * scale).powf(2.0)
|
||||
{
|
||||
// Close-range attack
|
||||
inputs.move_dir = Vec2::from(tgt_pos.0 - pos.0)
|
||||
.try_normalized()
|
||||
@ -397,9 +401,16 @@ impl<'a> System<'a> for Sys {
|
||||
* 0.1;
|
||||
|
||||
match tactic {
|
||||
Tactic::Melee | Tactic::Staff | Tactic::StoneGolemBoss => {
|
||||
Tactic::Melee | Tactic::StoneGolemBoss => {
|
||||
inputs.primary.set_state(true)
|
||||
},
|
||||
Tactic::Staff => {
|
||||
if energy.current() > 10 {
|
||||
inputs.secondary.set_state(true)
|
||||
} else {
|
||||
inputs.primary.set_state(true)
|
||||
}
|
||||
},
|
||||
Tactic::RangedPowerup => inputs.roll.set_state(true),
|
||||
}
|
||||
} else if dist_sqrd < MAX_CHASE_DIST.powf(2.0)
|
||||
@ -424,11 +435,13 @@ impl<'a> System<'a> for Sys {
|
||||
*powerup += dt.0;
|
||||
}
|
||||
} else if let Tactic::Staff = tactic {
|
||||
if !character_state.is_wield() {
|
||||
if *powerup > 2.5 {
|
||||
inputs.primary.set_state(false);
|
||||
*powerup = 0.0;
|
||||
} else {
|
||||
inputs.primary.set_state(true);
|
||||
*powerup += dt.0;
|
||||
}
|
||||
|
||||
inputs.secondary.set_state(true);
|
||||
} else if let Tactic::StoneGolemBoss = tactic {
|
||||
if *powerup > 5.0 {
|
||||
inputs.secondary.set_state(true);
|
||||
|
@ -232,7 +232,7 @@ impl<'a> System<'a> for Sys {
|
||||
if let Some(energy_mut) = beam_owner.and_then(|o| energies.get_mut(o)) {
|
||||
if energy_mut
|
||||
.try_change_by(
|
||||
-(beam_segment.energy_drain as i32), // Stamina use
|
||||
-(beam_segment.energy_cost as i32), // Stamina use
|
||||
EnergySource::Ability,
|
||||
)
|
||||
.is_ok()
|
||||
|
@ -264,7 +264,7 @@ impl<'a> System<'a> for Sys {
|
||||
CharacterState::ChargedMelee(data) => data.handle_event(&j, action),
|
||||
CharacterState::ChargedRanged(data) => data.handle_event(&j, action),
|
||||
CharacterState::RepeaterRanged(data) => data.handle_event(&j, action),
|
||||
CharacterState::GroundShockwave(data) => data.handle_event(&j, action),
|
||||
CharacterState::Shockwave(data) => data.handle_event(&j, action),
|
||||
CharacterState::BasicBeam(data) => data.handle_event(&j, action),
|
||||
};
|
||||
local_emitter.append(&mut state_update.local_events);
|
||||
@ -296,7 +296,7 @@ impl<'a> System<'a> for Sys {
|
||||
CharacterState::ChargedMelee(data) => data.behavior(&j),
|
||||
CharacterState::ChargedRanged(data) => data.behavior(&j),
|
||||
CharacterState::RepeaterRanged(data) => data.behavior(&j),
|
||||
CharacterState::GroundShockwave(data) => data.behavior(&j),
|
||||
CharacterState::Shockwave(data) => data.behavior(&j),
|
||||
CharacterState::BasicBeam(data) => data.behavior(&j),
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
comp::{
|
||||
BeamSegment, Collider, Gravity, Mass, Mounting, Ori, PhysicsState, Pos, Projectile, Scale,
|
||||
Sticky, Vel,
|
||||
Shockwave, Sticky, Vel,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
metrics::SysMetrics,
|
||||
@ -66,6 +66,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Mounting>,
|
||||
ReadStorage<'a, Projectile>,
|
||||
ReadStorage<'a, BeamSegment>,
|
||||
ReadStorage<'a, Shockwave>,
|
||||
);
|
||||
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
@ -91,6 +92,7 @@ impl<'a> System<'a> for Sys {
|
||||
mountings,
|
||||
projectiles,
|
||||
beams,
|
||||
shockwaves,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let start_time = std::time::Instant::now();
|
||||
@ -168,6 +170,7 @@ impl<'a> System<'a> for Sys {
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
) in (
|
||||
&entities,
|
||||
&uids,
|
||||
@ -178,6 +181,7 @@ impl<'a> System<'a> for Sys {
|
||||
!&projectiles,
|
||||
!&mountings,
|
||||
!&beams,
|
||||
!&shockwaves,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
|
@ -153,17 +153,15 @@ impl<'a> System<'a> for Sys {
|
||||
energy_mut.change_by(energy as i32, EnergySource::HitEnemy);
|
||||
}
|
||||
},
|
||||
projectile::Effect::Explode {
|
||||
power,
|
||||
percent_damage,
|
||||
} => server_emitter.emit(ServerEvent::Explosion {
|
||||
pos: pos.0,
|
||||
power,
|
||||
owner: projectile.owner,
|
||||
friendly_damage: false,
|
||||
reagent: None,
|
||||
percent_damage,
|
||||
}),
|
||||
projectile::Effect::Explode(e) => {
|
||||
server_emitter.emit(ServerEvent::Explosion {
|
||||
pos: pos.0,
|
||||
explosion: e,
|
||||
owner: projectile.owner,
|
||||
friendly_damage: false,
|
||||
reagent: None,
|
||||
})
|
||||
},
|
||||
projectile::Effect::Vanish => server_emitter.emit(ServerEvent::Destroy {
|
||||
entity,
|
||||
cause: HealthSource::World,
|
||||
@ -184,17 +182,15 @@ impl<'a> System<'a> for Sys {
|
||||
if physics.on_wall.is_some() || physics.on_ground || physics.on_ceiling {
|
||||
for effect in projectile.hit_solid.drain(..) {
|
||||
match effect {
|
||||
projectile::Effect::Explode {
|
||||
power,
|
||||
percent_damage,
|
||||
} => server_emitter.emit(ServerEvent::Explosion {
|
||||
pos: pos.0,
|
||||
power,
|
||||
owner: projectile.owner,
|
||||
friendly_damage: false,
|
||||
reagent: None,
|
||||
percent_damage,
|
||||
}),
|
||||
projectile::Effect::Explode(e) => {
|
||||
server_emitter.emit(ServerEvent::Explosion {
|
||||
pos: pos.0,
|
||||
explosion: e,
|
||||
owner: projectile.owner,
|
||||
friendly_damage: false,
|
||||
reagent: None,
|
||||
})
|
||||
},
|
||||
projectile::Effect::Vanish => server_emitter.emit(ServerEvent::Destroy {
|
||||
entity,
|
||||
cause: HealthSource::World,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
comp::{
|
||||
group, Body, CharacterState, Damage, DamageSource, HealthChange, HealthSource, Last,
|
||||
Loadout, Ori, PhysicsState, Pos, Scale, Shockwave, Stats,
|
||||
Loadout, Ori, PhysicsState, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats,
|
||||
},
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
state::{DeltaTime, Time},
|
||||
@ -37,6 +37,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, CharacterState>,
|
||||
ReadStorage<'a, PhysicsState>,
|
||||
WriteStorage<'a, Shockwave>,
|
||||
WriteStorage<'a, ShockwaveHitEntities>,
|
||||
);
|
||||
|
||||
fn run(
|
||||
@ -60,6 +61,7 @@ impl<'a> System<'a> for Sys {
|
||||
character_states,
|
||||
physics_states,
|
||||
mut shockwaves,
|
||||
mut shockwave_hit_lists,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = server_bus.emitter();
|
||||
@ -69,8 +71,15 @@ impl<'a> System<'a> for Sys {
|
||||
let dt = dt.0;
|
||||
|
||||
// Shockwaves
|
||||
for (entity, uid, pos, ori, shockwave) in
|
||||
(&entities, &uids, &positions, &orientations, &shockwaves).join()
|
||||
for (entity, uid, pos, ori, shockwave, shockwave_hit_list) in (
|
||||
&entities,
|
||||
&uids,
|
||||
&positions,
|
||||
&orientations,
|
||||
&shockwaves,
|
||||
&mut shockwave_hit_lists,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
let creation_time = match shockwave.creation {
|
||||
Some(time) => time,
|
||||
@ -142,6 +151,15 @@ impl<'a> System<'a> for Sys {
|
||||
)
|
||||
.join()
|
||||
{
|
||||
// Check to see if entity has already been hit
|
||||
if shockwave_hit_list
|
||||
.hit_entities
|
||||
.iter()
|
||||
.any(|&uid| uid == *uid_b)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2D versions
|
||||
let pos_b2 = pos_b.0.xy();
|
||||
let last_pos_b2_maybe = last_pos_b_maybe.map(|p| (p.0).0.xy());
|
||||
@ -152,7 +170,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// Angle checks
|
||||
let pos_b_ground = Vec3::new(pos_b.0.x, pos_b.0.y, pos.0.z);
|
||||
let max_angle = 15.0_f32.to_radians();
|
||||
let max_angle = shockwave.vertical_angle.to_radians();
|
||||
|
||||
// See if entities are in the same group
|
||||
let same_group = group
|
||||
@ -204,14 +222,16 @@ impl<'a> System<'a> for Sys {
|
||||
cause,
|
||||
},
|
||||
});
|
||||
shockwave_hit_list.hit_entities.push(*uid_b);
|
||||
}
|
||||
if shockwave.knockback != 0.0 && damage.healthchange != 0.0 {
|
||||
let kb_dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0));
|
||||
let impulse = if shockwave.knockback < 0.0 {
|
||||
shockwave.knockback
|
||||
* *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, -1.0)), 0.85)
|
||||
* *Dir::slerp(kb_dir, Dir::new(Vec3::new(0.0, 0.0, -1.0)), 0.85)
|
||||
} else {
|
||||
shockwave.knockback
|
||||
* *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5)
|
||||
* *Dir::slerp(kb_dir, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5)
|
||||
};
|
||||
server_emitter.emit(ServerEvent::Knockback { entity: b, impulse });
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ impl<'a> System<'a> for Sys {
|
||||
| CharacterState::ChargedMelee { .. }
|
||||
| CharacterState::ChargedRanged { .. }
|
||||
| CharacterState::RepeaterRanged { .. }
|
||||
| CharacterState::GroundShockwave { .. }
|
||||
| CharacterState::Shockwave { .. }
|
||||
| CharacterState::BasicBeam { .. } => {
|
||||
if energy.get_unchecked().regen_rate != 0.0 {
|
||||
energy.get_mut_unchecked().regen_rate = 0.0
|
||||
|
@ -19,7 +19,7 @@ use common::{
|
||||
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
|
||||
util::Dir,
|
||||
vol::RectVolSize,
|
||||
LoadoutBuilder,
|
||||
Explosion, LoadoutBuilder,
|
||||
};
|
||||
use rand::Rng;
|
||||
use specs::{Builder, Entity as EcsEntity, Join, WorldExt};
|
||||
@ -1129,11 +1129,18 @@ fn handle_explosion(
|
||||
ecs.read_resource::<EventBus<ServerEvent>>()
|
||||
.emit_now(ServerEvent::Explosion {
|
||||
pos: pos.0,
|
||||
power,
|
||||
explosion: Explosion {
|
||||
radius: 3.0 * power,
|
||||
max_damage: (100.0 * power) as u32,
|
||||
min_damage: 0,
|
||||
max_heal: 0,
|
||||
min_heal: 0,
|
||||
terrain_destruction_power: power,
|
||||
energy_regen: 0,
|
||||
},
|
||||
owner: ecs.read_storage::<Uid>().get(target).copied(),
|
||||
friendly_damage: true,
|
||||
reagent: None,
|
||||
percent_damage: 1.0,
|
||||
})
|
||||
},
|
||||
None => server.notify_client(
|
||||
|
@ -19,6 +19,7 @@ use common::{
|
||||
sys::combat::BLOCK_ANGLE,
|
||||
terrain::{Block, TerrainGrid},
|
||||
vol::ReadVol,
|
||||
Explosion,
|
||||
};
|
||||
use comp::item::Reagent;
|
||||
use rand::prelude::*;
|
||||
@ -487,23 +488,29 @@ pub fn handle_respawn(server: &Server, entity: EcsEntity) {
|
||||
pub fn handle_explosion(
|
||||
server: &Server,
|
||||
pos: Vec3<f32>,
|
||||
power: f32,
|
||||
explosion: Explosion,
|
||||
owner: Option<Uid>,
|
||||
friendly_damage: bool,
|
||||
reagent: Option<Reagent>,
|
||||
percent_damage: f32,
|
||||
) {
|
||||
// Go through all other entities
|
||||
let hit_range = 3.0 * power;
|
||||
let ecs = &server.state.ecs();
|
||||
|
||||
let outcome_power = if explosion.max_heal > explosion.max_damage {
|
||||
(-explosion.terrain_destruction_power).min(explosion.max_heal as f32 / -100.0)
|
||||
} else {
|
||||
explosion
|
||||
.terrain_destruction_power
|
||||
.max(explosion.max_damage as f32 / 100.0)
|
||||
};
|
||||
// Add an outcome
|
||||
ecs.write_resource::<Vec<Outcome>>()
|
||||
.push(Outcome::Explosion {
|
||||
pos,
|
||||
power,
|
||||
power: outcome_power,
|
||||
radius: explosion.radius,
|
||||
is_attack: explosion.max_heal > 0 || explosion.max_damage > 0,
|
||||
reagent,
|
||||
percent_damage,
|
||||
});
|
||||
let owner_entity = owner.and_then(|uid| {
|
||||
ecs.read_resource::<UidAllocator>()
|
||||
@ -525,7 +532,7 @@ pub fn handle_explosion(
|
||||
// Check if it is a hit
|
||||
if !stats_b.is_dead
|
||||
// RADIUS
|
||||
&& distance_squared < hit_range.powi(2)
|
||||
&& distance_squared < explosion.radius.powi(2)
|
||||
{
|
||||
// See if entities are in the same group
|
||||
let mut same_group = owner_entity
|
||||
@ -538,8 +545,8 @@ pub fn handle_explosion(
|
||||
}
|
||||
// Don't heal if outside group
|
||||
// Don't damage in the same group
|
||||
let is_damage = (friendly_damage || !same_group) && (percent_damage > 0.0);
|
||||
let is_heal = same_group && (percent_damage < 1.0);
|
||||
let is_damage = (friendly_damage || !same_group) && explosion.max_damage > 0;
|
||||
let is_heal = same_group && explosion.max_heal > 0 && !friendly_damage;
|
||||
if !is_heal && !is_damage {
|
||||
continue;
|
||||
}
|
||||
@ -550,11 +557,13 @@ pub fn handle_explosion(
|
||||
} else {
|
||||
DamageSource::Explosion
|
||||
};
|
||||
let strength = (1.0 - distance_squared / hit_range.powi(2)) * power * 130.0;
|
||||
let strength = 1.0 - distance_squared / explosion.radius.powi(2);
|
||||
let healthchange = if is_heal {
|
||||
strength * (1.0 - percent_damage)
|
||||
explosion.min_heal as f32
|
||||
+ (explosion.max_heal - explosion.min_heal) as f32 * strength
|
||||
} else {
|
||||
-strength * percent_damage
|
||||
-(explosion.min_damage as f32
|
||||
+ (explosion.max_damage - explosion.min_damage) as f32 * strength)
|
||||
};
|
||||
|
||||
let mut damage = Damage {
|
||||
@ -579,72 +588,76 @@ pub fn handle_explosion(
|
||||
amount: damage.healthchange as i32,
|
||||
cause,
|
||||
});
|
||||
if let Some(owner) = owner_entity {
|
||||
if let Some(energy) = ecs.write_storage::<comp::Energy>().get_mut(owner) {
|
||||
energy
|
||||
.change_by(explosion.energy_regen as i32, comp::EnergySource::HitEnemy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const RAYS: usize = 500;
|
||||
|
||||
if percent_damage > 0.9 {
|
||||
// Color terrain
|
||||
let mut touched_blocks = Vec::new();
|
||||
let color_range = power * 2.7;
|
||||
for _ in 0..RAYS {
|
||||
let dir = Vec3::new(
|
||||
rand::random::<f32>() - 0.5,
|
||||
rand::random::<f32>() - 0.5,
|
||||
rand::random::<f32>() - 0.5,
|
||||
)
|
||||
.normalized();
|
||||
// Color terrain
|
||||
let mut touched_blocks = Vec::new();
|
||||
let color_range = explosion.terrain_destruction_power * 2.7;
|
||||
for _ in 0..RAYS {
|
||||
let dir = Vec3::new(
|
||||
rand::random::<f32>() - 0.5,
|
||||
rand::random::<f32>() - 0.5,
|
||||
rand::random::<f32>() - 0.5,
|
||||
)
|
||||
.normalized();
|
||||
|
||||
let _ = ecs
|
||||
.read_resource::<TerrainGrid>()
|
||||
.ray(pos, pos + dir * color_range)
|
||||
// TODO: Faster RNG
|
||||
.until(|_| rand::random::<f32>() < 0.05)
|
||||
.for_each(|_: &Block, pos| touched_blocks.push(pos))
|
||||
.cast();
|
||||
}
|
||||
let _ = ecs
|
||||
.read_resource::<TerrainGrid>()
|
||||
.ray(pos, pos + dir * color_range)
|
||||
// TODO: Faster RNG
|
||||
.until(|_| rand::random::<f32>() < 0.05)
|
||||
.for_each(|_: &Block, pos| touched_blocks.push(pos))
|
||||
.cast();
|
||||
}
|
||||
|
||||
let terrain = ecs.read_resource::<TerrainGrid>();
|
||||
let mut block_change = ecs.write_resource::<BlockChange>();
|
||||
for block_pos in touched_blocks {
|
||||
if let Ok(block) = terrain.get(block_pos) {
|
||||
let diff2 = block_pos.map(|b| b as f32).distance_squared(pos);
|
||||
let fade = (1.0 - diff2 / color_range.powi(2)).max(0.0);
|
||||
if let Some(mut color) = block.get_color() {
|
||||
let r = color[0] as f32 + (fade * (color[0] as f32 * 0.5 - color[0] as f32));
|
||||
let g = color[1] as f32 + (fade * (color[1] as f32 * 0.3 - color[1] as f32));
|
||||
let b = color[2] as f32 + (fade * (color[2] as f32 * 0.3 - color[2] as f32));
|
||||
color[0] = r as u8;
|
||||
color[1] = g as u8;
|
||||
color[2] = b as u8;
|
||||
block_change.set(block_pos, Block::new(block.kind(), color));
|
||||
}
|
||||
let terrain = ecs.read_resource::<TerrainGrid>();
|
||||
let mut block_change = ecs.write_resource::<BlockChange>();
|
||||
for block_pos in touched_blocks {
|
||||
if let Ok(block) = terrain.get(block_pos) {
|
||||
let diff2 = block_pos.map(|b| b as f32).distance_squared(pos);
|
||||
let fade = (1.0 - diff2 / color_range.powi(2)).max(0.0);
|
||||
if let Some(mut color) = block.get_color() {
|
||||
let r = color[0] as f32 + (fade * (color[0] as f32 * 0.5 - color[0] as f32));
|
||||
let g = color[1] as f32 + (fade * (color[1] as f32 * 0.3 - color[1] as f32));
|
||||
let b = color[2] as f32 + (fade * (color[2] as f32 * 0.3 - color[2] as f32));
|
||||
color[0] = r as u8;
|
||||
color[1] = g as u8;
|
||||
color[2] = b as u8;
|
||||
block_change.set(block_pos, Block::new(block.kind(), color));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy terrain
|
||||
for _ in 0..RAYS {
|
||||
let dir = Vec3::new(
|
||||
rand::random::<f32>() - 0.5,
|
||||
rand::random::<f32>() - 0.5,
|
||||
rand::random::<f32>() - 0.15,
|
||||
)
|
||||
.normalized();
|
||||
// Destroy terrain
|
||||
for _ in 0..RAYS {
|
||||
let dir = Vec3::new(
|
||||
rand::random::<f32>() - 0.5,
|
||||
rand::random::<f32>() - 0.5,
|
||||
rand::random::<f32>() - 0.15,
|
||||
)
|
||||
.normalized();
|
||||
|
||||
let terrain = ecs.read_resource::<TerrainGrid>();
|
||||
let _ = terrain
|
||||
.ray(pos, pos + dir * power)
|
||||
// TODO: Faster RNG
|
||||
.until(|block| block.is_liquid() || rand::random::<f32>() < 0.05)
|
||||
.for_each(|block: &Block, pos| {
|
||||
if block.is_explodable() {
|
||||
block_change.set(pos, block.into_vacant());
|
||||
}
|
||||
})
|
||||
.cast();
|
||||
}
|
||||
let terrain = ecs.read_resource::<TerrainGrid>();
|
||||
let _ = terrain
|
||||
.ray(pos, pos + dir * explosion.terrain_destruction_power)
|
||||
// TODO: Faster RNG
|
||||
.until(|block| block.is_liquid() || rand::random::<f32>() < 0.05)
|
||||
.for_each(|block: &Block, pos| {
|
||||
if block.is_explodable() {
|
||||
block_change.set(pos, block.into_vacant());
|
||||
}
|
||||
})
|
||||
.cast();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,20 +56,11 @@ impl Server {
|
||||
match event {
|
||||
ServerEvent::Explosion {
|
||||
pos,
|
||||
power,
|
||||
explosion,
|
||||
owner,
|
||||
friendly_damage,
|
||||
reagent,
|
||||
percent_damage,
|
||||
} => handle_explosion(
|
||||
&self,
|
||||
pos,
|
||||
power,
|
||||
owner,
|
||||
friendly_damage,
|
||||
reagent,
|
||||
percent_damage,
|
||||
),
|
||||
} => handle_explosion(&self, pos, explosion, owner, friendly_damage, reagent),
|
||||
ServerEvent::Shoot {
|
||||
entity,
|
||||
dir,
|
||||
|
@ -162,6 +162,9 @@ impl StateExt for State {
|
||||
properties,
|
||||
creation: None,
|
||||
})
|
||||
.with(comp::ShockwaveHitEntities {
|
||||
hit_entities: Vec::<Uid>::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn create_beam(
|
||||
|
@ -3,6 +3,7 @@ use common::{
|
||||
event::{EventBus, ServerEvent},
|
||||
span,
|
||||
state::DeltaTime,
|
||||
Explosion,
|
||||
};
|
||||
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||
|
||||
@ -46,11 +47,18 @@ impl<'a> System<'a> for Sys {
|
||||
});
|
||||
server_emitter.emit(ServerEvent::Explosion {
|
||||
pos: pos.0,
|
||||
power: 4.0,
|
||||
explosion: Explosion {
|
||||
radius: 12.0,
|
||||
max_damage: 500,
|
||||
min_damage: 100,
|
||||
max_heal: 0,
|
||||
min_heal: 0,
|
||||
terrain_destruction_power: 4.0,
|
||||
energy_regen: 0,
|
||||
},
|
||||
owner: *owner,
|
||||
friendly_damage: true,
|
||||
reagent: None,
|
||||
percent_damage: 1.0,
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -62,11 +70,18 @@ impl<'a> System<'a> for Sys {
|
||||
});
|
||||
server_emitter.emit(ServerEvent::Explosion {
|
||||
pos: pos.0,
|
||||
power: 4.0,
|
||||
explosion: Explosion {
|
||||
radius: 12.0,
|
||||
max_damage: 50,
|
||||
min_damage: 10,
|
||||
max_heal: 0,
|
||||
min_heal: 0,
|
||||
terrain_destruction_power: 4.0,
|
||||
energy_regen: 0,
|
||||
},
|
||||
owner: *owner,
|
||||
friendly_damage: true,
|
||||
reagent: Some(*reagent),
|
||||
percent_damage: 1.0,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
223
voxygen/src/anim/src/character/beam.rs
Normal file
223
voxygen/src/anim/src/character/beam.rs
Normal file
@ -0,0 +1,223 @@
|
||||
use super::{
|
||||
super::{vek::*, Animation},
|
||||
CharacterSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::{
|
||||
comp::item::{Hands, ToolKind},
|
||||
states::utils::StageSection,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct Input {
|
||||
pub attack: bool,
|
||||
}
|
||||
pub struct BeamAnimation;
|
||||
|
||||
impl Animation for BeamAnimation {
|
||||
type Dependency = (
|
||||
Option<ToolKind>,
|
||||
Option<ToolKind>,
|
||||
f64,
|
||||
f32,
|
||||
Option<StageSection>,
|
||||
);
|
||||
type Skeleton = CharacterSkeleton;
|
||||
|
||||
#[cfg(feature = "use-dyn-lib")]
|
||||
const UPDATE_FN: &'static [u8] = b"character_beam\0";
|
||||
|
||||
#[cfg_attr(feature = "be-dyn-lib", export_name = "character_beam")]
|
||||
#[allow(clippy::single_match)] // TODO: Pending review in #587
|
||||
fn update_skeleton_inner(
|
||||
skeleton: &Self::Skeleton,
|
||||
(active_tool_kind, second_tool_kind, _global_time, velocity, stage_section): Self::Dependency,
|
||||
anim_time: f64,
|
||||
rate: &mut f32,
|
||||
skeleton_attr: &SkeletonAttr,
|
||||
) -> Self::Skeleton {
|
||||
*rate = 1.0;
|
||||
let mut next = (*skeleton).clone();
|
||||
|
||||
let movement = (anim_time as f32 * 1.0).min(1.0);
|
||||
|
||||
next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1);
|
||||
|
||||
next.l_hand.position = Vec3::new(0.0, 0.0, -4.0);
|
||||
next.l_hand.orientation = Quaternion::rotation_x(1.27) * Quaternion::rotation_y(0.0);
|
||||
next.l_hand.scale = Vec3::one() * 1.05;
|
||||
next.r_hand.position = Vec3::new(0.0, 0.0, 2.0);
|
||||
next.r_hand.orientation = Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.2);
|
||||
next.r_hand.scale = Vec3::one() * 1.05;
|
||||
next.main.position = Vec3::new(0.0, 0.0, 13.2);
|
||||
next.main.orientation = Quaternion::rotation_y(PI);
|
||||
|
||||
next.control.position = Vec3::new(-4.0, 7.0, 4.0);
|
||||
next.control.orientation = Quaternion::rotation_x(-0.3)
|
||||
* Quaternion::rotation_y(0.15)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.control.scale = Vec3::one();
|
||||
let slow = (anim_time as f32 * 8.0).sin();
|
||||
let slowalt = (anim_time as f32 * 8.0 + PI / 2.0).sin();
|
||||
let slowmid = (anim_time as f32 * 8.0 + PI / 4.0).sin();
|
||||
|
||||
let fast = (anim_time as f32 * 16.0).sin();
|
||||
|
||||
//println!("{:?}", anim_time);
|
||||
match active_tool_kind {
|
||||
//TODO: Inventory
|
||||
Some(ToolKind::Staff(_)) | Some(ToolKind::Sceptre(_)) => {
|
||||
if let Some(stage_section) = stage_section {
|
||||
match stage_section {
|
||||
StageSection::Buildup => {
|
||||
next.control.position = Vec3::new(
|
||||
-4.0 + movement * 16.0,
|
||||
7.0 + movement,
|
||||
4.0 + movement * 4.0,
|
||||
);
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(-0.3 + movement * -1.2)
|
||||
* Quaternion::rotation_y(0.15 + movement * -1.4)
|
||||
* Quaternion::rotation_z(0.0 + movement * -1.7);
|
||||
next.head.orientation =
|
||||
Quaternion::rotation_x(0.0) * Quaternion::rotation_z(0.0);
|
||||
|
||||
next.l_hand.position = Vec3::new(
|
||||
0.0 + movement * -1.0,
|
||||
0.0 + movement * -5.0,
|
||||
-4.0 + movement * 19.0,
|
||||
);
|
||||
|
||||
if velocity < 0.5 {
|
||||
next.head.orientation = Quaternion::rotation_z(movement * -0.5);
|
||||
|
||||
next.l_foot.position = Vec3::new(
|
||||
-skeleton_attr.foot.0,
|
||||
skeleton_attr.foot.1 + movement * -3.0,
|
||||
skeleton_attr.foot.2,
|
||||
);
|
||||
next.l_foot.orientation = Quaternion::rotation_x(movement * -0.5)
|
||||
* Quaternion::rotation_z(movement * 0.5);
|
||||
|
||||
next.r_foot.position = Vec3::new(
|
||||
skeleton_attr.foot.0,
|
||||
skeleton_attr.foot.1 + movement * 4.0,
|
||||
skeleton_attr.foot.2,
|
||||
);
|
||||
next.r_foot.orientation = Quaternion::rotation_z(movement * 0.5);
|
||||
next.chest.orientation = Quaternion::rotation_x(movement * -0.2)
|
||||
* Quaternion::rotation_z(movement * 0.5);
|
||||
next.belt.orientation = Quaternion::rotation_x(movement * 0.1)
|
||||
* Quaternion::rotation_z(movement * -0.1);
|
||||
next.shorts.orientation = Quaternion::rotation_x(movement * 0.2)
|
||||
* Quaternion::rotation_z(movement * -0.2);
|
||||
} else {
|
||||
};
|
||||
},
|
||||
StageSection::Cast => {
|
||||
next.control.position = Vec3::new(12.0, 8.0 + slow * 2.0, 8.0);
|
||||
next.control.orientation = Quaternion::rotation_x(-1.5)
|
||||
* Quaternion::rotation_y(-1.25 + fast * 0.07)
|
||||
* Quaternion::rotation_z(-1.7 + slowmid * 0.3);
|
||||
|
||||
next.l_hand.position = Vec3::new(
|
||||
-1.0 + slow * 3.5,
|
||||
-5.0 + slow * -2.0 + fast * -1.5,
|
||||
15.0 + slowalt * 3.5,
|
||||
);
|
||||
next.l_hand.orientation = Quaternion::rotation_x(1.57)
|
||||
* Quaternion::rotation_y(-1.1 + slowmid * -0.3)
|
||||
* Quaternion::rotation_z(-2.8);
|
||||
if velocity < 0.5 {
|
||||
next.head.orientation = Quaternion::rotation_z(-0.5 + fast * 0.05);
|
||||
|
||||
next.l_foot.position = Vec3::new(
|
||||
-skeleton_attr.foot.0,
|
||||
skeleton_attr.foot.1 - 3.0,
|
||||
skeleton_attr.foot.2,
|
||||
);
|
||||
next.l_foot.orientation =
|
||||
Quaternion::rotation_x(-0.5) * Quaternion::rotation_z(0.5);
|
||||
|
||||
next.r_foot.position = Vec3::new(
|
||||
skeleton_attr.foot.0,
|
||||
skeleton_attr.foot.1 + 4.0,
|
||||
skeleton_attr.foot.2,
|
||||
);
|
||||
next.r_foot.orientation = Quaternion::rotation_z(0.5);
|
||||
next.chest.orientation = Quaternion::rotation_x(-0.2 + slow * 0.05)
|
||||
* Quaternion::rotation_z(0.5);
|
||||
next.belt.orientation =
|
||||
Quaternion::rotation_x(0.1) * Quaternion::rotation_z(-0.1);
|
||||
next.shorts.orientation =
|
||||
Quaternion::rotation_x(0.2) * Quaternion::rotation_z(-0.2);
|
||||
} else {
|
||||
};
|
||||
},
|
||||
|
||||
StageSection::Recover => {
|
||||
next.control.position = Vec3::new(
|
||||
12.0 + movement * -16.0,
|
||||
8.0 + movement * -1.0,
|
||||
8.0 + movement * -4.0,
|
||||
);
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(-1.5 + movement * 1.2)
|
||||
* Quaternion::rotation_y(-1.25 + movement * 1.4)
|
||||
* Quaternion::rotation_z(-1.7 + movement * 1.7);
|
||||
next.l_hand.position = Vec3::new(
|
||||
-1.0 + movement,
|
||||
-5.0 + movement * 5.0,
|
||||
15.0 + movement * -19.0,
|
||||
);
|
||||
next.l_hand.orientation =
|
||||
Quaternion::rotation_x(1.57 + movement * -0.3)
|
||||
* Quaternion::rotation_y(-1.1 + movement * 1.1)
|
||||
* Quaternion::rotation_z(-2.8 + movement * 2.8);
|
||||
if velocity < 0.5 {
|
||||
next.head.orientation = Quaternion::rotation_z(movement * 0.5);
|
||||
|
||||
next.l_foot.position = Vec3::new(
|
||||
-skeleton_attr.foot.0,
|
||||
skeleton_attr.foot.1 - 3.0 + movement * 3.0,
|
||||
skeleton_attr.foot.2,
|
||||
);
|
||||
next.l_foot.orientation =
|
||||
Quaternion::rotation_x(-0.5 + movement * 0.5)
|
||||
* Quaternion::rotation_z(0.5 + movement * -0.5);
|
||||
|
||||
next.r_foot.position = Vec3::new(
|
||||
skeleton_attr.foot.0,
|
||||
skeleton_attr.foot.1 + 4.0 + movement * -4.0,
|
||||
skeleton_attr.foot.2,
|
||||
);
|
||||
next.r_foot.orientation =
|
||||
Quaternion::rotation_z(0.5 + movement * -0.5);
|
||||
next.chest.orientation =
|
||||
Quaternion::rotation_x(-0.2 + movement * 0.2)
|
||||
* Quaternion::rotation_z(0.5 + movement * -0.5);
|
||||
next.belt.orientation =
|
||||
Quaternion::rotation_x(0.1 + movement * -0.1)
|
||||
* Quaternion::rotation_z(-0.1 + movement * 0.1);
|
||||
next.shorts.orientation =
|
||||
Quaternion::rotation_x(0.2 + movement * -0.2)
|
||||
* Quaternion::rotation_z(-0.2 + movement * 0.2);
|
||||
} else {
|
||||
};
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
next.second.scale = match (
|
||||
active_tool_kind.map(|tk| tk.hands()),
|
||||
second_tool_kind.map(|tk| tk.hands()),
|
||||
) {
|
||||
(Some(Hands::OneHand), Some(Hands::OneHand)) => Vec3::one(),
|
||||
(_, _) => Vec3::zero(),
|
||||
};
|
||||
|
||||
next
|
||||
}
|
||||
}
|
@ -112,8 +112,8 @@ impl Animation for ChargeAnimation {
|
||||
|
||||
next.control.position = Vec3::new(
|
||||
-7.0 + quick * 3.5 * (1.0 / (stopa + 0.1)),
|
||||
6.0 + quicka * 3.5 * (1.0 / (stopa + 0.1)),
|
||||
6.0 - stop * 3.0,
|
||||
0.0 + quicka * 3.5 * (1.0 / (stopa + 0.1)),
|
||||
8.0 - stop * 3.0,
|
||||
);
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(stop * -0.2) * Quaternion::rotation_z(stop * 0.2);
|
||||
|
@ -104,25 +104,21 @@ impl Animation for EquipAnimation {
|
||||
next.control.scale = Vec3::one();
|
||||
},
|
||||
Some(ToolKind::Staff(_)) | Some(ToolKind::Sceptre(_)) => {
|
||||
next.l_hand.position = Vec3::new(1.0, -2.0, -5.0);
|
||||
next.l_hand.position = Vec3::new(0.0, 0.0, -4.0);
|
||||
next.l_hand.orientation =
|
||||
Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.3);
|
||||
Quaternion::rotation_x(1.27) * Quaternion::rotation_y(0.0);
|
||||
next.l_hand.scale = Vec3::one() * 1.05;
|
||||
next.r_hand.position = Vec3::new(9.0, 1.0, 0.0);
|
||||
next.r_hand.orientation = Quaternion::rotation_x(1.8)
|
||||
* Quaternion::rotation_y(0.5)
|
||||
* Quaternion::rotation_z(-0.27);
|
||||
next.r_hand.position = Vec3::new(0.0, 0.0, 2.0);
|
||||
next.r_hand.orientation =
|
||||
Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.2);
|
||||
next.r_hand.scale = Vec3::one() * 1.05;
|
||||
next.main.position = Vec3::new(11.0, 9.0, 10.0);
|
||||
next.main.orientation = Quaternion::rotation_x(-0.3)
|
||||
* Quaternion::rotation_y(3.14 + 0.3)
|
||||
* Quaternion::rotation_z(0.9);
|
||||
next.main.scale = Vec3::one();
|
||||
next.main.position = Vec3::new(0.0, 0.0, 13.2);
|
||||
next.main.orientation = Quaternion::rotation_y(3.14);
|
||||
|
||||
next.control.position = Vec3::new(-7.0, 6.0, 6.0);
|
||||
next.control.orientation = Quaternion::rotation_x(wave_ultra_slow * 0.2)
|
||||
* Quaternion::rotation_y(0.0)
|
||||
* Quaternion::rotation_z(wave_ultra_slow_cos * 0.1);
|
||||
next.control.position = Vec3::new(-4.0, 7.0, 4.0);
|
||||
next.control.orientation = Quaternion::rotation_x(-0.3)
|
||||
* Quaternion::rotation_y(0.15)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.control.scale = Vec3::one();
|
||||
},
|
||||
Some(ToolKind::Shield(_)) => {
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub mod alpha;
|
||||
pub mod beam;
|
||||
pub mod beta;
|
||||
pub mod block;
|
||||
pub mod blockidle;
|
||||
@ -16,6 +17,7 @@ pub mod leapmelee;
|
||||
pub mod repeater;
|
||||
pub mod roll;
|
||||
pub mod run;
|
||||
pub mod shockwave;
|
||||
pub mod shoot;
|
||||
pub mod sit;
|
||||
pub mod sneak;
|
||||
@ -28,13 +30,13 @@ pub mod wield;
|
||||
|
||||
// Reexports
|
||||
pub use self::{
|
||||
alpha::AlphaAnimation, beta::BetaAnimation, block::BlockAnimation,
|
||||
alpha::AlphaAnimation, beam::BeamAnimation, beta::BetaAnimation, block::BlockAnimation,
|
||||
blockidle::BlockIdleAnimation, charge::ChargeAnimation, chargeswing::ChargeswingAnimation,
|
||||
climb::ClimbAnimation, dance::DanceAnimation, dash::DashAnimation, equip::EquipAnimation,
|
||||
glidewield::GlideWieldAnimation, gliding::GlidingAnimation, idle::IdleAnimation,
|
||||
jump::JumpAnimation, leapmelee::LeapAnimation, repeater::RepeaterAnimation,
|
||||
roll::RollAnimation, run::RunAnimation, shoot::ShootAnimation, sit::SitAnimation,
|
||||
sneak::SneakAnimation, spin::SpinAnimation, spinmelee::SpinMeleeAnimation,
|
||||
roll::RollAnimation, run::RunAnimation, shockwave::ShockwaveAnimation, shoot::ShootAnimation,
|
||||
sit::SitAnimation, sneak::SneakAnimation, spin::SpinAnimation, spinmelee::SpinMeleeAnimation,
|
||||
stand::StandAnimation, swim::SwimAnimation, swimwield::SwimWieldAnimation,
|
||||
wield::WieldAnimation,
|
||||
};
|
||||
|
@ -130,13 +130,18 @@ impl Animation for RunAnimation {
|
||||
next.chest.orientation = Quaternion::rotation_z(short * 0.18 * walkintensity + tilt * -0.6)
|
||||
* Quaternion::rotation_y(tilt * 1.6)
|
||||
* Quaternion::rotation_x(
|
||||
impact * 0.06 + shortalter * 0.035 + wave_stop * speed * -0.07 + (tilt.abs()),
|
||||
impact * 0.06 + shortalter * 0.035 + wave_stop * speed * -0.09 + (tilt.abs()),
|
||||
);
|
||||
next.chest.scale = Vec3::one();
|
||||
|
||||
next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1);
|
||||
next.belt.orientation =
|
||||
Quaternion::rotation_z(short * 0.1 + tilt * -1.1) * Quaternion::rotation_y(tilt * 0.5);
|
||||
next.belt.position = Vec3::new(
|
||||
0.0,
|
||||
0.25 + skeleton_attr.belt.0,
|
||||
0.25 + skeleton_attr.belt.1,
|
||||
);
|
||||
next.belt.orientation = Quaternion::rotation_x(0.1)
|
||||
* Quaternion::rotation_z(short * 0.1 + tilt * -1.1)
|
||||
* Quaternion::rotation_y(tilt * 0.5);
|
||||
next.belt.scale = Vec3::one();
|
||||
|
||||
next.back.position = Vec3::new(0.0, skeleton_attr.back.0, skeleton_attr.back.1);
|
||||
@ -144,9 +149,14 @@ impl Animation for RunAnimation {
|
||||
Quaternion::rotation_x(-0.25 + short * 0.1 + noisea * 0.1 + noiseb * 0.1);
|
||||
next.back.scale = Vec3::one() * 1.02;
|
||||
|
||||
next.shorts.position = Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1);
|
||||
next.shorts.orientation =
|
||||
Quaternion::rotation_z(short * 0.25 + tilt * -1.5) * Quaternion::rotation_y(tilt * 0.7);
|
||||
next.shorts.position = Vec3::new(
|
||||
0.0,
|
||||
0.65 + skeleton_attr.shorts.0,
|
||||
0.65 + skeleton_attr.shorts.1,
|
||||
);
|
||||
next.shorts.orientation = Quaternion::rotation_x(0.2)
|
||||
* Quaternion::rotation_z(short * 0.25 + tilt * -1.5)
|
||||
* Quaternion::rotation_y(tilt * 0.7);
|
||||
next.shorts.scale = Vec3::one();
|
||||
|
||||
next.l_hand.position = Vec3::new(
|
||||
|
193
voxygen/src/anim/src/character/shockwave.rs
Normal file
193
voxygen/src/anim/src/character/shockwave.rs
Normal file
@ -0,0 +1,193 @@
|
||||
use super::{
|
||||
super::{vek::*, Animation},
|
||||
CharacterSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::{
|
||||
comp::item::{Hands, ToolKind},
|
||||
states::utils::StageSection,
|
||||
};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct Input {
|
||||
pub attack: bool,
|
||||
}
|
||||
pub struct ShockwaveAnimation;
|
||||
|
||||
impl Animation for ShockwaveAnimation {
|
||||
type Dependency = (
|
||||
Option<ToolKind>,
|
||||
Option<ToolKind>,
|
||||
f64,
|
||||
f32,
|
||||
Option<StageSection>,
|
||||
);
|
||||
type Skeleton = CharacterSkeleton;
|
||||
|
||||
#[cfg(feature = "use-dyn-lib")]
|
||||
const UPDATE_FN: &'static [u8] = b"character_shockwave\0";
|
||||
|
||||
#[cfg_attr(feature = "be-dyn-lib", export_name = "character_shockwave")]
|
||||
#[allow(clippy::single_match)] // TODO: Pending review in #587
|
||||
fn update_skeleton_inner(
|
||||
skeleton: &Self::Skeleton,
|
||||
(active_tool_kind, second_tool_kind, _global_time, velocity, stage_section): Self::Dependency,
|
||||
anim_time: f64,
|
||||
rate: &mut f32,
|
||||
skeleton_attr: &SkeletonAttr,
|
||||
) -> Self::Skeleton {
|
||||
*rate = 1.0;
|
||||
let mut next = (*skeleton).clone();
|
||||
|
||||
let movement = (anim_time as f32 * 1.0).min(1.0);
|
||||
|
||||
next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1);
|
||||
|
||||
next.l_hand.position = Vec3::new(0.0, 0.0, -4.0);
|
||||
next.l_hand.orientation = Quaternion::rotation_x(1.27) * Quaternion::rotation_y(0.0);
|
||||
next.l_hand.scale = Vec3::one() * 1.05;
|
||||
next.r_hand.position = Vec3::new(0.0, 0.0, 2.0);
|
||||
next.r_hand.orientation = Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.2);
|
||||
next.r_hand.scale = Vec3::one() * 1.05;
|
||||
next.main.position = Vec3::new(0.0, 0.0, 13.2);
|
||||
next.main.orientation = Quaternion::rotation_y(PI);
|
||||
|
||||
next.control.position = Vec3::new(-4.0, 7.0, 4.0);
|
||||
next.control.orientation = Quaternion::rotation_x(-0.3)
|
||||
* Quaternion::rotation_y(0.15)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.control.scale = Vec3::one();
|
||||
let twist = movement * 0.8;
|
||||
|
||||
match active_tool_kind {
|
||||
//TODO: Inventory
|
||||
Some(ToolKind::Staff(_)) => {
|
||||
if let Some(stage_section) = stage_section {
|
||||
match stage_section {
|
||||
StageSection::Buildup => {
|
||||
next.control.position = Vec3::new(
|
||||
-4.0 + movement * 5.0,
|
||||
7.0 + movement * 3.0,
|
||||
4.0 + movement * 10.0,
|
||||
);
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(-0.3 + movement * 0.8)
|
||||
* Quaternion::rotation_y(0.15 + movement * -0.15)
|
||||
* Quaternion::rotation_z(movement * 0.8);
|
||||
next.head.orientation = Quaternion::rotation_x(movement * 0.4)
|
||||
* Quaternion::rotation_z(twist * 0.2);
|
||||
|
||||
next.chest.position = Vec3::new(
|
||||
0.0,
|
||||
skeleton_attr.chest.0,
|
||||
skeleton_attr.chest.1 + movement * 2.0,
|
||||
);
|
||||
next.chest.orientation = Quaternion::rotation_z(twist * -0.2);
|
||||
|
||||
next.belt.orientation = Quaternion::rotation_z(twist * 0.6);
|
||||
|
||||
next.shorts.orientation = Quaternion::rotation_z(twist);
|
||||
|
||||
if velocity < 0.5 {
|
||||
next.l_foot.position = Vec3::new(
|
||||
-skeleton_attr.foot.0,
|
||||
skeleton_attr.foot.1 + movement * -7.0,
|
||||
skeleton_attr.foot.2,
|
||||
);
|
||||
next.l_foot.orientation = Quaternion::rotation_x(movement * -0.8)
|
||||
* Quaternion::rotation_z(movement * 0.3);
|
||||
|
||||
next.r_foot.position = Vec3::new(
|
||||
skeleton_attr.foot.0,
|
||||
skeleton_attr.foot.1 + movement * 5.0,
|
||||
skeleton_attr.foot.2,
|
||||
);
|
||||
next.r_foot.orientation = Quaternion::rotation_y(movement * -0.3)
|
||||
* Quaternion::rotation_z(movement * 0.4);
|
||||
} else {
|
||||
};
|
||||
},
|
||||
StageSection::Swing => {
|
||||
next.control.position = Vec3::new(1.0, 10.0, 14.0 + movement * -2.0);
|
||||
next.control.orientation = Quaternion::rotation_x(0.5 + movement * 0.3)
|
||||
* Quaternion::rotation_y(movement * 0.3)
|
||||
* Quaternion::rotation_z(0.8 + movement * -0.8);
|
||||
|
||||
next.head.orientation = Quaternion::rotation_x(0.4)
|
||||
* Quaternion::rotation_z(0.2 + movement * -0.8);
|
||||
|
||||
next.chest.position = Vec3::new(
|
||||
0.0,
|
||||
skeleton_attr.chest.0,
|
||||
skeleton_attr.chest.1 + 2.0 + movement * -4.0,
|
||||
);
|
||||
next.chest.orientation = Quaternion::rotation_x(movement * -0.8)
|
||||
* Quaternion::rotation_z(movement * -0.3);
|
||||
|
||||
next.belt.orientation = Quaternion::rotation_x(movement * 0.2)
|
||||
* Quaternion::rotation_z(0.48 + movement * -0.48);
|
||||
|
||||
next.shorts.orientation = Quaternion::rotation_x(movement * 0.3)
|
||||
* Quaternion::rotation_z(0.8 + movement * -0.8);
|
||||
if velocity < 0.5 {
|
||||
next.l_foot.position = Vec3::new(
|
||||
-skeleton_attr.foot.0,
|
||||
skeleton_attr.foot.1 - 7.0 + movement * 7.0,
|
||||
skeleton_attr.foot.2,
|
||||
);
|
||||
next.l_foot.orientation =
|
||||
Quaternion::rotation_x(-0.8 + movement * 0.8)
|
||||
* Quaternion::rotation_z(0.3 + movement * -0.3);
|
||||
|
||||
next.r_foot.position = Vec3::new(
|
||||
skeleton_attr.foot.0,
|
||||
skeleton_attr.foot.1 + 5.0 + movement * -5.0,
|
||||
skeleton_attr.foot.2,
|
||||
);
|
||||
next.r_foot.orientation =
|
||||
Quaternion::rotation_y(-0.3 + movement * 0.3)
|
||||
* Quaternion::rotation_z(0.4 + movement * -0.4);
|
||||
} else {
|
||||
};
|
||||
},
|
||||
StageSection::Recover => {
|
||||
next.control.position = Vec3::new(
|
||||
1.0 + movement * -5.0,
|
||||
10.0 + movement * -3.0,
|
||||
12.0 + movement * -8.0,
|
||||
);
|
||||
next.control.orientation =
|
||||
Quaternion::rotation_x(0.8 + movement * -1.1)
|
||||
* Quaternion::rotation_y(movement * -0.15)
|
||||
* Quaternion::rotation_z(0.0);
|
||||
next.head.orientation = Quaternion::rotation_x(0.4 + movement * -0.4)
|
||||
* Quaternion::rotation_z(-0.6 + movement * 0.6);
|
||||
|
||||
next.belt.orientation = Quaternion::rotation_x(0.2 + movement * -0.2);
|
||||
|
||||
next.shorts.orientation = Quaternion::rotation_x(0.3 + movement * -0.3);
|
||||
|
||||
next.chest.position = Vec3::new(
|
||||
0.0,
|
||||
skeleton_attr.chest.0,
|
||||
skeleton_attr.chest.1 - 2.0 + movement * 2.0,
|
||||
);
|
||||
next.chest.orientation = Quaternion::rotation_x(-0.8 + movement * 0.8)
|
||||
* Quaternion::rotation_z(-0.3 + movement * 0.3);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
next.second.scale = match (
|
||||
active_tool_kind.map(|tk| tk.hands()),
|
||||
second_tool_kind.map(|tk| tk.hands()),
|
||||
) {
|
||||
(Some(Hands::OneHand), Some(Hands::OneHand)) => Vec3::one(),
|
||||
(_, _) => Vec3::zero(),
|
||||
};
|
||||
|
||||
next
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ use std::{f32::consts::PI, ops::Mul};
|
||||
pub struct WieldAnimation;
|
||||
|
||||
impl Animation for WieldAnimation {
|
||||
type Dependency = (Option<ToolKind>, Option<ToolKind>, f32, f64);
|
||||
type Dependency = (Option<ToolKind>, Option<ToolKind>, Vec3<f32>, f64);
|
||||
type Skeleton = CharacterSkeleton;
|
||||
|
||||
#[cfg(feature = "use-dyn-lib")]
|
||||
@ -25,6 +25,7 @@ impl Animation for WieldAnimation {
|
||||
) -> Self::Skeleton {
|
||||
*rate = 1.0;
|
||||
let lab = 1.0;
|
||||
let speed = Vec2::<f32>::from(velocity).magnitude();
|
||||
|
||||
let mut next = (*skeleton).clone();
|
||||
let head_look = Vec2::new(
|
||||
@ -40,6 +41,14 @@ impl Animation for WieldAnimation {
|
||||
* 0.1,
|
||||
);
|
||||
|
||||
let footrotl = (((1.0)
|
||||
/ (0.5
|
||||
+ (0.5)
|
||||
* ((anim_time as f32 * 16.0 * lab as f32 + PI * 1.4).sin()).powf(2.0 as f32)))
|
||||
.sqrt())
|
||||
* ((anim_time as f32 * 16.0 * lab as f32 + PI * 1.4).sin());
|
||||
let foothoril = (anim_time as f32 * 16.0 * lab as f32 + PI * 1.45).sin();
|
||||
|
||||
let slowalt = (anim_time as f32 * 6.0 + PI).cos();
|
||||
let u_slow = (anim_time as f32 * 1.0 + PI).sin();
|
||||
let slow = (anim_time as f32 * 3.0 + PI).sin();
|
||||
@ -52,7 +61,7 @@ impl Animation for WieldAnimation {
|
||||
let noisea = (anim_time as f32 * 11.0 + PI / 6.0).sin();
|
||||
let noiseb = (anim_time as f32 * 19.0 + PI / 4.0).sin();
|
||||
|
||||
if velocity > 0.5 {
|
||||
if speed > 0.5 {
|
||||
next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler;
|
||||
next.torso.orientation = Quaternion::rotation_x(-0.2);
|
||||
next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler;
|
||||
@ -162,7 +171,7 @@ impl Animation for WieldAnimation {
|
||||
next.r_control.position = Vec3::new(7.0, 0.0, 0.0);
|
||||
},
|
||||
Some(ToolKind::Axe(_)) => {
|
||||
if velocity < 0.5 {
|
||||
if speed < 0.5 {
|
||||
next.head.position = Vec3::new(
|
||||
0.0,
|
||||
-3.5 + skeleton_attr.head.0,
|
||||
@ -229,21 +238,33 @@ impl Animation for WieldAnimation {
|
||||
next.control.scale = Vec3::one();
|
||||
},
|
||||
Some(ToolKind::Staff(_)) | Some(ToolKind::Sceptre(_)) => {
|
||||
next.l_hand.position = Vec3::new(11.0, 5.0, -4.0);
|
||||
if speed > 0.5 && velocity.z == 0.0 {
|
||||
next.r_hand.position = Vec3::new(
|
||||
4.0 + skeleton_attr.hand.0 + foothoril * 1.3,
|
||||
-2.0 + skeleton_attr.hand.1 + foothoril * -6.5,
|
||||
-2.0 + skeleton_attr.hand.2 - foothoril * 7.0,
|
||||
);
|
||||
next.r_hand.orientation = Quaternion::rotation_x(0.6 + footrotl * -1.2)
|
||||
* Quaternion::rotation_y(footrotl * -0.4);
|
||||
} else {
|
||||
next.r_hand.position = Vec3::new(0.0, 0.0, 2.0);
|
||||
next.r_hand.orientation =
|
||||
Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.2);
|
||||
};
|
||||
next.control.position = Vec3::new(-4.0, 7.0, 4.0);
|
||||
|
||||
next.l_hand.position = Vec3::new(0.0, 0.0, -4.0);
|
||||
next.l_hand.orientation =
|
||||
Quaternion::rotation_x(1.27) * Quaternion::rotation_y(0.0);
|
||||
next.l_hand.scale = Vec3::one() * 1.05;
|
||||
next.r_hand.position = Vec3::new(12.0, 5.5, 2.0);
|
||||
next.r_hand.orientation =
|
||||
Quaternion::rotation_x(1.57) * Quaternion::rotation_y(0.2);
|
||||
|
||||
next.r_hand.scale = Vec3::one() * 1.05;
|
||||
next.main.position = Vec3::new(12.0, 8.5, 13.2);
|
||||
next.main.position = Vec3::new(0.0, 0.0, 13.2);
|
||||
next.main.orientation = Quaternion::rotation_y(3.14);
|
||||
|
||||
next.control.position = Vec3::new(-18.0, 1.0, 6.0);
|
||||
next.control.orientation = Quaternion::rotation_x(-0.3 + u_slow * 0.1)
|
||||
* Quaternion::rotation_y(0.15)
|
||||
* Quaternion::rotation_z(u_slowalt * 0.08);
|
||||
* Quaternion::rotation_z(u_slowalt * 0.1);
|
||||
next.control.scale = Vec3::one();
|
||||
},
|
||||
Some(ToolKind::Shield(_)) => {
|
||||
@ -317,7 +338,7 @@ impl Animation for WieldAnimation {
|
||||
next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler;
|
||||
},
|
||||
Some(ToolKind::Farming(_)) => {
|
||||
if velocity < 0.5 {
|
||||
if speed < 0.5 {
|
||||
next.head.orientation = Quaternion::rotation_z(head_look.x)
|
||||
* Quaternion::rotation_x(-0.2 + head_look.y.abs());
|
||||
next.head.scale = Vec3::one() * skeleton_attr.head_scale;
|
||||
|
@ -286,7 +286,7 @@ impl SfxMgr {
|
||||
// TODO: from sfx config?
|
||||
"voxygen.audio.sfx.explosion",
|
||||
*pos,
|
||||
Some((*power / 2.5).min(1.5)),
|
||||
Some((power.abs() / 2.5).min(1.5)),
|
||||
);
|
||||
},
|
||||
Outcome::ProjectileShot { pos, body, .. } => {
|
||||
|
@ -135,17 +135,19 @@ image_ids! {
|
||||
twohaxe_m2: "voxygen.element.icons.2haxe_m2",
|
||||
bow_m1: "voxygen.element.icons.bow_m1",
|
||||
bow_m2: "voxygen.element.icons.bow_m2",
|
||||
staff_m1: "voxygen.element.icons.staff_m1",
|
||||
staff_m2: "voxygen.element.icons.staff_m2",
|
||||
staff_melee: "voxygen.element.icons.staff_m1",
|
||||
fireball: "voxygen.element.icons.staff_m2",
|
||||
flyingrod_m1: "voxygen.element.icons.debug_wand_m1",
|
||||
flyingrod_m2: "voxygen.element.icons.debug_wand_m2",
|
||||
sword_pierce: "voxygen.element.icons.skill_sword_pierce",
|
||||
hammergolf: "voxygen.element.icons.skill_hammergolf",
|
||||
axespin: "voxygen.element.icons.skill_axespin",
|
||||
fire_aoe: "voxygen.element.icons.skill_fire_aoe",
|
||||
flamethrower: "voxygen.element.icons.skill_flamethrower",
|
||||
|
||||
// Skillbar
|
||||
level_up: "voxygen.element.misc_bg.level_up",
|
||||
level_down:"voxygen.element.misc_bg.level_down",
|
||||
level_down: "voxygen.element.misc_bg.level_down",
|
||||
xp_bar_mid: "voxygen.element.skillbar.xp_bar_mid",
|
||||
xp_bar_left: "voxygen.element.skillbar.xp_bar_left",
|
||||
xp_bar_right: "voxygen.element.skillbar.xp_bar_right",
|
||||
@ -262,9 +264,9 @@ image_ids! {
|
||||
fitness_ico: "voxygen.element.icons.fitness",
|
||||
protection_ico: "voxygen.element.icons.protection",
|
||||
|
||||
not_found:"voxygen.element.not_found",
|
||||
not_found: "voxygen.element.not_found",
|
||||
|
||||
help:"voxygen.element.help",
|
||||
help: "voxygen.element.help",
|
||||
|
||||
death_bg: "voxygen.background.death",
|
||||
hurt_bg: "voxygen.background.hurt",
|
||||
|
@ -621,7 +621,7 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
ToolKind::Axe(_) => self.imgs.twohaxe_m1,
|
||||
ToolKind::Bow(_) => self.imgs.bow_m1,
|
||||
ToolKind::Sceptre(_) => self.imgs.heal_0,
|
||||
ToolKind::Staff(_) => self.imgs.staff_m1,
|
||||
ToolKind::Staff(_) => self.imgs.fireball,
|
||||
ToolKind::Debug(kind) => match kind.as_ref() {
|
||||
"Boost" => self.imgs.flyingrod_m1,
|
||||
_ => self.imgs.nothing,
|
||||
@ -700,7 +700,7 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
Some(ToolKind::Axe(_)) => self.imgs.axespin,
|
||||
Some(ToolKind::Bow(_)) => self.imgs.bow_m2,
|
||||
Some(ToolKind::Sceptre(_)) => self.imgs.heal_bomb,
|
||||
Some(ToolKind::Staff(_)) => self.imgs.staff_m2,
|
||||
Some(ToolKind::Staff(_)) => self.imgs.flamethrower,
|
||||
Some(ToolKind::Debug(kind)) => match kind.as_ref() {
|
||||
"Boost" => self.imgs.flyingrod_m2,
|
||||
_ => self.imgs.nothing,
|
||||
|
@ -82,7 +82,7 @@ impl SlotKey<Loadout, ItemImgs> for EquipSlot {
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum HotbarImage {
|
||||
Item(ItemKey),
|
||||
Fireball,
|
||||
FireAoe,
|
||||
SnakeArrow,
|
||||
SwordWhirlwind,
|
||||
HammerLeap,
|
||||
@ -112,7 +112,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
|
||||
.and_then(|kind| {
|
||||
match kind {
|
||||
ItemKind::Tool(Tool { kind, .. }) => match kind {
|
||||
ToolKind::Staff(_) => Some(HotbarImage::Fireball),
|
||||
ToolKind::Staff(_) => Some(HotbarImage::FireAoe),
|
||||
ToolKind::Hammer(_) => Some(HotbarImage::HammerLeap),
|
||||
ToolKind::Axe(_) => Some(HotbarImage::AxeLeapSlash),
|
||||
ToolKind::Bow(_) => Some(HotbarImage::BowJumpBurst),
|
||||
@ -126,9 +126,9 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
|
||||
_ => None,
|
||||
}
|
||||
.map(|image_key| match image_key {
|
||||
HotbarImage::Fireball => (
|
||||
HotbarImage::FireAoe => (
|
||||
image_key,
|
||||
(energy.current() < 450).then_some(Color::Rgba(0.3, 0.3, 0.3, 0.8)),
|
||||
(energy.current() < 600).then_some(Color::Rgba(0.3, 0.3, 0.3, 0.8)),
|
||||
),
|
||||
HotbarImage::HammerLeap => (
|
||||
image_key,
|
||||
@ -166,7 +166,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
|
||||
match key {
|
||||
HotbarImage::Item(key) => item_imgs.img_id_or_not_found_img(key.clone()),
|
||||
HotbarImage::SnakeArrow => imgs.snake_arrow_0,
|
||||
HotbarImage::Fireball => imgs.fire_spell_1,
|
||||
HotbarImage::FireAoe => imgs.fire_aoe,
|
||||
HotbarImage::SwordWhirlwind => imgs.sword_whirlwind,
|
||||
HotbarImage::HammerLeap => imgs.hammerleap,
|
||||
HotbarImage::AxeLeapSlash => imgs.skill_axe_leap_slash,
|
||||
|
@ -111,6 +111,8 @@ pub enum ParticleMode {
|
||||
GroundShockwave = 12,
|
||||
HealingBeam = 13,
|
||||
EnergyNature = 14,
|
||||
FlameThrower = 15,
|
||||
FireShockwave = 16,
|
||||
}
|
||||
|
||||
impl ParticleMode {
|
||||
|
@ -991,6 +991,34 @@ impl FigureMgr {
|
||||
skeleton_attr,
|
||||
)
|
||||
},
|
||||
CharacterState::Shockwave(s) => {
|
||||
let stage_time = s.timer.as_secs_f64();
|
||||
let stage_progress = match s.stage_section {
|
||||
StageSection::Buildup => {
|
||||
stage_time / s.static_data.buildup_duration.as_secs_f64()
|
||||
},
|
||||
StageSection::Swing => {
|
||||
stage_time / s.static_data.swing_duration.as_secs_f64()
|
||||
},
|
||||
StageSection::Recover => {
|
||||
stage_time / s.static_data.recover_duration.as_secs_f64()
|
||||
},
|
||||
_ => 0.0,
|
||||
};
|
||||
anim::character::ShockwaveAnimation::update_skeleton(
|
||||
&target_base,
|
||||
(
|
||||
active_tool_kind,
|
||||
second_tool_kind,
|
||||
time,
|
||||
vel.0.magnitude(),
|
||||
Some(s.stage_section),
|
||||
),
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
skeleton_attr,
|
||||
)
|
||||
},
|
||||
CharacterState::LeapMelee(s) => {
|
||||
let stage_progress = match active_tool_kind {
|
||||
Some(ToolKind::Axe(_) | ToolKind::Hammer(_)) => {
|
||||
@ -1067,18 +1095,28 @@ impl FigureMgr {
|
||||
skeleton_attr,
|
||||
)
|
||||
},
|
||||
CharacterState::BasicBeam(_) => {
|
||||
anim::character::ChargeAnimation::update_skeleton(
|
||||
CharacterState::BasicBeam(s) => {
|
||||
let stage_time = s.timer.as_secs_f64();
|
||||
let stage_progress = match s.stage_section {
|
||||
StageSection::Buildup => {
|
||||
stage_time / s.static_data.buildup_duration.as_secs_f64()
|
||||
},
|
||||
StageSection::Cast => s.timer.as_secs_f64(),
|
||||
StageSection::Recover => {
|
||||
stage_time / s.static_data.recover_duration.as_secs_f64()
|
||||
},
|
||||
_ => 0.0,
|
||||
};
|
||||
anim::character::BeamAnimation::update_skeleton(
|
||||
&target_base,
|
||||
(
|
||||
active_tool_kind,
|
||||
second_tool_kind,
|
||||
vel.0.magnitude(),
|
||||
ori,
|
||||
state.last_ori,
|
||||
time,
|
||||
vel.0.magnitude(),
|
||||
Some(s.stage_section),
|
||||
),
|
||||
state.state_time,
|
||||
stage_progress,
|
||||
&mut state_animation_rate,
|
||||
skeleton_attr,
|
||||
)
|
||||
@ -1188,7 +1226,7 @@ impl FigureMgr {
|
||||
} else {
|
||||
anim::character::WieldAnimation::update_skeleton(
|
||||
&target_base,
|
||||
(active_tool_kind, second_tool_kind, vel.0.magnitude(), time),
|
||||
(active_tool_kind, second_tool_kind, vel.0, time),
|
||||
state.state_time,
|
||||
&mut state_animation_rate,
|
||||
skeleton_attr,
|
||||
@ -2322,7 +2360,7 @@ impl FigureMgr {
|
||||
skeleton_attr,
|
||||
)
|
||||
},
|
||||
CharacterState::GroundShockwave(_) => {
|
||||
CharacterState::Shockwave(_) => {
|
||||
anim::golem::ShockwaveAnimation::update_skeleton(
|
||||
&target_base,
|
||||
(vel.0.magnitude(), time),
|
||||
|
@ -389,8 +389,9 @@ impl Scene {
|
||||
Outcome::Explosion {
|
||||
pos,
|
||||
power,
|
||||
radius: _,
|
||||
is_attack: _,
|
||||
reagent,
|
||||
percent_damage,
|
||||
} => self.event_lights.push(EventLight {
|
||||
light: Light::new(
|
||||
*pos,
|
||||
@ -401,14 +402,14 @@ impl Scene {
|
||||
Some(Reagent::Red) => Rgb::new(1.0, 0.0, 0.0),
|
||||
Some(Reagent::Yellow) => Rgb::new(1.0, 1.0, 0.0),
|
||||
None => {
|
||||
if *percent_damage < 0.5 {
|
||||
if *power < 0.0 {
|
||||
Rgb::new(0.0, 1.0, 0.0)
|
||||
} else {
|
||||
Rgb::new(1.0, 0.5, 0.0)
|
||||
}
|
||||
},
|
||||
},
|
||||
*power
|
||||
power.abs()
|
||||
* match reagent {
|
||||
Some(_) => 5.0,
|
||||
None => 2.5,
|
||||
|
@ -22,7 +22,7 @@ use dot_vox::DotVoxData;
|
||||
use hashbrown::HashMap;
|
||||
use rand::prelude::*;
|
||||
use specs::{Join, WorldExt};
|
||||
use std::time::Duration;
|
||||
use std::{f32::consts::PI, time::Duration};
|
||||
use vek::*;
|
||||
|
||||
pub struct ParticleMgr {
|
||||
@ -58,22 +58,38 @@ impl ParticleMgr {
|
||||
Outcome::Explosion {
|
||||
pos,
|
||||
power,
|
||||
radius,
|
||||
is_attack,
|
||||
reagent,
|
||||
percent_damage,
|
||||
} => {
|
||||
if *percent_damage < 0.5 {
|
||||
self.particles.resize_with(
|
||||
self.particles.len() + (200.0 * power) as usize,
|
||||
|| {
|
||||
Particle::new(
|
||||
Duration::from_secs(1),
|
||||
time,
|
||||
ParticleMode::EnergyNature,
|
||||
*pos + Vec3::<f32>::zero()
|
||||
.map(|_| rng.gen_range(-3.0, 3.0) * power),
|
||||
)
|
||||
},
|
||||
);
|
||||
if *is_attack {
|
||||
if *power < 0.0 {
|
||||
self.particles.resize_with(
|
||||
self.particles.len() + (200.0 * power.abs()) as usize,
|
||||
|| {
|
||||
Particle::new(
|
||||
Duration::from_secs(1),
|
||||
time,
|
||||
ParticleMode::EnergyNature,
|
||||
*pos + Vec3::<f32>::zero()
|
||||
.map(|_| rng.gen_range(-radius, radius)),
|
||||
)
|
||||
},
|
||||
);
|
||||
} else {
|
||||
self.particles.resize_with(
|
||||
self.particles.len() + (200.0 * power.abs()) as usize,
|
||||
|| {
|
||||
Particle::new(
|
||||
Duration::from_secs(1),
|
||||
time,
|
||||
ParticleMode::CampfireFire,
|
||||
*pos + Vec3::<f32>::zero()
|
||||
.map(|_| rng.gen_range(-radius, radius)),
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.particles.resize_with(
|
||||
self.particles.len() + if reagent.is_some() { 300 } else { 150 },
|
||||
@ -101,8 +117,7 @@ impl ParticleMgr {
|
||||
Duration::from_secs(4),
|
||||
time,
|
||||
ParticleMode::CampfireSmoke,
|
||||
*pos + Vec2::<f32>::zero()
|
||||
.map(|_| rng.gen_range(-1.0, 1.0) * power),
|
||||
*pos + Vec2::<f32>::zero().map(|_| rng.gen_range(-radius, radius)),
|
||||
)
|
||||
},
|
||||
);
|
||||
@ -354,16 +369,48 @@ impl ParticleMgr {
|
||||
if let CharacterState::BasicBeam(b) = character_state {
|
||||
let particle_ori = b.particle_ori.unwrap_or(*ori.vec());
|
||||
if b.stage_section == StageSection::Cast {
|
||||
for i in 0..self.scheduler.heartbeats(Duration::from_millis(1)) {
|
||||
self.particles.push(Particle::new_beam(
|
||||
b.static_data.beam_duration,
|
||||
time + i as f64 / 1000.0,
|
||||
ParticleMode::HealingBeam,
|
||||
pos.0 + particle_ori * 0.5 + Vec3::new(0.0, 0.0, b.offset),
|
||||
pos.0
|
||||
+ particle_ori * b.static_data.range
|
||||
+ Vec3::new(0.0, 0.0, b.offset),
|
||||
));
|
||||
if b.static_data.base_hps > 0 {
|
||||
for i in 0..self.scheduler.heartbeats(Duration::from_millis(1)) {
|
||||
self.particles.push(Particle::new_beam(
|
||||
b.static_data.beam_duration,
|
||||
time + i as f64 / 1000.0,
|
||||
ParticleMode::HealingBeam,
|
||||
pos.0 + particle_ori * 0.5 + Vec3::new(0.0, 0.0, b.offset),
|
||||
pos.0
|
||||
+ particle_ori * b.static_data.range
|
||||
+ Vec3::new(0.0, 0.0, b.offset),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
let mut rng = thread_rng();
|
||||
let (from, to) = (Vec3::<f32>::unit_z(), particle_ori);
|
||||
let m = Mat3::<f32>::rotation_from_to_3d(from, to);
|
||||
self.particles.resize_with(
|
||||
self.particles.len()
|
||||
+ 2 * usize::from(
|
||||
self.scheduler.heartbeats(Duration::from_millis(1)),
|
||||
),
|
||||
|| {
|
||||
let phi: f32 =
|
||||
rng.gen_range(0.0, b.static_data.max_angle.to_radians());
|
||||
let theta: f32 = rng.gen_range(0.0, 2.0 * PI);
|
||||
let offset_z = Vec3::new(
|
||||
phi.sin() * theta.cos(),
|
||||
phi.sin() * theta.sin(),
|
||||
phi.cos(),
|
||||
);
|
||||
let random_ori = offset_z * m * Vec3::new(-1.0, -1.0, 1.0);
|
||||
Particle::new_beam(
|
||||
b.static_data.beam_duration,
|
||||
time,
|
||||
ParticleMode::FlameThrower,
|
||||
pos.0 + random_ori * 0.5 + Vec3::new(0.0, 0.0, b.offset),
|
||||
pos.0
|
||||
+ random_ori * b.static_data.range
|
||||
+ Vec3::new(0.0, 0.0, b.offset),
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -514,37 +561,52 @@ impl ParticleMgr {
|
||||
let theta = ori.0.y.atan2(ori.0.x);
|
||||
let dtheta = radians / distance;
|
||||
|
||||
// 1 / 3 the size of terrain voxel
|
||||
let scale = 1.0 / 3.0;
|
||||
|
||||
let scaled_speed = shockwave.properties.speed * scale;
|
||||
|
||||
let heartbeats = self
|
||||
.scheduler
|
||||
.heartbeats(Duration::from_millis(scaled_speed as u64));
|
||||
let new_particle_count = distance / scale * heartbeats as f32;
|
||||
self.particles.reserve(new_particle_count as usize);
|
||||
let heartbeats = self.scheduler.heartbeats(Duration::from_millis(1));
|
||||
|
||||
for heartbeat in 0..heartbeats {
|
||||
let sub_tick_interpolation = scaled_speed * 1000.0 * heartbeat as f32;
|
||||
if shockwave.properties.requires_ground {
|
||||
// 1 / 3 the size of terrain voxel
|
||||
let scale = 1.0 / 3.0;
|
||||
|
||||
let distance =
|
||||
shockwave.properties.speed * (elapsed as f32 - sub_tick_interpolation);
|
||||
let scaled_speed = shockwave.properties.speed * scale;
|
||||
|
||||
for d in 0..((distance / scale) as i32) {
|
||||
let arc_position = theta - radians / 2.0 + dtheta * d as f32 * scale;
|
||||
let sub_tick_interpolation = scaled_speed * 1000.0 * heartbeat as f32;
|
||||
|
||||
let position =
|
||||
pos.0 + distance * Vec3::new(arc_position.cos(), arc_position.sin(), 0.0);
|
||||
let distance =
|
||||
shockwave.properties.speed * (elapsed as f32 - sub_tick_interpolation);
|
||||
|
||||
let position_snapped = ((position / scale).floor() + 0.5) * scale;
|
||||
let new_particle_count = distance / scale as f32;
|
||||
self.particles.reserve(new_particle_count as usize);
|
||||
|
||||
self.particles.push(Particle::new(
|
||||
Duration::from_millis(250),
|
||||
time,
|
||||
ParticleMode::GroundShockwave,
|
||||
position_snapped,
|
||||
));
|
||||
for d in 0..((distance / scale) as i32) {
|
||||
let arc_position = theta - radians / 2.0 + dtheta * d as f32 * scale;
|
||||
|
||||
let position = pos.0
|
||||
+ distance * Vec3::new(arc_position.cos(), arc_position.sin(), 0.0);
|
||||
|
||||
let position_snapped = ((position / scale).floor() + 0.5) * scale;
|
||||
|
||||
self.particles.push(Particle::new(
|
||||
Duration::from_millis(250),
|
||||
time,
|
||||
ParticleMode::GroundShockwave,
|
||||
position_snapped,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
for d in 0..10 * distance as i32 {
|
||||
let arc_position = theta - radians / 2.0 + dtheta * d as f32 / 10.0;
|
||||
|
||||
let position = pos.0
|
||||
+ distance * Vec3::new(arc_position.cos(), arc_position.sin(), 0.0);
|
||||
|
||||
self.particles.push(Particle::new(
|
||||
Duration::from_secs_f32(distance / 50.0),
|
||||
time,
|
||||
ParticleMode::FireShockwave,
|
||||
position,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user