mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Rampart
This commit is contained in:
parent
e1ce09e0a1
commit
7daa9a29eb
@ -235,7 +235,7 @@
|
||||
Simple(Hammer(PileDriver), "common.abilities.hammer.pile_driver"),
|
||||
Simple(Hammer(LungPummel), "common.abilities.hammer.lung_pummel"),
|
||||
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(Earthshaker), "common.abilities.hammer.earthshaker"),
|
||||
// Simple(Hammer(Judgement), "common.abilities.hammer.judgement"),
|
||||
|
23
assets/common/abilities/hammer/rampart.ron
Normal file
23
assets/common/abilities/hammer/rampart.ron
Normal 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
BIN
assets/voxygen/element/skills/hammer/rampart.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -434,3 +434,6 @@ common-abilities-hammer-upheaval = Upheaval
|
||||
common-abilities-hammer-dual_upheaval = Upheaval
|
||||
.desc =
|
||||
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.
|
||||
|
@ -2953,6 +2953,21 @@
|
||||
],
|
||||
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, ()): (
|
||||
variations: [
|
||||
|
@ -735,7 +735,8 @@ impl From<&CharacterState> for CharacterAbilityType {
|
||||
| CharacterState::SpriteInteract(_)
|
||||
| CharacterState::Skate(_)
|
||||
| CharacterState::Transform(_)
|
||||
| CharacterState::Wallrun(_) => Self::Other,
|
||||
| CharacterState::Wallrun(_)
|
||||
| CharacterState::StaticAura(_) => Self::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -974,6 +975,19 @@ pub enum CharacterAbility {
|
||||
#[serde(default)]
|
||||
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 {
|
||||
buildup_duration: f32,
|
||||
recover_duration: f32,
|
||||
@ -1146,7 +1160,12 @@ impl CharacterAbility {
|
||||
};
|
||||
from_meta
|
||||
&& match self {
|
||||
CharacterAbility::Roll { energy_cost, .. } => {
|
||||
CharacterAbility::Roll { energy_cost, .. }
|
||||
| CharacterAbility::StaticAura {
|
||||
energy_cost,
|
||||
sprite_info: Some(_),
|
||||
..
|
||||
} => {
|
||||
data.physics.on_ground.is_some()
|
||||
&& update.energy.try_change_by(-*energy_cost).is_ok()
|
||||
},
|
||||
@ -1161,6 +1180,11 @@ impl CharacterAbility {
|
||||
| CharacterAbility::ComboMelee2 {
|
||||
energy_cost_per_strike: energy_cost,
|
||||
..
|
||||
}
|
||||
| CharacterAbility::StaticAura {
|
||||
energy_cost,
|
||||
sprite_info: None,
|
||||
..
|
||||
} => update.energy.try_change_by(-*energy_cost).is_ok(),
|
||||
// Consumes energy within state, so value only checked before entering state
|
||||
CharacterAbility::RepeaterRanged { energy_cost, .. } => {
|
||||
@ -1610,6 +1634,39 @@ impl CharacterAbility {
|
||||
*range *= stats.range;
|
||||
*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 {
|
||||
ref mut buildup_duration,
|
||||
ref mut recover_duration,
|
||||
@ -1793,7 +1850,8 @@ impl CharacterAbility {
|
||||
}
|
||||
| DiveMelee { energy_cost, .. }
|
||||
| RiposteMelee { energy_cost, .. }
|
||||
| RapidMelee { energy_cost, .. } => *energy_cost,
|
||||
| RapidMelee { energy_cost, .. }
|
||||
| StaticAura { energy_cost, .. } => *energy_cost,
|
||||
BasicBeam { energy_drain, .. } => {
|
||||
if *energy_drain > f32::EPSILON {
|
||||
1.0
|
||||
@ -1856,7 +1914,8 @@ impl CharacterAbility {
|
||||
| Music { .. }
|
||||
| BasicSummon { .. }
|
||||
| SpriteSummon { .. }
|
||||
| Transform { .. } => 0,
|
||||
| Transform { .. }
|
||||
| StaticAura { .. } => 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1889,7 +1948,8 @@ impl CharacterAbility {
|
||||
| DiveMelee { meta, .. }
|
||||
| RiposteMelee { 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(),
|
||||
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 {
|
||||
buildup_duration,
|
||||
recover_duration,
|
||||
|
@ -52,6 +52,7 @@ event_emitters! {
|
||||
knockback: event::KnockbackEvent,
|
||||
sprite_light: event::ToggleSpriteLightEvent,
|
||||
transform: event::TransformEvent,
|
||||
create_aura_entity: event::CreateAuraEntityEvent,
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,6 +143,9 @@ pub enum CharacterState {
|
||||
BasicBeam(basic_beam::Data),
|
||||
/// Creates an aura that persists as long as you are actively casting
|
||||
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
|
||||
Blink(blink::Data),
|
||||
/// Summons creatures that fight for the caster
|
||||
@ -211,6 +215,7 @@ impl CharacterState {
|
||||
| CharacterState::DiveMelee(_)
|
||||
| CharacterState::RiposteMelee(_)
|
||||
| CharacterState::RapidMelee(_)
|
||||
| CharacterState::StaticAura(_)
|
||||
)
|
||||
}
|
||||
|
||||
@ -276,6 +281,7 @@ impl CharacterState {
|
||||
| CharacterState::DiveMelee(_)
|
||||
| CharacterState::RiposteMelee(_)
|
||||
| CharacterState::RapidMelee(_)
|
||||
| CharacterState::StaticAura(_)
|
||||
)
|
||||
}
|
||||
|
||||
@ -532,6 +538,7 @@ impl CharacterState {
|
||||
CharacterState::RiposteMelee(data) => data.behavior(j, output_events),
|
||||
CharacterState::RapidMelee(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::RapidMelee(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::RapidMelee(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::RapidMelee(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),
|
||||
..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::RapidMelee(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::RapidMelee(_) => Some(AttackSource::Melee),
|
||||
CharacterState::Transform(_) => None,
|
||||
CharacterState::StaticAura(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -433,6 +433,12 @@ pub struct RequestPluginsEvent {
|
||||
pub plugins: Vec<PluginHash>,
|
||||
}
|
||||
|
||||
pub struct CreateAuraEntityEvent {
|
||||
pub auras: comp::Auras,
|
||||
pub pos: Pos,
|
||||
pub creator_uid: Uid,
|
||||
}
|
||||
|
||||
pub struct EventBus<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::<TransformEvent>::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
|
||||
|
@ -33,6 +33,7 @@ pub mod sit;
|
||||
pub mod skate;
|
||||
pub mod sprite_interact;
|
||||
pub mod sprite_summon;
|
||||
pub mod static_aura;
|
||||
pub mod stunned;
|
||||
pub mod talk;
|
||||
pub mod transform;
|
||||
|
@ -117,99 +117,29 @@ impl CharacterBehavior for Data {
|
||||
let timer_frac =
|
||||
self.timer.as_secs_f32() / self.static_data.cast_duration.as_secs_f32();
|
||||
|
||||
// Determines distance from summoner sprites should be created. Goes outward
|
||||
// with time.
|
||||
let summon_distance = timer_frac
|
||||
* (self.static_data.summon_distance.1 - self.static_data.summon_distance.0)
|
||||
+ self.static_data.summon_distance.0;
|
||||
let summon_distance = summon_distance.round() as i32;
|
||||
|
||||
// Only summons sprites if summon distance is greater than achieved radius
|
||||
for radius in self.achieved_radius..=summon_distance {
|
||||
// 1 added to make range correct, too lazy to add 1 to both variables above
|
||||
let radius = radius + 1;
|
||||
// Creates a spiral iterator for the newly achieved radius
|
||||
let spiral = Spiral2d::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()
|
||||
<= (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)
|
||||
},
|
||||
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,
|
||||
let achieved_radius = create_sprites(
|
||||
data,
|
||||
output_events,
|
||||
self.static_data.sprite,
|
||||
timer_frac,
|
||||
self.static_data.summon_distance,
|
||||
self.achieved_radius,
|
||||
self.static_data.angle,
|
||||
self.static_data.sparseness,
|
||||
anchor_pos,
|
||||
matches!(self.static_data.anchor, SpriteSummonAnchor::Target),
|
||||
self.static_data.del_timeout,
|
||||
);
|
||||
|
||||
// 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 {
|
||||
timer: tick_attack_or_default(data, self.timer, None),
|
||||
achieved_radius: summon_distance,
|
||||
achieved_radius,
|
||||
..*self
|
||||
});
|
||||
// Send local event used for frontend shenanigans
|
||||
@ -270,3 +200,94 @@ impl CharacterBehavior for Data {
|
||||
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
|
||||
}
|
||||
|
169
common/src/states/static_aura.rs
Normal file
169
common/src/states/static_aura.rs
Normal 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,
|
||||
}
|
@ -70,6 +70,7 @@ sprites! {
|
||||
Bomb = 0x02,
|
||||
FireBlock = 0x03, // FireBlock for Burning Buff
|
||||
HotSurface = 0x04,
|
||||
Stones2 = 0x05, // Same as `Stones` but not collectible
|
||||
},
|
||||
// Furniture. In the future, we might add an attribute to customise material
|
||||
// TODO: Remove sizes and variants, represent with attributes
|
||||
|
@ -167,7 +167,8 @@ impl<'a> System<'a> for Sys {
|
||||
| CharacterState::FinisherMelee(_)
|
||||
| CharacterState::DiveMelee(_)
|
||||
| CharacterState::RiposteMelee(_)
|
||||
| CharacterState::RapidMelee(_) => {
|
||||
| CharacterState::RapidMelee(_)
|
||||
| CharacterState::StaticAura(_) => {
|
||||
if energy.needs_regen_rate_reset() {
|
||||
energy.reset_regen_rate();
|
||||
}
|
||||
|
@ -12,9 +12,9 @@ use common::{
|
||||
WaypointArea,
|
||||
},
|
||||
event::{
|
||||
CreateItemDropEvent, CreateNpcEvent, CreateObjectEvent, CreateShipEvent,
|
||||
CreateSpecialEntityEvent, EventBus, InitializeCharacterEvent, InitializeSpectatorEvent,
|
||||
ShockwaveEvent, ShootEvent, UpdateCharacterDataEvent,
|
||||
CreateAuraEntityEvent, CreateItemDropEvent, CreateNpcEvent, CreateObjectEvent,
|
||||
CreateShipEvent, CreateSpecialEntityEvent, EventBus, InitializeCharacterEvent,
|
||||
InitializeSpectatorEvent, ShockwaveEvent, ShootEvent, UpdateCharacterDataEvent,
|
||||
},
|
||||
generation::SpecialEntity,
|
||||
mounting::{Mounting, Volume, VolumeMounting, VolumePos},
|
||||
@ -489,3 +489,16 @@ pub fn handle_create_object(
|
||||
.maybe_with(stats)
|
||||
.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();
|
||||
}
|
||||
|
@ -13,9 +13,10 @@ use specs::{
|
||||
|
||||
use self::{
|
||||
entity_creation::{
|
||||
handle_create_item_drop, handle_create_npc, handle_create_object, handle_create_ship,
|
||||
handle_create_special_entity, handle_initialize_character, handle_initialize_spectator,
|
||||
handle_loaded_character_data, handle_shockwave, handle_shoot,
|
||||
handle_create_aura_entity, handle_create_item_drop, handle_create_npc,
|
||||
handle_create_object, handle_create_ship, handle_create_special_entity,
|
||||
handle_initialize_character, handle_initialize_spectator, handle_loaded_character_data,
|
||||
handle_shockwave, handle_shoot,
|
||||
},
|
||||
entity_manipulation::{handle_delete, handle_transform},
|
||||
interaction::handle_tame_pet,
|
||||
@ -149,6 +150,7 @@ impl Server {
|
||||
self.handle_serial_events(handle_create_special_entity);
|
||||
self.handle_serial_events(handle_create_item_drop);
|
||||
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_character_delete);
|
||||
|
@ -3,6 +3,7 @@ use super::{
|
||||
hammer_start, twist_back, twist_forward, CharacterSkeleton, SkeletonAttr,
|
||||
};
|
||||
use common::states::utils::StageSection;
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub struct Input {
|
||||
pub attack: bool,
|
||||
@ -136,6 +137,28 @@ impl Animation for ShockwaveAnimation {
|
||||
next.control.position += Vec3::new(-16.0, 0.0, 0.0) * 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);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
|
@ -330,6 +330,7 @@ image_ids! {
|
||||
hammer_helm_crusher: "voxygen.element.skills.hammer.helm_crusher",
|
||||
hammer_iron_tempest: "voxygen.element.skills.hammer.iron_tempest",
|
||||
hammer_upheaval: "voxygen.element.skills.hammer.upheaval",
|
||||
hammer_rampart: "voxygen.element.skills.hammer.rampart",
|
||||
// Skilltree Icons
|
||||
health_plus_skill: "voxygen.element.skills.skilltree.health_plus",
|
||||
energy_plus_skill: "voxygen.element.skills.skilltree.energy_plus",
|
||||
|
@ -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.upheaval" => imgs.hammer_upheaval,
|
||||
"common.abilities.hammer.dual_upheaval" => imgs.hammer_upheaval,
|
||||
"common.abilities.hammer.rampart" => imgs.hammer_rampart,
|
||||
// Bow
|
||||
"common.abilities.bow.charged" => imgs.bow_m1,
|
||||
"common.abilities.bow.repeater" => imgs.bow_m2,
|
||||
|
@ -1674,6 +1674,29 @@ impl FigureMgr {
|
||||
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) => {
|
||||
let stage_time = s.timer.as_secs_f32();
|
||||
let stage_progress = match s.stage_section {
|
||||
|
Loading…
Reference in New Issue
Block a user