diff options
Diffstat (limited to 'src/client/editor.rs')
| -rw-r--r-- | src/client/editor.rs | 189 |
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 + } +} |
