tilemap-editor 3.0.1__tar.gz → 3.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tilemap_editor-3.0.1/src/tilemap_editor.egg-info → tilemap_editor-3.1.2}/PKG-INFO +1 -1
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/pyproject.toml +1 -2
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/character_collision/editor.py +41 -1
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/character_collision/shape_editor.py +186 -7
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/object_tileset_collision/editor.py +45 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap.py +11 -7
- tilemap_editor-3.1.2/src/tilemap_editor/assets/icons/ToolMove.svg +1 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2/src/tilemap_editor.egg-info}/PKG-INFO +1 -1
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor.egg-info/SOURCES.txt +7 -1
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor.egg-info/top_level.txt +0 -1
- tilemap_editor-3.1.2/src/widgets/ui/collision_layer_mask.py +217 -0
- tilemap_editor-3.1.2/src/widgets/ui/collision_layer_sidebar.py +214 -0
- tilemap_editor-3.1.2/tests/test_collision_layer_mask.py +226 -0
- tilemap_editor-3.1.2/tests/test_collision_layer_sidebar.py +177 -0
- tilemap_editor-3.1.2/tests/test_project_paths.py +73 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/LICENSE +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/MANIFEST.in +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/README.md +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/check.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/checked.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/close.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/error.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/file.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/filedead.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/fit.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/folder.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/grid.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/image.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/info.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/load.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/loop.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/pan.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/pause.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/pencil.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/play.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/plus.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/radio.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/reset.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/save.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/stop.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/tileset.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/warning.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/setup.cfg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/configs/themes.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/constants.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/editor.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/event_map.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/layers.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/main.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/__init__.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/character_collision/__init__.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/character_collision/models.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/character_collision/protocols.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/character_collision/standalone.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/object_tileset_collision/__init__.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/object_tileset_collision/models.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/object_tileset_collision/protocols.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/object_tileset_collision/standalone.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/__init__.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/__main__.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/clipboard_util.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/editor.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/frame_picker.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/models.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/preview.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/protocols.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/runtime_load.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/standalone.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/timeline.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/sprite_animation/validation.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/tileset_collision/__init__.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/tileset_collision/collision_painter.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/tileset_collision/editor.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/tileset_collision/models.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/tileset_collision/protocols.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/tileset_collision/standalone.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/standalone_automap.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/standalone_error_console.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/standalone_filemanager.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/standalone_image_viewer.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/__init__.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/__main__.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/__init__.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/check.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/checked.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/close.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/error.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/file.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/fit.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/folder.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/grid.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/image.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/info.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/load.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/loop.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/pan.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/pause.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/play.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/plus.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/radio.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/reset.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/save.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/stop.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/warning.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/cli.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/main.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor/settings.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/tilemap_editor.egg-info/requires.txt +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/ttypes/__init__.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/ttypes/tilemap.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/__init__.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/editor_preference.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/error_handler.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/font_manager.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/history.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/icon_manager.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/icons_cache.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/log_capture.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/project_paths.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/serialization.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/standalone.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/utils/validation.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/__init__.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/automap_models.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/autotile_template.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/autotiler.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/filemanager.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/input.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/layer_selector.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/mapsetup.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/regex_automap_designer.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/tile_grid.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/tile_selector.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/draw_utils.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/fileinput.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/layer_type_dialog.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/menubar.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/mode_indicator.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/notification.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/property_editor.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/region_selector.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/status_bar.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/theme.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/tileset_type_dialog.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/toolbar.py +0 -0
- {tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/widgets/ui/tooltip.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tilemap-editor"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.1.2"
|
|
8
8
|
description = "Pygame tilemap editor with SVG icons, professional UI, and sprite animation tools"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -33,7 +33,6 @@ py-modules = [
|
|
|
33
33
|
"event_map",
|
|
34
34
|
"layers",
|
|
35
35
|
"tilemap",
|
|
36
|
-
"tilemap_parser",
|
|
37
36
|
"standalone_filemanager",
|
|
38
37
|
"standalone_image_viewer",
|
|
39
38
|
"standalone_automap",
|
|
@@ -29,6 +29,7 @@ from utils.icon_manager import icon_manager
|
|
|
29
29
|
from utils.error_handler import error_handler, error_context
|
|
30
30
|
from widgets.ui.theme import COLORS, FONTS
|
|
31
31
|
from widgets.ui.draw_utils import draw_panel, draw_button
|
|
32
|
+
from widgets.ui.collision_layer_sidebar import CollisionLayerSidebar
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
class CharacterCollisionEditor:
|
|
@@ -60,6 +61,14 @@ class CharacterCollisionEditor:
|
|
|
60
61
|
# Shape editor
|
|
61
62
|
self.shape_editor = ShapeEditor(shape_editor_rect, sprite_surface)
|
|
62
63
|
|
|
64
|
+
# Collision layer/mask sidebar (toggleable overlay)
|
|
65
|
+
self.layer_sidebar = CollisionLayerSidebar(
|
|
66
|
+
rect,
|
|
67
|
+
max_layers=16,
|
|
68
|
+
initial_layer=1,
|
|
69
|
+
initial_mask=0xFFFF,
|
|
70
|
+
)
|
|
71
|
+
|
|
63
72
|
# Fonts
|
|
64
73
|
self._font = font_manager.get_font(FONTS.name, FONTS.size_md, FontWeight.REGULAR)
|
|
65
74
|
self._font_sm = font_manager.get_font(FONTS.name, FONTS.size_sm, FontWeight.REGULAR)
|
|
@@ -93,6 +102,8 @@ class CharacterCollisionEditor:
|
|
|
93
102
|
)
|
|
94
103
|
|
|
95
104
|
self.shape_editor.resize(shape_editor_rect)
|
|
105
|
+
self.layer_sidebar.resize(rect)
|
|
106
|
+
|
|
96
107
|
self._init_shape_buttons()
|
|
97
108
|
|
|
98
109
|
def get_collision_data(self) -> CharacterCollisionData:
|
|
@@ -136,12 +147,20 @@ class CharacterCollisionEditor:
|
|
|
136
147
|
return CharacterCollisionData(
|
|
137
148
|
name=self.character_name,
|
|
138
149
|
shape=shape,
|
|
150
|
+
properties={
|
|
151
|
+
"collision_layer": self.layer_sidebar.get_layer(),
|
|
152
|
+
"collision_mask": self.layer_sidebar.get_mask(),
|
|
153
|
+
},
|
|
139
154
|
)
|
|
140
155
|
|
|
141
156
|
def load_collision_data(self, data: CharacterCollisionData) -> None:
|
|
142
157
|
"""Load collision data"""
|
|
143
158
|
self.character_name = data.name
|
|
144
159
|
self.shape_editor.load_shape_data(data.shape.to_dict())
|
|
160
|
+
layer = data.properties.get("collision_layer", 1)
|
|
161
|
+
mask = data.properties.get("collision_mask", 0xFFFF)
|
|
162
|
+
self.layer_sidebar.set_layer(layer)
|
|
163
|
+
self.layer_sidebar.set_mask(mask)
|
|
145
164
|
|
|
146
165
|
def _get_collision_dir(self) -> Path:
|
|
147
166
|
"""Get collision data directory (data_root/character_collision)"""
|
|
@@ -175,7 +194,22 @@ class CharacterCollisionEditor:
|
|
|
175
194
|
if not self.visible:
|
|
176
195
|
return False
|
|
177
196
|
|
|
178
|
-
#
|
|
197
|
+
# Layer/mask sidebar handles events when open
|
|
198
|
+
if self.layer_sidebar.handle_event(event):
|
|
199
|
+
return True
|
|
200
|
+
|
|
201
|
+
# Toggle button
|
|
202
|
+
if self.layer_sidebar.handle_toggle_event(event):
|
|
203
|
+
return True
|
|
204
|
+
|
|
205
|
+
# Keyboard shortcut: L to toggle sidebar
|
|
206
|
+
if event.type == pygame.KEYDOWN and event.key == pygame.K_l:
|
|
207
|
+
mods = pygame.key.get_mods()
|
|
208
|
+
if not (mods & (pygame.KMOD_CTRL | pygame.KMOD_LMETA)):
|
|
209
|
+
self.layer_sidebar.toggle()
|
|
210
|
+
return True
|
|
211
|
+
|
|
212
|
+
# Let shape editor handle events next
|
|
179
213
|
if self.shape_editor.handle_event(event):
|
|
180
214
|
return True
|
|
181
215
|
|
|
@@ -201,6 +235,12 @@ class CharacterCollisionEditor:
|
|
|
201
235
|
# Draw shape editor
|
|
202
236
|
self.shape_editor.draw(screen)
|
|
203
237
|
|
|
238
|
+
# Draw toggle button
|
|
239
|
+
self.layer_sidebar.draw_toggle_button(screen)
|
|
240
|
+
|
|
241
|
+
# Draw collision layer/mask sidebar (only when visible)
|
|
242
|
+
self.layer_sidebar.draw(screen)
|
|
243
|
+
|
|
204
244
|
# Draw properties panel
|
|
205
245
|
self._draw_properties(screen)
|
|
206
246
|
|
{tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/character_collision/shape_editor.py
RENAMED
|
@@ -18,6 +18,7 @@ import pygame
|
|
|
18
18
|
from pygame import Rect
|
|
19
19
|
from utils.font_manager import font_manager, FontWeight
|
|
20
20
|
from utils.error_handler import error_handler
|
|
21
|
+
from utils.icon_manager import icon_manager
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
ShapeType = Literal["rectangle", "circle", "capsule", "polygon"]
|
|
@@ -82,8 +83,14 @@ class ShapeEditor:
|
|
|
82
83
|
self.dragging_handle: Optional[str] = None
|
|
83
84
|
self.hover_handle: Optional[str] = None
|
|
84
85
|
self._panning = False
|
|
86
|
+
self._pan_mode = False
|
|
85
87
|
self._pan_start = (0, 0)
|
|
86
88
|
self._pan_start_offset = (0.0, 0.0)
|
|
89
|
+
self._move_icon_pos: Tuple[int, int] = (0, 0)
|
|
90
|
+
self._move_icon_hover = False
|
|
91
|
+
self._dragging_shape = False
|
|
92
|
+
self._drag_start = (0, 0)
|
|
93
|
+
self._drag_start_positions: dict = {}
|
|
87
94
|
|
|
88
95
|
# Settings
|
|
89
96
|
self.show_grid = True
|
|
@@ -95,6 +102,7 @@ class ShapeEditor:
|
|
|
95
102
|
|
|
96
103
|
# Center the sprite
|
|
97
104
|
self._center_view()
|
|
105
|
+
self._center_shape()
|
|
98
106
|
|
|
99
107
|
def _center_view(self) -> None:
|
|
100
108
|
"""Center the sprite in the viewport"""
|
|
@@ -102,6 +110,27 @@ class ShapeEditor:
|
|
|
102
110
|
self.offset_x = (self.rect.w - sw * self.zoom) / 2
|
|
103
111
|
self.offset_y = (self.rect.h - sh * self.zoom) / 2
|
|
104
112
|
|
|
113
|
+
def _center_shape(self) -> None:
|
|
114
|
+
"""Center all shape types on the sprite"""
|
|
115
|
+
sw, sh = self.sprite_surface.get_size()
|
|
116
|
+
|
|
117
|
+
# Rectangle: 50% of sprite, centered
|
|
118
|
+
self.rect_w = sw * 0.5
|
|
119
|
+
self.rect_h = sh * 0.5
|
|
120
|
+
self.rect_x = (sw - self.rect_w) / 2
|
|
121
|
+
self.rect_y = (sh - self.rect_h) / 2
|
|
122
|
+
|
|
123
|
+
# Circle: 25% of min dimension, centered
|
|
124
|
+
self.circle_radius = min(sw, sh) * 0.25
|
|
125
|
+
self.circle_x = sw / 2
|
|
126
|
+
self.circle_y = sh / 2
|
|
127
|
+
|
|
128
|
+
# Capsule: 50% height, 15% width radius, centered
|
|
129
|
+
self.capsule_height = sh * 0.5
|
|
130
|
+
self.capsule_radius = sw * 0.15
|
|
131
|
+
self.capsule_x = sw / 2
|
|
132
|
+
self.capsule_y = (sh - self.capsule_height) / 2
|
|
133
|
+
|
|
105
134
|
def _ensure_fonts(self) -> None:
|
|
106
135
|
"""Initialize fonts"""
|
|
107
136
|
if self._font is None:
|
|
@@ -182,6 +211,7 @@ class ShapeEditor:
|
|
|
182
211
|
def resize(self, rect: Rect) -> None:
|
|
183
212
|
"""Update rect"""
|
|
184
213
|
self.rect = rect
|
|
214
|
+
self._center_view()
|
|
185
215
|
|
|
186
216
|
def handle_event(self, event: pygame.event.Event) -> bool:
|
|
187
217
|
"""Handle input events"""
|
|
@@ -190,6 +220,7 @@ class ShapeEditor:
|
|
|
190
220
|
if not self.rect.collidepoint(mouse) and event.type not in (
|
|
191
221
|
pygame.MOUSEBUTTONUP,
|
|
192
222
|
pygame.MOUSEMOTION,
|
|
223
|
+
pygame.KEYDOWN,
|
|
193
224
|
):
|
|
194
225
|
return False
|
|
195
226
|
|
|
@@ -214,14 +245,25 @@ class ShapeEditor:
|
|
|
214
245
|
self.offset_y = self._pan_start_offset[1] + dy
|
|
215
246
|
return True
|
|
216
247
|
|
|
217
|
-
#
|
|
218
|
-
if self.rect.collidepoint(mouse):
|
|
219
|
-
self.hover_handle = self._find_handle_at(mouse)
|
|
220
|
-
|
|
221
|
-
# Drag handle
|
|
248
|
+
# Drag handle — always takes priority over shape drag
|
|
222
249
|
if self.dragging_handle:
|
|
223
250
|
self._drag_handle(mouse)
|
|
224
251
|
return True
|
|
252
|
+
|
|
253
|
+
# Drag entire shape via move icon
|
|
254
|
+
if self._dragging_shape:
|
|
255
|
+
dx = (mouse[0] - self._drag_start[0]) / self.zoom
|
|
256
|
+
dy = (mouse[1] - self._drag_start[1]) / self.zoom
|
|
257
|
+
self._apply_shape_delta(dx, dy)
|
|
258
|
+
return True
|
|
259
|
+
|
|
260
|
+
# Update hover state
|
|
261
|
+
if self.rect.collidepoint(mouse):
|
|
262
|
+
handle = self._find_handle_at(mouse)
|
|
263
|
+
if handle:
|
|
264
|
+
self.hover_handle = handle
|
|
265
|
+
else:
|
|
266
|
+
self.hover_handle = None
|
|
225
267
|
|
|
226
268
|
# Mouse wheel to zoom
|
|
227
269
|
if event.type == pygame.MOUSEWHEEL and self.rect.collidepoint(mouse):
|
|
@@ -238,10 +280,25 @@ class ShapeEditor:
|
|
|
238
280
|
# Left click
|
|
239
281
|
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
|
|
240
282
|
if self.rect.collidepoint(mouse):
|
|
283
|
+
# Pan mode — start dragging
|
|
284
|
+
if self._pan_mode:
|
|
285
|
+
self._panning = True
|
|
286
|
+
self._pan_start = mouse
|
|
287
|
+
self._pan_start_offset = (self.offset_x, self.offset_y)
|
|
288
|
+
return True
|
|
289
|
+
|
|
241
290
|
# Check if clicking on handle
|
|
242
291
|
handle = self._find_handle_at(mouse)
|
|
243
292
|
if handle:
|
|
244
293
|
self.dragging_handle = handle
|
|
294
|
+
self._dragging_shape = False
|
|
295
|
+
return True
|
|
296
|
+
|
|
297
|
+
# Check if clicking on move icon
|
|
298
|
+
if self._is_on_move_icon(mouse) and not self._pan_mode:
|
|
299
|
+
self._dragging_shape = True
|
|
300
|
+
self._drag_start = mouse
|
|
301
|
+
self._save_shape_positions()
|
|
245
302
|
return True
|
|
246
303
|
|
|
247
304
|
# Polygon mode - add vertex
|
|
@@ -256,6 +313,12 @@ class ShapeEditor:
|
|
|
256
313
|
return True
|
|
257
314
|
|
|
258
315
|
if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
|
|
316
|
+
if self._panning:
|
|
317
|
+
self._panning = False
|
|
318
|
+
return True
|
|
319
|
+
if self._dragging_shape:
|
|
320
|
+
self._dragging_shape = False
|
|
321
|
+
return True
|
|
259
322
|
if self.dragging_handle:
|
|
260
323
|
self.dragging_handle = None
|
|
261
324
|
return True
|
|
@@ -270,7 +333,13 @@ class ShapeEditor:
|
|
|
270
333
|
|
|
271
334
|
# Keyboard shortcuts
|
|
272
335
|
if event.type == pygame.KEYDOWN:
|
|
273
|
-
if event.key == pygame.
|
|
336
|
+
if event.key == pygame.K_SPACE:
|
|
337
|
+
self._pan_mode = not self._pan_mode
|
|
338
|
+
if self._pan_mode:
|
|
339
|
+
self._pan_start = mouse
|
|
340
|
+
self._pan_start_offset = (self.offset_x, self.offset_y)
|
|
341
|
+
return True
|
|
342
|
+
elif event.key == pygame.K_g:
|
|
274
343
|
self.show_grid = not self.show_grid
|
|
275
344
|
return True
|
|
276
345
|
elif event.key == pygame.K_r:
|
|
@@ -344,14 +413,22 @@ class ShapeEditor:
|
|
|
344
413
|
|
|
345
414
|
if self.shape_type == "rectangle":
|
|
346
415
|
if self.dragging_handle == "tl":
|
|
416
|
+
old_right = self.rect_x + self.rect_w
|
|
417
|
+
old_bottom = self.rect_y + self.rect_h
|
|
347
418
|
self.rect_x = max(0, min(sw, sprite_pos[0]))
|
|
348
419
|
self.rect_y = max(0, min(sh, sprite_pos[1]))
|
|
420
|
+
self.rect_w = max(1, old_right - self.rect_x)
|
|
421
|
+
self.rect_h = max(1, old_bottom - self.rect_y)
|
|
349
422
|
elif self.dragging_handle == "tr":
|
|
423
|
+
old_bottom = self.rect_y + self.rect_h
|
|
350
424
|
self.rect_w = max(1, sprite_pos[0] - self.rect_x)
|
|
351
425
|
self.rect_y = max(0, min(sh, sprite_pos[1]))
|
|
426
|
+
self.rect_h = max(1, old_bottom - self.rect_y)
|
|
352
427
|
elif self.dragging_handle == "bl":
|
|
428
|
+
old_right = self.rect_x + self.rect_w
|
|
353
429
|
self.rect_x = max(0, min(sw, sprite_pos[0]))
|
|
354
430
|
self.rect_h = max(1, sprite_pos[1] - self.rect_y)
|
|
431
|
+
self.rect_w = max(1, old_right - self.rect_x)
|
|
355
432
|
elif self.dragging_handle == "br":
|
|
356
433
|
self.rect_w = max(1, sprite_pos[0] - self.rect_x)
|
|
357
434
|
self.rect_h = max(1, sprite_pos[1] - self.rect_y)
|
|
@@ -368,12 +445,102 @@ class ShapeEditor:
|
|
|
368
445
|
|
|
369
446
|
elif self.shape_type == "capsule":
|
|
370
447
|
if self.dragging_handle == "top":
|
|
448
|
+
old_bottom = self.capsule_y + self.capsule_height
|
|
371
449
|
self.capsule_y = max(0, min(sh, sprite_pos[1]))
|
|
450
|
+
self.capsule_height = max(1, old_bottom - self.capsule_y)
|
|
372
451
|
elif self.dragging_handle == "bottom":
|
|
373
452
|
self.capsule_height = max(1, sprite_pos[1] - self.capsule_y)
|
|
374
453
|
elif self.dragging_handle == "radius":
|
|
375
454
|
self.capsule_radius = max(1, abs(sprite_pos[0] - self.capsule_x))
|
|
376
455
|
|
|
456
|
+
def _get_move_icon_pos(self) -> Tuple[int, int]:
|
|
457
|
+
"""Get screen position for the move icon at mid-top of the shape"""
|
|
458
|
+
if self.shape_type == "rectangle":
|
|
459
|
+
mid_x = self.rect_x + self.rect_w / 2
|
|
460
|
+
top_y = self.rect_y
|
|
461
|
+
return self._sprite_to_screen((mid_x, top_y))
|
|
462
|
+
elif self.shape_type == "circle":
|
|
463
|
+
return self._sprite_to_screen((self.circle_x, self.circle_y - self.circle_radius))
|
|
464
|
+
elif self.shape_type == "capsule":
|
|
465
|
+
return self._sprite_to_screen((self.capsule_x, self.capsule_y))
|
|
466
|
+
elif self.shape_type == "polygon" and len(self.polygon_vertices) >= 3:
|
|
467
|
+
# Top-center of polygon bounding box
|
|
468
|
+
min_y = min(v[1] for v in self.polygon_vertices)
|
|
469
|
+
mid_x = (min(v[0] for v in self.polygon_vertices) + max(v[0] for v in self.polygon_vertices)) / 2
|
|
470
|
+
return self._sprite_to_screen((mid_x, min_y))
|
|
471
|
+
return (0, 0)
|
|
472
|
+
|
|
473
|
+
def _is_on_move_icon(self, screen_pos: Tuple[int, int]) -> bool:
|
|
474
|
+
"""Check if a screen position is on the move icon (16x16 hit area)"""
|
|
475
|
+
icon_pos = self._get_move_icon_pos()
|
|
476
|
+
self._move_icon_pos = icon_pos
|
|
477
|
+
hit_size = 16
|
|
478
|
+
half = hit_size // 2
|
|
479
|
+
return (icon_pos[0] - half <= screen_pos[0] <= icon_pos[0] + half and
|
|
480
|
+
icon_pos[1] - half <= screen_pos[1] <= icon_pos[1] + half)
|
|
481
|
+
|
|
482
|
+
def _is_inside_shape(self, screen_pos: Tuple[int, int]) -> bool:
|
|
483
|
+
"""Check if a screen position is inside the current shape (for hover detection)"""
|
|
484
|
+
if self.shape_type == "rectangle":
|
|
485
|
+
tl = self._sprite_to_screen((self.rect_x, self.rect_y))
|
|
486
|
+
br = self._sprite_to_screen((self.rect_x + self.rect_w, self.rect_y + self.rect_h))
|
|
487
|
+
return (tl[0] <= screen_pos[0] <= br[0] and tl[1] <= screen_pos[1] <= br[1])
|
|
488
|
+
|
|
489
|
+
elif self.shape_type == "circle":
|
|
490
|
+
center = self._sprite_to_screen((self.circle_x, self.circle_y))
|
|
491
|
+
radius = int(self.circle_radius * self.zoom)
|
|
492
|
+
return math.hypot(screen_pos[0] - center[0], screen_pos[1] - center[1]) <= radius
|
|
493
|
+
|
|
494
|
+
elif self.shape_type == "capsule":
|
|
495
|
+
top = self._sprite_to_screen((self.capsule_x, self.capsule_y))
|
|
496
|
+
bottom = self._sprite_to_screen((self.capsule_x, self.capsule_y + self.capsule_height))
|
|
497
|
+
radius = int(self.capsule_radius * self.zoom)
|
|
498
|
+
if math.hypot(screen_pos[0] - top[0], screen_pos[1] - top[1]) <= radius:
|
|
499
|
+
return True
|
|
500
|
+
if math.hypot(screen_pos[0] - bottom[0], screen_pos[1] - bottom[1]) <= radius:
|
|
501
|
+
return True
|
|
502
|
+
left = top[0] - radius
|
|
503
|
+
right = top[0] + radius
|
|
504
|
+
return left <= screen_pos[0] <= right and top[1] <= screen_pos[1] <= bottom[1]
|
|
505
|
+
|
|
506
|
+
elif self.shape_type == "polygon" and len(self.polygon_vertices) >= 3:
|
|
507
|
+
px, py = self._screen_to_sprite(screen_pos)
|
|
508
|
+
n = len(self.polygon_vertices)
|
|
509
|
+
inside = False
|
|
510
|
+
j = n - 1
|
|
511
|
+
for i in range(n):
|
|
512
|
+
vi = self.polygon_vertices[i]
|
|
513
|
+
vj = self.polygon_vertices[j]
|
|
514
|
+
if ((vi[1] > py) != (vj[1] > py)) and (
|
|
515
|
+
px < (vj[0] - vi[0]) * (py - vi[1]) / (vj[1] - vi[1]) + vi[0]
|
|
516
|
+
):
|
|
517
|
+
inside = not inside
|
|
518
|
+
j = i
|
|
519
|
+
return inside
|
|
520
|
+
|
|
521
|
+
return False
|
|
522
|
+
|
|
523
|
+
def _save_shape_positions(self) -> None:
|
|
524
|
+
"""Store current shape positions for delta calculation"""
|
|
525
|
+
self._drag_start_positions = {
|
|
526
|
+
"rect": (self.rect_x, self.rect_y),
|
|
527
|
+
"circle": (self.circle_x, self.circle_y),
|
|
528
|
+
"capsule": (self.capsule_x, self.capsule_y),
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
def _apply_shape_delta(self, dx: float, dy: float) -> None:
|
|
532
|
+
"""Move shape by delta in sprite-local coordinates"""
|
|
533
|
+
sw, sh = self.sprite_surface.get_size()
|
|
534
|
+
if self.shape_type == "rectangle":
|
|
535
|
+
self.rect_x = max(0, min(sw - self.rect_w, self._drag_start_positions["rect"][0] + dx))
|
|
536
|
+
self.rect_y = max(0, min(sh - self.rect_h, self._drag_start_positions["rect"][1] + dy))
|
|
537
|
+
elif self.shape_type == "circle":
|
|
538
|
+
self.circle_x = max(0, min(sw, self._drag_start_positions["circle"][0] + dx))
|
|
539
|
+
self.circle_y = max(0, min(sh, self._drag_start_positions["circle"][1] + dy))
|
|
540
|
+
elif self.shape_type == "capsule":
|
|
541
|
+
self.capsule_x = max(0, min(sw, self._drag_start_positions["capsule"][0] + dx))
|
|
542
|
+
self.capsule_y = max(0, min(sh - self.capsule_height, self._drag_start_positions["capsule"][1] + dy))
|
|
543
|
+
|
|
377
544
|
def draw(self, screen: pygame.Surface) -> None:
|
|
378
545
|
"""Draw the shape editor"""
|
|
379
546
|
self._ensure_fonts()
|
|
@@ -406,6 +573,14 @@ class ShapeEditor:
|
|
|
406
573
|
# Draw shape
|
|
407
574
|
self._draw_shape(screen)
|
|
408
575
|
|
|
576
|
+
# Draw move icon at mid-top when hovering over shape body
|
|
577
|
+
if not self._pan_mode and not self.hover_handle:
|
|
578
|
+
mouse = pygame.mouse.get_pos()
|
|
579
|
+
if self.rect.collidepoint(mouse) and self._is_inside_shape(mouse):
|
|
580
|
+
icon_pos = self._get_move_icon_pos()
|
|
581
|
+
icon = icon_manager.get_icon("ToolMove", 16, _COLORS["shape_stroke"])
|
|
582
|
+
screen.blit(icon, (icon_pos[0] - 8, icon_pos[1] - 8))
|
|
583
|
+
|
|
409
584
|
# Draw status
|
|
410
585
|
self._draw_status(screen)
|
|
411
586
|
|
|
@@ -544,6 +719,10 @@ class ShapeEditor:
|
|
|
544
719
|
def _draw_status(self, screen: pygame.Surface) -> None:
|
|
545
720
|
"""Draw status text"""
|
|
546
721
|
lines = []
|
|
722
|
+
|
|
723
|
+
if self._pan_mode:
|
|
724
|
+
lines.append("PANNING MODE — drag to pan | Space to exit")
|
|
725
|
+
|
|
547
726
|
lines.append(f"Shape: {self.shape_type.upper()} | Zoom: {self.zoom:.1f}x")
|
|
548
727
|
|
|
549
728
|
if self.shape_type == "polygon":
|
|
@@ -554,7 +733,7 @@ class ShapeEditor:
|
|
|
554
733
|
else:
|
|
555
734
|
lines.append("Click to add vertices")
|
|
556
735
|
|
|
557
|
-
lines.append("G: toggle grid | R: reset view |
|
|
736
|
+
lines.append("G: toggle grid | R: reset view | Space: pan mode | Wheel: zoom")
|
|
558
737
|
|
|
559
738
|
y = self.rect.y + 5
|
|
560
739
|
for line in lines:
|
{tilemap_editor-3.0.1 → tilemap_editor-3.1.2}/src/plugins/object_tileset_collision/editor.py
RENAMED
|
@@ -39,6 +39,7 @@ from widgets.ui.status_bar import StatusBar, StatusType
|
|
|
39
39
|
from widgets.ui.mode_indicator import ModeIndicator, Mode
|
|
40
40
|
from widgets.ui.draw_utils import draw_panel, draw_button
|
|
41
41
|
from widgets.ui.theme import COLORS, FONTS, SHAPE
|
|
42
|
+
from widgets.ui.collision_layer_sidebar import CollisionLayerSidebar
|
|
42
43
|
from widgets.input import InlineTextInput
|
|
43
44
|
from utils.font_manager import font_manager, FontWeight
|
|
44
45
|
from utils.icon_manager import icon_manager
|
|
@@ -190,6 +191,15 @@ class ObjectTilesetCollisionEditor:
|
|
|
190
191
|
show_icons=True,
|
|
191
192
|
)
|
|
192
193
|
|
|
194
|
+
# Collision layer/mask sidebar (toggleable overlay)
|
|
195
|
+
self.layer_sidebar = CollisionLayerSidebar(
|
|
196
|
+
self.rect,
|
|
197
|
+
max_layers=16,
|
|
198
|
+
initial_layer=1,
|
|
199
|
+
initial_mask=0xFFFF,
|
|
200
|
+
on_changed=self._on_layer_mask_changed,
|
|
201
|
+
)
|
|
202
|
+
|
|
193
203
|
# Collision painter (middle area)
|
|
194
204
|
self.painter = CollisionPainter(
|
|
195
205
|
self.painter_rect,
|
|
@@ -245,6 +255,8 @@ class ObjectTilesetCollisionEditor:
|
|
|
245
255
|
self.status_bar.resize(self.status_bar_rect)
|
|
246
256
|
if hasattr(self, 'painter'):
|
|
247
257
|
self.painter.resize(self.painter_rect)
|
|
258
|
+
if hasattr(self, 'layer_sidebar'):
|
|
259
|
+
self.layer_sidebar.resize(self.rect)
|
|
248
260
|
|
|
249
261
|
# Mode indicator position (in toolbar, centered)
|
|
250
262
|
if hasattr(self, 'mode_indicator'):
|
|
@@ -332,6 +344,14 @@ class ObjectTilesetCollisionEditor:
|
|
|
332
344
|
"""Called when region selection changes"""
|
|
333
345
|
self._current_region_id = region_id
|
|
334
346
|
|
|
347
|
+
# Load layer/mask from region properties
|
|
348
|
+
if region_id and region_id in self.library.regions:
|
|
349
|
+
data = self.library.regions[region_id]
|
|
350
|
+
layer = data.properties.get("collision_layer", 1)
|
|
351
|
+
mask = data.properties.get("collision_mask", 0xFFFF)
|
|
352
|
+
self.layer_sidebar.set_layer(layer)
|
|
353
|
+
self.layer_sidebar.set_mask(mask)
|
|
354
|
+
|
|
335
355
|
# Update painter to show selected region
|
|
336
356
|
if self._mode == EditorMode.PAINT_COLLISION:
|
|
337
357
|
self._update_painter_for_current_region()
|
|
@@ -366,6 +386,12 @@ class ObjectTilesetCollisionEditor:
|
|
|
366
386
|
|
|
367
387
|
return status
|
|
368
388
|
|
|
389
|
+
def _on_layer_mask_changed(self, layer: int, mask: int) -> None:
|
|
390
|
+
"""Called when collision layer/mask widget changes."""
|
|
391
|
+
if self._current_region_id and self._current_region_id in self.library.regions:
|
|
392
|
+
self.library.regions[self._current_region_id].properties["collision_layer"] = layer
|
|
393
|
+
self.library.regions[self._current_region_id].properties["collision_mask"] = mask
|
|
394
|
+
|
|
369
395
|
# === Rename Functionality ===
|
|
370
396
|
|
|
371
397
|
def _start_rename(self, region_id: str) -> None:
|
|
@@ -805,6 +831,19 @@ class ObjectTilesetCollisionEditor:
|
|
|
805
831
|
if self.region_selector.handle_event(event):
|
|
806
832
|
return True
|
|
807
833
|
|
|
834
|
+
# Priority 5.5: Collision layer/mask sidebar (toggle + events when open)
|
|
835
|
+
if self.layer_sidebar.handle_event(event):
|
|
836
|
+
return True
|
|
837
|
+
if self.layer_sidebar.handle_toggle_event(event):
|
|
838
|
+
return True
|
|
839
|
+
|
|
840
|
+
# Keyboard shortcut: L to toggle sidebar
|
|
841
|
+
if event.type == pygame.KEYDOWN and event.key == pygame.K_l:
|
|
842
|
+
mods = pygame.key.get_mods()
|
|
843
|
+
if not (mods & (pygame.KMOD_CTRL | pygame.KMOD_LMETA)):
|
|
844
|
+
self.layer_sidebar.toggle()
|
|
845
|
+
return True
|
|
846
|
+
|
|
808
847
|
# Priority 6: Collision painter (paint collision mode)
|
|
809
848
|
if self._mode == EditorMode.PAINT_COLLISION:
|
|
810
849
|
if self.painter.handle_event(event):
|
|
@@ -902,6 +941,12 @@ class ObjectTilesetCollisionEditor:
|
|
|
902
941
|
# Draw status bar
|
|
903
942
|
self.status_bar.draw(screen)
|
|
904
943
|
|
|
944
|
+
# Draw toggle button
|
|
945
|
+
self.layer_sidebar.draw_toggle_button(screen)
|
|
946
|
+
|
|
947
|
+
# Draw collision layer/mask sidebar (only when visible)
|
|
948
|
+
self.layer_sidebar.draw(screen)
|
|
949
|
+
|
|
905
950
|
# Draw region selector (bottom)
|
|
906
951
|
self.region_selector.draw(screen)
|
|
907
952
|
|
|
@@ -226,11 +226,13 @@ class Tilemap:
|
|
|
226
226
|
if path_obj.is_absolute():
|
|
227
227
|
target_path = path_obj
|
|
228
228
|
else:
|
|
229
|
-
data_root = getattr(getattr(self,
|
|
229
|
+
data_root = getattr(getattr(self, "editor", None), "data_root", None)
|
|
230
230
|
if data_root:
|
|
231
231
|
target_path = data_root / path_obj
|
|
232
232
|
else:
|
|
233
|
-
raise RuntimeError(
|
|
233
|
+
raise RuntimeError(
|
|
234
|
+
"Cannot determine data_root - Editor not initialized with settings.json"
|
|
235
|
+
)
|
|
234
236
|
|
|
235
237
|
self.active_project_path = target_path
|
|
236
238
|
elif self.active_project_path:
|
|
@@ -241,7 +243,7 @@ class Tilemap:
|
|
|
241
243
|
if not target_path.parent.exists():
|
|
242
244
|
target_path.parent.mkdir(parents=True, exist_ok=True)
|
|
243
245
|
|
|
244
|
-
|
|
246
|
+
map_dir = target_path.parent
|
|
245
247
|
|
|
246
248
|
save_data = {
|
|
247
249
|
"meta": {
|
|
@@ -279,7 +281,7 @@ class Tilemap:
|
|
|
279
281
|
|
|
280
282
|
if hasattr(self.editor, "tileset_widget") and self.editor.tileset_widget:
|
|
281
283
|
for ts in self.editor.tileset_widget.tilesets:
|
|
282
|
-
path_str = to_project_path(ts.path,
|
|
284
|
+
path_str = to_project_path(ts.path, map_dir)
|
|
283
285
|
|
|
284
286
|
ts_data: Dict[str, Any] = {"path": path_str, "type": ts.tileset_type}
|
|
285
287
|
if ts.properties:
|
|
@@ -301,7 +303,7 @@ class Tilemap:
|
|
|
301
303
|
{
|
|
302
304
|
"name": group.name,
|
|
303
305
|
"rules": [
|
|
304
|
-
self._serialize_autotile_rule(rule,
|
|
306
|
+
self._serialize_autotile_rule(rule, map_dir)
|
|
305
307
|
for rule in group.rules
|
|
306
308
|
],
|
|
307
309
|
}
|
|
@@ -407,14 +409,16 @@ class Tilemap:
|
|
|
407
409
|
return data
|
|
408
410
|
|
|
409
411
|
def _path_matches_project_path(self, stored_path: str, actual_path: Path) -> bool:
|
|
410
|
-
|
|
412
|
+
map_dir = (
|
|
413
|
+
self.active_project_path.parent if self.active_project_path else Path()
|
|
414
|
+
)
|
|
411
415
|
stored = Path(stored_path).expanduser()
|
|
412
416
|
actual = Path(actual_path).expanduser()
|
|
413
417
|
|
|
414
418
|
if stored.is_absolute():
|
|
415
419
|
return stored.resolve() == actual.resolve()
|
|
416
420
|
|
|
417
|
-
return stored.as_posix() == to_project_path(actual,
|
|
421
|
+
return stored.as_posix() == to_project_path(actual, map_dir)
|
|
418
422
|
|
|
419
423
|
def _resolve_rule_resources(self, rule: "AutotileRule", ts_widget):
|
|
420
424
|
resolved_ts = None
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#e0e0e0"><circle cx="8" cy="8" r="2"/><path d="M8 1a1 1 0 0 0-.691.291l-2 2 1.414 1.414 1.293-1.293 1.293 1.293 1.414-1.414-2-2A1 1 0 0 0 8 1zM3.309 5.291l-2 2a1 1 0 0 0 0 1.414l2 2 1.414-1.414L3.43 7.998l1.293-1.293-1.414-1.414zm9.414 0-1.414 1.414 1.293 1.293-1.293 1.293 1.414 1.414 2-2a1 1 0 0 0 0-1.414l-2-2zm-6 6-1.414 1.414 2 2a1 1 0 0 0 1.414 0l2-2-1.414-1.414-1.293 1.293-1.293-1.293z"/></g></svg>
|
|
@@ -101,6 +101,7 @@ src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf
|
|
|
101
101
|
src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf
|
|
102
102
|
src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf
|
|
103
103
|
src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf
|
|
104
|
+
src/tilemap_editor/assets/icons/ToolMove.svg
|
|
104
105
|
src/tilemap_editor/assets/icons/arrow-down.svg
|
|
105
106
|
src/tilemap_editor/assets/icons/check.svg
|
|
106
107
|
src/tilemap_editor/assets/icons/checked.svg
|
|
@@ -156,6 +157,8 @@ src/widgets/mapsetup.py
|
|
|
156
157
|
src/widgets/regex_automap_designer.py
|
|
157
158
|
src/widgets/tile_grid.py
|
|
158
159
|
src/widgets/tile_selector.py
|
|
160
|
+
src/widgets/ui/collision_layer_mask.py
|
|
161
|
+
src/widgets/ui/collision_layer_sidebar.py
|
|
159
162
|
src/widgets/ui/draw_utils.py
|
|
160
163
|
src/widgets/ui/fileinput.py
|
|
161
164
|
src/widgets/ui/layer_type_dialog.py
|
|
@@ -168,4 +171,7 @@ src/widgets/ui/status_bar.py
|
|
|
168
171
|
src/widgets/ui/theme.py
|
|
169
172
|
src/widgets/ui/tileset_type_dialog.py
|
|
170
173
|
src/widgets/ui/toolbar.py
|
|
171
|
-
src/widgets/ui/tooltip.py
|
|
174
|
+
src/widgets/ui/tooltip.py
|
|
175
|
+
tests/test_collision_layer_mask.py
|
|
176
|
+
tests/test_collision_layer_sidebar.py
|
|
177
|
+
tests/test_project_paths.py
|