//! 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, Binding)>, active: ToolType, config: Config, server: Connection, } 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, ) -> Self { let map = Map::new(rl, rlt); let mut tools: HashMap, 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, ) { self.poll_net(); // 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); } fn poll_net(&mut self) { while let Some(cargo) = self.server().next_packet() { match cargo { Cargo::SetRoom((id, new_room)) => { if let Some(mark) = self.map.get_room_mut(id) { mark.set_room(new_room); } else { self.map.add_room(id, new_room); } } other => error!("Unknown packet received: {:?}", other), } } } /// 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 { &self.server } /// Get the server this editor is connected to mutably. pub fn server_mut(&mut self) -> &mut Connection { &mut self.server } }