mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
autodelete of summoned sprites
This commit is contained in:
parent
b127cff26c
commit
650ef9a5e2
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -6177,6 +6177,15 @@ dependencies = [
|
||||
"num_threads",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "timer-queue"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13756c29c43d836ff576221498bf4916b0d2f7ea24cd47d3531b70dc4341f038"
|
||||
dependencies = [
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
@ -6826,6 +6835,7 @@ dependencies = [
|
||||
"serde",
|
||||
"specs",
|
||||
"tar",
|
||||
"timer-queue",
|
||||
"toml",
|
||||
"tracing",
|
||||
"vek 0.15.8",
|
||||
|
@ -3,6 +3,7 @@ SpriteSummon(
|
||||
cast_duration: 0.4,
|
||||
recover_duration: 0.3,
|
||||
sprite: EnsnaringWeb,
|
||||
del_timeout: None,
|
||||
summon_distance: (0, 10),
|
||||
sparseness: 0.76,
|
||||
)
|
@ -3,6 +3,7 @@ SpriteSummon(
|
||||
cast_duration: 0.4,
|
||||
recover_duration: 0.3,
|
||||
sprite: EnsnaringWeb,
|
||||
del_timeout: None,
|
||||
summon_distance: (0, 9),
|
||||
sparseness: 0.8,
|
||||
)
|
@ -3,6 +3,7 @@ SpriteSummon(
|
||||
cast_duration: 0.1,
|
||||
recover_duration: 0.9,
|
||||
sprite: SeaUrchin,
|
||||
del_timeout: Some((4, 5)),
|
||||
summon_distance: (5, 3.1),
|
||||
sparseness: 0.2,
|
||||
)
|
@ -3,6 +3,7 @@ SpriteSummon(
|
||||
cast_duration: 0.4,
|
||||
recover_duration: 0.3,
|
||||
sprite: EnsnaringVines,
|
||||
del_timeout: None,
|
||||
summon_distance: (0, 25),
|
||||
sparseness: 0.67,
|
||||
)
|
@ -700,6 +700,7 @@ pub enum CharacterAbility {
|
||||
cast_duration: f32,
|
||||
recover_duration: f32,
|
||||
sprite: SpriteKind,
|
||||
del_timeout: Option<(f32, f32)>,
|
||||
summon_distance: (f32, f32),
|
||||
sparseness: f64,
|
||||
#[serde(default)]
|
||||
@ -1275,6 +1276,7 @@ impl CharacterAbility {
|
||||
ref mut cast_duration,
|
||||
ref mut recover_duration,
|
||||
sprite: _,
|
||||
del_timeout: _,
|
||||
summon_distance: (ref mut inner_dist, ref mut outer_dist),
|
||||
sparseness: _,
|
||||
meta: _,
|
||||
@ -2508,6 +2510,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
|
||||
cast_duration,
|
||||
recover_duration,
|
||||
sprite,
|
||||
del_timeout,
|
||||
summon_distance,
|
||||
sparseness,
|
||||
meta: _,
|
||||
@ -2517,6 +2520,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
|
||||
cast_duration: Duration::from_secs_f32(*cast_duration),
|
||||
recover_duration: Duration::from_secs_f32(*recover_duration),
|
||||
sprite: *sprite,
|
||||
del_timeout: *del_timeout,
|
||||
summon_distance: *summon_distance,
|
||||
sparseness: *sparseness,
|
||||
ability_info,
|
||||
|
@ -208,6 +208,7 @@ pub enum ServerEvent {
|
||||
CreateSprite {
|
||||
pos: Vec3<i32>,
|
||||
sprite: SpriteKind,
|
||||
del_timeout: Option<(f32, f32)>,
|
||||
},
|
||||
TamePet {
|
||||
pet_entity: EcsEntity,
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{combat::DamageContributor, comp, uid::Uid, DamageSource};
|
||||
use crate::{combat::DamageContributor, comp, uid::Uid, DamageSource, terrain::SpriteKind};
|
||||
use comp::{beam, item::Reagent, poise::PoiseState, skillset::SkillGroupKind, UtteranceKind};
|
||||
use hashbrown::HashSet;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -97,6 +97,10 @@ pub enum Outcome {
|
||||
pos: Vec3<f32>,
|
||||
wielded: bool,
|
||||
},
|
||||
SpriteDelete {
|
||||
pos: Vec3<f32>,
|
||||
sprite: SpriteKind,
|
||||
},
|
||||
}
|
||||
|
||||
impl Outcome {
|
||||
@ -115,6 +119,7 @@ impl Outcome {
|
||||
| Outcome::PoiseChange { pos, .. }
|
||||
| Outcome::GroundSlam { pos }
|
||||
| Outcome::Utterance { pos, .. }
|
||||
| Outcome::SpriteDelete { pos, .. }
|
||||
| Outcome::Glider { pos, .. } => Some(*pos),
|
||||
Outcome::BreakBlock { pos, .. } => Some(pos.map(|e| e as f32 + 0.5)),
|
||||
Outcome::ExpChange { .. }
|
||||
|
@ -25,6 +25,8 @@ pub struct StaticData {
|
||||
pub recover_duration: Duration,
|
||||
/// What kind of sprite is created by this state
|
||||
pub sprite: SpriteKind,
|
||||
/// Duration until sprite-delete begins (in sec), randomization-range of sprite-delete-time (in sec)
|
||||
pub del_timeout: Option<(f32, f32)>,
|
||||
/// Range that sprites are created relative to the summonner
|
||||
pub summon_distance: (f32, f32),
|
||||
/// Chance that sprite is not created on a particular square
|
||||
@ -127,6 +129,7 @@ impl CharacterBehavior for Data {
|
||||
output_events.emit_server(ServerEvent::CreateSprite {
|
||||
pos: Vec3::new(sprite_pos.x, sprite_pos.y, z + i),
|
||||
sprite: self.static_data.sprite,
|
||||
del_timeout: self.static_data.del_timeout,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ tar = { version = "0.4.37", optional = true }
|
||||
wasmer = { version = "2.0.0", optional = true, default-features = false, features = ["wat", "default-cranelift", "default-universal"] }
|
||||
bincode = { version = "1.3.1", optional = true }
|
||||
plugin-api = { package = "veloren-plugin-api", path = "../../plugin/api", optional = true }
|
||||
timer-queue = "0.1.0"
|
||||
|
||||
# Tweak running code
|
||||
#inline_tweak = { version = "1.0.8", features = ["release_tweak"] }
|
||||
|
@ -36,18 +36,20 @@ use specs::{
|
||||
Component, DispatcherBuilder, Entity as EcsEntity, WorldExt,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use timer_queue::TimerQueue;
|
||||
use vek::*;
|
||||
|
||||
/// How much faster should an in-game day be compared to a real day?
|
||||
// TODO: Don't hard-code this.
|
||||
const DAY_CYCLE_FACTOR: f64 = 24.0 * 2.0;
|
||||
|
||||
/// At what point should we stop speeding up physics to compensate for lag? If
|
||||
/// we speed physics up too fast, we'd skip important physics events like
|
||||
/// collisions. This constant determines the upper limit. If delta time exceeds
|
||||
/// this value, the game's physics will begin to produce time lag. Ideally, we'd
|
||||
/// avoid such a situation.
|
||||
const MAX_DELTA_TIME: f32 = 1.0;
|
||||
/// convert seconds to milliseconds to use in TimerQueue
|
||||
const SECONDS_TO_MILLISECONDS: f64 = 1000.0;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BlockChange {
|
||||
@ -69,6 +71,29 @@ impl BlockChange {
|
||||
pub fn clear(&mut self) { self.blocks.clear(); }
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ScheduledBlockChange {
|
||||
changes: TimerQueue<HashMap<Vec3<i32>, Block>>,
|
||||
outcomes: TimerQueue<HashMap<Vec3<i32>, Block>>,
|
||||
}
|
||||
impl ScheduledBlockChange {
|
||||
pub fn set(&mut self, pos: Vec3<i32>, block: Block, replace_time: f64) {
|
||||
let timer = self.changes.insert(
|
||||
(replace_time * SECONDS_TO_MILLISECONDS) as u64,
|
||||
HashMap::new(),
|
||||
);
|
||||
self.changes.get_mut(timer).insert(pos, block);
|
||||
}
|
||||
|
||||
pub fn outcome_set(&mut self, pos: Vec3<i32>, block: Block, replace_time: f64) {
|
||||
let outcome_timer = self.outcomes.insert(
|
||||
(replace_time * SECONDS_TO_MILLISECONDS) as u64,
|
||||
HashMap::new(),
|
||||
);
|
||||
self.outcomes.get_mut(outcome_timer).insert(pos, block);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TerrainChanges {
|
||||
pub new_chunks: HashSet<Vec2<i32>>,
|
||||
@ -234,6 +259,7 @@ impl State {
|
||||
ecs.insert(PlayerEntity(None));
|
||||
ecs.insert(TerrainGrid::new(map_size_lg, default_chunk).unwrap());
|
||||
ecs.insert(BlockChange::default());
|
||||
ecs.insert(ScheduledBlockChange::default());
|
||||
ecs.insert(crate::build_areas::BuildAreas::default());
|
||||
ecs.insert(TerrainChanges::default());
|
||||
ecs.insert(EventBus::<LocalEvent>::default());
|
||||
@ -415,6 +441,23 @@ impl State {
|
||||
self.ecs.write_resource::<BlockChange>().set(pos, block);
|
||||
}
|
||||
|
||||
/// Set a block in this state's terrain (used to delete temporary summoned
|
||||
/// sprites after a timeout).
|
||||
pub fn schedule_set_block(
|
||||
&self,
|
||||
pos: Vec3<i32>,
|
||||
block: Block,
|
||||
sprite_block: Block,
|
||||
replace_time: f64,
|
||||
) {
|
||||
self.ecs
|
||||
.write_resource::<ScheduledBlockChange>()
|
||||
.set(pos, block, replace_time);
|
||||
self.ecs
|
||||
.write_resource::<ScheduledBlockChange>()
|
||||
.outcome_set(pos, sprite_block, replace_time);
|
||||
}
|
||||
|
||||
/// Check if the block at given position `pos` has already been modified
|
||||
/// this tick.
|
||||
pub fn can_set_block(&self, pos: Vec3<i32>) -> bool {
|
||||
@ -500,6 +543,28 @@ impl State {
|
||||
let mut terrain = self.ecs.write_resource::<TerrainGrid>();
|
||||
let mut modified_blocks =
|
||||
std::mem::take(&mut self.ecs.write_resource::<BlockChange>().blocks);
|
||||
|
||||
let mut scheduled_changes = self.ecs.write_resource::<ScheduledBlockChange>();
|
||||
let current_time: f64 = self.ecs.read_resource::<Time>().0 * SECONDS_TO_MILLISECONDS;
|
||||
while let Some(changes) = scheduled_changes.changes.poll(current_time as u64) {
|
||||
modified_blocks.extend(changes.iter());
|
||||
}
|
||||
let outcome = self.ecs.read_resource::<EventBus<Outcome>>();
|
||||
while let Some(outcomes) = scheduled_changes.outcomes.poll(current_time as u64) {
|
||||
for (pos, block) in outcomes.iter() {
|
||||
let offset_dir = Vec3::<i32>::zero() - pos;
|
||||
let offset = offset_dir
|
||||
/ Vec3::new(offset_dir.x.abs(), offset_dir.y.abs(), offset_dir.z.abs());
|
||||
let outcome_pos = Vec3::new(pos.x as f32, pos.y as f32, pos.z as f32)
|
||||
- (Vec3::new(offset.x as f32, offset.y as f32, offset.z as f32) / 2.0);
|
||||
if let Some(sprite) = block.get_sprite() {
|
||||
outcome.emit_now(Outcome::SpriteDelete {
|
||||
pos: outcome_pos,
|
||||
sprite,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Apply block modifications
|
||||
// Only include in `TerrainChanges` if successful
|
||||
modified_blocks.retain(|pos, block| {
|
||||
|
@ -23,7 +23,7 @@ use common::{
|
||||
};
|
||||
use common_net::sync::WorldSyncExt;
|
||||
|
||||
use crate::{state_ext::StateExt, Server};
|
||||
use crate::{state_ext::StateExt, Server, Time};
|
||||
|
||||
use crate::pet::tame_pet;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
@ -293,16 +293,32 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_create_sprite(server: &mut Server, pos: Vec3<i32>, sprite: SpriteKind) {
|
||||
pub fn handle_create_sprite(
|
||||
server: &mut Server,
|
||||
pos: Vec3<i32>,
|
||||
sprite: SpriteKind,
|
||||
del_timeout: Option<(f32, f32)>,
|
||||
) {
|
||||
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);
|
||||
let old_block = block.unwrap_or_else(|| Block::air(SpriteKind::Empty));
|
||||
let new_block = old_block.with_sprite(sprite);
|
||||
state.set_block(pos, new_block);
|
||||
// Remove sprite after del_timeout and offset if specified
|
||||
if let Some((timeout, del_offset)) = del_timeout {
|
||||
use rand::Rng;
|
||||
let mut rng = rand::thread_rng();
|
||||
let offset = rng.gen_range(0.0..del_offset);
|
||||
let current_time: f64 = state.ecs().read_resource::<Time>().0;
|
||||
let replace_time = current_time + (timeout + offset) as f64;
|
||||
if old_block != new_block {
|
||||
server
|
||||
.state
|
||||
.schedule_set_block(pos, old_block, new_block, replace_time)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -267,9 +267,11 @@ 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)
|
||||
},
|
||||
ServerEvent::CreateSprite {
|
||||
pos,
|
||||
sprite,
|
||||
del_timeout,
|
||||
} => handle_create_sprite(self, pos, sprite, del_timeout),
|
||||
ServerEvent::TamePet {
|
||||
pet_entity,
|
||||
owner_entity,
|
||||
|
@ -101,7 +101,7 @@ use common::{
|
||||
InventoryUpdateEvent, UtteranceKind,
|
||||
},
|
||||
outcome::Outcome,
|
||||
terrain::{BlockKind, TerrainChunk},
|
||||
terrain::{BlockKind, SpriteKind, TerrainChunk},
|
||||
uid::Uid,
|
||||
DamageSource,
|
||||
};
|
||||
@ -585,6 +585,23 @@ impl SfxMgr {
|
||||
audio.emit_sfx(sfx_trigger_item, *pos, Some(1.0), underwater);
|
||||
}
|
||||
},
|
||||
Outcome::SpriteDelete { pos, sprite } => {
|
||||
match sprite {
|
||||
SpriteKind::SeaUrchin => {
|
||||
let power = (0.6 - pos.distance(audio.listener.pos) / 5_000.0)
|
||||
.max(0.0)
|
||||
.powi(7);
|
||||
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Explosion);
|
||||
audio.emit_sfx(
|
||||
sfx_trigger_item,
|
||||
*pos,
|
||||
Some((power.abs() / 2.5).min(0.3)),
|
||||
underwater,
|
||||
);
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
},
|
||||
Outcome::ExpChange { .. }
|
||||
| Outcome::ComboChange { .. }
|
||||
| Outcome::SummonedCreature { .. } => {},
|
||||
|
@ -19,7 +19,7 @@ use common::{
|
||||
resources::DeltaTime,
|
||||
spiral::Spiral2d,
|
||||
states::{self, utils::StageSection},
|
||||
terrain::{Block, TerrainChunk, TerrainGrid},
|
||||
terrain::{Block, SpriteKind, TerrainChunk, TerrainGrid},
|
||||
uid::UidAllocator,
|
||||
vol::{ReadVol, RectRasterableVol, SizedVol},
|
||||
};
|
||||
@ -74,6 +74,21 @@ impl ParticleMgr {
|
||||
)
|
||||
});
|
||||
},
|
||||
Outcome::SpriteDelete { pos, sprite } => match sprite {
|
||||
SpriteKind::SeaUrchin => {
|
||||
self.particles.resize_with(self.particles.len() + 10, || {
|
||||
Particle::new_directed(
|
||||
Duration::from_secs_f32(rng.gen_range(0.1..0.5)),
|
||||
time,
|
||||
ParticleMode::Steam,
|
||||
*pos + Vec3::new(0.0, 0.0, rng.gen_range(0.0..1.5)),
|
||||
*pos,
|
||||
)
|
||||
});
|
||||
},
|
||||
SpriteKind::EnsnaringVines => {},
|
||||
_ => {},
|
||||
},
|
||||
Outcome::Explosion {
|
||||
pos,
|
||||
power,
|
||||
|
Loading…
Reference in New Issue
Block a user