mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add entity targeting
This commit is contained in:
parent
eaa5bac8d6
commit
1741384d00
@ -242,6 +242,7 @@ pub struct DebugInfo {
|
||||
pub struct HudInfo {
|
||||
pub is_aiming: bool,
|
||||
pub is_first_person: bool,
|
||||
pub target_entity: Option<specs::Entity>,
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
@ -1045,7 +1046,9 @@ impl Hud {
|
||||
&uids,
|
||||
)
|
||||
.join()
|
||||
.filter(|(entity, _, _, stats, _, _, _, _, _, _)| *entity != me && !stats.is_dead)
|
||||
.filter(|(entity, _, _, stats, _, _, _, _, _, _)| *entity != me && !stats.is_dead
|
||||
&& (stats.health.current() != stats.health.maximum() || info.target_entity.map_or(false, |e| e == *entity))
|
||||
)
|
||||
// Don't show outside a certain range
|
||||
.filter(|(_, pos, _, _, _, _, _, _, hpfl, _)| {
|
||||
pos.0.distance_squared(player_pos)
|
||||
|
@ -22,8 +22,8 @@ use anim::{
|
||||
};
|
||||
use common::{
|
||||
comp::{
|
||||
item::ItemKind, Body, CharacterState, Last, LightAnimation, LightEmitter, Loadout, Ori,
|
||||
PhysicsState, Pos, Scale, Stats, Vel,
|
||||
item::ItemKind, Body, CharacterState, Item, Last, LightAnimation, LightEmitter, Loadout,
|
||||
Ori, PhysicsState, Pos, Scale, Stats, Vel,
|
||||
},
|
||||
state::{DeltaTime, State},
|
||||
states::triple_strike,
|
||||
@ -192,7 +192,6 @@ impl FigureMgr {
|
||||
.read_storage::<Pos>()
|
||||
.get(scene_data.player_entity)
|
||||
.map_or(Vec3::zero(), |pos| pos.0);
|
||||
|
||||
for (
|
||||
i,
|
||||
(
|
||||
@ -207,6 +206,7 @@ impl FigureMgr {
|
||||
physics,
|
||||
stats,
|
||||
loadout,
|
||||
item,
|
||||
),
|
||||
) in (
|
||||
&ecs.entities(),
|
||||
@ -220,6 +220,7 @@ impl FigureMgr {
|
||||
&ecs.read_storage::<PhysicsState>(),
|
||||
ecs.read_storage::<Stats>().maybe(),
|
||||
ecs.read_storage::<Loadout>().maybe(),
|
||||
ecs.read_storage::<Item>().maybe(),
|
||||
)
|
||||
.join()
|
||||
.enumerate()
|
||||
@ -519,7 +520,13 @@ impl FigureMgr {
|
||||
(c / (1.0 + DAMAGE_FADE_COEFFICIENT * s.health.last_change.0)) as f32
|
||||
})
|
||||
})
|
||||
.unwrap_or(Rgba::broadcast(1.0));
|
||||
.unwrap_or(Rgba::broadcast(1.0))
|
||||
// Highlight targeted collectible entities
|
||||
* if item.is_some() && scene_data.target_entity.map_or(false, |e| e == entity) {
|
||||
Rgba::new(2.0, 2.0, 2.0, 1.0)
|
||||
} else {
|
||||
Rgba::one()
|
||||
};
|
||||
|
||||
let scale = scale.map(|s| s.0).unwrap_or(1.0);
|
||||
|
||||
|
@ -70,6 +70,7 @@ pub struct Scene {
|
||||
pub struct SceneData<'a> {
|
||||
pub state: &'a State,
|
||||
pub player_entity: specs::Entity,
|
||||
pub target_entity: Option<specs::Entity>,
|
||||
pub loaded_distance: f32,
|
||||
pub view_distance: u32,
|
||||
pub tick: u64,
|
||||
|
@ -208,18 +208,6 @@ impl PlayState for SessionState {
|
||||
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 (is_aiming, aim_dir_offset) = {
|
||||
let client = self.client.borrow();
|
||||
let is_aiming = client
|
||||
@ -243,30 +231,10 @@ impl PlayState for SessionState {
|
||||
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 cam_ray = terrain
|
||||
.ray(cam_pos, cam_pos + cam_dir * 100.0)
|
||||
.until(|block| block.is_tangible())
|
||||
.cast();
|
||||
|
||||
let cam_dist = cam_ray.0;
|
||||
|
||||
match cam_ray.1 {
|
||||
Ok(Some(_))
|
||||
if player_pos.distance_squared(cam_pos + cam_dir * cam_dist)
|
||||
<= MAX_PICKUP_RANGE_SQR =>
|
||||
{
|
||||
(
|
||||
Some((cam_pos + cam_dir * (cam_dist - 0.01)).map(|e| e.floor() as i32)),
|
||||
Some((cam_pos + cam_dir * (cam_dist + 0.01)).map(|e| e.floor() as i32)),
|
||||
)
|
||||
},
|
||||
_ => (None, None),
|
||||
}
|
||||
};
|
||||
let (build_pos, select_pos, target_entity) =
|
||||
under_cursor(&self.client.borrow(), cam_pos, cam_dir);
|
||||
// Throw out distance info, it will be useful in the future
|
||||
let target_entity = target_entity.map(|x| x.0);
|
||||
|
||||
let can_build = self
|
||||
.client
|
||||
@ -708,6 +676,7 @@ impl PlayState for SessionState {
|
||||
self.scene.camera().get_mode(),
|
||||
camera::CameraMode::FirstPerson
|
||||
),
|
||||
target_entity,
|
||||
},
|
||||
);
|
||||
|
||||
@ -979,6 +948,7 @@ impl PlayState for SessionState {
|
||||
let scene_data = SceneData {
|
||||
state: client.state(),
|
||||
player_entity: client.entity(),
|
||||
target_entity,
|
||||
loaded_distance: client.loaded_distance(),
|
||||
view_distance: client.view_distance().unwrap_or(1),
|
||||
tick: client.get_tick(),
|
||||
@ -1055,3 +1025,102 @@ impl PlayState for SessionState {
|
||||
self.hud.render(renderer, self.scene.globals());
|
||||
}
|
||||
}
|
||||
|
||||
/// Max distance an entity can be "targeted"
|
||||
const MAX_TARGET_RANGE: f32 = 30.0;
|
||||
/// Calculate what the cursor is pointing at within the 3d scene
|
||||
fn under_cursor(
|
||||
client: &Client,
|
||||
cam_pos: Vec3<f32>,
|
||||
cam_dir: Vec3<f32>,
|
||||
) -> (
|
||||
Option<Vec3<i32>>,
|
||||
Option<Vec3<i32>>,
|
||||
Option<(specs::Entity, f32)>,
|
||||
) {
|
||||
// Choose a spot above the player's head for item distance checks
|
||||
let player_entity = client.entity();
|
||||
let player_pos = match client
|
||||
.state()
|
||||
.read_storage::<comp::Pos>()
|
||||
.get(player_entity)
|
||||
{
|
||||
Some(pos) => pos.0 + (Vec3::unit_z() * 2.0),
|
||||
_ => cam_pos, // Should never happen, but a safe fallback
|
||||
};
|
||||
let terrain = client.state().terrain();
|
||||
|
||||
let cam_ray = terrain
|
||||
.ray(cam_pos, cam_pos + cam_dir * 100.0)
|
||||
.until(|block| block.is_tangible())
|
||||
.cast();
|
||||
|
||||
let cam_dist = cam_ray.0;
|
||||
|
||||
// The ray hit something, is it within range?
|
||||
let (build_pos, select_pos) = if matches!(cam_ray.1, Ok(Some(_)) if
|
||||
player_pos.distance_squared(cam_pos + cam_dir * cam_dist)
|
||||
<= MAX_PICKUP_RANGE_SQR)
|
||||
{
|
||||
(
|
||||
Some((cam_pos + cam_dir * (cam_dist - 0.01)).map(|e| e.floor() as i32)),
|
||||
Some((cam_pos + cam_dir * (cam_dist + 0.01)).map(|e| e.floor() as i32)),
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
// See if ray hits entities
|
||||
// Currently treated as spheres
|
||||
let ecs = client.state().ecs();
|
||||
// Don't cast through blocks
|
||||
// Could check for intersection with entity from last frame to narrow this down
|
||||
let cast_dist = if let Ok(Some(_)) = cam_ray.1 {
|
||||
cam_dist.min(MAX_TARGET_RANGE)
|
||||
} else {
|
||||
MAX_TARGET_RANGE
|
||||
};
|
||||
|
||||
// Need to raycast by distance to cam
|
||||
// But also filter out by distance to the player (but this only needs to be done
|
||||
// on final result)
|
||||
let mut nearby = (
|
||||
&ecs.entities(),
|
||||
&ecs.read_storage::<comp::Pos>(),
|
||||
ecs.read_storage::<comp::Scale>().maybe(),
|
||||
&ecs.read_storage::<comp::Body>()
|
||||
)
|
||||
.join()
|
||||
.filter(|(e, _, _, _)| *e != player_entity)
|
||||
.map(|(e, p, s, b)| {
|
||||
const RADIUS_SCALE: f32 = 1.2;
|
||||
let radius = s.map_or(1.0, |s| s.0) * b.radius() * RADIUS_SCALE;
|
||||
// Move position up from the feet
|
||||
let pos = Vec3::new(p.0.x, p.0.y, p.0.z + radius);
|
||||
let dist_sqr = pos.distance_squared(cam_pos);
|
||||
(e, pos, radius, dist_sqr)
|
||||
})
|
||||
// Ignore entities intersecting the camera
|
||||
.filter(|(_, _, r, d_sqr)| *d_sqr > r.powi(2))
|
||||
.filter(|(_, _, r, d_sqr)| *d_sqr <= cast_dist.powi(2) + 2.0 * cast_dist * r + r.powi(2))
|
||||
.map(|(e, p, r, d_sqr)| (e, p, r, d_sqr.sqrt() - r))
|
||||
.collect::<Vec<_>>();
|
||||
nearby.sort_unstable_by(|a, b| a.3.partial_cmp(&b.3).unwrap());
|
||||
|
||||
let seg_ray = LineSegment3 {
|
||||
start: cam_pos,
|
||||
end: cam_pos + cam_dir * cam_dist,
|
||||
};
|
||||
// TODO: fuzzy borders
|
||||
let target_entity = nearby
|
||||
.iter()
|
||||
.map(|(e, p, r, _)| (e, *p, r))
|
||||
.find(|(_, p, r)| seg_ray.projected_point(*p).distance_squared(*p) < r.powi(2))
|
||||
.and_then(|(e, p, r)| {
|
||||
let dist_to_player = p.distance(player_pos);
|
||||
(dist_to_player - r < MAX_TARGET_RANGE).then_some((*e, dist_to_player))
|
||||
});
|
||||
|
||||
// TODO: consider setting build/select to None when targeting an entity
|
||||
(build_pos, select_pos, target_entity)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user