diff --git a/voxygen/src/session/interactable.rs b/voxygen/src/session/interactable.rs
index 36a4dc72ca..54ac3dcfbb 100644
--- a/voxygen/src/session/interactable.rs
+++ b/voxygen/src/session/interactable.rs
@@ -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>();
diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs
index 51ec4cf5c2..48010786fb 100644
--- a/voxygen/src/session/mod.rs
+++ b/voxygen/src/session/mod.rs
@@ -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,
diff --git a/voxygen/src/session/target.rs b/voxygen/src/session/target.rs
index a79208579a..cef45106cf 100644
--- a/voxygen/src/session/target.rs
+++ b/voxygen/src/session/target.rs
@@ -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.