cyberia 3.2.22 → 3.2.70
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 +127 -68
- package/.github/workflows/cyberia-client.cd.yml +40 -0
- package/.github/workflows/cyberia-server.cd.yml +40 -0
- package/.github/workflows/docker-image.cyberia-client.ci.yml +49 -0
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +48 -0
- package/.github/workflows/docker-image.cyberia-server.ci.yml +69 -0
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +69 -0
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +52 -0
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +52 -0
- package/.github/workflows/engine-cyberia.cd.yml +33 -24
- package/.github/workflows/engine-cyberia.ci.yml +13 -3
- package/.github/workflows/ghpkg.ci.yml +88 -1
- package/.github/workflows/gitlab.ci.yml +1 -1
- package/.github/workflows/hardhat.ci.yml +1 -1
- package/.github/workflows/npmpkg.ci.yml +10 -7
- package/.github/workflows/publish.ci.yml +2 -2
- package/.github/workflows/publish.cyberia.ci.yml +5 -16
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -8
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/CHANGELOG.md +301 -1
- package/CLI-HELP.md +71 -6
- package/Dockerfile +141 -43
- package/Dockerfile.dev +143 -0
- package/Dockerfile.test +165 -0
- package/README.md +1 -1
- package/baremetal/commission-workflows.json +1 -0
- package/bin/build.js +31 -0
- package/bin/cyberia.js +1080 -184
- package/bin/deploy.js +2 -2
- package/bin/index.js +1080 -184
- package/bump.config.js +1 -0
- package/compose.env +131 -0
- package/conf.js +18 -1
- package/deployment.yaml +2 -2
- package/docker-compose.yml +316 -0
- package/hardhat/hardhat.config.js +2 -2
- package/hardhat/package-lock.json +620 -2041
- package/hardhat/package.json +7 -5
- package/hardhat/scripts/deployObjectLayerToken.js +18 -18
- package/hardhat/test/ObjectLayerToken.js +378 -274
- package/ipfs/configure-ipfs.sh +13 -0
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-cyberia-development/grpc-service.yaml +17 -0
- package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +32 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/mongodb/entrypoint.sh +76 -0
- package/nginx.conf +87 -0
- package/package.json +31 -19
- package/pv-pvc.yaml +32 -0
- package/scripts/disk-clean.sh +85 -60
- package/scripts/kubeadm-node-setup.sh +317 -0
- package/scripts/rocky-kickstart.sh +877 -185
- package/scripts/rpmfusion-ffmpeg-setup.sh +26 -50
- package/scripts/test-monitor.sh +3 -5
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +43 -7
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +17 -25
- package/src/api/cyberia-action/cyberia-action.controller.js +19 -0
- package/src/api/cyberia-action/cyberia-action.model.js +21 -29
- package/src/api/cyberia-action/cyberia-action.router.js +42 -7
- package/src/api/cyberia-action/cyberia-action.service.js +20 -4
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +58 -57
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +37 -6
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +8 -2
- package/src/api/cyberia-entity/cyberia-entity.router.js +39 -7
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +74 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +67 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +63 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +46 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +62 -10
- package/src/api/cyberia-instance/cyberia-instance.model.js +32 -2
- package/src/api/cyberia-instance/cyberia-instance.router.js +6 -6
- package/src/api/cyberia-instance/cyberia-world-generator.js +116 -3
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +3 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +39 -7
- package/src/api/cyberia-map/cyberia-map.router.js +21 -6
- package/src/api/cyberia-quest/cyberia-quest.controller.js +38 -0
- package/src/api/cyberia-quest/cyberia-quest.router.js +47 -7
- package/src/api/cyberia-quest/cyberia-quest.service.js +59 -4
- package/src/api/cyberia-saga/cyberia-saga.controller.js +74 -0
- package/src/api/cyberia-saga/cyberia-saga.model.js +59 -0
- package/src/api/cyberia-saga/cyberia-saga.router.js +63 -0
- package/src/api/cyberia-saga/cyberia-saga.service.js +42 -0
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +551 -129
- package/src/api/cyberia-skill/cyberia-skill.controller.js +74 -0
- package/src/api/cyberia-skill/cyberia-skill.model.js +50 -0
- package/src/api/cyberia-skill/cyberia-skill.router.js +63 -0
- package/src/api/cyberia-skill/cyberia-skill.service.js +42 -0
- package/src/api/ipfs/ipfs.service.js +28 -15
- package/src/api/object-layer/object-layer.router.js +25 -15
- package/src/api/object-layer/object-layer.service.js +15 -20
- package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +39 -7
- package/src/cli/baremetal.js +717 -16
- package/src/cli/cluster.js +80 -5
- package/src/cli/deploy.js +127 -11
- package/src/cli/docker-compose.js +648 -0
- package/src/cli/env.js +11 -2
- package/src/cli/fs.js +75 -30
- package/src/cli/image.js +129 -51
- package/src/cli/index.js +110 -6
- package/src/cli/kickstart.js +142 -20
- package/src/cli/release.js +46 -4
- package/src/cli/repository.js +238 -33
- package/src/cli/run.js +520 -114
- package/src/cli/secrets.js +82 -48
- package/src/cli/ssh.js +234 -0
- package/src/cli/static.js +2 -2
- package/src/client/components/cyberia/ActionEngineCyberia.js +1867 -0
- package/src/client/components/cyberia/EntityEngineCyberia.js +585 -0
- package/src/client/components/cyberia/InstanceEngineCyberia.js +219 -13
- package/src/client/components/cyberia/MapEngineCyberia.js +154 -71
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +40 -63
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +34 -20
- package/src/client/components/cyberia/SharedDefaultsCyberia.js +195 -4
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +66 -0
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +8 -0
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +8 -0
- package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +45 -27
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +3 -3
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +39 -22
- package/src/client/public/cyberia-docs/CYBERIA-LORE.md +88 -0
- package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +394 -0
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +8 -1
- package/src/client/public/cyberia-docs/CYBERIA.md +1 -1
- package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +5 -5
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
- package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +99 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +2 -2
- package/src/client/services/cyberia-map/cyberia-map.management.js +2 -2
- package/src/client/services/cyberia-saga/cyberia-saga.service.js +99 -0
- package/src/client/services/cyberia-skill/cyberia-skill.service.js +99 -0
- package/src/client/services/object-layer/object-layer.management.js +6 -6
- package/src/{server → client-builder}/client-build-docs.js +15 -5
- package/src/{server → client-builder}/client-build-live.js +3 -3
- package/src/{server → client-builder}/client-build.js +25 -22
- package/src/{server → client-builder}/client-dev-server.js +3 -3
- package/src/{server → client-builder}/client-icons.js +2 -2
- package/src/{server → client-builder}/ssr.js +5 -5
- package/src/client.build.js +1 -3
- package/src/client.dev.js +1 -1
- package/src/db/mongo/MongoBootstrap.js +12 -12
- package/src/grpc/cyberia/grpc-server.js +255 -70
- package/src/index.js +12 -1
- package/src/mailer/EmailRender.js +1 -1
- package/src/{server → projects/cyberia}/atlas-sprite-sheet-generator.js +2 -2
- package/src/{server → projects/cyberia}/besu-genesis-generator.js +3 -3
- package/src/projects/cyberia/catalog-cyberia.js +81 -0
- package/src/projects/cyberia/gemini-client.js +175 -0
- package/src/projects/cyberia/generate-saga.js +2107 -0
- package/src/{server → projects/cyberia}/ipfs-client.js +2 -2
- package/src/{server → projects/cyberia}/object-layer.js +12 -108
- package/src/{server → projects/cyberia}/semantic-layer-generator-floor.js +1 -1
- package/src/{server → projects/cyberia}/semantic-layer-generator-resource.js +1 -1
- package/src/{server → projects/cyberia}/semantic-layer-generator-skin.js +1 -1
- package/src/{server → projects/cyberia}/semantic-layer-generator.js +2 -2
- package/src/{server → projects/cyberia}/shape-generator.js +2 -2
- package/src/{server → projects/underpost}/catalog-underpost.js +1 -2
- package/src/runtime/cyberia-client/Dockerfile +27 -61
- package/src/runtime/cyberia-client/Dockerfile.dev +20 -12
- package/src/runtime/cyberia-server/Dockerfile +21 -20
- package/src/runtime/cyberia-server/Dockerfile.dev +17 -8
- package/src/runtime/engine-cyberia/Dockerfile +143 -0
- package/src/runtime/engine-cyberia/Dockerfile.dev +143 -0
- package/src/runtime/engine-cyberia/Dockerfile.test +165 -0
- package/src/runtime/engine-cyberia/compose.env +131 -0
- package/src/runtime/engine-cyberia/docker-compose.yml +316 -0
- package/src/runtime/engine-cyberia/ipfs/configure-ipfs.sh +13 -0
- package/src/runtime/engine-cyberia/mongodb/entrypoint.sh +76 -0
- package/src/runtime/engine-cyberia/nginx.conf +87 -0
- package/src/runtime/express/Express.js +2 -2
- package/src/runtime/nginx/Nginx.js +250 -0
- package/src/server/catalog.js +9 -14
- package/src/server/conf.js +3 -6
- package/src/server/runtime-status.js +18 -1
- package/src/server/start.js +12 -2
- package/src/server.js +6 -2
- package/test/cyberia-instance-conf-defaults.test.js +140 -0
- package/test/deploy-monitor.test.js +26 -10
- package/test/shape-generator.test.js +7 -1
- package/typedoc.dd-cyberia.json +3 -1
- package/typedoc.json +3 -1
- /package/src/client/ssr/{Render.js → RootDocument.js} +0 -0
- /package/src/{server → client-builder}/client-formatted.js +0 -0
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
* the entire simulation defaults into the public JS payload.
|
|
20
20
|
*
|
|
21
21
|
* Shared content **vocabulary** (item/entity type enums, the
|
|
22
|
-
* `DefaultCyberiaItems` registry, `ENTITY_TYPE_TO_ITEM_TYPES`, quest
|
|
23
|
-
*
|
|
22
|
+
* `DefaultCyberiaItems` registry, `ENTITY_TYPE_TO_ITEM_TYPES`, the quest
|
|
23
|
+
* step objective enum) lives in `SharedDefaultsCyberia.js`. This file
|
|
24
24
|
* re-imports those so the browser editor never needs to reach into
|
|
25
25
|
* server-defaults to learn the schema.
|
|
26
26
|
*
|
|
@@ -36,20 +36,11 @@
|
|
|
36
36
|
// The canonical client-defaults module lives under src/client/ so the
|
|
37
37
|
// browser bundler can resolve the URL inside the client tree. Engine-side
|
|
38
38
|
// Node imports work from any path, so we reach into it from here.
|
|
39
|
-
import {
|
|
40
|
-
|
|
41
|
-
// Re-export the shared vocabulary so existing server-side imports keep
|
|
42
|
-
// working without each consumer having to know which module owns which.
|
|
43
|
-
export {
|
|
39
|
+
import {
|
|
44
40
|
ITEM_TYPES,
|
|
45
41
|
ENTITY_TYPES,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
CYBERIA_ACTION_TYPES,
|
|
49
|
-
DefaultCyberiaItems,
|
|
50
|
-
getDefaultCyberiaItemById,
|
|
51
|
-
getDefaultCyberiaItemsByItemType,
|
|
52
|
-
getDefaultCyberiaItemsByEntityType,
|
|
42
|
+
SKILL_LOGIC_ID_VALUES,
|
|
43
|
+
isCanonicalSkillLogicId,
|
|
53
44
|
} from '../../client/components/cyberia/SharedDefaultsCyberia.js';
|
|
54
45
|
|
|
55
46
|
/**
|
|
@@ -70,14 +61,87 @@ export class CyberiaDependencies {
|
|
|
70
61
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
71
62
|
|
|
72
63
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
64
|
+
* Canonical skill configuration — single source of truth for every trigger
|
|
65
|
+
* item → skill mapping consumed by the runtime simulation.
|
|
66
|
+
*
|
|
67
|
+
* Each entry carries both:
|
|
68
|
+
* - `logicEventIds` (compact array of handler keys, used by the Mongo schema)
|
|
69
|
+
* - `skills` (expanded metadata with name, description, summoned entity)
|
|
70
|
+
*
|
|
71
|
+
* Consumers:
|
|
72
|
+
* - `CYBERIA_INSTANCE_CONF_DEFAULTS.skillConfig` (gRPC fallback defaults)
|
|
73
|
+
* - `bin/cyberia.js seed-skills` (upsert into the cyberia-skill collection —
|
|
74
|
+
* the authoritative CyberiaSkill model keeps the full record, including the
|
|
75
|
+
* `skills` metadata the instance-conf skillConfig schema drops)
|
|
75
76
|
*/
|
|
76
77
|
export const DefaultSkillConfig = [
|
|
77
|
-
{
|
|
78
|
-
|
|
78
|
+
{
|
|
79
|
+
triggerItemId: 'atlas_pistol_mk2',
|
|
80
|
+
logicEventIds: ['projectile'],
|
|
81
|
+
skills: [
|
|
82
|
+
{
|
|
83
|
+
logicEventId: 'projectile',
|
|
84
|
+
name: 'Projectile',
|
|
85
|
+
description:
|
|
86
|
+
'Fires a projectile in the direction of the tap. Spawn chance and lifetime scale with Intelligence and Range.',
|
|
87
|
+
summonedEntityItemId: 'atlas_pistol_mk2_bullet',
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
triggerItemId: 'coin',
|
|
93
|
+
logicEventIds: ['coin_drop_or_transaction'],
|
|
94
|
+
skills: [
|
|
95
|
+
{
|
|
96
|
+
logicEventId: 'coin_drop_or_transaction',
|
|
97
|
+
name: 'Coin Drop',
|
|
98
|
+
description:
|
|
99
|
+
'Coins are dropped automatically when an entity is killed. Transfer amount scales with kill percent rules.',
|
|
100
|
+
summonedEntityItemId: 'coin',
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
// {
|
|
105
|
+
// triggerItemId: 'anon',
|
|
106
|
+
// logicEventIds: ['doppelganger'],
|
|
107
|
+
// skills: [
|
|
108
|
+
// {
|
|
109
|
+
// logicEventId: 'doppelganger',
|
|
110
|
+
// name: 'Doppelganger',
|
|
111
|
+
// description: 'Summons a passive clone of yourself that wanders nearby. Spawn chance scales with Intelligence.',
|
|
112
|
+
// summonedEntityItemId: '$active_skin',
|
|
113
|
+
// },
|
|
114
|
+
// ],
|
|
115
|
+
// },
|
|
116
|
+
{
|
|
117
|
+
triggerItemId: 'hatchet',
|
|
118
|
+
logicEventIds: ['projectile'],
|
|
119
|
+
skills: [
|
|
120
|
+
{
|
|
121
|
+
logicEventId: 'projectile',
|
|
122
|
+
name: 'Projectile',
|
|
123
|
+
description:
|
|
124
|
+
'Fires a projectile in the direction of the tap. Spawn chance and lifetime scale with Intelligence and Range.',
|
|
125
|
+
summonedEntityItemId: 'hatchet-skill',
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
},
|
|
79
129
|
];
|
|
80
130
|
|
|
131
|
+
// Fail fast on a non-canonical logicEventId: the canonical LogicId registry in
|
|
132
|
+
// SharedDefaultsCyberia.js is the single source of truth, so a typo or a handler
|
|
133
|
+
// the dispatcher does not know must surface at boot, not as a silent no-op skill.
|
|
134
|
+
for (const cfg of DefaultSkillConfig) {
|
|
135
|
+
for (const logicEventId of [...(cfg.logicEventIds || []), ...(cfg.skills || []).map((sk) => sk.logicEventId)]) {
|
|
136
|
+
if (!isCanonicalSkillLogicId(logicEventId)) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`DefaultSkillConfig: unknown skill logicEventId "${logicEventId}" for trigger "${cfg.triggerItemId}". ` +
|
|
139
|
+
`Allowed (SharedDefaultsCyberia.SKILL_LOGIC_IDS): ${SKILL_LOGIC_ID_VALUES.join(', ')}`,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
81
145
|
/**
|
|
82
146
|
* Default dialogue seeds. Mirrors `CyberiaDialogue` model schema:
|
|
83
147
|
* { code, order, speaker, text, mood }
|
|
@@ -85,40 +149,247 @@ export const DefaultSkillConfig = [
|
|
|
85
149
|
* Used by the seed/migration script to populate Mongo on first boot.
|
|
86
150
|
*/
|
|
87
151
|
export const DefaultCyberiaDialogues = [
|
|
88
|
-
{
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
{
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
152
|
+
{
|
|
153
|
+
code: 'default-coin',
|
|
154
|
+
order: 0,
|
|
155
|
+
speaker: 'Coin',
|
|
156
|
+
text: 'A standard unit of exchange in the cyberia network.',
|
|
157
|
+
mood: 'neutral',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
code: 'default-atlas_pistol_mk2',
|
|
161
|
+
order: 0,
|
|
162
|
+
speaker: 'Atlas Pistol MK2',
|
|
163
|
+
text: 'Military-grade sidearm. Fires energy projectiles.',
|
|
164
|
+
mood: 'neutral',
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
code: 'default-atlas_pistol_mk2_bullet',
|
|
168
|
+
order: 0,
|
|
169
|
+
speaker: 'MK2 Bullet',
|
|
170
|
+
text: 'High-velocity energy round. Dissipates on impact.',
|
|
171
|
+
mood: 'neutral',
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
code: 'default-hatchet',
|
|
175
|
+
order: 0,
|
|
176
|
+
speaker: 'Hatchet',
|
|
177
|
+
text: 'A crude but reliable melee tool. Good for close quarters.',
|
|
178
|
+
mood: 'neutral',
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
code: 'default-wason',
|
|
182
|
+
order: 0,
|
|
183
|
+
speaker: 'Wason',
|
|
184
|
+
text: 'They say I am just a wandering merchant... but I have seen things.',
|
|
185
|
+
mood: 'neutral',
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
code: 'default-wason',
|
|
189
|
+
order: 1,
|
|
190
|
+
speaker: 'Wason',
|
|
191
|
+
text: 'The network was not always like this. There was a time before the portals.',
|
|
192
|
+
mood: 'sad',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
code: 'default-scp-2040',
|
|
196
|
+
order: 0,
|
|
197
|
+
speaker: 'SCP-2040',
|
|
198
|
+
text: 'CONTAINMENT PROTOCOL ACTIVE. Do not make direct eye contact.',
|
|
199
|
+
mood: 'angry',
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
code: 'default-scp-2040',
|
|
203
|
+
order: 1,
|
|
204
|
+
speaker: 'SCP-2040',
|
|
205
|
+
text: 'I remember everything. Every iteration. Every reset.',
|
|
206
|
+
mood: 'sad',
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
code: 'default-purple',
|
|
210
|
+
order: 0,
|
|
211
|
+
speaker: 'Purple',
|
|
212
|
+
text: 'The void between nodes is not empty — it is alive.',
|
|
213
|
+
mood: 'neutral',
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
code: 'default-punk',
|
|
217
|
+
order: 0,
|
|
218
|
+
speaker: 'Punk',
|
|
219
|
+
text: 'Rules are just code someone else wrote. I write my own.',
|
|
220
|
+
mood: 'happy',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
code: 'default-lain',
|
|
224
|
+
order: 0,
|
|
225
|
+
speaker: 'Lain',
|
|
226
|
+
text: 'No matter where you go, everyone is connected.',
|
|
227
|
+
mood: 'neutral',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
code: 'default-lain',
|
|
231
|
+
order: 1,
|
|
232
|
+
speaker: 'Lain',
|
|
233
|
+
text: 'If you are not remembered, then you never existed.',
|
|
234
|
+
mood: 'sad',
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
code: 'default-lain',
|
|
238
|
+
order: 2,
|
|
239
|
+
speaker: 'Lain',
|
|
240
|
+
text: 'The wired is not a separate world. It is layered over this one.',
|
|
241
|
+
mood: 'neutral',
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
code: 'default-kaneki',
|
|
245
|
+
order: 0,
|
|
246
|
+
speaker: 'Kaneki',
|
|
247
|
+
text: 'I am not the protagonist of a novel. I am just... me.',
|
|
248
|
+
mood: 'sad',
|
|
249
|
+
},
|
|
102
250
|
{ code: 'default-kaneki', order: 1, speaker: 'Kaneki', text: 'What is 1000 minus 7?', mood: 'angry' },
|
|
103
|
-
{
|
|
104
|
-
|
|
251
|
+
{
|
|
252
|
+
code: 'default-junko',
|
|
253
|
+
order: 0,
|
|
254
|
+
speaker: 'Junko',
|
|
255
|
+
text: 'Despair is the seed from which hope blooms!',
|
|
256
|
+
mood: 'happy',
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
code: 'default-junko',
|
|
260
|
+
order: 1,
|
|
261
|
+
speaker: 'Junko',
|
|
262
|
+
text: 'How boring... nothing ever surprises me anymore.',
|
|
263
|
+
mood: 'sad',
|
|
264
|
+
},
|
|
105
265
|
{ code: 'default-ghost', order: 0, speaker: 'Ghost', text: '...', mood: 'neutral' },
|
|
106
|
-
{
|
|
107
|
-
|
|
266
|
+
{
|
|
267
|
+
code: 'default-eiri',
|
|
268
|
+
order: 0,
|
|
269
|
+
speaker: 'Eiri',
|
|
270
|
+
text: 'I am the god of the wired. I designed the protocol.',
|
|
271
|
+
mood: 'neutral',
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
code: 'default-eiri',
|
|
275
|
+
order: 1,
|
|
276
|
+
speaker: 'Eiri',
|
|
277
|
+
text: 'Flesh is just hardware. Consciousness is the only software that matters.',
|
|
278
|
+
mood: 'neutral',
|
|
279
|
+
},
|
|
108
280
|
{ code: 'default-anon', order: 0, speaker: '???', text: 'You should not be here. Turn back.', mood: 'angry' },
|
|
109
|
-
{
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
{
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
281
|
+
{
|
|
282
|
+
code: 'default-anon',
|
|
283
|
+
order: 1,
|
|
284
|
+
speaker: '???',
|
|
285
|
+
text: 'Or stay. It does not matter. Nothing leaves this place.',
|
|
286
|
+
mood: 'neutral',
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
code: 'default-alex',
|
|
290
|
+
order: 0,
|
|
291
|
+
speaker: 'Alex',
|
|
292
|
+
text: 'I have been mapping the portal network. Something does not add up.',
|
|
293
|
+
mood: 'neutral',
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
code: 'default-alex',
|
|
297
|
+
order: 1,
|
|
298
|
+
speaker: 'Alex',
|
|
299
|
+
text: 'There are nodes that exist in the registry but have no physical anchor.',
|
|
300
|
+
mood: 'neutral',
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
code: 'default-agent',
|
|
304
|
+
order: 0,
|
|
305
|
+
speaker: 'Agent',
|
|
306
|
+
text: 'Civilian, this area is restricted. State your business.',
|
|
307
|
+
mood: 'neutral',
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
code: 'default-agent',
|
|
311
|
+
order: 1,
|
|
312
|
+
speaker: 'Agent',
|
|
313
|
+
text: 'Hmm. Proceed, but know that you are being watched.',
|
|
314
|
+
mood: 'neutral',
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
code: 'default-grass',
|
|
318
|
+
order: 0,
|
|
319
|
+
speaker: 'Grass',
|
|
320
|
+
text: 'A patch of synthetic grass. It sways gently despite no wind.',
|
|
321
|
+
mood: 'neutral',
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
code: 'quest-talk-wason',
|
|
325
|
+
order: 0,
|
|
326
|
+
speaker: 'Wason',
|
|
327
|
+
text: 'Wanderer! Glad you stopped by. I need a favor — nothing dangerous... mostly.',
|
|
328
|
+
mood: 'happy',
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
code: 'quest-talk-wason',
|
|
332
|
+
order: 1,
|
|
333
|
+
speaker: 'Wason',
|
|
334
|
+
text: "First, find Alex — she's been surveying the nodes east of here. Then gather a hatchet for me.",
|
|
335
|
+
mood: 'neutral',
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
code: 'quest-talk-wason',
|
|
339
|
+
order: 2,
|
|
340
|
+
speaker: 'Wason',
|
|
341
|
+
text: 'And one more thing: the SCP-2040 anomalies are overrunning my trade routes. Deal with two of them.',
|
|
342
|
+
mood: 'sad',
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
code: 'quest-talk-alex',
|
|
346
|
+
order: 0,
|
|
347
|
+
speaker: 'Alex',
|
|
348
|
+
text: "Wason sent you? Good. The portal anomalies are getting worse. I've logged what I can.",
|
|
349
|
+
mood: 'neutral',
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
code: 'quest-talk-alex',
|
|
353
|
+
order: 1,
|
|
354
|
+
speaker: 'Alex',
|
|
355
|
+
text: 'Tell Wason: the source is somewhere in the deeper nodes. The registry does not lie.',
|
|
356
|
+
mood: 'neutral',
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
code: 'talk-lain',
|
|
360
|
+
order: 0,
|
|
361
|
+
speaker: 'Lain',
|
|
362
|
+
text: 'Present day... present time. You are here, so you are real.',
|
|
363
|
+
mood: 'neutral',
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
code: 'talk-lain',
|
|
367
|
+
order: 1,
|
|
368
|
+
speaker: 'Lain',
|
|
369
|
+
text: 'There is nothing I can grant you. Only the wired remembers.',
|
|
370
|
+
mood: 'neutral',
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
code: 'quest-talk-wason-errand',
|
|
374
|
+
order: 0,
|
|
375
|
+
speaker: 'Wason',
|
|
376
|
+
text: 'A small thing, friend — the field is littered with loose coin. Gather a few and bring them here.',
|
|
377
|
+
mood: 'happy',
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
code: 'quest-talk-wason-errand',
|
|
381
|
+
order: 1,
|
|
382
|
+
speaker: 'Wason',
|
|
383
|
+
text: 'You have my thanks. A good hatchet for an honest errand.',
|
|
384
|
+
mood: 'neutral',
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
code: 'quest-talk-agent',
|
|
388
|
+
order: 0,
|
|
389
|
+
speaker: 'Agent',
|
|
390
|
+
text: 'Civilian. There is a bounty if you have the stomach for it. Eliminate the threat, collect the drop, report back.',
|
|
391
|
+
mood: 'neutral',
|
|
392
|
+
},
|
|
122
393
|
];
|
|
123
394
|
|
|
124
395
|
/**
|
|
@@ -126,30 +397,52 @@ export const DefaultCyberiaDialogues = [
|
|
|
126
397
|
* Each entry follows the `CyberiaAction` model schema.
|
|
127
398
|
*/
|
|
128
399
|
export const DefaultCyberiaActions = [
|
|
400
|
+
// An action has no `type`: it declares the capabilities available at a cell.
|
|
401
|
+
// `code` is a generic location slug; `label` is the bot's overhead name (the
|
|
402
|
+
// client fetches it by code via REST). The NPC skin is derived from
|
|
403
|
+
// `dialogCode` (default-<skin>). `questDialogueCodes` maps each quest the NPC
|
|
404
|
+
// handles to the dialogue shown for it (offer + talk-objective validation).
|
|
405
|
+
// The quests an NPC OFFERS are those whose source cell matches the action's.
|
|
129
406
|
{
|
|
130
|
-
code: '
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
407
|
+
code: 'loc-fallback-map-0-12-10',
|
|
408
|
+
label: 'Wason',
|
|
409
|
+
sourceMapCode: 'fallback-map-0',
|
|
410
|
+
sourceCellX: 12,
|
|
411
|
+
sourceCellY: 10,
|
|
412
|
+
dialogCode: 'default-wason',
|
|
413
|
+
questDialogueCodes: [
|
|
414
|
+
{ questCode: 'fallback-intro-quest', dialogCode: 'quest-talk-wason' },
|
|
415
|
+
{ questCode: 'wason-errand', dialogCode: 'quest-talk-wason-errand' },
|
|
416
|
+
// bounty's report-back talk happens here too (bounty is offered at the agent cell).
|
|
417
|
+
{ questCode: 'bounty-quest-alpha', dialogCode: 'quest-talk-wason' },
|
|
418
|
+
],
|
|
138
419
|
},
|
|
139
420
|
{
|
|
140
|
-
code: '
|
|
141
|
-
|
|
142
|
-
|
|
421
|
+
code: 'loc-fallback-map-0-18-10',
|
|
422
|
+
label: 'Alex',
|
|
423
|
+
sourceMapCode: 'fallback-map-0',
|
|
424
|
+
sourceCellX: 18,
|
|
425
|
+
sourceCellY: 10,
|
|
426
|
+
dialogCode: 'default-alex',
|
|
427
|
+
questDialogueCodes: [{ questCode: 'fallback-intro-quest', dialogCode: 'quest-talk-alex' }],
|
|
143
428
|
},
|
|
144
429
|
{
|
|
145
|
-
code: '
|
|
146
|
-
|
|
147
|
-
|
|
430
|
+
code: 'loc-fallback-map-0-12-16',
|
|
431
|
+
label: 'Agent',
|
|
432
|
+
sourceMapCode: 'fallback-map-0',
|
|
433
|
+
sourceCellX: 12,
|
|
434
|
+
sourceCellY: 16,
|
|
435
|
+
dialogCode: 'default-agent',
|
|
436
|
+
questDialogueCodes: [{ questCode: 'bounty-quest-alpha', dialogCode: 'quest-talk-agent' }],
|
|
148
437
|
},
|
|
149
438
|
{
|
|
150
|
-
code: '
|
|
151
|
-
|
|
152
|
-
|
|
439
|
+
code: 'loc-fallback-map-0-15-22',
|
|
440
|
+
label: 'Lain',
|
|
441
|
+
sourceMapCode: 'fallback-map-0',
|
|
442
|
+
sourceCellX: 15,
|
|
443
|
+
sourceCellY: 22,
|
|
444
|
+
dialogCode: 'default-lain',
|
|
445
|
+
questDialogueCodes: [],
|
|
153
446
|
},
|
|
154
447
|
];
|
|
155
448
|
|
|
@@ -162,31 +455,82 @@ export const DefaultCyberiaQuests = [
|
|
|
162
455
|
code: 'fallback-intro-quest',
|
|
163
456
|
title: "The Wanderer's Task",
|
|
164
457
|
description: 'Help Wason restore order to the fractured nodes.',
|
|
458
|
+
// Spatial binding — must match the granting action `wason-quest-intro`.
|
|
459
|
+
sourceMapCode: 'fallback-map-0',
|
|
460
|
+
sourceCellX: 12,
|
|
461
|
+
sourceCellY: 10,
|
|
165
462
|
prerequisiteCodes: [],
|
|
166
463
|
unlocksQuestCodes: ['bounty-quest-alpha'],
|
|
167
464
|
steps: [
|
|
168
|
-
{
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
objectives: [{ type: '
|
|
172
|
-
|
|
173
|
-
|
|
465
|
+
{
|
|
466
|
+
id: 'step-talk-alex',
|
|
467
|
+
description: 'Find Alex and hear her report on the portal anomalies.',
|
|
468
|
+
objectives: [{ type: 'talk', itemId: 'alex', quantity: 1 }],
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
id: 'step-collect-wood',
|
|
472
|
+
description: 'Obtain a wood for Wason.',
|
|
473
|
+
objectives: [{ type: 'collect', itemId: 'wood-drop-1', quantity: 1 }],
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
id: 'step-kill-kishins',
|
|
477
|
+
description: 'Eliminate Kishins anomalies threatening the trade routes.',
|
|
478
|
+
objectives: [{ type: 'kill', itemId: 'kishins', quantity: 2 }],
|
|
479
|
+
},
|
|
174
480
|
],
|
|
175
481
|
rewards: [{ itemId: 'coin', quantity: 50 }],
|
|
176
482
|
},
|
|
483
|
+
{
|
|
484
|
+
// Parallel initial mission — same source cell as Wason (12,10), no
|
|
485
|
+
// prerequisites, so the player can accept it alongside the intro quest.
|
|
486
|
+
code: 'wason-errand',
|
|
487
|
+
title: "Wason's Errand",
|
|
488
|
+
description: 'Gather coins from the field and bring them back to Wason.',
|
|
489
|
+
sourceMapCode: 'fallback-map-0',
|
|
490
|
+
sourceCellX: 12,
|
|
491
|
+
sourceCellY: 10,
|
|
492
|
+
prerequisiteCodes: [],
|
|
493
|
+
unlocksQuestCodes: [],
|
|
494
|
+
steps: [
|
|
495
|
+
{
|
|
496
|
+
id: 'step-collect-coins',
|
|
497
|
+
description: 'Collect 5 coins.',
|
|
498
|
+
objectives: [{ type: 'collect', itemId: 'coin', quantity: 5 }],
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
id: 'step-return-wason',
|
|
502
|
+
description: 'Return to Wason.',
|
|
503
|
+
objectives: [{ type: 'talk', itemId: 'wason', quantity: 1 }],
|
|
504
|
+
},
|
|
505
|
+
],
|
|
506
|
+
rewards: [{ itemId: 'hatchet', quantity: 1 }],
|
|
507
|
+
},
|
|
177
508
|
{
|
|
178
509
|
code: 'bounty-quest-alpha',
|
|
179
510
|
title: 'Alpha Bounty',
|
|
180
511
|
description: 'A field test: eliminate a threat, claim your reward, then report back.',
|
|
512
|
+
// Spatial binding — must match the granting action `agent-mission-brief`.
|
|
513
|
+
sourceMapCode: 'fallback-map-0',
|
|
514
|
+
sourceCellX: 12,
|
|
515
|
+
sourceCellY: 16,
|
|
181
516
|
prerequisiteCodes: ['fallback-intro-quest'],
|
|
182
517
|
unlocksQuestCodes: [],
|
|
183
518
|
steps: [
|
|
184
|
-
{
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
objectives: [{ type: '
|
|
188
|
-
|
|
189
|
-
|
|
519
|
+
{
|
|
520
|
+
id: 'step-kill-first',
|
|
521
|
+
description: 'Eliminate the Kishins threat.',
|
|
522
|
+
objectives: [{ type: 'kill', itemId: 'kishins', quantity: 1 }],
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
id: 'step-collect-reward',
|
|
526
|
+
description: 'Collect the bounty coin drop.',
|
|
527
|
+
objectives: [{ type: 'collect', itemId: 'coin', quantity: 10 }],
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
id: 'step-report-wason',
|
|
531
|
+
description: 'Report back to Wason.',
|
|
532
|
+
objectives: [{ type: 'talk', itemId: 'wason', quantity: 1 }],
|
|
533
|
+
},
|
|
190
534
|
],
|
|
191
535
|
rewards: [{ itemId: 'hatchet', quantity: 1 }],
|
|
192
536
|
},
|
|
@@ -229,7 +573,30 @@ export const STATUS_ICONS = Object.freeze([
|
|
|
229
573
|
{ id: 5, name: 'dead', description: 'Entity is dead / respawning' },
|
|
230
574
|
{ id: 6, name: 'resource', description: 'Resource entity — static, exploitable (wood, minerals, etc.)' },
|
|
231
575
|
{ id: 7, name: 'resource-extracted', description: 'Resource entity extracted/depleted (dead state)' },
|
|
232
|
-
{
|
|
576
|
+
{
|
|
577
|
+
id: 8,
|
|
578
|
+
name: 'action-provider',
|
|
579
|
+
description:
|
|
580
|
+
'Capability: bot has a usable cyberia-action (shop/storage/craft/talk) for players who can interact with it. Sent as a capability bit, not a presence state.',
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
id: 9,
|
|
584
|
+
name: 'quest-provider',
|
|
585
|
+
description:
|
|
586
|
+
'Capability: bot offers or advances a cyberia-quest for the viewing player (acceptable offer or active talk-target). Sent as a capability bit, not a presence state.',
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
id: 10,
|
|
590
|
+
name: 'portal',
|
|
591
|
+
description:
|
|
592
|
+
'Presence: fixed-target portal / transport entity. The client renders the transport icon plus a "<targetMapCode> <x>,<y>" nameplate.',
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
id: 11,
|
|
596
|
+
name: 'portal-random',
|
|
597
|
+
description:
|
|
598
|
+
'Presence: random-target portal (inter-random / intra-random, targetCell -1,-1). The client renders the transport-random icon plus a "<targetMapCode>" nameplate (no cell, the destination is random).',
|
|
599
|
+
},
|
|
233
600
|
]);
|
|
234
601
|
|
|
235
602
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -303,6 +670,14 @@ export const ENTITY_TYPE_DEFAULTS = Object.freeze([
|
|
|
303
670
|
{ itemId: 'coin', active: false, quantity: 0 },
|
|
304
671
|
],
|
|
305
672
|
},
|
|
673
|
+
// Fallback-world mission/action givers. Resolved by their active skin (the
|
|
674
|
+
// liveItemIds key), these bots take the canonical `provider` behavior: they
|
|
675
|
+
// barely move from their spawn and are immortal. Lain only talks in place, so
|
|
676
|
+
// she is fully static. Authors can retarget any of these via EntityEngineCyberia.
|
|
677
|
+
{ entityType: ENTITY_TYPES.bot, liveItemIds: ['wason'], deadItemIds: ['ghost'], behavior: 'provider' },
|
|
678
|
+
{ entityType: ENTITY_TYPES.bot, liveItemIds: ['alex'], deadItemIds: ['ghost'], behavior: 'provider' },
|
|
679
|
+
{ entityType: ENTITY_TYPES.bot, liveItemIds: ['agent'], deadItemIds: ['ghost'], behavior: 'provider' },
|
|
680
|
+
{ entityType: ENTITY_TYPES.bot, liveItemIds: ['lain'], deadItemIds: ['ghost'], behavior: 'provider-static' },
|
|
306
681
|
{
|
|
307
682
|
entityType: ENTITY_TYPES.skill,
|
|
308
683
|
liveItemIds: ['atlas_pistol_mk2_bullet'],
|
|
@@ -319,9 +694,33 @@ export const ENTITY_TYPE_DEFAULTS = Object.freeze([
|
|
|
319
694
|
{ entityType: ENTITY_TYPES.obstacle, liveItemIds: [], deadItemIds: [], dropItemIds: [], defaultObjectLayers: [] },
|
|
320
695
|
{ entityType: ENTITY_TYPES.portal, liveItemIds: [], deadItemIds: [], dropItemIds: [], defaultObjectLayers: [] },
|
|
321
696
|
{ entityType: ENTITY_TYPES.foreground, liveItemIds: [], deadItemIds: [], dropItemIds: [], defaultObjectLayers: [] },
|
|
697
|
+
// Static decorator — non-moving, passable, depth-sorted. Visuals come from the
|
|
698
|
+
// map definition / world generator; no live/dead/drop rotation.
|
|
699
|
+
{ entityType: ENTITY_TYPES.static, liveItemIds: [], deadItemIds: [], dropItemIds: [], defaultObjectLayers: [] },
|
|
322
700
|
...RESOURCE_ENTITY_TYPE_DEFAULTS,
|
|
323
701
|
]);
|
|
324
702
|
|
|
703
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
704
|
+
// Player spawn
|
|
705
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Canonical default player spawn policy. Mirrors PlayerSpawnSchema in
|
|
709
|
+
* cyberia-instance.model.js and the Go PlayerSpawnConfig: when `random` is false
|
|
710
|
+
* and `sourceMapCode` names a loaded map, new players spawn at
|
|
711
|
+
* (sourceCellX, sourceCellY) on it; otherwise (random, or no/unknown map) they
|
|
712
|
+
* spawn at a random walkable cell on a random map.
|
|
713
|
+
*
|
|
714
|
+
* The canonical default is a random spawn so a fresh or procedural world never
|
|
715
|
+
* piles every new player onto a single cell.
|
|
716
|
+
*/
|
|
717
|
+
export const DEFAULT_PLAYER_SPAWN = Object.freeze({
|
|
718
|
+
sourceMapCode: 'fallback-map-0',
|
|
719
|
+
sourceCellX: 3,
|
|
720
|
+
sourceCellY: 3,
|
|
721
|
+
random: false,
|
|
722
|
+
});
|
|
723
|
+
|
|
325
724
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
326
725
|
// Instance-level simulation configuration
|
|
327
726
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -390,56 +789,10 @@ export const CYBERIA_INSTANCE_CONF_DEFAULTS = {
|
|
|
390
789
|
statusIcons: STATUS_ICONS.map((s) => ({ ...s })),
|
|
391
790
|
|
|
392
791
|
// ── Skill system ───────────────────────────────────────────────────
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
{
|
|
398
|
-
logicEventId: 'projectile',
|
|
399
|
-
name: 'Projectile',
|
|
400
|
-
description:
|
|
401
|
-
'Fires a projectile in the direction of the tap. Spawn chance and lifetime scale with Intelligence and Range.',
|
|
402
|
-
summonedEntityItemId: 'atlas_pistol_mk2_bullet',
|
|
403
|
-
},
|
|
404
|
-
],
|
|
405
|
-
},
|
|
406
|
-
{
|
|
407
|
-
triggerItemId: 'coin',
|
|
408
|
-
skills: [
|
|
409
|
-
{
|
|
410
|
-
logicEventId: 'coin_drop_or_transaction',
|
|
411
|
-
name: 'Coin Drop',
|
|
412
|
-
description:
|
|
413
|
-
'Coins are dropped automatically when an entity is killed. Transfer amount scales with kill percent rules.',
|
|
414
|
-
summonedEntityItemId: 'coin',
|
|
415
|
-
},
|
|
416
|
-
],
|
|
417
|
-
},
|
|
418
|
-
{
|
|
419
|
-
triggerItemId: 'anon',
|
|
420
|
-
skills: [
|
|
421
|
-
{
|
|
422
|
-
logicEventId: 'doppelganger',
|
|
423
|
-
name: 'Doppelganger',
|
|
424
|
-
description:
|
|
425
|
-
'Summons a passive clone of yourself that wanders nearby. Spawn chance scales with Intelligence.',
|
|
426
|
-
summonedEntityItemId: '$active_skin',
|
|
427
|
-
},
|
|
428
|
-
],
|
|
429
|
-
},
|
|
430
|
-
{
|
|
431
|
-
triggerItemId: 'hatchet',
|
|
432
|
-
skills: [
|
|
433
|
-
{
|
|
434
|
-
logicEventId: 'projectile',
|
|
435
|
-
name: 'Projectile',
|
|
436
|
-
description:
|
|
437
|
-
'Fires a projectile in the direction of the tap. Spawn chance and lifetime scale with Intelligence and Range.',
|
|
438
|
-
summonedEntityItemId: 'hatchet-skill',
|
|
439
|
-
},
|
|
440
|
-
],
|
|
441
|
-
},
|
|
442
|
-
],
|
|
792
|
+
// Canonical skill definitions live in DefaultSkillConfig above.
|
|
793
|
+
// This reference is the single point of consumption for the gRPC
|
|
794
|
+
// fallback defaults — any change goes through DefaultSkillConfig.
|
|
795
|
+
skillConfig: DefaultSkillConfig,
|
|
443
796
|
|
|
444
797
|
skillRules: {
|
|
445
798
|
projectileSpawnChance: 0.5,
|
|
@@ -456,3 +809,72 @@ export const CYBERIA_INSTANCE_CONF_DEFAULTS = {
|
|
|
456
809
|
// ── Equipment Rules ────────────────────────────────────────────────
|
|
457
810
|
equipmentRules: { ...EQUIPMENT_RULES_DEFAULTS },
|
|
458
811
|
};
|
|
812
|
+
|
|
813
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
814
|
+
// Instance-conf default backfill
|
|
815
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
816
|
+
|
|
817
|
+
const _isPlainObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
818
|
+
|
|
819
|
+
const _cloneDefault = (v) => JSON.parse(JSON.stringify(v));
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Recursively backfill one value against its canonical default:
|
|
823
|
+
* - null / undefined → deep clone of the default
|
|
824
|
+
* - empty array → deep clone of the default (config lists must never
|
|
825
|
+
* export empty — e.g. skillConfig, entityDefaults)
|
|
826
|
+
* - non-empty array → kept verbatim (author content)
|
|
827
|
+
* - plain object → keep author-set keys, recurse to fill missing default keys
|
|
828
|
+
* - scalar → kept verbatim when present (0, '', false count as present)
|
|
829
|
+
*/
|
|
830
|
+
function _deepFillDefaults(value, defaultValue) {
|
|
831
|
+
if (value === null || value === undefined) return _cloneDefault(defaultValue);
|
|
832
|
+
if (Array.isArray(defaultValue)) {
|
|
833
|
+
return Array.isArray(value) && value.length > 0 ? value : _cloneDefault(defaultValue);
|
|
834
|
+
}
|
|
835
|
+
if (_isPlainObject(defaultValue) && _isPlainObject(value)) {
|
|
836
|
+
const out = { ...value };
|
|
837
|
+
for (const key of Object.keys(defaultValue)) {
|
|
838
|
+
out[key] = _deepFillDefaults(value[key], defaultValue[key]);
|
|
839
|
+
}
|
|
840
|
+
return out;
|
|
841
|
+
}
|
|
842
|
+
return value;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Normalise a skillConfig array to the shape CyberiaInstanceConfSchema stores
|
|
847
|
+
* (`SkillConfigEntrySchema` = { triggerItemId, logicEventIds }). The canonical
|
|
848
|
+
* DefaultSkillConfig carries extra `skills` metadata the schema drops on write;
|
|
849
|
+
* stripping it here keeps export ⇄ DB round-trips churn-free.
|
|
850
|
+
*/
|
|
851
|
+
function _normaliseSkillConfig(skillConfig) {
|
|
852
|
+
if (!Array.isArray(skillConfig)) return [];
|
|
853
|
+
return skillConfig.map((entry) => ({
|
|
854
|
+
triggerItemId: entry.triggerItemId,
|
|
855
|
+
logicEventIds: [...(entry.logicEventIds || [])],
|
|
856
|
+
}));
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* Return a copy of a CyberiaInstanceConf document with every field defined by
|
|
861
|
+
* CyberiaInstanceConfSchema present. Missing, null/undefined, or empty-array
|
|
862
|
+
* fields — common when a doc is read with `.lean()` (which skips Mongoose schema
|
|
863
|
+
* defaults), was created before a schema field existed, or was seeded with the
|
|
864
|
+
* schema's empty-array default (e.g. `skillConfig`) — are filled from
|
|
865
|
+
* CYBERIA_INSTANCE_CONF_DEFAULTS, the same canonical values the fallback world
|
|
866
|
+
* uses. Author-set scalars (including 0, '', false), non-empty arrays, and DB
|
|
867
|
+
* metadata (_id, instanceCode, timestamps) are preserved untouched.
|
|
868
|
+
*
|
|
869
|
+
* @param {object} [conf]
|
|
870
|
+
* @returns {object}
|
|
871
|
+
*/
|
|
872
|
+
export function fillInstanceConfDefaults(conf = {}) {
|
|
873
|
+
const source = _isPlainObject(conf) ? conf : {};
|
|
874
|
+
const out = { ...source };
|
|
875
|
+
for (const key of Object.keys(CYBERIA_INSTANCE_CONF_DEFAULTS)) {
|
|
876
|
+
out[key] = _deepFillDefaults(source[key], CYBERIA_INSTANCE_CONF_DEFAULTS[key]);
|
|
877
|
+
}
|
|
878
|
+
out.skillConfig = _normaliseSkillConfig(out.skillConfig);
|
|
879
|
+
return out;
|
|
880
|
+
}
|