cyberia 3.1.3 → 3.2.5
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 +0 -2
- package/.github/workflows/engine-cyberia.cd.yml +10 -8
- package/.github/workflows/engine-cyberia.ci.yml +12 -29
- package/.github/workflows/ghpkg.ci.yml +4 -4
- package/.github/workflows/npmpkg.ci.yml +28 -11
- package/.github/workflows/publish.ci.yml +21 -2
- package/.github/workflows/pwa-microservices-template-page.cd.yml +4 -5
- package/.github/workflows/pwa-microservices-template-test.ci.yml +3 -3
- package/.github/workflows/release.cd.yml +13 -8
- package/CHANGELOG.md +433 -1
- package/CLI-HELP.md +57 -7
- package/Dockerfile +4 -2
- package/README.md +347 -22
- package/bin/build.js +5 -2
- package/bin/cyberia.js +1789 -112
- package/bin/deploy.js +177 -124
- package/bin/file.js +3 -0
- package/bin/index.js +1789 -112
- package/conf.js +64 -8
- package/deployment.yaml +92 -20
- package/hardhat/hardhat.config.js +13 -13
- package/hardhat/ignition/modules/ObjectLayerToken.js +1 -1
- package/hardhat/package-lock.json +2554 -5859
- package/hardhat/package.json +13 -22
- package/hardhat/scripts/deployObjectLayerToken.js +1 -1
- package/hardhat/test/ObjectLayerToken.js +4 -2
- package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
- package/hardhat/types/ethers-contracts/common.ts +92 -0
- package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
- package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
- package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
- package/hardhat/types/ethers-contracts/index.ts +6 -0
- package/jsdoc.dd-cyberia.json +64 -55
- package/jsdoc.json +64 -55
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +5 -4
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +5 -4
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +92 -20
- package/manifests/deployment/dd-cyberia-development/proxy.yaml +54 -18
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +88 -74
- package/manifests/deployment/dd-test-development/proxy.yaml +13 -4
- package/manifests/deployment/playwright/deployment.yaml +1 -1
- package/nodemon.json +1 -1
- package/package.json +22 -16
- package/proxy.yaml +54 -18
- package/scripts/rhel-grpc-setup.sh +56 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +44 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +16 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +80 -7
- package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +93 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +29 -0
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +51 -0
- package/src/api/cyberia-entity/cyberia-entity.controller.js +74 -0
- package/src/api/cyberia-entity/cyberia-entity.model.js +24 -0
- package/src/api/cyberia-entity/cyberia-entity.router.js +27 -0
- package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +368 -0
- package/src/api/cyberia-instance/cyberia-instance.controller.js +92 -0
- package/src/api/cyberia-instance/cyberia-instance.model.js +84 -0
- package/src/api/cyberia-instance/cyberia-instance.router.js +63 -0
- package/src/api/cyberia-instance/cyberia-instance.service.js +191 -0
- package/src/api/cyberia-instance/cyberia-portal-connector.js +486 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +74 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +413 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +228 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +27 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +42 -0
- package/src/api/cyberia-map/cyberia-map.controller.js +79 -0
- package/src/api/cyberia-map/cyberia-map.model.js +30 -0
- package/src/api/cyberia-map/cyberia-map.router.js +40 -0
- package/src/api/cyberia-map/cyberia-map.service.js +74 -0
- package/src/api/file/file.ref.json +18 -0
- package/src/api/ipfs/ipfs.controller.js +4 -25
- package/src/api/ipfs/ipfs.model.js +43 -34
- package/src/api/ipfs/ipfs.router.js +8 -13
- package/src/api/ipfs/ipfs.service.js +54 -102
- package/src/api/object-layer/README.md +347 -22
- package/src/api/object-layer/object-layer.router.js +30 -0
- package/src/api/object-layer/object-layer.service.js +114 -31
- package/src/api/user/user.service.js +8 -7
- package/src/cli/cluster.js +7 -7
- package/src/cli/db.js +710 -827
- package/src/cli/deploy.js +151 -93
- package/src/cli/env.js +29 -0
- package/src/cli/fs.js +5 -2
- package/src/cli/index.js +48 -2
- package/src/cli/kubectl.js +211 -0
- package/src/cli/release.js +284 -0
- package/src/cli/repository.js +438 -75
- package/src/cli/run.js +195 -35
- package/src/cli/secrets.js +73 -0
- package/src/cli/test.js +3 -3
- package/src/client/Cryptokoyn.index.js +3 -4
- package/src/client/CyberiaPortal.index.js +3 -4
- package/src/client/Default.index.js +3 -4
- package/src/client/Itemledger.index.js +3 -4
- package/src/client/Underpost.index.js +3 -4
- package/src/client/components/core/AppStore.js +69 -0
- package/src/client/components/core/CalendarCore.js +2 -2
- package/src/client/components/core/DropDown.js +137 -17
- package/src/client/components/core/Keyboard.js +2 -2
- package/src/client/components/core/LogIn.js +2 -2
- package/src/client/components/core/LogOut.js +2 -2
- package/src/client/components/core/Modal.js +0 -1
- package/src/client/components/core/Panel.js +0 -1
- package/src/client/components/core/PanelForm.js +19 -19
- package/src/client/components/core/SocketIo.js +82 -29
- package/src/client/components/core/SocketIoHandler.js +75 -0
- package/src/client/components/core/Stream.js +143 -95
- package/src/client/components/core/Webhook.js +40 -7
- package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
- package/src/client/components/cryptokoyn/LogInCryptokoyn.js +3 -3
- package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +2 -2
- package/src/client/components/cryptokoyn/MenuCryptokoyn.js +3 -3
- package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +3 -51
- package/src/client/components/cyberia/InstanceEngineCyberia.js +700 -0
- package/src/client/components/cyberia/MapEngineCyberia.js +1359 -2
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +17 -6
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +92 -54
- package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
- package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +216 -30
- package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +3 -3
- package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +2 -2
- package/src/client/components/cyberia-portal/MenuCyberiaPortal.js +40 -7
- package/src/client/components/cyberia-portal/RoutesCyberiaPortal.js +4 -0
- package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +3 -49
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
- package/src/client/components/default/AppStoreDefault.js +5 -0
- package/src/client/components/default/LogInDefault.js +3 -3
- package/src/client/components/default/LogOutDefault.js +2 -2
- package/src/client/components/default/MenuDefault.js +5 -5
- package/src/client/components/default/SocketIoDefault.js +3 -51
- package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
- package/src/client/components/itemledger/LogInItemledger.js +3 -3
- package/src/client/components/itemledger/LogOutItemledger.js +2 -2
- package/src/client/components/itemledger/MenuItemledger.js +3 -3
- package/src/client/components/itemledger/SocketIoItemledger.js +3 -51
- package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
- package/src/client/components/underpost/LogInUnderpost.js +3 -3
- package/src/client/components/underpost/LogOutUnderpost.js +2 -2
- package/src/client/components/underpost/MenuUnderpost.js +5 -5
- package/src/client/components/underpost/SocketIoUnderpost.js +3 -51
- package/src/client/services/core/core.service.js +20 -8
- package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +105 -0
- package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
- package/src/client/services/cyberia-entity/cyberia-entity.service.js +105 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
- package/src/client/services/cyberia-instance/cyberia-instance.service.js +122 -0
- package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +105 -0
- package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
- package/src/client/services/cyberia-map/cyberia-map.service.js +126 -0
- package/src/client/services/instance/instance.management.js +2 -2
- package/src/client/services/ipfs/ipfs.service.js +3 -23
- package/src/client/services/object-layer/object-layer.management.js +3 -3
- package/src/client/services/object-layer/object-layer.service.js +21 -0
- package/src/client/services/user/user.management.js +2 -2
- package/src/client/ssr/pages/CyberiaServerMetrics.js +1 -1
- package/src/grpc/cyberia/OFF_CHAIN_ECONOMY.md +305 -0
- package/src/grpc/cyberia/README.md +326 -0
- package/src/grpc/cyberia/grpc-server.js +530 -0
- package/src/index.js +24 -1
- package/src/runtime/express/Dockerfile +4 -0
- package/src/runtime/express/Express.js +18 -1
- package/src/runtime/lampp/Dockerfile +13 -2
- package/src/runtime/lampp/Lampp.js +27 -4
- package/src/runtime/wp/Dockerfile +68 -0
- package/src/runtime/wp/Wp.js +639 -0
- package/src/server/auth.js +24 -1
- package/src/server/backup.js +37 -9
- package/src/server/client-build-docs.js +9 -2
- package/src/server/client-build.js +31 -31
- package/src/server/client-formatted.js +109 -57
- package/src/server/conf.js +24 -9
- package/src/server/cron.js +25 -23
- package/src/server/dns.js +2 -1
- package/src/server/ipfs-client.js +24 -1
- package/src/server/object-layer.js +149 -108
- package/src/server/peer.js +8 -0
- package/src/server/runtime.js +25 -1
- package/src/server/semantic-layer-generator-floor.js +359 -0
- package/src/server/semantic-layer-generator-skin.js +1294 -0
- package/src/server/semantic-layer-generator.js +116 -555
- package/src/server/start.js +2 -2
- package/src/ws/IoInterface.js +1 -10
- package/src/ws/IoServer.js +14 -33
- package/src/ws/core/channels/core.ws.chat.js +65 -20
- package/src/ws/core/channels/core.ws.mailer.js +113 -32
- package/src/ws/core/channels/core.ws.stream.js +90 -31
- package/src/ws/core/core.ws.connection.js +12 -33
- package/src/ws/core/core.ws.emit.js +10 -26
- package/src/ws/core/core.ws.server.js +25 -58
- package/src/ws/default/channels/default.ws.main.js +53 -12
- package/src/ws/default/default.ws.connection.js +26 -13
- package/src/ws/default/default.ws.server.js +30 -12
- package/src/client/components/cryptokoyn/CommonCryptokoyn.js +0 -29
- package/src/client/components/cryptokoyn/ElementsCryptokoyn.js +0 -38
- package/src/client/components/cyberia-portal/ElementsCyberiaPortal.js +0 -38
- package/src/client/components/default/ElementsDefault.js +0 -38
- package/src/client/components/itemledger/CommonItemledger.js +0 -29
- package/src/client/components/itemledger/ElementsItemledger.js +0 -38
- package/src/client/components/underpost/CommonUnderpost.js +0 -29
- package/src/client/components/underpost/ElementsUnderpost.js +0 -38
- package/src/ws/core/management/core.ws.chat.js +0 -8
- package/src/ws/core/management/core.ws.mailer.js +0 -16
- package/src/ws/core/management/core.ws.stream.js +0 -8
- package/src/ws/default/management/default.ws.main.js +0 -8
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Semantic Layer Generator for Cyberia Online.
|
|
2
|
+
* Semantic Layer Generator for Cyberia Online — Router / Dispatcher.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* This module is the public API surface. It:
|
|
5
|
+
* 1. Owns the semantic registry (registerSemantic / lookupSemantic).
|
|
6
|
+
* 2. Provides the shared utility functions used by category submodules.
|
|
7
|
+
* 3. Implements the default shape/noise-field generation pipeline
|
|
8
|
+
* (generateFrame / generateMultiFrame).
|
|
9
|
+
* 4. Delegates to descriptor-attached custom generators when present
|
|
10
|
+
* (e.g. skin uses template-based pixel painting via customMultiFrameGenerator).
|
|
11
|
+
* 5. Loads category submodules at initialisation:
|
|
12
|
+
* • semantic-layer-generator-floor.js — all floor-* descriptors
|
|
13
|
+
* • semantic-layer-generator-skin.js — skin-* descriptors
|
|
10
14
|
*
|
|
11
15
|
* @module src/server/semantic-layer-generator.js
|
|
12
16
|
* @namespace SemanticLayerGenerator
|
|
@@ -17,6 +21,9 @@ import crypto from 'crypto';
|
|
|
17
21
|
import { createRng, seedToInt, createNoise2D, generateShape, listShapes } from './shape-generator.js';
|
|
18
22
|
import { loggerFactory } from './logger.js';
|
|
19
23
|
|
|
24
|
+
import { registerFloorSemantics } from './semantic-layer-generator-floor.js';
|
|
25
|
+
import { registerSkinSemantics } from './semantic-layer-generator-skin.js';
|
|
26
|
+
|
|
20
27
|
const logger = loggerFactory(import.meta);
|
|
21
28
|
|
|
22
29
|
/* ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -25,39 +32,34 @@ const logger = loggerFactory(import.meta);
|
|
|
25
32
|
|
|
26
33
|
/**
|
|
27
34
|
* Produces a deterministic 32-bit integer hash from an arbitrary string.
|
|
28
|
-
* Used to derive per-layer and per-frame seeds.
|
|
29
35
|
* @param {string} str
|
|
30
36
|
* @returns {number}
|
|
31
37
|
* @memberof SemanticLayerGenerator
|
|
32
38
|
*/
|
|
39
|
+
function hashString(str) {
|
|
40
|
+
let h = 0;
|
|
41
|
+
for (let i = 0; i < str.length; i++) {
|
|
42
|
+
h = (Math.imul(31, h) + str.charCodeAt(i)) | 0;
|
|
43
|
+
}
|
|
44
|
+
return h;
|
|
45
|
+
}
|
|
46
|
+
|
|
33
47
|
/**
|
|
34
48
|
* Derives a deterministic UUID v4 from an arbitrary seed string.
|
|
35
|
-
* Uses SHA-256 to hash the seed, then formats 16 bytes as a valid UUID v4
|
|
36
|
-
* (version nibble = 4, variant bits = 10xx).
|
|
37
49
|
* @param {string} seed
|
|
38
|
-
* @returns {string}
|
|
50
|
+
* @returns {string}
|
|
39
51
|
* @memberof SemanticLayerGenerator
|
|
40
52
|
*/
|
|
41
53
|
function seedToUUIDv4(seed) {
|
|
42
54
|
const hash = crypto.createHash('sha256').update(seed).digest();
|
|
43
|
-
// Set version nibble (byte 6, high nibble) to 0100 (version 4)
|
|
44
55
|
hash[6] = (hash[6] & 0x0f) | 0x40;
|
|
45
|
-
// Set variant bits (byte 8, high 2 bits) to 10xx
|
|
46
56
|
hash[8] = (hash[8] & 0x3f) | 0x80;
|
|
47
57
|
const hex = hash.toString('hex');
|
|
48
58
|
return [hex.slice(0, 8), hex.slice(8, 12), hex.slice(12, 16), hex.slice(16, 20), hex.slice(20, 32)].join('-');
|
|
49
59
|
}
|
|
50
60
|
|
|
51
|
-
function hashString(str) {
|
|
52
|
-
let h = 0;
|
|
53
|
-
for (let i = 0; i < str.length; i++) {
|
|
54
|
-
h = (Math.imul(31, h) + str.charCodeAt(i)) | 0;
|
|
55
|
-
}
|
|
56
|
-
return h;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
61
|
/**
|
|
60
|
-
* Derives a per-layer seed
|
|
62
|
+
* Derives a per-layer seed.
|
|
61
63
|
* @param {string} seed
|
|
62
64
|
* @param {string} itemId
|
|
63
65
|
* @param {string} layerKey
|
|
@@ -69,7 +71,7 @@ function deriveLayerSeed(seed, itemId, layerKey) {
|
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
/**
|
|
72
|
-
* Derives a per-frame seed
|
|
74
|
+
* Derives a per-frame seed.
|
|
73
75
|
* @param {number} layerSeed
|
|
74
76
|
* @param {number} frameIndex
|
|
75
77
|
* @returns {number}
|
|
@@ -81,34 +83,34 @@ function deriveFrameSeed(layerSeed, frameIndex) {
|
|
|
81
83
|
|
|
82
84
|
/* ═══════════════════════════════════════════════════════════════════════════
|
|
83
85
|
* SEMANTIC REGISTRY
|
|
84
|
-
* Maps item-id prefixes to semantic descriptors that drive generation.
|
|
85
86
|
* ═══════════════════════════════════════════════════════════════════════════ */
|
|
86
87
|
|
|
87
88
|
/**
|
|
88
89
|
* @typedef {Object} SemanticDescriptor
|
|
89
|
-
* @property {string[]}
|
|
90
|
-
* @property {number[][]}
|
|
91
|
-
* @property {Object<string,number>}
|
|
92
|
-
* @property {Object<string,LayerSpec>} layers
|
|
93
|
-
* @property {string}
|
|
90
|
+
* @property {string[]} semanticTags Conceptual tags.
|
|
91
|
+
* @property {number[][]} paletteHints RGBA palette colours.
|
|
92
|
+
* @property {Object<string,number>} preferredShapes Shape key → weight.
|
|
93
|
+
* @property {Object<string,LayerSpec>} layers Named layer specs.
|
|
94
|
+
* @property {string} itemType floor | skin | weapon | skill | coin
|
|
95
|
+
* @property {Function} [customMultiFrameGenerator] Overrides the default multi-frame pipeline.
|
|
94
96
|
* @memberof SemanticLayerGenerator
|
|
95
97
|
*/
|
|
96
98
|
|
|
97
99
|
/**
|
|
98
100
|
* @typedef {Object} LayerSpec
|
|
99
|
-
* @property {string} generator
|
|
100
|
-
* @property {string[]} shapes
|
|
101
|
-
* @property {number} count
|
|
102
|
-
* @property {number} scaleVariance
|
|
103
|
-
* @property {number} rotationVariance
|
|
104
|
-
* @property {number} colorShift
|
|
105
|
-
* @property {number} jitter
|
|
106
|
-
* @property {number} noiseLevel
|
|
107
|
-
* @property {number} detailLevel
|
|
108
|
-
* @property {number} sparsity
|
|
109
|
-
* @property {number} [frameJitter]
|
|
110
|
-
* @property {number} [frameRotation]
|
|
111
|
-
* @property {number} [frameScale]
|
|
101
|
+
* @property {string} generator 'shape' | 'noise-field' | 'template-zone'
|
|
102
|
+
* @property {string[]} [shapes]
|
|
103
|
+
* @property {number} [count]
|
|
104
|
+
* @property {number} [scaleVariance]
|
|
105
|
+
* @property {number} [rotationVariance]
|
|
106
|
+
* @property {number} [colorShift]
|
|
107
|
+
* @property {number} [jitter]
|
|
108
|
+
* @property {number} [noiseLevel]
|
|
109
|
+
* @property {number} [detailLevel]
|
|
110
|
+
* @property {number} [sparsity]
|
|
111
|
+
* @property {number} [frameJitter]
|
|
112
|
+
* @property {number} [frameRotation]
|
|
113
|
+
* @property {number} [frameScale]
|
|
112
114
|
* @memberof SemanticLayerGenerator
|
|
113
115
|
*/
|
|
114
116
|
|
|
@@ -137,7 +139,6 @@ export function registerSemantic(prefix, descriptor) {
|
|
|
137
139
|
*/
|
|
138
140
|
export function lookupSemantic(itemId) {
|
|
139
141
|
if (semanticRegistry[itemId]) return semanticRegistry[itemId];
|
|
140
|
-
// Longest prefix match
|
|
141
142
|
let best = null;
|
|
142
143
|
let bestLen = 0;
|
|
143
144
|
for (const prefix of Object.keys(semanticRegistry)) {
|
|
@@ -149,385 +150,9 @@ export function lookupSemantic(itemId) {
|
|
|
149
150
|
return best;
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
/* ──
|
|
153
|
-
|
|
154
|
-
registerSemantic
|
|
155
|
-
semanticTags: ['sand', 'dune', 'arid', 'dry'],
|
|
156
|
-
paletteHints: [
|
|
157
|
-
[210, 180, 120, 255], // warm sand
|
|
158
|
-
[194, 160, 100, 255], // darker sand
|
|
159
|
-
[230, 205, 150, 255], // light sand
|
|
160
|
-
[180, 140, 80, 255], // ochre
|
|
161
|
-
[160, 120, 70, 255], // deep ochre
|
|
162
|
-
[120, 90, 55, 255], // rock brown
|
|
163
|
-
[100, 80, 50, 255], // shadow
|
|
164
|
-
[240, 220, 175, 255], // highlight
|
|
165
|
-
],
|
|
166
|
-
preferredShapes: { ellipse: 3, circle: 2, cactus: 1, star: 0.5 },
|
|
167
|
-
itemType: 'floor',
|
|
168
|
-
layers: {
|
|
169
|
-
base: {
|
|
170
|
-
generator: 'noise-field',
|
|
171
|
-
shapes: ['ellipse'],
|
|
172
|
-
count: 0,
|
|
173
|
-
scaleVariance: 0,
|
|
174
|
-
rotationVariance: 0,
|
|
175
|
-
colorShift: 8,
|
|
176
|
-
jitter: 0,
|
|
177
|
-
noiseLevel: 0.3,
|
|
178
|
-
detailLevel: 1,
|
|
179
|
-
sparsity: 0,
|
|
180
|
-
frameJitter: 0,
|
|
181
|
-
frameRotation: 0,
|
|
182
|
-
frameScale: 0,
|
|
183
|
-
},
|
|
184
|
-
dunes: {
|
|
185
|
-
generator: 'shape',
|
|
186
|
-
shapes: ['ellipse', 'circle'],
|
|
187
|
-
count: 5,
|
|
188
|
-
scaleVariance: 0.3,
|
|
189
|
-
rotationVariance: 15,
|
|
190
|
-
colorShift: 12,
|
|
191
|
-
jitter: 0.08,
|
|
192
|
-
noiseLevel: 0.15,
|
|
193
|
-
detailLevel: 1.2,
|
|
194
|
-
sparsity: 0.3,
|
|
195
|
-
frameJitter: 0.005,
|
|
196
|
-
frameRotation: 0.5,
|
|
197
|
-
frameScale: 0.005,
|
|
198
|
-
},
|
|
199
|
-
rocks: {
|
|
200
|
-
generator: 'shape',
|
|
201
|
-
shapes: ['star', 'circle'],
|
|
202
|
-
count: 3,
|
|
203
|
-
scaleVariance: 0.4,
|
|
204
|
-
rotationVariance: 45,
|
|
205
|
-
colorShift: 15,
|
|
206
|
-
jitter: 0.12,
|
|
207
|
-
noiseLevel: 0.2,
|
|
208
|
-
detailLevel: 1,
|
|
209
|
-
sparsity: 0.5,
|
|
210
|
-
frameJitter: 0.002,
|
|
211
|
-
frameRotation: 0.3,
|
|
212
|
-
frameScale: 0.003,
|
|
213
|
-
},
|
|
214
|
-
tufts: {
|
|
215
|
-
generator: 'shape',
|
|
216
|
-
shapes: ['cactus', 'star'],
|
|
217
|
-
count: 2,
|
|
218
|
-
scaleVariance: 0.25,
|
|
219
|
-
rotationVariance: 20,
|
|
220
|
-
colorShift: 10,
|
|
221
|
-
jitter: 0.1,
|
|
222
|
-
noiseLevel: 0.1,
|
|
223
|
-
detailLevel: 0.8,
|
|
224
|
-
sparsity: 0.6,
|
|
225
|
-
frameJitter: 0.008,
|
|
226
|
-
frameRotation: 1.0,
|
|
227
|
-
frameScale: 0.006,
|
|
228
|
-
},
|
|
229
|
-
},
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
registerSemantic('floor-grass', {
|
|
233
|
-
semanticTags: ['grass', 'meadow', 'green', 'earth'],
|
|
234
|
-
paletteHints: [
|
|
235
|
-
[80, 140, 60, 255], // base green
|
|
236
|
-
[60, 120, 45, 255], // dark green
|
|
237
|
-
[110, 170, 80, 255], // light green
|
|
238
|
-
[90, 130, 55, 255], // mid green
|
|
239
|
-
[130, 100, 65, 255], // earth
|
|
240
|
-
[70, 110, 40, 255], // shadow green
|
|
241
|
-
[150, 190, 100, 255], // highlight
|
|
242
|
-
[100, 85, 50, 255], // dark earth
|
|
243
|
-
],
|
|
244
|
-
preferredShapes: { ellipse: 3, circle: 2, heart: 0.5 },
|
|
245
|
-
itemType: 'floor',
|
|
246
|
-
layers: {
|
|
247
|
-
base: {
|
|
248
|
-
generator: 'noise-field',
|
|
249
|
-
shapes: ['circle'],
|
|
250
|
-
count: 0,
|
|
251
|
-
scaleVariance: 0,
|
|
252
|
-
rotationVariance: 0,
|
|
253
|
-
colorShift: 10,
|
|
254
|
-
jitter: 0,
|
|
255
|
-
noiseLevel: 0.25,
|
|
256
|
-
detailLevel: 1,
|
|
257
|
-
sparsity: 0,
|
|
258
|
-
frameJitter: 0,
|
|
259
|
-
frameRotation: 0,
|
|
260
|
-
frameScale: 0,
|
|
261
|
-
},
|
|
262
|
-
blades: {
|
|
263
|
-
generator: 'shape',
|
|
264
|
-
shapes: ['ellipse', 'heart'],
|
|
265
|
-
count: 6,
|
|
266
|
-
scaleVariance: 0.35,
|
|
267
|
-
rotationVariance: 40,
|
|
268
|
-
colorShift: 15,
|
|
269
|
-
jitter: 0.1,
|
|
270
|
-
noiseLevel: 0.12,
|
|
271
|
-
detailLevel: 1.0,
|
|
272
|
-
sparsity: 0.25,
|
|
273
|
-
frameJitter: 0.01,
|
|
274
|
-
frameRotation: 2.0,
|
|
275
|
-
frameScale: 0.005,
|
|
276
|
-
},
|
|
277
|
-
patches: {
|
|
278
|
-
generator: 'shape',
|
|
279
|
-
shapes: ['circle', 'ellipse'],
|
|
280
|
-
count: 3,
|
|
281
|
-
scaleVariance: 0.3,
|
|
282
|
-
rotationVariance: 30,
|
|
283
|
-
colorShift: 12,
|
|
284
|
-
jitter: 0.08,
|
|
285
|
-
noiseLevel: 0.15,
|
|
286
|
-
detailLevel: 0.9,
|
|
287
|
-
sparsity: 0.4,
|
|
288
|
-
frameJitter: 0.003,
|
|
289
|
-
frameRotation: 0.5,
|
|
290
|
-
frameScale: 0.003,
|
|
291
|
-
},
|
|
292
|
-
},
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
registerSemantic('floor-water', {
|
|
296
|
-
semanticTags: ['water', 'ocean', 'wave', 'liquid'],
|
|
297
|
-
paletteHints: [
|
|
298
|
-
[40, 100, 180, 255], // deep blue
|
|
299
|
-
[60, 130, 200, 255], // mid blue
|
|
300
|
-
[90, 160, 220, 255], // light blue
|
|
301
|
-
[120, 190, 230, 255], // highlight
|
|
302
|
-
[30, 80, 150, 255], // dark blue
|
|
303
|
-
[70, 140, 210, 255], // wave crest
|
|
304
|
-
[150, 210, 240, 255], // foam
|
|
305
|
-
[20, 60, 120, 255], // abyss
|
|
306
|
-
],
|
|
307
|
-
preferredShapes: { ellipse: 3, circle: 2 },
|
|
308
|
-
itemType: 'floor',
|
|
309
|
-
layers: {
|
|
310
|
-
base: {
|
|
311
|
-
generator: 'noise-field',
|
|
312
|
-
shapes: ['ellipse'],
|
|
313
|
-
count: 0,
|
|
314
|
-
scaleVariance: 0,
|
|
315
|
-
rotationVariance: 0,
|
|
316
|
-
colorShift: 12,
|
|
317
|
-
jitter: 0,
|
|
318
|
-
noiseLevel: 0.35,
|
|
319
|
-
detailLevel: 1,
|
|
320
|
-
sparsity: 0,
|
|
321
|
-
frameJitter: 0,
|
|
322
|
-
frameRotation: 0,
|
|
323
|
-
frameScale: 0,
|
|
324
|
-
},
|
|
325
|
-
waves: {
|
|
326
|
-
generator: 'shape',
|
|
327
|
-
shapes: ['ellipse'],
|
|
328
|
-
count: 4,
|
|
329
|
-
scaleVariance: 0.3,
|
|
330
|
-
rotationVariance: 10,
|
|
331
|
-
colorShift: 18,
|
|
332
|
-
jitter: 0.06,
|
|
333
|
-
noiseLevel: 0.2,
|
|
334
|
-
detailLevel: 1.2,
|
|
335
|
-
sparsity: 0.2,
|
|
336
|
-
frameJitter: 0.015,
|
|
337
|
-
frameRotation: 1.5,
|
|
338
|
-
frameScale: 0.008,
|
|
339
|
-
},
|
|
340
|
-
foam: {
|
|
341
|
-
generator: 'shape',
|
|
342
|
-
shapes: ['circle'],
|
|
343
|
-
count: 3,
|
|
344
|
-
scaleVariance: 0.5,
|
|
345
|
-
rotationVariance: 0,
|
|
346
|
-
colorShift: 10,
|
|
347
|
-
jitter: 0.15,
|
|
348
|
-
noiseLevel: 0.1,
|
|
349
|
-
detailLevel: 0.7,
|
|
350
|
-
sparsity: 0.5,
|
|
351
|
-
frameJitter: 0.012,
|
|
352
|
-
frameRotation: 0.5,
|
|
353
|
-
frameScale: 0.01,
|
|
354
|
-
},
|
|
355
|
-
},
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
registerSemantic('floor-stone', {
|
|
359
|
-
semanticTags: ['stone', 'rock', 'cobble', 'grey'],
|
|
360
|
-
paletteHints: [
|
|
361
|
-
[140, 140, 145, 255], // base grey
|
|
362
|
-
[120, 118, 125, 255], // dark grey
|
|
363
|
-
[165, 165, 170, 255], // light grey
|
|
364
|
-
[100, 98, 105, 255], // shadow
|
|
365
|
-
[180, 180, 185, 255], // highlight
|
|
366
|
-
[90, 85, 80, 255], // dark rock
|
|
367
|
-
[155, 150, 148, 255], // warm grey
|
|
368
|
-
[110, 108, 115, 255], // cool grey
|
|
369
|
-
],
|
|
370
|
-
preferredShapes: { circle: 3, ellipse: 2, star: 1, 'pixel-art': 0.5 },
|
|
371
|
-
itemType: 'floor',
|
|
372
|
-
layers: {
|
|
373
|
-
base: {
|
|
374
|
-
generator: 'noise-field',
|
|
375
|
-
shapes: ['circle'],
|
|
376
|
-
count: 0,
|
|
377
|
-
scaleVariance: 0,
|
|
378
|
-
rotationVariance: 0,
|
|
379
|
-
colorShift: 6,
|
|
380
|
-
jitter: 0,
|
|
381
|
-
noiseLevel: 0.2,
|
|
382
|
-
detailLevel: 1,
|
|
383
|
-
sparsity: 0,
|
|
384
|
-
frameJitter: 0,
|
|
385
|
-
frameRotation: 0,
|
|
386
|
-
frameScale: 0,
|
|
387
|
-
},
|
|
388
|
-
cobbles: {
|
|
389
|
-
generator: 'shape',
|
|
390
|
-
shapes: ['circle', 'ellipse', 'pixel-art'],
|
|
391
|
-
count: 6,
|
|
392
|
-
scaleVariance: 0.35,
|
|
393
|
-
rotationVariance: 90,
|
|
394
|
-
colorShift: 10,
|
|
395
|
-
jitter: 0.06,
|
|
396
|
-
noiseLevel: 0.15,
|
|
397
|
-
detailLevel: 1.0,
|
|
398
|
-
sparsity: 0.2,
|
|
399
|
-
frameJitter: 0.001,
|
|
400
|
-
frameRotation: 0.2,
|
|
401
|
-
frameScale: 0.001,
|
|
402
|
-
},
|
|
403
|
-
cracks: {
|
|
404
|
-
generator: 'shape',
|
|
405
|
-
shapes: ['star'],
|
|
406
|
-
count: 2,
|
|
407
|
-
scaleVariance: 0.5,
|
|
408
|
-
rotationVariance: 180,
|
|
409
|
-
colorShift: 20,
|
|
410
|
-
jitter: 0.1,
|
|
411
|
-
noiseLevel: 0.25,
|
|
412
|
-
detailLevel: 0.8,
|
|
413
|
-
sparsity: 0.6,
|
|
414
|
-
frameJitter: 0.0,
|
|
415
|
-
frameRotation: 0.0,
|
|
416
|
-
frameScale: 0.0,
|
|
417
|
-
},
|
|
418
|
-
},
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
registerSemantic('floor-lava', {
|
|
422
|
-
semanticTags: ['lava', 'magma', 'fire', 'hot'],
|
|
423
|
-
paletteHints: [
|
|
424
|
-
[200, 50, 20, 255], // hot red
|
|
425
|
-
[230, 100, 30, 255], // orange
|
|
426
|
-
[255, 180, 50, 255], // bright yellow
|
|
427
|
-
[160, 30, 10, 255], // dark red
|
|
428
|
-
[80, 20, 10, 255], // cooled rock
|
|
429
|
-
[50, 15, 8, 255], // dark crust
|
|
430
|
-
[240, 140, 40, 255], // glow
|
|
431
|
-
[120, 25, 12, 255], // mid lava
|
|
432
|
-
],
|
|
433
|
-
preferredShapes: { circle: 3, ellipse: 2, cactus: 1 },
|
|
434
|
-
itemType: 'floor',
|
|
435
|
-
layers: {
|
|
436
|
-
base: {
|
|
437
|
-
generator: 'noise-field',
|
|
438
|
-
shapes: ['circle'],
|
|
439
|
-
count: 0,
|
|
440
|
-
scaleVariance: 0,
|
|
441
|
-
rotationVariance: 0,
|
|
442
|
-
colorShift: 15,
|
|
443
|
-
jitter: 0,
|
|
444
|
-
noiseLevel: 0.4,
|
|
445
|
-
detailLevel: 1,
|
|
446
|
-
sparsity: 0,
|
|
447
|
-
frameJitter: 0,
|
|
448
|
-
frameRotation: 0,
|
|
449
|
-
frameScale: 0,
|
|
450
|
-
},
|
|
451
|
-
flow: {
|
|
452
|
-
generator: 'shape',
|
|
453
|
-
shapes: ['ellipse', 'circle'],
|
|
454
|
-
count: 5,
|
|
455
|
-
scaleVariance: 0.35,
|
|
456
|
-
rotationVariance: 25,
|
|
457
|
-
colorShift: 20,
|
|
458
|
-
jitter: 0.1,
|
|
459
|
-
noiseLevel: 0.2,
|
|
460
|
-
detailLevel: 1.1,
|
|
461
|
-
sparsity: 0.2,
|
|
462
|
-
frameJitter: 0.02,
|
|
463
|
-
frameRotation: 2.0,
|
|
464
|
-
frameScale: 0.01,
|
|
465
|
-
},
|
|
466
|
-
crust: {
|
|
467
|
-
generator: 'shape',
|
|
468
|
-
shapes: ['star', 'cactus'],
|
|
469
|
-
count: 3,
|
|
470
|
-
scaleVariance: 0.4,
|
|
471
|
-
rotationVariance: 60,
|
|
472
|
-
colorShift: 10,
|
|
473
|
-
jitter: 0.08,
|
|
474
|
-
noiseLevel: 0.15,
|
|
475
|
-
detailLevel: 0.9,
|
|
476
|
-
sparsity: 0.4,
|
|
477
|
-
frameJitter: 0.005,
|
|
478
|
-
frameRotation: 0.5,
|
|
479
|
-
frameScale: 0.004,
|
|
480
|
-
},
|
|
481
|
-
},
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
registerSemantic('skin-', {
|
|
485
|
-
semanticTags: ['character', 'body', 'humanoid'],
|
|
486
|
-
paletteHints: [
|
|
487
|
-
[180, 140, 110, 255],
|
|
488
|
-
[160, 120, 90, 255],
|
|
489
|
-
[200, 160, 130, 255],
|
|
490
|
-
[140, 100, 70, 255],
|
|
491
|
-
[100, 70, 50, 255],
|
|
492
|
-
[60, 60, 80, 255],
|
|
493
|
-
[80, 80, 100, 255],
|
|
494
|
-
[220, 180, 150, 255],
|
|
495
|
-
],
|
|
496
|
-
preferredShapes: { 'skull-bone': 2, circle: 2, ellipse: 1 },
|
|
497
|
-
itemType: 'skin',
|
|
498
|
-
layers: {
|
|
499
|
-
body: {
|
|
500
|
-
generator: 'shape',
|
|
501
|
-
shapes: ['ellipse', 'circle'],
|
|
502
|
-
count: 4,
|
|
503
|
-
scaleVariance: 0.3,
|
|
504
|
-
rotationVariance: 10,
|
|
505
|
-
colorShift: 12,
|
|
506
|
-
jitter: 0.05,
|
|
507
|
-
noiseLevel: 0.1,
|
|
508
|
-
detailLevel: 1.2,
|
|
509
|
-
sparsity: 0.2,
|
|
510
|
-
frameJitter: 0.003,
|
|
511
|
-
frameRotation: 0.5,
|
|
512
|
-
frameScale: 0.003,
|
|
513
|
-
},
|
|
514
|
-
detail: {
|
|
515
|
-
generator: 'shape',
|
|
516
|
-
shapes: ['skull-bone', 'star'],
|
|
517
|
-
count: 2,
|
|
518
|
-
scaleVariance: 0.2,
|
|
519
|
-
rotationVariance: 5,
|
|
520
|
-
colorShift: 8,
|
|
521
|
-
jitter: 0.03,
|
|
522
|
-
noiseLevel: 0.08,
|
|
523
|
-
detailLevel: 1.5,
|
|
524
|
-
sparsity: 0.5,
|
|
525
|
-
frameJitter: 0.002,
|
|
526
|
-
frameRotation: 0.3,
|
|
527
|
-
frameScale: 0.002,
|
|
528
|
-
},
|
|
529
|
-
},
|
|
530
|
-
});
|
|
153
|
+
/* ── Load category submodules at startup ────────────────────────────────── */
|
|
154
|
+
registerFloorSemantics(registerSemantic);
|
|
155
|
+
registerSkinSemantics(registerSemantic);
|
|
531
156
|
|
|
532
157
|
/* ═══════════════════════════════════════════════════════════════════════════
|
|
533
158
|
* COLOR UTILITIES
|
|
@@ -537,7 +162,7 @@ registerSemantic('skin-', {
|
|
|
537
162
|
* Picks a palette color deterministically from the hint palette.
|
|
538
163
|
* @param {number[][]} palette
|
|
539
164
|
* @param {function():number} rng
|
|
540
|
-
* @param {number} colorShift
|
|
165
|
+
* @param {number} colorShift
|
|
541
166
|
* @returns {number[]} RGBA array.
|
|
542
167
|
* @memberof SemanticLayerGenerator
|
|
543
168
|
*/
|
|
@@ -569,7 +194,6 @@ function clamp(v, min, max) {
|
|
|
569
194
|
|
|
570
195
|
/**
|
|
571
196
|
* Picks a shape key from candidate list, weighted by the descriptor's preferredShapes.
|
|
572
|
-
* Falls back to uniform if no weights match.
|
|
573
197
|
* @param {string[]} candidates
|
|
574
198
|
* @param {Object<string,number>} weights
|
|
575
199
|
* @param {function():number} rng
|
|
@@ -577,13 +201,9 @@ function clamp(v, min, max) {
|
|
|
577
201
|
* @memberof SemanticLayerGenerator
|
|
578
202
|
*/
|
|
579
203
|
function pickShape(candidates, weights, rng) {
|
|
580
|
-
// Filter to only shapes that actually exist in the shape registry
|
|
581
204
|
const available = listShapes();
|
|
582
205
|
const valid = candidates.filter((c) => available.includes(c));
|
|
583
|
-
if (valid.length === 0)
|
|
584
|
-
// fallback to any available shape
|
|
585
|
-
return available[Math.floor(rng() * available.length)];
|
|
586
|
-
}
|
|
206
|
+
if (valid.length === 0) return available[Math.floor(rng() * available.length)];
|
|
587
207
|
const w = valid.map((k) => weights[k] ?? 1);
|
|
588
208
|
const total = w.reduce((s, v) => s + v, 0);
|
|
589
209
|
let r = rng() * total;
|
|
@@ -596,46 +216,33 @@ function pickShape(candidates, weights, rng) {
|
|
|
596
216
|
|
|
597
217
|
/* ═══════════════════════════════════════════════════════════════════════════
|
|
598
218
|
* FRAME MATRIX GENERATION
|
|
599
|
-
* Converts generated shape layers into a 24×24 frame_matrix compatible
|
|
600
|
-
* with the ObjectLayerEngine frame format.
|
|
601
219
|
* ═══════════════════════════════════════════════════════════════════════════ */
|
|
602
220
|
|
|
603
|
-
const GRID_DIM = 24;
|
|
221
|
+
const GRID_DIM = 24;
|
|
604
222
|
|
|
605
223
|
/**
|
|
606
224
|
* Generates a noise-field base layer as a 24×24 frame matrix.
|
|
607
|
-
* Uses low-frequency 2D noise seeded per-frame for temporal coherence.
|
|
608
|
-
* @param {number[][]} palette
|
|
609
|
-
* @param {number} layerSeedInt
|
|
610
|
-
* @param {number} frameIndex
|
|
611
|
-
* @param {LayerSpec} spec
|
|
612
|
-
* @param {number} density
|
|
613
|
-
* @returns {{frameMatrix: number[][], colors: number[][]}}
|
|
614
225
|
* @memberof SemanticLayerGenerator
|
|
615
226
|
*/
|
|
616
227
|
function generateNoiseFieldLayer(palette, layerSeedInt, frameIndex, spec, density) {
|
|
617
228
|
const frameSeedInt = deriveFrameSeed(layerSeedInt, frameIndex);
|
|
618
229
|
const rng = createRng(frameSeedInt);
|
|
619
|
-
const noise = createNoise2D(createRng(layerSeedInt));
|
|
230
|
+
const noise = createNoise2D(createRng(layerSeedInt));
|
|
620
231
|
|
|
621
232
|
const colors = [];
|
|
622
233
|
const frameMatrix = [];
|
|
623
234
|
|
|
624
|
-
// Pre-pick a small set of colors for the base
|
|
625
235
|
const baseColors = [];
|
|
626
|
-
const colorRng = createRng(layerSeedInt + 7);
|
|
236
|
+
const colorRng = createRng(layerSeedInt + 7);
|
|
627
237
|
for (let i = 0; i < Math.min(palette.length, 4); i++) {
|
|
628
238
|
baseColors.push(pickColor(palette, colorRng, spec.colorShift));
|
|
629
239
|
}
|
|
630
|
-
|
|
631
|
-
// Register base colors
|
|
632
240
|
const colorIndices = baseColors.map((c) => {
|
|
633
241
|
colors.push(c);
|
|
634
242
|
return colors.length - 1;
|
|
635
243
|
});
|
|
636
244
|
|
|
637
245
|
const noiseFreq = 0.15 + spec.noiseLevel * 0.3;
|
|
638
|
-
// Frame-level noise offset for smooth temporal variation
|
|
639
246
|
const frameOffsetX = frameIndex * 0.05;
|
|
640
247
|
const frameOffsetY = frameIndex * 0.03;
|
|
641
248
|
|
|
@@ -643,8 +250,7 @@ function generateNoiseFieldLayer(palette, layerSeedInt, frameIndex, spec, densit
|
|
|
643
250
|
const row = [];
|
|
644
251
|
for (let x = 0; x < GRID_DIM; x++) {
|
|
645
252
|
const n = noise((x + frameOffsetX) * noiseFreq, (y + frameOffsetY) * noiseFreq);
|
|
646
|
-
|
|
647
|
-
const normalized = (n + 1) / 2; // 0..1
|
|
253
|
+
const normalized = (n + 1) / 2;
|
|
648
254
|
const idx = Math.min(colorIndices.length - 1, Math.floor(normalized * colorIndices.length));
|
|
649
255
|
row.push(colorIndices[idx]);
|
|
650
256
|
}
|
|
@@ -656,18 +262,9 @@ function generateNoiseFieldLayer(palette, layerSeedInt, frameIndex, spec, densit
|
|
|
656
262
|
|
|
657
263
|
/**
|
|
658
264
|
* Stamps a parametric shape onto a frame matrix at a given position/scale.
|
|
659
|
-
* @param {number[][]} frameMatrix - Mutable 24×24 matrix.
|
|
660
|
-
* @param {number[][]} colors - Mutable color palette.
|
|
661
|
-
* @param {string} shapeKey
|
|
662
|
-
* @param {Object} transform - { x, y, scale, rotation }
|
|
663
|
-
* @param {number[]} color - RGBA color for this stamp.
|
|
664
|
-
* @param {number} noiseLevel
|
|
665
|
-
* @param {number} detailLevel
|
|
666
|
-
* @param {string} seed - String seed for shape generation.
|
|
667
265
|
* @memberof SemanticLayerGenerator
|
|
668
266
|
*/
|
|
669
267
|
function stampShape(frameMatrix, colors, shapeKey, transform, color, noiseLevel, detailLevel, seed) {
|
|
670
|
-
// Generate shape using intCoords for pixel-level placement
|
|
671
268
|
const gridSize = Math.max(4, Math.round(GRID_DIM * transform.scale));
|
|
672
269
|
const result = generateShape(shapeKey, {
|
|
673
270
|
intCoords: [gridSize, gridSize],
|
|
@@ -677,7 +274,6 @@ function stampShape(frameMatrix, colors, shapeKey, transform, color, noiseLevel,
|
|
|
677
274
|
count: Math.round(80 * detailLevel),
|
|
678
275
|
});
|
|
679
276
|
|
|
680
|
-
// Register color
|
|
681
277
|
const existingIdx = colors.findIndex(
|
|
682
278
|
(c) => c[0] === color[0] && c[1] === color[1] && c[2] === color[2] && c[3] === color[3],
|
|
683
279
|
);
|
|
@@ -689,7 +285,6 @@ function stampShape(frameMatrix, colors, shapeKey, transform, color, noiseLevel,
|
|
|
689
285
|
colorIdx = colors.length - 1;
|
|
690
286
|
}
|
|
691
287
|
|
|
692
|
-
// Compute offset to center the shape at (transform.x, transform.y)
|
|
693
288
|
const offsetX = Math.round(transform.x * GRID_DIM - gridSize / 2);
|
|
694
289
|
const offsetY = Math.round(transform.y * GRID_DIM - gridSize / 2);
|
|
695
290
|
|
|
@@ -708,40 +303,40 @@ function stampShape(frameMatrix, colors, shapeKey, transform, color, noiseLevel,
|
|
|
708
303
|
|
|
709
304
|
/**
|
|
710
305
|
* @typedef {Object} GenerateLayerOptions
|
|
711
|
-
* @property {string}
|
|
712
|
-
* @property {string}
|
|
713
|
-
* @property {number}
|
|
714
|
-
* @property {number}
|
|
715
|
-
* @property {number}
|
|
306
|
+
* @property {string} itemId
|
|
307
|
+
* @property {string} seed
|
|
308
|
+
* @property {number} [frameIndex=0]
|
|
309
|
+
* @property {number} [count=3]
|
|
310
|
+
* @property {number} [density=0.5]
|
|
716
311
|
* @memberof SemanticLayerGenerator
|
|
717
312
|
*/
|
|
718
313
|
|
|
719
314
|
/**
|
|
720
315
|
* @typedef {Object} GeneratedLayer
|
|
721
|
-
* @property {string}
|
|
722
|
-
* @property {string}
|
|
723
|
-
* @property {Object[]}
|
|
724
|
-
* @property {number[][]} frameMatrix
|
|
725
|
-
* @property {number[][]} colors
|
|
316
|
+
* @property {string} layerId
|
|
317
|
+
* @property {string} layerKey
|
|
318
|
+
* @property {Object[]} keys
|
|
319
|
+
* @property {number[][]} frameMatrix
|
|
320
|
+
* @property {number[][]} colors
|
|
726
321
|
* @memberof SemanticLayerGenerator
|
|
727
322
|
*/
|
|
728
323
|
|
|
729
324
|
/**
|
|
730
325
|
* @typedef {Object} GenerationResult
|
|
731
|
-
* @property {string}
|
|
732
|
-
* @property {string}
|
|
733
|
-
* @property {number}
|
|
326
|
+
* @property {string} itemId
|
|
327
|
+
* @property {string} seed
|
|
328
|
+
* @property {number} frameIndex
|
|
734
329
|
* @property {GeneratedLayer[]} layers
|
|
735
|
-
* @property {number[][]}
|
|
736
|
-
* @property {number[][]}
|
|
330
|
+
* @property {number[][]} compositeFrameMatrix
|
|
331
|
+
* @property {number[][]} compositeColors
|
|
737
332
|
* @memberof SemanticLayerGenerator
|
|
738
333
|
*/
|
|
739
334
|
|
|
740
335
|
/**
|
|
741
336
|
* Generates all semantic layers for a single frame of an item.
|
|
742
337
|
*
|
|
743
|
-
*
|
|
744
|
-
*
|
|
338
|
+
* If the descriptor provides `customMultiFrameGenerator`, a stub result is
|
|
339
|
+
* returned — use `generateMultiFrame` for those descriptors.
|
|
745
340
|
*
|
|
746
341
|
* @param {GenerateLayerOptions} options
|
|
747
342
|
* @returns {GenerationResult}
|
|
@@ -758,13 +353,23 @@ export function generateFrame(options) {
|
|
|
758
353
|
);
|
|
759
354
|
}
|
|
760
355
|
|
|
356
|
+
// Descriptors with a custom multi-frame generator: return a transparent stub.
|
|
357
|
+
if (typeof descriptor.customMultiFrameGenerator === 'function') {
|
|
358
|
+
return {
|
|
359
|
+
itemId,
|
|
360
|
+
seed,
|
|
361
|
+
frameIndex,
|
|
362
|
+
layers: [],
|
|
363
|
+
compositeFrameMatrix: Array.from({ length: GRID_DIM }, () => Array(GRID_DIM).fill(0)),
|
|
364
|
+
compositeColors: [[0, 0, 0, 0]],
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
761
368
|
const { paletteHints, preferredShapes, layers: layerSpecs } = descriptor;
|
|
762
369
|
const layerKeys = Object.keys(layerSpecs);
|
|
763
370
|
|
|
764
|
-
|
|
765
|
-
const compositeColors = [[0, 0, 0, 0]]; // index 0 = transparent
|
|
371
|
+
const compositeColors = [[0, 0, 0, 0]];
|
|
766
372
|
const compositeMatrix = Array.from({ length: GRID_DIM }, () => Array(GRID_DIM).fill(0));
|
|
767
|
-
|
|
768
373
|
const generatedLayers = [];
|
|
769
374
|
|
|
770
375
|
for (const layerKey of layerKeys) {
|
|
@@ -773,7 +378,6 @@ export function generateFrame(options) {
|
|
|
773
378
|
const layerId = `${itemId}-${layerKey}`;
|
|
774
379
|
|
|
775
380
|
if (spec.generator === 'noise-field') {
|
|
776
|
-
// ── Noise-field base layer ──
|
|
777
381
|
const { frameMatrix, colors: layerColors } = generateNoiseFieldLayer(
|
|
778
382
|
paletteHints,
|
|
779
383
|
layerSeedInt,
|
|
@@ -782,7 +386,6 @@ export function generateFrame(options) {
|
|
|
782
386
|
density,
|
|
783
387
|
);
|
|
784
388
|
|
|
785
|
-
// Merge colors into composite palette
|
|
786
389
|
const colorMap = {};
|
|
787
390
|
for (let ci = 0; ci < layerColors.length; ci++) {
|
|
788
391
|
const c = layerColors[ci];
|
|
@@ -796,13 +399,10 @@ export function generateFrame(options) {
|
|
|
796
399
|
colorMap[ci] = found;
|
|
797
400
|
}
|
|
798
401
|
|
|
799
|
-
// Stamp onto composite
|
|
800
402
|
for (let y = 0; y < GRID_DIM; y++) {
|
|
801
403
|
for (let x = 0; x < GRID_DIM; x++) {
|
|
802
404
|
const mapped = colorMap[frameMatrix[y][x]];
|
|
803
|
-
if (mapped !== undefined && mapped !== 0)
|
|
804
|
-
compositeMatrix[y][x] = mapped;
|
|
805
|
-
}
|
|
405
|
+
if (mapped !== undefined && mapped !== 0) compositeMatrix[y][x] = mapped;
|
|
806
406
|
}
|
|
807
407
|
}
|
|
808
408
|
|
|
@@ -814,7 +414,6 @@ export function generateFrame(options) {
|
|
|
814
414
|
colors: layerColors,
|
|
815
415
|
});
|
|
816
416
|
} else if (spec.generator === 'shape') {
|
|
817
|
-
// ── Shape element layer ──
|
|
818
417
|
const layerRng = createRng(layerSeedInt);
|
|
819
418
|
const frameSeedInt = deriveFrameSeed(layerSeedInt, frameIndex);
|
|
820
419
|
const frameRng = createRng(frameSeedInt);
|
|
@@ -822,31 +421,24 @@ export function generateFrame(options) {
|
|
|
822
421
|
|
|
823
422
|
const effectiveCount = Math.max(1, Math.round(spec.count * count * density));
|
|
824
423
|
const layerEntries = [];
|
|
825
|
-
|
|
826
|
-
// Frame matrix for this layer alone (for metadata)
|
|
827
424
|
const layerMatrix = Array.from({ length: GRID_DIM }, () => Array(GRID_DIM).fill(-1));
|
|
828
425
|
const layerColors = [];
|
|
829
426
|
|
|
830
427
|
for (let ei = 0; ei < effectiveCount; ei++) {
|
|
831
|
-
// Sparsity check — deterministic per element
|
|
832
428
|
if (layerRng() < spec.sparsity) continue;
|
|
833
429
|
|
|
834
|
-
// ── Shape selection (stable across frames) ──
|
|
835
430
|
const shapeKey = pickShape(spec.shapes, preferredShapes, createRng(layerSeedInt + ei * 97));
|
|
836
431
|
|
|
837
|
-
// ── Base transform (stable across frames — from layerSeed) ──
|
|
838
432
|
const baseRng = createRng(layerSeedInt + ei * 31 + 17);
|
|
839
433
|
const baseX = baseRng();
|
|
840
434
|
const baseY = baseRng();
|
|
841
435
|
const baseScale = 0.15 + baseRng() * 0.25 + (baseRng() * 2 - 1) * spec.scaleVariance * 0.15;
|
|
842
436
|
const baseRotation = (baseRng() * 2 - 1) * spec.rotationVariance;
|
|
843
437
|
|
|
844
|
-
// ── Per-frame smooth perturbation (temporal coherence) ──
|
|
845
438
|
const fj = spec.frameJitter || 0;
|
|
846
439
|
const fr = spec.frameRotation || 0;
|
|
847
440
|
const fs = spec.frameScale || 0;
|
|
848
441
|
|
|
849
|
-
// Low-frequency noise indexed by element position for smooth frame-to-frame change
|
|
850
442
|
const noiseX = frameNoise(ei * 2.5, frameIndex * 0.1) * fj;
|
|
851
443
|
const noiseY = frameNoise(ei * 2.5 + 100, frameIndex * 0.1) * fj;
|
|
852
444
|
const noiseRot = frameNoise(ei * 2.5 + 200, frameIndex * 0.1) * fr;
|
|
@@ -859,48 +451,23 @@ export function generateFrame(options) {
|
|
|
859
451
|
rotation: baseRotation + noiseRot,
|
|
860
452
|
};
|
|
861
453
|
|
|
862
|
-
// ── Color (stable per element, small per-frame shift) ──
|
|
863
454
|
const colorRng = createRng(layerSeedInt + ei * 53 + 7);
|
|
864
455
|
const baseColor = pickColor(paletteHints, colorRng, spec.colorShift);
|
|
865
|
-
|
|
866
|
-
// Small per-frame color shift for shimmer
|
|
867
456
|
const frameColorShift = Math.round(frameNoise(ei * 3.7 + 400, frameIndex * 0.08) * 3);
|
|
868
457
|
const color = baseColor.map((ch, ci) => (ci < 3 ? clamp(ch + frameColorShift, 0, 255) : ch));
|
|
869
458
|
|
|
870
|
-
// ── Shape seed (stable topology) ──
|
|
871
459
|
const shapeSeed = `${seed}:${itemId}:${layerKey}:${ei}`;
|
|
872
460
|
|
|
873
|
-
// Stamp onto composite matrix
|
|
874
461
|
stampShape(
|
|
875
|
-
compositeMatrix,
|
|
876
|
-
|
|
877
|
-
shapeKey,
|
|
878
|
-
transform,
|
|
879
|
-
color,
|
|
880
|
-
spec.noiseLevel * (spec.jitter + 0.5),
|
|
881
|
-
spec.detailLevel,
|
|
882
|
-
shapeSeed,
|
|
462
|
+
compositeMatrix, compositeColors, shapeKey, transform, color,
|
|
463
|
+
spec.noiseLevel * (spec.jitter + 0.5), spec.detailLevel, shapeSeed,
|
|
883
464
|
);
|
|
884
|
-
|
|
885
|
-
// Also stamp onto layer-local matrix for metadata
|
|
886
465
|
stampShape(
|
|
887
|
-
layerMatrix,
|
|
888
|
-
|
|
889
|
-
shapeKey,
|
|
890
|
-
transform,
|
|
891
|
-
color,
|
|
892
|
-
spec.noiseLevel * 0.5,
|
|
893
|
-
spec.detailLevel,
|
|
894
|
-
shapeSeed,
|
|
466
|
+
layerMatrix, layerColors, shapeKey, transform, color,
|
|
467
|
+
spec.noiseLevel * 0.5, spec.detailLevel, shapeSeed,
|
|
895
468
|
);
|
|
896
469
|
|
|
897
|
-
layerEntries.push({
|
|
898
|
-
type: 'shape',
|
|
899
|
-
shapeKey,
|
|
900
|
-
transform,
|
|
901
|
-
color,
|
|
902
|
-
shapeSeed,
|
|
903
|
-
});
|
|
470
|
+
layerEntries.push({ type: 'shape', shapeKey, transform, color, shapeSeed });
|
|
904
471
|
}
|
|
905
472
|
|
|
906
473
|
generatedLayers.push({
|
|
@@ -911,6 +478,7 @@ export function generateFrame(options) {
|
|
|
911
478
|
colors: layerColors,
|
|
912
479
|
});
|
|
913
480
|
}
|
|
481
|
+
// 'template-zone' handled by customMultiFrameGenerator — skip here
|
|
914
482
|
}
|
|
915
483
|
|
|
916
484
|
return {
|
|
@@ -925,32 +493,34 @@ export function generateFrame(options) {
|
|
|
925
493
|
|
|
926
494
|
/* ═══════════════════════════════════════════════════════════════════════════
|
|
927
495
|
* MULTI-FRAME GENERATION
|
|
928
|
-
* Generates multiple consecutive frames with temporal coherence.
|
|
929
496
|
* ═══════════════════════════════════════════════════════════════════════════ */
|
|
930
497
|
|
|
931
498
|
/**
|
|
932
499
|
* @typedef {Object} MultiFrameResult
|
|
933
|
-
* @property {string}
|
|
934
|
-
* @property {string}
|
|
935
|
-
* @property {number}
|
|
500
|
+
* @property {string} itemId
|
|
501
|
+
* @property {string} seed
|
|
502
|
+
* @property {number} frameCount
|
|
936
503
|
* @property {GenerationResult[]} frames
|
|
937
|
-
* @property {Object}
|
|
938
|
-
* @property {Object}
|
|
504
|
+
* @property {Object} objectLayerRenderFramesData
|
|
505
|
+
* @property {Object} objectLayerData
|
|
939
506
|
* @memberof SemanticLayerGenerator
|
|
940
507
|
*/
|
|
941
508
|
|
|
942
509
|
/**
|
|
943
|
-
* Generates N consecutive frames for an item-id
|
|
944
|
-
*
|
|
510
|
+
* Generates N consecutive frames for an item-id.
|
|
511
|
+
*
|
|
512
|
+
* If the matched descriptor provides `customMultiFrameGenerator`, it is invoked
|
|
513
|
+
* directly and its result is returned unchanged — allowing category submodules
|
|
514
|
+
* (e.g. skin) to fully control frame layout and direction assignment.
|
|
945
515
|
*
|
|
946
|
-
* @param {Object}
|
|
516
|
+
* @param {Object} options
|
|
947
517
|
* @param {string} options.itemId
|
|
948
518
|
* @param {string} options.seed
|
|
949
|
-
* @param {number} [options.frameCount=1]
|
|
950
|
-
* @param {number} [options.startFrame=0]
|
|
951
|
-
* @param {number} [options.count=3]
|
|
952
|
-
* @param {number} [options.density=0.5]
|
|
953
|
-
* @param {number} [options.frameDuration=250]
|
|
519
|
+
* @param {number} [options.frameCount=1]
|
|
520
|
+
* @param {number} [options.startFrame=0]
|
|
521
|
+
* @param {number} [options.count=3]
|
|
522
|
+
* @param {number} [options.density=0.5]
|
|
523
|
+
* @param {number} [options.frameDuration=250]
|
|
954
524
|
* @returns {MultiFrameResult}
|
|
955
525
|
* @memberof SemanticLayerGenerator
|
|
956
526
|
*/
|
|
@@ -965,22 +535,17 @@ export function generateMultiFrame(options) {
|
|
|
965
535
|
);
|
|
966
536
|
}
|
|
967
537
|
|
|
968
|
-
|
|
969
|
-
|
|
538
|
+
// ── Dispatch to custom generator ─────────────────────────────────────────
|
|
539
|
+
if (typeof descriptor.customMultiFrameGenerator === 'function') {
|
|
540
|
+
return descriptor.customMultiFrameGenerator(options, descriptor);
|
|
541
|
+
}
|
|
970
542
|
|
|
971
|
-
//
|
|
543
|
+
// ── Default shape / noise-field pipeline ─────────────────────────────────
|
|
544
|
+
const frames = [];
|
|
972
545
|
for (let fi = 0; fi < frameCount; fi++) {
|
|
973
|
-
|
|
974
|
-
itemId,
|
|
975
|
-
seed,
|
|
976
|
-
frameIndex: startFrame + fi,
|
|
977
|
-
count,
|
|
978
|
-
density,
|
|
979
|
-
});
|
|
980
|
-
frames.push(result);
|
|
546
|
+
frames.push(generateFrame({ itemId, seed, frameIndex: startFrame + fi, count, density }));
|
|
981
547
|
}
|
|
982
548
|
|
|
983
|
-
// Second pass: unify color palettes across all frames
|
|
984
549
|
const globalColors = [[0, 0, 0, 0]];
|
|
985
550
|
const frameMappings = [];
|
|
986
551
|
|
|
@@ -988,7 +553,9 @@ export function generateMultiFrame(options) {
|
|
|
988
553
|
const mapping = {};
|
|
989
554
|
for (let ci = 0; ci < frame.compositeColors.length; ci++) {
|
|
990
555
|
const c = frame.compositeColors[ci];
|
|
991
|
-
let found = globalColors.findIndex(
|
|
556
|
+
let found = globalColors.findIndex(
|
|
557
|
+
(gc) => gc[0] === c[0] && gc[1] === c[1] && gc[2] === c[2] && gc[3] === c[3],
|
|
558
|
+
);
|
|
992
559
|
if (found < 0) {
|
|
993
560
|
globalColors.push([...c]);
|
|
994
561
|
found = globalColors.length - 1;
|
|
@@ -998,14 +565,11 @@ export function generateMultiFrame(options) {
|
|
|
998
565
|
frameMappings.push(mapping);
|
|
999
566
|
}
|
|
1000
567
|
|
|
1001
|
-
// Third pass: remap frame matrices to global palette
|
|
1002
568
|
const remappedFrames = frames.map((frame, fi) => {
|
|
1003
569
|
const mapping = frameMappings[fi];
|
|
1004
570
|
return frame.compositeFrameMatrix.map((row) => row.map((ci) => (mapping[ci] !== undefined ? mapping[ci] : 0)));
|
|
1005
571
|
});
|
|
1006
572
|
|
|
1007
|
-
// Build objectLayerRenderFramesData structure
|
|
1008
|
-
// For floor types: use direction '08' (down_idle / default_idle / none_idle)
|
|
1009
573
|
const objectLayerRenderFramesData = {
|
|
1010
574
|
frame_duration: frameDuration,
|
|
1011
575
|
is_stateless: descriptor.itemType === 'floor',
|
|
@@ -1013,7 +577,6 @@ export function generateMultiFrame(options) {
|
|
|
1013
577
|
colors: globalColors,
|
|
1014
578
|
};
|
|
1015
579
|
|
|
1016
|
-
// Assign frames to directions
|
|
1017
580
|
const directionMappings = {
|
|
1018
581
|
floor: [['down_idle', 'none_idle', 'default_idle']],
|
|
1019
582
|
skin: [
|
|
@@ -1028,14 +591,12 @@ export function generateMultiFrame(options) {
|
|
|
1028
591
|
};
|
|
1029
592
|
|
|
1030
593
|
const dirGroups = directionMappings[descriptor.itemType] || directionMappings.floor;
|
|
1031
|
-
|
|
1032
594
|
for (const dirGroup of dirGroups) {
|
|
1033
595
|
for (const dirName of dirGroup) {
|
|
1034
596
|
objectLayerRenderFramesData.frames[dirName] = [...remappedFrames];
|
|
1035
597
|
}
|
|
1036
598
|
}
|
|
1037
599
|
|
|
1038
|
-
// Build objectLayerData
|
|
1039
600
|
const objectLayerData = {
|
|
1040
601
|
data: {
|
|
1041
602
|
item: {
|