tilemap-editor 2.0.10__tar.gz → 3.0.0__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.
Files changed (177) hide show
  1. tilemap_editor-3.0.0/PKG-INFO +53 -0
  2. tilemap_editor-3.0.0/README.md +37 -0
  3. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/pyproject.toml +14 -1
  4. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/editor.py +10 -17
  5. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/character_collision/standalone.py +4 -1
  6. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/object_tileset_collision/editor.py +62 -16
  7. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/object_tileset_collision/standalone.py +5 -2
  8. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/sprite_animation/editor.py +45 -23
  9. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/sprite_animation/models.py +10 -4
  10. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/sprite_animation/runtime_load.py +10 -11
  11. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/sprite_animation/standalone.py +9 -2
  12. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/tileset_collision/collision_painter.py +3 -2
  13. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/tileset_collision/standalone.py +4 -1
  14. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/standalone_filemanager.py +19 -4
  15. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/tilemap.py +78 -36
  16. tilemap_editor-3.0.0/src/tilemap_editor/assets/__init__.py +7 -0
  17. tilemap_editor-3.0.0/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  18. tilemap_editor-3.0.0/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  19. tilemap_editor-3.0.0/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  20. tilemap_editor-3.0.0/src/tilemap_editor/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  21. tilemap_editor-3.0.0/src/tilemap_editor/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  22. tilemap_editor-3.0.0/src/tilemap_editor/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  23. tilemap_editor-3.0.0/src/tilemap_editor/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  24. tilemap_editor-3.0.0/src/tilemap_editor/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  25. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/arrow-down.svg +1 -0
  26. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/check.svg +1 -0
  27. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/checked.svg +1 -0
  28. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/close.svg +1 -0
  29. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/duplicate.svg +1 -0
  30. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/error.svg +1 -0
  31. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/file.svg +1 -0
  32. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/filedead.svg +1 -0
  33. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/fit.svg +1 -0
  34. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/folder.svg +1 -0
  35. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/grid.svg +1 -0
  36. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/image.svg +1 -0
  37. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/info.svg +1 -0
  38. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/load.svg +1 -0
  39. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/loop.svg +1 -0
  40. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/pan.svg +1 -0
  41. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/pause.svg +1 -0
  42. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/pencil.svg +2 -0
  43. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/play.svg +1 -0
  44. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/plus.svg +1 -0
  45. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/radio.svg +1 -0
  46. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/reset.svg +2 -0
  47. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/save.svg +1 -0
  48. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/stop.svg +1 -0
  49. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/tilemap.svg +1 -0
  50. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/tileset.svg +1 -0
  51. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/unchecked.svg +1 -0
  52. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/warning.svg +1 -0
  53. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/zoomin.svg +6 -0
  54. tilemap_editor-3.0.0/src/tilemap_editor/assets/icons/zoomout.svg +6 -0
  55. tilemap_editor-3.0.0/src/tilemap_editor.egg-info/PKG-INFO +53 -0
  56. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/tilemap_editor.egg-info/SOURCES.txt +40 -0
  57. tilemap_editor-3.0.0/src/utils/__init__.py +11 -0
  58. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/utils/icon_manager.py +49 -1
  59. tilemap_editor-3.0.0/src/utils/project_paths.py +56 -0
  60. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/utils/standalone.py +7 -7
  61. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/autotiler.py +8 -4
  62. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/filemanager.py +274 -5
  63. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/tile_selector.py +139 -1
  64. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/ui/layer_type_dialog.py +2 -1
  65. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/ui/status_bar.py +76 -23
  66. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/ui/tileset_type_dialog.py +48 -14
  67. tilemap_editor-2.0.10/PKG-INFO +0 -24
  68. tilemap_editor-2.0.10/README.md +0 -8
  69. tilemap_editor-2.0.10/src/tilemap_editor.egg-info/PKG-INFO +0 -24
  70. tilemap_editor-2.0.10/src/utils/__init__.py +0 -4
  71. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/LICENSE +0 -0
  72. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/MANIFEST.in +0 -0
  73. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Bold.ttf +0 -0
  74. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-BoldItalic.ttf +0 -0
  75. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Italic.ttf +0 -0
  76. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/fonts/jetbrain-fonts/JetBrainsMono-Regular.ttf +0 -0
  77. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/fonts/noto/NotoSans-Bold.ttf +0 -0
  78. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/fonts/noto/NotoSans-BoldItalic.ttf +0 -0
  79. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/fonts/noto/NotoSans-Italic.ttf +0 -0
  80. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  81. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/arrow-down.svg +0 -0
  82. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/check.svg +0 -0
  83. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/checked.svg +0 -0
  84. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/close.svg +0 -0
  85. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/duplicate.svg +0 -0
  86. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/error.svg +0 -0
  87. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/file.svg +0 -0
  88. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/filedead.svg +0 -0
  89. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/fit.svg +0 -0
  90. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/folder.svg +0 -0
  91. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/grid.svg +0 -0
  92. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/image.svg +0 -0
  93. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/info.svg +0 -0
  94. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/load.svg +0 -0
  95. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/loop.svg +0 -0
  96. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/pan.svg +0 -0
  97. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/pause.svg +0 -0
  98. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/pencil.svg +0 -0
  99. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/play.svg +0 -0
  100. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/plus.svg +0 -0
  101. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/radio.svg +0 -0
  102. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/reset.svg +0 -0
  103. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/save.svg +0 -0
  104. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/stop.svg +0 -0
  105. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/tilemap.svg +0 -0
  106. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/tileset.svg +0 -0
  107. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/unchecked.svg +0 -0
  108. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/warning.svg +0 -0
  109. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/zoomin.svg +0 -0
  110. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/assets/icons/zoomout.svg +0 -0
  111. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/setup.cfg +0 -0
  112. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/configs/themes.py +0 -0
  113. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/constants.py +0 -0
  114. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/event_map.py +0 -0
  115. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/layers.py +0 -0
  116. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/main.py +0 -0
  117. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/__init__.py +0 -0
  118. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/character_collision/__init__.py +0 -0
  119. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/character_collision/editor.py +0 -0
  120. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/character_collision/models.py +0 -0
  121. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/character_collision/protocols.py +0 -0
  122. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/character_collision/shape_editor.py +0 -0
  123. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/object_tileset_collision/__init__.py +0 -0
  124. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/object_tileset_collision/models.py +0 -0
  125. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/object_tileset_collision/protocols.py +0 -0
  126. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/sprite_animation/__init__.py +0 -0
  127. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/sprite_animation/__main__.py +0 -0
  128. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/sprite_animation/clipboard_util.py +0 -0
  129. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/sprite_animation/frame_picker.py +0 -0
  130. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/sprite_animation/preview.py +0 -0
  131. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/sprite_animation/protocols.py +0 -0
  132. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/sprite_animation/timeline.py +0 -0
  133. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/sprite_animation/validation.py +0 -0
  134. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/tileset_collision/__init__.py +0 -0
  135. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/tileset_collision/editor.py +0 -0
  136. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/tileset_collision/models.py +0 -0
  137. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/plugins/tileset_collision/protocols.py +0 -0
  138. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/standalone_automap.py +0 -0
  139. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/standalone_error_console.py +0 -0
  140. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/standalone_image_viewer.py +0 -0
  141. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/tilemap_editor/__init__.py +0 -0
  142. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/tilemap_editor/__main__.py +0 -0
  143. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/tilemap_editor/cli.py +0 -0
  144. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/tilemap_editor/main.py +0 -0
  145. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/tilemap_editor/settings.py +0 -0
  146. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/tilemap_editor.egg-info/dependency_links.txt +0 -0
  147. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/tilemap_editor.egg-info/entry_points.txt +0 -0
  148. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/tilemap_editor.egg-info/requires.txt +0 -0
  149. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/tilemap_editor.egg-info/top_level.txt +0 -0
  150. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/ttypes/__init__.py +0 -0
  151. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/ttypes/tilemap.py +0 -0
  152. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/utils/editor_preference.py +0 -0
  153. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/utils/error_handler.py +0 -0
  154. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/utils/font_manager.py +0 -0
  155. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/utils/history.py +0 -0
  156. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/utils/icons_cache.py +0 -0
  157. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/utils/log_capture.py +0 -0
  158. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/utils/serialization.py +0 -0
  159. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/utils/validation.py +0 -0
  160. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/__init__.py +0 -0
  161. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/automap_models.py +0 -0
  162. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/autotile_template.py +0 -0
  163. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/input.py +0 -0
  164. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/layer_selector.py +0 -0
  165. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/mapsetup.py +0 -0
  166. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/regex_automap_designer.py +0 -0
  167. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/tile_grid.py +0 -0
  168. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/ui/draw_utils.py +0 -0
  169. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/ui/fileinput.py +0 -0
  170. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/ui/menubar.py +0 -0
  171. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/ui/mode_indicator.py +0 -0
  172. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/ui/notification.py +0 -0
  173. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/ui/property_editor.py +0 -0
  174. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/ui/region_selector.py +0 -0
  175. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/ui/theme.py +0 -0
  176. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/ui/toolbar.py +0 -0
  177. {tilemap_editor-2.0.10 → tilemap_editor-3.0.0}/src/widgets/ui/tooltip.py +0 -0
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: tilemap-editor
3
+ Version: 3.0.0
4
+ Summary: Pygame tilemap editor with SVG icons, professional UI, and sprite animation tools
5
+ Author: tilemap editor contributors
6
+ License: Proprietary
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3 :: Only
9
+ Classifier: Topic :: Games/Entertainment
10
+ Classifier: Topic :: Software Development
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: pygame-ce>=2.5
15
+ Dynamic: license-file
16
+
17
+ # tilemap-editor
18
+
19
+ `tilemap-editor` is a pygame-based map editor focused on fast iteration for 2D games.
20
+
21
+ ## Installation
22
+
23
+ [tilemap-editor on PyPI](https://pypi.org/project/tilemap-editor/)
24
+
25
+ ## Parser
26
+
27
+ `tilemap-parser` is a utility to parse and display maps created by this editor. You can use it to load and visualize tilemaps in your game or application.
28
+
29
+ [tilemap-parser on Vercel](https://tilemap-parser.vercel.app/)
30
+
31
+ ### Quick Start
32
+
33
+ If used for the first time do run the following commands. Afterward you can simply run wherever you want.
34
+
35
+ It is required because it initialize project settings on `settings.json`
36
+
37
+ ```bash
38
+ tilemap-editor init
39
+ ```
40
+
41
+ ```bash
42
+ tilemap-editor run
43
+ ```
44
+
45
+
46
+ ```python
47
+ from editor import Editor
48
+
49
+ if __name__ == "__main__":
50
+ editor = Editor()
51
+ editor.run()
52
+ ```
53
+
@@ -0,0 +1,37 @@
1
+ # tilemap-editor
2
+
3
+ `tilemap-editor` is a pygame-based map editor focused on fast iteration for 2D games.
4
+
5
+ ## Installation
6
+
7
+ [tilemap-editor on PyPI](https://pypi.org/project/tilemap-editor/)
8
+
9
+ ## Parser
10
+
11
+ `tilemap-parser` is a utility to parse and display maps created by this editor. You can use it to load and visualize tilemaps in your game or application.
12
+
13
+ [tilemap-parser on Vercel](https://tilemap-parser.vercel.app/)
14
+
15
+ ### Quick Start
16
+
17
+ If used for the first time do run the following commands. Afterward you can simply run wherever you want.
18
+
19
+ It is required because it initialize project settings on `settings.json`
20
+
21
+ ```bash
22
+ tilemap-editor init
23
+ ```
24
+
25
+ ```bash
26
+ tilemap-editor run
27
+ ```
28
+
29
+
30
+ ```python
31
+ from editor import Editor
32
+
33
+ if __name__ == "__main__":
34
+ editor = Editor()
35
+ editor.run()
36
+ ```
37
+
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tilemap-editor"
7
- version = "2.0.10"
7
+ version = "3.0.0"
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"
@@ -52,5 +52,18 @@ include = [
52
52
  ]
53
53
  namespaces = true
54
54
 
55
+ [tool.setuptools.package-data]
56
+ "tilemap_editor.assets" = [
57
+ "icons/*.svg",
58
+ "fonts/noto/*.ttf",
59
+ "fonts/jetbrain-fonts/*.ttf"
60
+ ]
61
+
55
62
  [tool.setuptools.exclude-package-data]
56
63
  "*" = ["*.spec", "*.sh"]
64
+
65
+ # Keep data-files for backward compatibility (one release cycle)
66
+ [tool.setuptools.data-files]
67
+ "share/tilemap_editor/assets/icons" = ["assets/icons/*.svg"]
68
+ "share/tilemap_editor/assets/fonts/noto" = ["assets/fonts/noto/*.ttf"]
69
+ "share/tilemap_editor/assets/fonts/jetbrain-fonts" = ["assets/fonts/jetbrain-fonts/*.ttf"]
@@ -253,13 +253,10 @@ class Editor:
253
253
  ]
254
254
 
255
255
  if initial_dir:
256
- try:
257
- rel_path = initial_dir.relative_to(self.base_path)
258
- args.extend(["--initial-dir", str(rel_path)])
259
- except ValueError:
260
- args.extend(["--initial-dir", str(initial_dir)])
256
+ args.extend(["--initial-dir", str(initial_dir.resolve())])
261
257
  else:
262
- args.extend(["--initial-dir", str(self.base_path / "data")])
258
+ args.extend(["--initial-dir", str(self.data_root.resolve())])
259
+ args.extend(["--data-root", str(self.data_root.resolve())])
263
260
 
264
261
  # Add allowed extensions
265
262
  if allowed_exts:
@@ -392,7 +389,7 @@ class Editor:
392
389
  if self.tilemap.active_project_path:
393
390
  default_name = self.tilemap.active_project_path.name
394
391
  self.open_file_manager(
395
- initial_dir=self.base_path / "data",
392
+ initial_dir=self.data_root,
396
393
  allowed_exts=[".json"],
397
394
  mode="save",
398
395
  default_name=default_name,
@@ -414,7 +411,7 @@ class Editor:
414
411
  def perform_load(self):
415
412
  self.open_file_manager(
416
413
  on_select=self.on_map_file_selected,
417
- initial_dir=self.base_path / "data",
414
+ initial_dir=self.data_root,
418
415
  allowed_exts=[".json"],
419
416
  )
420
417
 
@@ -595,6 +592,7 @@ class Editor:
595
592
  tile_size=(32, 32), # Default tile size
596
593
  consumer=consumer,
597
594
  )
595
+ self.animation_panel._data_root = self.data_root
598
596
  except Exception as e:
599
597
  error_handler.capture(e, context="init_animation_panel")
600
598
  self.notifications.notify(f"Failed to init animation panel: {e}")
@@ -650,17 +648,12 @@ class Editor:
650
648
  def launch_animation_editor(self):
651
649
  """Launch the sprite animation editor in a new window.
652
650
 
653
- Uses the active tileset image when one is loaded so the editor opens
654
- immediately. If no tileset is selected, a file picker asks for a sheet.
651
+ Always asks for a spritesheet instead of assuming the active tileset is
652
+ the animation source.
655
653
  """
656
- sheet = self._active_tileset_image_path()
657
- if sheet is not None:
658
- self._launch_animation_editor_with_image(sheet)
659
- return
660
-
661
654
  self.open_file_manager(
662
655
  on_select=self._launch_animation_editor_with_image,
663
- initial_dir=self.base_path / "data",
656
+ initial_dir=self.data_root,
664
657
  allowed_exts=[".png", ".jpg", ".jpeg"],
665
658
  mode="open",
666
659
  )
@@ -773,7 +766,7 @@ class Editor:
773
766
  """
774
767
  self.open_file_manager(
775
768
  on_select=self._launch_character_collision_editor_with_image,
776
- initial_dir=self.base_path / "data",
769
+ initial_dir=self.data_root,
777
770
  allowed_exts=[".png", ".jpg", ".jpeg"],
778
771
  mode="open",
779
772
  )
@@ -21,7 +21,10 @@ if str(_src_dir) not in sys.path:
21
21
 
22
22
  import pygame
23
23
 
24
- from .editor import CharacterCollisionEditor
24
+ try:
25
+ from .editor import CharacterCollisionEditor
26
+ except ImportError:
27
+ from plugins.character_collision.editor import CharacterCollisionEditor
25
28
  from utils import error_handler, error_context
26
29
 
27
30
 
@@ -6,8 +6,8 @@ Layout:
6
6
  | [Save] [Load] | Define Regions | Paint Collision | [?] |
7
7
  +---------------+--------------------------------+-------------+
8
8
  | Regions List | Collision Painter (Region) | StatusBar |
9
- | - Tree () | | |
10
- | - Rock () | [Region image, zoomed] | 3 shapes |
9
+ | - Tree (ok) | | |
10
+ | - Rock (warn) | [Region image, zoomed] | 3 shapes |
11
11
  | - Player (?)| | Complete |
12
12
  +---------------+--------------------------------+-------------+
13
13
  | Object Tileset (Region Selector) |
@@ -59,6 +59,36 @@ class RegionStatus:
59
59
  is_valid: bool = True
60
60
 
61
61
 
62
+ def _render_fit_text(
63
+ font: pygame.font.Font,
64
+ text: str,
65
+ color: Tuple[int, int, int],
66
+ max_width: int,
67
+ ) -> Surface:
68
+ """Render text constrained to max_width with an ellipsis."""
69
+ if max_width <= 0:
70
+ return font.render("", True, color)
71
+
72
+ surf = font.render(text, True, color)
73
+ if surf.get_width() <= max_width:
74
+ return surf
75
+
76
+ ellipsis = "..."
77
+ if font.size(ellipsis)[0] > max_width:
78
+ return font.render("", True, color)
79
+
80
+ low, high = 0, len(text)
81
+ while low < high:
82
+ mid = (low + high + 1) // 2
83
+ candidate = text[:mid].rstrip() + ellipsis
84
+ if font.size(candidate)[0] <= max_width:
85
+ low = mid
86
+ else:
87
+ high = mid - 1
88
+
89
+ return font.render(text[:low].rstrip() + ellipsis, True, color)
90
+
91
+
62
92
  class ObjectTilesetCollisionEditor:
63
93
  """
64
94
  Main editor for object tileset collision shapes.
@@ -610,8 +640,8 @@ class ObjectTilesetCollisionEditor:
610
640
  # Close button
611
641
  close_btn = Rect(panel_rect.right - 35, panel_y + 10, 25, 25)
612
642
  pygame.draw.rect(screen, COLORS.danger, close_btn, border_radius=SHAPE.radius_sm)
613
- close_text = self._font.render("×", True, COLORS.text)
614
- screen.blit(close_text, (close_btn.centerx - close_text.get_width() // 2, close_btn.centery - close_text.get_height() // 2))
643
+ close_icon = icon_manager.get_icon("close", 15, COLORS.text)
644
+ screen.blit(close_icon, close_icon.get_rect(center=close_btn.center))
615
645
 
616
646
  # Content
617
647
  help_text = [
@@ -642,8 +672,8 @@ class ObjectTilesetCollisionEditor:
642
672
  (" • Middle mouse drag: Pan (always works)", False),
643
673
  ("", False),
644
674
  ("Status Icons:", True),
645
- (" = Has collision shapes", False),
646
- (" = No collision defined yet", False),
675
+ (" Check icon = Has collision shapes", False),
676
+ (" Warning icon = No collision defined yet", False),
647
677
  (" ? = Unnamed region", False),
648
678
  ("", False),
649
679
  ("Shortcuts:", True),
@@ -900,11 +930,6 @@ class ObjectTilesetCollisionEditor:
900
930
  load_label = self._font.render("Load", True, COLORS.text)
901
931
  draw_button(screen, load_btn_rect, load_label, hover=load_hover)
902
932
 
903
- # Title (centered, after mode indicator)
904
- title = self._font.render(f"— {self._tileset_name}", True, COLORS.text_dim)
905
- title_x = self.mode_indicator.rect.right + 20
906
- screen.blit(title, (title_x, self.toolbar_rect.centery - title.get_height() // 2))
907
-
908
933
  # Help button (info icon)
909
934
  help_btn_rect = Rect(self.toolbar_rect.right - 38, self.toolbar_rect.y + 8, 28, 28)
910
935
  help_hover = help_btn_rect.collidepoint(mouse)
@@ -913,6 +938,17 @@ class ObjectTilesetCollisionEditor:
913
938
  pygame.draw.rect(screen, COLORS.border_soft, help_btn_rect, 1, border_radius=SHAPE.radius_sm)
914
939
  screen.blit(help_icon, (help_btn_rect.centerx - 10, help_btn_rect.centery - 10))
915
940
 
941
+ # Title after mode indicator, clipped before the help button.
942
+ title_x = self.mode_indicator.rect.right + 20
943
+ title_max_w = help_btn_rect.x - title_x - 10
944
+ title = _render_fit_text(
945
+ self._font,
946
+ f"- {self._tileset_name}",
947
+ COLORS.text_dim,
948
+ title_max_w,
949
+ )
950
+ screen.blit(title, (title_x, self.toolbar_rect.centery - title.get_height() // 2))
951
+
916
952
  def _draw_regions_list(self, screen: Surface) -> None:
917
953
  """Draw the regions list panel"""
918
954
  draw_panel(screen, self.regions_list_rect, COLORS.panel, COLORS.border)
@@ -950,9 +986,9 @@ class ObjectTilesetCollisionEditor:
950
986
  icon_y = item_rect.centery - 6
951
987
 
952
988
  if status.has_collision:
953
- icon = self._font_sm.render("", True, COLORS.success)
989
+ icon = icon_manager.get_icon("check", 12, COLORS.success)
954
990
  elif region.name and not region.name.startswith("Region "):
955
- icon = self._font_sm.render("", True, COLORS.warning)
991
+ icon = icon_manager.get_icon("warning", 12, COLORS.warning)
956
992
  else:
957
993
  icon = self._font_sm.render("?", True, COLORS.text_dim)
958
994
 
@@ -974,10 +1010,20 @@ class ObjectTilesetCollisionEditor:
974
1010
  pygame.draw.rect(screen, (100, 120, 140),
975
1011
  Rect(name_x, item_rect.y + 5, item_rect.width - 30, 20), border_radius=2)
976
1012
 
977
- name_surf = self._font.render(display_name, True, COLORS.text)
1013
+ name_surf = _render_fit_text(
1014
+ self._font,
1015
+ display_name,
1016
+ COLORS.text,
1017
+ item_rect.right - name_x - 8,
1018
+ )
978
1019
  else:
979
1020
  name = region.name or "Unnamed"
980
- name_surf = self._font.render(name, True, COLORS.text)
1021
+ name_surf = _render_fit_text(
1022
+ self._font,
1023
+ name,
1024
+ COLORS.text,
1025
+ item_rect.right - name_x - 8,
1026
+ )
981
1027
 
982
1028
  screen.blit(name_surf, (name_x, item_rect.y + 8))
983
1029
 
@@ -1080,4 +1126,4 @@ class ObjectTilesetCollisionEditor:
1080
1126
  screen.fill((20, 20, 20))
1081
1127
  self.draw(screen)
1082
1128
  pygame.display.flip()
1083
- clock.tick(60)
1129
+ clock.tick(60)
@@ -22,7 +22,10 @@ if sys.platform == "darwin":
22
22
 
23
23
  import pygame
24
24
 
25
- from .editor import ObjectTilesetCollisionEditor
25
+ try:
26
+ from .editor import ObjectTilesetCollisionEditor
27
+ except ImportError:
28
+ from plugins.object_tileset_collision.editor import ObjectTilesetCollisionEditor
26
29
  from utils import error_handler, error_context
27
30
 
28
31
 
@@ -94,4 +97,4 @@ def main(argv: list[str] | None = None) -> None:
94
97
 
95
98
 
96
99
  if __name__ == "__main__":
97
- main()
100
+ main()
@@ -22,6 +22,7 @@ import pygame
22
22
  from pygame import Rect, Surface, Color
23
23
  from utils.font_manager import font_manager, FontWeight, FontStyle
24
24
  from utils.icon_manager import icon_manager
25
+ from utils.project_paths import resolve_project_path
25
26
  from utils import error_handler
26
27
  from .clipboard_util import copy_plain_text
27
28
  from .frame_picker import FramePicker
@@ -103,7 +104,7 @@ class SpriteAnimationEditor:
103
104
  Layout::
104
105
 
105
106
  +----------------------------------------------------+
106
- | Toolbar: [idle] [+] [] | [Save] [Load] |
107
+ | Toolbar: [idle] [+] [X] | [Save] [Load] |
107
108
  +---------------+------------------------------------+
108
109
  | Preview | Spritesheet Frame Picker |
109
110
  | (220px w) | (pan/zoom, click to add) |
@@ -242,6 +243,7 @@ class SpriteAnimationEditor:
242
243
 
243
244
  # Track the last saved path for quick save (Ctrl+S)
244
245
  self._last_saved_path: Optional[Path] = None
246
+ self._data_root: Optional[Path] = Path.cwd() / "data"
245
247
 
246
248
  # Sync sub-widgets
247
249
  self._sync_active_animation()
@@ -867,7 +869,7 @@ class SpriteAnimationEditor:
867
869
  self._draw_toolbar_btn(screen, self._btn_new, "+", mouse)
868
870
  x += 32
869
871
 
870
- # [✕ Delete]
872
+ # Delete
871
873
  self._btn_del = Rect(x, cy, 28, bh)
872
874
  # Close/X icon for delete
873
875
  close_icon = icon_manager.get_icon("close", 14, _COLORS["btn_danger_hover"])
@@ -1174,7 +1176,7 @@ class SpriteAnimationEditor:
1174
1176
  if self._info_tooltip_pinned:
1175
1177
  lines.append("Esc or click outside card to close")
1176
1178
  else:
1177
- lines.append("Click to pin this panel")
1179
+ lines.append("Click info to pin this panel")
1178
1180
 
1179
1181
  padding = 8
1180
1182
  line_height = 16
@@ -1674,8 +1676,8 @@ class SpriteAnimationEditor:
1674
1676
 
1675
1677
  pygame.draw.rect(screen, _COLORS["btn_danger"], del_rect, border_radius=3)
1676
1678
  pygame.draw.rect(screen, _COLORS["border"], del_rect, 1, border_radius=3)
1677
- x_lbl = self._font_sm.render("×", True, _COLORS["text"])
1678
- screen.blit(x_lbl, x_lbl.get_rect(center=del_rect.center))
1679
+ close_icon = icon_manager.get_icon("close", 12, _COLORS["text"])
1680
+ screen.blit(close_icon, close_icon.get_rect(center=del_rect.center))
1679
1681
 
1680
1682
  row_y += META_ROW_H
1681
1683
 
@@ -1802,13 +1804,12 @@ class SpriteAnimationEditor:
1802
1804
  # Import FileManager
1803
1805
  try:
1804
1806
  from widgets.filemanager import FileManager
1805
- from constants import BASE_PATH
1806
1807
  except ImportError as e:
1807
1808
  # Fallback to old behavior if FileManager not available
1808
1809
  print(f"Warning: Could not import FileManager: {e}")
1809
1810
  path = self._default_save_path()
1810
1811
  try:
1811
- self.library.save(path)
1812
+ self.library.save(path, base_path=self._project_base_path())
1812
1813
  self._last_saved_path = path
1813
1814
  print(f"Animations saved to {path}")
1814
1815
  except Exception as e:
@@ -1821,12 +1822,12 @@ class SpriteAnimationEditor:
1821
1822
  initial_dir = self._last_saved_path.parent
1822
1823
  default_name = self._last_saved_path.name
1823
1824
  elif self.library.spritesheet_path:
1824
- # Use spritesheet location
1825
- initial_dir = Path(self.library.spritesheet_path).parent
1825
+ initial_dir = self._data_root / "animations"
1826
1826
  default_name = Path(self.library.spritesheet_path).stem + ".anim.json"
1827
1827
  else:
1828
1828
  initial_dir = self._data_root / "animations"
1829
1829
  default_name = "animations.anim.json"
1830
+ initial_dir.mkdir(parents=True, exist_ok=True)
1830
1831
 
1831
1832
  # Create file manager for save
1832
1833
  screen = pygame.display.get_surface()
@@ -1842,6 +1843,7 @@ class SpriteAnimationEditor:
1842
1843
  mode="save",
1843
1844
  default_name=default_name,
1844
1845
  on_cancel=self._close_file_manager,
1846
+ data_root=self._data_root,
1845
1847
  )
1846
1848
 
1847
1849
  def _quick_save(self) -> None:
@@ -1852,7 +1854,10 @@ class SpriteAnimationEditor:
1852
1854
  if self._last_saved_path:
1853
1855
  # Save to existing path
1854
1856
  try:
1855
- self.library.save(self._last_saved_path)
1857
+ self.library.save(
1858
+ self._last_saved_path,
1859
+ base_path=self._project_base_path(),
1860
+ )
1856
1861
  print(f"Animations saved to {self._last_saved_path}")
1857
1862
  except Exception as e:
1858
1863
  error_handler.capture(e, context="save_animations_quick")
@@ -1867,13 +1872,13 @@ class SpriteAnimationEditor:
1867
1872
  # Import FileManager
1868
1873
  try:
1869
1874
  from widgets.filemanager import FileManager
1870
- from constants import BASE_PATH
1871
1875
  except ImportError:
1872
1876
  # Fallback to old behavior if FileManager not available
1873
1877
  path = self._default_save_path()
1874
1878
  if path.exists():
1875
1879
  try:
1876
1880
  self.library = AnimationLibrary.load(path)
1881
+ self._resolve_library_paths(path)
1877
1882
  names = self.library.animation_names()
1878
1883
  self._active_anim_name = names[0] if names else None
1879
1884
  if not names:
@@ -1886,11 +1891,9 @@ class SpriteAnimationEditor:
1886
1891
  print(f"No animation file found at {path}")
1887
1892
  return
1888
1893
 
1889
- # Get initial directory from spritesheet path or use data folder
1890
- if self.library.spritesheet_path:
1891
- initial_dir = Path(self.library.spritesheet_path).parent
1892
- else:
1893
- initial_dir = self._data_root / "animations"
1894
+ # Load project animation files from the configured data folder.
1895
+ initial_dir = self._data_root / "animations"
1896
+ initial_dir.mkdir(parents=True, exist_ok=True)
1894
1897
 
1895
1898
  # Create file manager for load
1896
1899
  screen = pygame.display.get_surface()
@@ -1905,12 +1908,13 @@ class SpriteAnimationEditor:
1905
1908
  on_select=self._on_load_file_selected,
1906
1909
  mode="open",
1907
1910
  on_cancel=self._close_file_manager,
1911
+ data_root=self._data_root,
1908
1912
  )
1909
1913
 
1910
1914
  def _on_save_file_selected(self, path: Path) -> None:
1911
1915
  """Callback when user selects a file to save to."""
1912
1916
  try:
1913
- self.library.save(path)
1917
+ self.library.save(path, base_path=self._project_base_path())
1914
1918
  self._last_saved_path = path # Track for quick save
1915
1919
  print(f"Animations saved to {path}")
1916
1920
  except Exception as e:
@@ -1928,6 +1932,7 @@ class SpriteAnimationEditor:
1928
1932
  if path.exists():
1929
1933
  try:
1930
1934
  self.library = AnimationLibrary.load(path)
1935
+ self._resolve_library_paths(path)
1931
1936
  self._last_saved_path = path # Track loaded file as save location
1932
1937
  names = self.library.animation_names()
1933
1938
  self._active_anim_name = names[0] if names else None
@@ -1952,16 +1957,12 @@ class SpriteAnimationEditor:
1952
1957
  # Import FileManager
1953
1958
  try:
1954
1959
  from widgets.filemanager import FileManager
1955
- from constants import BASE_PATH
1956
1960
  except ImportError as e:
1957
1961
  print(f"Warning: Could not import FileManager: {e}")
1958
1962
  return
1959
1963
 
1960
- # Get initial directory
1961
- if self.library.spritesheet_path:
1962
- initial_dir = Path(self.library.spritesheet_path).parent
1963
- else:
1964
- initial_dir = self._data_root / "animations"
1964
+ initial_dir = self._data_root
1965
+ initial_dir.mkdir(parents=True, exist_ok=True)
1965
1966
 
1966
1967
  # Create file manager for loading spritesheet
1967
1968
  screen = cast(Surface, pygame.display.get_surface())
@@ -1976,6 +1977,7 @@ class SpriteAnimationEditor:
1976
1977
  on_select=self._on_spritesheet_selected,
1977
1978
  mode="open",
1978
1979
  on_cancel=self._close_file_manager,
1980
+ data_root=self._data_root,
1979
1981
  )
1980
1982
 
1981
1983
  def _on_spritesheet_selected(self, path: Union[Path, List[Path]]) -> None:
@@ -2022,6 +2024,26 @@ class SpriteAnimationEditor:
2022
2024
  return Path(self.library.spritesheet_path).parent / f"{base}.anim.json"
2023
2025
  return Path.cwd() / "animations.anim.json"
2024
2026
 
2027
+ def _project_base_path(self) -> Optional[Path]:
2028
+ if self._data_root is None:
2029
+ return None
2030
+ return Path(self._data_root).parent
2031
+
2032
+ def _resolve_library_paths(self, json_path: Path) -> None:
2033
+ if not self.library.spritesheet_path:
2034
+ return
2035
+ base_path = self._project_base_path()
2036
+ if base_path is None:
2037
+ return
2038
+ self.library.spritesheet_path = str(
2039
+ resolve_project_path(
2040
+ self.library.spritesheet_path,
2041
+ base_path,
2042
+ fallback_roots=[json_path.parent],
2043
+ must_exist=True,
2044
+ )
2045
+ )
2046
+
2025
2047
  # ------------------------------------------------------------------
2026
2048
  # Integration helpers
2027
2049
  # ------------------------------------------------------------------
@@ -12,6 +12,8 @@ from dataclasses import dataclass, field
12
12
  from pathlib import Path
13
13
  from typing import Any, Dict, List, Optional, Tuple
14
14
 
15
+ from utils.project_paths import to_project_path
16
+
15
17
 
16
18
  @dataclass
17
19
  class AnimationMarker:
@@ -189,9 +191,13 @@ class AnimationLibrary:
189
191
  def animation_names(self) -> List[str]:
190
192
  return list(self.animations.keys())
191
193
 
192
- def to_dict(self) -> dict:
194
+ def to_dict(self, *, base_path: Optional[Path] = None) -> dict:
195
+ spritesheet_path = self.spritesheet_path
196
+ if spritesheet_path and base_path is not None:
197
+ spritesheet_path = to_project_path(spritesheet_path, base_path)
198
+
193
199
  return {
194
- "spritesheet_path": self.spritesheet_path,
200
+ "spritesheet_path": spritesheet_path,
195
201
  "tile_size": list(self.tile_size),
196
202
  "animations": {
197
203
  name: anim.to_dict() for name, anim in self.animations.items()
@@ -208,10 +214,10 @@ class AnimationLibrary:
208
214
  lib.animations[name] = Animation.from_dict(anim_data)
209
215
  return lib
210
216
 
211
- def save(self, path: Path) -> None:
217
+ def save(self, path: Path, *, base_path: Optional[Path] = None) -> None:
212
218
  path.parent.mkdir(parents=True, exist_ok=True)
213
219
  with open(path, "w", encoding="utf-8") as f:
214
- json.dump(self.to_dict(), f, indent=2)
220
+ json.dump(self.to_dict(base_path=base_path), f, indent=2)
215
221
 
216
222
  @staticmethod
217
223
  def load(path: Path) -> AnimationLibrary:
@@ -198,17 +198,16 @@ class SpriteAnimRuntime:
198
198
 
199
199
  img_path = Path(sheet_ref)
200
200
  if not img_path.is_absolute():
201
- cand = (p.parent / img_path).resolve()
202
- if cand.is_file():
203
- img_path = cand
204
- elif extra_search_base is not None:
205
- cand2 = (Path(extra_search_base) / sheet_ref).resolve()
206
- if cand2.is_file():
207
- img_path = cand2
208
- else:
209
- img_path = cand
210
- else:
211
- img_path = cand
201
+ candidates = [(p.parent / img_path).resolve()]
202
+ if extra_search_base is not None:
203
+ candidates.append((Path(extra_search_base) / sheet_ref).resolve())
204
+ candidates.extend((parent / img_path).resolve() for parent in p.parents)
205
+
206
+ img_path = candidates[0]
207
+ for candidate in candidates:
208
+ if candidate.is_file():
209
+ img_path = candidate
210
+ break
212
211
 
213
212
  if not img_path.is_file():
214
213
  raise AnimationParseError(f"Spritesheet not found: {sheet_ref!r} (tried {img_path})")
@@ -22,7 +22,10 @@ if str(_src_dir) not in sys.path:
22
22
 
23
23
  import pygame
24
24
 
25
- from .editor import SpriteAnimationEditor
25
+ try:
26
+ from .editor import SpriteAnimationEditor
27
+ except ImportError:
28
+ from plugins.sprite_animation.editor import SpriteAnimationEditor
26
29
  from utils import error_handler, error_context
27
30
 
28
31
 
@@ -106,10 +109,14 @@ def main(argv: list[str] | None = None) -> None:
106
109
  )
107
110
 
108
111
  if args.load and args.load.exists():
109
- from .models import AnimationLibrary
112
+ try:
113
+ from .models import AnimationLibrary
114
+ except ImportError:
115
+ from plugins.sprite_animation.models import AnimationLibrary
110
116
 
111
117
  lib = AnimationLibrary.load(args.load)
112
118
  editor.load_animation_data(lib.to_dict())
119
+ editor._resolve_library_paths(args.load)
113
120
  print(f"Loaded animations from {args.load}")
114
121
 
115
122
  editor.run()