add flag to clamp to ground for RepositionOnChunkLoad

This commit is contained in:
Isse 2023-04-12 10:17:49 +02:00
parent 6be4a2b33f
commit 0bb6fa7946
7 changed files with 34 additions and 19 deletions

View File

@ -137,7 +137,7 @@ impl Link for Mounting {
let old_pos = pos.0.map(|e| e.floor() as i32); let old_pos = pos.0.map(|e| e.floor() as i32);
pos.0 = safe_pos pos.0 = safe_pos
.map(|p| p.0.map(|e| e.floor())) .map(|p| p.0.map(|e| e.floor()))
.unwrap_or_else(|| terrain.find_space(old_pos).map(|e| e as f32)) .unwrap_or_else(|| terrain.find_ground(old_pos).map(|e| e as f32))
+ Vec3::new(0.5, 0.5, 0.0); + Vec3::new(0.5, 0.5, 0.0);
if let Some(force_update) = force_update.get_mut(rider) { if let Some(force_update) = force_update.get_mut(rider) {
force_update.update(); force_update.update();

View File

@ -222,11 +222,13 @@ impl TerrainChunkMeta {
pub type TerrainChunk = chonk::Chonk<Block, TerrainChunkSize, TerrainChunkMeta>; pub type TerrainChunk = chonk::Chonk<Block, TerrainChunkSize, TerrainChunkMeta>;
pub type TerrainGrid = VolGrid2d<TerrainChunk>; pub type TerrainGrid = VolGrid2d<TerrainChunk>;
const TERRAIN_GRID_SEARCH_DIST: i32 = 63;
impl TerrainGrid { impl TerrainGrid {
/// Find a location suitable for spawning an entity near the given /// Find a location suitable for spawning an entity near the given
/// position (but in the same chunk). /// position (but in the same chunk).
pub fn find_space(&self, pos: Vec3<i32>) -> Vec3<i32> { pub fn find_ground(&self, pos: Vec3<i32>) -> Vec3<i32> {
self.try_find_space(pos).unwrap_or(pos) self.try_find_ground(pos).unwrap_or(pos)
} }
pub fn is_space(&self, pos: Vec3<i32>) -> bool { pub fn is_space(&self, pos: Vec3<i32>) -> bool {
@ -237,8 +239,14 @@ impl TerrainGrid {
} }
pub fn try_find_space(&self, pos: Vec3<i32>) -> Option<Vec3<i32>> { pub fn try_find_space(&self, pos: Vec3<i32>) -> Option<Vec3<i32>> {
const SEARCH_DIST: i32 = 63; (0..TERRAIN_GRID_SEARCH_DIST * 2 + 1)
(0..SEARCH_DIST * 2 + 1) .map(|i| if i % 2 == 0 { i } else { -i } / 2)
.map(|z_diff| pos + Vec3::unit_z() * z_diff)
.find(|pos| self.is_space(*pos))
}
pub fn try_find_ground(&self, pos: Vec3<i32>) -> Option<Vec3<i32>> {
(0..TERRAIN_GRID_SEARCH_DIST * 2 + 1)
.map(|i| if i % 2 == 0 { i } else { -i } / 2) .map(|i| if i % 2 == 0 { i } else { -i } / 2)
.map(|z_diff| pos + Vec3::unit_z() * z_diff) .map(|z_diff| pos + Vec3::unit_z() * z_diff)
.find(|pos| { .find(|pos| {

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
client::Client, events::player::handle_exit_ingame, persistence::PersistedComponents, sys, client::Client, events::player::handle_exit_ingame, persistence::PersistedComponents,
CharacterUpdater, Server, StateExt, presence::RepositionOnChunkLoad, presence::RepositionOnChunkLoad, sys, CharacterUpdater, Server, StateExt,
}; };
use common::{ use common::{
character::CharacterId, character::CharacterId,
@ -131,7 +131,9 @@ pub fn handle_create_npc(server: &mut Server, pos: Pos, mut npc: NpcBuilder) ->
}; };
let entity = if let Some(rtsim_entity) = npc.rtsim_entity { let entity = if let Some(rtsim_entity) = npc.rtsim_entity {
entity.with(rtsim_entity).with(RepositionOnChunkLoad) entity.with(rtsim_entity).with(RepositionOnChunkLoad {
needs_ground: false,
})
} else { } else {
entity entity
}; };

View File

@ -1,6 +1,6 @@
use hashbrown::HashSet; use hashbrown::HashSet;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use specs::{Component, NullStorage}; use specs::{Component, VecStorage};
use vek::*; use vek::*;
// Distance from fuzzy_chunk before snapping to current chunk // Distance from fuzzy_chunk before snapping to current chunk
@ -20,8 +20,10 @@ impl Component for RegionSubscription {
} }
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
pub struct RepositionOnChunkLoad; pub struct RepositionOnChunkLoad {
pub needs_ground: bool,
}
impl Component for RepositionOnChunkLoad { impl Component for RepositionOnChunkLoad {
type Storage = NullStorage<Self>; type Storage = VecStorage<Self>;
} }

View File

@ -659,7 +659,9 @@ impl StateExt for State {
); );
if let Some(waypoint) = waypoint { if let Some(waypoint) = waypoint {
self.write_component_ignore_entity_dead(entity, RepositionOnChunkLoad); self.write_component_ignore_entity_dead(entity, RepositionOnChunkLoad {
needs_ground: true,
});
self.write_component_ignore_entity_dead(entity, waypoint); self.write_component_ignore_entity_dead(entity, waypoint);
self.write_component_ignore_entity_dead(entity, comp::Pos(waypoint.get_pos())); self.write_component_ignore_entity_dead(entity, comp::Pos(waypoint.get_pos()));
self.write_component_ignore_entity_dead(entity, comp::Vel(Vec3::zero())); self.write_component_ignore_entity_dead(entity, comp::Vel(Vec3::zero()));

View File

@ -66,7 +66,7 @@ impl<'a> System<'a> for Sys {
// TODO: Create a teleportation event to handle this instead of // TODO: Create a teleportation event to handle this instead of
// processing the entity position move here // processing the entity position move here
pet_pos.0 = terrain pet_pos.0 = terrain
.find_space(owner_pos.0.map(|e| e.floor() as i32)) .find_ground(owner_pos.0.map(|e| e.floor() as i32))
.map(|e| e as f32); .map(|e| e as f32);
} }
} }

View File

@ -228,11 +228,11 @@ impl<'a> System<'a> for Sys {
// TODO: Consider putting this in another system since this forces us to take // TODO: Consider putting this in another system since this forces us to take
// positions by write rather than read access. // positions by write rather than read access.
let repositioned = (&entities, &mut positions, (&mut force_update).maybe(), reposition_on_load.mask()) let repositioned = (&entities, &mut positions, (&mut force_update).maybe(), &mut reposition_on_load)
// TODO: Consider using par_bridge() because Rayon has very poor work splitting for // TODO: Consider using par_bridge() because Rayon has very poor work splitting for
// sparse joins. // sparse joins.
.par_join() .par_join()
.filter_map(|(entity, pos, force_update, _)| { .filter_map(|(entity, pos, force_update, reposition)| {
// NOTE: We use regular as casts rather than as_ because we want to saturate on // NOTE: We use regular as casts rather than as_ because we want to saturate on
// overflow. // overflow.
let entity_pos = pos.0.map(|x| x as i32); let entity_pos = pos.0.map(|x| x as i32);
@ -240,10 +240,11 @@ impl<'a> System<'a> for Sys {
// from having just logged in), reposition them. // from having just logged in), reposition them.
let chunk_pos = TerrainGrid::chunk_key(entity_pos); let chunk_pos = TerrainGrid::chunk_key(entity_pos);
let chunk = terrain.get_key(chunk_pos)?; let chunk = terrain.get_key(chunk_pos)?;
let new_pos = terrain let new_pos = if reposition.needs_ground {
.try_find_space(entity_pos) terrain.try_find_ground(entity_pos)
.map(|x| x.as_::<f32>()) } else {
.unwrap_or_else(|| chunk.find_accessible_pos(entity_pos.xy(), false)); terrain.try_find_space(entity_pos)
}.map(|x| x.as_::<f32>()).unwrap_or_else(|| chunk.find_accessible_pos(entity_pos.xy(), false));
pos.0 = new_pos; pos.0 = new_pos;
force_update.map(|force_update| force_update.update()); force_update.map(|force_update| force_update.update());
Some((entity, new_pos)) Some((entity, new_pos))