changes per MR feedback. simplier Option handling with zip, filter, and fewr unwraps. use kind keyword (not typed). if lets when possible. additional syntax cleanup

This commit is contained in:
anomaluridae 2021-08-17 22:08:16 -07:00
parent 8b83b48b9b
commit f60bd80cc2
3 changed files with 68 additions and 81 deletions

View File

@ -3,7 +3,7 @@ use specs::{Join, WorldExt};
use vek::*;
use super::target::{self, Target};
use client::{self, Client};
use client::Client;
use common::{
comp,
consts::MAX_PICKUP_RANGE,
@ -61,42 +61,39 @@ pub(super) fn select_interactable(
if let Some(interactable) = entity_target
.and_then(|t| {
if t.distance < MAX_PICKUP_RANGE {
let entity = t.typed.0;
let entity = t.kind.0;
Some(Interactable::Entity(entity))
} else {
None
}
})
.or_else(|| {
collect_target
.map(|t| {
get_block(client, t).map(|b| {
Interactable::Block(b, t.position_int(), Some(Interaction::Collect))
})
})
.unwrap_or(None)
collect_target.and_then(|t| {
get_block(client, t)
.map(|b| Interactable::Block(b, t.position_int(), Some(Interaction::Collect)))
})
})
.or_else(|| {
mine_target
.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
}
})
mine_target.and_then(|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)
})
})
{
Some(interactable)
} else {
// If there are no directly targeted interactables select the closest one if any
// are in range
let ecs = client.state().ecs();
let player_entity = client.entity();
let positions = ecs.read_storage::<comp::Pos>();

View File

@ -425,28 +425,26 @@ impl PlayState for SessionState {
drop(client);
fn is_nearest_target<T>(shortest_dist: f32, target: Option<Target<T>>) -> bool {
target
.map(|t| (t.distance <= shortest_dist))
.unwrap_or(false)
fn is_nearest_target<T>(shortest_dist: f32, target: Target<T>) -> bool {
target.distance <= shortest_dist
}
// Only highlight terrain blocks which can be interacted with
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(shortest_dist, build_target) {
build_target.map(|bt| self.scene.set_select_pos(Some(bt.position_int())));
if let Some(mt) =
mine_target.filter(|mt| is_mining && is_nearest_target(shortest_dist, *mt))
{
self.scene.set_select_pos(Some(mt.position_int()));
} else if let Some(bt) =
build_target.filter(|bt| can_build && is_nearest_target(shortest_dist, *bt))
{
self.scene.set_select_pos(Some(bt.position_int()));
} else {
self.scene.set_select_pos(None);
self.inputs.select_pos = entity_target.map(|et| et.position);
}
// Throw out distance info, it will be useful in the future
self.target_entity = if let Some(target::Entity(e)) = entity_target.map(|t| t.typed) {
Some(e)
} else {
None
};
self.target_entity = entity_target.map(|t| t.kind.0);
// Handle window events.
for event in events {
@ -471,15 +469,17 @@ 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(shortest_dist, build_target)
{
self.inputs.select_pos = build_target.map(|t| t.position);
client.remove_block(build_target.unwrap().position_int());
// Order of precedence: build, then mining, then attack.
if let Some(build_target) = build_target.filter(|bt| {
state && can_build && is_nearest_target(shortest_dist, *bt)
}) {
self.inputs.select_pos = Some(build_target.position);
client.remove_block(build_target.position_int());
} else {
if is_mining && is_nearest_target(shortest_dist, mine_target) {
self.inputs.select_pos = mine_target.map(|t| t.position);
if let Some(mine_target) = mine_target.filter(|mt| {
is_mining && is_nearest_target(shortest_dist, *mt)
}) {
self.inputs.select_pos = Some(mine_target.position);
}
client.handle_input(
InputKind::Primary,
@ -491,17 +491,14 @@ impl PlayState for SessionState {
},
GameInput::Secondary => {
let mut client = self.client.borrow_mut();
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(
build_target.position_int(),
self.selected_block,
);
}
if let Some(build_target) = build_target.filter(|bt| {
state && can_build && is_nearest_target(shortest_dist, *bt)
}) {
self.inputs.select_pos = Some(build_target.position);
client.place_block(
build_target.position_int(),
self.selected_block,
);
} else {
client.handle_input(
InputKind::Secondary,

View File

@ -1,3 +1,4 @@
use ordered_float::OrderedFloat;
use specs::{Join, WorldExt};
use vek::*;
@ -13,7 +14,7 @@ use common_base::span;
#[derive(Clone, Copy, Debug)]
pub struct Target<T> {
pub typed: T,
pub kind: T,
pub distance: f32,
pub position: Vec3<f32>,
}
@ -98,9 +99,7 @@ pub(super) fn targets_under_cursor(
let (mine_pos, _, mine_cam_ray) = is_mining
.then(|| find_pos(|b: Block| b.mine_tool().is_some()))
.unwrap_or((None, None, None));
let (_, solid_pos, solid_cam_ray) = can_build
.then(|| find_pos(|b: Block| b.is_solid()))
.unwrap_or((None, None, None));
let (_, solid_pos, solid_cam_ray) = find_pos(|b: Block| b.is_solid());
// Find shortest cam_dist of non-entity targets.
// Note that some of these targets can technically be in Air, such as the
@ -117,7 +116,7 @@ pub(super) fn targets_under_cursor(
Some((d, Ok(Some(_)))) => Some(d),
_ => None,
})
.min_by(|d1, d2| d1.partial_cmp(d2).unwrap())
.min_by(|d1, d2| OrderedFloat(*d1).cmp(&OrderedFloat(*d2)))
.unwrap_or(MAX_PICKUP_RANGE);
// See if ray hits entities
@ -189,16 +188,16 @@ 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: Entity(*e),
kind: Entity(*e),
position: p,
distance: dist_to_player,
})
} else { None }
});
let build_target = if let (Some(position), Some(ray)) = (solid_pos, solid_cam_ray) {
Some(Target {
typed: Build,
let build_target = if can_build {
solid_pos.zip(solid_cam_ray).map(|(position, ray)| Target {
kind: Build,
distance: ray.0,
position,
})
@ -206,25 +205,19 @@ pub(super) fn targets_under_cursor(
None
};
let collect_target = if let (Some(position), Some(ray)) = (collect_pos, collect_cam_ray) {
Some(Target {
typed: Collectable,
let collect_target = collect_pos
.zip(collect_cam_ray)
.map(|(position, ray)| Target {
kind: Collectable,
distance: ray.0,
position,
})
} else {
None
};
});
let mine_target = if let (Some(position), Some(ray)) = (mine_pos, mine_cam_ray) {
Some(Target {
typed: Mine,
distance: ray.0,
position,
})
} else {
None
};
let mine_target = mine_pos.zip(mine_cam_ray).map(|(position, ray)| Target {
kind: Mine,
distance: ray.0,
position,
});
// Return multiple possible targets
// GameInput events determine which target to use.