This commit is contained in:
Sam 2024-03-27 19:45:27 -04:00
parent e1ce09e0a1
commit 7daa9a29eb
19 changed files with 515 additions and 103 deletions

View File

@ -235,7 +235,7 @@
Simple(Hammer(PileDriver), "common.abilities.hammer.pile_driver"), Simple(Hammer(PileDriver), "common.abilities.hammer.pile_driver"),
Simple(Hammer(LungPummel), "common.abilities.hammer.lung_pummel"), Simple(Hammer(LungPummel), "common.abilities.hammer.lung_pummel"),
Simple(Hammer(HelmCrusher), "common.abilities.hammer.helm_crusher"), Simple(Hammer(HelmCrusher), "common.abilities.hammer.helm_crusher"),
// Simple(Hammer(Rampart), "common.abilities.hammer.rampart"), Simple(Hammer(Rampart), "common.abilities.hammer.rampart"),
// Simple(Hammer(Tenacity), "common.abilities.hammer.tenacity"), // Simple(Hammer(Tenacity), "common.abilities.hammer.tenacity"),
// Simple(Hammer(Earthshaker), "common.abilities.hammer.earthshaker"), // Simple(Hammer(Earthshaker), "common.abilities.hammer.earthshaker"),
// Simple(Hammer(Judgement), "common.abilities.hammer.judgement"), // Simple(Hammer(Judgement), "common.abilities.hammer.judgement"),

View File

@ -0,0 +1,23 @@
StaticAura(
buildup_duration: 0.4,
cast_duration: 0.3,
recover_duration: 0.4,
energy_cost: 20,
targets: InGroup,
auras: [
(
kind: ProtectingWard,
strength: 0.3,
duration: Some(1),
category: Magical,
),
],
aura_duration: Some(20),
range: 10.0,
sprite_info: Some((
sprite: Stones2,
del_timeout: Some((19, 3)),
summon_distance: (7, 10),
sparseness: 0.97,
)),
)

BIN
assets/voxygen/element/skills/hammer/rampart.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -434,3 +434,6 @@ common-abilities-hammer-upheaval = Upheaval
common-abilities-hammer-dual_upheaval = Upheaval common-abilities-hammer-dual_upheaval = Upheaval
.desc = .desc =
Slam your hammers into your foes, knocking them into the air and leaving them vulnerable to staggers. Slam your hammers into your foes, knocking them into the air and leaving them vulnerable to staggers.
common-abilities-hammer-rampart = Rampart
.desc =
Strike the ground, causing very mild tectonic uplift which protects your allies from attacks.

View File

@ -2953,6 +2953,21 @@
], ],
wind_sway: 0.0, wind_sway: 0.0,
), ),
(Stones2, ()): (
variations: [
(
model: "voxygen.voxel.sprite.rocks.rock-0",
offset: (-3.0, -3.5, 0.0),
lod_axes: (1.0, 1.0, 1.0),
),
(
model: "voxygen.voxel.sprite.rocks.rock-2",
offset: (-4.5, -4.5, 0.0),
lod_axes: (1.0, 1.0, 1.0),
),
],
wind_sway: 0.0,
),
// Twigs // Twigs
(Twigs, ()): ( (Twigs, ()): (
variations: [ variations: [

View File

@ -735,7 +735,8 @@ impl From<&CharacterState> for CharacterAbilityType {
| CharacterState::SpriteInteract(_) | CharacterState::SpriteInteract(_)
| CharacterState::Skate(_) | CharacterState::Skate(_)
| CharacterState::Transform(_) | CharacterState::Transform(_)
| CharacterState::Wallrun(_) => Self::Other, | CharacterState::Wallrun(_)
| CharacterState::StaticAura(_) => Self::Other,
} }
} }
} }
@ -974,6 +975,19 @@ pub enum CharacterAbility {
#[serde(default)] #[serde(default)]
meta: AbilityMeta, meta: AbilityMeta,
}, },
StaticAura {
buildup_duration: f32,
cast_duration: f32,
recover_duration: f32,
energy_cost: f32,
targets: combat::GroupTarget,
auras: Vec<aura::AuraBuffConstructor>,
aura_duration: Option<Secs>,
range: f32,
sprite_info: Option<static_aura::SpriteInfo>,
#[serde(default)]
meta: AbilityMeta,
},
Blink { Blink {
buildup_duration: f32, buildup_duration: f32,
recover_duration: f32, recover_duration: f32,
@ -1146,7 +1160,12 @@ impl CharacterAbility {
}; };
from_meta from_meta
&& match self { && match self {
CharacterAbility::Roll { energy_cost, .. } => { CharacterAbility::Roll { energy_cost, .. }
| CharacterAbility::StaticAura {
energy_cost,
sprite_info: Some(_),
..
} => {
data.physics.on_ground.is_some() data.physics.on_ground.is_some()
&& update.energy.try_change_by(-*energy_cost).is_ok() && update.energy.try_change_by(-*energy_cost).is_ok()
}, },
@ -1161,6 +1180,11 @@ impl CharacterAbility {
| CharacterAbility::ComboMelee2 { | CharacterAbility::ComboMelee2 {
energy_cost_per_strike: energy_cost, energy_cost_per_strike: energy_cost,
.. ..
}
| CharacterAbility::StaticAura {
energy_cost,
sprite_info: None,
..
} => update.energy.try_change_by(-*energy_cost).is_ok(), } => update.energy.try_change_by(-*energy_cost).is_ok(),
// Consumes energy within state, so value only checked before entering state // Consumes energy within state, so value only checked before entering state
CharacterAbility::RepeaterRanged { energy_cost, .. } => { CharacterAbility::RepeaterRanged { energy_cost, .. } => {
@ -1610,6 +1634,39 @@ impl CharacterAbility {
*range *= stats.range; *range *= stats.range;
*energy_cost /= stats.energy_efficiency; *energy_cost /= stats.energy_efficiency;
}, },
StaticAura {
ref mut buildup_duration,
ref mut cast_duration,
ref mut recover_duration,
targets: _,
ref mut auras,
aura_duration: _,
ref mut range,
ref mut energy_cost,
ref mut sprite_info,
meta: _,
} => {
*buildup_duration /= stats.speed;
*cast_duration /= stats.speed;
*recover_duration /= stats.speed;
auras.iter_mut().for_each(
|aura::AuraBuffConstructor {
kind: _,
ref mut strength,
duration: _,
category: _,
}| {
*strength *= stats.diminished_buff_strength();
},
);
*range *= stats.range;
*energy_cost /= stats.energy_efficiency;
*sprite_info = sprite_info.map(|mut si| {
si.summon_distance.0 *= stats.range;
si.summon_distance.1 *= stats.range;
si
});
},
Blink { Blink {
ref mut buildup_duration, ref mut buildup_duration,
ref mut recover_duration, ref mut recover_duration,
@ -1793,7 +1850,8 @@ impl CharacterAbility {
} }
| DiveMelee { energy_cost, .. } | DiveMelee { energy_cost, .. }
| RiposteMelee { energy_cost, .. } | RiposteMelee { energy_cost, .. }
| RapidMelee { energy_cost, .. } => *energy_cost, | RapidMelee { energy_cost, .. }
| StaticAura { energy_cost, .. } => *energy_cost,
BasicBeam { energy_drain, .. } => { BasicBeam { energy_drain, .. } => {
if *energy_drain > f32::EPSILON { if *energy_drain > f32::EPSILON {
1.0 1.0
@ -1856,7 +1914,8 @@ impl CharacterAbility {
| Music { .. } | Music { .. }
| BasicSummon { .. } | BasicSummon { .. }
| SpriteSummon { .. } | SpriteSummon { .. }
| Transform { .. } => 0, | Transform { .. }
| StaticAura { .. } => 0,
} }
} }
@ -1889,7 +1948,8 @@ impl CharacterAbility {
| DiveMelee { meta, .. } | DiveMelee { meta, .. }
| RiposteMelee { meta, .. } | RiposteMelee { meta, .. }
| RapidMelee { meta, .. } | RapidMelee { meta, .. }
| Transform { meta, .. } => *meta, | Transform { meta, .. }
| StaticAura { meta, .. } => *meta,
} }
} }
@ -2675,6 +2735,33 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
timer: Duration::default(), timer: Duration::default(),
stage_section: StageSection::Buildup, stage_section: StageSection::Buildup,
}), }),
CharacterAbility::StaticAura {
buildup_duration,
cast_duration,
recover_duration,
targets,
auras,
aura_duration,
range,
energy_cost: _,
sprite_info,
meta: _,
} => CharacterState::StaticAura(static_aura::Data {
static_data: static_aura::StaticData {
buildup_duration: Duration::from_secs_f32(*buildup_duration),
cast_duration: Duration::from_secs_f32(*cast_duration),
recover_duration: Duration::from_secs_f32(*recover_duration),
targets: *targets,
auras: auras.clone(),
aura_duration: *aura_duration,
range: *range,
ability_info,
sprite_info: *sprite_info,
},
timer: Duration::default(),
stage_section: StageSection::Buildup,
achieved_radius: sprite_info.map(|si| si.summon_distance.0.floor() as i32 - 1),
}),
CharacterAbility::Blink { CharacterAbility::Blink {
buildup_duration, buildup_duration,
recover_duration, recover_duration,

View File

@ -52,6 +52,7 @@ event_emitters! {
knockback: event::KnockbackEvent, knockback: event::KnockbackEvent,
sprite_light: event::ToggleSpriteLightEvent, sprite_light: event::ToggleSpriteLightEvent,
transform: event::TransformEvent, transform: event::TransformEvent,
create_aura_entity: event::CreateAuraEntityEvent,
} }
} }
@ -142,6 +143,9 @@ pub enum CharacterState {
BasicBeam(basic_beam::Data), BasicBeam(basic_beam::Data),
/// Creates an aura that persists as long as you are actively casting /// Creates an aura that persists as long as you are actively casting
BasicAura(basic_aura::Data), BasicAura(basic_aura::Data),
/// Creates an aura that is attached to a pseudo entity, so it doesn't move
/// with you Optionally allows for sprites to be created as well
StaticAura(static_aura::Data),
/// A short teleport that targets either a position or entity /// A short teleport that targets either a position or entity
Blink(blink::Data), Blink(blink::Data),
/// Summons creatures that fight for the caster /// Summons creatures that fight for the caster
@ -211,6 +215,7 @@ impl CharacterState {
| CharacterState::DiveMelee(_) | CharacterState::DiveMelee(_)
| CharacterState::RiposteMelee(_) | CharacterState::RiposteMelee(_)
| CharacterState::RapidMelee(_) | CharacterState::RapidMelee(_)
| CharacterState::StaticAura(_)
) )
} }
@ -276,6 +281,7 @@ impl CharacterState {
| CharacterState::DiveMelee(_) | CharacterState::DiveMelee(_)
| CharacterState::RiposteMelee(_) | CharacterState::RiposteMelee(_)
| CharacterState::RapidMelee(_) | CharacterState::RapidMelee(_)
| CharacterState::StaticAura(_)
) )
} }
@ -532,6 +538,7 @@ impl CharacterState {
CharacterState::RiposteMelee(data) => data.behavior(j, output_events), CharacterState::RiposteMelee(data) => data.behavior(j, output_events),
CharacterState::RapidMelee(data) => data.behavior(j, output_events), CharacterState::RapidMelee(data) => data.behavior(j, output_events),
CharacterState::Transform(data) => data.behavior(j, output_events), CharacterState::Transform(data) => data.behavior(j, output_events),
CharacterState::StaticAura(data) => data.behavior(j, output_events),
} }
} }
@ -586,6 +593,7 @@ impl CharacterState {
CharacterState::RiposteMelee(data) => data.handle_event(j, output_events, action), CharacterState::RiposteMelee(data) => data.handle_event(j, output_events, action),
CharacterState::RapidMelee(data) => data.handle_event(j, output_events, action), CharacterState::RapidMelee(data) => data.handle_event(j, output_events, action),
CharacterState::Transform(data) => data.handle_event(j, output_events, action), CharacterState::Transform(data) => data.handle_event(j, output_events, action),
CharacterState::StaticAura(data) => data.handle_event(j, output_events, action),
} }
} }
@ -639,6 +647,7 @@ impl CharacterState {
CharacterState::RiposteMelee(data) => Some(data.static_data.ability_info), CharacterState::RiposteMelee(data) => Some(data.static_data.ability_info),
CharacterState::RapidMelee(data) => Some(data.static_data.ability_info), CharacterState::RapidMelee(data) => Some(data.static_data.ability_info),
CharacterState::Transform(data) => Some(data.static_data.ability_info), CharacterState::Transform(data) => Some(data.static_data.ability_info),
CharacterState::StaticAura(data) => Some(data.static_data.ability_info),
} }
} }
@ -684,6 +693,7 @@ impl CharacterState {
CharacterState::RiposteMelee(data) => Some(data.stage_section), CharacterState::RiposteMelee(data) => Some(data.stage_section),
CharacterState::RapidMelee(data) => Some(data.stage_section), CharacterState::RapidMelee(data) => Some(data.stage_section),
CharacterState::Transform(data) => Some(data.stage_section), CharacterState::Transform(data) => Some(data.stage_section),
CharacterState::StaticAura(data) => Some(data.stage_section),
} }
} }
@ -868,6 +878,12 @@ impl CharacterState {
recover: Some(data.static_data.recover_duration), recover: Some(data.static_data.recover_duration),
..Default::default() ..Default::default()
}), }),
CharacterState::StaticAura(data) => Some(DurationsInfo {
buildup: Some(data.static_data.buildup_duration),
action: Some(data.static_data.cast_duration),
recover: Some(data.static_data.recover_duration),
..Default::default()
}),
} }
} }
@ -913,6 +929,7 @@ impl CharacterState {
CharacterState::RiposteMelee(data) => Some(data.timer), CharacterState::RiposteMelee(data) => Some(data.timer),
CharacterState::RapidMelee(data) => Some(data.timer), CharacterState::RapidMelee(data) => Some(data.timer),
CharacterState::Transform(data) => Some(data.timer), CharacterState::Transform(data) => Some(data.timer),
CharacterState::StaticAura(data) => Some(data.timer),
} }
} }
@ -973,6 +990,7 @@ impl CharacterState {
CharacterState::RiposteMelee(_) => Some(AttackSource::Melee), CharacterState::RiposteMelee(_) => Some(AttackSource::Melee),
CharacterState::RapidMelee(_) => Some(AttackSource::Melee), CharacterState::RapidMelee(_) => Some(AttackSource::Melee),
CharacterState::Transform(_) => None, CharacterState::Transform(_) => None,
CharacterState::StaticAura(_) => None,
} }
} }
} }

View File

@ -433,6 +433,12 @@ pub struct RequestPluginsEvent {
pub plugins: Vec<PluginHash>, pub plugins: Vec<PluginHash>,
} }
pub struct CreateAuraEntityEvent {
pub auras: comp::Auras,
pub pos: Pos,
pub creator_uid: Uid,
}
pub struct EventBus<E> { pub struct EventBus<E> {
queue: Mutex<VecDeque<E>>, queue: Mutex<VecDeque<E>>,
} }
@ -556,6 +562,7 @@ pub fn register_event_busses(ecs: &mut World) {
ecs.insert(EventBus::<ToggleSpriteLightEvent>::default()); ecs.insert(EventBus::<ToggleSpriteLightEvent>::default());
ecs.insert(EventBus::<TransformEvent>::default()); ecs.insert(EventBus::<TransformEvent>::default());
ecs.insert(EventBus::<RequestPluginsEvent>::default()); ecs.insert(EventBus::<RequestPluginsEvent>::default());
ecs.insert(EventBus::<CreateAuraEntityEvent>::default());
} }
/// Define ecs read data for event busses. And a way to convert them all to /// Define ecs read data for event busses. And a way to convert them all to

View File

@ -33,6 +33,7 @@ pub mod sit;
pub mod skate; pub mod skate;
pub mod sprite_interact; pub mod sprite_interact;
pub mod sprite_summon; pub mod sprite_summon;
pub mod static_aura;
pub mod stunned; pub mod stunned;
pub mod talk; pub mod talk;
pub mod transform; pub mod transform;

View File

@ -117,99 +117,29 @@ impl CharacterBehavior for Data {
let timer_frac = let timer_frac =
self.timer.as_secs_f32() / self.static_data.cast_duration.as_secs_f32(); self.timer.as_secs_f32() / self.static_data.cast_duration.as_secs_f32();
// Determines distance from summoner sprites should be created. Goes outward let anchor_pos = match self.static_data.anchor {
// with time. SpriteSummonAnchor::Summoner => data.pos.0,
let summon_distance = timer_frac // Use the selected target position, falling back to the
* (self.static_data.summon_distance.1 - self.static_data.summon_distance.0) // summoner position
+ self.static_data.summon_distance.0; SpriteSummonAnchor::Target => target_pos().unwrap_or(data.pos.0),
let summon_distance = summon_distance.round() as i32; };
let achieved_radius = create_sprites(
// Only summons sprites if summon distance is greater than achieved radius data,
for radius in self.achieved_radius..=summon_distance { output_events,
// 1 added to make range correct, too lazy to add 1 to both variables above self.static_data.sprite,
let radius = radius + 1; timer_frac,
// Creates a spiral iterator for the newly achieved radius self.static_data.summon_distance,
let spiral = Spiral2d::with_edge_radius(radius); self.achieved_radius,
for point in spiral { self.static_data.angle,
// If square is in the angle and is not sparse, generate sprite self.static_data.sparseness,
if data anchor_pos,
.ori matches!(self.static_data.anchor, SpriteSummonAnchor::Target),
.look_vec() self.static_data.del_timeout,
.xy() );
.angle_between(point.as_())
.to_degrees()
<= (self.static_data.angle / 2.0)
&& !thread_rng().gen_bool(self.static_data.sparseness)
{
let anchor_pos = match self.static_data.anchor {
SpriteSummonAnchor::Summoner => data.pos.0,
// Use the selected target position, falling back to the
// summoner position
SpriteSummonAnchor::Target => {
target_pos().unwrap_or(data.pos.0)
},
};
// The coordinates of where the sprite is created
let sprite_pos = Vec3::new(
anchor_pos.x.floor() as i32 + point.x,
anchor_pos.y.floor() as i32 + point.y,
anchor_pos.z.floor() as i32,
);
// Check for collision in z up to 10 blocks up or down
let (obstacle_z, obstale_z_result) = data
.terrain
.ray(
sprite_pos.map(|x| x as f32 + 0.5) + Vec3::unit_z() * 10.0,
sprite_pos.map(|x| x as f32 + 0.5) - Vec3::unit_z() * 10.0,
)
.until(|b| {
// Until reaching a solid block that is not the created
// sprite
Block::is_solid(b)
&& b.get_sprite() != Some(self.static_data.sprite)
})
.cast();
let z = match self.static_data.sprite {
// z height - 1 to delete sprite layer below caster
SpriteKind::Empty => {
sprite_pos.z + (10.5 - obstacle_z).ceil() as i32 - 1
},
_ => {
sprite_pos.z
+ if let (SpriteSummonAnchor::Target, Ok(None)) =
(&self.static_data.anchor, obstale_z_result)
{
0
} else {
(10.5 - obstacle_z).ceil() as i32
}
},
};
// Location sprite will be created
let sprite_pos = Vec3::new(sprite_pos.x, sprite_pos.y, z);
// Layers of sprites
let layers = match self.static_data.sprite {
SpriteKind::SeaUrchin => 2,
_ => 1,
};
for i in 0..layers {
// Send server event to create sprite
output_events.emit_server(CreateSpriteEvent {
pos: Vec3::new(sprite_pos.x, sprite_pos.y, z + i),
sprite: self.static_data.sprite,
del_timeout: self.static_data.del_timeout,
});
}
}
}
}
update.character = CharacterState::SpriteSummon(Data { update.character = CharacterState::SpriteSummon(Data {
timer: tick_attack_or_default(data, self.timer, None), timer: tick_attack_or_default(data, self.timer, None),
achieved_radius: summon_distance, achieved_radius,
..*self ..*self
}); });
// Send local event used for frontend shenanigans // Send local event used for frontend shenanigans
@ -270,3 +200,94 @@ impl CharacterBehavior for Data {
update update
} }
} }
/// Returns achieved radius
pub fn create_sprites(
data: &JoinData,
output_events: &mut OutputEvents,
sprite: SpriteKind,
timer_frac: f32,
summon_distance: (f32, f32),
achieved_radius: i32,
angle: f32,
sparseness: f64,
anchor_pos: Vec3<f32>,
stack_sprites: bool,
del_timeout: Option<(f32, f32)>,
) -> i32 {
// Determines distance from summoner sprites should be created. Goes outward
// with time.
let summon_distance = timer_frac * (summon_distance.1 - summon_distance.0) + summon_distance.0;
let summon_distance = summon_distance.round() as i32;
// Only summons sprites if summon distance is greater than achieved radius
for radius in achieved_radius..=summon_distance {
// 1 added to make range correct, too lazy to add 1 to both variables above
let radius = radius + 1;
// Creates a spiral iterator for the newly achieved radius
let spiral = Spiral2d::with_edge_radius(radius);
for point in spiral {
// If square is in the angle and is not sparse, generate sprite
if data
.ori
.look_vec()
.xy()
.angle_between(point.as_())
.to_degrees()
<= (angle / 2.0)
&& !thread_rng().gen_bool(sparseness)
{
// The coordinates of where the sprite is created
let sprite_pos = Vec3::new(
anchor_pos.x.floor() as i32 + point.x,
anchor_pos.y.floor() as i32 + point.y,
anchor_pos.z.floor() as i32,
);
// Check for collision in z up to 10 blocks up or down
let (obstacle_z, obstacle_z_result) = data
.terrain
.ray(
sprite_pos.map(|x| x as f32 + 0.5) + Vec3::unit_z() * 10.0,
sprite_pos.map(|x| x as f32 + 0.5) - Vec3::unit_z() * 10.0,
)
.until(|b| {
// Until reaching a solid block that is not the created
// sprite
Block::is_solid(b) && b.get_sprite() != Some(sprite)
})
.cast();
let z = match sprite {
// z height - 1 to delete sprite layer below caster
SpriteKind::Empty => sprite_pos.z + (10.5 - obstacle_z).ceil() as i32 - 1,
_ => {
sprite_pos.z
+ if let (true, Ok(None)) = (stack_sprites, obstacle_z_result) {
0
} else {
(10.5 - obstacle_z).ceil() as i32
}
},
};
// Location sprite will be created
let sprite_pos = Vec3::new(sprite_pos.x, sprite_pos.y, z);
// Layers of sprites
let layers = match sprite {
SpriteKind::SeaUrchin => 2,
_ => 1,
};
for i in 0..layers {
// Send server event to create sprite
output_events.emit_server(CreateSpriteEvent {
pos: Vec3::new(sprite_pos.x, sprite_pos.y, z + i),
sprite,
del_timeout,
});
}
}
}
}
summon_distance
}

View File

@ -0,0 +1,169 @@
use crate::{
combat::GroupTarget,
comp::{
aura::{AuraBuffConstructor, AuraTarget, Auras},
character_state::OutputEvents,
CharacterState, StateUpdate,
},
event::CreateAuraEntityEvent,
resources::Secs,
states::{
behavior::{CharacterBehavior, JoinData},
sprite_summon::create_sprites,
utils::*,
},
terrain::SpriteKind,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
/// Separated out to condense update portions of character state
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct StaticData {
/// How long until state should create the aura
pub buildup_duration: Duration,
/// How long the state is creating an aura
pub cast_duration: Duration,
/// How long the state has until exiting
pub recover_duration: Duration,
/// Determines how the aura selects its targets
pub targets: GroupTarget,
/// Has information used to construct the auras
pub auras: Vec<AuraBuffConstructor>,
/// How long aura lasts
pub aura_duration: Option<Secs>,
/// Radius of aura
pub range: f32,
/// Information about sprites if the state should create sprites
pub sprite_info: Option<SpriteInfo>,
/// What key is used to press ability
pub ability_info: AbilityInfo,
}
#[derive(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,
/// If creates sprites, what radius has been achieved so far
pub achieved_radius: Option<i32>,
}
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
let mut update = StateUpdate::from(data);
match self.stage_section {
StageSection::Buildup => {
if self.timer < self.static_data.buildup_duration {
// Build up
update.character = CharacterState::StaticAura(Data {
static_data: self.static_data.clone(),
timer: tick_attack_or_default(data, self.timer, None),
..*self
});
} else {
// Build up
update.character = CharacterState::StaticAura(Data {
static_data: self.static_data.clone(),
timer: Duration::default(),
stage_section: StageSection::Action,
achieved_radius: self.achieved_radius,
});
}
},
StageSection::Action => {
if self.timer < self.static_data.cast_duration {
// If creates sprites, create sprites
let achieved_radius = if let Some(sprite_info) = self.static_data.sprite_info {
let timer_frac =
self.timer.as_secs_f32() / self.static_data.cast_duration.as_secs_f32();
let achieved_radius = create_sprites(
data,
output_events,
sprite_info.sprite,
timer_frac,
sprite_info.summon_distance,
self.achieved_radius.unwrap_or(0),
360.0,
sprite_info.sparseness,
data.pos.0,
false,
sprite_info.del_timeout,
);
Some(achieved_radius)
} else {
None
};
// Cast
update.character = CharacterState::StaticAura(Data {
static_data: self.static_data.clone(),
timer: tick_attack_or_default(data, self.timer, None),
achieved_radius,
..*self
});
} else {
// Creates aura
let targets =
AuraTarget::from((Some(self.static_data.targets), Some(data.uid)));
let mut auras = Vec::new();
for aura_data in &self.static_data.auras {
let aura = aura_data.to_aura(
data.uid,
self.static_data.range,
self.static_data.aura_duration,
targets,
*data.time,
);
auras.push(aura);
}
output_events.emit_server(CreateAuraEntityEvent {
auras: Auras::new(auras),
pos: *data.pos,
creator_uid: *data.uid,
});
update.character = CharacterState::StaticAura(Data {
static_data: self.static_data.clone(),
timer: Duration::default(),
stage_section: StageSection::Recover,
achieved_radius: self.achieved_radius,
});
}
},
StageSection::Recover => {
if self.timer < self.static_data.recover_duration {
update.character = CharacterState::StaticAura(Data {
static_data: self.static_data.clone(),
timer: tick_attack_or_default(data, self.timer, None),
..*self
});
} else {
// Done
end_ability(data, &mut update);
}
},
_ => {
// If it somehow ends up in an incorrect stage section
end_ability(data, &mut update);
},
}
// At end of state logic so an interrupt isn't overwritten
handle_interrupts(data, &mut update, output_events);
update
}
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SpriteInfo {
pub sprite: SpriteKind,
pub del_timeout: Option<(f32, f32)>,
pub summon_distance: (f32, f32),
pub sparseness: f64,
}

View File

@ -70,6 +70,7 @@ sprites! {
Bomb = 0x02, Bomb = 0x02,
FireBlock = 0x03, // FireBlock for Burning Buff FireBlock = 0x03, // FireBlock for Burning Buff
HotSurface = 0x04, HotSurface = 0x04,
Stones2 = 0x05, // Same as `Stones` but not collectible
}, },
// Furniture. In the future, we might add an attribute to customise material // Furniture. In the future, we might add an attribute to customise material
// TODO: Remove sizes and variants, represent with attributes // TODO: Remove sizes and variants, represent with attributes

View File

@ -167,7 +167,8 @@ impl<'a> System<'a> for Sys {
| CharacterState::FinisherMelee(_) | CharacterState::FinisherMelee(_)
| CharacterState::DiveMelee(_) | CharacterState::DiveMelee(_)
| CharacterState::RiposteMelee(_) | CharacterState::RiposteMelee(_)
| CharacterState::RapidMelee(_) => { | CharacterState::RapidMelee(_)
| CharacterState::StaticAura(_) => {
if energy.needs_regen_rate_reset() { if energy.needs_regen_rate_reset() {
energy.reset_regen_rate(); energy.reset_regen_rate();
} }

View File

@ -12,9 +12,9 @@ use common::{
WaypointArea, WaypointArea,
}, },
event::{ event::{
CreateItemDropEvent, CreateNpcEvent, CreateObjectEvent, CreateShipEvent, CreateAuraEntityEvent, CreateItemDropEvent, CreateNpcEvent, CreateObjectEvent,
CreateSpecialEntityEvent, EventBus, InitializeCharacterEvent, InitializeSpectatorEvent, CreateShipEvent, CreateSpecialEntityEvent, EventBus, InitializeCharacterEvent,
ShockwaveEvent, ShootEvent, UpdateCharacterDataEvent, InitializeSpectatorEvent, ShockwaveEvent, ShootEvent, UpdateCharacterDataEvent,
}, },
generation::SpecialEntity, generation::SpecialEntity,
mounting::{Mounting, Volume, VolumeMounting, VolumePos}, mounting::{Mounting, Volume, VolumeMounting, VolumePos},
@ -489,3 +489,16 @@ pub fn handle_create_object(
.maybe_with(stats) .maybe_with(stats)
.build(); .build();
} }
pub fn handle_create_aura_entity(server: &mut Server, ev: CreateAuraEntityEvent) {
server
.state
.ecs_mut()
.create_entity_synced()
.with(ev.pos)
.with(comp::Vel(Vec3::zero()))
.with(comp::Ori::default())
.with(ev.auras)
.with(comp::Alignment::Owned(ev.creator_uid))
.build();
}

View File

@ -13,9 +13,10 @@ use specs::{
use self::{ use self::{
entity_creation::{ entity_creation::{
handle_create_item_drop, handle_create_npc, handle_create_object, handle_create_ship, handle_create_aura_entity, handle_create_item_drop, handle_create_npc,
handle_create_special_entity, handle_initialize_character, handle_initialize_spectator, handle_create_object, handle_create_ship, handle_create_special_entity,
handle_loaded_character_data, handle_shockwave, handle_shoot, handle_initialize_character, handle_initialize_spectator, handle_loaded_character_data,
handle_shockwave, handle_shoot,
}, },
entity_manipulation::{handle_delete, handle_transform}, entity_manipulation::{handle_delete, handle_transform},
interaction::handle_tame_pet, interaction::handle_tame_pet,
@ -149,6 +150,7 @@ impl Server {
self.handle_serial_events(handle_create_special_entity); self.handle_serial_events(handle_create_special_entity);
self.handle_serial_events(handle_create_item_drop); self.handle_serial_events(handle_create_item_drop);
self.handle_serial_events(handle_create_object); self.handle_serial_events(handle_create_object);
self.handle_serial_events(handle_create_aura_entity);
self.handle_serial_events(handle_delete); self.handle_serial_events(handle_delete);
self.handle_serial_events(handle_character_delete); self.handle_serial_events(handle_character_delete);

View File

@ -3,6 +3,7 @@ use super::{
hammer_start, twist_back, twist_forward, CharacterSkeleton, SkeletonAttr, hammer_start, twist_back, twist_forward, CharacterSkeleton, SkeletonAttr,
}; };
use common::states::utils::StageSection; use common::states::utils::StageSection;
use std::f32::consts::PI;
pub struct Input { pub struct Input {
pub attack: bool, pub attack: bool,
@ -136,6 +137,28 @@ impl Animation for ShockwaveAnimation {
next.control.position += Vec3::new(-16.0, 0.0, 0.0) * move2; next.control.position += Vec3::new(-16.0, 0.0, 0.0) * move2;
next.chest.orientation.rotate_x(-0.8 * move2); next.chest.orientation.rotate_x(-0.8 * move2);
}, },
Some("common.abilities.hammer.rampart") => {
hammer_start(&mut next, s_a);
let (move1, move2, move3) = match stage_section {
Some(StageSection::Buildup) => (anim_time, 0.0, 0.0),
Some(StageSection::Action) => (1.0, anim_time, 0.0),
Some(StageSection::Recover) => (1.0, 1.0, anim_time),
_ => (0.0, 0.0, 0.0),
};
let pullback = 1.0 - move3;
let move1 = move1 * pullback;
let move2 = move2 * pullback;
next.control.orientation.rotate_x(move1 * 0.6);
next.control.orientation.rotate_y(move1 * -PI / 2.0);
next.hand_l.orientation.rotate_y(move1 * -PI);
next.hand_r.orientation.rotate_y(move1 * -PI);
next.control.position += Vec3::new(-5.0, 0.0, 30.0) * move1;
next.control.position += Vec3::new(0.0, 0.0, -10.0) * move2;
next.torso.orientation.rotate_x(move2 * -0.6);
next.control.orientation.rotate_x(move2 * 0.6);
},
_ => {}, _ => {},
} }

View File

@ -330,6 +330,7 @@ image_ids! {
hammer_helm_crusher: "voxygen.element.skills.hammer.helm_crusher", hammer_helm_crusher: "voxygen.element.skills.hammer.helm_crusher",
hammer_iron_tempest: "voxygen.element.skills.hammer.iron_tempest", hammer_iron_tempest: "voxygen.element.skills.hammer.iron_tempest",
hammer_upheaval: "voxygen.element.skills.hammer.upheaval", hammer_upheaval: "voxygen.element.skills.hammer.upheaval",
hammer_rampart: "voxygen.element.skills.hammer.rampart",
// Skilltree Icons // Skilltree Icons
health_plus_skill: "voxygen.element.skills.skilltree.health_plus", health_plus_skill: "voxygen.element.skills.skilltree.health_plus",
energy_plus_skill: "voxygen.element.skills.skilltree.energy_plus", energy_plus_skill: "voxygen.element.skills.skilltree.energy_plus",

View File

@ -653,6 +653,7 @@ pub fn ability_image(imgs: &img_ids::Imgs, ability_id: &str) -> image::Id {
"common.abilities.hammer.dual_iron_tempest" => imgs.hammer_iron_tempest, "common.abilities.hammer.dual_iron_tempest" => imgs.hammer_iron_tempest,
"common.abilities.hammer.upheaval" => imgs.hammer_upheaval, "common.abilities.hammer.upheaval" => imgs.hammer_upheaval,
"common.abilities.hammer.dual_upheaval" => imgs.hammer_upheaval, "common.abilities.hammer.dual_upheaval" => imgs.hammer_upheaval,
"common.abilities.hammer.rampart" => imgs.hammer_rampart,
// Bow // Bow
"common.abilities.bow.charged" => imgs.bow_m1, "common.abilities.bow.charged" => imgs.bow_m1,
"common.abilities.bow.repeater" => imgs.bow_m2, "common.abilities.bow.repeater" => imgs.bow_m2,

View File

@ -1674,6 +1674,29 @@ impl FigureMgr {
skeleton_attr, skeleton_attr,
) )
}, },
CharacterState::StaticAura(s) => {
let stage_time = s.timer.as_secs_f32();
let stage_progress = match s.stage_section {
StageSection::Buildup => {
stage_time / s.static_data.buildup_duration.as_secs_f32()
},
StageSection::Action => {
stage_time / s.static_data.cast_duration.as_secs_f32()
},
StageSection::Recover => {
stage_time / s.static_data.recover_duration.as_secs_f32()
},
_ => 0.0,
};
anim::character::ShockwaveAnimation::update_skeleton(
&target_base,
(ability_id, time, rel_vel.magnitude(), Some(s.stage_section)),
stage_progress,
&mut state_animation_rate,
skeleton_attr,
)
},
CharacterState::LeapMelee(s) => { CharacterState::LeapMelee(s) => {
let stage_time = s.timer.as_secs_f32(); let stage_time = s.timer.as_secs_f32();
let stage_progress = match s.stage_section { let stage_progress = match s.stage_section {