tilemap-editor 4.3.4__tar.gz → 4.3.6__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-4.3.4/src/tilemap_editor.egg-info → tilemap_editor-4.3.6}/PKG-INFO +13 -1
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/README.md +12 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/pyproject.toml +1 -1
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/editor.py +2 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/node_manager.py +23 -7
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/character_collision/editor.py +25 -2
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/character_collision/shape_editor.py +39 -6
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/object_tileset_collision/editor.py +3 -2
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/tileset_collision/collision_painter.py +13 -6
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/tileset_collision/editor.py +19 -4
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap.py +2 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6/src/tilemap_editor.egg-info}/PKG-INFO +13 -1
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor.egg-info/SOURCES.txt +1 -0
- tilemap_editor-4.3.6/src/utils/property_suggestions.py +70 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/layer_selector.py +1 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/tile_grid.py +3 -1
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/tile_selector.py +5 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/node_editor.py +2 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/property_editor.py +118 -18
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/region_selector.py +23 -12
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/LICENSE +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/MANIFEST.in +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/check.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/checked.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/close.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/error.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/file.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/filedead.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/fit.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/folder.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/grid.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/image.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/info.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/load.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/loop.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/onion.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/pan.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/pause.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/pencil.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/play.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/plus.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/radio.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/reset.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/save.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/skip-back.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/skip-forward.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/stop.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/tileset.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/warning.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/setup.cfg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/constants.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/event_map.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/layers.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/main.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/nodes.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/__init__.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/character_collision/__init__.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/character_collision/models.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/character_collision/protocols.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/character_collision/standalone.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/object_tileset_collision/__init__.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/object_tileset_collision/models.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/object_tileset_collision/protocols.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/object_tileset_collision/standalone.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/__init__.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/__main__.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/clipboard_util.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/editor.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/frame_picker.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/models.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/preview.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/protocols.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/runtime_load.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/standalone.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/timeline.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_animation/validation.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_editor/__init__.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_editor/dialogs.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_editor/editor.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_editor/region_export.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/sprite_editor/standalone.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/tileset_collision/__init__.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/tileset_collision/models.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/tileset_collision/protocols.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/tileset_collision/standalone.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/standalone_automap.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/standalone_error_console.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/standalone_filemanager.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/standalone_image_viewer.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/__init__.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/__main__.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/__init__.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/ToolMove.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/arrow-down.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/check.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/checked.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/close.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/duplicate.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/error.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/file.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/filedead.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/fit.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/fold_down_arrow.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/folder.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/gear.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/grid.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/image.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/info.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/load.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/loop.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/miniobj.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/onion.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/pan.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/pause.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/pencil.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/play.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/plus.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/radio.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/reset.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/save.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/skip-back.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/skip-forward.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/stop.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/tilemap.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/tileset.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/unchecked.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/unfold_right_arrow.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/warning.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/zoomin.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/assets/icons/zoomout.svg +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/cli.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/main.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor/settings.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor.egg-info/requires.txt +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/tilemap_editor.egg-info/top_level.txt +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/ttypes/__init__.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/ttypes/tilemap.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/__init__.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/editor_preference.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/error_handler.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/font_manager.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/history.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/icon_manager.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/icons_cache.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/log_capture.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/project_paths.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/serialization.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/standalone.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/utils/validation.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/__init__.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/automap_models.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/autotile_template.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/autotiler.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/filemanager.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/input.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/map_properties.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/mapsetup.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/particle_presets.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/particle_system.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/regex_automap_designer.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/spritesheet_grid.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/button.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/checkbox.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/collision_layer_mask.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/collision_layer_sidebar.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/confirm_dialog.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/dialog_base.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/drag_tracker.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/draw_utils.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/fileinput.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/label.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/layer_type_dialog.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/menubar.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/mode_indicator.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/node_selector.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/notification.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/particle_config_dialog.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/scrollbar.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/sidebar_container.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/splitter.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/status_bar.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/theme.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/tileset_type_dialog.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/toast.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/tool_manager.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/toolbar.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/tooltip.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/ui/tree_widget.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/widgets/widget_base.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_animation_library_grid_offset.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_collision_layer_mask.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_collision_layer_sidebar.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_drag_tracker.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_editor_pan_mode.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_gid_collision.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_layers_autotile.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_project_paths.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_render_scale.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_serialization_regression.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_sprite_animation_editor_grid.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_tile_grid_selection.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_tile_selector_pick.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_tilemap_save.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_timeline_invalidate_cache.py +0 -0
- {tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/tests/test_toolbar_tools.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tilemap-editor
|
|
3
|
-
Version: 4.3.
|
|
3
|
+
Version: 4.3.6
|
|
4
4
|
Summary: Pygame tilemap editor with collision, animation, sprite & particle tools
|
|
5
5
|
Author: tilemap editor contributors
|
|
6
6
|
License: Proprietary
|
|
@@ -18,6 +18,15 @@ Dynamic: license-file
|
|
|
18
18
|
|
|
19
19
|
`tilemap-editor` is a pygame-based map editor focused on fast iteration for 2D games.
|
|
20
20
|
|
|
21
|
+
# tilemap-editor
|
|
22
|
+
|
|
23
|
+
`tilemap-editor` is a Pygame-based map editor focused on fast iteration for 2D games.
|
|
24
|
+
|
|
25
|
+
> **Credits:** The initial version of this project was based on DaFluffyPotato's platformer tutorial which is about 6-hours. If you want to understand the foundation this project started from, I highly recommend watching his video: https://www.youtube.com/watch?v=2gABYM5M0ww. While the editor has since evolved with many additional features and improvements, the original map format has been preserved for compatibility.
|
|
26
|
+
|
|
27
|
+
Several features and design ideas were also inspired by the Godot Engine, including the animation workflow, polygon collision editing, and character collision (Sprite2D-style) editing.
|
|
28
|
+
|
|
29
|
+
|
|
21
30
|
## Installation
|
|
22
31
|
|
|
23
32
|
[tilemap-editor on PyPI](https://pypi.org/project/tilemap-editor/)
|
|
@@ -55,3 +64,6 @@ if __name__ == "__main__":
|
|
|
55
64
|
editor = Editor()
|
|
56
65
|
editor.run()
|
|
57
66
|
```
|
|
67
|
+
|
|
68
|
+
> [!WARNING]
|
|
69
|
+
> The current release requires **Python 3.11+**. The documentation incorrectly lists Python 3.10 support due to an accidental dependency on `typing.NotRequired`. This will be fixed in a future update.
|
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
`tilemap-editor` is a pygame-based map editor focused on fast iteration for 2D games.
|
|
4
4
|
|
|
5
|
+
# tilemap-editor
|
|
6
|
+
|
|
7
|
+
`tilemap-editor` is a Pygame-based map editor focused on fast iteration for 2D games.
|
|
8
|
+
|
|
9
|
+
> **Credits:** The initial version of this project was based on DaFluffyPotato's platformer tutorial which is about 6-hours. If you want to understand the foundation this project started from, I highly recommend watching his video: https://www.youtube.com/watch?v=2gABYM5M0ww. While the editor has since evolved with many additional features and improvements, the original map format has been preserved for compatibility.
|
|
10
|
+
|
|
11
|
+
Several features and design ideas were also inspired by the Godot Engine, including the animation workflow, polygon collision editing, and character collision (Sprite2D-style) editing.
|
|
12
|
+
|
|
13
|
+
|
|
5
14
|
## Installation
|
|
6
15
|
|
|
7
16
|
[tilemap-editor on PyPI](https://pypi.org/project/tilemap-editor/)
|
|
@@ -39,3 +48,6 @@ if __name__ == "__main__":
|
|
|
39
48
|
editor = Editor()
|
|
40
49
|
editor.run()
|
|
41
50
|
```
|
|
51
|
+
|
|
52
|
+
> [!WARNING]
|
|
53
|
+
> The current release requires **Python 3.11+**. The documentation incorrectly lists Python 3.10 support due to an accidental dependency on `typing.NotRequired`. This will be fixed in a future update.
|
|
@@ -21,6 +21,7 @@ from constants import BASE_PATH
|
|
|
21
21
|
from node_manager import NodeManager
|
|
22
22
|
from tilemap import Tilemap
|
|
23
23
|
from utils import error_context, error_handler
|
|
24
|
+
from utils.property_suggestions import PropertySuggestionRegistry
|
|
24
25
|
from utils.standalone import launch_standalone
|
|
25
26
|
from widgets.autotiler import AutotileRuleDesigner
|
|
26
27
|
from widgets.layer_selector import LayerSelector
|
|
@@ -212,6 +213,7 @@ class Editor:
|
|
|
212
213
|
self.regex_automap_designer = RegexAutomapDesigner(self, 150, 100)
|
|
213
214
|
self.notifications = NotificationManager(self)
|
|
214
215
|
self.tooltip = TooltipManager()
|
|
216
|
+
self.suggestion_registry = PropertySuggestionRegistry()
|
|
215
217
|
self.error_console_process: subprocess.Popen | None = None
|
|
216
218
|
self.property_editor: PropertyEditor | None = None
|
|
217
219
|
self.particle_config_dialog: ParticleConfigDialog | None = None
|
|
@@ -13,7 +13,7 @@ def create_node_id() -> str:
|
|
|
13
13
|
return str(uuid.uuid4())
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
SIDECAR_VERSION =
|
|
16
|
+
SIDECAR_VERSION = 2
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class NodeManager:
|
|
@@ -49,10 +49,15 @@ class NodeManager:
|
|
|
49
49
|
raw = json.loads(sidecar.read_text(encoding="utf-8"))
|
|
50
50
|
if not isinstance(raw, dict):
|
|
51
51
|
return
|
|
52
|
+
version = raw.get("version", 1)
|
|
52
53
|
self.groups = list(raw.get("groups", []))
|
|
54
|
+
rs = self.editor.tilemap.render_scale
|
|
53
55
|
for item in raw.get("nodes", []):
|
|
54
56
|
if isinstance(item, dict):
|
|
55
57
|
node = Node.from_dict(item)
|
|
58
|
+
if version >= 2:
|
|
59
|
+
node.area.x = int(node.area.x * rs)
|
|
60
|
+
node.area.y = int(node.area.y * rs)
|
|
56
61
|
self.nodes[node.node_id] = node
|
|
57
62
|
except (json.JSONDecodeError, KeyError, ValueError, TypeError):
|
|
58
63
|
import logging
|
|
@@ -62,12 +67,23 @@ class NodeManager:
|
|
|
62
67
|
def save(self, map_path: Path) -> None:
|
|
63
68
|
sidecar = self._sidecar_path_for(map_path)
|
|
64
69
|
self.nodes_dir.mkdir(parents=True, exist_ok=True)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
rs = self.editor.tilemap.render_scale
|
|
71
|
+
orig_areas: dict[str, tuple[int, int]] = {}
|
|
72
|
+
for nid, node in self.nodes.items():
|
|
73
|
+
orig_areas[nid] = (node.area.x, node.area.y)
|
|
74
|
+
node.area.x = int(node.area.x / rs)
|
|
75
|
+
node.area.y = int(node.area.y / rs)
|
|
76
|
+
try:
|
|
77
|
+
data: dict[str, Any] = {
|
|
78
|
+
"version": SIDECAR_VERSION,
|
|
79
|
+
"groups": self.groups,
|
|
80
|
+
"nodes": [node.to_dict() for node in self.nodes.values()],
|
|
81
|
+
}
|
|
82
|
+
sidecar.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
|
83
|
+
finally:
|
|
84
|
+
for nid, (ox, oy) in orig_areas.items():
|
|
85
|
+
self.nodes[nid].area.x = ox
|
|
86
|
+
self.nodes[nid].area.y = oy
|
|
71
87
|
|
|
72
88
|
def add_node(self, node: Node) -> str:
|
|
73
89
|
self.nodes[node.node_id] = node
|
|
@@ -182,11 +182,32 @@ class CharacterCollisionEditor:
|
|
|
182
182
|
self._image_path = self._rel_path(path)
|
|
183
183
|
pygame.display.set_caption(f"Character Collision Editor — {path.stem}")
|
|
184
184
|
self._toast_manager.success(f"Loaded sprite: {path.name}")
|
|
185
|
+
self._auto_load_collision()
|
|
185
186
|
except Exception as e:
|
|
186
187
|
self._toast_manager.error(f"Failed to load image: {e}")
|
|
187
188
|
finally:
|
|
188
189
|
self._image_dialog = None
|
|
189
190
|
|
|
191
|
+
def _auto_load_collision(self) -> bool:
|
|
192
|
+
name = self._name_input.text.strip()
|
|
193
|
+
if not name:
|
|
194
|
+
return False
|
|
195
|
+
safe = re.sub(r"[^\w\-]+", "-", name).strip("-").lower()
|
|
196
|
+
coll_filename = f"{safe}.collision.json"
|
|
197
|
+
try:
|
|
198
|
+
coll_dir = self._get_collision_dir()
|
|
199
|
+
except RuntimeError:
|
|
200
|
+
return False
|
|
201
|
+
coll_path = coll_dir / coll_filename
|
|
202
|
+
if coll_path.exists():
|
|
203
|
+
try:
|
|
204
|
+
self.load_from_file(coll_path)
|
|
205
|
+
self._toast_manager.success(f"Loaded collision: {coll_path.name}")
|
|
206
|
+
return True
|
|
207
|
+
except Exception:
|
|
208
|
+
pass
|
|
209
|
+
return False
|
|
210
|
+
|
|
190
211
|
def _open_image_dialog(self) -> None:
|
|
191
212
|
try:
|
|
192
213
|
data_root = self._data_root or Path.cwd()
|
|
@@ -281,7 +302,6 @@ class CharacterCollisionEditor:
|
|
|
281
302
|
self.character_name = data.name
|
|
282
303
|
self._name_input.text = data.name
|
|
283
304
|
self._name_input.cursor_pos = len(data.name)
|
|
284
|
-
self.shape_editor.load_shape_data(data.shape.to_dict())
|
|
285
305
|
layer = data.properties.get("collision_layer", 1)
|
|
286
306
|
mask = data.properties.get("collision_mask", 0xFFFF)
|
|
287
307
|
self.layer_sidebar.set_layer(layer)
|
|
@@ -293,7 +313,9 @@ class CharacterCollisionEditor:
|
|
|
293
313
|
if resolved:
|
|
294
314
|
self._load_sprite_from_path(resolved)
|
|
295
315
|
self._toast_manager.success(f"Sprite loaded: {resolved.name}")
|
|
296
|
-
|
|
316
|
+
|
|
317
|
+
self.shape_editor.load_shape_data(data.shape.to_dict())
|
|
318
|
+
return
|
|
297
319
|
|
|
298
320
|
if data.name:
|
|
299
321
|
for d in [self._data_root]:
|
|
@@ -545,6 +567,7 @@ class CharacterCollisionEditor:
|
|
|
545
567
|
editor = cls(rect, surface, character_name)
|
|
546
568
|
editor._data_root = data_root
|
|
547
569
|
editor._image_path = image_path
|
|
570
|
+
editor._auto_load_collision()
|
|
548
571
|
return editor
|
|
549
572
|
|
|
550
573
|
def run(self) -> None:
|
{tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/character_collision/shape_editor.py
RENAMED
|
@@ -100,6 +100,30 @@ class ShapeEditor:
|
|
|
100
100
|
self.offset_x = (self.rect.w - sw * self.zoom) / 2
|
|
101
101
|
self.offset_y = (self.rect.h - sh * self.zoom) / 2
|
|
102
102
|
|
|
103
|
+
def _focus_on_shape(self) -> None:
|
|
104
|
+
"""Center the viewport on the shape's bounding box"""
|
|
105
|
+
left, top, right, bottom = self._shape_bounds()
|
|
106
|
+
cx = (left + right) / 2
|
|
107
|
+
cy = (top + bottom) / 2
|
|
108
|
+
self.offset_x = self.rect.w / 2 - cx * self.zoom
|
|
109
|
+
self.offset_y = self.rect.h / 2 - cy * self.zoom
|
|
110
|
+
|
|
111
|
+
def _shape_bounds(self) -> tuple[float, float, float, float]:
|
|
112
|
+
"""Get shape bounding box in sprite-local coords (left, top, right, bottom)"""
|
|
113
|
+
if self.shape_type == "rectangle":
|
|
114
|
+
return (self.rect_x, self.rect_y, self.rect_x + self.rect_w, self.rect_y + self.rect_h)
|
|
115
|
+
if self.shape_type == "circle":
|
|
116
|
+
return (self.circle_x - self.circle_radius, self.circle_y - self.circle_radius,
|
|
117
|
+
self.circle_x + self.circle_radius, self.circle_y + self.circle_radius)
|
|
118
|
+
if self.shape_type == "capsule":
|
|
119
|
+
return (self.capsule_x - self.capsule_radius, self.capsule_y,
|
|
120
|
+
self.capsule_x + self.capsule_radius, self.capsule_y + self.capsule_height)
|
|
121
|
+
if self.shape_type == "polygon" and self.polygon_vertices:
|
|
122
|
+
xs = [v[0] for v in self.polygon_vertices]
|
|
123
|
+
ys = [v[1] for v in self.polygon_vertices]
|
|
124
|
+
return (min(xs), min(ys), max(xs), max(ys))
|
|
125
|
+
return (0, 0, 0, 0)
|
|
126
|
+
|
|
103
127
|
def _center_shape(self) -> None:
|
|
104
128
|
"""Center all shape types on the sprite"""
|
|
105
129
|
sw, sh = self.sprite_surface.get_size()
|
|
@@ -195,6 +219,8 @@ class ShapeEditor:
|
|
|
195
219
|
elif shape_type == "polygon":
|
|
196
220
|
self.polygon_vertices = [tuple(v) for v in data.get("vertices", [])]
|
|
197
221
|
|
|
222
|
+
self._focus_on_shape()
|
|
223
|
+
|
|
198
224
|
def resize(self, rect: Rect) -> None:
|
|
199
225
|
"""Update rect"""
|
|
200
226
|
self.rect = rect
|
|
@@ -254,12 +280,19 @@ class ShapeEditor:
|
|
|
254
280
|
self.hover_handle = None
|
|
255
281
|
|
|
256
282
|
if event.type == pygame.MOUSEWHEEL and self.rect.collidepoint(mouse):
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
283
|
+
mods = pygame.key.get_mods()
|
|
284
|
+
if mods & (pygame.KMOD_CTRL | pygame.KMOD_META):
|
|
285
|
+
self.zoom *= 1.15 if event.y > 0 else 0.87
|
|
286
|
+
self.zoom = max(0.5, min(self.zoom, 8.0))
|
|
287
|
+
sprite_pos = self._screen_to_sprite(mouse)
|
|
288
|
+
self.offset_x = mouse[0] - self.rect.x - sprite_pos[0] * self.zoom
|
|
289
|
+
self.offset_y = mouse[1] - self.rect.y - sprite_pos[1] * self.zoom
|
|
290
|
+
elif mods & pygame.KMOD_SHIFT:
|
|
291
|
+
scroll_val = event.y if event.y != 0 else event.x
|
|
292
|
+
self.offset_x -= scroll_val * 30
|
|
293
|
+
else:
|
|
294
|
+
scroll_val = event.y if event.y != 0 else event.x
|
|
295
|
+
self.offset_y -= scroll_val * 30
|
|
263
296
|
return True
|
|
264
297
|
|
|
265
298
|
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
|
{tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/object_tileset_collision/editor.py
RENAMED
|
@@ -234,12 +234,13 @@ class ObjectTilesetCollisionEditor:
|
|
|
234
234
|
def _layout_toolbar_buttons(self) -> None:
|
|
235
235
|
x = self.toolbar_rect.x + 10
|
|
236
236
|
y = self.toolbar_rect.y + 8
|
|
237
|
-
for
|
|
237
|
+
for btn in self._toolbar_buttons:
|
|
238
238
|
if btn.text == "?":
|
|
239
239
|
w, h = 28, 28
|
|
240
240
|
else:
|
|
241
241
|
w, h = 60, 28
|
|
242
|
-
btn.resize(x
|
|
242
|
+
btn.resize(x, y, w, h)
|
|
243
|
+
x += w + 8
|
|
243
244
|
|
|
244
245
|
def _on_splitter_drag(self, pos: int) -> None:
|
|
245
246
|
self.tileset_selector_height = max(
|
{tilemap_editor-4.3.4 → tilemap_editor-4.3.6}/src/plugins/tileset_collision/collision_painter.py
RENAMED
|
@@ -476,12 +476,19 @@ class CollisionPainter:
|
|
|
476
476
|
return True
|
|
477
477
|
|
|
478
478
|
if event.type == pygame.MOUSEWHEEL and self.rect.collidepoint(mouse):
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
479
|
+
mods = pygame.key.get_mods()
|
|
480
|
+
if mods & (pygame.KMOD_CTRL | pygame.KMOD_META):
|
|
481
|
+
self.zoom *= 1.15 if event.y > 0 else 0.87
|
|
482
|
+
self.zoom = max(0.5, min(self.zoom, 8.0))
|
|
483
|
+
tile_pos = self._screen_to_tile(mouse)
|
|
484
|
+
self.offset_x = mouse[0] - self.rect.x - tile_pos[0] * self.zoom
|
|
485
|
+
self.offset_y = mouse[1] - self.rect.y - tile_pos[1] * self.zoom
|
|
486
|
+
elif mods & pygame.KMOD_SHIFT:
|
|
487
|
+
scroll_val = event.y if event.y != 0 else event.x
|
|
488
|
+
self.offset_x -= scroll_val * 30
|
|
489
|
+
else:
|
|
490
|
+
scroll_val = event.y if event.y != 0 else event.x
|
|
491
|
+
self.offset_y -= scroll_val * 30
|
|
485
492
|
return True
|
|
486
493
|
|
|
487
494
|
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
|
|
@@ -716,8 +716,18 @@ class TilesetCollisionEditor:
|
|
|
716
716
|
return True
|
|
717
717
|
|
|
718
718
|
if event.type == pygame.MOUSEWHEEL:
|
|
719
|
-
|
|
720
|
-
|
|
719
|
+
mods = pygame.key.get_mods()
|
|
720
|
+
if mods & (pygame.KMOD_CTRL | pygame.KMOD_META):
|
|
721
|
+
self.tileset_zoom *= 1.15 if event.y > 0 else 0.87
|
|
722
|
+
self.tileset_zoom = max(0.5, min(self.tileset_zoom, 8.0))
|
|
723
|
+
elif mods & pygame.KMOD_SHIFT:
|
|
724
|
+
scroll_val = event.y if event.y != 0 else event.x
|
|
725
|
+
self.tileset_scroll_x -= scroll_val * 30
|
|
726
|
+
else:
|
|
727
|
+
scroll_val = event.y if event.y != 0 else event.x
|
|
728
|
+
self.tileset_scroll_y -= scroll_val * 30
|
|
729
|
+
self.tileset_scroll_x = max(0, self.tileset_scroll_x)
|
|
730
|
+
self.tileset_scroll_y = max(0, self.tileset_scroll_y)
|
|
721
731
|
return True
|
|
722
732
|
|
|
723
733
|
if event.type == pygame.MOUSEBUTTONUP:
|
|
@@ -947,12 +957,17 @@ class TilesetCollisionEditor:
|
|
|
947
957
|
tw = int(self._tile_size[0] * self.tileset_zoom)
|
|
948
958
|
th = int(self._tile_size[1] * self.tileset_zoom)
|
|
949
959
|
|
|
960
|
+
tileset_scr_w = self.tile_cols * tw
|
|
961
|
+
tileset_scr_h = self.tile_rows * th
|
|
962
|
+
center_off_x = max(0, (self.tileset_selector_rect.w - tileset_scr_w) // 2)
|
|
963
|
+
center_off_y = max(0, (self.tileset_selector_rect.h - tileset_scr_h) // 2)
|
|
964
|
+
|
|
950
965
|
for row in range(self.tile_rows):
|
|
951
966
|
for col in range(self.tile_cols):
|
|
952
967
|
tile_id = row * self.tile_cols + col
|
|
953
968
|
|
|
954
|
-
x = self.tileset_selector_rect.x + col * tw - self.tileset_scroll_x
|
|
955
|
-
y = self.tileset_selector_rect.y + row * th - self.tileset_scroll_y
|
|
969
|
+
x = self.tileset_selector_rect.x + col * tw - self.tileset_scroll_x + center_off_x
|
|
970
|
+
y = self.tileset_selector_rect.y + row * th - self.tileset_scroll_y + center_off_y
|
|
956
971
|
|
|
957
972
|
if (
|
|
958
973
|
x + tw < self.tileset_selector_rect.x
|
|
@@ -623,6 +623,8 @@ class Tilemap:
|
|
|
623
623
|
if hasattr(self.editor, "node_selector"):
|
|
624
624
|
self.editor.node_selector._rebuild_filter()
|
|
625
625
|
|
|
626
|
+
if hasattr(self.editor, "suggestion_registry"):
|
|
627
|
+
self.editor.suggestion_registry.refresh(self.editor)
|
|
626
628
|
self.initialized = True
|
|
627
629
|
|
|
628
630
|
if self.editor.tile_grid_widget:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tilemap-editor
|
|
3
|
-
Version: 4.3.
|
|
3
|
+
Version: 4.3.6
|
|
4
4
|
Summary: Pygame tilemap editor with collision, animation, sprite & particle tools
|
|
5
5
|
Author: tilemap editor contributors
|
|
6
6
|
License: Proprietary
|
|
@@ -18,6 +18,15 @@ Dynamic: license-file
|
|
|
18
18
|
|
|
19
19
|
`tilemap-editor` is a pygame-based map editor focused on fast iteration for 2D games.
|
|
20
20
|
|
|
21
|
+
# tilemap-editor
|
|
22
|
+
|
|
23
|
+
`tilemap-editor` is a Pygame-based map editor focused on fast iteration for 2D games.
|
|
24
|
+
|
|
25
|
+
> **Credits:** The initial version of this project was based on DaFluffyPotato's platformer tutorial which is about 6-hours. If you want to understand the foundation this project started from, I highly recommend watching his video: https://www.youtube.com/watch?v=2gABYM5M0ww. While the editor has since evolved with many additional features and improvements, the original map format has been preserved for compatibility.
|
|
26
|
+
|
|
27
|
+
Several features and design ideas were also inspired by the Godot Engine, including the animation workflow, polygon collision editing, and character collision (Sprite2D-style) editing.
|
|
28
|
+
|
|
29
|
+
|
|
21
30
|
## Installation
|
|
22
31
|
|
|
23
32
|
[tilemap-editor on PyPI](https://pypi.org/project/tilemap-editor/)
|
|
@@ -55,3 +64,6 @@ if __name__ == "__main__":
|
|
|
55
64
|
editor = Editor()
|
|
56
65
|
editor.run()
|
|
57
66
|
```
|
|
67
|
+
|
|
68
|
+
> [!WARNING]
|
|
69
|
+
> The current release requires **Python 3.11+**. The documentation incorrectly lists Python 3.10 support due to an accidental dependency on `typing.NotRequired`. This will be fixed in a future update.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from editor import Editor
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PropertySuggestionRegistry:
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self._data: dict[str, list[str]] = {}
|
|
10
|
+
|
|
11
|
+
def refresh(self, editor: "Editor"):
|
|
12
|
+
values: dict[str, set[str]] = {}
|
|
13
|
+
|
|
14
|
+
if editor.tileset_widget:
|
|
15
|
+
for ts in editor.tileset_widget.tilesets:
|
|
16
|
+
for k, v in ts.properties.items():
|
|
17
|
+
values.setdefault(k, set()).add(str(v))
|
|
18
|
+
for tile_props in ts.tile_properties.values():
|
|
19
|
+
for k, v in tile_props.items():
|
|
20
|
+
values.setdefault(k, set()).add(str(v))
|
|
21
|
+
|
|
22
|
+
if editor.tilemap:
|
|
23
|
+
for layer in editor.tilemap.layer_manager.layers:
|
|
24
|
+
for k, v in layer.properties.items():
|
|
25
|
+
values.setdefault(k, set()).add(str(v))
|
|
26
|
+
for tile in layer.tiles.values():
|
|
27
|
+
if "properties" in tile:
|
|
28
|
+
for k, v in tile["properties"].items():
|
|
29
|
+
values.setdefault(k, set()).add(str(v))
|
|
30
|
+
if layer.layer_type == "object":
|
|
31
|
+
for obj in layer.objects.values():
|
|
32
|
+
if "properties" in obj:
|
|
33
|
+
for k, v in obj["properties"].items():
|
|
34
|
+
values.setdefault(k, set()).add(str(v))
|
|
35
|
+
|
|
36
|
+
if editor.node_manager:
|
|
37
|
+
for node in editor.node_manager.nodes.values():
|
|
38
|
+
for k, v in node.properties.items():
|
|
39
|
+
values.setdefault(k, set()).add(str(v))
|
|
40
|
+
|
|
41
|
+
self._data = {k: sorted(v) for k, v in values.items()}
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def known_keys(self) -> list[str]:
|
|
45
|
+
return sorted(self._data.keys())
|
|
46
|
+
|
|
47
|
+
def ghost_text(self, key: str, input_text: str) -> str:
|
|
48
|
+
if key not in self._data:
|
|
49
|
+
return ""
|
|
50
|
+
prefix = input_text.lower()
|
|
51
|
+
matches = [
|
|
52
|
+
v
|
|
53
|
+
for v in self._data[key]
|
|
54
|
+
if v.lower().startswith(prefix) and v != input_text
|
|
55
|
+
]
|
|
56
|
+
if matches:
|
|
57
|
+
return matches[0][len(input_text):]
|
|
58
|
+
return ""
|
|
59
|
+
|
|
60
|
+
def key_ghost(self, input_text: str) -> str:
|
|
61
|
+
if not input_text:
|
|
62
|
+
return ""
|
|
63
|
+
prefix = input_text.lower()
|
|
64
|
+
matches = [
|
|
65
|
+
k for k in self._data
|
|
66
|
+
if k.lower().startswith(prefix) and k != input_text
|
|
67
|
+
]
|
|
68
|
+
if matches:
|
|
69
|
+
return sorted(matches)[0][len(input_text):]
|
|
70
|
+
return ""
|
|
@@ -408,6 +408,7 @@ class LayerSelector:
|
|
|
408
408
|
|
|
409
409
|
def _save_layer_properties(self, layer: Layer, props: dict):
|
|
410
410
|
layer.properties = props
|
|
411
|
+
self.editor.suggestion_registry.refresh(self.editor)
|
|
411
412
|
print(f"Saved properties for layer: {layer.name}")
|
|
412
413
|
|
|
413
414
|
def draw(self, screen: Surface) -> None:
|
|
@@ -799,10 +799,12 @@ class TileGrid:
|
|
|
799
799
|
elif active_layer.layer_type == "object":
|
|
800
800
|
mouse_pos = pygame.mouse.get_pos()
|
|
801
801
|
world_pos = self.screen_to_world(mouse_pos)
|
|
802
|
+
rs = self.editor.tilemap.render_scale
|
|
803
|
+
pick_pos = (int(world_pos[0] / rs), int(world_pos[1] / rs)) if rs else world_pos
|
|
802
804
|
for _obj_id, obj in active_layer.get_all_objects().items():
|
|
803
805
|
area = obj["area"]
|
|
804
806
|
obj_rect = Rect(area["x"], area["y"], area["w"], area["h"])
|
|
805
|
-
if obj_rect.collidepoint(
|
|
807
|
+
if obj_rect.collidepoint(pick_pos):
|
|
806
808
|
ttype = int(obj["ttype"])
|
|
807
809
|
variant = obj["variant"]
|
|
808
810
|
ts_widget.select_tile_by_variant(ttype, variant)
|
|
@@ -416,6 +416,7 @@ class TileSelector(WidgetBase):
|
|
|
416
416
|
self._tree.selected_ids = {tileset_data.uid}
|
|
417
417
|
if tileset_type == "object":
|
|
418
418
|
self.selected_tile = (0, 0, surf.get_width(), surf.get_height())
|
|
419
|
+
self.editor.suggestion_registry.refresh(self.editor)
|
|
419
420
|
except Exception as e:
|
|
420
421
|
error_handler.capture(e, context="load_tileset_surface")
|
|
421
422
|
|
|
@@ -479,6 +480,7 @@ class TileSelector(WidgetBase):
|
|
|
479
480
|
self._sync_tree()
|
|
480
481
|
self._tree.selected_ids = {tileset_data.uid}
|
|
481
482
|
|
|
483
|
+
self.editor.suggestion_registry.refresh(self.editor)
|
|
482
484
|
delattr(self, "_pending_tileset_path")
|
|
483
485
|
delattr(self, "_pending_tileset_surf")
|
|
484
486
|
if self._pending_tileset_queue:
|
|
@@ -561,6 +563,7 @@ class TileSelector(WidgetBase):
|
|
|
561
563
|
if not self.tilesets:
|
|
562
564
|
self.active_idx = -1
|
|
563
565
|
self._sync_tree()
|
|
566
|
+
self.editor.suggestion_registry.refresh(self.editor)
|
|
564
567
|
|
|
565
568
|
def open_collision_editor(self):
|
|
566
569
|
if self.active_idx == -1 or self.active_idx >= len(self.tilesets):
|
|
@@ -573,6 +576,7 @@ class TileSelector(WidgetBase):
|
|
|
573
576
|
|
|
574
577
|
def _save_tileset_properties(self, ts: TilesetData, props: dict):
|
|
575
578
|
ts.properties = props
|
|
579
|
+
self.editor.suggestion_registry.refresh(self.editor)
|
|
576
580
|
print(f"Saved properties for tileset: {ts.name}")
|
|
577
581
|
|
|
578
582
|
def _save_tile_properties(self, ts: TilesetData, variant_id: int, props: dict):
|
|
@@ -582,6 +586,7 @@ class TileSelector(WidgetBase):
|
|
|
582
586
|
def _save_tile_properties_multi(self, ts: TilesetData, variant_ids: list[int], props: dict):
|
|
583
587
|
for vid in variant_ids:
|
|
584
588
|
ts.tile_properties[vid] = props.copy()
|
|
589
|
+
self.editor.suggestion_registry.refresh(self.editor)
|
|
585
590
|
if len(variant_ids) == 1:
|
|
586
591
|
print(f"Saved properties for tile {variant_ids[0]} in tileset: {ts.name}")
|
|
587
592
|
else:
|
|
@@ -356,12 +356,14 @@ class NodeEditor:
|
|
|
356
356
|
node = self.editor.node_manager.get_active_node()
|
|
357
357
|
if node:
|
|
358
358
|
node.properties = props
|
|
359
|
+
self.editor.suggestion_registry.refresh(self.editor)
|
|
359
360
|
|
|
360
361
|
def _save_particle_config(self, cfg: dict):
|
|
361
362
|
node = self.editor.node_manager.get_active_node()
|
|
362
363
|
if node:
|
|
363
364
|
node.properties.clear()
|
|
364
365
|
node.properties.update(cfg)
|
|
366
|
+
self.editor.suggestion_registry.refresh(self.editor)
|
|
365
367
|
self.editor.tile_grid_widget.reset_particle_preview(node.node_id, cfg)
|
|
366
368
|
|
|
367
369
|
def draw(self, screen: pygame.Surface):
|