aboutsummaryrefslogtreecommitdiff
path: root/src/client/tool
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/tool')
-rw-r--r--src/client/tool/deletion_tool.rs82
-rw-r--r--src/client/tool/icon_tool.rs88
-rw-r--r--src/client/tool/mod.rs98
-rw-r--r--src/client/tool/polygon_room_tool.rs141
-rw-r--r--src/client/tool/rect_room_tool.rs91
-rw-r--r--src/client/tool/selection_tool.rs74
-rw-r--r--src/client/tool/wall_tool.rs76
7 files changed, 650 insertions, 0 deletions
diff --git a/src/client/tool/deletion_tool.rs b/src/client/tool/deletion_tool.rs
new file mode 100644
index 0000000..3095ff5
--- /dev/null
+++ b/src/client/tool/deletion_tool.rs
@@ -0,0 +1,82 @@
+//! A meta tool for selecting parts of a map and removing them in a single operation.
+//!
+//! The user can draw a rectangle, which currently must have it's side parallel to the x and y-axes
+//! of the world. With the first node placement, the mode is started, while the second placement would
+//! finish the process and delete all elements that are *completely* contained in the rectangle
+//! (partially contained items are not deleted) or abort it, in which case the selection is removed
+//! and nothing is deleted.
+
+use super::Tool;
+use crate::client::colours::DEFAULT_COLOURS;
+use crate::client::map::Map;
+use crate::client::transform::Transform;
+use crate::client::Connection;
+use crate::math::{ExactSurface, Rect, Vec2};
+use crate::net::Cargo;
+use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle};
+
+/// The deletion tool itself.
+pub struct DeletionTool {
+ deletion_rect: Option<(Vec2<f64>, Vec2<f64>)>,
+}
+
+impl DeletionTool {
+ /// Create a new deletion tool, there should only be one deletion tool and it should be created
+ /// by the editor.
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> Self {
+ Self {
+ deletion_rect: None,
+ }
+ }
+}
+
+fn delete_rect((pos1, pos2): (&Vec2<f64>, &Vec2<f64>), map: &Map, server: &Connection<Cargo>) {
+ let bounds = Rect::bounding_rect(*pos1, *pos2);
+
+ for (id, e) in map.elements() {
+ if bounds.contains_rect(&e.as_component().bounding_rect()) {
+ server.send(Cargo::Remove(id));
+ }
+ }
+}
+
+impl Tool for DeletionTool {
+ fn deactivate(&mut self) {
+ self.deletion_rect = None;
+ }
+
+ fn update(&mut self, _map: &Map, mouse_pos_m: &Vec2<f64>) {
+ if let Some((_, ref mut pos2)) = &mut self.deletion_rect {
+ *pos2 = *mouse_pos_m;
+ }
+ }
+
+ fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) {
+ if let Some((pos1, pos2)) = self.deletion_rect {
+ let rect_px = transform.rect_m_to_px(&Rect::bounding_rect(pos1, pos2));
+ rld.draw_rectangle_rec(rect_px, DEFAULT_COLOURS.deletion_rect);
+ rld.draw_rectangle_lines_ex(rect_px, 4, DEFAULT_COLOURS.deletion_rect_outline);
+ }
+ }
+
+ fn place_single(&mut self, map: &mut Map, server: &Connection<Cargo>, mouse_pos_m: &Vec2<f64>) {
+ if let Some((pos1, pos2)) = self.deletion_rect {
+ delete_rect((&pos1, &pos2), &map, server);
+ self.deletion_rect = None;
+ } else {
+ self.deletion_rect = Some((*mouse_pos_m, *mouse_pos_m));
+ }
+ }
+
+ fn finish(&mut self, map: &mut Map, server: &Connection<Cargo>) {
+ if let Some((pos1, pos2)) = self.deletion_rect {
+ delete_rect((&pos1, &pos2), &map, server);
+ self.deletion_rect = None;
+ }
+ }
+
+ fn abort(&mut self) {
+ self.deletion_rect = None;
+ }
+}
diff --git a/src/client/tool/icon_tool.rs b/src/client/tool/icon_tool.rs
new file mode 100644
index 0000000..caf9d60
--- /dev/null
+++ b/src/client/tool/icon_tool.rs
@@ -0,0 +1,88 @@
+//! Tool for creating icons. For explanation of icons, please see
+//! [the icon module](crate::map::icon).
+
+use crate::client::config::IconToolBinds;
+use crate::client::input::Input;
+use crate::client::map::{icon_texture_manager::IconTextureManager, IconMark, Map, Mappable};
+use crate::client::tool::Tool;
+use crate::client::transform::Transform;
+use crate::math::Vec2;
+use crate::net::Cargo;
+use crate::net::Connection;
+use raylib::core::drawing::RaylibDrawHandle;
+use std::ops::Deref;
+use std::rc::Rc;
+
+/// The icon tool itself.
+pub struct IconTool {
+ keybindings: IconToolBinds,
+ /// Saves whether the IconTool is the currently active tool or not.
+ active: bool,
+ /// The information of the icon that should be placed / is currently being placed, if it
+ /// exists.
+ current_icon: IconMark,
+ textures: Rc<IconTextureManager>,
+}
+
+impl IconTool {
+ /// Create a new icon tool that renders icons with the provided icon renderer. There should only
+ /// be one instance of the tool for the program, which should be created in the editor.
+ pub fn new(keybindings: IconToolBinds, textures: Rc<IconTextureManager>) -> Self {
+ Self {
+ keybindings,
+ active: false,
+ current_icon: IconMark::new(0, Vec2::default(), 0., textures.clone()),
+ textures,
+ }
+ }
+}
+
+impl Tool for IconTool {
+ fn activate(&mut self) {
+ self.active = true;
+ }
+
+ fn deactivate(&mut self) {
+ self.active = false;
+ }
+
+ fn update(&mut self, _map: &Map, mouse_pos_m: &Vec2<f64>) {
+ self.current_icon.position = *mouse_pos_m;
+ }
+
+ fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) {
+ if self.active {
+ self.current_icon.draw(rld, transform);
+ }
+ }
+
+ fn place_single(
+ &mut self,
+ _map: &mut Map,
+ server: &Connection<Cargo>,
+ _mouse_pos_m: &Vec2<f64>,
+ ) {
+ server.send(Cargo::AddIcon(self.current_icon.deref().clone()));
+ }
+
+ fn handle_custom_bindings(
+ &mut self,
+ _map: &mut Map,
+ _server: &Connection<Cargo>,
+ input: &mut Input,
+ ) {
+ if input.poll_global(&self.keybindings.next) {
+ self.current_icon.id = (self.current_icon.id + 1) % self.textures.num_icons();
+ }
+ if input.poll_global(&self.keybindings.previous) {
+ self.current_icon.id =
+ (self.current_icon.id + self.textures.num_icons() - 1) % self.textures.num_icons();
+ }
+ if input.poll_global(&self.keybindings.rotate_clockwise) {
+ self.current_icon.rotation += 45.;
+ }
+ if input.poll_global(&self.keybindings.rotate_counterclockwise) {
+ self.current_icon.rotation -= 45.;
+ }
+ }
+}
diff --git a/src/client/tool/mod.rs b/src/client/tool/mod.rs
new file mode 100644
index 0000000..08e1380
--- /dev/null
+++ b/src/client/tool/mod.rs
@@ -0,0 +1,98 @@
+//! Tools, which are user interfaces that must be specifically selected in order to do something.
+//!
+//! As stated, a tool is not simply everything that helps a user do something, think of it more as a
+//! mode which must be elected by the user to perform a task on a specific object type or a class of
+//! objects. If instead the operation is defined by the state of the program, it is not a tool, since
+//! the user didn't explicitly ask for this function to be performed, but it is rather an option
+//! that's inherent to the situation the user finds themselves in.
+
+pub mod deletion_tool;
+pub mod icon_tool;
+pub mod polygon_room_tool;
+pub mod rect_room_tool;
+pub mod selection_tool;
+pub mod wall_tool;
+
+pub use deletion_tool::DeletionTool;
+pub use icon_tool::IconTool;
+pub use polygon_room_tool::PolygonRoomTool;
+pub use rect_room_tool::RectRoomTool;
+pub use selection_tool::SelectionTool;
+pub use wall_tool::WallTool;
+
+use crate::client::input::Input;
+use crate::client::map::Map;
+use crate::client::transform::Transform;
+use crate::client::Connection;
+use crate::math::Vec2;
+use crate::net::Cargo;
+use raylib::core::drawing::RaylibDrawHandle;
+
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+#[repr(u8)]
+/// The types of tools available in graf karto. For information about the tool itself, please see the
+/// referenced Tool's documentation.
+pub enum ToolType {
+ /// See [RectRoomTool] for information on this tool.
+ RectRoomTool,
+ /// See [PolygonRoomTool] for information on this tool.
+ PolygonRoomTool,
+ /// See [WallTool] for information on this tool.
+ WallTool,
+ /// See [IconTool] for information on this tool.
+ IconTool,
+ /// See [DeletionTool] for information on this tool.
+ DeletionTool,
+ /// See [SelectionTool] for information on this tool.
+ SelectionTool,
+ /// Not a real tool but used to know how many tools are available. New tools must be added
+ /// above this variant.
+ // TODO: Since we now use a hash map in the editor, check if this is still necessary at all.
+ NumTools,
+}
+
+/// Base trait for tools. A tool is something that performs a specific action on one or more types of
+/// elements. It must be selected in order to be active. For this reason, the selection tool is a
+/// tool (it must be selected from the toolbox), but the dimension indicator for instance is not,
+/// since it is automatically updated when applicable.
+pub trait Tool {
+ /// Code that needs to be called when this Tool is activated or reactivated goes here.
+ fn activate(&mut self) {}
+ /// Cleanup that needs to be done when the user switches from this tool to something else goes here.
+ fn deactivate(&mut self) {}
+
+ /// Called on each frame when this tool is active.
+ fn update(&mut self, map: &Map, mouse_pos_m: &Vec2<f64>);
+
+ /// Draw the contents of this tool.
+ fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform);
+
+ /// Generic keybinding.
+ /// Code to place a single node for this tool.
+ fn place_single(
+ &mut self,
+ _map: &mut Map,
+ _server: &Connection<Cargo>,
+ _mouse_pos_m: &Vec2<f64>,
+ ) {
+ }
+
+ /// Generic keybinding.
+ /// Code to finish whatever one is doing with this tool currently and trying to apply the
+ /// changes to the map data.
+ fn finish(&mut self, _map: &mut Map, _server: &Connection<Cargo>) {}
+
+ /// Generic keybinding.
+ /// Stop whatever one is doing with this tool and do not apply any changes to the map data.
+ fn abort(&mut self) {}
+
+ /// If there are any additional keybindings that need to be handled by this tool, these can be
+ /// handled here.
+ fn handle_custom_bindings(
+ &mut self,
+ _map: &mut Map,
+ _server: &Connection<Cargo>,
+ _input: &mut Input,
+ ) {
+ }
+}
diff --git a/src/client/tool/polygon_room_tool.rs b/src/client/tool/polygon_room_tool.rs
new file mode 100644
index 0000000..63456cc
--- /dev/null
+++ b/src/client/tool/polygon_room_tool.rs
@@ -0,0 +1,141 @@
+//! Tool to create rooms in the shape of generic polygons.
+
+use super::Tool;
+use crate::client::colours::DEFAULT_COLOURS;
+use crate::client::map::Map;
+use crate::client::transform::Transform;
+use crate::client::FLOAT_MARGIN;
+use crate::math::{self, PolygonGraph, Vec2};
+use crate::net::{Cargo, Connection};
+use crate::world::Room;
+use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle};
+
+/// The tool itself.
+pub struct PolygonRoomTool {
+ unfinished_room: Option<(PolygonGraph<f64>, Vec2<f64>)>,
+ last_mouse_pos_m: Vec2<f64>,
+}
+
+impl PolygonRoomTool {
+ /// Create a new polygon room tool. There should be only one instance and it should be created
+ /// in the editor.
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> Self {
+ Self {
+ unfinished_room: None,
+ last_mouse_pos_m: Vec2::new(0., 0.),
+ }
+ }
+
+ /* Helper function to try and finish the currently drawn polygon. If successful, it will add it
+ * to the map, clear the currently drawn polygon and return bool. Otherwise it will leave the
+ * unfinished polygon as is and return false without pushing anything.
+ */
+ fn try_push(&mut self, server: &Connection<Cargo>) -> bool {
+ if self.unfinished_room.is_none() {
+ return false;
+ }
+
+ match self
+ .unfinished_room
+ .as_ref()
+ .unwrap()
+ .0
+ .clone()
+ .bounding_polygon(FLOAT_MARGIN)
+ {
+ Some(polygon) => {
+ server.send(Cargo::AddRoom(Room::new(polygon)));
+ self.unfinished_room = None;
+ true
+ }
+ None => false,
+ }
+ }
+}
+
+impl Tool for PolygonRoomTool {
+ fn deactivate(&mut self) {
+ self.unfinished_room = None;
+ }
+
+ fn update(&mut self, _map: &Map, mouse_pos_m: &Vec2<f64>) {
+ // Update the last mouse position that was seen for later use.
+ self.last_mouse_pos_m = *mouse_pos_m;
+ }
+
+ fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) {
+ if let Some((graph, last_node)) = &self.unfinished_room {
+ /* To turn the graph into a polygon, we need a copy, might as well do
+ * it now, so we can add the working corner to it.
+ */
+ let mut graph = graph.clone();
+
+ // Add the current mouse position as the next position if possible.
+ graph.add_edge(&last_node, &self.last_mouse_pos_m);
+
+ if graph.num_nodes() <= 1 {
+ // Only able to draw a point
+ rld.draw_circle_v(
+ transform.point_m_to_px(&self.last_mouse_pos_m),
+ transform.length_m_to_px(0.1) as f32,
+ DEFAULT_COLOURS.room_selected,
+ );
+ } else if let Some(polygon) = graph.clone().bounding_polygon(FLOAT_MARGIN) {
+ let triangles = math::triangulate(polygon, FLOAT_MARGIN);
+ for triangle in triangles {
+ let triangle: [Vec2<f64>; 3] = triangle.into();
+ rld.draw_triangle(
+ transform.point_m_to_px(&triangle[0]),
+ transform.point_m_to_px(&triangle[1]),
+ transform.point_m_to_px(&triangle[2]),
+ DEFAULT_COLOURS.room_selected,
+ )
+ }
+ } else {
+ // For some reason the polygon creation failed. Draw lines for the edges instead.
+ for edge in graph.edge_iter() {
+ rld.draw_line_ex(
+ transform.point_m_to_px(&edge.start),
+ transform.point_m_to_px(&edge.end),
+ transform.length_m_to_px(0.1) as f32,
+ DEFAULT_COLOURS.room_selected,
+ );
+ }
+ }
+ }
+ }
+
+ fn place_single(
+ &mut self,
+ _map: &mut Map,
+ server: &Connection<Cargo>,
+ mouse_pos_m: &Vec2<f64>,
+ ) {
+ if let Some((ref mut graph, ref mut last_placed)) = &mut self.unfinished_room {
+ // If the corner already exists in the polygon, try to finish and push it after adding the
+ // next edge.
+ let try_finish = graph.has_node(&mouse_pos_m);
+
+ // Add an edge from the last corner to the currently active position if possible.
+ if graph.add_edge(last_placed, &mouse_pos_m) {
+ *last_placed = *mouse_pos_m;
+ }
+
+ if try_finish {
+ self.try_push(server);
+ }
+ } else {
+ // Start a new unfinished polygon
+ self.unfinished_room = Some((PolygonGraph::new(), *mouse_pos_m));
+ }
+ }
+
+ fn finish(&mut self, _map: &mut Map, server: &Connection<Cargo>) {
+ self.try_push(server);
+ }
+
+ fn abort(&mut self) {
+ self.unfinished_room = None;
+ }
+}
diff --git a/src/client/tool/rect_room_tool.rs b/src/client/tool/rect_room_tool.rs
new file mode 100644
index 0000000..41f2a91
--- /dev/null
+++ b/src/client/tool/rect_room_tool.rs
@@ -0,0 +1,91 @@
+//! The rectangle room tool is a specialised tool to create rooms of rectangular shape and with the
+//! sides of the room parallel to the x and y-axes. This is often useful, when a quick room creation
+//! is necessary and the shape of the room does not have to be very special.
+
+use super::Tool;
+use crate::client::colours::DEFAULT_COLOURS;
+use crate::client::map::Map;
+use crate::client::transform::Transform;
+use crate::math::{Rect, Vec2};
+use crate::net::{Cargo, Connection};
+use crate::world::Room;
+use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle};
+
+/// The tool to create simple, rectangular rooms.
+pub struct RectRoomTool {
+ /// The rectangle that is currently being drawn by the user. Once it is finished, it will be
+ /// pushed into the room_rects.
+ unfinished_rect: Option<(Vec2<f64>, Vec2<f64>)>,
+}
+
+impl RectRoomTool {
+ /// Create a new room tool where no rooms have been drawn yet. Should be created only once per
+ /// program instance and by the editor.
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> Self {
+ Self {
+ unfinished_rect: None,
+ }
+ }
+}
+
+impl Tool for RectRoomTool {
+ fn deactivate(&mut self) {
+ self.unfinished_rect = None;
+ }
+
+ fn update(&mut self, _map: &Map, mouse_pos_m: &Vec2<f64>) {
+ if let Some((_, ref mut pos2)) = &mut self.unfinished_rect {
+ *pos2 = *mouse_pos_m;
+ }
+ }
+
+ fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) {
+ if let Some((pos1, pos2)) = self.unfinished_rect {
+ rld.draw_rectangle_rec(
+ transform.rect_m_to_px(&Rect::bounding_rect(pos1, pos2)),
+ DEFAULT_COLOURS.room_selected,
+ );
+ }
+ }
+
+ fn place_single(
+ &mut self,
+ _map: &mut Map,
+ server: &Connection<Cargo>,
+ mouse_pos_m: &Vec2<f64>,
+ ) {
+ // Try to finish the rectangle if it has been started.
+ if let Some((pos1, pos2)) = self.unfinished_rect {
+ if pos1 == pos2 {
+ warn!("Cannot place rectangle with start and endpoint being the same");
+ return;
+ }
+
+ server.send(Cargo::AddRoom(Room::new(
+ Rect::bounding_rect(pos1, pos2).into(),
+ )));
+ self.unfinished_rect = None;
+ } else {
+ self.unfinished_rect = Some((*mouse_pos_m, *mouse_pos_m));
+ }
+ }
+
+ fn finish(&mut self, _map: &mut Map, server: &Connection<Cargo>) {
+ if let Some((pos1, pos2)) = self.unfinished_rect {
+ if pos1 == pos2 {
+ warn!("Cannot place rectangle with start and endpoint being the same");
+ return;
+ }
+
+ server.send(Cargo::AddRoom(Room::new(
+ Rect::bounding_rect(pos1, pos2).into(),
+ )));
+ self.unfinished_rect = None;
+ }
+ }
+
+ fn abort(&mut self) {
+ self.unfinished_rect = None;
+ }
+}
diff --git a/src/client/tool/selection_tool.rs b/src/client/tool/selection_tool.rs
new file mode 100644
index 0000000..52c2155
--- /dev/null
+++ b/src/client/tool/selection_tool.rs
@@ -0,0 +1,74 @@
+//! Selection of items on the map.
+//!
+//! When selecting items on the map, the editor goes into a different mode than when editing a
+//! specific kind of item. Actions that are available for specific types of items become
+//! unavailable, while other actions that make use of the properties to a wide range of items
+//! become available instead.
+//! For this reason, the selection tool can be thought of as a kind of meta tool over tools.
+
+use super::Tool;
+use crate::client::colours::DEFAULT_COLOURS;
+use crate::client::map::Map;
+use crate::client::transform::Transform;
+use crate::math::{ExactSurface, Rect, Vec2};
+use crate::net::{Cargo, Connection};
+use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle};
+
+/// The selection tool makes it possible to select any item on the map when activated.
+pub struct SelectionTool {
+ selection_rect: Option<(Vec2<f64>, Vec2<f64>)>,
+}
+
+impl SelectionTool {
+ /// Create a new selection tool. There should be only one such tool per program instance and it
+ /// should be created in the editor.
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> Self {
+ Self {
+ selection_rect: None,
+ }
+ }
+}
+
+impl Tool for SelectionTool {
+ fn deactivate(&mut self) {
+ self.selection_rect = None;
+ }
+
+ fn update(&mut self, _map: &Map, mouse_pos_m: &Vec2<f64>) {
+ if let Some((_, ref mut pos2)) = &mut self.selection_rect {
+ *pos2 = *mouse_pos_m;
+ }
+ }
+
+ fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) {
+ if let Some((pos1, pos2)) = self.selection_rect {
+ let rect_px = transform.rect_m_to_px(&Rect::bounding_rect(pos1, pos2));
+ rld.draw_rectangle_rec(rect_px, DEFAULT_COLOURS.selection_rect);
+ rld.draw_rectangle_lines_ex(rect_px, 4, DEFAULT_COLOURS.selection_rect_outline);
+ }
+ }
+
+ fn place_single(
+ &mut self,
+ map: &mut Map,
+ _server: &Connection<Cargo>,
+ mouse_pos_m: &Vec2<f64>,
+ ) {
+ if let Some((pos1, pos2)) = self.selection_rect {
+ // Select all items on the map that are inside of the selection rectangle
+ let bounds = Rect::bounding_rect(pos1, pos2);
+ for (_id, element) in map.elements_mut() {
+ // TODO: Make it possible to do this additively by custom keybinding.
+ element.set_selected(bounds.contains_rect(&element.as_component().bounding_rect()));
+ }
+ self.selection_rect = None;
+ } else {
+ self.selection_rect = Some((*mouse_pos_m, *mouse_pos_m));
+ }
+ }
+
+ fn abort(&mut self) {
+ self.selection_rect = None;
+ }
+}
diff --git a/src/client/tool/wall_tool.rs b/src/client/tool/wall_tool.rs
new file mode 100644
index 0000000..857beea
--- /dev/null
+++ b/src/client/tool/wall_tool.rs
@@ -0,0 +1,76 @@
+//! Tool to create walls. For information about walls, see also
+//! [the wall module](crate::map::wall).
+
+use super::Tool;
+use crate::client::map::Map;
+use crate::client::transform::Transform;
+use crate::math::{LineSegment, Vec2};
+use crate::net::{Cargo, Connection};
+use crate::world::Wall;
+use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle};
+use raylib::ffi::{Color, Vector2};
+
+/// The wall tool to create solid barriers a player usually cannot cross.
+pub struct WallTool {
+ unfinished_wall: Option<LineSegment<f64>>,
+}
+
+impl WallTool {
+ /// Create a new wall tool. There should only be one wall tool per program instance, which should
+ /// be created inside of the editor.
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> Self {
+ Self {
+ unfinished_wall: None,
+ }
+ }
+}
+
+impl Tool for WallTool {
+ fn deactivate(&mut self) {
+ self.unfinished_wall = None;
+ }
+
+ fn update(&mut self, _map: &Map, mouse_pos_m: &Vec2<f64>) {
+ if let Some(ref mut wall) = &mut self.unfinished_wall {
+ wall.end = *mouse_pos_m;
+ }
+ }
+
+ fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) {
+ if let Some(ref wall) = self.unfinished_wall {
+ let start: Vector2 = transform.point_m_to_px(&wall.start).into();
+ let end: Vector2 = transform.point_m_to_px(&wall.end).into();
+ rld.draw_line_ex(
+ start,
+ end,
+ transform.length_m_to_px(0.1) as f32,
+ Color {
+ r: 150,
+ g: 200,
+ b: 150,
+ a: 255,
+ },
+ );
+ }
+ }
+
+ fn place_single(
+ &mut self,
+ _map: &mut Map,
+ server: &Connection<Cargo>,
+ mouse_pos_m: &Vec2<f64>,
+ ) {
+ if let Some(wall) = self.unfinished_wall.take() {
+ // Continue with the next wall straight away.
+ self.unfinished_wall = Some(LineSegment::new(wall.end, wall.end));
+ server.send(Cargo::AddWall(Wall::new(wall)));
+ } else {
+ self.unfinished_wall = Some(LineSegment::new(*mouse_pos_m, *mouse_pos_m));
+ }
+ }
+
+ fn abort(&mut self) {
+ self.unfinished_wall = None;
+ }
+}