aboutsummaryrefslogtreecommitdiff
path: root/src/client/editor.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/editor.rs')
-rw-r--r--src/client/editor.rs189
1 files changed, 189 insertions, 0 deletions
diff --git a/src/client/editor.rs b/src/client/editor.rs
new file mode 100644
index 0000000..0fb5794
--- /dev/null
+++ b/src/client/editor.rs
@@ -0,0 +1,189 @@
+//! Element creation base
+//!
+//! The actual editor environment sits here. This especially means all tools that require low-level
+//! access to the data of items currently being created. While this may be subject to change, there is
+//! currently a difference between things that are being created (inside the editor) and things that
+//! are part of the environment (the map).
+
+use crate::client::config::Config;
+use crate::client::input::{Binding, Input};
+use crate::client::map::Map;
+use crate::client::snapping::Snapper;
+use crate::client::tool::*;
+use crate::client::transform::Transform;
+use crate::net::{Cargo, Connection};
+use raylib::core::drawing::RaylibDrawHandle;
+use raylib::{RaylibHandle, RaylibThread};
+use std::collections::HashMap;
+
+/// The editor containing all tools and currently the map of the stuff that has been created.
+pub struct Editor {
+ map: Map,
+ /// HashMap that matches the ToolType with its proper activation key and of course the tool
+ /// itself.
+ tools: HashMap<ToolType, (Box<dyn Tool>, Binding)>,
+ active: ToolType,
+ config: Config,
+ server: Connection<Cargo>,
+}
+
+impl Editor {
+ /// Create a new editor with all tools necessary. There should be only one editor per program
+ /// instance.
+ pub fn new(
+ rl: &mut RaylibHandle,
+ rlt: &RaylibThread,
+ config: Config,
+ server: Connection<Cargo>,
+ ) -> Self {
+ let map = Map::new(rl, rlt);
+
+ let mut tools: HashMap<ToolType, (Box<dyn Tool>, Binding)> =
+ HashMap::with_capacity(ToolType::NumTools as usize);
+
+ tools.insert(
+ ToolType::RectRoomTool,
+ (
+ Box::new(RectRoomTool::new()),
+ config.tool_activation_binds.rect_room.clone(),
+ ),
+ );
+ tools.insert(
+ ToolType::PolygonRoomTool,
+ (
+ Box::new(PolygonRoomTool::new()),
+ config.tool_activation_binds.polygon_room.clone(),
+ ),
+ );
+ tools.insert(
+ ToolType::WallTool,
+ (
+ Box::new(WallTool::new()),
+ config.tool_activation_binds.wall.clone(),
+ ),
+ );
+ tools.insert(
+ ToolType::IconTool,
+ (
+ Box::new(IconTool::new(
+ config.icon_binds.clone(),
+ map.icon_renderer(),
+ )),
+ config.tool_activation_binds.icon.clone(),
+ ),
+ );
+ tools.insert(
+ ToolType::DeletionTool,
+ (
+ Box::new(DeletionTool::new()),
+ config.tool_activation_binds.deletion.clone(),
+ ),
+ );
+ tools.insert(
+ ToolType::SelectionTool,
+ (
+ Box::new(SelectionTool::new()),
+ config.tool_activation_binds.selection.clone(),
+ ),
+ );
+
+ assert_eq!(ToolType::NumTools as usize, tools.len());
+
+ Self {
+ map,
+ tools,
+ active: ToolType::RectRoomTool,
+ config,
+ server,
+ }
+ }
+
+ /// Get the currently active tool. Since the every tool exists only once, it is entirely indexable
+ /// by its type, which is what is actually returned.
+ pub fn active(&self) -> ToolType {
+ self.active
+ }
+
+ /// Set the currently active tool. Any process currently going on in a different tool will be
+ /// aborted.
+ pub fn set_active(&mut self, tool: ToolType) {
+ if tool != self.active {
+ self.tools.get_mut(&self.active).unwrap().0.deactivate();
+ self.active = tool;
+ self.tools
+ .get_mut(&self.active)
+ .expect("{:?} is not a Tool in the Editor. Maybe you forgot to register it?")
+ .0
+ .activate();
+ }
+ }
+
+ /// Update the internal editor data where necessary and handle selecting different tools, aswell
+ /// as updating the currently active tool. Should be called once every frame.
+ pub fn update(
+ &mut self,
+ rl: &mut RaylibHandle,
+ transform: &Transform,
+ snapper: &Snapper,
+ input: &mut Input,
+ ) {
+ // Handle keybindings for tool change
+ for (&tool_type, (_, activation_bind)) in self.tools.iter() {
+ if input.poll_global(&activation_bind) {
+ // Don't do anything if the tool does not change.
+ if tool_type == self.active {
+ break;
+ }
+
+ // Activate the tool of which the key binding has been pressed.
+ self.set_active(tool_type);
+ break;
+ }
+ }
+
+ let mouse_pos_m = transform.point_px_to_m(&rl.get_mouse_position().into());
+ let snapped_mouse_pos = snapper.snap(mouse_pos_m);
+
+ // Update the currently active tool
+ let active_tool = &mut self.tools.get_mut(&self.active).unwrap().0;
+ active_tool.update(&self.map, &snapped_mouse_pos);
+
+ // Handle common keybindings many of the tools have.
+ if input.poll_global(&self.config.tool_general_binds.place_single) {
+ active_tool.place_single(&mut self.map, &self.server, &snapped_mouse_pos);
+ }
+ if input.poll_global(&self.config.tool_general_binds.finish) {
+ active_tool.finish(&mut self.map, &self.server);
+ }
+ if input.poll_global(&self.config.tool_general_binds.abort) {
+ active_tool.abort();
+ }
+
+ // Handle custom keybindings in case the tool has any.
+ active_tool.handle_custom_bindings(&mut self.map, &self.server, input);
+ }
+
+ /// Draw all tools and in case of the active tool also what is currently being edited by it, if
+ /// that exists.
+ pub fn draw_tools(&self, rld: &mut RaylibDrawHandle, transform: &Transform) {
+ for (tool, _) in self.tools.values() {
+ tool.draw(rld, transform);
+ }
+ }
+
+ /// Get the world containing all finished elements.
+ pub fn map(&self) -> &Map {
+ &self.map
+ }
+
+ /// Get the server this editor is connected to. Even if the program is executed locally, this will
+ /// return a server, since one must have been started locally.
+ pub fn server(&self) -> &Connection<Cargo> {
+ &self.server
+ }
+
+ /// Get the server this editor is connected to mutably.
+ pub fn server_mut(&mut self) -> &mut Connection<Cargo> {
+ &mut self.server
+ }
+}