mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Slightly functional sprite summon.
This commit is contained in:
parent
15a2fbc555
commit
ed503236d6
@ -1,15 +1,7 @@
|
||||
BasicRanged(
|
||||
energy_cost: 0,
|
||||
buildup_duration: 0.5,
|
||||
recover_duration: 0.8,
|
||||
projectile: ExplodingPumpkin(
|
||||
damage: 500.0,
|
||||
knockback: 25.0,
|
||||
radius: 10.0,
|
||||
),
|
||||
projectile_body: Object(Pumpkin),
|
||||
projectile_light: None,
|
||||
projectile_speed: 30.0,
|
||||
num_projectiles: 1,
|
||||
projectile_spread: 0.0,
|
||||
)
|
||||
SpriteSummon(
|
||||
buildup_duration: 0.3,
|
||||
cast_duration: 1.0,
|
||||
recover_duration: 0.2,
|
||||
sprite: EnsnaringVines,
|
||||
summon_distance: (0, 25),
|
||||
)
|
BIN
assets/voxygen/voxel/sprite/misc/ensnaring_vines.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/misc/ensnaring_vines.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -3247,4 +3247,14 @@ CookingPot: Some((
|
||||
],
|
||||
wind_sway: 0.0,
|
||||
)),
|
||||
EnsnaringVines: Some((
|
||||
variations: [
|
||||
(
|
||||
model: "voxygen.voxel.sprite.misc.ensnaring_vines",
|
||||
offset: (-5.0, -6.5, 0.0),
|
||||
lod_axes: (0.0, 0.0, 0.0),
|
||||
),
|
||||
],
|
||||
wind_sway: 0.0,
|
||||
)),
|
||||
)
|
||||
|
@ -10,6 +10,7 @@ use crate::{
|
||||
utils::{AbilityInfo, StageSection},
|
||||
*,
|
||||
},
|
||||
terrain::SpriteKind,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
@ -294,6 +295,13 @@ pub enum CharacterAbility {
|
||||
buff_duration: Option<f32>,
|
||||
energy_cost: f32,
|
||||
},
|
||||
SpriteSummon {
|
||||
buildup_duration: f32,
|
||||
cast_duration: f32,
|
||||
recover_duration: f32,
|
||||
sprite: SpriteKind,
|
||||
summon_distance: (f32, f32),
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for CharacterAbility {
|
||||
@ -365,7 +373,8 @@ impl CharacterAbility {
|
||||
| CharacterAbility::Boost { .. }
|
||||
| CharacterAbility::BasicBeam { .. }
|
||||
| CharacterAbility::Blink { .. }
|
||||
| CharacterAbility::BasicSummon { .. } => true,
|
||||
| CharacterAbility::BasicSummon { .. }
|
||||
| CharacterAbility::SpriteSummon { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,6 +636,17 @@ impl CharacterAbility {
|
||||
*cast_duration /= speed;
|
||||
*recover_duration /= speed;
|
||||
},
|
||||
SpriteSummon {
|
||||
ref mut buildup_duration,
|
||||
ref mut cast_duration,
|
||||
ref mut recover_duration,
|
||||
..
|
||||
} => {
|
||||
// TODO: Figure out how/if power should affect this
|
||||
*buildup_duration /= speed;
|
||||
*cast_duration /= speed;
|
||||
*recover_duration /= speed;
|
||||
},
|
||||
}
|
||||
self
|
||||
}
|
||||
@ -655,7 +675,11 @@ impl CharacterAbility {
|
||||
0
|
||||
}
|
||||
},
|
||||
Boost { .. } | ComboMelee { .. } | Blink { .. } | BasicSummon { .. } => 0,
|
||||
Boost { .. }
|
||||
| ComboMelee { .. }
|
||||
| Blink { .. }
|
||||
| BasicSummon { .. }
|
||||
| SpriteSummon { .. } => 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1781,6 +1805,25 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
}),
|
||||
CharacterAbility::SpriteSummon {
|
||||
buildup_duration,
|
||||
cast_duration,
|
||||
recover_duration,
|
||||
sprite,
|
||||
summon_distance,
|
||||
} => CharacterState::SpriteSummon(sprite_summon::Data {
|
||||
static_data: sprite_summon::StaticData {
|
||||
buildup_duration: Duration::from_secs_f32(*buildup_duration),
|
||||
cast_duration: Duration::from_secs_f32(*cast_duration),
|
||||
recover_duration: Duration::from_secs_f32(*recover_duration),
|
||||
sprite: *sprite,
|
||||
summon_distance: *summon_distance,
|
||||
ability_info,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
achieved_radius: 0,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -103,6 +103,8 @@ pub enum CharacterState {
|
||||
BasicSummon(basic_summon::Data),
|
||||
/// Inserts a buff on the caster
|
||||
SelfBuff(self_buff::Data),
|
||||
/// Creates sprites around the caster
|
||||
SpriteSummon(sprite_summon::Data),
|
||||
}
|
||||
|
||||
impl CharacterState {
|
||||
@ -125,6 +127,9 @@ impl CharacterState {
|
||||
| CharacterState::BasicAura(_)
|
||||
| CharacterState::HealingBeam(_)
|
||||
| CharacterState::SelfBuff(_)
|
||||
| CharacterState::Blink(_)
|
||||
| CharacterState::BasicSummon(_)
|
||||
| CharacterState::SpriteSummon(_)
|
||||
)
|
||||
}
|
||||
|
||||
@ -149,6 +154,9 @@ impl CharacterState {
|
||||
| CharacterState::BasicAura(_)
|
||||
| CharacterState::HealingBeam(_)
|
||||
| CharacterState::SelfBuff(_)
|
||||
| CharacterState::Blink(_)
|
||||
| CharacterState::BasicSummon(_)
|
||||
| CharacterState::SpriteSummon(_)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ use crate::{
|
||||
},
|
||||
outcome::Outcome,
|
||||
rtsim::RtSimEntity,
|
||||
terrain::SpriteKind,
|
||||
trade::{TradeAction, TradeId},
|
||||
uid::Uid,
|
||||
util::Dir,
|
||||
@ -181,6 +182,10 @@ pub enum ServerEvent {
|
||||
Sound {
|
||||
sound: Sound,
|
||||
},
|
||||
CreateSprite {
|
||||
pos: Vec3<i32>,
|
||||
sprite: SpriteKind,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct EventBus<E> {
|
||||
|
@ -10,12 +10,22 @@ pub struct Spiral2d {
|
||||
|
||||
impl Spiral2d {
|
||||
#[allow(clippy::new_without_default)] // TODO: Pending review in #587
|
||||
/// Creates a new spiral starting at the origin
|
||||
pub fn new() -> Self { Self { layer: 0, i: 0 } }
|
||||
|
||||
/// Creates an iterator over points in a spiral starting at the origin and
|
||||
/// going out to some radius
|
||||
pub fn radius(self, radius: i32) -> impl Iterator<Item = Vec2<i32>> {
|
||||
self.take((radius * 2 + 1).pow(2) as usize)
|
||||
.filter(move |pos| pos.magnitude_squared() < (radius + 1).pow(2))
|
||||
}
|
||||
|
||||
/// Creates an iterator over points in the edge of a circle of some radius
|
||||
pub fn edge_radius(self, radius: i32) -> impl Iterator<Item = Vec2<i32>> {
|
||||
self.take((radius * 2 + 1).pow(2) as usize)
|
||||
.filter(move |pos| pos.magnitude_squared() < (radius + 1).pow(2))
|
||||
.filter(move |pos| pos.magnitude_squared() >= radius.pow(2))
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Spiral2d {
|
||||
|
@ -26,6 +26,7 @@ pub mod shockwave;
|
||||
pub mod sit;
|
||||
pub mod sneak;
|
||||
pub mod spin_melee;
|
||||
pub mod sprite_summon;
|
||||
pub mod stunned;
|
||||
pub mod talk;
|
||||
pub mod utils;
|
||||
|
156
common/src/states/sprite_summon.rs
Normal file
156
common/src/states/sprite_summon.rs
Normal file
@ -0,0 +1,156 @@
|
||||
use crate::{
|
||||
comp::{CharacterState, StateUpdate},
|
||||
event::ServerEvent,
|
||||
spiral::Spiral2d,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
},
|
||||
terrain::{Block, SpriteKind},
|
||||
vol::ReadVol,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use vek::*;
|
||||
|
||||
/// Separated out to condense update portions of character state
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct StaticData {
|
||||
/// How long the state builds up for
|
||||
pub buildup_duration: Duration,
|
||||
/// How long the state is casting for
|
||||
pub cast_duration: Duration,
|
||||
/// How long the state recovers for
|
||||
pub recover_duration: Duration,
|
||||
/// What kind of sprite is created by this state
|
||||
pub sprite: SpriteKind,
|
||||
/// Range that sprites are created relative to the summonner
|
||||
pub summon_distance: (f32, f32),
|
||||
/// Miscellaneous information about the ability
|
||||
pub ability_info: AbilityInfo,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Data {
|
||||
/// Struct containing data that does not change over the course of the
|
||||
/// character state
|
||||
pub static_data: StaticData,
|
||||
/// Timer for each stage
|
||||
pub timer: Duration,
|
||||
/// What section the character stage is in
|
||||
pub stage_section: StageSection,
|
||||
/// What radius of sprites have already been summoned
|
||||
pub achieved_radius: i32,
|
||||
}
|
||||
|
||||
impl CharacterBehavior for Data {
|
||||
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
|
||||
match self.stage_section {
|
||||
StageSection::Buildup => {
|
||||
if self.timer < self.static_data.buildup_duration {
|
||||
// Build up
|
||||
update.character = CharacterState::SpriteSummon(Data {
|
||||
timer: tick_attack_or_default(data, self.timer, None),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Transitions to recover section of stage
|
||||
update.character = CharacterState::SpriteSummon(Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Cast,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Cast => {
|
||||
if self.timer < self.static_data.cast_duration {
|
||||
let timer_frac =
|
||||
self.timer.as_secs_f32() / self.static_data.cast_duration.as_secs_f32();
|
||||
|
||||
// Determines distance from summoner sprites should be created. Goes outward
|
||||
// with time.
|
||||
let summon_distance = timer_frac
|
||||
* (self.static_data.summon_distance.1 - self.static_data.summon_distance.0)
|
||||
+ self.static_data.summon_distance.0;
|
||||
let summon_distance = summon_distance.round() as i32;
|
||||
|
||||
// Only summons sprites if summon distance is greater than achieved radius
|
||||
if summon_distance > self.achieved_radius {
|
||||
// Creates a spiral iterator for the newly achieved radius
|
||||
let spiral = Spiral2d::new().edge_radius(summon_distance);
|
||||
for point in spiral {
|
||||
// The coordinates of where the sprite is created
|
||||
let sprite_pos = Vec3::new(
|
||||
data.pos.0.x.floor() as i32 + point.x,
|
||||
data.pos.0.y.floor() as i32 + point.y,
|
||||
data.pos.0.z.floor() as i32,
|
||||
);
|
||||
|
||||
// Check for collision in z up to 25 blocks up or down
|
||||
let obstacle_z = data
|
||||
.terrain
|
||||
.ray(
|
||||
sprite_pos.map(|x| x as f32 + 0.5) + Vec3::unit_z() * 25.0,
|
||||
sprite_pos.map(|x| x as f32 + 0.5) - Vec3::unit_z() * 25.0,
|
||||
)
|
||||
.until(|b| {
|
||||
Block::is_solid(b)
|
||||
&& !matches!(
|
||||
b.get_sprite(),
|
||||
Some(SpriteKind::EnsnaringVines)
|
||||
)
|
||||
})
|
||||
.cast()
|
||||
.0;
|
||||
|
||||
// z height relative to caster
|
||||
let z = sprite_pos.z + (25.5 - obstacle_z).ceil() as i32;
|
||||
|
||||
// Location sprite will be created
|
||||
let sprite_pos = Vec3::new(sprite_pos.x as i32, sprite_pos.y as i32, z);
|
||||
|
||||
// Send server event to create sprite
|
||||
update.server_events.push_front(ServerEvent::CreateSprite {
|
||||
pos: sprite_pos,
|
||||
sprite: self.static_data.sprite,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
update.character = CharacterState::SpriteSummon(Data {
|
||||
timer: tick_attack_or_default(data, self.timer, None),
|
||||
achieved_radius: summon_distance,
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Transitions to recover section of stage
|
||||
update.character = CharacterState::SpriteSummon(Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Recover,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Recover => {
|
||||
if self.timer < self.static_data.recover_duration {
|
||||
// Recovery
|
||||
update.character = CharacterState::SpriteSummon(Data {
|
||||
timer: tick_attack_or_default(data, self.timer, None),
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Done
|
||||
update.character = CharacterState::Wielding;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
update.character = CharacterState::Wielding;
|
||||
},
|
||||
}
|
||||
|
||||
update
|
||||
}
|
||||
}
|
@ -173,6 +173,7 @@ make_case_elim!(
|
||||
CrystalLow = 0x92,
|
||||
CeilingMushroom = 0x93,
|
||||
Orb = 0x94,
|
||||
EnsnaringVines = 0x95,
|
||||
}
|
||||
);
|
||||
|
||||
@ -256,6 +257,7 @@ impl SpriteKind {
|
||||
| SpriteKind::Tin
|
||||
| SpriteKind::Silver
|
||||
| SpriteKind::Gold => 0.6,
|
||||
SpriteKind::EnsnaringVines => 0.1,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
@ -331,6 +331,7 @@ impl<'a> System<'a> for Sys {
|
||||
CharacterState::Blink(data) => data.handle_event(&j, action),
|
||||
CharacterState::BasicSummon(data) => data.handle_event(&j, action),
|
||||
CharacterState::SelfBuff(data) => data.handle_event(&j, action),
|
||||
CharacterState::SpriteSummon(data) => data.handle_event(&j, action),
|
||||
};
|
||||
local_emitter.append(&mut state_update.local_events);
|
||||
server_emitter.append(&mut state_update.server_events);
|
||||
@ -386,6 +387,7 @@ impl<'a> System<'a> for Sys {
|
||||
CharacterState::Blink(data) => data.behavior(&j),
|
||||
CharacterState::BasicSummon(data) => data.behavior(&j),
|
||||
CharacterState::SelfBuff(data) => data.behavior(&j),
|
||||
CharacterState::SpriteSummon(data) => data.behavior(&j),
|
||||
};
|
||||
|
||||
local_emitter.append(&mut state_update.local_events);
|
||||
|
@ -279,7 +279,8 @@ impl<'a> System<'a> for Sys {
|
||||
| CharacterState::HealingBeam { .. }
|
||||
| CharacterState::Blink { .. }
|
||||
| CharacterState::BasicSummon { .. }
|
||||
| CharacterState::SelfBuff { .. } => {
|
||||
| CharacterState::SelfBuff { .. }
|
||||
| CharacterState::SpriteSummon { .. } => {
|
||||
if energy.get_unchecked().regen_rate != 0.0 {
|
||||
energy.get_mut_unchecked().regen_rate = 0.0
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ use common::{
|
||||
},
|
||||
consts::{MAX_MOUNT_RANGE, SOUND_TRAVEL_DIST_PER_VOLUME},
|
||||
outcome::Outcome,
|
||||
terrain::{Block, SpriteKind},
|
||||
uid::Uid,
|
||||
vol::ReadVol,
|
||||
};
|
||||
@ -418,3 +419,17 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) {
|
||||
ecs.write_resource::<Vec<Outcome>>().push(outcome);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_create_sprite(server: &mut Server, pos: Vec3<i32>, sprite: SpriteKind) {
|
||||
let state = server.state_mut();
|
||||
if state.can_set_block(pos) {
|
||||
let block = state.terrain().get(pos).ok().copied();
|
||||
if block.map_or(false, |b| (*b).is_air()) {
|
||||
let new_block = state
|
||||
.get_block(pos)
|
||||
.unwrap_or_else(|| Block::air(SpriteKind::Empty))
|
||||
.with_sprite(sprite);
|
||||
server.state.set_block(pos, new_block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ use entity_manipulation::{
|
||||
use group_manip::handle_group;
|
||||
use information::handle_site_info;
|
||||
use interaction::{
|
||||
handle_lantern, handle_mine_block, handle_mount, handle_npc_interaction, handle_possess,
|
||||
handle_sound, handle_unmount,
|
||||
handle_create_sprite, handle_lantern, handle_mine_block, handle_mount, handle_npc_interaction,
|
||||
handle_possess, handle_sound, handle_unmount,
|
||||
};
|
||||
use inventory_manip::handle_inventory;
|
||||
use invite::{handle_invite, handle_invite_response};
|
||||
@ -221,6 +221,9 @@ impl Server {
|
||||
self.state.create_safezone(range, pos).build();
|
||||
},
|
||||
ServerEvent::Sound { sound } => handle_sound(self, &sound),
|
||||
ServerEvent::CreateSprite { pos, sprite } => {
|
||||
handle_create_sprite(self, pos, sprite)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user