@jgengine/shell 0.18.0 → 0.18.1
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/CHANGELOG.md +114 -2
- package/dist/GamePlayerShell.js +10 -4
- package/dist/JoinGate.d.ts +17 -0
- package/dist/JoinGate.js +14 -0
- package/dist/Shell3dPresentation.d.ts +8 -1
- package/dist/Shell3dPresentation.js +34 -5
- package/dist/ShellChrome.d.ts +2 -0
- package/dist/ShellChrome.js +6 -0
- package/dist/TerritoryOverlay.d.ts +21 -0
- package/dist/TerritoryOverlay.js +9 -0
- package/dist/audio/AudioComponents.js +8 -1
- package/dist/audio/audioEngine.d.ts +7 -1
- package/dist/audio/audioEngine.js +67 -5
- package/dist/camera/cameraRigs.js +7 -0
- package/dist/defineGame.js +21 -6
- package/dist/devtools/KeysPanel.js +3 -3
- package/dist/drivers/FrameDriver.d.ts +5 -1
- package/dist/drivers/FrameDriver.js +61 -24
- package/dist/drivers/HudOnlyDriver.js +9 -4
- package/dist/environment/EnvironmentScene.js +1 -1
- package/dist/input/gamepadMerge.d.ts +10 -0
- package/dist/input/gamepadMerge.js +12 -0
- package/dist/input/gamepadSource.d.ts +12 -0
- package/dist/input/gamepadSource.js +107 -0
- package/dist/materialOverride.d.ts +4 -1
- package/dist/materialOverride.js +10 -1
- package/dist/postfx/PostProcessing.d.ts +10 -7
- package/dist/postfx/PostProcessing.js +33 -15
- package/dist/postfx/gradeShader.js +13 -0
- package/dist/render/CascadedShadows.d.ts +11 -0
- package/dist/render/CascadedShadows.js +19 -8
- package/dist/render/EnvironmentLighting.d.ts +4 -1
- package/dist/render/EnvironmentLighting.js +45 -9
- package/dist/render/SceneLighting.d.ts +16 -2
- package/dist/render/SceneLighting.js +56 -6
- package/dist/render/SceneModels.js +45 -9
- package/dist/render/modelLoad.d.ts +10 -0
- package/dist/render/modelLoad.js +28 -0
- package/dist/render/modelRender.d.ts +3 -0
- package/dist/render/modelRender.js +6 -2
- package/dist/render/testFixtures/proceduralSpriteAtlas.d.ts +15 -0
- package/dist/render/testFixtures/proceduralSpriteAtlas.js +30 -0
- package/dist/render/testFixtures/proceduralTileLayer.d.ts +13 -0
- package/dist/render/testFixtures/proceduralTileLayer.js +30 -0
- package/dist/render/useFootIk.d.ts +9 -0
- package/dist/render/useFootIk.js +89 -0
- package/dist/render/useModelAnimation.d.ts +2 -0
- package/dist/render/useModelAnimation.js +163 -107
- package/dist/scene/CityRenderer.js +661 -236
- package/dist/scene/cityWindowMaterial.d.ts +8 -6
- package/dist/scene/cityWindowMaterial.js +80 -21
- package/dist/settings/appliedSettings.d.ts +3 -1
- package/dist/settings/appliedSettings.js +5 -2
- package/dist/terrain/terrainDetailMaterial.js +7 -3
- package/dist/useShellMultiplayerSync.d.ts +8 -1
- package/dist/useShellMultiplayerSync.js +25 -5
- package/dist/visibility/CullingProvider.d.ts +2 -1
- package/dist/visibility/CullingProvider.js +7 -2
- package/dist/world/GridWorldScene.js +4 -0
- package/dist/world/TileLayerRenderer.d.ts +9 -0
- package/dist/world/TileLayerRenderer.js +64 -0
- package/dist/world/WorldParticles.d.ts +31 -0
- package/dist/world/WorldParticles.js +128 -0
- package/dist/world/WorldScene.d.ts +2 -1
- package/dist/world/WorldScene.js +46 -4
- package/dist/world/entityPose.d.ts +4 -0
- package/dist/world/entityPose.js +9 -0
- package/dist/world/tileLayerInstances.d.ts +11 -0
- package/dist/world/tileLayerInstances.js +28 -0
- package/dist/worldSync.d.ts +5 -1
- package/dist/worldSync.js +25 -4
- package/package.json +4 -4
- package/skills/ce-handoff/SKILL.md +1 -1
- package/skills/game-design/SKILL.md +11 -1
- package/skills/jgengine/SKILL.md +2 -0
- package/skills/jgengine/api.md +94 -19
- package/skills/jgengine/capabilities.md +28 -0
- package/skills/jgengine-ui/SKILL.md +8 -0
- package/skills/jgengine-ui/api.md +127 -16
- package/skills/jgengine-ui/capabilities.md +21 -0
- package/skills/jgengine-ui/reference.md +3 -0
- package/skills/jgengine-world/SKILL.md +6 -0
- package/skills/jgengine-world/api.md +312 -12
- package/skills/jgengine-world/capabilities.md +102 -0
- package/skills/level-design/SKILL.md +1 -1
- package/skills/jgengine-ui/references/visual-scorecard.md +0 -73
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
Reach for these before hand-rolling. Each row is *the thing you need* → *the primitive that already does it*.
|
|
6
6
|
|
|
7
|
+
## ai-decision-graph — Evaluate serializable selector, sequence, condition, action, and utility AI decisions.
|
|
8
|
+
|
|
9
|
+
- `createDecisionGraphRuntime` (function) · `import { createDecisionGraphRuntime } from "@jgengine/core/ai/decisionGraph"`
|
|
10
|
+
|
|
7
11
|
## ai-driver — difficulty-aware chase/route driving step producing throttle/brake/steer for the vehicle sim
|
|
8
12
|
|
|
9
13
|
- `driveStep` (function) · `import { driveStep } from "@jgengine/core/ai/driver"`
|
|
@@ -16,6 +20,16 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
16
20
|
|
|
17
21
|
- `createDriverState` (function) · `import { createDriverState } from "@jgengine/core/ai/driver"`
|
|
18
22
|
|
|
23
|
+
## anim-graph — data-first animation state machine with blend trees, crossfades, layers, and clip events
|
|
24
|
+
|
|
25
|
+
- `createAnimGraphRuntime` (function) · `import { createAnimGraphRuntime } from "@jgengine/core/anim/animGraph"`
|
|
26
|
+
|
|
27
|
+
## animation — inverse kinematics orient a bone toward a target with constrained yaw and pitch
|
|
28
|
+
|
|
29
|
+
- `lookAt` (function) · `import { lookAt } from "@jgengine/core/anim/ikSolver"`
|
|
30
|
+
- `solveFabrik` (function) · `import { solveFabrik } from "@jgengine/core/anim/ikSolver"`
|
|
31
|
+
- `solveTwoBone` (function) · `import { solveTwoBone } from "@jgengine/core/anim/ikSolver"`
|
|
32
|
+
|
|
19
33
|
## area-effect-field — source-following area membership with enter/refresh/leave edges, stacking, and cleanup
|
|
20
34
|
|
|
21
35
|
- `createAreaEffectField` (function) · `import { createAreaEffectField } from "@jgengine/core/world"`
|
|
@@ -102,6 +116,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
102
116
|
- `registerBuiltinTriggerActions` (function) · `import { registerBuiltinTriggerActions } from "@jgengine/core/scene/authoredTriggers"`
|
|
103
117
|
- `registerTriggerAction` (function) · `import { registerTriggerAction } from "@jgengine/core/world"`
|
|
104
118
|
|
|
119
|
+
## behavior-actions — register named callbacks for decision graph behavior descriptors
|
|
120
|
+
|
|
121
|
+
- `registerBehaviorActions` (function) · `import { registerBehaviorActions } from "@jgengine/core/world"`
|
|
122
|
+
|
|
105
123
|
## behavior-control — pause/disable/resume/seek/inspect behavior instances per entity
|
|
106
124
|
|
|
107
125
|
- `behaviorControl` (function) · `import { behaviorControl } from "@jgengine/core/world"`
|
|
@@ -142,6 +160,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
142
160
|
|
|
143
161
|
- `createCameraShake` (function) · `import { createCameraShake } from "@jgengine/core/world"`
|
|
144
162
|
|
|
163
|
+
## character-controller — capsule walk over the physics backend — slide, step-up, slopes, crouch, platforms
|
|
164
|
+
|
|
165
|
+
- `createCharacterController` (function) · `import { createCharacterController } from "@jgengine/core/movement/characterController"`
|
|
166
|
+
|
|
145
167
|
## city-district — polygon math for road-derived blocks, parcels, and buildable footprints
|
|
146
168
|
|
|
147
169
|
- `Vec2` (type) · `import { Vec2 } from "@jgengine/core/world/cityGeometry"`
|
|
@@ -279,6 +301,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
279
301
|
|
|
280
302
|
- `DEFAULT_FORWARD` (const) · `import { DEFAULT_FORWARD } from "@jgengine/core/world"`
|
|
281
303
|
|
|
304
|
+
## free-flight — camera-relative creative/spectator/noclip/hover flight with correct strafe, sprint, and optional gravity
|
|
305
|
+
|
|
306
|
+
- `createFreeFlightController` (function) · `import { createFreeFlightController } from "@jgengine/core/world"`
|
|
307
|
+
|
|
308
|
+
## gamepad-input — Resolve sampled gamepad input into actions with deadzones and response curves.
|
|
309
|
+
|
|
310
|
+
- `resolveGamepadFrame` (function) · `import { resolveGamepadFrame } from "@jgengine/core/input/gamepadModel"`
|
|
311
|
+
|
|
282
312
|
## glide — gliding/wingsuit descent control from a launch
|
|
283
313
|
|
|
284
314
|
- `createGlideModel` (function) · `import { createGlideModel } from "@jgengine/core/world"`
|
|
@@ -337,6 +367,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
337
367
|
|
|
338
368
|
- `generateLock` (function) · `import { generateLock } from "@jgengine/core/world"`
|
|
339
369
|
|
|
370
|
+
## locomotion-graph — build the default idle/walk/run plus one-shot graph from clip names
|
|
371
|
+
|
|
372
|
+
- `locomotionGraph` (function) · `import { locomotionGraph } from "@jgengine/core/anim/locomotionGraph"`
|
|
373
|
+
|
|
340
374
|
## map-annotations — player-drawn map annotation layer — freehand strokes, area shapes, and pinned notes, projected into the map's routes/zones props and serializable
|
|
341
375
|
|
|
342
376
|
- `createAnnotationLayer` (function) · `import { createAnnotationLayer } from "@jgengine/core/world"`
|
|
@@ -378,6 +412,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
378
412
|
|
|
379
413
|
- `createOrderRegistry` (function) · `import { createOrderRegistry } from "@jgengine/core/world"`
|
|
380
414
|
|
|
415
|
+
## particle-director — game-reachable VFX seam: queue one-shot particle bursts and keyed follow-capable standing emitters as serializable data for the shell to render
|
|
416
|
+
|
|
417
|
+
- `createParticleDirector` (function) · `import { createParticleDirector } from "@jgengine/core/vfx/particleDirector"`
|
|
418
|
+
|
|
381
419
|
## particle-system — deterministic pooled particle emitter with SoA render buffers, burst/continuous emission, gravity/drag, size/color/alpha-over-life, and serializable state
|
|
382
420
|
|
|
383
421
|
- `createParticleSystem` (function) · `import { createParticleSystem } from "@jgengine/core/world"`
|
|
@@ -403,6 +441,22 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
403
441
|
|
|
404
442
|
- `definePatrolOrder` (function) · `import { definePatrolOrder } from "@jgengine/core/world"`
|
|
405
443
|
|
|
444
|
+
## perception-service — retain sight, sound, and damage observations with range, cone, occlusion, and decay policy
|
|
445
|
+
|
|
446
|
+
- `createPerception` (function) · `import { createPerception } from "@jgengine/core/sensor/perception"`
|
|
447
|
+
|
|
448
|
+
## physics-backend — one interface over interchangeable rigid-body solvers — bodies, joints, raycast, shapecast, snapshot
|
|
449
|
+
|
|
450
|
+
- `PhysicsBackend` (interface) · `import { PhysicsBackend } from "@jgengine/core/physics/physicsBackend"`
|
|
451
|
+
|
|
452
|
+
## physics-backend-conformance — the shared test contract a physics adapter runs to prove it matches the seam
|
|
453
|
+
|
|
454
|
+
- `runPhysicsBackendConformance` (function) · `import { runPhysicsBackendConformance } from "@jgengine/core/physics/physicsBackendConformance"`
|
|
455
|
+
|
|
456
|
+
## physics-world-backend — the zero-dependency PhysicsWorld solver exposed through the PhysicsBackend seam
|
|
457
|
+
|
|
458
|
+
- `createPhysicsWorldBackend` (function) · `import { createPhysicsWorldBackend } from "@jgengine/core/physics/physicsWorldBackend"`
|
|
459
|
+
|
|
406
460
|
## piece-sockets-from-model — derive build-piece snap sockets from a model's named attach points
|
|
407
461
|
|
|
408
462
|
- `pieceSocketsFromModel` (function) · `import { pieceSocketsFromModel } from "@jgengine/core/world/buildSockets"`
|
|
@@ -451,6 +505,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
451
505
|
|
|
452
506
|
- `evaluateQteSequence` (function) · `import { evaluateQteSequence } from "@jgengine/core/world"`
|
|
453
507
|
|
|
508
|
+
## rapier-physics-backend — native capsules, shapecasts, rotation, CCD, joints, and collision queries
|
|
509
|
+
|
|
510
|
+
- `createRapierBackend` (function) · `import { createRapierBackend } from "@jgengine/rapier"`
|
|
511
|
+
|
|
512
|
+
## rate-window-policy — Evaluate deterministic request windows with retry timing.
|
|
513
|
+
|
|
514
|
+
- `decideRateWindow` (function) · `import { decideRateWindow } from "@jgengine/core/time/rateWindow"`
|
|
515
|
+
|
|
454
516
|
## reputation — faction standing that crosses named reputation tiers
|
|
455
517
|
|
|
456
518
|
- `tierForStanding` (function) · `import { tierForStanding } from "@jgengine/core/world"`
|
|
@@ -560,6 +622,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
560
622
|
|
|
561
623
|
- `surfaceRing` (function) · `import { surfaceRing } from "@jgengine/core/world"`
|
|
562
624
|
|
|
625
|
+
## tactical-cover-points — derive cover candidates from navigation-mesh boundaries
|
|
626
|
+
|
|
627
|
+
- `coverPoints` (function) · `import { coverPoints } from "@jgengine/core/ai/tacticalQueries"`
|
|
628
|
+
|
|
629
|
+
## tactical-position-score — rank bounded tactical positions with caller-defined weights
|
|
630
|
+
|
|
631
|
+
- `scorePositions` (function) · `import { scorePositions } from "@jgengine/core/ai/tacticalQueries"`
|
|
632
|
+
|
|
563
633
|
## target-acquirer — stateful held-target wrapper over an acquisition policy with retention
|
|
564
634
|
|
|
565
635
|
- `createTargetAcquirer` (function) · `import { createTargetAcquirer } from "@jgengine/core/world"`
|
|
@@ -584,6 +654,30 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
584
654
|
|
|
585
655
|
- `raycastHeightField` (function) · `import { raycastHeightField } from "@jgengine/core/world/terrain"`
|
|
586
656
|
|
|
657
|
+
## territory-cell-chunk — Map ownership cells to persisted world chunks.
|
|
658
|
+
|
|
659
|
+
- `territoryChunkKey` (function) · `import { territoryChunkKey } from "@jgengine/core/world/territory"`
|
|
660
|
+
|
|
661
|
+
## territory-footprint — Resolve exactly the ownership cells touched by a placement footprint.
|
|
662
|
+
|
|
663
|
+
- `territoryFootprintCells` (function) · `import { territoryFootprintCells } from "@jgengine/core/world/territory"`
|
|
664
|
+
|
|
665
|
+
## territory-owner-query — Read a loaded land cell owner for authoritative placement checks.
|
|
666
|
+
|
|
667
|
+
- `territoryOwnerOf` (function) · `import { territoryOwnerOf } from "@jgengine/core/world/territory"`
|
|
668
|
+
|
|
669
|
+
## territory-ownership — Claim, release and query land through host-owned snapshot storage.
|
|
670
|
+
|
|
671
|
+
- `createTerritory` (function) · `import { createTerritory } from "@jgengine/core/world/territory"`
|
|
672
|
+
|
|
673
|
+
## territory-placement — Purchase footprint cells atomically with object placement.
|
|
674
|
+
|
|
675
|
+
- `placeWithTerritory` (function) · `import { placeWithTerritory } from "@jgengine/core/world/territory"`
|
|
676
|
+
|
|
677
|
+
## territory-read-scope — Load the footprint and foreign-owner gap without scanning the world.
|
|
678
|
+
|
|
679
|
+
- `territoryChunkKeys` (function) · `import { territoryChunkKeys } from "@jgengine/core/world/territory"`
|
|
680
|
+
|
|
587
681
|
## touch-controls — switch the on-screen touch control set when gameplay context changes (enter/exit vehicle, mount, build mode)
|
|
588
682
|
|
|
589
683
|
- `setTouchControlsMode` (function) · `import { setTouchControlsMode } from "@jgengine/core/input/touchControlsMode"`
|
|
@@ -601,6 +695,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
601
695
|
|
|
602
696
|
- `resolveWalkerStep` (function) · `import { resolveWalkerStep } from "@jgengine/core/movement/solidObstacles"`
|
|
603
697
|
|
|
698
|
+
## walker-solid-step-source — slide a step against solid geometry from a pure rules package
|
|
699
|
+
|
|
700
|
+
- `resolveSourceWalkerStep` (function) · `import { resolveSourceWalkerStep } from "@jgengine/core/movement/solidObstacles"`
|
|
701
|
+
|
|
604
702
|
## wave-runner — observable serializable wave/spawn runner over the seeded spawn director — WAVE N, wave progress, budget/alert readout, onSpawn sink, snapshot/restore
|
|
605
703
|
|
|
606
704
|
- `createWaveRunner` (function) · `import { createWaveRunner } from "@jgengine/core/world"`
|
|
@@ -620,6 +718,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
620
718
|
- `buildTrimmedIntersections` (function) · `import { buildTrimmedIntersections } from "@jgengine/core/world"`
|
|
621
719
|
- `trimBandAtJunctions` (function) · `import { trimBandAtJunctions } from "@jgengine/core/world"`
|
|
622
720
|
|
|
721
|
+
## world-physics-colliders — Synchronize terrain and authored static colliders with a physics backend.
|
|
722
|
+
|
|
723
|
+
- `syncWorldColliders` (function) · `import { syncWorldColliders } from "@jgengine/core/physics/worldColliders"`
|
|
724
|
+
|
|
623
725
|
## world-place — declare the place a game happens in — flat/round/voxel/board ground, surface laws, per-place physics
|
|
624
726
|
|
|
625
727
|
- `world` (function) · `import { world } from "@jgengine/core/world"`
|
|
@@ -11,7 +11,7 @@ Own the spatial realization of game design: player metrics, topology, routes, la
|
|
|
11
11
|
|
|
12
12
|
All authorable world content belongs in `editor.scene.json`, changed through the editor GUI or RPC/CLI. Never hand-edit the document or keep geometry, paths, spawns, zones, objectives, terrain, foliage, encounters, or coordinate arrays in code. File an editor `[FEATURE]` issue before any fallback.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
For a greenfield build, read [references/application-playbook.md](references/application-playbook.md) and produce its greenfield contract (promise, pillars, anti-pillars, core decision, and falsifier) before writing code. Audits may read only the playbook's audit sections. Read [references/live-editor-loop.md](references/live-editor-loop.md) whenever live captures, an editor-agent handoff, or iterative scene improvement is involved. Read [references/field-guide.md](references/field-guide.md) for metric families, topology, pacing, genre lenses, accessibility, and research.
|
|
15
15
|
|
|
16
16
|
## Choose the mode
|
|
17
17
|
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# Visual scorecard
|
|
2
|
-
|
|
3
|
-
This repeatable 0–3 rubric makes "looks shipped" consistent across sessions and usable by a fresh-eyes reviewer. Apply it to milestone shots, not every tweak, and place the scored table beside PR screenshots.
|
|
4
|
-
|
|
5
|
-
Structure and thresholds adapted (concept only, reworded for this stack — not copied verbatim) from `majidmanzarpour/threejs-game-skills`' `visual-scorecard.md` (MIT); see `CREDITS.md`.
|
|
6
|
-
|
|
7
|
-
## The 0–3 scale
|
|
8
|
-
|
|
9
|
-
Score each category independently against what's actually in the shot, not what the code intends.
|
|
10
|
-
|
|
11
|
-
| Score | Meaning |
|
|
12
|
-
| --- | --- |
|
|
13
|
-
| **0** | Engine defaults or debug output. Untextured primitives, default grey material, flat ambient light, no sky/fog treatment, no authored placement. |
|
|
14
|
-
| **1** | Basic styled prototype. Readable and on-genre, but generic — solid-color materials, one light, sparse or untuned `AuthoredScene` content, no distinct silhouettes. |
|
|
15
|
-
| **2** | Premium stylized. Authored silhouettes on hero/obstacles, a coherent material system (not one grey PBR everywhere), tuned `environment()` lighting/sky/post, foliage and props placed through the editor read as a real place, not a demo. |
|
|
16
|
-
| **3** | Showcase art direction. Everything in 2, plus: no repeated silhouette anywhere in frame, deliberate foreground/midground/background composition, purposeful VFX and motion, a HUD that could not be mistaken for another game. Reads like a store page, not a build. |
|
|
17
|
-
|
|
18
|
-
## The 10 categories
|
|
19
|
-
|
|
20
|
-
Score all ten from one `--mode play` shot plus deterministic world/document evidence from `jgengine-verify`. Data presence is a prerequisite, not a substitute for visual judgment.
|
|
21
|
-
|
|
22
|
-
1. **Art direction** — one coherent palette and mood across ground, props, sky, and UI; not three unrelated hue choices stapled together.
|
|
23
|
-
2. **Hero/player** — an authored silhouette and material set the player is meant to be, not a default primitive or a capsule with an emissive glow standing in for a character.
|
|
24
|
-
3. **Obstacles/enemies** — distinct types read apart at a glance; not one mesh reused at different scales/colors.
|
|
25
|
-
4. **Rewards/interactables** — visually distinct from hazards and terrain at a glance, not the same proxy geometry as everything else.
|
|
26
|
-
5. **World/environment** — foreground, midground, and background read as separate layers (props/landmarks near camera, terrain shaping mid-distance, sky/fog treatment at the horizon); not one flat plane running to a bare horizon.
|
|
27
|
-
6. **Materials/textures** — terrain and props carry texture and color variation (`environment({ sculpt, materialRegions })`, catalog GLBs via `jgengine-assets`), not flat untextured solids.
|
|
28
|
-
7. **Lighting/render** — a tuned daylight/sun+hemisphere rig and post stack (per `look: "cinematic"`, #773), not the bare untouched three.js default light.
|
|
29
|
-
8. **VFX/motion** — particle/motion feedback tied to actual gameplay events (hits, pickups, weather), not absent, and not one looping default effect standing in everywhere.
|
|
30
|
-
9. **UI/HUD** — a game-specific `HudCanvas`/`HudPanel` composition with unique art direction; fails automatically if it could pass for a generic admin dashboard **or** for another game shipping stock engine widgets/theme presets unskinned.
|
|
31
|
-
10. **Performance evidence** — the look holds at the scale the game actually runs (`InstancedScatter` density, many entities), not only a hand-posed hero shot with three objects in frame.
|
|
32
|
-
|
|
33
|
-
## Measured-evidence row
|
|
34
|
-
|
|
35
|
-
Pixel metrics from `bun run shoot <id> --mode play --inspect` (or `bun run inspect-shot.ts <shot.png>` on an existing PNG — [#788](https://github.com/Noisemaker111/jgengine/issues/788), shipped) are the objective inputs to categories 5–7 — they don't replace the eye, they catch what a rushed glance misses. Output lands at `shots/<name>.metrics.json`:
|
|
36
|
-
|
|
37
|
-
| Metric | Threshold | Flags |
|
|
38
|
-
| --- | --- | --- |
|
|
39
|
-
| `colorEntropyBits` | `< 3.0` | sparse palette |
|
|
40
|
-
| `dominantColorShare` | `> 0.6` | sparse / one material dominates the frame |
|
|
41
|
-
| `edgeDensity` | `< 0.04` | primitive-dominant, too few silhouettes |
|
|
42
|
-
| `luminance.contrast` | `< 60` | fog/post compressing out real geometry |
|
|
43
|
-
|
|
44
|
-
Any metric over threshold caps the affected category at **1** regardless of how the shot reads by eye — the number is a floor, not a veto the eye can talk it out of. `--strict` hard-fails on a crossed threshold for CI; the scorecard itself always records this row from the JSON, never "not measured."
|
|
45
|
-
|
|
46
|
-
## Pass thresholds
|
|
47
|
-
|
|
48
|
-
- **Premium**: every category ≥ 2, average ≥ 2.3.
|
|
49
|
-
- **Showcase**: at least 6 categories at 3, no category < 2, average ≥ 2.7.
|
|
50
|
-
|
|
51
|
-
Below "premium" on a claimed milestone is not shippable; keep working the art stack or narrow the claim.
|
|
52
|
-
|
|
53
|
-
## Automatic failures
|
|
54
|
-
|
|
55
|
-
Any one of these fails the shot regardless of the averaged score:
|
|
56
|
-
|
|
57
|
-
- **Primitive-dominant shots** — untextured boxes/spheres/cylinders make up the visible mass of the frame.
|
|
58
|
-
- **Default primitive + glow standing in for a hero** — an emissive-glow capsule/sphere is not a character, even at full brightness.
|
|
59
|
-
- **One repeated silhouette everywhere** — `InstancedScatter`'s stylized proxy species (or any single catalog model) used with no palette variety across the whole visible world.
|
|
60
|
-
- **Fog/bloom hiding missing geometry** — atmosphere used to obscure an empty or unfinished scene instead of to add depth to a populated one.
|
|
61
|
-
- **No active-play shot** — only editor/menu/poster shots submitted. `--mode play` (or `drive` past the menu) evidence is mandatory; a scene that only looks good in the editor camera hasn't proven anything about how the game actually reads.
|
|
62
|
-
|
|
63
|
-
## Fresh-eyes review and the lower-score rule
|
|
64
|
-
|
|
65
|
-
Hand a reviewer **only** the screenshots, this rubric, and the measured-evidence JSON — no PR description, no "here's what I was going for." A reviewer who knows the intent scores the intent; a fresh reviewer scores the pixels, which is the only thing a player ever sees.
|
|
66
|
-
|
|
67
|
-
- The author scores first (self-score).
|
|
68
|
-
- A fresh-eyes pass (a different session, or the same session after clearing framing context) scores independently.
|
|
69
|
-
- Where the two disagree, **the PR carries the lower score**, never the average and never the higher one. Optimistic self-scoring is the failure mode this guards against — take the lower number and keep working the art stack instead of arguing the rubric.
|
|
70
|
-
|
|
71
|
-
## Using this in a PR
|
|
72
|
-
|
|
73
|
-
Any PR that changes what a player sees pastes the ten-category table (score + one-line reason per category below 2), the measured-evidence row, and the reconciled score next to embedded screenshots. Report that verdict through `jgengine-verify`'s evidence format.
|