@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.
- package/.env.example +17 -5
- package/.github/workflows/coverall.cyberia.ci.yml +10 -0
- package/.github/workflows/docker-image.cyberia-client.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-server.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +1 -1
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +1 -1
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +1 -1
- package/.github/workflows/engine-cyberia.ci.yml +1 -1
- package/.github/workflows/publish.cyberia.ci.yml +44 -0
- package/AGENTS.md +61 -12
- package/CHANGELOG.md +159 -161
- package/CLI-HELP.md +20 -13
- package/Dockerfile +1 -1
- package/Dockerfile.dev +1 -1
- package/Dockerfile.test +2 -2
- package/README.md +28 -28
- package/bin/build.js +96 -21
- package/bin/cyberia.js +1247 -1373
- package/bin/index.js +1247 -1373
- package/compose.env +17 -5
- package/conf.js +7 -4
- package/deploy/cyberia-client/deploy.sh +10 -11
- package/deploy/cyberia-client/package.sh +0 -1
- package/deploy/cyberia-client/state.sh +3 -2
- package/deploy/cyberia-server/deploy.sh +10 -11
- package/deploy/cyberia-server/package.sh +0 -1
- package/deploy/cyberia-server/state.sh +3 -2
- package/deploy/dd-cyberia/deploy.sh +5 -5
- package/deploy/dd-cyberia/init.sh +15 -14
- package/deploy/dd-cyberia/package.sh +0 -1
- package/deploy/dd-cyberia/state.sh +2 -2
- package/deploy/dd-cyberia/sync-deploy.sh +139 -75
- package/deploy/lib/config.sh +29 -0
- package/deploy/lib/github-actions-logging.sh +92 -76
- package/deploy/lib/host.sh +28 -19
- package/deploy/lib/state.sh +4 -1
- package/deploy/pwa-microservices-template/deploy.sh +10 -8
- package/deploy/release/deploy.sh +0 -2
- package/deployment.yaml +1 -1
- package/docker-compose.yml +23 -17
- package/docs/coverage/cyberia/api/cyberia-instance/cyberia-fallback-capture.js.html +1591 -0
- package/docs/coverage/cyberia/api/cyberia-instance/index.html +116 -0
- package/docs/coverage/cyberia/api/cyberia-server-defaults/cyberia-server-defaults.js.html +4138 -0
- package/docs/coverage/cyberia/api/cyberia-server-defaults/index.html +116 -0
- package/docs/coverage/cyberia/api/object-layer/index.html +116 -0
- package/docs/coverage/cyberia/api/object-layer/object-layer.model.js.html +1393 -0
- package/docs/coverage/cyberia/base.css +362 -0
- package/docs/coverage/cyberia/block-navigation.js +82 -0
- package/docs/coverage/cyberia/favicon.png +0 -0
- package/docs/coverage/cyberia/index.html +161 -0
- package/docs/coverage/cyberia/prettify.css +101 -0
- package/docs/coverage/cyberia/prettify.js +937 -0
- package/docs/coverage/cyberia/projects/cyberia/atlas-sprite-sheet-generator.js.html +1315 -0
- package/docs/coverage/cyberia/projects/cyberia/index.html +176 -0
- package/docs/coverage/cyberia/projects/cyberia/instance-backup.js.html +715 -0
- package/docs/coverage/cyberia/projects/cyberia/instance-data.js.html +2182 -0
- package/docs/coverage/cyberia/projects/cyberia/shape-generator.js.html +3265 -0
- package/docs/coverage/cyberia/projects/cyberia/stat-balance.js.html +328 -0
- package/docs/coverage/cyberia/sort-arrow-sprite.png +0 -0
- package/docs/coverage/cyberia/sorter.js +205 -0
- package/hardhat/package-lock.json +2 -2
- package/hardhat/package.json +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +1 -1
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/mongo-express/deployment.yaml +46 -27
- package/manifests/deployment/mongo-express/kustomization.yaml +9 -0
- package/manifests/deployment/mongo-express/mongo-express-nodeport.yaml +18 -0
- package/manifests/deployment/mongo-express/service.yaml +16 -0
- package/manifests/deployment/mongo-express-no-auth/disable-mongodb-auth.yaml +16 -0
- package/manifests/deployment/mongo-express-no-auth/kustomization.yaml +16 -0
- package/package.json +19 -17
- package/scripts/test-monitor.sh +3 -3
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +14 -9
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +36 -5
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +88 -184
- package/src/api/cyberia-action/cyberia-action.router.js +2 -2
- package/src/api/cyberia-action/cyberia-action.service.js +3 -4
- package/src/api/cyberia-audio/cyberia-audio.controller.js +6 -0
- package/src/api/cyberia-audio/cyberia-audio.model.js +61 -0
- package/src/api/cyberia-audio/cyberia-audio.router.js +21 -0
- package/src/api/cyberia-audio/cyberia-audio.service.js +220 -0
- package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -3
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +10 -19
- package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +12 -34
- package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +15 -32
- package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +1 -1
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
- package/src/api/cyberia-entity/cyberia-entity.model.js +2 -0
- package/src/api/cyberia-entity/cyberia-entity.service.js +1 -1
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +8 -2
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +31 -20
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +16 -1
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +466 -3
- package/src/api/cyberia-instance/cyberia-fallback-capture.js +87 -51
- package/src/api/cyberia-instance/cyberia-fallback-world.js +21 -40
- package/src/api/cyberia-instance/cyberia-instance-boot.service.js +5 -5
- package/src/api/cyberia-instance/cyberia-instance-items.js +98 -0
- package/src/api/cyberia-instance/cyberia-instance-map.service.js +34 -141
- package/src/api/cyberia-instance/cyberia-instance.controller.js +1 -4
- package/src/api/cyberia-instance/cyberia-instance.model.js +0 -14
- package/src/api/cyberia-instance/cyberia-instance.router.js +3 -16
- package/src/api/cyberia-instance/cyberia-instance.service.js +3 -43
- package/src/api/cyberia-instance/cyberia-world-generator.js +8 -13
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +49 -75
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +74 -1
- package/src/api/cyberia-map/cyberia-map.service.js +6 -2
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.controller.js +31 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.model.js +56 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.router.js +33 -0
- package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +189 -0
- package/src/api/cyberia-quest/cyberia-quest.router.js +3 -3
- package/src/api/cyberia-quest/cyberia-quest.service.js +3 -3
- package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +1 -1
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +403 -188
- package/src/api/cyberia-skill/cyberia-skill.model.js +6 -6
- package/src/api/file/file.ref.js +153 -0
- package/src/api/file/file.ref.json +11 -2
- package/src/api/object-layer/object-layer.model.js +47 -12
- package/src/api/object-layer/object-layer.router.js +10 -3
- package/src/api/object-layer/object-layer.service.js +113 -120
- package/src/cli/app.js +12 -1
- package/src/cli/client.js +2 -2
- package/src/cli/cluster.js +80 -18
- package/src/cli/deploy.js +215 -34
- package/src/cli/docker-compose.js +1 -1
- package/src/cli/fs-selection.js +115 -0
- package/src/cli/fs.js +158 -342
- package/src/cli/index.js +22 -10
- package/src/cli/release.js +3 -1
- package/src/cli/repository.js +35 -9
- package/src/cli/run.js +80 -199
- package/src/cli/test.js +4 -0
- package/src/cli/wireguard.js +32 -31
- package/src/client/components/core/Badge.js +0 -1
- package/src/client/components/core/CssCore.js +8 -1
- package/src/client/components/core/Docs.js +77 -100
- package/src/client/components/core/Modal.js +77 -23
- package/src/client/components/core/ToolTip.js +15 -0
- package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +0 -2
- package/src/client/components/cyberia/ActionEngineCyberia.js +12 -36
- package/src/client/components/cyberia/EntityEngineCyberia.js +492 -121
- package/src/client/components/cyberia/InstanceEngineCyberia.js +129 -160
- package/src/client/components/cyberia/InstanceSelectionView.js +7 -1
- package/src/client/components/cyberia/MapEngineCyberia.js +175 -69
- package/src/client/components/cyberia/ObjectLayerEngine.js +1011 -97
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +81 -155
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +113 -98
- package/src/client/components/cyberia/SharedDefaultsCyberia.js +215 -56
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +0 -34
- package/src/client/components/cyberia-portal/CssCyberiaPortal.js +1 -1
- package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +712 -76
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +0 -4
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +0 -4
- package/src/client/components/default/AppShellDefault.js +1 -4
- package/src/client/components/itemledger/AppShellItemledger.js +0 -2
- package/src/client/components/underpost/AppShellUnderpost.js +1 -1
- package/src/client/public/cyberia-docs/ARCHITECTURE.md +15 -15
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +280 -24
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +51 -29
- package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +52 -53
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +16 -11
- package/src/client/public/cyberia-docs/CYBERIA.md +28 -28
- package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +11 -5
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/STATS-PROGRESSION.md +217 -0
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +20 -12
- package/src/client/services/cyberia-audio/cyberia-audio.service.js +99 -0
- package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +38 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +5 -4
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +0 -40
- package/src/client/services/cyberia-map/cyberia-map.management.js +5 -4
- package/src/client/services/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +138 -0
- package/src/client/services/object-layer/object-layer.management.js +6 -10
- package/src/client/services/object-layer/object-layer.service.js +3 -1
- package/src/client/ssr/views/Cyberia404.js +3 -3
- package/src/client-builder/client-build-docs.js +52 -52
- package/src/client-builder/client-build.js +67 -44
- package/src/client-builder/client-bundle.js +347 -0
- package/src/db/mongo/MongoExpress.js +187 -0
- package/src/grpc/cyberia/grpc-server.js +5 -7
- package/src/index.js +1 -1
- package/src/projects/cyberia/atlas-sprite-sheet-generator.js +143 -146
- package/src/projects/cyberia/atlas-sprite-sheet-store.js +391 -0
- package/src/projects/cyberia/besu-genesis-generator.js +5 -14
- package/src/projects/cyberia/boot-contract-fixtures.js +32 -0
- package/src/projects/cyberia/catalog-cyberia.js +3 -2
- package/src/projects/cyberia/gemini-client.js +3 -3
- package/src/projects/cyberia/generate-saga.js +4 -11
- package/src/projects/cyberia/hot-reload-trigger.js +17 -28
- package/src/projects/cyberia/instance-backup.js +210 -0
- package/src/projects/cyberia/instance-data.js +177 -257
- package/src/projects/cyberia/map-preview-generator.js +32 -68
- package/src/projects/cyberia/object-layer.js +152 -319
- package/src/projects/cyberia/seed-audio.js +139 -0
- package/src/projects/cyberia/semantic-layer-generator-skin.js +2 -8
- package/src/projects/cyberia/semantic-layer-generator.js +2 -18
- package/src/projects/cyberia/stat-balance.js +81 -0
- package/src/projects/cyberia/stat-commands.js +23 -0
- package/src/projects/cyberia/stat-contract-generator.js +101 -0
- package/src/runtime/cyberia-client/Dockerfile +8 -4
- package/src/runtime/cyberia-client/Dockerfile.dev +5 -2
- package/src/runtime/cyberia-server/Dockerfile +9 -2
- package/src/runtime/cyberia-server/Dockerfile.dev +8 -1
- package/src/runtime/engine-cyberia/Dockerfile +1 -1
- package/src/runtime/engine-cyberia/Dockerfile.dev +1 -1
- package/src/runtime/engine-cyberia/Dockerfile.test +2 -2
- package/src/runtime/engine-cyberia/compose.env +17 -5
- package/src/runtime/engine-cyberia/docker-compose.yml +23 -17
- package/src/server/build/coverage.js +113 -42
- package/src/server/build/package.js +55 -12
- package/src/server/build/testing.js +59 -9
- package/src/server/network/middlewares.js +8 -2
- package/src/server/network/underpost-gateway.js +113 -14
- package/src/server/ops/logger.js +70 -35
- package/src/server/runtime/conf.js +40 -9
- package/src/server/runtime/start.js +11 -2
- package/src/server/storage/data-query.js +16 -0
- package/src/server/storage/downloader.js +62 -14
- package/src/server/storage/zip.js +153 -0
- package/test/integration/app/cyberia/atlas-sprite-sheet-store.test.js +402 -0
- package/test/integration/app/cyberia/atlas-sprite-sheet.test.js +86 -0
- package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +34 -0
- package/test/integration/app/cyberia/cyberia-entity-type-default.test.js +842 -0
- package/test/integration/app/cyberia/cyberia-instance-conf-coerce.test.js +53 -0
- package/test/integration/app/cyberia/cyberia-instance-conf-defaults.test.js +16 -22
- package/test/integration/app/cyberia/cyberia-instance-items.test.js +90 -0
- package/test/integration/app/cyberia/cyberia-load.test.js +6 -7
- package/test/integration/app/cyberia/cyberia-map-audio-conf.test.js +256 -0
- package/test/integration/app/cyberia/cyberia-stats.test.js +150 -0
- package/test/integration/app/cyberia/fallback-world-capture.test.js +87 -2
- package/test/integration/app/cyberia/object-layer-item-selection.test.js +56 -0
- package/test/integration/app/cyberia/object-layer-natural-key.test.js +32 -0
- package/test/integration/app/cyberia/seed-audio.test.js +275 -0
- package/test/integration/infra/2-network/wireguard-cli.test.js +33 -9
- package/test/integration/infra/2-network/wireguard-edge.test.js +58 -18
- package/test/integration/infra/3-cluster/mongo-express-deploy.test.js +156 -0
- package/test/integration/infra/4-ingress/api-cross-origin.test.js +79 -0
- package/test/integration/infra/4-ingress/underpost-ingress.test.js +1 -1
- package/test/unit/catalog.test.js +13 -2
- package/test/unit/client-build-docs.test.js +97 -70
- package/test/unit/client-bundle.test.js +313 -0
- package/test/unit/conf-resolution.test.js +58 -7
- package/test/unit/coverage-artifact.test.js +120 -29
- package/test/unit/cyberia/instance-backup.test.js +183 -0
- package/test/unit/cyberia/instance-data.test.js +314 -0
- package/test/unit/cyberia/instance-object-layer-items.test.js +46 -0
- package/test/unit/cyberia/publish-workflow.test.js +22 -0
- package/test/unit/cyberia/stat-balance.test.js +69 -0
- package/test/unit/deploy-legacy-gateway-sweep.test.js +63 -0
- package/test/unit/deploy-log-table.test.js +76 -5
- package/test/unit/downloader-integrity.test.js +98 -0
- package/test/unit/file-reference-registry.test.js +141 -0
- package/test/unit/fs-storage-paths.test.js +596 -0
- package/test/unit/gateway-pod-fetch.test.js +95 -0
- package/test/unit/letsencrypt-issuer.test.js +101 -0
- package/test/unit/logger-redaction.test.js +29 -0
- package/test/unit/package.test.js +70 -16
- package/test/unit/prepare-host.test.js +84 -1
- package/test/unit/release-bump.test.js +3 -1
- package/test/unit/start-options.test.js +12 -0
- package/test/unit/test-tiers.test.js +27 -0
- package/test/unit/zip-archive.test.js +142 -0
- package/vitest.config.js +11 -3
- package/src/api/cyberia-instance/cyberia-fallback-default-items.js +0 -63
- package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +0 -368
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { BtnIcon } from '../core/BtnIcon.js';
|
|
2
|
-
import { Input } from '../core/Input.js';
|
|
3
2
|
import { commonModeratorGuard } from '../core/CommonJs.js';
|
|
4
3
|
import { htmls, s } from '../core/VanillaJs.js';
|
|
5
4
|
import { NotificationManager } from '../core/NotificationManager.js';
|
|
6
5
|
import { Translate } from '../core/Translate.js';
|
|
7
|
-
import { darkTheme,
|
|
6
|
+
import { darkTheme, ThemeEvents } from '../core/Css.js';
|
|
8
7
|
import { DropDown } from '../core/DropDown.js';
|
|
9
8
|
import { AgGrid } from '../core/AgGrid.js';
|
|
9
|
+
import { getProxyPath } from '../core/Router.js';
|
|
10
10
|
import { ObjectLayerService } from '../../services/object-layer/object-layer.service.js';
|
|
11
11
|
import { CyberiaEntityTypeDefaultService } from '../../services/cyberia-entity-type-default/cyberia-entity-type-default.service.js';
|
|
12
|
+
import { CyberiaInstanceConfService } from '../../services/cyberia-instance-conf/cyberia-instance-conf.service.js';
|
|
12
13
|
import { ENTITY_TYPES, SELECTABLE_ENTITY_BEHAVIORS } from './SharedDefaultsCyberia.js';
|
|
13
14
|
|
|
14
15
|
// DropDown invokes optionData.onClick on selection, so every option must carry one.
|
|
@@ -19,8 +20,8 @@ const subtleBorder = () => (darkTheme ? '#444' : '#e0e0e0');
|
|
|
19
20
|
// EntityEngineCyberia — CRUD surface for the cyberia-entity-type-default
|
|
20
21
|
// collection (the DB-backed mirror of ENTITY_TYPE_DEFAULTS). An ag-grid lists
|
|
21
22
|
// every default; clicking a row loads it into the form. The form binds an
|
|
22
|
-
// entityType to its lifecycle item-id sets (live / dead / drop) plus the
|
|
23
|
-
//
|
|
23
|
+
// entityType to its lifecycle item-id sets (live / dead / drop) plus the
|
|
24
|
+
// inventory-only extras, with server-backed item-id autocomplete pickers so the
|
|
24
25
|
// high-frequency itemId fields stay fast and typo-free. The runtime linking
|
|
25
26
|
// rule (entity matches entityType AND carries an active itemId from liveItemIds)
|
|
26
27
|
// is content-authority only; this engine never mutates entities or the
|
|
@@ -33,7 +34,17 @@ class EntityEngineCyberia {
|
|
|
33
34
|
static liveItemIds = [];
|
|
34
35
|
static deadItemIds = [];
|
|
35
36
|
static dropItemIds = [];
|
|
36
|
-
static
|
|
37
|
+
static inventoryItemsIds = [];
|
|
38
|
+
// Per-id overrides of what the lists derive: { itemId, active, quantity }.
|
|
39
|
+
static overrideItemsIdsState = [];
|
|
40
|
+
// Instance codes whose conf references the loaded default, and every code that could.
|
|
41
|
+
static instanceCodes = [];
|
|
42
|
+
static availableInstanceCodes = [];
|
|
43
|
+
// entityDefault _id → instance codes, for the grid column. Built with the list.
|
|
44
|
+
static instanceLinks = new Map();
|
|
45
|
+
// itemId → item type. A sprite lives under its type, so an item id alone cannot be previewed.
|
|
46
|
+
// Types never change, so this is a cache that only ever grows: ids reach it from the list and
|
|
47
|
+
// from every id added in the form, which is what makes a just-picked item preview immediately.
|
|
37
48
|
|
|
38
49
|
static gridId = 'entity-engine-grid';
|
|
39
50
|
|
|
@@ -43,7 +54,9 @@ class EntityEngineCyberia {
|
|
|
43
54
|
liveItemPicker: 'entity-engine-live-item-picker',
|
|
44
55
|
deadItemPicker: 'entity-engine-dead-item-picker',
|
|
45
56
|
dropItemPicker: 'entity-engine-drop-item-picker',
|
|
46
|
-
|
|
57
|
+
inventoryItemPicker: 'entity-engine-inventory-item-picker',
|
|
58
|
+
instancePicker: 'entity-engine-instance-picker',
|
|
59
|
+
overrideItemPicker: 'entity-engine-override-item-picker',
|
|
47
60
|
};
|
|
48
61
|
|
|
49
62
|
// Behavior options: a leading "auto" (empty → runtime derives armed→hostile,
|
|
@@ -84,20 +97,51 @@ class EntityEngineCyberia {
|
|
|
84
97
|
containerClass: 'inl',
|
|
85
98
|
serviceProvider: async (q) => {
|
|
86
99
|
const result = await ObjectLayerService.searchItemIds({ q });
|
|
87
|
-
if (result.status === 'success' && result.data?.
|
|
88
|
-
return result.data.
|
|
100
|
+
if (result.status === 'success' && result.data?.items) {
|
|
101
|
+
return result.data.items.map(({ id }) => dropdownOption(id));
|
|
89
102
|
}
|
|
90
103
|
return [];
|
|
91
104
|
},
|
|
92
105
|
});
|
|
93
106
|
}
|
|
94
107
|
|
|
95
|
-
//
|
|
108
|
+
// Instance codes are a small, already-loaded set, so the picker filters them in place rather
|
|
109
|
+
// than round-tripping per keystroke the way the item-id pickers do.
|
|
110
|
+
static async buildInstanceDropdown(id) {
|
|
111
|
+
return await DropDown.instance({
|
|
112
|
+
id,
|
|
113
|
+
label: html`Search instance code`,
|
|
114
|
+
data: [],
|
|
115
|
+
containerClass: 'inl',
|
|
116
|
+
serviceProvider: async (q) => {
|
|
117
|
+
const query = String(q || '').toLowerCase();
|
|
118
|
+
return EntityEngineCyberia.availableInstanceCodes
|
|
119
|
+
.filter((code) => !query || code.toLowerCase().includes(query))
|
|
120
|
+
.map((code) => dropdownOption(code));
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ── Item-id list editors ─────────────────────────────────────────────────
|
|
126
|
+
// The lifecycle discriminators plus the inventory-only extras. All four are plain id lists:
|
|
127
|
+
// what an entity carries is their union, and what is active follows from the list, never from
|
|
128
|
+
// a flag stored per row. Form field → stored key, so nothing that holds item ids can be left
|
|
129
|
+
// out of the type resolution or the grid.
|
|
130
|
+
static ITEM_FIELD_KEYS = Object.freeze({
|
|
131
|
+
live: 'liveItemIds',
|
|
132
|
+
dead: 'deadItemIds',
|
|
133
|
+
drop: 'dropItemIds',
|
|
134
|
+
inventory: 'inventoryItemsIds',
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
static ITEM_FIELDS = Object.keys(EntityEngineCyberia.ITEM_FIELD_KEYS);
|
|
138
|
+
|
|
96
139
|
static listFieldOf(field) {
|
|
97
140
|
return {
|
|
98
141
|
live: EntityEngineCyberia.liveItemIds,
|
|
99
142
|
dead: EntityEngineCyberia.deadItemIds,
|
|
100
143
|
drop: EntityEngineCyberia.dropItemIds,
|
|
144
|
+
inventory: EntityEngineCyberia.inventoryItemsIds,
|
|
101
145
|
}[field];
|
|
102
146
|
}
|
|
103
147
|
|
|
@@ -111,7 +155,9 @@ class EntityEngineCyberia {
|
|
|
111
155
|
class="fl"
|
|
112
156
|
style="border-bottom:1px solid ${subtleBorder()};padding:3px 0;align-items:center;font-size:12px;font-family:monospace;"
|
|
113
157
|
>
|
|
114
|
-
<div class="in fll" style="flex:1;"
|
|
158
|
+
<div class="in fll" style="flex:1;display:flex;align-items:center;gap:6px;">
|
|
159
|
+
${EntityEngineCyberia.itemPreview(items[i])}${items[i]}
|
|
160
|
+
</div>
|
|
115
161
|
${await BtnIcon.instance({
|
|
116
162
|
class: `btn-entity-engine-rm-${field}-${i}`,
|
|
117
163
|
label: html`<i class="fa-solid fa-xmark"></i>`,
|
|
@@ -130,7 +176,7 @@ class EntityEngineCyberia {
|
|
|
130
176
|
}
|
|
131
177
|
}
|
|
132
178
|
|
|
133
|
-
static addItem(field, pickerId) {
|
|
179
|
+
static async addItem(field, pickerId) {
|
|
134
180
|
const itemId = EntityEngineCyberia.getSingleDropdownValue(pickerId);
|
|
135
181
|
if (!itemId) {
|
|
136
182
|
NotificationManager.Push({ html: 'Select an itemId first.', status: 'error' });
|
|
@@ -143,83 +189,342 @@ class EntityEngineCyberia {
|
|
|
143
189
|
}
|
|
144
190
|
list.push(itemId);
|
|
145
191
|
EntityEngineCyberia.setSingleDropdownValue(pickerId, '');
|
|
146
|
-
EntityEngineCyberia.renderItemList(field);
|
|
192
|
+
await EntityEngineCyberia.renderItemList(field);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ── Override rules ({ itemId, active, quantity }) ─────────────────────────
|
|
196
|
+
// Every id the four lists carry, which is exactly what an override may adjust.
|
|
197
|
+
static carriedItemIds() {
|
|
198
|
+
return [
|
|
199
|
+
...new Set(
|
|
200
|
+
EntityEngineCyberia.ITEM_FIELDS.flatMap((field) => EntityEngineCyberia.listFieldOf(field)).filter(Boolean),
|
|
201
|
+
),
|
|
202
|
+
];
|
|
147
203
|
}
|
|
148
204
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const container = s('.entity-engine-layer-list');
|
|
205
|
+
static async renderOverrideList() {
|
|
206
|
+
const container = s('.entity-engine-override-list');
|
|
152
207
|
if (!container) return;
|
|
208
|
+
const carried = new Set(EntityEngineCyberia.carriedItemIds());
|
|
153
209
|
let out = '';
|
|
154
|
-
for (let i = 0; i < EntityEngineCyberia.
|
|
155
|
-
const
|
|
210
|
+
for (let i = 0; i < EntityEngineCyberia.overrideItemsIdsState.length; i++) {
|
|
211
|
+
const rule = EntityEngineCyberia.overrideItemsIdsState[i];
|
|
212
|
+
// An override for an id no list carries is inert — the union decides membership.
|
|
213
|
+
const orphan = !carried.has(rule.itemId);
|
|
214
|
+
// Drop chance only reaches the simulation for an id the drop list carries, so the input is
|
|
215
|
+
// offered where it can do something and the reason is stated where it cannot.
|
|
216
|
+
const isDrop = EntityEngineCyberia.listFieldOf('drop').includes(rule.itemId);
|
|
156
217
|
out += html`<div
|
|
157
218
|
class="fl"
|
|
158
219
|
style="border-bottom:1px solid ${subtleBorder()};padding:3px 0;align-items:center;font-size:12px;font-family:monospace;"
|
|
159
220
|
>
|
|
160
|
-
<div class="in fll" style="flex:1;">
|
|
161
|
-
${
|
|
162
|
-
<span style="
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
221
|
+
<div class="in fll" style="flex:1;display:flex;align-items:center;gap:6px;">
|
|
222
|
+
${EntityEngineCyberia.itemPreview(rule.itemId)}
|
|
223
|
+
<span style="${orphan ? 'color:#c90;' : ''}">${rule.itemId}</span>
|
|
224
|
+
<label style="cursor:pointer;display:inline-flex;align-items:center;gap:4px;">
|
|
225
|
+
<input class="entity-engine-override-active-${i}" type="checkbox" ${rule.active ? 'checked' : ''} />
|
|
226
|
+
active
|
|
227
|
+
</label>
|
|
228
|
+
<span>×</span>
|
|
229
|
+
<input
|
|
230
|
+
class="entity-engine-override-qty-${i}"
|
|
231
|
+
type="number"
|
|
232
|
+
min="1"
|
|
233
|
+
value="${rule.quantity}"
|
|
234
|
+
style="width:60px;"
|
|
235
|
+
/>
|
|
236
|
+
${isDrop
|
|
237
|
+
? html`<label
|
|
238
|
+
style="cursor:pointer;display:inline-flex;align-items:center;gap:4px;"
|
|
239
|
+
title="Probability this id scatters on death. 1 always drops, 0 never does."
|
|
240
|
+
>
|
|
241
|
+
drop
|
|
242
|
+
<input
|
|
243
|
+
class="entity-engine-override-drop-chance-${i}"
|
|
244
|
+
type="number"
|
|
245
|
+
min="0"
|
|
246
|
+
max="1"
|
|
247
|
+
step="0.05"
|
|
248
|
+
value="${Number.isFinite(rule.dropChance) ? rule.dropChance : 1}"
|
|
249
|
+
style="width:60px;"
|
|
250
|
+
/>
|
|
251
|
+
</label>`
|
|
252
|
+
: ''}
|
|
253
|
+
${orphan ? html`<span style="color:#c90;font-size:11px;">no list carries this id</span>` : ''}
|
|
166
254
|
</div>
|
|
167
255
|
${await BtnIcon.instance({
|
|
168
|
-
class: `btn-entity-engine-rm-
|
|
256
|
+
class: `btn-entity-engine-rm-override-${i}`,
|
|
169
257
|
label: html`<i class="fa-solid fa-xmark"></i>`,
|
|
170
258
|
style: 'min-width:30px;padding:2px 8px;',
|
|
171
259
|
})}
|
|
172
260
|
</div>`;
|
|
173
261
|
}
|
|
174
|
-
if (!out) out = '<div style="color:#888;font-size:12px;">No
|
|
175
|
-
htmls('.entity-engine-
|
|
176
|
-
for (let i = 0; i < EntityEngineCyberia.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
262
|
+
if (!out) out = '<div style="color:#888;font-size:12px;">No overrides — every id starts as its list derives.</div>';
|
|
263
|
+
htmls('.entity-engine-override-list', out);
|
|
264
|
+
for (let i = 0; i < EntityEngineCyberia.overrideItemsIdsState.length; i++) {
|
|
265
|
+
const active = s(`.entity-engine-override-active-${i}`);
|
|
266
|
+
if (active) active.onchange = () => (EntityEngineCyberia.overrideItemsIdsState[i].active = active.checked);
|
|
267
|
+
const qty = s(`.entity-engine-override-qty-${i}`);
|
|
268
|
+
if (qty)
|
|
269
|
+
qty.onchange = () =>
|
|
270
|
+
(EntityEngineCyberia.overrideItemsIdsState[i].quantity = Math.max(1, parseInt(qty.value) || 1));
|
|
271
|
+
const dropChance = s(`.entity-engine-override-drop-chance-${i}`);
|
|
272
|
+
if (dropChance)
|
|
273
|
+
dropChance.onchange = () => {
|
|
274
|
+
const parsed = parseFloat(dropChance.value);
|
|
275
|
+
const rule = EntityEngineCyberia.overrideItemsIdsState[i];
|
|
276
|
+
// An empty or unreadable box means "say nothing", which the whole pipeline reads as
|
|
277
|
+
// always drops — the same as a world that never set one.
|
|
278
|
+
if (!Number.isFinite(parsed)) delete rule.dropChance;
|
|
279
|
+
else rule.dropChance = Math.min(1, Math.max(0, parsed));
|
|
280
|
+
dropChance.value = Number.isFinite(rule.dropChance) ? rule.dropChance : 1;
|
|
281
|
+
};
|
|
282
|
+
if (s(`.btn-entity-engine-rm-override-${i}`))
|
|
283
|
+
s(`.btn-entity-engine-rm-override-${i}`).onclick = () => {
|
|
284
|
+
EntityEngineCyberia.overrideItemsIdsState.splice(i, 1);
|
|
285
|
+
EntityEngineCyberia.renderOverrideList();
|
|
181
286
|
};
|
|
182
287
|
}
|
|
183
288
|
}
|
|
184
289
|
|
|
185
|
-
static
|
|
186
|
-
const itemId = EntityEngineCyberia.getSingleDropdownValue(EntityEngineCyberia.ids.
|
|
290
|
+
static async addOverride() {
|
|
291
|
+
const itemId = EntityEngineCyberia.getSingleDropdownValue(EntityEngineCyberia.ids.overrideItemPicker);
|
|
187
292
|
if (!itemId) {
|
|
188
|
-
NotificationManager.Push({ html: 'Select an itemId
|
|
293
|
+
NotificationManager.Push({ html: 'Select an itemId to override.', status: 'error' });
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (EntityEngineCyberia.overrideItemsIdsState.some((rule) => rule.itemId === itemId)) {
|
|
297
|
+
NotificationManager.Push({ html: `"${itemId}" already has an override.`, status: 'warning' });
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
EntityEngineCyberia.overrideItemsIdsState.push({
|
|
301
|
+
itemId,
|
|
302
|
+
active: EntityEngineCyberia.listFieldOf('live').includes(itemId),
|
|
303
|
+
quantity: 1,
|
|
304
|
+
});
|
|
305
|
+
EntityEngineCyberia.setSingleDropdownValue(EntityEngineCyberia.ids.overrideItemPicker, '');
|
|
306
|
+
await EntityEngineCyberia.renderOverrideList();
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ── Instance links (CyberiaInstanceConf.entityDefaults) ───────────────────
|
|
310
|
+
static async renderInstanceList() {
|
|
311
|
+
const container = s('.entity-engine-instance-list');
|
|
312
|
+
if (!container) return;
|
|
313
|
+
let out = '';
|
|
314
|
+
for (let i = 0; i < EntityEngineCyberia.instanceCodes.length; i++) {
|
|
315
|
+
out += html`<div
|
|
316
|
+
class="fl"
|
|
317
|
+
style="border-bottom:1px solid ${subtleBorder()};padding:3px 0;align-items:center;font-size:12px;font-family:monospace;"
|
|
318
|
+
>
|
|
319
|
+
<div class="in fll" style="flex:1;">${EntityEngineCyberia.instanceCodes[i]}</div>
|
|
320
|
+
${await BtnIcon.instance({
|
|
321
|
+
class: `btn-entity-engine-rm-instance-${i}`,
|
|
322
|
+
label: html`<i class="fa-solid fa-xmark"></i>`,
|
|
323
|
+
style: 'min-width:30px;padding:2px 8px;',
|
|
324
|
+
})}
|
|
325
|
+
</div>`;
|
|
326
|
+
}
|
|
327
|
+
if (!out) {
|
|
328
|
+
out = html`<div style="color:#888;font-size:12px;">
|
|
329
|
+
No instance runs on this default — it reaches no world until one references it.
|
|
330
|
+
</div>`;
|
|
331
|
+
}
|
|
332
|
+
htmls('.entity-engine-instance-list', out);
|
|
333
|
+
for (let i = 0; i < EntityEngineCyberia.instanceCodes.length; i++) {
|
|
334
|
+
if (s(`.btn-entity-engine-rm-instance-${i}`))
|
|
335
|
+
s(`.btn-entity-engine-rm-instance-${i}`).onclick = () => {
|
|
336
|
+
EntityEngineCyberia.instanceCodes.splice(i, 1);
|
|
337
|
+
EntityEngineCyberia.renderInstanceList();
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
static addInstance() {
|
|
343
|
+
const code = EntityEngineCyberia.getSingleDropdownValue(EntityEngineCyberia.ids.instancePicker);
|
|
344
|
+
if (!code) {
|
|
345
|
+
NotificationManager.Push({ html: 'Select an instance code first.', status: 'error' });
|
|
189
346
|
return;
|
|
190
347
|
}
|
|
191
|
-
if (EntityEngineCyberia.
|
|
192
|
-
NotificationManager.Push({ html: `
|
|
348
|
+
if (EntityEngineCyberia.instanceCodes.includes(code)) {
|
|
349
|
+
NotificationManager.Push({ html: `"${code}" already linked.`, status: 'warning' });
|
|
193
350
|
return;
|
|
194
351
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
EntityEngineCyberia.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
352
|
+
EntityEngineCyberia.instanceCodes.push(code);
|
|
353
|
+
EntityEngineCyberia.setSingleDropdownValue(EntityEngineCyberia.ids.instancePicker, '');
|
|
354
|
+
EntityEngineCyberia.renderInstanceList();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Persisted after the default itself: an instance can only reference a document that exists.
|
|
358
|
+
static async persistInstanceLinks() {
|
|
359
|
+
if (!EntityEngineCyberia.currentId) return;
|
|
360
|
+
const result = await CyberiaEntityTypeDefaultService.setInstances({
|
|
361
|
+
id: EntityEngineCyberia.currentId,
|
|
362
|
+
body: { instanceCodes: EntityEngineCyberia.instanceCodes },
|
|
363
|
+
});
|
|
364
|
+
if (result.status === 'error') {
|
|
365
|
+
NotificationManager.Push({ html: result.message, status: 'error' });
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const { linked = [], unlinked = [] } = result.data || {};
|
|
369
|
+
if (linked.length || unlinked.length) {
|
|
370
|
+
NotificationManager.Push({
|
|
371
|
+
html: `Instances updated${linked.length ? ` · linked ${linked.join(', ')}` : ''}${
|
|
372
|
+
unlinked.length ? ` · unlinked ${unlinked.join(', ')}` : ''
|
|
373
|
+
}`,
|
|
374
|
+
status: 'success',
|
|
375
|
+
});
|
|
376
|
+
}
|
|
202
377
|
}
|
|
203
378
|
|
|
204
379
|
// ── Grid (CRUD list surface) ──────────────────────────────────────────────
|
|
380
|
+
static ITEM_PREVIEW_PX = 60;
|
|
381
|
+
|
|
382
|
+
// The item's first idle frame, or the placeholder when its type is unresolved or the art is
|
|
383
|
+
// missing. Both absences look the same on purpose: either way the runtime cannot draw it.
|
|
384
|
+
static itemPreview(itemId) {
|
|
385
|
+
const size = EntityEngineCyberia.ITEM_PREVIEW_PX;
|
|
386
|
+
const placeholder = (display) =>
|
|
387
|
+
html`<div style="width:${size}px;height:${size}px;display:${display};align-items:center;justify-content:center;">
|
|
388
|
+
<i class="fas fa-image" style="font-size:${Math.round(size / 2)}px;color:#999;"></i>
|
|
389
|
+
</div>`;
|
|
390
|
+
return html`<img
|
|
391
|
+
src="${getProxyPath()}api/atlas-sprite-sheet/idle-preview/${itemId}"
|
|
392
|
+
style="width:${size}px;height:${size}px;display:block;image-rendering:pixelated;"
|
|
393
|
+
alt="${itemId}"
|
|
394
|
+
onerror="this.style.display='none';this.nextElementSibling.style.display='flex';"
|
|
395
|
+
/>
|
|
396
|
+
${placeholder('none')}`;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Item ids read as a strip of sprites, each labelled with its id: a default is recognised by
|
|
400
|
+
// what it looks like long before anyone reads the ids. Arrays stay arrays on the row so the
|
|
401
|
+
// renderer can lay them out; ag-grid's text filter still sees the comma-joined form.
|
|
402
|
+
static itemIdsRenderer(field) {
|
|
403
|
+
return class {
|
|
404
|
+
eGui;
|
|
405
|
+
|
|
406
|
+
init(params) {
|
|
407
|
+
this.eGui = document.createElement('div');
|
|
408
|
+
const itemIds = params.data?.[field] || [];
|
|
409
|
+
if (itemIds.length === 0) {
|
|
410
|
+
this.eGui.innerHTML = html`<span style="color:#666;font-style:italic;">—</span>`;
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
this.eGui.style.cssText = 'display:flex;flex-wrap:wrap;align-items:center;gap:6px;padding:4px 0;';
|
|
414
|
+
this.eGui.innerHTML = itemIds
|
|
415
|
+
.map(
|
|
416
|
+
(itemId, index) =>
|
|
417
|
+
html`<span
|
|
418
|
+
style="display:inline-flex;align-items:center;gap:4px;font-size:11px;font-family:monospace;"
|
|
419
|
+
title="${itemId}"
|
|
420
|
+
>${EntityEngineCyberia.itemPreview(itemId)}${itemId}${index < itemIds.length - 1 ? ',' : ''}</span
|
|
421
|
+
>`,
|
|
422
|
+
)
|
|
423
|
+
.join('');
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
getGui() {
|
|
427
|
+
return this.eGui;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
refresh() {
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// The overrides a default carries, one rule per line. No sprite here: a rule is read as a rule —
|
|
437
|
+
// whether the id is active, how many of it, and which id it adjusts — and the id's own strip is
|
|
438
|
+
// already three columns to the left.
|
|
439
|
+
static overridesRenderer() {
|
|
440
|
+
return class {
|
|
441
|
+
eGui;
|
|
442
|
+
|
|
443
|
+
init(params) {
|
|
444
|
+
this.eGui = document.createElement('div');
|
|
445
|
+
const rules = params.data?.overrideItemsIdsState || [];
|
|
446
|
+
if (rules.length === 0) {
|
|
447
|
+
this.eGui.innerHTML = html`<span style="color:#666;font-style:italic;">—</span>`;
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
this.eGui.style.cssText = 'display:flex;flex-direction:column;gap:2px;padding:4px 0;';
|
|
451
|
+
this.eGui.innerHTML = rules
|
|
452
|
+
.map(
|
|
453
|
+
({ itemId, active, quantity, dropChance }) =>
|
|
454
|
+
html`<span
|
|
455
|
+
style="font-size:11px;font-family:monospace;white-space:nowrap;${active ? '' : 'color:#888;'}"
|
|
456
|
+
title="${itemId}"
|
|
457
|
+
>${active ? 'active' : 'inactive'} ×${Math.max(1, quantity || 1)}${Number.isFinite(dropChance) &&
|
|
458
|
+
1 !== dropChance
|
|
459
|
+
? ` drop ${dropChance}`
|
|
460
|
+
: ''} ${itemId}</span
|
|
461
|
+
>`,
|
|
462
|
+
)
|
|
463
|
+
.join('');
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
getGui() {
|
|
467
|
+
return this.eGui;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
refresh() {
|
|
471
|
+
return true;
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// What the grid's text filter reads for a column of rules, since an array of objects has no
|
|
477
|
+
// useful string form of its own.
|
|
478
|
+
static overridesFilterText = (rules = []) =>
|
|
479
|
+
rules
|
|
480
|
+
.map(
|
|
481
|
+
({ itemId, active, quantity, dropChance }) =>
|
|
482
|
+
`${itemId} ${active ? 'active' : 'inactive'} x${quantity || 1}` +
|
|
483
|
+
(Number.isFinite(dropChance) && 1 !== dropChance ? ` drop${dropChance}` : ''),
|
|
484
|
+
)
|
|
485
|
+
.join(', ');
|
|
486
|
+
|
|
205
487
|
static toRow(doc) {
|
|
206
488
|
return {
|
|
207
489
|
_id: doc._id,
|
|
208
490
|
entityType: doc.entityType || '',
|
|
209
491
|
behavior: doc.behavior || '',
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
492
|
+
instances: (EntityEngineCyberia.instanceLinks.get(String(doc._id)) || []).join(', '),
|
|
493
|
+
liveItemIds: [...(doc.liveItemIds || [])],
|
|
494
|
+
deadItemIds: [...(doc.deadItemIds || [])],
|
|
495
|
+
dropItemIds: [...(doc.dropItemIds || [])],
|
|
496
|
+
inventoryItemsIds: [...(doc.inventoryItemsIds || [])],
|
|
497
|
+
overrideItemsIdsState: [...(doc.overrideItemsIdsState || [])],
|
|
214
498
|
};
|
|
215
499
|
}
|
|
216
500
|
|
|
501
|
+
// The link lives on CyberiaInstanceConf.entityDefaults, so one conf read answers it for every
|
|
502
|
+
// row at once — there is no second place to ask, and nothing to keep in sync.
|
|
503
|
+
static async refreshInstanceLinks() {
|
|
504
|
+
const res = await CyberiaInstanceConfService.get({ limit: 500 });
|
|
505
|
+
const confs = res?.data?.data || [];
|
|
506
|
+
EntityEngineCyberia.availableInstanceCodes = confs.map((conf) => conf.instanceCode).sort();
|
|
507
|
+
EntityEngineCyberia.instanceLinks = new Map();
|
|
508
|
+
for (const conf of confs) {
|
|
509
|
+
for (const ref of conf.entityDefaults || []) {
|
|
510
|
+
const key = String(ref);
|
|
511
|
+
if (!EntityEngineCyberia.instanceLinks.has(key)) EntityEngineCyberia.instanceLinks.set(key, []);
|
|
512
|
+
EntityEngineCyberia.instanceLinks.get(key).push(conf.instanceCode);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
for (const codes of EntityEngineCyberia.instanceLinks.values()) codes.sort();
|
|
516
|
+
}
|
|
517
|
+
|
|
217
518
|
static async refreshList() {
|
|
218
|
-
const res = await
|
|
519
|
+
const [res] = await Promise.all([
|
|
520
|
+
CyberiaEntityTypeDefaultService.get({ limit: 500 }),
|
|
521
|
+
EntityEngineCyberia.refreshInstanceLinks(),
|
|
522
|
+
]);
|
|
219
523
|
EntityEngineCyberia.listCache = res?.data?.data || [];
|
|
220
524
|
const rows = EntityEngineCyberia.listCache.map((d) => EntityEngineCyberia.toRow(d));
|
|
221
525
|
if (AgGrid.grids[EntityEngineCyberia.gridId])
|
|
222
526
|
AgGrid.grids[EntityEngineCyberia.gridId].setGridOption('rowData', rows);
|
|
527
|
+
EntityEngineCyberia.renderInstanceList();
|
|
223
528
|
}
|
|
224
529
|
|
|
225
530
|
static notifyResult(result, isUpdate) {
|
|
@@ -242,10 +547,14 @@ class EntityEngineCyberia {
|
|
|
242
547
|
liveItemIds: [...EntityEngineCyberia.liveItemIds],
|
|
243
548
|
deadItemIds: [...EntityEngineCyberia.deadItemIds],
|
|
244
549
|
dropItemIds: [...EntityEngineCyberia.dropItemIds],
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
550
|
+
inventoryItemsIds: [...EntityEngineCyberia.inventoryItemsIds],
|
|
551
|
+
overrideItemsIdsState: EntityEngineCyberia.overrideItemsIdsState.map((rule) => ({
|
|
552
|
+
itemId: rule.itemId,
|
|
553
|
+
active: !!rule.active,
|
|
554
|
+
quantity: Math.max(1, rule.quantity || 1),
|
|
555
|
+
// Sent only when authored. Omitting it is what makes the id drop every time, so a rule
|
|
556
|
+
// that never touched the input reads the same as every rule written before it existed.
|
|
557
|
+
...(Number.isFinite(rule.dropChance) ? { dropChance: rule.dropChance } : {}),
|
|
249
558
|
})),
|
|
250
559
|
};
|
|
251
560
|
}
|
|
@@ -257,15 +566,17 @@ class EntityEngineCyberia {
|
|
|
257
566
|
EntityEngineCyberia.liveItemIds = [...(doc.liveItemIds || [])];
|
|
258
567
|
EntityEngineCyberia.deadItemIds = [...(doc.deadItemIds || [])];
|
|
259
568
|
EntityEngineCyberia.dropItemIds = [...(doc.dropItemIds || [])];
|
|
260
|
-
EntityEngineCyberia.
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
569
|
+
EntityEngineCyberia.inventoryItemsIds = [...(doc.inventoryItemsIds || [])];
|
|
570
|
+
EntityEngineCyberia.overrideItemsIdsState = (doc.overrideItemsIdsState || []).map((rule) => ({
|
|
571
|
+
itemId: rule.itemId,
|
|
572
|
+
active: !!rule.active,
|
|
573
|
+
quantity: Math.max(1, rule.quantity || 1),
|
|
574
|
+
...(Number.isFinite(rule.dropChance) ? { dropChance: rule.dropChance } : {}),
|
|
264
575
|
}));
|
|
265
|
-
EntityEngineCyberia.
|
|
266
|
-
EntityEngineCyberia.renderItemList(
|
|
267
|
-
EntityEngineCyberia.
|
|
268
|
-
EntityEngineCyberia.
|
|
576
|
+
EntityEngineCyberia.instanceCodes = [...(EntityEngineCyberia.instanceLinks.get(String(doc._id)) || [])];
|
|
577
|
+
for (const field of EntityEngineCyberia.ITEM_FIELDS) EntityEngineCyberia.renderItemList(field);
|
|
578
|
+
EntityEngineCyberia.renderOverrideList();
|
|
579
|
+
EntityEngineCyberia.renderInstanceList();
|
|
269
580
|
NotificationManager.Push({ html: `Entity default "${doc.entityType}" loaded`, status: 'success' });
|
|
270
581
|
}
|
|
271
582
|
|
|
@@ -274,13 +585,14 @@ class EntityEngineCyberia {
|
|
|
274
585
|
EntityEngineCyberia.liveItemIds = [];
|
|
275
586
|
EntityEngineCyberia.deadItemIds = [];
|
|
276
587
|
EntityEngineCyberia.dropItemIds = [];
|
|
277
|
-
EntityEngineCyberia.
|
|
588
|
+
EntityEngineCyberia.inventoryItemsIds = [];
|
|
589
|
+
EntityEngineCyberia.overrideItemsIdsState = [];
|
|
590
|
+
EntityEngineCyberia.instanceCodes = [];
|
|
278
591
|
EntityEngineCyberia.setSingleDropdownValue(EntityEngineCyberia.ids.entityType, '');
|
|
279
592
|
EntityEngineCyberia.setSingleDropdownValue(EntityEngineCyberia.ids.behavior, '');
|
|
280
|
-
EntityEngineCyberia.renderItemList(
|
|
281
|
-
EntityEngineCyberia.
|
|
282
|
-
EntityEngineCyberia.
|
|
283
|
-
EntityEngineCyberia.renderLayerList();
|
|
593
|
+
for (const field of EntityEngineCyberia.ITEM_FIELDS) EntityEngineCyberia.renderItemList(field);
|
|
594
|
+
EntityEngineCyberia.renderOverrideList();
|
|
595
|
+
EntityEngineCyberia.renderInstanceList();
|
|
284
596
|
}
|
|
285
597
|
|
|
286
598
|
static validate(body) {
|
|
@@ -310,6 +622,7 @@ class EntityEngineCyberia {
|
|
|
310
622
|
EntityEngineCyberia.notifyResult(result, isUpdate);
|
|
311
623
|
if (result.status === 'success') {
|
|
312
624
|
if (result.data?._id) EntityEngineCyberia.currentId = result.data._id;
|
|
625
|
+
await EntityEngineCyberia.persistInstanceLinks();
|
|
313
626
|
await EntityEngineCyberia.refreshList();
|
|
314
627
|
}
|
|
315
628
|
}
|
|
@@ -324,7 +637,10 @@ class EntityEngineCyberia {
|
|
|
324
637
|
if (!EntityEngineCyberia.validate(body)) return;
|
|
325
638
|
const result = await CyberiaEntityTypeDefaultService.put({ id: EntityEngineCyberia.currentId, body });
|
|
326
639
|
EntityEngineCyberia.notifyResult(result, true);
|
|
327
|
-
if (result.status === 'success')
|
|
640
|
+
if (result.status === 'success') {
|
|
641
|
+
await EntityEngineCyberia.persistInstanceLinks();
|
|
642
|
+
await EntityEngineCyberia.refreshList();
|
|
643
|
+
}
|
|
328
644
|
}
|
|
329
645
|
|
|
330
646
|
// Clone: create a NEW document from the current form values (POST), then load
|
|
@@ -338,6 +654,7 @@ class EntityEngineCyberia {
|
|
|
338
654
|
EntityEngineCyberia.notifyResult(result, false);
|
|
339
655
|
if (result.status === 'success') {
|
|
340
656
|
if (result.data?._id) EntityEngineCyberia.currentId = result.data._id;
|
|
657
|
+
await EntityEngineCyberia.persistInstanceLinks();
|
|
341
658
|
await EntityEngineCyberia.refreshList();
|
|
342
659
|
NotificationManager.Push({ html: 'Cloned into a new entity default.', status: 'success' });
|
|
343
660
|
}
|
|
@@ -370,12 +687,12 @@ class EntityEngineCyberia {
|
|
|
370
687
|
EntityEngineCyberia.liveItemIds = [];
|
|
371
688
|
EntityEngineCyberia.deadItemIds = [];
|
|
372
689
|
EntityEngineCyberia.dropItemIds = [];
|
|
373
|
-
EntityEngineCyberia.
|
|
690
|
+
EntityEngineCyberia.inventoryItemsIds = [];
|
|
691
|
+
EntityEngineCyberia.overrideItemsIdsState = [];
|
|
692
|
+
EntityEngineCyberia.instanceCodes = [];
|
|
374
693
|
|
|
375
694
|
const ids = EntityEngineCyberia.ids;
|
|
376
695
|
const entityTypeOptions = Object.values(ENTITY_TYPES).map((t) => dropdownOption(t));
|
|
377
|
-
const cont = 'entity-engine-container';
|
|
378
|
-
const dc = { layer: 'entity-engine-dc-layer' };
|
|
379
696
|
|
|
380
697
|
const group = (title, icon, inner) =>
|
|
381
698
|
html`<div class="in" style="border:1px solid ${groupBorder()};border-radius:8px;padding:12px;margin-bottom:14px;">
|
|
@@ -405,19 +722,19 @@ class EntityEngineCyberia {
|
|
|
405
722
|
|
|
406
723
|
setTimeout(async () => {
|
|
407
724
|
ThemeEvents['entity-engine-theme'] = () => {
|
|
408
|
-
EntityEngineCyberia.renderItemList(
|
|
409
|
-
EntityEngineCyberia.
|
|
410
|
-
EntityEngineCyberia.renderItemList('drop');
|
|
411
|
-
EntityEngineCyberia.renderLayerList();
|
|
725
|
+
for (const field of EntityEngineCyberia.ITEM_FIELDS) EntityEngineCyberia.renderItemList(field);
|
|
726
|
+
EntityEngineCyberia.renderOverrideList();
|
|
412
727
|
};
|
|
413
728
|
|
|
414
|
-
for (const field of
|
|
729
|
+
for (const field of EntityEngineCyberia.ITEM_FIELDS)
|
|
415
730
|
if (s(`.btn-entity-engine-add-${field}`))
|
|
416
731
|
s(`.btn-entity-engine-add-${field}`).onclick = () =>
|
|
417
732
|
EntityEngineCyberia.addItem(field, ids[`${field}ItemPicker`]);
|
|
418
733
|
|
|
419
|
-
if (s('.btn-entity-engine-add-
|
|
420
|
-
s('.btn-entity-engine-add-
|
|
734
|
+
if (s('.btn-entity-engine-add-override'))
|
|
735
|
+
s('.btn-entity-engine-add-override').onclick = () => EntityEngineCyberia.addOverride();
|
|
736
|
+
if (s('.btn-entity-engine-add-instance'))
|
|
737
|
+
s('.btn-entity-engine-add-instance').onclick = () => EntityEngineCyberia.addInstance();
|
|
421
738
|
if (s('.btn-entity-engine-save')) s('.btn-entity-engine-save').onclick = () => EntityEngineCyberia.save();
|
|
422
739
|
if (s('.btn-entity-engine-update')) s('.btn-entity-engine-update').onclick = () => EntityEngineCyberia.update();
|
|
423
740
|
if (s('.btn-entity-engine-clone')) s('.btn-entity-engine-clone').onclick = () => EntityEngineCyberia.clone();
|
|
@@ -426,10 +743,8 @@ class EntityEngineCyberia {
|
|
|
426
743
|
if (s('.btn-entity-engine-refresh'))
|
|
427
744
|
s('.btn-entity-engine-refresh').onclick = () => EntityEngineCyberia.refreshList();
|
|
428
745
|
|
|
429
|
-
EntityEngineCyberia.renderItemList(
|
|
430
|
-
EntityEngineCyberia.
|
|
431
|
-
EntityEngineCyberia.renderItemList('drop');
|
|
432
|
-
EntityEngineCyberia.renderLayerList();
|
|
746
|
+
for (const field of EntityEngineCyberia.ITEM_FIELDS) EntityEngineCyberia.renderItemList(field);
|
|
747
|
+
EntityEngineCyberia.renderOverrideList();
|
|
433
748
|
await EntityEngineCyberia.refreshList();
|
|
434
749
|
});
|
|
435
750
|
|
|
@@ -457,10 +772,44 @@ class EntityEngineCyberia {
|
|
|
457
772
|
columnDefs: [
|
|
458
773
|
{ field: 'entityType', headerName: 'Entity Type', minWidth: 120 },
|
|
459
774
|
{ field: 'behavior', headerName: 'Behavior', minWidth: 110 },
|
|
460
|
-
{ field: '
|
|
461
|
-
{
|
|
462
|
-
|
|
463
|
-
|
|
775
|
+
{ field: 'instances', headerName: 'Instances', minWidth: 160 },
|
|
776
|
+
{
|
|
777
|
+
field: 'liveItemIds',
|
|
778
|
+
headerName: 'Live Item Ids',
|
|
779
|
+
minWidth: 220,
|
|
780
|
+
autoHeight: true,
|
|
781
|
+
cellRenderer: EntityEngineCyberia.itemIdsRenderer('liveItemIds'),
|
|
782
|
+
},
|
|
783
|
+
{
|
|
784
|
+
field: 'deadItemIds',
|
|
785
|
+
headerName: 'Dead Item Ids',
|
|
786
|
+
minWidth: 220,
|
|
787
|
+
autoHeight: true,
|
|
788
|
+
cellRenderer: EntityEngineCyberia.itemIdsRenderer('deadItemIds'),
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
field: 'dropItemIds',
|
|
792
|
+
headerName: 'Drop Item Ids',
|
|
793
|
+
minWidth: 220,
|
|
794
|
+
autoHeight: true,
|
|
795
|
+
cellRenderer: EntityEngineCyberia.itemIdsRenderer('dropItemIds'),
|
|
796
|
+
},
|
|
797
|
+
{
|
|
798
|
+
field: 'inventoryItemsIds',
|
|
799
|
+
headerName: 'Inventory Items Id',
|
|
800
|
+
minWidth: 220,
|
|
801
|
+
autoHeight: true,
|
|
802
|
+
cellRenderer: EntityEngineCyberia.itemIdsRenderer('inventoryItemsIds'),
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
field: 'overrideItemsIdsState',
|
|
806
|
+
headerName: 'Resume Override',
|
|
807
|
+
minWidth: 200,
|
|
808
|
+
autoHeight: true,
|
|
809
|
+
cellRenderer: EntityEngineCyberia.overridesRenderer(),
|
|
810
|
+
filterValueGetter: (params) =>
|
|
811
|
+
EntityEngineCyberia.overridesFilterText(params.data?.overrideItemsIdsState),
|
|
812
|
+
},
|
|
464
813
|
],
|
|
465
814
|
rowSelection: 'single',
|
|
466
815
|
onRowClicked: (event) => {
|
|
@@ -509,67 +858,89 @@ class EntityEngineCyberia {
|
|
|
509
858
|
${group('Dead Item Ids', 'fa-solid fa-skull', await itemListEditor('dead', html`Search dead itemId`))}
|
|
510
859
|
${group('Drop Item Ids', 'fa-solid fa-gift', await itemListEditor('drop', html`Search drop itemId`))}
|
|
511
860
|
${group(
|
|
512
|
-
'
|
|
513
|
-
'fa-solid fa-
|
|
514
|
-
html
|
|
861
|
+
'Inventory Items Id',
|
|
862
|
+
'fa-solid fa-box-open',
|
|
863
|
+
html`<div class="in" style="font-size:12px;color:#888;margin-bottom:8px;">
|
|
864
|
+
<i class="fa-solid fa-circle-info"></i> The entity carries the union of all four lists. These are the extras
|
|
865
|
+
no lifecycle state activates — a coin balance, say; live, dead and drop ids are already carried and are
|
|
866
|
+
activated by context.
|
|
867
|
+
</div>
|
|
868
|
+
${await itemListEditor('inventory', html`Search inventory itemId`)}`,
|
|
869
|
+
)}
|
|
870
|
+
${group(
|
|
871
|
+
'Override Item State',
|
|
872
|
+
'fa-solid fa-sliders',
|
|
873
|
+
html`<div class="in" style="font-size:12px;color:#888;margin-bottom:8px;">
|
|
874
|
+
<i class="fa-solid fa-circle-info"></i> Adjusts what the lists above derive for one id they already carry:
|
|
875
|
+
<b>active</b> forces the spawn state — a skin the equipment rules would otherwise leave inactive — and the
|
|
876
|
+
quantity sizes a stack, which is how a drop bundle declares how many it scatters. An override never adds an
|
|
877
|
+
id.
|
|
878
|
+
</div>
|
|
515
879
|
<div class="fl" style="align-items:flex-end;">
|
|
516
|
-
<div class="in fll
|
|
517
|
-
${await EntityEngineCyberia.buildItemIdDropdown(ids.
|
|
880
|
+
<div class="in fll" style="flex:1;">
|
|
881
|
+
${await EntityEngineCyberia.buildItemIdDropdown(ids.overrideItemPicker, html`Search carried itemId`)}
|
|
518
882
|
</div>
|
|
519
|
-
<div class="in fll
|
|
520
|
-
${await
|
|
521
|
-
|
|
522
|
-
label: html
|
|
523
|
-
containerClass: 'inl',
|
|
524
|
-
type: 'number',
|
|
525
|
-
min: 0,
|
|
526
|
-
value: 0,
|
|
883
|
+
<div class="in fll" style="padding-left:6px;">
|
|
884
|
+
${await BtnIcon.instance({
|
|
885
|
+
class: 'wfa btn-entity-engine-add-override',
|
|
886
|
+
label: html`<i class="fa-solid fa-plus"></i> Override`,
|
|
527
887
|
})}
|
|
528
888
|
</div>
|
|
529
889
|
</div>
|
|
530
|
-
<div class="in" style="margin-top:8px;"
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
890
|
+
<div class="in entity-engine-override-list" style="margin-top:8px;"></div>`,
|
|
891
|
+
)}
|
|
892
|
+
${group(
|
|
893
|
+
'Instances',
|
|
894
|
+
'fa-solid fa-earth-americas',
|
|
895
|
+
html`<div class="in" style="font-size:12px;opacity:.8;margin-bottom:8px;">
|
|
896
|
+
A world runs on this default only while its conf references it, and the reference is what scopes it — an
|
|
897
|
+
instance never picks up another's wiring by sharing item ids.
|
|
534
898
|
</div>
|
|
535
|
-
<div class="
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
899
|
+
<div class="fl" style="align-items:flex-end;">
|
|
900
|
+
<div class="in fll" style="flex:1;">
|
|
901
|
+
${await EntityEngineCyberia.buildInstanceDropdown(ids.instancePicker)}
|
|
902
|
+
</div>
|
|
903
|
+
<div class="in fll" style="padding-left:6px;">
|
|
904
|
+
${await BtnIcon.instance({
|
|
905
|
+
class: 'wfa btn-entity-engine-add-instance',
|
|
906
|
+
label: html`<i class="fa-solid fa-plus"></i> Link`,
|
|
907
|
+
})}
|
|
908
|
+
</div>
|
|
540
909
|
</div>
|
|
541
|
-
<div class="in entity-engine-
|
|
910
|
+
<div class="in entity-engine-instance-list" style="margin-top:8px;"></div>`,
|
|
542
911
|
)}
|
|
543
912
|
${group(
|
|
544
913
|
'Save Entity Default',
|
|
545
914
|
'fa-solid fa-floppy-disk',
|
|
546
915
|
html`<div class="fl" style="margin-top:4px;flex-wrap:wrap;">
|
|
547
|
-
${
|
|
548
|
-
|
|
549
|
-
|
|
916
|
+
${
|
|
917
|
+
EntityEngineCyberia.canMutate
|
|
918
|
+
? html`<div class="in fll" style="flex:1 1 120px;padding:3px;">
|
|
919
|
+
${await BtnIcon.instance({
|
|
550
920
|
class: 'wfa btn-entity-engine-save',
|
|
551
921
|
label: html`<i class="fa-solid fa-floppy-disk"></i> Save`,
|
|
552
922
|
})}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
923
|
+
</div>
|
|
924
|
+
<div class="in fll" style="flex:1 1 120px;padding:3px;">
|
|
925
|
+
${await BtnIcon.instance({
|
|
556
926
|
class: 'wfa btn-entity-engine-update',
|
|
557
927
|
label: html`<i class="fa-solid fa-pen-to-square"></i> Update`,
|
|
558
928
|
})}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
929
|
+
</div>
|
|
930
|
+
<div class="in fll" style="flex:1 1 120px;padding:3px;">
|
|
931
|
+
${await BtnIcon.instance({
|
|
562
932
|
class: 'wfa btn-entity-engine-clone',
|
|
563
933
|
label: html`<i class="fa-solid fa-clone"></i> Clone`,
|
|
564
934
|
})}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
935
|
+
</div>
|
|
936
|
+
<div class="in fll" style="flex:1 1 120px;padding:3px;">
|
|
937
|
+
${await BtnIcon.instance({
|
|
568
938
|
class: 'wfa btn-entity-engine-delete',
|
|
569
939
|
label: html`<i class="fa-solid fa-trash"></i> Delete`,
|
|
570
940
|
})}
|
|
571
|
-
|
|
572
|
-
|
|
941
|
+
</div>`
|
|
942
|
+
: ''
|
|
943
|
+
}
|
|
573
944
|
<div class="in fll" style="flex:1 1 120px;padding:3px;">
|
|
574
945
|
${await BtnIcon.instance({
|
|
575
946
|
class: 'wfa btn-entity-engine-new',
|