diff --git a/editor/resources/die.png b/editor/resources/die.png new file mode 100644 index 000000000..b10b1790a Binary files /dev/null and b/editor/resources/die.png differ diff --git a/editor/resources/flip_x.png b/editor/resources/flip_x.png new file mode 100644 index 000000000..377fb6325 Binary files /dev/null and b/editor/resources/flip_x.png differ diff --git a/editor/resources/flip_y.png b/editor/resources/flip_y.png new file mode 100644 index 000000000..220439900 Binary files /dev/null and b/editor/resources/flip_y.png differ diff --git a/editor/resources/palette.png b/editor/resources/palette.png new file mode 100644 index 000000000..a09d981c4 Binary files /dev/null and b/editor/resources/palette.png differ diff --git a/editor/resources/turn_left.png b/editor/resources/turn_left.png new file mode 100644 index 000000000..0f98456d2 Binary files /dev/null and b/editor/resources/turn_left.png differ diff --git a/editor/resources/turn_right.png b/editor/resources/turn_right.png new file mode 100644 index 000000000..f8a2972c4 Binary files /dev/null and b/editor/resources/turn_right.png differ diff --git a/editor/src/asset/item.rs b/editor/src/asset/item.rs index 2b431d4db..4e337a937 100644 --- a/editor/src/asset/item.rs +++ b/editor/src/asset/item.rs @@ -18,6 +18,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +use fyrox::core::log::Log; + use crate::{ asset::open_in_explorer, fyrox::{ @@ -41,7 +43,7 @@ use crate::{ UserInterface, }, material::Material, - scene::tilemap::tileset::TileSet, + scene::tilemap::{brush::TileMapBrush, tileset::TileSet}, }, message::MessageSender, Message, @@ -134,19 +136,31 @@ impl AssetItem { if self .path .extension() - .map_or(false, |ext| ext == "rgs" || ext == "ui") + .is_some_and(|ext| ext == "rgs" || ext == "ui") { sender.send(Message::LoadScene(self.path.clone())); - } else if self.path.extension().map_or(false, |ext| ext == "material") { + } else if self.path.extension().is_some_and(|ext| ext == "material") { if let Ok(path) = make_relative_path(&self.path) { if let Ok(material) = block_on(resource_manager.request::(path)) { sender.send(Message::OpenMaterialEditor(material)); } } - } else if self.path.extension().map_or(false, |ext| ext == "tileset") { + } else if self.path.extension().is_some_and(|ext| ext == "tileset") { + if let Ok(path) = make_relative_path(&self.path) { + match block_on(resource_manager.request::(path)) { + Ok(tile_set) => sender.send(Message::OpenTileSetEditor(tile_set)), + Err(err) => Log::err(format!("Open tileset error: {err:?}")), + } + } + } else if self + .path + .extension() + .is_some_and(|ext| ext == "tile_map_brush") + { if let Ok(path) = make_relative_path(&self.path) { - if let Ok(tile_set) = block_on(resource_manager.request::(path)) { - sender.send(Message::OpenTileSetEditor(tile_set)); + match block_on(resource_manager.request::(path)) { + Ok(brush) => sender.send(Message::OpenTileMapBrushEditor(brush)), + Err(err) => Log::err(format!("Open tile_map_brush error: {err:?}")), } } } else if self.path.is_dir() { diff --git a/editor/src/asset/mod.rs b/editor/src/asset/mod.rs index d307fdb62..dfc2abae9 100644 --- a/editor/src/asset/mod.rs +++ b/editor/src/asset/mod.rs @@ -953,7 +953,7 @@ impl AssetBrowser { folders.push(entry_path); } else if entry_path .extension() - .map_or(false, |ext| is_supported_resource(ext, resource_manager)) + .is_some_and(|ext| is_supported_resource(ext, resource_manager)) { resources.push(entry_path); } diff --git a/editor/src/interaction/mod.rs b/editor/src/interaction/mod.rs index 8824b54db..066f57718 100644 --- a/editor/src/interaction/mod.rs +++ b/editor/src/interaction/mod.rs @@ -115,6 +115,33 @@ pub trait InteractionMode: BaseInteractionMode { settings: &Settings, ); + /// Called when the mouse enters the scene viewer while this interaction mode is active. + #[allow(unused_variables)] + fn on_mouse_enter( + &mut self, + editor_selection: &Selection, + controller: &mut dyn SceneController, + engine: &mut Engine, + frame_size: Vector2, + settings: &Settings, + ) { + } + + /// Called when the mouse leaves the scene viewer while this interaction mode is active. + /// - `mouse_position`: The position of the mouse relative to the scene viewer, with (0,0) being the left-top corner. + /// - `editor_selection`: The currently selected object in the editor. + #[allow(unused_variables)] + fn on_mouse_leave( + &mut self, + mouse_position: Vector2, + editor_selection: &Selection, + controller: &mut dyn SceneController, + engine: &mut Engine, + frame_size: Vector2, + settings: &Settings, + ) { + } + fn update( &mut self, #[allow(unused_variables)] editor_selection: &Selection, diff --git a/editor/src/lib.rs b/editor/src/lib.rs index cfa9b5ece..f8223cab3 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -272,9 +272,8 @@ pub fn create_terrain_layer_material() -> MaterialResource { pub fn make_scene_file_filter() -> Filter { Filter::new(|p: &Path| { p.is_dir() - || p.extension().map_or(false, |ext| { - matches!(ext.to_string_lossy().as_ref(), "rgs" | "ui") - }) + || p.extension() + .is_some_and(|ext| matches!(ext.to_string_lossy().as_ref(), "rgs" | "ui")) }) } @@ -1812,7 +1811,7 @@ impl Editor { || self .scenes .current_scene_controller_ref() - .map_or(false, |s| s.is_interacting()) + .is_some_and(|s| s.is_interacting()) || stays_active } @@ -1858,7 +1857,7 @@ impl Editor { fn load_scene(&mut self, scene_path: PathBuf) { for entry in self.scenes.entries.iter() { - if entry.path.as_ref().map_or(false, |p| p == &scene_path) { + if entry.path.as_ref() == Some(&scene_path) { self.set_current_scene(entry.id); return; } diff --git a/editor/src/message.rs b/editor/src/message.rs index 1e45d1306..e5267ec16 100644 --- a/editor/src/message.rs +++ b/editor/src/message.rs @@ -33,7 +33,7 @@ use crate::{ scene::Selection, SaveSceneConfirmationDialogAction, }; -use fyrox::scene::tilemap::tileset::TileSetResource; +use fyrox::scene::tilemap::{brush::TileMapBrushResource, tileset::TileSetResource}; use std::sync::mpsc::channel; use std::{path::PathBuf, sync::mpsc::Sender}; @@ -66,6 +66,7 @@ pub enum Message { OpenAbsmEditor, OpenMaterialEditor(MaterialResource), OpenTileSetEditor(TileSetResource), + OpenTileMapBrushEditor(TileMapBrushResource), OpenNodeRemovalDialog, ShowInAssetBrowser(PathBuf), LocateObject { diff --git a/editor/src/plugins/absm/canvas.rs b/editor/src/plugins/absm/canvas.rs index 07c55fd8d..27369a506 100644 --- a/editor/src/plugins/absm/canvas.rs +++ b/editor/src/plugins/absm/canvas.rs @@ -211,7 +211,7 @@ impl AbsmCanvas { { if ui .try_get(node_handle) - .map_or(false, |n| n.has_component::()) + .is_some_and(|n| n.has_component::()) { return node_handle; } diff --git a/editor/src/plugins/absm/state_graph/mod.rs b/editor/src/plugins/absm/state_graph/mod.rs index c9dadd279..e82e9e058 100644 --- a/editor/src/plugins/absm/state_graph/mod.rs +++ b/editor/src/plugins/absm/state_graph/mod.rs @@ -132,7 +132,7 @@ impl StateGraphViewer { if let Some(view_handle) = ui.node(self.canvas).children().iter().cloned().find(|c| { ui.node(*c) .query_component::() - .map_or(false, |transition_view_ref| { + .is_some_and(|transition_view_ref| { transition == transition_view_ref.model_handle.into() }) }) { diff --git a/editor/src/plugins/absm/state_viewer/mod.rs b/editor/src/plugins/absm/state_viewer/mod.rs index 5591d6275..623bcc619 100644 --- a/editor/src/plugins/absm/state_viewer/mod.rs +++ b/editor/src/plugins/absm/state_viewer/mod.rs @@ -446,7 +446,7 @@ impl StateViewer { if machine_layer .nodes() .try_borrow(pose_node.model_handle) - .map_or(false, |node| node.parent_state == self.state.into()) + .is_some_and(|node| node.parent_state == self.state.into()) { true } else { diff --git a/editor/src/plugins/absm/toolbar.rs b/editor/src/plugins/absm/toolbar.rs index 10d18d49d..fa349c814 100644 --- a/editor/src/plugins/absm/toolbar.rs +++ b/editor/src/plugins/absm/toolbar.rs @@ -268,7 +268,7 @@ impl Toolbar { .filter(|n| { graph .try_get(*n) - .map_or(false, |n| !unique_nodes.contains(&n.parent())) + .is_some_and(|n| !unique_nodes.contains(&n.parent())) }) .collect::>(); diff --git a/editor/src/plugins/animation/toolbar.rs b/editor/src/plugins/animation/toolbar.rs index 23f24f94d..1ed97778f 100644 --- a/editor/src/plugins/animation/toolbar.rs +++ b/editor/src/plugins/animation/toolbar.rs @@ -825,12 +825,12 @@ impl Toolbar { .with_title(WindowTitle::text("Select Animation To Import")), ) .with_filter(Filter::new(|p: &Path| { + // TODO: Here we allow importing only FBX files, but they can contain + // multiple animations and it might be good to also add animation selector + // that will be used to select a particular animation to import. p.is_dir() - || p.extension().map_or(false, |ext| - // TODO: Here we allow importing only FBX files, but they can contain - // multiple animations and it might be good to also add animation selector - // that will be used to select a particular animation to import. - ext.to_string_lossy().as_ref() == "fbx") + || p.extension() + .is_some_and(|ext| ext.to_string_lossy().as_ref() == "fbx") })) .build(ctx); diff --git a/editor/src/plugins/inspector/editors/mod.rs b/editor/src/plugins/inspector/editors/mod.rs index 22ae2a857..263d388e2 100644 --- a/editor/src/plugins/inspector/editors/mod.rs +++ b/editor/src/plugins/inspector/editors/mod.rs @@ -18,18 +18,26 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::plugins::inspector::editors::{ - animation::{ - AnimationContainerPropertyEditorDefinition, AnimationPropertyEditorDefinition, - MachinePropertyEditorDefinition, +use fyrox::scene::tilemap::TileDefinitionHandle; + +use crate::plugins::{ + inspector::editors::{ + animation::{ + AnimationContainerPropertyEditorDefinition, AnimationPropertyEditorDefinition, + MachinePropertyEditorDefinition, + }, + font::FontPropertyEditorDefinition, + handle::{EntityKind, NodeHandlePropertyEditorDefinition}, + resource::ResourceFieldPropertyEditorDefinition, + script::ScriptPropertyEditorDefinition, + spritesheet::SpriteSheetFramesContainerEditorDefinition, + surface::SurfaceDataPropertyEditorDefinition, + texture::TexturePropertyEditorDefinition, + }, + tilemap::{ + OptionTileDefinitionHandlePropertyEditorDefinition, + TileDefinitionHandlePropertyEditorDefinition, }, - font::FontPropertyEditorDefinition, - handle::{EntityKind, NodeHandlePropertyEditorDefinition}, - resource::ResourceFieldPropertyEditorDefinition, - script::ScriptPropertyEditorDefinition, - spritesheet::SpriteSheetFramesContainerEditorDefinition, - surface::SurfaceDataPropertyEditorDefinition, - texture::TexturePropertyEditorDefinition, }; use crate::{ fyrox::{ @@ -106,7 +114,7 @@ use crate::{ }, terrain::{Chunk, Layer}, tilemap::brush::{TileMapBrush, TileMapBrushResource}, - tilemap::tileset::TileCollider, + tilemap::TileCollider, tilemap::{tileset::TileSet, Tile}, transform::Transform, }, @@ -211,6 +219,11 @@ where pub fn make_property_editors_container(sender: MessageSender) -> PropertyEditorDefinitionContainer { let container = PropertyEditorDefinitionContainer::with_default_editors(); + container.insert(TileDefinitionHandlePropertyEditorDefinition); + container.insert(OptionTileDefinitionHandlePropertyEditorDefinition); + container.register_inheritable_vec_collection::(); + container.register_inheritable_vec_collection::>(); + container.insert(TexturePropertyEditorDefinition { untyped: false }); container.insert(TexturePropertyEditorDefinition { untyped: true }); container.insert(FontPropertyEditorDefinition); diff --git a/editor/src/plugins/path_fixer.rs b/editor/src/plugins/path_fixer.rs index 813b19a8c..dc932c8e1 100644 --- a/editor/src/plugins/path_fixer.rs +++ b/editor/src/plugins/path_fixer.rs @@ -326,7 +326,7 @@ impl PathFixer { // Turn hash map into vec to be able to index it. self.orphaned_scene_resources = scene_resources .into_iter() - .filter(|r| !r.kind().path().map_or(false, |p| p.exists())) + .filter(|r| !r.kind().path().is_some_and(|p| p.exists())) .collect::>(); let ctx = &mut ui.build_ctx(); diff --git a/editor/src/plugins/tilemap/collider_editor.rs b/editor/src/plugins/tilemap/collider_editor.rs new file mode 100644 index 000000000..dff304286 --- /dev/null +++ b/editor/src/plugins/tilemap/collider_editor.rs @@ -0,0 +1,445 @@ +// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! The [`TileEditor`] for a tile's collider layer. This allows the tile set editor +//! to display a dropdown list for the various [`TileCollider`] options, and displays +//! a text box for editing custom colliders when appropriate. +//! See [`TileColliderEditor`] for more information. + +use commands::SetTileSetTilesCommand; +use fyrox::{ + asset::{untyped::ResourceKind, Resource}, + core::{algebra::Vector2, color::Color, pool::Handle, type_traits::prelude::*}, + gui::{ + border::BorderBuilder, + brush::Brush, + button::{Button, ButtonBuilder, ButtonMessage}, + decorator::{DecoratorBuilder, DecoratorMessage}, + dropdown_list::{DropdownListBuilder, DropdownListMessage}, + formatted_text::WrapMode, + grid::{Column, GridBuilder, Row}, + image::ImageBuilder, + message::UiMessage, + stack_panel::StackPanelBuilder, + text::{TextBuilder, TextMessage}, + text_box::{TextBoxBuilder, TextCommitMode}, + utils::make_simple_tooltip, + widget::WidgetBuilder, + BuildContext, HorizontalAlignment, Thickness, UiNode, UserInterface, VerticalAlignment, + }, + scene::tilemap::{tileset::*, CustomTileCollider, CustomTileColliderResource, TileSetUpdate}, +}; +use std::str::FromStr; + +use crate::{send_sync_message, MSG_SYNC_FLAG}; + +use super::*; + +const COLLIDER_NAMES: &[&str] = &["None", "Full", "Custom"]; + +fn collider_to_index(tile_collider: &TileCollider) -> Option { + match tile_collider { + TileCollider::None => Some(0), + TileCollider::Rectangle => Some(1), + TileCollider::Custom(_) => Some(2), + TileCollider::Mesh => None, + } +} + +fn send_visibility(ui: &UserInterface, destination: Handle, visible: bool) { + ui.send_message(WidgetMessage::visibility( + destination, + MessageDirection::ToWidget, + visible, + )); +} + +fn highlight_tool_button(button: Handle, highlight: bool, ui: &UserInterface) { + let decorator = *ui.try_get_of_type::