diff options
Diffstat (limited to 'src/client/editor.rs')
| -rw-r--r-- | src/client/editor.rs | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/src/client/editor.rs b/src/client/editor.rs new file mode 100644 index 0000000..14f25ef --- /dev/null +++ b/src/client/editor.rs @@ -0,0 +1,228 @@ +//! 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, + ) { + 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)) => match self.map.get_room_mut(id) { + Some(mark) => mark.set_room(new_room), + None => { + self.map.add_room(id, new_room); + } + }, + Cargo::SetIcon((id, new_icon)) => match self.map.get_icon_mut(id) { + Some(mark) => mark.set_icon(new_icon), + None => { + self.map.add_icon(id, new_icon); + } + }, + Cargo::SetWall((id, new_wall)) => match self.map.get_wall_mut(id) { + Some(mark) => mark.set_wall(new_wall), + None => { + self.map.add_wall(id, new_wall); + } + }, + Cargo::ClearAll => self.map.clear(), + Cargo::Remove(id) => { + self.map.remove(id); + } + Cargo::AddMapData(_) => unimplemented!(), + Cargo::UpdateMapData(_) => unimplemented!(), + Cargo::AddIcon(_) + | Cargo::AddRoom(_) + | Cargo::AddWall(_) + | Cargo::ApplyMatrix(_) => { + error!("Packet is only valid in Client -> Server direction") + } + } + } + } + + /// 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 + } +} |
