mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Check that a targeted collectible is within a certain pickup range of the character before highlighting it
This commit is contained in:
parent
5598fe7166
commit
b2312a0487
@ -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>>,
|
||||
|
@ -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};
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user