@underpostnet/cyberia 3.3.73 → 3.3.77

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 (269) hide show
  1. package/.env.example +17 -5
  2. package/.github/workflows/coverall.cyberia.ci.yml +10 -0
  3. package/.github/workflows/docker-image.cyberia-client.ci.yml +1 -1
  4. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +1 -1
  5. package/.github/workflows/docker-image.cyberia-server.ci.yml +1 -1
  6. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +1 -1
  7. package/.github/workflows/docker-image.engine-cyberia.ci.yml +1 -1
  8. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +1 -1
  9. package/.github/workflows/engine-cyberia.ci.yml +1 -1
  10. package/.github/workflows/publish.cyberia.ci.yml +44 -0
  11. package/AGENTS.md +61 -12
  12. package/CHANGELOG.md +159 -161
  13. package/CLI-HELP.md +20 -13
  14. package/Dockerfile +1 -1
  15. package/Dockerfile.dev +1 -1
  16. package/Dockerfile.test +2 -2
  17. package/README.md +28 -28
  18. package/bin/build.js +96 -21
  19. package/bin/cyberia.js +1247 -1373
  20. package/bin/index.js +1247 -1373
  21. package/compose.env +17 -5
  22. package/conf.js +7 -4
  23. package/deploy/cyberia-client/deploy.sh +10 -11
  24. package/deploy/cyberia-client/package.sh +0 -1
  25. package/deploy/cyberia-client/state.sh +3 -2
  26. package/deploy/cyberia-server/deploy.sh +10 -11
  27. package/deploy/cyberia-server/package.sh +0 -1
  28. package/deploy/cyberia-server/state.sh +3 -2
  29. package/deploy/dd-cyberia/deploy.sh +5 -5
  30. package/deploy/dd-cyberia/init.sh +15 -14
  31. package/deploy/dd-cyberia/package.sh +0 -1
  32. package/deploy/dd-cyberia/state.sh +2 -2
  33. package/deploy/dd-cyberia/sync-deploy.sh +139 -75
  34. package/deploy/lib/config.sh +29 -0
  35. package/deploy/lib/github-actions-logging.sh +92 -76
  36. package/deploy/lib/host.sh +28 -19
  37. package/deploy/lib/state.sh +4 -1
  38. package/deploy/pwa-microservices-template/deploy.sh +10 -8
  39. package/deploy/release/deploy.sh +0 -2
  40. package/deployment.yaml +1 -1
  41. package/docker-compose.yml +23 -17
  42. package/docs/coverage/cyberia/api/cyberia-instance/cyberia-fallback-capture.js.html +1591 -0
  43. package/docs/coverage/cyberia/api/cyberia-instance/index.html +116 -0
  44. package/docs/coverage/cyberia/api/cyberia-server-defaults/cyberia-server-defaults.js.html +4138 -0
  45. package/docs/coverage/cyberia/api/cyberia-server-defaults/index.html +116 -0
  46. package/docs/coverage/cyberia/api/object-layer/index.html +116 -0
  47. package/docs/coverage/cyberia/api/object-layer/object-layer.model.js.html +1393 -0
  48. package/docs/coverage/cyberia/base.css +362 -0
  49. package/docs/coverage/cyberia/block-navigation.js +82 -0
  50. package/docs/coverage/cyberia/favicon.png +0 -0
  51. package/docs/coverage/cyberia/index.html +161 -0
  52. package/docs/coverage/cyberia/prettify.css +101 -0
  53. package/docs/coverage/cyberia/prettify.js +937 -0
  54. package/docs/coverage/cyberia/projects/cyberia/atlas-sprite-sheet-generator.js.html +1315 -0
  55. package/docs/coverage/cyberia/projects/cyberia/index.html +176 -0
  56. package/docs/coverage/cyberia/projects/cyberia/instance-backup.js.html +715 -0
  57. package/docs/coverage/cyberia/projects/cyberia/instance-data.js.html +2182 -0
  58. package/docs/coverage/cyberia/projects/cyberia/shape-generator.js.html +3265 -0
  59. package/docs/coverage/cyberia/projects/cyberia/stat-balance.js.html +328 -0
  60. package/docs/coverage/cyberia/sort-arrow-sprite.png +0 -0
  61. package/docs/coverage/cyberia/sorter.js +205 -0
  62. package/hardhat/package-lock.json +2 -2
  63. package/hardhat/package.json +1 -1
  64. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  65. package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +1 -1
  66. package/manifests/deployment/dd-cyberia-development/deployment.yaml +1 -1
  67. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  68. package/manifests/deployment/mongo-express/deployment.yaml +46 -27
  69. package/manifests/deployment/mongo-express/kustomization.yaml +9 -0
  70. package/manifests/deployment/mongo-express/mongo-express-nodeport.yaml +18 -0
  71. package/manifests/deployment/mongo-express/service.yaml +16 -0
  72. package/manifests/deployment/mongo-express-no-auth/disable-mongodb-auth.yaml +16 -0
  73. package/manifests/deployment/mongo-express-no-auth/kustomization.yaml +16 -0
  74. package/package.json +19 -17
  75. package/scripts/test-monitor.sh +3 -3
  76. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +14 -9
  77. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +36 -5
  78. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
  79. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +88 -184
  80. package/src/api/cyberia-action/cyberia-action.router.js +2 -2
  81. package/src/api/cyberia-action/cyberia-action.service.js +3 -4
  82. package/src/api/cyberia-audio/cyberia-audio.controller.js +6 -0
  83. package/src/api/cyberia-audio/cyberia-audio.model.js +61 -0
  84. package/src/api/cyberia-audio/cyberia-audio.router.js +21 -0
  85. package/src/api/cyberia-audio/cyberia-audio.service.js +220 -0
  86. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -3
  87. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +10 -19
  88. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +12 -34
  89. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +15 -32
  90. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +1 -1
  91. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
  92. package/src/api/cyberia-entity/cyberia-entity.model.js +2 -0
  93. package/src/api/cyberia-entity/cyberia-entity.service.js +1 -1
  94. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +8 -2
  95. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +31 -20
  96. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +16 -1
  97. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +466 -3
  98. package/src/api/cyberia-instance/cyberia-fallback-capture.js +87 -51
  99. package/src/api/cyberia-instance/cyberia-fallback-world.js +21 -40
  100. package/src/api/cyberia-instance/cyberia-instance-boot.service.js +5 -5
  101. package/src/api/cyberia-instance/cyberia-instance-items.js +98 -0
  102. package/src/api/cyberia-instance/cyberia-instance-map.service.js +34 -141
  103. package/src/api/cyberia-instance/cyberia-instance.controller.js +1 -4
  104. package/src/api/cyberia-instance/cyberia-instance.model.js +0 -14
  105. package/src/api/cyberia-instance/cyberia-instance.router.js +3 -16
  106. package/src/api/cyberia-instance/cyberia-instance.service.js +3 -43
  107. package/src/api/cyberia-instance/cyberia-world-generator.js +8 -13
  108. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +49 -75
  109. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +74 -1
  110. package/src/api/cyberia-map/cyberia-map.service.js +6 -2
  111. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.controller.js +31 -0
  112. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.model.js +56 -0
  113. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.router.js +33 -0
  114. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +189 -0
  115. package/src/api/cyberia-quest/cyberia-quest.router.js +3 -3
  116. package/src/api/cyberia-quest/cyberia-quest.service.js +3 -3
  117. package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +1 -1
  118. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +403 -188
  119. package/src/api/cyberia-skill/cyberia-skill.model.js +6 -6
  120. package/src/api/file/file.ref.js +153 -0
  121. package/src/api/file/file.ref.json +11 -2
  122. package/src/api/object-layer/object-layer.model.js +47 -12
  123. package/src/api/object-layer/object-layer.router.js +10 -3
  124. package/src/api/object-layer/object-layer.service.js +113 -120
  125. package/src/cli/app.js +12 -1
  126. package/src/cli/client.js +2 -2
  127. package/src/cli/cluster.js +80 -18
  128. package/src/cli/deploy.js +215 -34
  129. package/src/cli/docker-compose.js +1 -1
  130. package/src/cli/fs-selection.js +115 -0
  131. package/src/cli/fs.js +158 -342
  132. package/src/cli/index.js +22 -10
  133. package/src/cli/release.js +3 -1
  134. package/src/cli/repository.js +35 -9
  135. package/src/cli/run.js +80 -199
  136. package/src/cli/test.js +4 -0
  137. package/src/cli/wireguard.js +32 -31
  138. package/src/client/components/core/Badge.js +0 -1
  139. package/src/client/components/core/CssCore.js +8 -1
  140. package/src/client/components/core/Docs.js +77 -100
  141. package/src/client/components/core/Modal.js +77 -23
  142. package/src/client/components/core/ToolTip.js +15 -0
  143. package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +0 -2
  144. package/src/client/components/cyberia/ActionEngineCyberia.js +12 -36
  145. package/src/client/components/cyberia/EntityEngineCyberia.js +492 -121
  146. package/src/client/components/cyberia/InstanceEngineCyberia.js +129 -160
  147. package/src/client/components/cyberia/InstanceSelectionView.js +7 -1
  148. package/src/client/components/cyberia/MapEngineCyberia.js +175 -69
  149. package/src/client/components/cyberia/ObjectLayerEngine.js +1011 -97
  150. package/src/client/components/cyberia/ObjectLayerEngineModal.js +81 -155
  151. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +113 -98
  152. package/src/client/components/cyberia/SharedDefaultsCyberia.js +215 -56
  153. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +0 -34
  154. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +1 -1
  155. package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +712 -76
  156. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +0 -4
  157. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +0 -4
  158. package/src/client/components/default/AppShellDefault.js +1 -4
  159. package/src/client/components/itemledger/AppShellItemledger.js +0 -2
  160. package/src/client/components/underpost/AppShellUnderpost.js +1 -1
  161. package/src/client/public/cyberia-docs/ARCHITECTURE.md +15 -15
  162. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +280 -24
  163. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +51 -29
  164. package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +52 -53
  165. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +16 -11
  166. package/src/client/public/cyberia-docs/CYBERIA.md +28 -28
  167. package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +11 -5
  168. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  169. package/src/client/public/cyberia-docs/STATS-PROGRESSION.md +217 -0
  170. package/src/client/public/cyberia-docs/WHITE-PAPER.md +20 -12
  171. package/src/client/services/cyberia-audio/cyberia-audio.service.js +99 -0
  172. package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +38 -0
  173. package/src/client/services/cyberia-instance/cyberia-instance.management.js +5 -4
  174. package/src/client/services/cyberia-instance/cyberia-instance.service.js +0 -40
  175. package/src/client/services/cyberia-map/cyberia-map.management.js +5 -4
  176. package/src/client/services/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +138 -0
  177. package/src/client/services/object-layer/object-layer.management.js +6 -10
  178. package/src/client/services/object-layer/object-layer.service.js +3 -1
  179. package/src/client/ssr/views/Cyberia404.js +3 -3
  180. package/src/client-builder/client-build-docs.js +52 -52
  181. package/src/client-builder/client-build.js +67 -44
  182. package/src/client-builder/client-bundle.js +347 -0
  183. package/src/db/mongo/MongoExpress.js +187 -0
  184. package/src/grpc/cyberia/grpc-server.js +5 -7
  185. package/src/index.js +1 -1
  186. package/src/projects/cyberia/atlas-sprite-sheet-generator.js +143 -146
  187. package/src/projects/cyberia/atlas-sprite-sheet-store.js +391 -0
  188. package/src/projects/cyberia/besu-genesis-generator.js +5 -14
  189. package/src/projects/cyberia/boot-contract-fixtures.js +32 -0
  190. package/src/projects/cyberia/catalog-cyberia.js +3 -2
  191. package/src/projects/cyberia/gemini-client.js +3 -3
  192. package/src/projects/cyberia/generate-saga.js +4 -11
  193. package/src/projects/cyberia/hot-reload-trigger.js +17 -28
  194. package/src/projects/cyberia/instance-backup.js +210 -0
  195. package/src/projects/cyberia/instance-data.js +177 -257
  196. package/src/projects/cyberia/map-preview-generator.js +32 -68
  197. package/src/projects/cyberia/object-layer.js +152 -319
  198. package/src/projects/cyberia/seed-audio.js +139 -0
  199. package/src/projects/cyberia/semantic-layer-generator-skin.js +2 -8
  200. package/src/projects/cyberia/semantic-layer-generator.js +2 -18
  201. package/src/projects/cyberia/stat-balance.js +81 -0
  202. package/src/projects/cyberia/stat-commands.js +23 -0
  203. package/src/projects/cyberia/stat-contract-generator.js +101 -0
  204. package/src/runtime/cyberia-client/Dockerfile +8 -4
  205. package/src/runtime/cyberia-client/Dockerfile.dev +5 -2
  206. package/src/runtime/cyberia-server/Dockerfile +9 -2
  207. package/src/runtime/cyberia-server/Dockerfile.dev +8 -1
  208. package/src/runtime/engine-cyberia/Dockerfile +1 -1
  209. package/src/runtime/engine-cyberia/Dockerfile.dev +1 -1
  210. package/src/runtime/engine-cyberia/Dockerfile.test +2 -2
  211. package/src/runtime/engine-cyberia/compose.env +17 -5
  212. package/src/runtime/engine-cyberia/docker-compose.yml +23 -17
  213. package/src/server/build/coverage.js +113 -42
  214. package/src/server/build/package.js +55 -12
  215. package/src/server/build/testing.js +59 -9
  216. package/src/server/network/middlewares.js +8 -2
  217. package/src/server/network/underpost-gateway.js +113 -14
  218. package/src/server/ops/logger.js +70 -35
  219. package/src/server/runtime/conf.js +40 -9
  220. package/src/server/runtime/start.js +11 -2
  221. package/src/server/storage/data-query.js +16 -0
  222. package/src/server/storage/downloader.js +62 -14
  223. package/src/server/storage/zip.js +153 -0
  224. package/test/integration/app/cyberia/atlas-sprite-sheet-store.test.js +402 -0
  225. package/test/integration/app/cyberia/atlas-sprite-sheet.test.js +86 -0
  226. package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +34 -0
  227. package/test/integration/app/cyberia/cyberia-entity-type-default.test.js +842 -0
  228. package/test/integration/app/cyberia/cyberia-instance-conf-coerce.test.js +53 -0
  229. package/test/integration/app/cyberia/cyberia-instance-conf-defaults.test.js +16 -22
  230. package/test/integration/app/cyberia/cyberia-instance-items.test.js +90 -0
  231. package/test/integration/app/cyberia/cyberia-load.test.js +6 -7
  232. package/test/integration/app/cyberia/cyberia-map-audio-conf.test.js +256 -0
  233. package/test/integration/app/cyberia/cyberia-stats.test.js +150 -0
  234. package/test/integration/app/cyberia/fallback-world-capture.test.js +87 -2
  235. package/test/integration/app/cyberia/object-layer-item-selection.test.js +56 -0
  236. package/test/integration/app/cyberia/object-layer-natural-key.test.js +32 -0
  237. package/test/integration/app/cyberia/seed-audio.test.js +275 -0
  238. package/test/integration/infra/2-network/wireguard-cli.test.js +33 -9
  239. package/test/integration/infra/2-network/wireguard-edge.test.js +58 -18
  240. package/test/integration/infra/3-cluster/mongo-express-deploy.test.js +156 -0
  241. package/test/integration/infra/4-ingress/api-cross-origin.test.js +79 -0
  242. package/test/integration/infra/4-ingress/underpost-ingress.test.js +1 -1
  243. package/test/unit/catalog.test.js +13 -2
  244. package/test/unit/client-build-docs.test.js +97 -70
  245. package/test/unit/client-bundle.test.js +313 -0
  246. package/test/unit/conf-resolution.test.js +58 -7
  247. package/test/unit/coverage-artifact.test.js +120 -29
  248. package/test/unit/cyberia/instance-backup.test.js +183 -0
  249. package/test/unit/cyberia/instance-data.test.js +314 -0
  250. package/test/unit/cyberia/instance-object-layer-items.test.js +46 -0
  251. package/test/unit/cyberia/publish-workflow.test.js +22 -0
  252. package/test/unit/cyberia/stat-balance.test.js +69 -0
  253. package/test/unit/deploy-legacy-gateway-sweep.test.js +63 -0
  254. package/test/unit/deploy-log-table.test.js +76 -5
  255. package/test/unit/downloader-integrity.test.js +98 -0
  256. package/test/unit/file-reference-registry.test.js +141 -0
  257. package/test/unit/fs-storage-paths.test.js +596 -0
  258. package/test/unit/gateway-pod-fetch.test.js +95 -0
  259. package/test/unit/letsencrypt-issuer.test.js +101 -0
  260. package/test/unit/logger-redaction.test.js +29 -0
  261. package/test/unit/package.test.js +70 -16
  262. package/test/unit/prepare-host.test.js +84 -1
  263. package/test/unit/release-bump.test.js +3 -1
  264. package/test/unit/start-options.test.js +12 -0
  265. package/test/unit/test-tiers.test.js +27 -0
  266. package/test/unit/zip-archive.test.js +142 -0
  267. package/vitest.config.js +11 -3
  268. package/src/api/cyberia-instance/cyberia-fallback-default-items.js +0 -63
  269. package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +0 -368
@@ -15,13 +15,18 @@ import { AgGrid } from '../core/AgGrid.js';
15
15
  import { Modal } from '../core/Modal.js';
16
16
  import { LoadingAnimation } from '../core/LoadingAnimation.js';
17
17
  import { DefaultManagement } from '../../services/default/default.management.js';
18
- import * as _ from '../cyberia/ObjectLayerEngine.js';
18
+ import { ObjectLayerEngineElement } from '../cyberia/ObjectLayerEngine.js';
19
19
  import {
20
20
  ITEM_TYPES,
21
21
  OBJECT_LAYER_DIRECTION_CODES,
22
22
  OBJECT_LAYER_DIRECTION_LABELS,
23
23
  getKeyframeDirectionsByCode,
24
24
  STAT_TYPES,
25
+ STAT_DESCRIPTIONS,
26
+ STAT_MODIFIER_MIN,
27
+ STAT_MODIFIER_MAX,
28
+ validateStats,
29
+ generateRandomStats,
25
30
  } from './SharedDefaultsCyberia.js';
26
31
  import '../core/ColorPaletteElement.js';
27
32
 
@@ -93,8 +98,8 @@ const DEFAULT_DISTORTION_TYPE = DISTORTION_TYPES[0].value;
93
98
  const DEFAULT_DISTORTION_STATUS =
94
99
  'Applies the selected canvas behavior directly to the current editor frame. factorA controls distortion density or mosaic tile scale.';
95
100
  const DEFAULT_DISTORTION_FACTOR_A = 0.12;
96
- const DEFAULT_STAT_RANDOM_MIN = 0;
97
- const DEFAULT_STAT_RANDOM_MAX = 10;
101
+ const DEFAULT_STAT_RANDOM_MIN = STAT_MODIFIER_MIN;
102
+ const DEFAULT_STAT_RANDOM_MAX = STAT_MODIFIER_MAX;
98
103
  const UNIFORM_OPACITY_TOGGLE_ID = 'ol-uniform-opacity-lock';
99
104
  const DIRECTION_PREVIEW_MODAL_ID = 'modal-object-layer-direction-preview';
100
105
  const CANVAS_BEHAVIOR_BY_VALUE = Object.freeze(
@@ -541,45 +546,7 @@ class ObjectLayerEngineModal {
541
546
  data: [],
542
547
  },
543
548
  ];
544
- static statDescriptions = {
545
- effect: {
546
- title: 'Effect',
547
- icon: 'stat-effect.png',
548
- description: 'Amount of life removed when an entity collides or deals an impact.',
549
- detail: 'Measured in life points.',
550
- },
551
- resistance: {
552
- title: 'Resistance',
553
- icon: 'stat-resistance.png',
554
- description: "Adds to the owner's maximum life (survivability cap).",
555
- detail:
556
- "This value is summed with the entity's base max life. It also increases the amount of life restored when a regeneration event occurs (adds directly to current life).",
557
- },
558
- agility: {
559
- title: 'Agility',
560
- icon: 'stat-agility.png',
561
- description: 'Increases the movement speed of entities.',
562
- detail: 'Higher values result in faster movement.',
563
- },
564
- range: {
565
- title: 'Range',
566
- icon: 'stat-range.png',
567
- description: 'Increases the lifetime of a cast/summoned entity.',
568
- detail: 'Measured in milliseconds.',
569
- },
570
- intelligence: {
571
- title: 'Intelligence',
572
- icon: 'stat-intelligence.png',
573
- description: 'Probability-based stat that increases the chance to spawn/trigger a summoned entity.',
574
- detail: 'Higher values increase summoning success rate.',
575
- },
576
- utility: {
577
- title: 'Utility',
578
- icon: 'stat-utility.png',
579
- description: 'Reduces the cooldown time between actions, allowing for more frequent actions.',
580
- detail: 'It also increases the chance to trigger life-regeneration events.',
581
- },
582
- };
549
+ static statDescriptions = STAT_DESCRIPTIONS;
583
550
 
584
551
  static RenderTemplate = (colorTemplate) => {
585
552
  const ole = s('object-layer-engine');
@@ -735,7 +702,7 @@ class ObjectLayerEngineModal {
735
702
  const statsRandomMinInputId = 'ol-input-stats-random-min';
736
703
  const statsRandomMaxInputId = 'ol-input-stats-random-max';
737
704
 
738
- // Check if we have an 'id' query parameter to load existing object layer
705
+ // ?itemId=<item-id> opens the editor on a stored object layer.
739
706
  const queryParams = getQueryParams();
740
707
  let loadedData = null;
741
708
 
@@ -762,42 +729,19 @@ class ObjectLayerEngineModal {
762
729
  return normalizedFactor;
763
730
  };
764
731
 
765
- const readRandomStatBounds = () => {
766
- const minInput = getRenderedInputNode(statsRandomMinInputId);
767
- const maxInput = getRenderedInputNode(statsRandomMaxInputId);
768
- let minValue = Number.parseInt(minInput?.value, 10);
769
- let maxValue = Number.parseInt(maxInput?.value, 10);
770
-
771
- minValue = clampNumber(Number.isFinite(minValue) ? minValue : DEFAULT_STAT_RANDOM_MIN, 0, 10);
772
- maxValue = clampNumber(Number.isFinite(maxValue) ? maxValue : DEFAULT_STAT_RANDOM_MAX, 0, 10);
773
-
774
- if (minValue > maxValue) {
775
- const nextMin = maxValue;
776
- maxValue = minValue;
777
- minValue = nextMin;
778
- }
779
-
780
- if (minInput) minInput.value = String(minValue);
781
- if (maxInput) maxInput.value = String(maxValue);
782
-
783
- return { minValue, maxValue };
784
- };
785
-
786
732
  const randomizeStatInputs = () => {
787
- const { minValue, maxValue } = readRandomStatBounds();
788
-
789
- for (const statType of statTypes) {
790
- const statInput = getRenderedInputNode(`ol-input-item-stats-${statType}`);
791
- if (!statInput) continue;
792
-
793
- const randomValue = Math.floor(Math.random() * (maxValue - minValue + 1)) + minValue;
794
- statInput.value = String(randomValue);
733
+ try {
734
+ const minValue = Number(getRenderedInputNode(statsRandomMinInputId)?.value);
735
+ const maxValue = Number(getRenderedInputNode(statsRandomMaxInputId)?.value);
736
+ const stats = generateRandomStats(minValue, maxValue);
737
+ for (const statType of statTypes) {
738
+ const input = getRenderedInputNode(`ol-input-item-stats-${statType}`);
739
+ if (input) input.value = String(stats[statType]);
740
+ }
741
+ NotificationManager.Push({ html: `Stats randomized between ${minValue} and ${maxValue}.`, status: 'success' });
742
+ } catch (error) {
743
+ NotificationManager.Push({ html: error.message, status: 'error' });
795
744
  }
796
-
797
- NotificationManager.Push({
798
- html: `Stats randomized between ${minValue} and ${maxValue}.`,
799
- status: 'success',
800
- });
801
745
  };
802
746
 
803
747
  let uniformOpacitySyncInProgress = false;
@@ -966,12 +910,13 @@ class ObjectLayerEngineModal {
966
910
  });
967
911
  };
968
912
 
969
- if (queryParams.id) {
970
- ObjectLayerEngineModal.existingObjectLayerId = queryParams.id;
971
- loadedData = await ObjectLayerEngineModal.loadFromDatabase(queryParams.id);
913
+ if (queryParams.itemId) {
914
+ loadedData = await ObjectLayerEngineModal.loadFromDatabase(queryParams.itemId);
972
915
 
973
916
  if (loadedData) {
974
917
  const { metadata, objectLayerRenderFramesId } = loadedData;
918
+ // The URL carries the item id; writes address the document it resolved to.
919
+ ObjectLayerEngineModal.existingObjectLayerId = metadata._id;
975
920
 
976
921
  // Set form values from metadata
977
922
  if (metadata.data) {
@@ -1297,40 +1242,25 @@ class ObjectLayerEngineModal {
1297
1242
  s(`.frame-editor-container`).classList.remove('hide');
1298
1243
  };
1299
1244
 
1300
- // Helper function to process and add frame from PNG URL using ObjectLayerPngLoader
1301
- const processAndAddFrameFromPngUrl = async (directionCode, pngUrl) => {
1302
- // Wait for components to be available with retry logic
1303
- let ole = s('object-layer-engine');
1304
- let loader = s('object-layer-png-loader');
1305
-
1306
- if (!ole || !loader) {
1307
- console.warn('object-layer-engine or object-layer-png-loader component not found after retries');
1308
- return;
1309
- }
1310
-
1245
+ // Adds one stored frame to a direction code, rebuilt cell for cell from the render-frames
1246
+ // document: the index matrix keeps each frame's own width and height, where the asset PNG
1247
+ // would be resampled into whatever size the editor happens to have. The editor is not
1248
+ // involved: a frame that passed through it was captured whenever something else wrote to
1249
+ // the canvas in between, and came back empty.
1250
+ const processAndAddStoredFrame = async (directionCode, indexMatrix, colors) => {
1311
1251
  try {
1312
- // Load PNG using the loader component - it will automatically load into the editor
1313
- await loader.loadPngUrl(pngUrl);
1314
-
1315
- // Export as blob and JSON from component after loading
1316
- const image = await ole.toBlob();
1317
- const json = ole.exportMatrixJSON();
1252
+ const matrix = indexMatrix.map((row) => row.map((index) => colors[index].slice()));
1253
+ const image = await ObjectLayerEngineElement.matrixToBlob(matrix, pixelSize);
1254
+ const json = ObjectLayerEngineElement.matrixJSON(matrix);
1318
1255
  const id = `frame-loaded-${s4()}-${s4()}`;
1319
1256
 
1320
- // Add to ObjectLayerData
1321
1257
  if (!ObjectLayerEngineModal.ObjectLayerData[directionCode]) {
1322
1258
  ObjectLayerEngineModal.ObjectLayerData[directionCode] = [];
1323
1259
  }
1324
1260
  ObjectLayerEngineModal.ObjectLayerData[directionCode].push({ id, image, json });
1325
- console.log(
1326
- `Stored frame ${id} in direction code ${directionCode}. Total frames:`,
1327
- ObjectLayerEngineModal.ObjectLayerData[directionCode].length,
1328
- );
1329
-
1330
- // Add to UI
1331
1261
  await addFrameToBar(directionCode, id, image, json);
1332
1262
  } catch (error) {
1333
- console.error('Error loading frame from PNG URL:', error);
1263
+ console.error('Error loading stored frame:', error);
1334
1264
  }
1335
1265
  };
1336
1266
 
@@ -1381,8 +1311,8 @@ class ObjectLayerEngineModal {
1381
1311
  </div>`,
1382
1312
  containerClass: 'inl',
1383
1313
  type: 'number',
1384
- min: 0,
1385
- max: 10,
1314
+ min: STAT_MODIFIER_MIN,
1315
+ max: STAT_MODIFIER_MAX,
1386
1316
  placeholder: true,
1387
1317
  value: statValue,
1388
1318
  })}
@@ -1426,36 +1356,21 @@ class ObjectLayerEngineModal {
1426
1356
  // Wait longer to ensure all direction bars are rendered
1427
1357
 
1428
1358
  if (loadedData && loadedData.metadata && loadedData.metadata.data && currentDirectionCode) {
1429
- // Show loading animation only once on first direction that has frames
1430
-
1431
- const { type, id } = loadedData.metadata.data.item;
1432
1359
  const directions = ObjectLayerEngineModal.getDirectionsFromDirectionCode(currentDirectionCode);
1433
1360
 
1434
- console.log(`Loading frames for direction code: ${currentDirectionCode}, directions:`, directions);
1435
-
1436
1361
  // Check if frames exist for any direction mapped to this direction code.
1437
1362
  // Object layers with an empty render (no render-frames doc yet) load
1438
1363
  // with zero frames so the user can author them from scratch.
1439
- const frames = loadedData.objectLayerRenderFramesId?.frames;
1364
+ const { frames, colors } = loadedData.objectLayerRenderFramesId ?? {};
1440
1365
  for (const direction of directions) {
1441
1366
  if (frames && frames[direction] && frames[direction].length > 0) {
1442
1367
  // Track this direction code as having original data
1443
1368
  if (!ObjectLayerEngineModal.originalDirectionCodes.includes(currentDirectionCode)) {
1444
1369
  ObjectLayerEngineModal.originalDirectionCodes.push(currentDirectionCode);
1445
1370
  }
1446
- // Load frames from static PNG URLs sequentially to avoid race conditions
1447
- const frameCount = frames[direction].length;
1448
- console.log(
1449
- `Found ${frameCount} frames for direction: ${direction} (code: ${currentDirectionCode})`,
1450
- );
1451
- for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) {
1452
- const pngUrl = `${getProxyPath()}assets/${type}/${id}/${currentDirectionCode}/${frameIndex}.png`;
1453
- console.log(
1454
- `Loading frame ${frameIndex} for direction code ${currentDirectionCode} from: ${pngUrl}`,
1455
- );
1456
- await processAndAddFrameFromPngUrl(currentDirectionCode, pngUrl);
1371
+ for (const indexMatrix of frames[direction]) {
1372
+ await processAndAddStoredFrame(currentDirectionCode, indexMatrix, colors);
1457
1373
  }
1458
- console.log(`Completed loading ${frameCount} frames for direction code: ${currentDirectionCode}`);
1459
1374
  // Once we found frames for this direction code, we can break to avoid duplicates
1460
1375
  break;
1461
1376
  }
@@ -1796,14 +1711,19 @@ class ObjectLayerEngineModal {
1796
1711
  }
1797
1712
  }
1798
1713
  objectLayerRenderFramesData.frame_duration = parseInt(s(`.ol-input-render-frame-duration`).value);
1799
- objectLayer.data.stats = {
1800
- effect: parseInt(s(`.ol-input-item-stats-effect`).value),
1801
- resistance: parseInt(s(`.ol-input-item-stats-resistance`).value),
1802
- agility: parseInt(s(`.ol-input-item-stats-agility`).value),
1803
- range: parseInt(s(`.ol-input-item-stats-range`).value),
1804
- intelligence: parseInt(s(`.ol-input-item-stats-intelligence`).value),
1805
- utility: parseInt(s(`.ol-input-item-stats-utility`).value),
1806
- };
1714
+ try {
1715
+ objectLayer.data.stats = validateStats(
1716
+ Object.fromEntries(
1717
+ STAT_TYPES.map((key) => {
1718
+ const input = getRenderedInputNode(`ol-input-item-stats-${key}`);
1719
+ return [key, input?.value.trim() ? Number(input.value) : NaN];
1720
+ }),
1721
+ ),
1722
+ );
1723
+ } catch (error) {
1724
+ NotificationManager.Push({ html: error.message, status: 'error' });
1725
+ return;
1726
+ }
1807
1727
  objectLayer.data.item = {
1808
1728
  type: ObjectLayerEngineModal.selectItemType,
1809
1729
  activable: ObjectLayerEngineModal.itemActivable,
@@ -1997,9 +1917,12 @@ class ObjectLayerEngineModal {
1997
1917
  padding: 0.5rem;
1998
1918
  width: 70px;
1999
1919
  }
1920
+ /* One square tile per frame whatever its aspect: a wide frame drawn at natural height
1921
+ sat almost entirely under the edit and trash buttons, so a click never reached it. */
2000
1922
  .direction-code-bar-frames-img {
2001
1923
  width: 100px;
2002
- height: auto;
1924
+ height: 100px;
1925
+ object-fit: contain;
2003
1926
  margin: 3px;
2004
1927
  cursor: pointer;
2005
1928
  }
@@ -2312,6 +2235,7 @@ class ObjectLayerEngineModal {
2312
2235
  <div class="in fll ${idSectionB}-col-b">
2313
2236
  <div class="in section-mp section-mp-border">
2314
2237
  <div class="in sub-title-modal"><i class="fa-solid fa-database"></i> Stats data</div>
2238
+ <div class="in">−100 penalty ↔ 0 neutral ↔ +100 bonus</div>
2315
2239
  <div class="fl" style="align-items: flex-end; gap: 8px; flex-wrap: wrap; margin-bottom: 10px;">
2316
2240
  <div class="in fll" style="width: 110px;">
2317
2241
  ${await Input.instance({
@@ -2319,8 +2243,8 @@ class ObjectLayerEngineModal {
2319
2243
  label: html`Random min`,
2320
2244
  containerClass: 'inl',
2321
2245
  type: 'number',
2322
- min: 0,
2323
- max: 10,
2246
+ min: STAT_MODIFIER_MIN,
2247
+ max: STAT_MODIFIER_MAX,
2324
2248
  placeholder: true,
2325
2249
  value: DEFAULT_STAT_RANDOM_MIN,
2326
2250
  })}
@@ -2331,8 +2255,8 @@ class ObjectLayerEngineModal {
2331
2255
  label: html`Random max`,
2332
2256
  containerClass: 'inl',
2333
2257
  type: 'number',
2334
- min: 0,
2335
- max: 10,
2258
+ min: STAT_MODIFIER_MIN,
2259
+ max: STAT_MODIFIER_MAX,
2336
2260
  placeholder: true,
2337
2261
  value: DEFAULT_STAT_RANDOM_MAX,
2338
2262
  })}
@@ -2350,19 +2274,23 @@ class ObjectLayerEngineModal {
2350
2274
  </div>
2351
2275
 
2352
2276
  <div class="fl section-mp">
2353
- ${canMutate
2354
- ? await BtnIcon.instance({
2355
- label: html`<i class="submit-btn-icon fa-solid fa-folder-open"></i>
2356
- ${ObjectLayerEngineModal.existingObjectLayerId ? 'Update' : Translate.instance('save')}`,
2357
- class: `in flr ol-btn-save`,
2358
- })
2359
- : ''}
2360
- ${canMutate && ObjectLayerEngineModal.existingObjectLayerId
2361
- ? await BtnIcon.instance({
2362
- label: html`<i class="submit-btn-icon fa-solid fa-clone"></i> Clone`,
2363
- class: `in flr ol-btn-clone`,
2364
- })
2365
- : ''}
2277
+ ${
2278
+ canMutate
2279
+ ? await BtnIcon.instance({
2280
+ label: html`<i class="submit-btn-icon fa-solid fa-folder-open"></i>
2281
+ ${ObjectLayerEngineModal.existingObjectLayerId ? 'Update' : Translate.instance('save')}`,
2282
+ class: `in flr ol-btn-save`,
2283
+ })
2284
+ : ''
2285
+ }
2286
+ ${
2287
+ canMutate && ObjectLayerEngineModal.existingObjectLayerId
2288
+ ? await BtnIcon.instance({
2289
+ label: html`<i class="submit-btn-icon fa-solid fa-clone"></i> Clone`,
2290
+ class: `in flr ol-btn-clone`,
2291
+ })
2292
+ : ''
2293
+ }
2366
2294
  ${await BtnIcon.instance({
2367
2295
  label: html`<i class="submit-btn-icon fa-solid fa-broom"></i> ${Translate.instance('reset')}`,
2368
2296
  class: `in flr ol-btn-reset`,
@@ -2382,10 +2310,8 @@ class ObjectLayerEngineModal {
2382
2310
  const modalId = `modal-object-layer-engine-${subModalId}`;
2383
2311
  await DefaultManagement.runIsolated(modalId, async () => {
2384
2312
  setPath(`${getProxyPath()}object-layer-engine-management`);
2385
- const queryParams = getQueryParams();
2386
- queryParams.id = id ? id : '';
2387
- queryParams.page = 1;
2388
- setQueryParams(queryParams, { replace: true });
2313
+ // The grid filters by document id; itemId belongs to the editor route only.
2314
+ setQueryParams({ ...getQueryParams(), itemId: null, id: id ? id : '', page: 1 }, { replace: true });
2389
2315
 
2390
2316
  if (id) {
2391
2317
  DefaultManagement.setIdFilter(modalId, id);