cyberia 3.2.5 → 3.2.9

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 (301) hide show
  1. package/.github/workflows/engine-cyberia.cd.yml +2 -2
  2. package/.github/workflows/release.cd.yml +1 -2
  3. package/CHANGELOG.md +351 -1
  4. package/CLI-HELP.md +40 -13
  5. package/Dockerfile +0 -4
  6. package/README.md +242 -497
  7. package/bin/build.js +19 -5
  8. package/bin/cyberia.js +1149 -240
  9. package/bin/deploy.js +570 -1
  10. package/bin/file.js +6 -0
  11. package/bin/index.js +1149 -240
  12. package/bin/vs.js +1 -1
  13. package/conf.js +67 -89
  14. package/deployment.yaml +4 -222
  15. package/hardhat/package-lock.json +32 -32
  16. package/hardhat/package.json +3 -3
  17. package/jsconfig.json +1 -1
  18. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -2
  19. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +2 -2
  20. package/manifests/deployment/dd-cyberia-development/deployment.yaml +4 -222
  21. package/manifests/deployment/dd-cyberia-development/proxy.yaml +10 -118
  22. package/manifests/deployment/dd-default-development/deployment.yaml +2 -6
  23. package/manifests/deployment/dd-test-development/deployment.yaml +136 -66
  24. package/manifests/deployment/dd-test-development/proxy.yaml +41 -5
  25. package/package.json +23 -14
  26. package/proxy.yaml +10 -118
  27. package/scripts/k3s-node-setup.sh +2 -2
  28. package/scripts/nat-iptables.sh +103 -18
  29. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +18 -18
  30. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +7 -14
  31. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +76 -21
  32. package/src/api/core/core.controller.js +10 -10
  33. package/src/api/core/core.service.js +10 -10
  34. package/src/api/crypto/crypto.controller.js +8 -8
  35. package/src/api/crypto/crypto.service.js +8 -8
  36. package/src/api/cyberia-action/cyberia-action.controller.js +74 -0
  37. package/src/api/cyberia-action/cyberia-action.model.js +87 -0
  38. package/src/api/cyberia-action/cyberia-action.router.js +27 -0
  39. package/src/api/cyberia-action/cyberia-action.service.js +42 -0
  40. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +13 -13
  41. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +11 -11
  42. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +2 -2
  43. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +16 -16
  44. package/src/api/cyberia-entity/cyberia-entity.controller.js +10 -10
  45. package/src/api/cyberia-entity/cyberia-entity.service.js +10 -10
  46. package/src/api/cyberia-instance/cyberia-fallback-world.js +19 -209
  47. package/src/api/cyberia-instance/cyberia-instance.controller.js +14 -14
  48. package/src/api/cyberia-instance/cyberia-instance.model.js +3 -0
  49. package/src/api/cyberia-instance/cyberia-instance.service.js +22 -57
  50. package/src/api/cyberia-instance/cyberia-portal-connector.js +20 -246
  51. package/src/api/cyberia-instance/cyberia-world-generator.js +505 -0
  52. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +10 -10
  53. package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +216 -55
  54. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +4 -1
  55. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +18 -14
  56. package/src/api/cyberia-map/cyberia-map.controller.js +10 -10
  57. package/src/api/cyberia-map/cyberia-map.service.js +10 -10
  58. package/src/api/cyberia-quest/cyberia-quest.controller.js +74 -0
  59. package/src/api/cyberia-quest/cyberia-quest.model.js +67 -0
  60. package/src/api/cyberia-quest/cyberia-quest.router.js +27 -0
  61. package/src/api/cyberia-quest/cyberia-quest.service.js +42 -0
  62. package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +74 -0
  63. package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +49 -0
  64. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +27 -0
  65. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +42 -0
  66. package/src/api/default/default.controller.js +10 -10
  67. package/src/api/default/default.service.js +10 -10
  68. package/src/api/document/document.controller.js +12 -12
  69. package/src/api/document/document.model.js +10 -16
  70. package/src/api/file/file.controller.js +8 -8
  71. package/src/api/file/file.model.js +10 -10
  72. package/src/api/file/file.service.js +36 -36
  73. package/src/api/instance/instance.controller.js +10 -10
  74. package/src/api/instance/instance.model.js +4 -10
  75. package/src/api/instance/instance.service.js +10 -10
  76. package/src/api/ipfs/ipfs.controller.js +12 -12
  77. package/src/api/ipfs/ipfs.model.js +4 -13
  78. package/src/api/ipfs/ipfs.service.js +14 -28
  79. package/src/api/object-layer/object-layer.controller.js +12 -12
  80. package/src/api/object-layer/object-layer.model.js +4 -17
  81. package/src/api/object-layer/object-layer.service.js +12 -12
  82. package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +10 -10
  83. package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +6 -16
  84. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +18 -14
  85. package/src/api/test/test.controller.js +8 -8
  86. package/src/api/test/test.service.js +8 -8
  87. package/src/api/user/guest.service.js +99 -0
  88. package/src/api/user/user.controller.js +6 -6
  89. package/src/api/user/user.model.js +8 -13
  90. package/src/api/user/user.service.js +3 -20
  91. package/src/cli/cluster.js +61 -14
  92. package/src/cli/db.js +47 -2
  93. package/src/cli/deploy.js +67 -35
  94. package/src/cli/fs.js +79 -8
  95. package/src/cli/image.js +43 -1
  96. package/src/cli/index.js +26 -1
  97. package/src/cli/release.js +57 -1
  98. package/src/cli/repository.js +69 -31
  99. package/src/cli/run.js +415 -36
  100. package/src/cli/ssh.js +1 -1
  101. package/src/cli/static.js +43 -115
  102. package/src/client/Cryptokoyn.index.js +18 -21
  103. package/src/client/CyberiaPortal.index.js +19 -23
  104. package/src/client/Default.index.js +21 -33
  105. package/src/client/Itemledger.index.js +20 -26
  106. package/src/client/Underpost.index.js +19 -23
  107. package/src/client/components/core/404.js +4 -4
  108. package/src/client/components/core/500.js +4 -4
  109. package/src/client/components/core/Account.js +73 -60
  110. package/src/client/components/core/AgGrid.js +23 -33
  111. package/src/client/components/core/Alert.js +12 -13
  112. package/src/client/components/core/AppStore.js +1 -1
  113. package/src/client/components/core/Auth.js +35 -37
  114. package/src/client/components/core/Badge.js +7 -13
  115. package/src/client/components/core/BtnIcon.js +15 -17
  116. package/src/client/components/core/CalendarCore.js +42 -63
  117. package/src/client/components/core/Chat.js +13 -15
  118. package/src/client/components/core/ClientEvents.js +87 -0
  119. package/src/client/components/core/ColorPaletteElement.js +309 -0
  120. package/src/client/components/core/Content.js +17 -14
  121. package/src/client/components/core/Css.js +15 -71
  122. package/src/client/components/core/CssCore.js +12 -16
  123. package/src/client/components/core/D3Chart.js +4 -4
  124. package/src/client/components/core/Docs.js +64 -91
  125. package/src/client/components/core/DropDown.js +69 -91
  126. package/src/client/components/core/EventBus.js +92 -0
  127. package/src/client/components/core/EventsUI.js +14 -17
  128. package/src/client/components/core/FileExplorer.js +96 -228
  129. package/src/client/components/core/FullScreen.js +47 -75
  130. package/src/client/components/core/Input.js +24 -69
  131. package/src/client/components/core/Keyboard.js +25 -18
  132. package/src/client/components/core/KeyboardAvoidance.js +145 -0
  133. package/src/client/components/core/LoadingAnimation.js +25 -31
  134. package/src/client/components/core/LogIn.js +41 -41
  135. package/src/client/components/core/LogOut.js +23 -14
  136. package/src/client/components/core/Modal.js +462 -178
  137. package/src/client/components/core/NotificationManager.js +14 -18
  138. package/src/client/components/core/Panel.js +54 -50
  139. package/src/client/components/core/PanelForm.js +25 -125
  140. package/src/client/components/core/Polyhedron.js +110 -214
  141. package/src/client/components/core/PublicProfile.js +39 -32
  142. package/src/client/components/core/Recover.js +48 -44
  143. package/src/client/components/core/Responsive.js +88 -32
  144. package/src/client/components/core/RichText.js +9 -18
  145. package/src/client/components/core/Router.js +24 -3
  146. package/src/client/components/core/SearchBox.js +37 -37
  147. package/src/client/components/core/SignUp.js +39 -30
  148. package/src/client/components/core/SocketIo.js +31 -2
  149. package/src/client/components/core/SocketIoHandler.js +6 -6
  150. package/src/client/components/core/ToggleSwitch.js +8 -20
  151. package/src/client/components/core/ToolTip.js +5 -17
  152. package/src/client/components/core/Translate.js +56 -59
  153. package/src/client/components/core/Validator.js +26 -16
  154. package/src/client/components/core/Wallet.js +15 -26
  155. package/src/client/components/core/Worker.js +163 -27
  156. package/src/client/components/core/windowGetDimensions.js +7 -7
  157. package/src/client/components/cryptokoyn/{MenuCryptokoyn.js → AppShellCryptokoyn.js} +57 -57
  158. package/src/client/components/cryptokoyn/CssCryptokoyn.js +15 -15
  159. package/src/client/components/cryptokoyn/LogInCryptokoyn.js +6 -4
  160. package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +6 -4
  161. package/src/client/components/cryptokoyn/RouterCryptokoyn.js +37 -0
  162. package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +4 -4
  163. package/src/client/components/cryptokoyn/SignUpCryptokoyn.js +6 -4
  164. package/src/client/components/cyberia/InstanceEngineCyberia.js +141 -60
  165. package/src/client/components/cyberia/MapEngineCyberia.js +691 -214
  166. package/src/client/components/cyberia/ObjectLayerEngine.js +19 -0
  167. package/src/client/components/cyberia/ObjectLayerEngineModal.js +1204 -94
  168. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +196 -298
  169. package/src/client/components/cyberia-portal/{MenuCyberiaPortal.js → AppShellCyberiaPortal.js} +102 -102
  170. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +305 -61
  171. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +15 -15
  172. package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +6 -4
  173. package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +6 -4
  174. package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +4 -4
  175. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +60 -0
  176. package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +4 -4
  177. package/src/client/components/cyberia-portal/SignUpCyberiaPortal.js +6 -4
  178. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -4
  179. package/src/client/components/default/{MenuDefault.js → AppShellDefault.js} +87 -87
  180. package/src/client/components/default/CssDefault.js +12 -12
  181. package/src/client/components/default/LogInDefault.js +6 -4
  182. package/src/client/components/default/LogOutDefault.js +6 -4
  183. package/src/client/components/default/RouterDefault.js +47 -0
  184. package/src/client/components/default/SettingsDefault.js +4 -4
  185. package/src/client/components/default/SignUpDefault.js +6 -4
  186. package/src/client/components/default/TranslateDefault.js +3 -3
  187. package/src/client/components/itemledger/{MenuItemledger.js → AppShellItemledger.js} +57 -57
  188. package/src/client/components/itemledger/CssItemledger.js +15 -15
  189. package/src/client/components/itemledger/LogInItemledger.js +6 -4
  190. package/src/client/components/itemledger/LogOutItemledger.js +6 -4
  191. package/src/client/components/itemledger/RouterItemledger.js +38 -0
  192. package/src/client/components/itemledger/SettingsItemledger.js +4 -4
  193. package/src/client/components/itemledger/SignUpItemledger.js +6 -4
  194. package/src/client/components/itemledger/TranslateItemledger.js +3 -3
  195. package/src/client/components/underpost/{MenuUnderpost.js → AppShellUnderpost.js} +88 -88
  196. package/src/client/components/underpost/CssUnderpost.js +14 -14
  197. package/src/client/components/underpost/CyberpunkBloggerUnderpost.js +4 -4
  198. package/src/client/components/underpost/DocumentSearchProvider.js +1 -1
  199. package/src/client/components/underpost/LabGalleryUnderpost.js +12 -15
  200. package/src/client/components/underpost/LogInUnderpost.js +6 -4
  201. package/src/client/components/underpost/LogOutUnderpost.js +6 -4
  202. package/src/client/components/underpost/RouterUnderpost.js +45 -0
  203. package/src/client/components/underpost/SettingsUnderpost.js +4 -4
  204. package/src/client/components/underpost/SignUpUnderpost.js +6 -4
  205. package/src/client/components/underpost/TranslateUnderpost.js +4 -4
  206. package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +235 -0
  207. package/src/client/public/cyberia-docs/ARCHITECTURE.md +443 -0
  208. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +417 -0
  209. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +313 -0
  210. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +260 -0
  211. package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +241 -0
  212. package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +300 -0
  213. package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +279 -0
  214. package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +206 -0
  215. package/src/client/public/cyberia-docs/ROADMAP.md +240 -0
  216. package/src/client/public/cyberia-docs/WHITE-PAPER.md +732 -0
  217. package/src/client/services/atlas-sprite-sheet/atlas-sprite-sheet.service.js +14 -20
  218. package/src/client/services/core/core.service.js +17 -49
  219. package/src/client/services/crypto/crypto.service.js +8 -13
  220. package/src/client/services/cyberia-action/cyberia-action.service.js +99 -0
  221. package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +10 -16
  222. package/src/client/services/cyberia-entity/cyberia-entity.management.js +5 -5
  223. package/src/client/services/cyberia-entity/cyberia-entity.service.js +10 -16
  224. package/src/client/services/cyberia-instance/cyberia-instance.management.js +6 -6
  225. package/src/client/services/cyberia-instance/cyberia-instance.service.js +12 -18
  226. package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +10 -16
  227. package/src/client/services/cyberia-map/cyberia-map.management.js +6 -6
  228. package/src/client/services/cyberia-map/cyberia-map.service.js +12 -18
  229. package/src/client/services/cyberia-quest/cyberia-quest.service.js +99 -0
  230. package/src/client/services/cyberia-quest-progress/cyberia-quest-progress.service.js +99 -0
  231. package/src/client/services/default/default.management.js +159 -267
  232. package/src/client/services/default/default.service.js +10 -16
  233. package/src/client/services/document/document.service.js +14 -19
  234. package/src/client/services/file/file.service.js +8 -13
  235. package/src/client/services/instance/instance.management.js +5 -5
  236. package/src/client/services/instance/instance.service.js +10 -15
  237. package/src/client/services/ipfs/ipfs.service.js +12 -18
  238. package/src/client/services/object-layer/object-layer.management.js +12 -12
  239. package/src/client/services/object-layer/object-layer.service.js +20 -26
  240. package/src/client/services/object-layer-render-frames/object-layer-render-frames.service.js +10 -16
  241. package/src/client/services/test/test.service.js +8 -13
  242. package/src/client/services/user/guest.service.js +86 -0
  243. package/src/client/services/user/user.management.js +5 -5
  244. package/src/client/services/user/user.service.js +14 -20
  245. package/src/client/ssr/body/404.js +3 -3
  246. package/src/client/ssr/body/500.js +3 -3
  247. package/src/client/ssr/body/CacheControl.js +5 -2
  248. package/src/client/ssr/body/DefaultSplashScreen.js +19 -12
  249. package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +13 -6
  250. package/src/client/ssr/head/PwaItemledger.js +197 -60
  251. package/src/client/ssr/mailer/DefaultRecoverEmail.js +19 -20
  252. package/src/client/ssr/mailer/DefaultVerifyEmail.js +15 -16
  253. package/src/client/ssr/offline/Maintenance.js +12 -11
  254. package/src/client/ssr/offline/NoNetworkConnection.js +3 -3
  255. package/src/client/ssr/pages/Test.js +2 -2
  256. package/src/client/sw/core.sw.js +212 -0
  257. package/src/grpc/cyberia/grpc-server.js +179 -67
  258. package/src/index.js +1 -1
  259. package/src/runtime/cyberia-client/Dockerfile +80 -0
  260. package/src/runtime/cyberia-server/Dockerfile +37 -0
  261. package/src/runtime/express/Dockerfile +4 -4
  262. package/src/runtime/lampp/Dockerfile +8 -7
  263. package/src/runtime/wp/Dockerfile +11 -17
  264. package/src/server/atlas-sprite-sheet-generator.js +4 -2
  265. package/src/server/client-build-docs.js +45 -46
  266. package/src/server/client-build.js +334 -60
  267. package/src/server/client-formatted.js +47 -16
  268. package/src/server/conf.js +5 -4
  269. package/src/server/data-query.js +32 -20
  270. package/src/server/dns.js +22 -0
  271. package/src/server/ipfs-client.js +232 -91
  272. package/src/server/object-layer.js +1 -6
  273. package/src/server/process.js +13 -27
  274. package/src/server/semantic-layer-generator-floor.js +11 -51
  275. package/src/server/semantic-layer-generator-resource.js +259 -0
  276. package/src/server/semantic-layer-generator-skin.js +41 -171
  277. package/src/server/semantic-layer-generator.js +122 -14
  278. package/src/server/shape-generator.js +108 -0
  279. package/src/server/start.js +17 -3
  280. package/src/server/valkey.js +141 -235
  281. package/tsconfig.docs.json +15 -0
  282. package/typedoc.dd-cyberia.json +29 -0
  283. package/typedoc.json +29 -0
  284. package/WHITE-PAPER.md +0 -1540
  285. package/hardhat/README.md +0 -531
  286. package/hardhat/WHITE-PAPER.md +0 -1540
  287. package/jsdoc.dd-cyberia.json +0 -68
  288. package/jsdoc.json +0 -68
  289. package/src/api/object-layer/README.md +0 -672
  290. package/src/client/components/core/ColorPalette.js +0 -5267
  291. package/src/client/components/core/JoyStick.js +0 -80
  292. package/src/client/components/cryptokoyn/RoutesCryptokoyn.js +0 -39
  293. package/src/client/components/cyberia-portal/RoutesCyberiaPortal.js +0 -62
  294. package/src/client/components/cyberia-portal/ServerCyberiaPortal.js +0 -136
  295. package/src/client/components/default/RoutesDefault.js +0 -49
  296. package/src/client/components/itemledger/RoutesItemledger.js +0 -40
  297. package/src/client/components/underpost/RoutesUnderpost.js +0 -47
  298. package/src/client/sw/default.sw.js +0 -127
  299. package/src/client/sw/template.sw.js +0 -84
  300. package/src/grpc/cyberia/OFF_CHAIN_ECONOMY.md +0 -305
  301. package/src/grpc/cyberia/README.md +0 -326
@@ -13,6 +13,17 @@ import { DefaultManagement } from '../../services/default/default.management.js'
13
13
  import { getApiBaseUrl } from '../../services/core/core.service.js';
14
14
  import { ObjectLayerService } from '../../services/object-layer/object-layer.service.js';
15
15
  import { getProxyPath } from '../core/Router.js';
16
+ import { ENTITY_TYPES, getDefaultCyberiaItemById } from '../cyberia-portal/CommonCyberiaPortal.js';
17
+ import '../core/ColorPaletteElement.js';
18
+
19
+ const DEFAULT_ENTITY_TYPE = ENTITY_TYPES.floor;
20
+ const dropdownValueKey = (value = '') => String(value).trim().replaceAll(' ', '-');
21
+ const createDropdownOption = (value, onClick = () => {}, display = value, data = value) => ({
22
+ value,
23
+ display,
24
+ data,
25
+ onClick,
26
+ });
16
27
 
17
28
  class MapEngineCyberia {
18
29
  static entities = [];
@@ -23,13 +34,352 @@ class MapEngineCyberia {
23
34
  static showGridBorders = true;
24
35
  static addOnClick = true;
25
36
  static showObjectLayers = false;
26
- static randomDim = false;
37
+ static enableRandomFactors = false;
27
38
  static captureObjLayerThumbnail = true;
28
39
  static imageCache = {};
40
+ static entityUndoStack = [];
41
+ static entityRedoStack = [];
42
+ static maxEntityHistory = 200;
43
+ static entityHistorySync = null;
44
+ static entityHistoryHotkeyHandler = null;
45
+ static entityTypeDropdownId = 'map-engine-entity-type';
46
+ static objectLayerDropdownId = 'map-engine-obj-layer-dropdown';
47
+ static objectLayerDropdownHostId = 'map-engine-obj-layer-dropdown-host';
48
+ static renameSourceObjectLayerInputId = 'map-engine-rename-source-object-layer-item-id';
49
+ static renameTargetObjectLayerInputId = 'map-engine-rename-target-object-layer-item-id';
50
+
51
+ static getSelectedDropdownValue(dropdownId, fallback = '') {
52
+ return DropDown.Tokens[dropdownId]?.value || s(`.${dropdownId}`)?.value || fallback;
53
+ }
54
+
55
+ static getSelectedEntityType() {
56
+ return MapEngineCyberia.getSelectedDropdownValue(MapEngineCyberia.entityTypeDropdownId, DEFAULT_ENTITY_TYPE);
57
+ }
58
+
59
+ static getSelectedObjectLayerItemIds() {
60
+ return DropDown.Tokens[MapEngineCyberia.objectLayerDropdownId]?.value
61
+ ? [...DropDown.Tokens[MapEngineCyberia.objectLayerDropdownId].value]
62
+ : [];
63
+ }
64
+
65
+ static getRenameObjectLayerItemIds() {
66
+ return {
67
+ source: s(`.${MapEngineCyberia.renameSourceObjectLayerInputId}`)?.value?.trim() || '',
68
+ target: s(`.${MapEngineCyberia.renameTargetObjectLayerInputId}`)?.value?.trim() || '',
69
+ };
70
+ }
71
+
72
+ static setDropdownValue(dropdownId, value) {
73
+ if (!value || !DropDown.Tokens[dropdownId]) return;
74
+ DropDown.Tokens[dropdownId].value = value;
75
+ if (s(`.${dropdownId}`)) s(`.${dropdownId}`).value = value;
76
+ htmls(`.dropdown-current-${dropdownId}`, value);
77
+ }
78
+
79
+ static getEntityTypeDropdownOptions() {
80
+ return Object.values(ENTITY_TYPES).map((entityType) => createDropdownOption(entityType));
81
+ }
82
+
83
+ static syncObjectLayerDropdownSelection(itemIds = []) {
84
+ const dropdownId = MapEngineCyberia.objectLayerDropdownId;
85
+ if (!DropDown.Tokens[dropdownId]) return;
86
+
87
+ DropDown.Tokens[dropdownId].oncheckvalues = {};
88
+ for (const itemId of itemIds) {
89
+ const key = dropdownValueKey(itemId);
90
+ DropDown.Tokens[dropdownId].oncheckvalues[key] = {
91
+ data: itemId,
92
+ display: itemId,
93
+ value: itemId,
94
+ };
95
+ }
96
+ DropDown.Tokens[dropdownId].value = [...itemIds];
97
+ if (s(`.${dropdownId}`)) s(`.${dropdownId}`).value = [...itemIds];
98
+ DropDown.Tokens[dropdownId]._renderSelectedBadges?.();
99
+ }
100
+
101
+ static async buildObjectLayerDropdown() {
102
+ return await DropDown.instance({
103
+ id: MapEngineCyberia.objectLayerDropdownId,
104
+ label: html`Object Layers`,
105
+ data: [],
106
+ type: 'checkbox',
107
+ containerClass: 'inl',
108
+ excludeSelected: true,
109
+ serviceProvider: async (q) => {
110
+ const result = await ObjectLayerService.searchItemIds({ q });
111
+ if (result.status === 'success' && result.data?.itemIds) {
112
+ return result.data.itemIds.map((itemId) => createDropdownOption(itemId));
113
+ }
114
+ return [];
115
+ },
116
+ });
117
+ }
118
+
119
+ static async renderObjectLayerDropdown({ selectedItemIds = [] } = {}) {
120
+ const hostId = MapEngineCyberia.objectLayerDropdownHostId;
121
+ if (!s(`.${hostId}`)) return;
122
+
123
+ htmls(`.${hostId}`, await MapEngineCyberia.buildObjectLayerDropdown());
124
+ MapEngineCyberia.syncObjectLayerDropdownSelection(selectedItemIds);
125
+ }
126
+
127
+ static renameFilteredObjectLayerItemId() {
128
+ const { source, target } = MapEngineCyberia.getRenameObjectLayerItemIds();
129
+ if (!source || !target) {
130
+ NotificationManager.Push({
131
+ html: 'Source and target ItemId are required.',
132
+ status: 'error',
133
+ });
134
+ return false;
135
+ }
136
+
137
+ if (source === target) {
138
+ NotificationManager.Push({
139
+ html: 'Source and target ItemId must be different.',
140
+ status: 'error',
141
+ });
142
+ return false;
143
+ }
144
+
145
+ const filtered = MapEngineCyberia.getFilteredEntities();
146
+ if (!filtered.length) {
147
+ NotificationManager.Push({
148
+ html: 'No filtered entities available for ItemId rename.',
149
+ status: 'error',
150
+ });
151
+ return false;
152
+ }
153
+
154
+ let matchedEntities = 0;
155
+ let renamedReferences = 0;
156
+ const changed = MapEngineCyberia.commitEntityMutation(() => {
157
+ for (const { i } of filtered) {
158
+ const entity = MapEngineCyberia.entities[i];
159
+ if (!Array.isArray(entity?.objectLayerItemIds) || entity.objectLayerItemIds.length === 0) continue;
160
+
161
+ let entityChanged = false;
162
+ entity.objectLayerItemIds = entity.objectLayerItemIds.map((itemId) => {
163
+ if (itemId !== source) return itemId;
164
+ entityChanged = true;
165
+ renamedReferences += 1;
166
+ return target;
167
+ });
168
+
169
+ if (entityChanged) matchedEntities += 1;
170
+ }
171
+ });
172
+
173
+ if (!changed) {
174
+ NotificationManager.Push({
175
+ html: `No exact ItemId matches for "${source}" were found in the current filtered entities.`,
176
+ status: 'error',
177
+ });
178
+ return false;
179
+ }
180
+
181
+ NotificationManager.Push({
182
+ html: `Renamed ${renamedReferences} object layer reference${renamedReferences === 1 ? '' : 's'} across ${matchedEntities} filtered entit${matchedEntities === 1 ? 'y' : 'ies'}.`,
183
+ status: 'success',
184
+ });
185
+ return true;
186
+ }
187
+
188
+ static cloneEntity(entity) {
189
+ return {
190
+ ...entity,
191
+ objectLayerItemIds: Array.isArray(entity?.objectLayerItemIds) ? [...entity.objectLayerItemIds] : [],
192
+ };
193
+ }
194
+
195
+ static cloneEntities(entities = MapEngineCyberia.entities) {
196
+ return (entities || []).map((entity) => MapEngineCyberia.cloneEntity(entity));
197
+ }
198
+
199
+ static entitySnapshotsEqual(left, right) {
200
+ if (left === right) return true;
201
+ if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) return false;
202
+
203
+ for (let i = 0; i < left.length; i++) {
204
+ const a = left[i] || {};
205
+ const b = right[i] || {};
206
+ const aKeys = Object.keys(a);
207
+ const bKeys = Object.keys(b);
208
+ if (aKeys.length !== bKeys.length) return false;
209
+
210
+ for (const key of aKeys) {
211
+ const aValue = a[key];
212
+ const bValue = b[key];
213
+
214
+ if (Array.isArray(aValue) || Array.isArray(bValue)) {
215
+ if (!Array.isArray(aValue) || !Array.isArray(bValue) || aValue.length !== bValue.length) return false;
216
+ for (let j = 0; j < aValue.length; j++) {
217
+ if (aValue[j] !== bValue[j]) return false;
218
+ }
219
+ continue;
220
+ }
221
+
222
+ if (aValue !== bValue) return false;
223
+ }
224
+ }
225
+
226
+ return true;
227
+ }
228
+
229
+ static pushEntityHistory(stack, snapshot) {
230
+ stack.push(snapshot);
231
+ if (stack.length > MapEngineCyberia.maxEntityHistory) stack.shift();
232
+ }
233
+
234
+ static setEntityHistorySync(callback) {
235
+ MapEngineCyberia.entityHistorySync = callback;
236
+ }
237
+
238
+ static preloadEntityObjectLayers(onLoad = null) {
239
+ for (const entity of MapEngineCyberia.entities) {
240
+ for (const itemId of entity.objectLayerItemIds || []) {
241
+ MapEngineCyberia.loadObjectLayerImage(itemId, onLoad);
242
+ }
243
+ }
244
+ }
245
+
246
+ static refreshEntityEditor() {
247
+ const callback = MapEngineCyberia.entityHistorySync;
248
+ if (typeof callback !== 'function') return;
249
+ MapEngineCyberia.preloadEntityObjectLayers(callback);
250
+ callback();
251
+ }
252
+
253
+ static clearEntityHistory() {
254
+ MapEngineCyberia.entityUndoStack.length = 0;
255
+ MapEngineCyberia.entityRedoStack.length = 0;
256
+ }
257
+
258
+ static setEntities(entities, { clearHistory = false } = {}) {
259
+ MapEngineCyberia.entities = MapEngineCyberia.cloneEntities(entities);
260
+ if (clearHistory) MapEngineCyberia.clearEntityHistory();
261
+ MapEngineCyberia.refreshEntityEditor();
262
+ }
263
+
264
+ static commitEntityMutation(mutate) {
265
+ if (typeof mutate !== 'function') return false;
266
+
267
+ const before = MapEngineCyberia.cloneEntities();
268
+ mutate();
269
+ const after = MapEngineCyberia.cloneEntities();
270
+
271
+ if (MapEngineCyberia.entitySnapshotsEqual(before, after)) return false;
272
+
273
+ MapEngineCyberia.pushEntityHistory(MapEngineCyberia.entityUndoStack, before);
274
+ MapEngineCyberia.entityRedoStack.length = 0;
275
+ MapEngineCyberia.refreshEntityEditor();
276
+ return true;
277
+ }
278
+
279
+ static undoEntityMutation() {
280
+ if (!MapEngineCyberia.entityUndoStack.length) return false;
281
+
282
+ const previous = MapEngineCyberia.entityUndoStack.pop();
283
+ MapEngineCyberia.pushEntityHistory(MapEngineCyberia.entityRedoStack, MapEngineCyberia.cloneEntities());
284
+ MapEngineCyberia.setEntities(previous);
285
+ return true;
286
+ }
287
+
288
+ static redoEntityMutation() {
289
+ if (!MapEngineCyberia.entityRedoStack.length) return false;
290
+
291
+ const next = MapEngineCyberia.entityRedoStack.pop();
292
+ MapEngineCyberia.pushEntityHistory(MapEngineCyberia.entityUndoStack, MapEngineCyberia.cloneEntities());
293
+ MapEngineCyberia.setEntities(next);
294
+ return true;
295
+ }
296
+
297
+ static isEditableTarget(target) {
298
+ if (!target) return false;
299
+ const tagName = target.tagName?.toUpperCase();
300
+ return target.isContentEditable || tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT';
301
+ }
302
+
303
+ static isEntityHistoryActive() {
304
+ const container = s('.map-engine-container');
305
+ return !!container && container.isConnected && container.getClientRects().length > 0;
306
+ }
307
+
308
+ static bindEntityHistoryHotkeys() {
309
+ if (MapEngineCyberia.entityHistoryHotkeyHandler) {
310
+ window.removeEventListener('keydown', MapEngineCyberia.entityHistoryHotkeyHandler);
311
+ }
312
+
313
+ MapEngineCyberia.entityHistoryHotkeyHandler = (event) => {
314
+ if (
315
+ event.defaultPrevented ||
316
+ event.altKey ||
317
+ !MapEngineCyberia.isEntityHistoryActive() ||
318
+ MapEngineCyberia.isEditableTarget(event.target)
319
+ ) {
320
+ return;
321
+ }
322
+
323
+ const isModifierPressed = event.ctrlKey || event.metaKey;
324
+ if (!isModifierPressed) return;
325
+
326
+ if (event.key === 'z' || event.key === 'Z') {
327
+ const handled = event.shiftKey ? MapEngineCyberia.redoEntityMutation() : MapEngineCyberia.undoEntityMutation();
328
+ if (handled) event.preventDefault();
329
+ return;
330
+ }
331
+
332
+ if (event.key === 'y' || event.key === 'Y') {
333
+ if (MapEngineCyberia.redoEntityMutation()) event.preventDefault();
334
+ }
335
+ };
336
+
337
+ window.addEventListener('keydown', MapEngineCyberia.entityHistoryHotkeyHandler);
338
+ }
339
+
340
+ static getEntityFilters() {
341
+ return {
342
+ filterType: s('.map-engine-filter-entity-type')?.value?.trim().toLowerCase() || '',
343
+ filterX: s('.map-engine-filter-init-x')?.value?.trim() || '',
344
+ filterY: s('.map-engine-filter-init-y')?.value?.trim() || '',
345
+ };
346
+ }
347
+
348
+ static getFilteredEntities() {
349
+ const { filterType, filterX, filterY } = MapEngineCyberia.getEntityFilters();
350
+
351
+ return MapEngineCyberia.entities.reduce((acc, entity, i) => {
352
+ if (filterType && !(entity.entityType || '').toLowerCase().includes(filterType)) return acc;
353
+ if (filterX !== '' && String(entity.initCellX) !== filterX) return acc;
354
+ if (filterY !== '' && String(entity.initCellY) !== filterY) return acc;
355
+ acc.push({ entity, i });
356
+ return acc;
357
+ }, []);
358
+ }
29
359
 
30
360
  static loadObjectLayerImage(itemId, onLoad) {
31
361
  if (MapEngineCyberia.imageCache[itemId]) return;
32
362
  MapEngineCyberia.imageCache[itemId] = { img: null, loaded: false, error: false };
363
+
364
+ const loadImage = (type, id) => {
365
+ const img = new Image();
366
+ img.onload = () => {
367
+ MapEngineCyberia.imageCache[itemId].img = img;
368
+ MapEngineCyberia.imageCache[itemId].loaded = true;
369
+ if (onLoad) onLoad();
370
+ };
371
+ img.onerror = () => {
372
+ MapEngineCyberia.imageCache[itemId].error = true;
373
+ };
374
+ img.src = `${getProxyPath()}assets/${type}/${id}/08/0.png`;
375
+ };
376
+
377
+ const sharedItem = getDefaultCyberiaItemById(itemId)?.item;
378
+ if (sharedItem?.type && sharedItem?.id) {
379
+ loadImage(sharedItem.type, sharedItem.id);
380
+ return;
381
+ }
382
+
33
383
  ObjectLayerService.get({
34
384
  limit: 1,
35
385
  filterModel: { 'data.item.id': { filterType: 'text', type: 'equals', filter: itemId } },
@@ -41,16 +391,7 @@ class MapEngineCyberia {
41
391
  return;
42
392
  }
43
393
  const { type, id } = doc.data.item;
44
- const img = new Image();
45
- img.onload = () => {
46
- MapEngineCyberia.imageCache[itemId].img = img;
47
- MapEngineCyberia.imageCache[itemId].loaded = true;
48
- if (onLoad) onLoad();
49
- };
50
- img.onerror = () => {
51
- MapEngineCyberia.imageCache[itemId].error = true;
52
- };
53
- img.src = `${getProxyPath()}assets/${type}/${id}/08/0.png`;
394
+ loadImage(type, id);
54
395
  })
55
396
  .catch(() => {
56
397
  MapEngineCyberia.imageCache[itemId].error = true;
@@ -129,17 +470,13 @@ class MapEngineCyberia {
129
470
  const container = s(`.${containerId}`);
130
471
  if (!container) return;
131
472
 
132
- const filterType = s('.map-engine-filter-entity-type')?.value?.trim().toLowerCase() || '';
133
- const filterX = s('.map-engine-filter-init-x')?.value?.trim() || '';
134
- const filterY = s('.map-engine-filter-init-y')?.value?.trim() || '';
135
-
136
- const filtered = [];
137
- MapEngineCyberia.entities.forEach((entity, i) => {
138
- if (filterType && !(entity.entityType || '').toLowerCase().includes(filterType)) return;
139
- if (filterX !== '' && !String(entity.initCellX).includes(filterX)) return;
140
- if (filterY !== '' && !String(entity.initCellY).includes(filterY)) return;
141
- filtered.push({ entity, i });
142
- });
473
+ const filtered = MapEngineCyberia.getFilteredEntities();
474
+ const counter = s('.map-engine-entity-filter-count');
475
+ if (counter) {
476
+ const total = MapEngineCyberia.entities.length;
477
+ const visible = filtered.length;
478
+ counter.innerHTML = `Showing ${visible} of ${total} entities`;
479
+ }
143
480
 
144
481
  let html = '';
145
482
  filtered.forEach(({ entity, i }) => {
@@ -189,17 +526,10 @@ class MapEngineCyberia {
189
526
 
190
527
  container.querySelectorAll('.btn-map-engine-remove-entity').forEach((btn) => {
191
528
  btn.onclick = () => {
192
- const idx = parseInt(btn.dataset.index);
193
- MapEngineCyberia.entities.splice(idx, 1);
194
- MapEngineCyberia.renderEntityList(containerId);
195
- const canvasEl = s('.map-engine-canvas');
196
- if (canvasEl) {
197
- const cols = parseInt(s('.map-engine-input-x')?.value) || 16;
198
- const rows = parseInt(s('.map-engine-input-y')?.value) || 16;
199
- const cellW = parseInt(s('.map-engine-input-cell-w')?.value) || 32;
200
- const cellH = parseInt(s('.map-engine-input-cell-h')?.value) || 32;
201
- MapEngineCyberia.renderGrid(canvasEl, cols, rows, cellW, cellH, MapEngineCyberia.showGridBorders);
202
- }
529
+ const idx = parseInt(btn.dataset.index, 10);
530
+ MapEngineCyberia.commitEntityMutation(() => {
531
+ MapEngineCyberia.entities.splice(idx, 1);
532
+ });
203
533
  };
204
534
  });
205
535
 
@@ -209,7 +539,10 @@ class MapEngineCyberia {
209
539
  const entity = MapEngineCyberia.entities[idx];
210
540
  if (!entity) return;
211
541
 
212
- if (s('.map-engine-entity-type')) s('.map-engine-entity-type').value = entity.entityType || 'floor';
542
+ const entityType = entity.entityType || DEFAULT_ENTITY_TYPE;
543
+ const itemIds = entity.objectLayerItemIds || [];
544
+
545
+ MapEngineCyberia.setDropdownValue(MapEngineCyberia.entityTypeDropdownId, entityType);
213
546
  if (s('.map-engine-init-cell-x')) s('.map-engine-init-cell-x').value = entity.initCellX || 0;
214
547
  if (s('.map-engine-init-cell-y')) s('.map-engine-init-cell-y').value = entity.initCellY || 0;
215
548
  if (s('.map-engine-dim-x')) s('.map-engine-dim-x').value = entity.dimX || 1;
@@ -230,22 +563,7 @@ class MapEngineCyberia {
230
563
  if (s('.map-engine-color')) s('.map-engine-color').dispatchEvent(new Event('input'));
231
564
  }
232
565
 
233
- // Load object layer item IDs into the dropdown
234
- const ddId = 'map-engine-obj-layer-dropdown';
235
- if (DropDown.Tokens[ddId]) {
236
- DropDown.Tokens[ddId].oncheckvalues = {};
237
- const itemIds = entity.objectLayerItemIds || [];
238
- for (const itemId of itemIds) {
239
- const key = itemId.trim().replaceAll(' ', '-');
240
- DropDown.Tokens[ddId].oncheckvalues[key] = { data: itemId, display: itemId, value: itemId };
241
- }
242
- DropDown.Tokens[ddId].value = itemIds;
243
- if (s(`.${ddId}`)) s(`.${ddId}`).value = itemIds;
244
- // Trigger badge re-render
245
- if (s(`.dropdown-current-${ddId}`)) {
246
- DropDown.Tokens[ddId]._renderSelectedBadges?.();
247
- }
248
- }
566
+ MapEngineCyberia.syncObjectLayerDropdownSelection(itemIds);
249
567
  };
250
568
  });
251
569
  }
@@ -266,22 +584,26 @@ class MapEngineCyberia {
266
584
  const idCellH = 'map-engine-input-cell-h';
267
585
  const canvasId = 'map-engine-canvas';
268
586
 
269
- const idEntityType = 'map-engine-entity-type';
587
+ const idEntityType = MapEngineCyberia.entityTypeDropdownId;
270
588
  const idInitCellX = 'map-engine-init-cell-x';
271
589
  const idInitCellY = 'map-engine-init-cell-y';
272
590
  const idDimX = 'map-engine-dim-x';
273
591
  const idDimY = 'map-engine-dim-y';
274
592
  const idColor = 'map-engine-color';
593
+ const idColorPalette = 'map-engine-color-palette';
275
594
  const idAlpha = 'map-engine-alpha';
276
595
  const idFactorA = 'map-engine-factor-a';
277
596
  const idFactorB = 'map-engine-factor-b';
278
597
  const idVariationPreserve = 'map-engine-variation-preserve';
279
598
  const rgbaDisplayId = 'map-engine-rgba-display';
280
599
  const entityListId = 'map-engine-entity-list';
281
- const idObjLayerDropdown = 'map-engine-obj-layer-dropdown';
600
+ const idObjLayerDropdown = MapEngineCyberia.objectLayerDropdownId;
601
+ const idObjLayerDropdownHost = MapEngineCyberia.objectLayerDropdownHostId;
602
+ const idRenameSourceObjectLayer = MapEngineCyberia.renameSourceObjectLayerInputId;
603
+ const idRenameTargetObjectLayer = MapEngineCyberia.renameTargetObjectLayerInputId;
282
604
  const managementId = 'modal-cyberia-map-engine';
283
605
 
284
- MapEngineCyberia.entities = [];
606
+ MapEngineCyberia.setEntities([], { clearHistory: true });
285
607
  MapEngineCyberia.currentMapId = null;
286
608
  MapEngineCyberia.currentThumbnailId = null;
287
609
 
@@ -299,6 +621,44 @@ class MapEngineCyberia {
299
621
  cellH: parseInt(s(`.${idCellH}`)?.value) || 32,
300
622
  });
301
623
 
624
+ const getFactorRange = () => {
625
+ const rawA = parseFloat(s(`.${idFactorA}`)?.value);
626
+ const rawB = parseFloat(s(`.${idFactorB}`)?.value);
627
+ const safeA = Number.isFinite(rawA) ? rawA : 0.5;
628
+ const safeB = Number.isFinite(rawB) ? rawB : 1.5;
629
+ return {
630
+ min: Math.min(safeA, safeB),
631
+ max: Math.max(safeA, safeB),
632
+ };
633
+ };
634
+
635
+ const getPreserveSet = () => {
636
+ const preserveRaw = s(`.${idVariationPreserve}`)?.value || '';
637
+ return new Set(
638
+ preserveRaw
639
+ .split(',')
640
+ .map((t) => t.trim().toLowerCase())
641
+ .filter((t) => t),
642
+ );
643
+ };
644
+
645
+ const getPreserveIndices = () => {
646
+ const preserveSet = getPreserveSet();
647
+ return MapEngineCyberia.entities.reduce((acc, entity, index) => {
648
+ if (preserveSet.has((entity.entityType || '').toLowerCase())) acc.push(index);
649
+ return acc;
650
+ }, []);
651
+ };
652
+
653
+ const getAffectedEntityCount = (total) => {
654
+ if (total <= 0) return 0;
655
+ if (!MapEngineCyberia.enableRandomFactors) return Math.max(1, Math.round(total / 2));
656
+
657
+ const { min, max } = getFactorRange();
658
+ const factor = min + Math.random() * (max - min);
659
+ return Math.max(1, Math.min(total, Math.round(total * factor)));
660
+ };
661
+
302
662
  const rerenderCanvas = () => {
303
663
  const canvas = s(`.${canvasId}`);
304
664
  if (!canvas) return;
@@ -310,7 +670,7 @@ class MapEngineCyberia {
310
670
  const hex = s(`.${idColor}`)?.value || '#ff0000';
311
671
  const alpha = parseFloat(s(`.${idAlpha}`)?.value);
312
672
  return {
313
- entityType: s(`.${idEntityType}`)?.value || 'floor',
673
+ entityType: MapEngineCyberia.getSelectedEntityType(),
314
674
  initCellX: parseInt(s(`.${idInitCellX}`)?.value) || 0,
315
675
  initCellY: parseInt(s(`.${idInitCellY}`)?.value) || 0,
316
676
  dimX: parseInt(s(`.${idDimX}`)?.value) || 1,
@@ -319,12 +679,9 @@ class MapEngineCyberia {
319
679
  };
320
680
  };
321
681
 
322
- const applyRandomDim = (ep) => {
323
- if (!MapEngineCyberia.randomDim) return;
324
- const a = parseFloat(s(`.${idFactorA}`)?.value) || 0.5;
325
- const b = parseFloat(s(`.${idFactorB}`)?.value) || 1.5;
326
- const min = Math.min(a, b);
327
- const max = Math.max(a, b);
682
+ const applyRandomFactorsToDimensions = (ep) => {
683
+ if (!MapEngineCyberia.enableRandomFactors) return;
684
+ const { min, max } = getFactorRange();
328
685
  const factor = min + Math.random() * (max - min);
329
686
  ep.dimX = Math.max(1, Math.round(ep.dimX * factor));
330
687
  ep.dimY = Math.max(1, Math.round(ep.dimY * factor));
@@ -335,13 +692,10 @@ class MapEngineCyberia {
335
692
  ep.objectLayerItemIds = DropDown.Tokens[idObjLayerDropdown]?.value
336
693
  ? [...DropDown.Tokens[idObjLayerDropdown].value]
337
694
  : [];
338
- applyRandomDim(ep);
339
- MapEngineCyberia.entities.push(ep);
340
- for (const itemId of ep.objectLayerItemIds) {
341
- MapEngineCyberia.loadObjectLayerImage(itemId, rerenderCanvas);
342
- }
343
- MapEngineCyberia.renderEntityList(entityListId);
344
- rerenderCanvas();
695
+ applyRandomFactorsToDimensions(ep);
696
+ MapEngineCyberia.commitEntityMutation(() => {
697
+ MapEngineCyberia.entities.push(ep);
698
+ });
345
699
  };
346
700
 
347
701
  const fillMapWithEntity = () => {
@@ -352,67 +706,115 @@ class MapEngineCyberia {
352
706
  const { cols, rows } = getCanvasParams();
353
707
  const dimX = ep.dimX || 1;
354
708
  const dimY = ep.dimY || 1;
355
- for (let r = 0; r < rows; r += dimY) {
356
- for (let c = 0; c < cols; c += dimX) {
357
- const tile = {
358
- ...ep,
359
- initCellX: c,
360
- initCellY: r,
361
- objectLayerItemIds: [...ep.objectLayerItemIds],
362
- };
363
- applyRandomDim(tile);
364
- MapEngineCyberia.entities.push(tile);
709
+ MapEngineCyberia.commitEntityMutation(() => {
710
+ for (let r = 0; r < rows; r += dimY) {
711
+ for (let c = 0; c < cols; c += dimX) {
712
+ const tile = {
713
+ ...ep,
714
+ initCellX: c,
715
+ initCellY: r,
716
+ objectLayerItemIds: [...ep.objectLayerItemIds],
717
+ };
718
+ applyRandomFactorsToDimensions(tile);
719
+ MapEngineCyberia.entities.push(tile);
720
+ }
365
721
  }
366
- }
367
- for (const itemId of ep.objectLayerItemIds) {
368
- MapEngineCyberia.loadObjectLayerImage(itemId, rerenderCanvas);
369
- }
370
- MapEngineCyberia.renderEntityList(entityListId);
371
- rerenderCanvas();
722
+ });
372
723
  };
373
724
 
374
725
  const generateVariation = () => {
375
- const a = parseFloat(s(`.${idFactorA}`)?.value) || 0.5;
376
- const b = parseFloat(s(`.${idFactorB}`)?.value) || 1.5;
377
- const min = Math.min(a, b);
378
- const max = Math.max(a, b);
379
- const preserveRaw = s(`.${idVariationPreserve}`)?.value || '';
380
- const preserveSet = new Set(
381
- preserveRaw
382
- .split(',')
383
- .map((t) => t.trim().toLowerCase())
384
- .filter((t) => t),
385
- );
726
+ const { min, max } = getFactorRange();
727
+ const preserveSet = getPreserveSet();
386
728
  const { cols, rows } = getCanvasParams();
387
- for (const entity of MapEngineCyberia.entities) {
388
- if (preserveSet.has((entity.entityType || '').toLowerCase())) continue;
389
- const dimFactor = min + Math.random() * (max - min);
390
- entity.dimX = Math.max(1, Math.round(entity.dimX * dimFactor));
391
- entity.dimY = Math.max(1, Math.round(entity.dimY * dimFactor));
392
- const posFactor = min + Math.random() * (max - min);
393
- entity.initCellX = Math.max(0, Math.min(cols - 1, Math.round(entity.initCellX * posFactor)));
394
- entity.initCellY = Math.max(0, Math.min(rows - 1, Math.round(entity.initCellY * posFactor)));
395
- }
396
- MapEngineCyberia.renderEntityList(entityListId);
397
- rerenderCanvas();
729
+ MapEngineCyberia.commitEntityMutation(() => {
730
+ for (const entity of MapEngineCyberia.entities) {
731
+ if (preserveSet.has((entity.entityType || '').toLowerCase())) continue;
732
+ const dimFactor = min + Math.random() * (max - min);
733
+ entity.dimX = Math.max(1, Math.round(entity.dimX * dimFactor));
734
+ entity.dimY = Math.max(1, Math.round(entity.dimY * dimFactor));
735
+ const posFactor = min + Math.random() * (max - min);
736
+ entity.initCellX = Math.max(0, Math.min(cols - 1, Math.round(entity.initCellX * posFactor)));
737
+ entity.initCellY = Math.max(0, Math.min(rows - 1, Math.round(entity.initCellY * posFactor)));
738
+ }
739
+ });
740
+ };
741
+
742
+ const clearAllEntities = () => {
743
+ MapEngineCyberia.commitEntityMutation(() => {
744
+ MapEngineCyberia.entities = [];
745
+ });
746
+ };
747
+
748
+ const randomSwapPreserveEntities = () => {
749
+ const preserveIndices = getPreserveIndices();
750
+ if (preserveIndices.length < 2) return;
751
+
752
+ const selectedCount = getAffectedEntityCount(preserveIndices.length);
753
+ const shuffled = [...preserveIndices].sort(() => Math.random() - 0.5).slice(0, selectedCount);
754
+ const swapCount = Math.max(1, Math.floor(shuffled.length / 2));
755
+
756
+ MapEngineCyberia.commitEntityMutation(() => {
757
+ for (let i = 0; i < swapCount * 2; i += 2) {
758
+ const firstIndex = shuffled[i];
759
+ const secondIndex = shuffled[i + 1];
760
+ if (firstIndex === undefined || secondIndex === undefined) continue;
761
+
762
+ const first = MapEngineCyberia.entities[firstIndex];
763
+ const second = MapEngineCyberia.entities[secondIndex];
764
+ const firstPos = { initCellX: first.initCellX, initCellY: first.initCellY };
765
+
766
+ first.initCellX = second.initCellX;
767
+ first.initCellY = second.initCellY;
768
+ second.initCellX = firstPos.initCellX;
769
+ second.initCellY = firstPos.initCellY;
770
+ }
771
+ });
772
+ };
773
+
774
+ const replacePreserveEntitiesWithCurrentConfig = () => {
775
+ const preserveIndices = getPreserveIndices();
776
+ if (preserveIndices.length === 0) return;
777
+
778
+ const selectedCount = getAffectedEntityCount(preserveIndices.length);
779
+ const selected = [...preserveIndices].sort(() => Math.random() - 0.5).slice(0, selectedCount);
780
+ const template = getEntityParams();
781
+ template.objectLayerItemIds = DropDown.Tokens[idObjLayerDropdown]?.value
782
+ ? [...DropDown.Tokens[idObjLayerDropdown].value]
783
+ : [];
784
+
785
+ MapEngineCyberia.commitEntityMutation(() => {
786
+ for (const index of selected) {
787
+ const target = MapEngineCyberia.entities[index];
788
+ const replacement = {
789
+ ...target,
790
+ entityType: template.entityType,
791
+ dimX: template.dimX,
792
+ dimY: template.dimY,
793
+ color: template.color,
794
+ objectLayerItemIds: [...template.objectLayerItemIds],
795
+ };
796
+ applyRandomFactorsToDimensions(replacement);
797
+ MapEngineCyberia.entities[index] = replacement;
798
+ }
799
+ });
398
800
  };
399
801
 
400
802
  const flipHorizontal = () => {
401
803
  const { cols } = getCanvasParams();
402
- for (const entity of MapEngineCyberia.entities) {
403
- entity.initCellX = cols - entity.initCellX - entity.dimX;
404
- }
405
- MapEngineCyberia.renderEntityList(entityListId);
406
- rerenderCanvas();
804
+ MapEngineCyberia.commitEntityMutation(() => {
805
+ for (const entity of MapEngineCyberia.entities) {
806
+ entity.initCellX = cols - entity.initCellX - entity.dimX;
807
+ }
808
+ });
407
809
  };
408
810
 
409
811
  const flipVertical = () => {
410
812
  const { rows } = getCanvasParams();
411
- for (const entity of MapEngineCyberia.entities) {
412
- entity.initCellY = rows - entity.initCellY - entity.dimY;
413
- }
414
- MapEngineCyberia.renderEntityList(entityListId);
415
- rerenderCanvas();
813
+ MapEngineCyberia.commitEntityMutation(() => {
814
+ for (const entity of MapEngineCyberia.entities) {
815
+ entity.initCellY = rows - entity.initCellY - entity.dimY;
816
+ }
817
+ });
416
818
  };
417
819
 
418
820
  const getMapPayload = () => {
@@ -491,8 +893,8 @@ class MapEngineCyberia {
491
893
  result.status === 'error'
492
894
  ? result.message
493
895
  : MapEngineCyberia.currentMapId
494
- ? Translate.Render('success-update-item')
495
- : Translate.Render('success-create-item'),
896
+ ? Translate.instance('success-update-item')
897
+ : Translate.instance('success-create-item'),
496
898
  status: result.status,
497
899
  });
498
900
  if (result.status === 'success') {
@@ -504,26 +906,10 @@ class MapEngineCyberia {
504
906
  const cloneMap = async () => {
505
907
  if (!MapEngineCyberia.currentMapId) return;
506
908
 
507
- // Always upload a new thumbnail file for the clone so it doesn't share the original's file
508
- const thumbnailInput = s(`.${idThumbnail}`);
509
909
  let cloneThumbnailId = null;
510
- if (thumbnailInput && thumbnailInput.files && thumbnailInput.files.length > 0) {
511
- const formData = new FormData();
512
- formData.append('file', thumbnailInput.files[0]);
513
- const uploadResult = await FileService.post({ body: formData });
514
- if (uploadResult.status === 'success' && uploadResult.data && uploadResult.data.length > 0) {
515
- cloneThumbnailId = uploadResult.data[0]._id;
516
- } else {
517
- NotificationManager.Push({
518
- html: uploadResult.message || 'Failed to upload thumbnail',
519
- status: 'error',
520
- });
521
- return;
522
- }
523
- }
524
910
 
525
- // Capture object layer thumbnail for clone if checkbox is checked
526
- if (!cloneThumbnailId && MapEngineCyberia.captureObjLayerThumbnail) {
911
+ // When enabled, clone should use a fresh object-layer capture instead of reusing the current thumbnail file.
912
+ if (MapEngineCyberia.captureObjLayerThumbnail) {
527
913
  const { cols, rows, cellW, cellH } = getCanvasParams();
528
914
  const offscreen = MapEngineCyberia.renderToOffscreenCanvas(cols, rows, cellW, cellH, {
529
915
  forceObjectLayers: true,
@@ -538,13 +924,29 @@ class MapEngineCyberia {
538
924
  cloneThumbnailId = uploadResult.data[0]._id;
539
925
  }
540
926
  }
927
+ } else {
928
+ const thumbnailInput = s(`.${idThumbnail}`);
929
+ if (thumbnailInput && thumbnailInput.files && thumbnailInput.files.length > 0) {
930
+ const formData = new FormData();
931
+ formData.append('file', thumbnailInput.files[0]);
932
+ const uploadResult = await FileService.post({ body: formData });
933
+ if (uploadResult.status === 'success' && uploadResult.data && uploadResult.data.length > 0) {
934
+ cloneThumbnailId = uploadResult.data[0]._id;
935
+ } else {
936
+ NotificationManager.Push({
937
+ html: uploadResult.message || 'Failed to upload thumbnail',
938
+ status: 'error',
939
+ });
940
+ return;
941
+ }
942
+ }
541
943
  }
542
944
 
543
945
  const body = getMapPayload();
544
946
  if (cloneThumbnailId) body.thumbnail = cloneThumbnailId;
545
947
  const result = await CyberiaMapService.post({ body });
546
948
  NotificationManager.Push({
547
- html: result.status === 'error' ? result.message : Translate.Render('success-create-item'),
949
+ html: result.status === 'error' ? result.message : Translate.instance('success-create-item'),
548
950
  status: result.status,
549
951
  });
550
952
  if (result.status === 'success') {
@@ -624,7 +1026,7 @@ class MapEngineCyberia {
624
1026
  }
625
1027
  }
626
1028
 
627
- MapEngineCyberia.entities = (mapData.entities || []).map((e) => ({
1029
+ const nextEntities = (mapData.entities || []).map((e) => ({
628
1030
  entityType: e.entityType,
629
1031
  initCellX: e.initCellX,
630
1032
  initCellY: e.initCellY,
@@ -633,13 +1035,7 @@ class MapEngineCyberia {
633
1035
  color: e.color,
634
1036
  objectLayerItemIds: e.objectLayerItemIds || [],
635
1037
  }));
636
- for (const entity of MapEngineCyberia.entities) {
637
- for (const itemId of entity.objectLayerItemIds || []) {
638
- MapEngineCyberia.loadObjectLayerImage(itemId, rerenderCanvas);
639
- }
640
- }
641
- MapEngineCyberia.renderEntityList(entityListId);
642
- rerenderCanvas();
1038
+ MapEngineCyberia.setEntities(nextEntities, { clearHistory: true });
643
1039
  };
644
1040
 
645
1041
  MapEngineCyberia.loadMap = loadMap;
@@ -669,9 +1065,10 @@ class MapEngineCyberia {
669
1065
  if (s(`.${idCellW}`)) s(`.${idCellW}`).value = 32;
670
1066
  if (s(`.${idCellH}`)) s(`.${idCellH}`).value = 32;
671
1067
  if (s(`.${idVariationPreserve}`)) s(`.${idVariationPreserve}`).value = '';
672
- MapEngineCyberia.entities = [];
673
- MapEngineCyberia.renderEntityList(entityListId);
674
- rerenderCanvas();
1068
+ if (s(`.${idRenameSourceObjectLayer}`)) s(`.${idRenameSourceObjectLayer}`).value = '';
1069
+ if (s(`.${idRenameTargetObjectLayer}`)) s(`.${idRenameTargetObjectLayer}`).value = '';
1070
+ MapEngineCyberia.setEntities([], { clearHistory: true });
1071
+ MapEngineCyberia.setDropdownValue(idEntityType, DEFAULT_ENTITY_TYPE);
675
1072
  if (DropDown.Tokens[idObjLayerDropdown]) {
676
1073
  DropDown.Tokens[idObjLayerDropdown].oncheckvalues = {};
677
1074
  DropDown.Tokens[idObjLayerDropdown].value = [];
@@ -684,6 +1081,20 @@ class MapEngineCyberia {
684
1081
  const canvas = s(`.${canvasId}`);
685
1082
  if (!canvas) return;
686
1083
 
1084
+ MapEngineCyberia.setEntityHistorySync(() => {
1085
+ MapEngineCyberia.renderEntityList(entityListId);
1086
+ rerenderCanvas();
1087
+ });
1088
+ MapEngineCyberia.bindEntityHistoryHotkeys();
1089
+
1090
+ const syncPaletteFromColorInput = () => {
1091
+ const colorPalette = s(`.${idColorPalette}`);
1092
+ const hex = (s(`.${idColor}`)?.value || '#ff0000').toUpperCase();
1093
+ if (colorPalette && colorPalette.value !== hex) {
1094
+ colorPalette.value = hex;
1095
+ }
1096
+ };
1097
+
687
1098
  const updateRgbaDisplay = () => {
688
1099
  const hex = s(`.${idColor}`)?.value || '#ff0000';
689
1100
  const alpha = parseFloat(s(`.${idAlpha}`)?.value);
@@ -693,10 +1104,20 @@ class MapEngineCyberia {
693
1104
  `.${rgbaDisplayId}`,
694
1105
  `<span style="display:inline-block;width:16px;height:16px;background:${rgba};border:1px solid #888;vertical-align:middle;margin-right:6px;"></span>${rgba}`,
695
1106
  );
1107
+ syncPaletteFromColorInput();
696
1108
  };
697
1109
 
698
1110
  if (s(`.${idColor}`)) s(`.${idColor}`).addEventListener('input', updateRgbaDisplay);
699
1111
  if (s(`.${idAlpha}`)) s(`.${idAlpha}`).addEventListener('input', updateRgbaDisplay);
1112
+ if (s(`.${idColorPalette}`)) {
1113
+ s(`.${idColorPalette}`).addEventListener('colorchange', (event) => {
1114
+ const nextHex = (event.detail?.value || event.detail?.hex || '#ff0000').toLowerCase();
1115
+ if (s(`.${idColor}`) && s(`.${idColor}`).value !== nextHex) {
1116
+ s(`.${idColor}`).value = nextHex;
1117
+ s(`.${idColor}`).dispatchEvent(new Event('input'));
1118
+ }
1119
+ });
1120
+ }
700
1121
  updateRgbaDisplay();
701
1122
 
702
1123
  const params = getCanvasParams();
@@ -708,6 +1129,7 @@ class MapEngineCyberia {
708
1129
  params.cellH,
709
1130
  MapEngineCyberia.showGridBorders,
710
1131
  );
1132
+ MapEngineCyberia.refreshEntityEditor();
711
1133
 
712
1134
  canvas.onclick = (e) => {
713
1135
  const rect = canvas.getBoundingClientRect();
@@ -731,6 +1153,12 @@ class MapEngineCyberia {
731
1153
  if (s(`.btn-map-engine-generate-variation`))
732
1154
  s(`.btn-map-engine-generate-variation`).onclick = () => generateVariation();
733
1155
 
1156
+ if (s(`.btn-map-engine-swap-preserve-entities`))
1157
+ s(`.btn-map-engine-swap-preserve-entities`).onclick = () => randomSwapPreserveEntities();
1158
+
1159
+ if (s(`.btn-map-engine-replace-preserve-entities`))
1160
+ s(`.btn-map-engine-replace-preserve-entities`).onclick = () => replacePreserveEntitiesWithCurrentConfig();
1161
+
734
1162
  if (s(`.btn-map-engine-flip-horizontal`)) s(`.btn-map-engine-flip-horizontal`).onclick = () => flipHorizontal();
735
1163
 
736
1164
  if (s(`.btn-map-engine-flip-vertical`)) s(`.btn-map-engine-flip-vertical`).onclick = () => flipVertical();
@@ -812,6 +1240,14 @@ class MapEngineCyberia {
812
1240
  });
813
1241
  MapEngineCyberia.renderEntityList(entityListId);
814
1242
  };
1243
+
1244
+ if (s('.btn-map-engine-clear-all-entities'))
1245
+ s('.btn-map-engine-clear-all-entities').onclick = () => clearAllEntities();
1246
+
1247
+ if (s('.btn-map-engine-rename-filtered-object-layer-item-id'))
1248
+ s('.btn-map-engine-rename-filtered-object-layer-item-id').onclick = () => {
1249
+ MapEngineCyberia.renameFilteredObjectLayerItemId();
1250
+ };
815
1251
  });
816
1252
 
817
1253
  const statusOptions = [
@@ -821,7 +1257,7 @@ class MapEngineCyberia {
821
1257
  { value: 'archived', display: 'archived', data: 'archived', onClick: () => {} },
822
1258
  ];
823
1259
 
824
- const managementTableHtml = await CyberiaMapManagement.RenderTable({
1260
+ const managementTableHtml = await CyberiaMapManagement.instance({
825
1261
  idModal: managementId,
826
1262
  loadMapCallback: loadMap,
827
1263
  appStore,
@@ -855,7 +1291,7 @@ class MapEngineCyberia {
855
1291
  ${dynamicCol({ containerSelector: 'map-engine-container', id: dcMapFields, type: 'search-inputs' })}
856
1292
  <div class="fl">
857
1293
  <div class="in fll ${dcMapFields}-col-a">
858
- ${await Input.Render({
1294
+ ${await Input.instance({
859
1295
  id: idCode,
860
1296
  label: html`Code`,
861
1297
  containerClass: 'inl',
@@ -863,7 +1299,7 @@ class MapEngineCyberia {
863
1299
  })}
864
1300
  </div>
865
1301
  <div class="in fll ${dcMapFields}-col-b">
866
- ${await Input.Render({
1302
+ ${await Input.instance({
867
1303
  id: idName,
868
1304
  label: html`Name`,
869
1305
  containerClass: 'inl',
@@ -871,7 +1307,7 @@ class MapEngineCyberia {
871
1307
  })}
872
1308
  </div>
873
1309
  <div class="in fll ${dcMapFields}-col-c">
874
- ${await Input.Render({
1310
+ ${await Input.instance({
875
1311
  id: idDescription,
876
1312
  label: html`Description`,
877
1313
  containerClass: 'inl',
@@ -882,7 +1318,7 @@ class MapEngineCyberia {
882
1318
  ${dynamicCol({ containerSelector: 'map-engine-container', id: dcMetaFields, type: 'search-inputs' })}
883
1319
  <div class="fl">
884
1320
  <div class="in fll ${dcMetaFields}-col-a">
885
- ${await Input.Render({
1321
+ ${await Input.instance({
886
1322
  id: idTags,
887
1323
  label: html`Tags (comma separated)`,
888
1324
  containerClass: 'inl',
@@ -890,7 +1326,7 @@ class MapEngineCyberia {
890
1326
  })}
891
1327
  </div>
892
1328
  <div class="in fll ${dcMetaFields}-col-b">
893
- ${await DropDown.Render({
1329
+ ${await DropDown.instance({
894
1330
  id: idStatus,
895
1331
  label: html`Status`,
896
1332
  data: statusOptions.map((opt) => ({ ...opt })),
@@ -909,12 +1345,12 @@ class MapEngineCyberia {
909
1345
  </div>
910
1346
  <div class="in section-mp" style="margin-top: 5px;">
911
1347
  <div class="in map-engine-thumbnail-preview" style="margin-bottom: 5px;"></div>
912
- ${await BtnIcon.Render({
1348
+ ${await BtnIcon.instance({
913
1349
  class: 'wfa btn-map-engine-capture-thumbnail',
914
1350
  label: html`<i class="fa-solid fa-camera"></i> Capture Thumbnail`,
915
1351
  })}
916
1352
  <div class="fl" style="align-items: center; gap: 8px; font-size: 20px; text-align: left; margin: 5px 0;">
917
- ${await ToggleSwitch.Render({
1353
+ ${await ToggleSwitch.instance({
918
1354
  id: 'map-engine-capture-obj-layer-thumb',
919
1355
  type: 'checkbox',
920
1356
  displayMode: 'checkbox',
@@ -929,14 +1365,14 @@ class MapEngineCyberia {
929
1365
  },
930
1366
  },
931
1367
  })}
932
- <div class="section-mp">&nbsp &nbsp Capture Object Layer Map Thumbnail on Save/Update</div>
1368
+ <div class="section-mp">&nbsp &nbsp Capture Object Layer Map Thumbnail on Save/Update/Clone</div>
933
1369
  </div>
934
- ${await BtnIcon.Render({
1370
+ ${await BtnIcon.instance({
935
1371
  class: 'wfa btn-map-engine-toggle-thumbnail',
936
1372
  label: html`<i class="fa-solid fa-caret-right map-engine-thumbnail-caret"></i> Thumbnail`,
937
1373
  })}
938
1374
  <div class="in map-engine-thumbnail-body hide">
939
- ${await InputFile.Render(
1375
+ ${await InputFile.instance(
940
1376
  {
941
1377
  id: idThumbnail,
942
1378
  multiple: false,
@@ -970,7 +1406,7 @@ class MapEngineCyberia {
970
1406
  ${dynamicCol({ containerSelector: 'map-engine-container', id: dcGridSize, type: 'a-50-b-50' })}
971
1407
  <div class="fl">
972
1408
  <div class="in fll ${dcGridSize}-col-a">
973
- ${await Input.Render({
1409
+ ${await Input.instance({
974
1410
  id: idX,
975
1411
  label: html`X`,
976
1412
  containerClass: 'inl',
@@ -980,7 +1416,7 @@ class MapEngineCyberia {
980
1416
  })}
981
1417
  </div>
982
1418
  <div class="in fll ${dcGridSize}-col-b">
983
- ${await Input.Render({
1419
+ ${await Input.instance({
984
1420
  id: idY,
985
1421
  label: html`Y`,
986
1422
  containerClass: 'inl',
@@ -993,7 +1429,7 @@ class MapEngineCyberia {
993
1429
  ${dynamicCol({ containerSelector: 'map-engine-container', id: dcCellSize, type: 'a-50-b-50' })}
994
1430
  <div class="fl">
995
1431
  <div class="in fll ${dcCellSize}-col-a">
996
- ${await Input.Render({
1432
+ ${await Input.instance({
997
1433
  id: idCellW,
998
1434
  label: html`Cell Width (px)`,
999
1435
  containerClass: 'inl',
@@ -1003,7 +1439,7 @@ class MapEngineCyberia {
1003
1439
  })}
1004
1440
  </div>
1005
1441
  <div class="in fll ${dcCellSize}-col-b">
1006
- ${await Input.Render({
1442
+ ${await Input.instance({
1007
1443
  id: idCellH,
1008
1444
  label: html`Cell Height (px)`,
1009
1445
  containerClass: 'inl',
@@ -1015,7 +1451,7 @@ class MapEngineCyberia {
1015
1451
  </div>
1016
1452
  <div class="fl">
1017
1453
  <div class="in wfa" style="padding: 10px; max-width: 200px; margin: auto;">
1018
- ${await BtnIcon.Render({
1454
+ ${await BtnIcon.instance({
1019
1455
  class: 'wfa btn-map-engine-generate',
1020
1456
  label: html`<i class="fa-solid fa-arrows-rotate"></i> Generate`,
1021
1457
  })}
@@ -1026,7 +1462,7 @@ class MapEngineCyberia {
1026
1462
  <div class="fl" style="margin-bottom: 5px;">
1027
1463
  <div class="in fll ${dcCanvasOpts}-col-a">
1028
1464
  <div class="fl" style="align-items: center; gap: 8px; font-size: 20px; text-align: left;">
1029
- ${await ToggleSwitch.Render({
1465
+ ${await ToggleSwitch.instance({
1030
1466
  id: 'map-engine-show-grid',
1031
1467
  type: 'checkbox',
1032
1468
  displayMode: 'checkbox',
@@ -1048,7 +1484,7 @@ class MapEngineCyberia {
1048
1484
  </div>
1049
1485
  <div class="in fll ${dcCanvasOpts}-col-b">
1050
1486
  <div class="fl" style="align-items: center; gap: 8px; font-size: 20px; text-align: left;">
1051
- ${await ToggleSwitch.Render({
1487
+ ${await ToggleSwitch.instance({
1052
1488
  id: 'map-engine-add-on-click',
1053
1489
  type: 'checkbox',
1054
1490
  displayMode: 'checkbox',
@@ -1068,7 +1504,7 @@ class MapEngineCyberia {
1068
1504
  </div>
1069
1505
  <div class="in fll ${dcCanvasOpts}-col-c">
1070
1506
  <div class="fl" style="align-items: center; gap: 8px; font-size: 20px; text-align: left;">
1071
- ${await ToggleSwitch.Render({
1507
+ ${await ToggleSwitch.instance({
1072
1508
  id: 'map-engine-show-object-layers',
1073
1509
  type: 'checkbox',
1074
1510
  displayMode: 'checkbox',
@@ -1098,16 +1534,16 @@ class MapEngineCyberia {
1098
1534
  ${dynamicCol({ containerSelector: 'map-engine-container', id: dcEntityType, type: 'a-50-b-50' })}
1099
1535
  <div class="fl">
1100
1536
  <div class="in fll ${dcEntityType}-col-a">
1101
- ${await Input.Render({
1537
+ ${await DropDown.instance({
1102
1538
  id: idEntityType,
1103
1539
  label: html`Entity Type`,
1540
+ data: MapEngineCyberia.getEntityTypeDropdownOptions(),
1541
+ value: DEFAULT_ENTITY_TYPE,
1104
1542
  containerClass: 'inl',
1105
- type: 'text',
1106
- value: 'floor',
1107
1543
  })}
1108
1544
  </div>
1109
1545
  <div class="in fll ${dcEntityType}-col-b">
1110
- ${await Input.Render({
1546
+ ${await Input.instance({
1111
1547
  id: idColor,
1112
1548
  label: html`Color`,
1113
1549
  containerClass: 'inl',
@@ -1143,10 +1579,14 @@ class MapEngineCyberia {
1143
1579
  <div class="in ${rgbaDisplayId}" style="font-family: monospace; font-size: 13px;"></div>
1144
1580
  </div>
1145
1581
  </div>
1582
+ <div class="in section-mp-border" style="margin-top: 10px;">
1583
+ <div class="in input-label">Color Palette</div>
1584
+ <color-palette class="${idColorPalette}" value="#FF0000"></color-palette>
1585
+ </div>
1146
1586
  ${dynamicCol({ containerSelector: 'map-engine-container', id: dcCellPos, type: 'a-50-b-50' })}
1147
1587
  <div class="fl">
1148
1588
  <div class="in fll ${dcCellPos}-col-a">
1149
- ${await Input.Render({
1589
+ ${await Input.instance({
1150
1590
  id: idInitCellX,
1151
1591
  label: html`initCellX`,
1152
1592
  containerClass: 'inl',
@@ -1156,7 +1596,7 @@ class MapEngineCyberia {
1156
1596
  })}
1157
1597
  </div>
1158
1598
  <div class="in fll ${dcCellPos}-col-b">
1159
- ${await Input.Render({
1599
+ ${await Input.instance({
1160
1600
  id: idInitCellY,
1161
1601
  label: html`initCellY`,
1162
1602
  containerClass: 'inl',
@@ -1169,7 +1609,7 @@ class MapEngineCyberia {
1169
1609
  ${dynamicCol({ containerSelector: 'map-engine-container', id: dcDim, type: 'a-50-b-50' })}
1170
1610
  <div class="fl">
1171
1611
  <div class="in fll ${dcDim}-col-a">
1172
- ${await Input.Render({
1612
+ ${await Input.instance({
1173
1613
  id: idDimX,
1174
1614
  label: html`dimX`,
1175
1615
  containerClass: 'inl',
@@ -1179,7 +1619,7 @@ class MapEngineCyberia {
1179
1619
  })}
1180
1620
  </div>
1181
1621
  <div class="in fll ${dcDim}-col-b">
1182
- ${await Input.Render({
1622
+ ${await Input.instance({
1183
1623
  id: idDimY,
1184
1624
  label: html`dimY`,
1185
1625
  containerClass: 'inl',
@@ -1192,7 +1632,7 @@ class MapEngineCyberia {
1192
1632
  ${dynamicCol({ containerSelector: 'map-engine-container', id: dcFactors, type: 'a-50-b-50' })}
1193
1633
  <div class="fl">
1194
1634
  <div class="in fll ${dcFactors}-col-a">
1195
- ${await Input.Render({
1635
+ ${await Input.instance({
1196
1636
  id: idFactorA,
1197
1637
  label: html`factorA`,
1198
1638
  containerClass: 'inl',
@@ -1202,7 +1642,7 @@ class MapEngineCyberia {
1202
1642
  })}
1203
1643
  </div>
1204
1644
  <div class="in fll ${dcFactors}-col-b">
1205
- ${await Input.Render({
1645
+ ${await Input.instance({
1206
1646
  id: idFactorB,
1207
1647
  label: html`factorB`,
1208
1648
  containerClass: 'inl',
@@ -1212,7 +1652,7 @@ class MapEngineCyberia {
1212
1652
  })}
1213
1653
  </div>
1214
1654
  </div>
1215
- ${await Input.Render({
1655
+ ${await Input.instance({
1216
1656
  id: idVariationPreserve,
1217
1657
  label: html`Variation Preserve List`,
1218
1658
  containerClass: 'inl',
@@ -1220,7 +1660,7 @@ class MapEngineCyberia {
1220
1660
  placeholder: true,
1221
1661
  })}
1222
1662
  <div class="fl" style="align-items: center; gap: 8px; font-size: 20px; text-align: left; margin: 5px 0;">
1223
- ${await ToggleSwitch.Render({
1663
+ ${await ToggleSwitch.instance({
1224
1664
  id: 'map-engine-random-dim',
1225
1665
  type: 'checkbox',
1226
1666
  displayMode: 'checkbox',
@@ -1228,69 +1668,94 @@ class MapEngineCyberia {
1228
1668
  checked: false,
1229
1669
  on: {
1230
1670
  checked: () => {
1231
- MapEngineCyberia.randomDim = true;
1671
+ MapEngineCyberia.enableRandomFactors = true;
1232
1672
  },
1233
1673
  unchecked: () => {
1234
- MapEngineCyberia.randomDim = false;
1674
+ MapEngineCyberia.enableRandomFactors = false;
1235
1675
  },
1236
1676
  },
1237
1677
  })}
1238
- <div class="section-mp">&nbsp &nbsp Random Dim</div>
1678
+ <div class="section-mp">&nbsp &nbsp Enable Random Factors</div>
1239
1679
  </div>
1240
1680
  <div class="in" style="margin: 10px;">
1241
- ${await DropDown.Render({
1242
- id: idObjLayerDropdown,
1243
- label: html`Object Layers`,
1244
- data: [],
1245
- type: 'checkbox',
1246
- containerClass: 'inl',
1247
- excludeSelected: true,
1248
- serviceProvider: async (q) => {
1249
- const result = await ObjectLayerService.searchItemIds({ q });
1250
- if (result.status === 'success' && result.data?.itemIds) {
1251
- return result.data.itemIds.map((itemId) => ({
1252
- value: itemId,
1253
- display: itemId,
1254
- data: itemId,
1255
- onClick: () => {},
1256
- }));
1257
- }
1258
- return [];
1259
- },
1260
- })}
1681
+ <div class="${idObjLayerDropdownHost}">${await MapEngineCyberia.buildObjectLayerDropdown()}</div>
1682
+ </div>
1683
+ <div class="in section-mp-border" style="margin: 10px; padding: 10px;">
1684
+ <div class="in input-label">Rename Object Layer ItemId on Filtered Entities</div>
1685
+ <div class="in" style="font-size:12px;color:#888;margin-bottom:8px;">
1686
+ Replaces only exact source ItemId matches inside entities currently visible through the filters.
1687
+ </div>
1688
+ <div class="fl">
1689
+ <div class="in fll" style="flex:1;padding-right:5px;">
1690
+ ${await Input.instance({
1691
+ id: idRenameSourceObjectLayer,
1692
+ label: html`Source ItemId`,
1693
+ containerClass: 'inl',
1694
+ type: 'text',
1695
+ placeholder: true,
1696
+ })}
1697
+ </div>
1698
+ <div class="in fll" style="flex:1;padding-left:5px;">
1699
+ ${await Input.instance({
1700
+ id: idRenameTargetObjectLayer,
1701
+ label: html`Target ItemId`,
1702
+ containerClass: 'inl',
1703
+ type: 'text',
1704
+ placeholder: true,
1705
+ })}
1706
+ </div>
1707
+ </div>
1708
+ <div class="in" style="margin-top: 5px;">
1709
+ ${await BtnIcon.instance({
1710
+ class: 'wfa btn-map-engine-rename-filtered-object-layer-item-id',
1711
+ label: html`<i class="fa-solid fa-arrow-right-arrow-left"></i> Rename Filtered ItemId`,
1712
+ })}
1713
+ </div>
1261
1714
  </div>
1262
1715
  <div class="in">
1263
- ${await BtnIcon.Render({
1716
+ ${await BtnIcon.instance({
1264
1717
  class: 'wfa btn-map-engine-add-entity',
1265
1718
  label: html`<i class="fa-solid fa-plus"></i> Add Entity`,
1266
1719
  })}
1267
1720
  </div>
1268
1721
  <div class="in" style="margin-top: 5px;">
1269
- ${await BtnIcon.Render({
1722
+ ${await BtnIcon.instance({
1270
1723
  class: 'wfa btn-map-engine-fill-map',
1271
1724
  label: html`<i class="fa-solid fa-fill-drip"></i> Map Fill`,
1272
1725
  })}
1273
1726
  </div>
1274
1727
  <div class="in" style="margin-top: 5px;">
1275
- ${await BtnIcon.Render({
1728
+ ${await BtnIcon.instance({
1276
1729
  class: 'wfa btn-map-engine-generate-variation',
1277
1730
  label: html`<i class="fa-solid fa-shuffle"></i> Generate Variation`,
1278
1731
  })}
1279
1732
  </div>
1280
1733
  <div class="in" style="margin-top: 5px;">
1281
- ${await BtnIcon.Render({
1734
+ ${await BtnIcon.instance({
1735
+ class: 'wfa btn-map-engine-swap-preserve-entities',
1736
+ label: html`<i class="fa-solid fa-arrows-rotate"></i> Swap Preserve Positions`,
1737
+ })}
1738
+ </div>
1739
+ <div class="in" style="margin-top: 5px;">
1740
+ ${await BtnIcon.instance({
1741
+ class: 'wfa btn-map-engine-replace-preserve-entities',
1742
+ label: html`<i class="fa-solid fa-wand-magic-sparkles"></i> Replace Preserve Entities`,
1743
+ })}
1744
+ </div>
1745
+ <div class="in" style="margin-top: 5px;">
1746
+ ${await BtnIcon.instance({
1282
1747
  class: 'wfa btn-map-engine-flip-horizontal',
1283
1748
  label: html`<i class="fa-solid fa-arrows-left-right"></i> Flip Horizontal`,
1284
1749
  })}
1285
1750
  </div>
1286
1751
  <div class="in" style="margin-top: 5px;">
1287
- ${await BtnIcon.Render({
1752
+ ${await BtnIcon.instance({
1288
1753
  class: 'wfa btn-map-engine-flip-vertical',
1289
1754
  label: html`<i class="fa-solid fa-arrows-up-down"></i> Flip Vertical`,
1290
1755
  })}
1291
1756
  </div>
1292
1757
  <div class="in" style="margin-top: 10px;">
1293
- ${await BtnIcon.Render({
1758
+ ${await BtnIcon.instance({
1294
1759
  class: 'wfa btn-map-engine-toggle-entity-filter',
1295
1760
  label: html`<i class="fa-solid fa-caret-right map-engine-entity-filter-caret"></i> Filters`,
1296
1761
  })}
@@ -1298,7 +1763,7 @@ class MapEngineCyberia {
1298
1763
  ${dynamicCol({ containerSelector: 'map-engine-container', id: dcEntityFilter, type: 'search-inputs' })}
1299
1764
  <div class="fl">
1300
1765
  <div class="in fll ${dcEntityFilter}-col-a">
1301
- ${await Input.Render({
1766
+ ${await Input.instance({
1302
1767
  id: idFilterEntityType,
1303
1768
  label: html`Entity Type`,
1304
1769
  containerClass: 'inl',
@@ -1307,7 +1772,7 @@ class MapEngineCyberia {
1307
1772
  })}
1308
1773
  </div>
1309
1774
  <div class="in fll ${dcEntityFilter}-col-b">
1310
- ${await Input.Render({
1775
+ ${await Input.instance({
1311
1776
  id: idFilterInitX,
1312
1777
  label: html`initCellX`,
1313
1778
  containerClass: 'inl',
@@ -1316,7 +1781,7 @@ class MapEngineCyberia {
1316
1781
  })}
1317
1782
  </div>
1318
1783
  <div class="in fll ${dcEntityFilter}-col-c">
1319
- ${await Input.Render({
1784
+ ${await Input.instance({
1320
1785
  id: idFilterInitY,
1321
1786
  label: html`initCellY`,
1322
1787
  containerClass: 'inl',
@@ -1325,31 +1790,43 @@ class MapEngineCyberia {
1325
1790
  })}
1326
1791
  </div>
1327
1792
  </div>
1793
+ <div
1794
+ class="in map-engine-entity-filter-count"
1795
+ style="margin-top:5px;font-size:12px;color:#888;font-family:monospace;"
1796
+ >
1797
+ Showing 0 of 0 entities
1798
+ </div>
1328
1799
  <div class="in" style="margin-top:5px;">
1329
- ${await BtnIcon.Render({
1800
+ ${await BtnIcon.instance({
1330
1801
  class: 'wfa btn-map-engine-clear-entity-filter',
1331
1802
  label: html`<i class="fa-solid fa-broom"></i> Clear Filters`,
1332
1803
  })}
1333
1804
  </div>
1805
+ <div class="in" style="margin-top:5px;">
1806
+ ${await BtnIcon.instance({
1807
+ class: 'wfa btn-map-engine-clear-all-entities',
1808
+ label: html`<i class="fa-solid fa-trash-can"></i> Delete All Entities`,
1809
+ })}
1810
+ </div>
1334
1811
  </div>
1335
1812
  </div>
1336
1813
  <div class="in ${entityListId}" style="margin-top: 10px; max-height: 200px; overflow-y: auto;"></div>
1337
1814
  ${dynamicCol({ containerSelector: 'map-engine-container', id: dcSaveNew, type: 'search-inputs' })}
1338
1815
  <div class="fl" style="margin-top: 10px;">
1339
1816
  <div class="in fll ${dcSaveNew}-col-a" style="padding: 5px;">
1340
- ${await BtnIcon.Render({
1817
+ ${await BtnIcon.instance({
1341
1818
  class: 'wfa btn-map-engine-save-map',
1342
1819
  label: html`<i class="fa-solid fa-floppy-disk"></i> Save Map`,
1343
1820
  })}
1344
1821
  </div>
1345
1822
  <div class="in fll ${dcSaveNew}-col-b" style="padding: 5px;">
1346
- ${await BtnIcon.Render({
1823
+ ${await BtnIcon.instance({
1347
1824
  class: 'wfa btn-map-engine-clone-map',
1348
1825
  label: html`<i class="fa-solid fa-clone"></i> Clone Map`,
1349
1826
  })}
1350
1827
  </div>
1351
1828
  <div class="in fll ${dcSaveNew}-col-c" style="padding: 5px;">
1352
- ${await BtnIcon.Render({
1829
+ ${await BtnIcon.instance({
1353
1830
  class: 'wfa btn-map-engine-new-map',
1354
1831
  label: html`<i class="fa-solid fa-file"></i> New Map`,
1355
1832
  })}