mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
don't sit through walls
This commit is contained in:
parent
7eba16a2d7
commit
6ddfd631d6
@ -21,9 +21,9 @@ use crate::{
|
||||
event::{LocalEvent, ServerEvent},
|
||||
outcome::Outcome,
|
||||
states::{behavior::JoinData, utils::CharacterState::Idle, *},
|
||||
terrain::{TerrainGrid, UnlockKind},
|
||||
terrain::{TerrainGrid, UnlockKind, SpriteKind},
|
||||
util::Dir,
|
||||
vol::ReadVol, terrain::SpriteKind,
|
||||
vol::ReadVol,
|
||||
};
|
||||
use core::hash::BuildHasherDefault;
|
||||
use fxhash::FxHasher64;
|
||||
@ -796,14 +796,15 @@ pub fn sprite_mount_points(sprite_kind: SpriteKind, pos: Vec3<i32>, ori: u8) ->
|
||||
})
|
||||
}
|
||||
|
||||
pub const SPRITE_MOUNT_DIST_SQR: f32 = 5.0;
|
||||
const SPRITE_MOUNT_RANGE: f32 = 2.0;
|
||||
pub const SPRITE_MOUNT_RANGE_SQR: f32 = SPRITE_MOUNT_RANGE * SPRITE_MOUNT_RANGE;
|
||||
|
||||
pub fn attempt_mount_sprite(data: &JoinData<'_>, update: &mut StateUpdate, pos: Vec3<i32>) {
|
||||
if let Some((kind, ori)) = data.terrain.get(pos).ok().and_then(|block| block.get_sprite().zip(block.get_ori())) {
|
||||
if let Some((mount_pos, mount_dir)) = sprite_mount_points(kind, pos, ori).min_by_key(|(pos, _)| {
|
||||
OrderedFloat(data.pos.0.distance_squared(*pos))
|
||||
}) {
|
||||
if mount_pos.distance_squared(data.pos.0) < SPRITE_MOUNT_DIST_SQR {
|
||||
if reach_block(data.pos.0, pos, SPRITE_MOUNT_RANGE_SQR, data.body, data.terrain) {
|
||||
update.character = CharacterState::MountSprite(mount_sprite::Data {
|
||||
static_data: mount_sprite::StaticData {
|
||||
mount_pos,
|
||||
@ -886,6 +887,71 @@ pub fn attempt_swap_equipped_weapons(data: &JoinData<'_>, update: &mut StateUpda
|
||||
}
|
||||
}
|
||||
|
||||
fn reach_block(player_pos: Vec3<f32>, block_pos: Vec3<i32>, range: f32, body: &Body, terrain: &TerrainGrid) -> bool {
|
||||
let block_pos_f32 = block_pos.map(|x| x as f32 + 0.5);
|
||||
// Closure to check if distance between a point and the block is less than
|
||||
// MAX_PICKUP_RANGE and the radius of the body
|
||||
let block_range_check = |pos: Vec3<f32>| {
|
||||
(block_pos_f32 - pos).magnitude_squared()
|
||||
< (range + body.max_radius()).powi(2)
|
||||
};
|
||||
|
||||
// Checks if player's feet or head is near to block
|
||||
let close_to_block = block_range_check(player_pos)
|
||||
|| block_range_check(player_pos + Vec3::new(0.0, 0.0, body.height()));
|
||||
if close_to_block {
|
||||
// Do a check that a path can be found between sprite and entity
|
||||
// interacting with sprite Use manhattan distance * 1.5 for number
|
||||
// of iterations
|
||||
let iters =
|
||||
(3.0 * (block_pos_f32 - player_pos).map(|x| x.abs()).sum()) as usize;
|
||||
// Heuristic compares manhattan distance of start and end pos
|
||||
let heuristic = move |pos: &Vec3<i32>, _: &Vec3<i32>| {
|
||||
(block_pos - pos).map(|x| x.abs()).sum() as f32
|
||||
};
|
||||
|
||||
let mut astar = Astar::new(
|
||||
iters,
|
||||
player_pos.map(|x| x.floor() as i32),
|
||||
BuildHasherDefault::<FxHasher64>::default(),
|
||||
);
|
||||
|
||||
// Neighbors are all neighboring blocks that are air
|
||||
let neighbors = |pos: &Vec3<i32>| {
|
||||
const DIRS: [Vec3<i32>; 6] = [
|
||||
Vec3::new(1, 0, 0),
|
||||
Vec3::new(-1, 0, 0),
|
||||
Vec3::new(0, 1, 0),
|
||||
Vec3::new(0, -1, 0),
|
||||
Vec3::new(0, 0, 1),
|
||||
Vec3::new(0, 0, -1),
|
||||
];
|
||||
let pos = *pos;
|
||||
DIRS.iter().map(move |dir| dir + pos).filter(|pos| {
|
||||
terrain
|
||||
.get(*pos)
|
||||
.ok()
|
||||
.map_or(false, |block| !block.is_filled())
|
||||
})
|
||||
};
|
||||
// Transition uses manhattan distance as the cost, with a slightly lower cost
|
||||
// for z transitions
|
||||
let transition = |a: Vec3<i32>, b: Vec3<i32>| {
|
||||
let (a, b) = (a.map(|x| x as f32), b.map(|x| x as f32));
|
||||
((a - b) * Vec3::new(1.0, 1.0, 0.9)).map(|e| e.abs()).sum()
|
||||
};
|
||||
// Pathing satisfied when it reaches the block position
|
||||
let satisfied = |pos: &Vec3<i32>| *pos == block_pos;
|
||||
|
||||
astar
|
||||
.poll(iters, heuristic, neighbors, transition, satisfied)
|
||||
.into_path()
|
||||
.is_some()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles inventory manipulations that affect the loadout
|
||||
pub fn handle_manipulate_loadout(
|
||||
data: &JoinData<'_>,
|
||||
@ -927,90 +993,21 @@ pub fn handle_manipulate_loadout(
|
||||
}
|
||||
},
|
||||
InventoryAction::Collect(sprite_pos) => {
|
||||
let sprite_pos_f32 = sprite_pos.map(|x| x as f32 + 0.5);
|
||||
// Closure to check if distance between a point and the sprite is less than
|
||||
// MAX_PICKUP_RANGE and the radius of the body
|
||||
let sprite_range_check = |pos: Vec3<f32>| {
|
||||
(sprite_pos_f32 - pos).magnitude_squared()
|
||||
< (MAX_PICKUP_RANGE + data.body.max_radius()).powi(2)
|
||||
};
|
||||
|
||||
// Checks if player's feet or head is near to sprite
|
||||
let close_to_sprite = sprite_range_check(data.pos.0)
|
||||
|| sprite_range_check(data.pos.0 + Vec3::new(0.0, 0.0, data.body.height()));
|
||||
if close_to_sprite {
|
||||
// First, get sprite data for position, if there is a sprite
|
||||
use sprite_interact::SpriteInteractKind;
|
||||
let sprite_chunk_pos = TerrainGrid::chunk_offs(sprite_pos);
|
||||
let sprite_cfg = data
|
||||
.terrain
|
||||
.pos_chunk(sprite_pos)
|
||||
.and_then(|chunk| chunk.meta().sprite_cfg_at(sprite_chunk_pos));
|
||||
let sprite_at_pos = data
|
||||
.terrain
|
||||
.get(sprite_pos)
|
||||
.ok()
|
||||
.copied()
|
||||
.and_then(|b| b.get_sprite());
|
||||
|
||||
// Checks if position has a collectible sprite as well as what sprite is at the
|
||||
// position
|
||||
let sprite_interact = sprite_at_pos.and_then(Option::<SpriteInteractKind>::from);
|
||||
|
||||
if let Some(sprite_interact) = sprite_interact {
|
||||
// Do a check that a path can be found between sprite and entity
|
||||
// interacting with sprite Use manhattan distance * 1.5 for number
|
||||
// of iterations
|
||||
let iters =
|
||||
(3.0 * (sprite_pos_f32 - data.pos.0).map(|x| x.abs()).sum()) as usize;
|
||||
// Heuristic compares manhattan distance of start and end pos
|
||||
let heuristic = move |pos: &Vec3<i32>, _: &Vec3<i32>| {
|
||||
(sprite_pos - pos).map(|x| x.abs()).sum() as f32
|
||||
};
|
||||
|
||||
let mut astar = Astar::new(
|
||||
iters,
|
||||
data.pos.0.map(|x| x.floor() as i32),
|
||||
BuildHasherDefault::<FxHasher64>::default(),
|
||||
);
|
||||
|
||||
// Transition uses manhattan distance as the cost, with a slightly lower cost
|
||||
// for z transitions
|
||||
let transition = |a: Vec3<i32>, b: Vec3<i32>| {
|
||||
let (a, b) = (a.map(|x| x as f32), b.map(|x| x as f32));
|
||||
((a - b) * Vec3::new(1.0, 1.0, 0.9)).map(|e| e.abs()).sum()
|
||||
};
|
||||
// Neighbors are all neighboring blocks that are air
|
||||
let neighbors = |pos: &Vec3<i32>| {
|
||||
const DIRS: [Vec3<i32>; 6] = [
|
||||
Vec3::new(1, 0, 0),
|
||||
Vec3::new(-1, 0, 0),
|
||||
Vec3::new(0, 1, 0),
|
||||
Vec3::new(0, -1, 0),
|
||||
Vec3::new(0, 0, 1),
|
||||
Vec3::new(0, 0, -1),
|
||||
];
|
||||
let pos = *pos;
|
||||
DIRS.iter()
|
||||
.map(move |dir| {
|
||||
let dest = dir + pos;
|
||||
(dest, transition(pos, dest))
|
||||
})
|
||||
.filter(|(pos, _)| {
|
||||
data.terrain
|
||||
.get(*pos)
|
||||
.ok()
|
||||
.map_or(false, |block| !block.is_filled())
|
||||
})
|
||||
};
|
||||
// Pathing satisfied when it reaches the sprite position
|
||||
let satisfied = |pos: &Vec3<i32>| *pos == sprite_pos;
|
||||
|
||||
let not_blocked_by_terrain = astar
|
||||
.poll(iters, heuristic, neighbors, satisfied)
|
||||
.into_path()
|
||||
.is_some();
|
||||
|
||||
// First, get sprite data for position, if there is a sprite
|
||||
let sprite_at_pos = data.terrain
|
||||
.get(sprite_pos)
|
||||
.ok()
|
||||
.copied()
|
||||
.and_then(|b| b.get_sprite());
|
||||
// Checks if position has a collectible sprite as well as what sprite is at the
|
||||
// position
|
||||
let sprite_interact = sprite_at_pos.and_then(Option::<sprite_interact::SpriteInteractKind>::from);
|
||||
if let Some(sprite_interact) = sprite_interact {
|
||||
if reach_block(data.pos.0, sprite_pos, MAX_PICKUP_RANGE, data.body, data.terrain) {
|
||||
let sprite_chunk_pos = TerrainGrid::chunk_offs(sprite_pos);
|
||||
let sprite_cfg = data.terrain
|
||||
.pos_chunk(sprite_pos)
|
||||
.and_then(|chunk| chunk.meta().sprite_cfg_at(sprite_chunk_pos));
|
||||
let required_item =
|
||||
sprite_at_pos.and_then(|s| match s.unlock_condition(sprite_cfg.cloned()) {
|
||||
UnlockKind::Free => None,
|
||||
@ -1029,38 +1026,33 @@ pub fn handle_manipulate_loadout(
|
||||
.map(|slot| Some((item_id, slot, consume))),
|
||||
None => Some(None),
|
||||
};
|
||||
if let Some(required_item) = has_required_items {
|
||||
// If the sprite is collectible, enter the sprite interaction character
|
||||
// state TODO: Handle cases for sprite being
|
||||
// interactible, but not collectible (none currently
|
||||
// exist)
|
||||
let (buildup_duration, use_duration, recover_duration) =
|
||||
sprite_interact.durations();
|
||||
|
||||
// If path can be found between entity interacting with sprite and entity, start
|
||||
// interaction with sprite
|
||||
if not_blocked_by_terrain {
|
||||
if let Some(required_item) = has_required_items {
|
||||
// If the sprite is collectible, enter the sprite interaction character
|
||||
// state TODO: Handle cases for sprite being
|
||||
// interactible, but not collectible (none currently
|
||||
// exist)
|
||||
let (buildup_duration, use_duration, recover_duration) =
|
||||
sprite_interact.durations();
|
||||
|
||||
update.character =
|
||||
CharacterState::SpriteInteract(sprite_interact::Data {
|
||||
static_data: sprite_interact::StaticData {
|
||||
buildup_duration,
|
||||
use_duration,
|
||||
recover_duration,
|
||||
sprite_pos,
|
||||
sprite_kind: sprite_interact,
|
||||
was_wielded: data.character.is_wield(),
|
||||
was_sneak: data.character.is_stealthy(),
|
||||
required_item,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
})
|
||||
} else {
|
||||
output_events.emit_local(LocalEvent::CreateOutcome(
|
||||
Outcome::FailedSpriteUnlock { pos: sprite_pos },
|
||||
));
|
||||
}
|
||||
update.character =
|
||||
CharacterState::SpriteInteract(sprite_interact::Data {
|
||||
static_data: sprite_interact::StaticData {
|
||||
buildup_duration,
|
||||
use_duration,
|
||||
recover_duration,
|
||||
sprite_pos,
|
||||
sprite_kind: sprite_interact,
|
||||
was_wielded: data.character.is_wield(),
|
||||
was_sneak: data.character.is_stealthy(),
|
||||
required_item,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
})
|
||||
} else {
|
||||
output_events.emit_local(LocalEvent::CreateOutcome(
|
||||
Outcome::FailedSpriteUnlock { pos: sprite_pos },
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,9 +13,10 @@ use common::{
|
||||
consts::MAX_PICKUP_RANGE,
|
||||
link::Is,
|
||||
mounting::Mount,
|
||||
states::utils::SPRITE_MOUNT_RANGE_SQR,
|
||||
terrain::{Block, TerrainGrid, UnlockKind},
|
||||
util::find_dist::{Cube, Cylinder, FindDist},
|
||||
vol::ReadVol, states::utils::SPRITE_MOUNT_DIST_SQR,
|
||||
vol::ReadVol,
|
||||
};
|
||||
use common_base::span;
|
||||
|
||||
@ -257,7 +258,7 @@ pub(super) fn select_interactable(
|
||||
} else if let Interaction::Mount(mount_points) = interaction {
|
||||
mount_points.iter()
|
||||
.map(|p| (p + pos.as_()).distance_squared(player_pos))
|
||||
.filter(|dist| *dist < SPRITE_MOUNT_DIST_SQR)
|
||||
.filter(|dist| *dist < SPRITE_MOUNT_RANGE_SQR)
|
||||
.min_by_key(|dist| OrderedFloat(*dist))
|
||||
.map(|dist| (pos, dist))
|
||||
.zip(Some(interaction))
|
||||
|
@ -887,14 +887,15 @@ impl PlayState for SessionState {
|
||||
let mut client = self.client.borrow_mut();
|
||||
if client.is_riding() {
|
||||
client.unmount();
|
||||
} else if client.stand_if_mounted() { } else {
|
||||
} else if client.stand_if_mounted() {
|
||||
} else {
|
||||
if let Some(interactable) = &self.interactable {
|
||||
match interactable {
|
||||
Interactable::Block(_, pos, interaction) => {
|
||||
if matches!(interaction, BlockInteraction::Mount(_)) {
|
||||
client.mount_sprite(*pos)
|
||||
}
|
||||
}
|
||||
},
|
||||
Interactable::Entity(entity) => client.mount(*entity),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user