mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
make Target generic, over the specific typed targets
This commit is contained in:
parent
ba4af5ee8e
commit
8b83b48b9b
@ -2,7 +2,7 @@ use ordered_float::OrderedFloat;
|
||||
use specs::{Join, WorldExt};
|
||||
use vek::*;
|
||||
|
||||
use super::target::{Target, TargetType};
|
||||
use super::target::{self, Target};
|
||||
use client::{self, Client};
|
||||
use common::{
|
||||
comp,
|
||||
@ -28,38 +28,6 @@ impl Interactable {
|
||||
Self::Block(_, _, _) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_target(target: Target, client: &Client) -> Option<Interactable> {
|
||||
match target.typed {
|
||||
TargetType::Collectable => client
|
||||
.state()
|
||||
.terrain()
|
||||
.get(target.position_int())
|
||||
.ok()
|
||||
.copied()
|
||||
.map(|b| Interactable::Block(b, target.position_int(), Some(Interaction::Collect))),
|
||||
TargetType::Entity(e) => Some(Interactable::Entity(e)),
|
||||
TargetType::Mine => client
|
||||
.state()
|
||||
.terrain()
|
||||
.get(target.position_int())
|
||||
.ok()
|
||||
.copied()
|
||||
.and_then(|b| {
|
||||
// Handling edge detection. sometimes the casting (in Target mod) returns a
|
||||
// position which is actually empty, which we do not want labeled as an
|
||||
// interactable. We are only returning the mineable air
|
||||
// elements (e.g. minerals). The mineable weakrock are used
|
||||
// in the terrain selected_pos, but is not an interactable.
|
||||
if b.mine_tool().is_some() && b.is_air() {
|
||||
Some(Interactable::Block(b, target.position_int(), None))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
TargetType::Build => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Select interactable to hightlight, display interaction text for, and to
|
||||
@ -73,30 +41,57 @@ impl Interactable {
|
||||
/// -> closest of nearest interactable entity/block
|
||||
pub(super) fn select_interactable(
|
||||
client: &Client,
|
||||
collect_target: Option<Target>,
|
||||
entity_target: Option<Target>,
|
||||
mine_target: Option<Target>,
|
||||
collect_target: Option<Target<target::Collectable>>,
|
||||
entity_target: Option<Target<target::Entity>>,
|
||||
mine_target: Option<Target<target::Mine>>,
|
||||
scene: &Scene,
|
||||
) -> Option<Interactable> {
|
||||
span!(_guard, "select_interactable");
|
||||
use common::{spiral::Spiral2d, terrain::TerrainChunk, vol::RectRasterableVol};
|
||||
|
||||
fn get_block<T>(client: &Client, target: Target<T>) -> Option<Block> {
|
||||
client
|
||||
.state()
|
||||
.terrain()
|
||||
.get(target.position_int())
|
||||
.ok()
|
||||
.copied()
|
||||
}
|
||||
|
||||
if let Some(interactable) = entity_target
|
||||
.and_then(|t| {
|
||||
if t.distance < MAX_PICKUP_RANGE {
|
||||
Interactable::from_target(t, client)
|
||||
let entity = t.typed.0;
|
||||
Some(Interactable::Entity(entity))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.or_else(|| {
|
||||
collect_target
|
||||
.map(|t| Interactable::from_target(t, client))
|
||||
.map(|t| {
|
||||
get_block(client, t).map(|b| {
|
||||
Interactable::Block(b, t.position_int(), Some(Interaction::Collect))
|
||||
})
|
||||
})
|
||||
.unwrap_or(None)
|
||||
})
|
||||
.or_else(|| {
|
||||
mine_target
|
||||
.map(|t| Interactable::from_target(t, client))
|
||||
.map(|t| {
|
||||
get_block(client, t).and_then(|b| {
|
||||
// Handling edge detection. sometimes the casting (in Target mod) returns a
|
||||
// position which is actually empty, which we do not want labeled as an
|
||||
// interactable. We are only returning the mineable air
|
||||
// elements (e.g. minerals). The mineable weakrock are used
|
||||
// in the terrain selected_pos, but is not an interactable.
|
||||
if b.mine_tool().is_some() && b.is_air() {
|
||||
Some(Interactable::Block(b, t.position_int(), None))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.unwrap_or(None)
|
||||
})
|
||||
{
|
||||
|
@ -50,7 +50,7 @@ use crate::{
|
||||
use hashbrown::HashMap;
|
||||
use interactable::{select_interactable, Interactable};
|
||||
use settings_change::Language::ChangeLanguage;
|
||||
use target::{targets_under_cursor, Target, TargetType};
|
||||
use target::{targets_under_cursor, Target};
|
||||
#[cfg(feature = "egui-ui")]
|
||||
use voxygen_egui::EguiDebugInfo;
|
||||
|
||||
@ -425,16 +425,16 @@ impl PlayState for SessionState {
|
||||
|
||||
drop(client);
|
||||
|
||||
let is_nearest_target = |target: Option<Target>| {
|
||||
fn is_nearest_target<T>(shortest_dist: f32, target: Option<Target<T>>) -> bool {
|
||||
target
|
||||
.map(|t| (t.distance <= shortest_dist))
|
||||
.unwrap_or(false)
|
||||
};
|
||||
}
|
||||
|
||||
// Only highlight terrain blocks which can be interacted with
|
||||
if is_mining && is_nearest_target(mine_target) {
|
||||
if is_mining && is_nearest_target(shortest_dist, mine_target) {
|
||||
mine_target.map(|mt| self.scene.set_select_pos(Some(mt.position_int())));
|
||||
} else if can_build && is_nearest_target(build_target) {
|
||||
} else if can_build && is_nearest_target(shortest_dist, build_target) {
|
||||
build_target.map(|bt| self.scene.set_select_pos(Some(bt.position_int())));
|
||||
} else {
|
||||
self.scene.set_select_pos(None);
|
||||
@ -442,8 +442,7 @@ impl PlayState for SessionState {
|
||||
}
|
||||
|
||||
// Throw out distance info, it will be useful in the future
|
||||
self.target_entity = if let Some(TargetType::Entity(e)) = entity_target.map(|t| t.typed)
|
||||
{
|
||||
self.target_entity = if let Some(target::Entity(e)) = entity_target.map(|t| t.typed) {
|
||||
Some(e)
|
||||
} else {
|
||||
None
|
||||
@ -472,11 +471,14 @@ impl PlayState for SessionState {
|
||||
let mut client = self.client.borrow_mut();
|
||||
// Mine and build targets can be the same block. make building take
|
||||
// precedence.
|
||||
if state && can_build && is_nearest_target(build_target) {
|
||||
if state
|
||||
&& can_build
|
||||
&& is_nearest_target(shortest_dist, build_target)
|
||||
{
|
||||
self.inputs.select_pos = build_target.map(|t| t.position);
|
||||
client.remove_block(build_target.unwrap().position_int());
|
||||
} else {
|
||||
if is_mining && is_nearest_target(mine_target) {
|
||||
if is_mining && is_nearest_target(shortest_dist, mine_target) {
|
||||
self.inputs.select_pos = mine_target.map(|t| t.position);
|
||||
}
|
||||
client.handle_input(
|
||||
@ -489,7 +491,10 @@ impl PlayState for SessionState {
|
||||
},
|
||||
GameInput::Secondary => {
|
||||
let mut client = self.client.borrow_mut();
|
||||
if state && can_build && is_nearest_target(build_target) {
|
||||
if state
|
||||
&& can_build
|
||||
&& is_nearest_target(shortest_dist, build_target)
|
||||
{
|
||||
if let Some(build_target) = build_target {
|
||||
self.inputs.select_pos = Some(build_target.position);
|
||||
client.place_block(
|
||||
|
@ -12,21 +12,25 @@ use common::{
|
||||
use common_base::span;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum TargetType {
|
||||
Build,
|
||||
Collectable,
|
||||
Entity(specs::Entity),
|
||||
Mine,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Target {
|
||||
pub typed: TargetType,
|
||||
pub struct Target<T> {
|
||||
pub typed: T,
|
||||
pub distance: f32,
|
||||
pub position: Vec3<f32>,
|
||||
}
|
||||
|
||||
impl Target {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Build;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Collectable;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Entity(pub specs::Entity);
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Mine;
|
||||
|
||||
impl<T> Target<T> {
|
||||
pub fn position_int(self) -> Vec3<i32> { self.position.map(|p| p.floor() as i32) }
|
||||
}
|
||||
|
||||
@ -41,10 +45,10 @@ pub(super) fn targets_under_cursor(
|
||||
can_build: bool,
|
||||
is_mining: bool,
|
||||
) -> (
|
||||
Option<Target>,
|
||||
Option<Target>,
|
||||
Option<Target>,
|
||||
Option<Target>,
|
||||
Option<Target<Build>>,
|
||||
Option<Target<Collectable>>,
|
||||
Option<Target<Entity>>,
|
||||
Option<Target<Mine>>,
|
||||
f32,
|
||||
) {
|
||||
span!(_guard, "targets_under_cursor");
|
||||
@ -185,7 +189,7 @@ pub(super) fn targets_under_cursor(
|
||||
let dist_to_player = player_cylinder.min_distance(target_cylinder);
|
||||
if dist_to_player < MAX_TARGET_RANGE {
|
||||
Some(Target {
|
||||
typed: TargetType::Entity(*e),
|
||||
typed: Entity(*e),
|
||||
position: p,
|
||||
distance: dist_to_player,
|
||||
})
|
||||
@ -194,7 +198,7 @@ pub(super) fn targets_under_cursor(
|
||||
|
||||
let build_target = if let (Some(position), Some(ray)) = (solid_pos, solid_cam_ray) {
|
||||
Some(Target {
|
||||
typed: TargetType::Build,
|
||||
typed: Build,
|
||||
distance: ray.0,
|
||||
position,
|
||||
})
|
||||
@ -204,7 +208,7 @@ pub(super) fn targets_under_cursor(
|
||||
|
||||
let collect_target = if let (Some(position), Some(ray)) = (collect_pos, collect_cam_ray) {
|
||||
Some(Target {
|
||||
typed: TargetType::Collectable,
|
||||
typed: Collectable,
|
||||
distance: ray.0,
|
||||
position,
|
||||
})
|
||||
@ -214,7 +218,7 @@ pub(super) fn targets_under_cursor(
|
||||
|
||||
let mine_target = if let (Some(position), Some(ray)) = (mine_pos, mine_cam_ray) {
|
||||
Some(Target {
|
||||
typed: TargetType::Mine,
|
||||
typed: Mine,
|
||||
distance: ray.0,
|
||||
position,
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user