Merge branch 'shandley/pickup-player-relative' into 'master'

Constrain item pickup range

See merge request veloren/veloren!843
This commit is contained in:
Acrimon 2020-03-10 20:50:04 +00:00
commit e75aff7bb7
4 changed files with 81 additions and 11 deletions

View File

@ -8,6 +8,9 @@ use specs::{Component, FlaggedStorage, HashMapStorage};
use specs_idvs::IDVStorage;
use std::ops::Not;
// The limit on distance between the entity and a collectible (squared)
pub const MAX_PICKUP_RANGE_SQR: f32 = 64.0;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Inventory {
pub slots: Vec<Option<Item>>,

View File

@ -28,7 +28,9 @@ pub use controller::{
};
pub use energy::{Energy, EnergySource};
pub use inputs::CanBuild;
pub use inventory::{item, Inventory, InventoryUpdate, InventoryUpdateEvent, Item, ItemKind};
pub use inventory::{
item, Inventory, InventoryUpdate, InventoryUpdateEvent, Item, ItemKind, MAX_PICKUP_RANGE_SQR,
};
pub use last::Last;
pub use location::{Waypoint, WaypointArea};
pub use phys::{ForceUpdate, Gravity, Mass, Ori, PhysicsState, Pos, Scale, Sticky, Vel};

View File

@ -1,6 +1,6 @@
use crate::{Server, StateExt};
use common::{
comp,
comp::{self, Pos, MAX_PICKUP_RANGE_SQR},
sync::WorldSyncExt,
terrain::block::Block,
vol::{ReadVol, Vox},
@ -16,7 +16,6 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
match manip {
comp::InventoryManip::Pickup(uid) => {
// TODO: enforce max pickup range
let item_entity = if let (Some((item, item_entity)), Some(inv)) = (
state
.ecs()
@ -33,7 +32,11 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
.write_storage::<comp::Inventory>()
.get_mut(entity),
) {
if inv.push(item).is_none() {
if within_pickup_range(
state.ecs().read_storage::<comp::Pos>().get(entity),
state.ecs().read_storage::<comp::Pos>().get(item_entity),
) && inv.push(item).is_none()
{
Some(item_entity)
} else {
None
@ -56,6 +59,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
comp::InventoryManip::Collect(pos) => {
let block = state.terrain().get(pos).ok().copied();
if let Some(block) = block {
if block.is_collectible()
&& state
@ -231,3 +235,39 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
.build();
}
}
fn within_pickup_range(player_position: Option<&Pos>, item_position: Option<&Pos>) -> bool {
match (player_position, item_position) {
(Some(ppos), Some(ipos)) => ppos.0.distance_squared(ipos.0) < MAX_PICKUP_RANGE_SQR,
_ => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use common::comp::Pos;
use vek::Vec3;
#[test]
fn pickup_distance_within_range() {
let player_position = Pos(Vec3::zero());
let item_position = Pos(Vec3::one());
assert_eq!(
within_pickup_range(Some(&player_position), Some(&item_position)),
true
);
}
#[test]
fn pickup_distance_not_within_range() {
let player_position = Pos(Vec3::zero());
let item_position = Pos(Vec3::one() * 500.0);
assert_eq!(
within_pickup_range(Some(&player_position), Some(&item_position)),
false
);
}
}

View File

@ -13,7 +13,7 @@ use common::{
assets::{load_watched, watch},
clock::Clock,
comp,
comp::{Pos, Vel},
comp::{Pos, Vel, MAX_PICKUP_RANGE_SQR},
msg::ClientState,
terrain::{Block, BlockKind},
vol::ReadVol,
@ -155,27 +155,52 @@ impl PlayState for SessionState {
let camera::Dependents {
view_mat, cam_pos, ..
} = self.scene.camera().dependents();
// Choose a spot above the player's head for item distance checks
let player_pos = match self
.client
.borrow()
.state()
.read_storage::<comp::Pos>()
.get(self.client.borrow().entity())
{
Some(pos) => pos.0 + (Vec3::unit_z() * 2.0),
_ => cam_pos, // Should never happen, but a safe fallback
};
let cam_dir: Vec3<f32> = Vec3::from(view_mat.inverted() * -Vec4::unit_z());
// Check to see whether we're aiming at anything
let (build_pos, select_pos) = {
let client = self.client.borrow();
let terrain = client.state().terrain();
let ray = terrain
let cam_ray = terrain
.ray(cam_pos, cam_pos + cam_dir * 100.0)
.until(|block| block.is_tangible())
.cast();
let dist = ray.0;
if let Ok(Some(_)) = ray.1 {
// Hit something!
let cam_dist = cam_ray.0;
if let Ok(Some(_)) = cam_ray.1 {
// The ray hit something, is it within pickup range?
let select_pos = if player_pos.distance_squared(cam_pos + cam_dir * cam_dist)
<= MAX_PICKUP_RANGE_SQR
{
Some((cam_pos + cam_dir * cam_dist).map(|e| e.floor() as i32))
} else {
None
};
(
Some((cam_pos + cam_dir * (dist - 0.01)).map(|e| e.floor() as i32)),
Some((cam_pos + cam_dir * dist).map(|e| e.floor() as i32)),
Some((cam_pos + cam_dir * (cam_dist - 0.01)).map(|e| e.floor() as i32)),
select_pos,
)
} else {
(None, None)
}
};
// Only highlight collectables
self.scene.set_select_pos(select_pos.filter(|sp| {
self.client