Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ jobs:
- name: License check
run: npm run license-check

- name: Check generated site resources are current
run: npm run sites:check

- name: Install Playwright browsers
run: npx playwright install --with-deps chromium

Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ jobs:
- name: License check
run: npm run license-check

- name: Check generated site resources are current
run: npm run sites:check

- name: Install Playwright browsers
run: npx playwright install --with-deps chromium

Expand Down
117 changes: 117 additions & 0 deletions docs/baseline/schemas/site-presentation.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://diving-simulator.local/schemas/site-presentation.json",
"title": "Dive site presentation resource",
"description": "Art-only site data: features, visual zones, atmosphere and decoration rules. Nothing here may reach collision, air, spawning or currents — those live in the gameplay resource. Feature kinds carry their own extra fields, so feature objects stay open; every field this schema does name is typed, so a string where a number belongs fails validation.",
"type": "object",
"additionalProperties": false,
"required": ["schemaVersion", "kind", "sourceDigest", "generator", "sites"],
"properties": {
"schemaVersion": { "const": 1 },
"kind": { "const": "diving-simulator-site-presentation" },
"sourceDigest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
"generator": { "type": "string", "minLength": 1 },
"sites": {
"type": "object",
"minProperties": 1,
"additionalProperties": { "$ref": "#/$defs/site" }
}
},
"$defs": {
"feature": {
"type": "object",
"required": ["kind", "x"],
"properties": {
"kind": { "type": "string", "minLength": 1 },
"x": { "type": "number" },
"d": { "type": "number", "minimum": 0 },
"dTop": { "type": "number", "minimum": 0 },
"dBottom": { "type": "number", "minimum": 0 }
}
},
"visualZone": {
"type": "object",
"additionalProperties": false,
"required": ["id", "x1", "x2", "d1", "d2"],
"properties": {
"id": { "type": "string", "minLength": 1 },
"x1": { "type": "number" },
"x2": { "type": "number" },
"d1": { "type": "number", "minimum": 0 },
"d2": { "type": "number", "minimum": 0 },
"priority": { "type": "number" },
"blend": { "type": "number", "minimum": 0 },
"tags": { "type": "array", "items": { "type": "string" } }
}
},
"atmosphereProfile": {
"type": "object",
"additionalProperties": false,
"properties": {
"visibility": { "type": "number", "minimum": 0 },
"tint": {
"type": "array",
"minItems": 3,
"maxItems": 3,
"items": { "type": "number" }
},
"particleDensity": { "type": "number", "minimum": 0 },
"particleBrightness": { "type": "number", "minimum": 0 },
"ambient": { "type": "number", "minimum": 0 }
}
},
"decorationRule": {
"description": "Every field renderer.js reads off a rule, whether or not the shipped data authors it yet. An undeclared numeric field is worse than an absent one: the renderer's guards are all `rule.f != null && x < rule.f`, so a string makes the comparison NaN-false and the guard silently stops guarding. Closed against unknown fields so a typo'd name fails here rather than being read as `undefined` and defaulted away.",
"type": "object",
"additionalProperties": false,
"required": ["id", "zone"],
"properties": {
"id": { "type": "string", "minLength": 1 },
"zone": { "type": "string", "minLength": 1 },
"spacing": { "type": "number", "exclusiveMinimum": 0 },
"density": { "type": "number", "minimum": 0 },
"seed": { "type": "number" },
"surface": { "type": "string", "enum": ["floor", "ceiling"] },
"minDepth": { "type": "number", "minimum": 0 },
"maxDepth": { "type": "number", "minimum": 0 },
"minScale": { "type": "number", "exclusiveMinimum": 0 },
"maxScale": { "type": "number", "exclusiveMinimum": 0 },
"maxPerScreen": { "type": "integer", "minimum": 0 },
"rotationJitter": { "type": "number", "minimum": 0 },
"alpha": { "type": "number", "minimum": 0, "maximum": 1 },
"props": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["kind"],
"properties": {
"kind": { "type": "string", "minLength": 1 },
"weight": { "type": "number", "exclusiveMinimum": 0 }
}
}
}
}
},
"site": {
"type": "object",
"additionalProperties": false,
"required": ["id"],
"properties": {
"id": { "type": "string", "minLength": 1 },
"name": { "type": "string", "minLength": 1 },
"surfaceMarker": { "type": "string" },
"features": { "type": "array", "items": { "$ref": "#/$defs/feature" } },
"visualZones": { "type": "array", "items": { "$ref": "#/$defs/visualZone" } },
"atmosphereProfiles": {
"type": "object",
"additionalProperties": { "$ref": "#/$defs/atmosphereProfile" }
},
"decorationRules": {
"type": "array",
"items": { "$ref": "#/$defs/decorationRule" }
}
}
}
}
}
101 changes: 101 additions & 0 deletions docs/baseline/schemas/site.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://diving-simulator.local/schemas/site-gameplay.json",
"title": "Dive site gameplay resource",
"description": "Renderer-neutral collision, air and spawn data. Nothing here describes how a site looks; art lives in the presentation resource and cannot reach these fields.",
"type": "object",
"additionalProperties": false,
"required": ["schemaVersion", "kind", "sourceDigest", "generator", "sites"],
"properties": {
"schemaVersion": { "const": 1 },
"kind": { "const": "diving-simulator-site-gameplay" },
"sourceDigest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
"generator": { "type": "string", "minLength": 1 },
"sites": {
"type": "object",
"minProperties": 1,
"additionalProperties": { "$ref": "#/$defs/site" }
}
},
"$defs": {
"entryPoint": {
"description": "Where the diver enters the water. Gameplay data, so its type has to be pinned: `{ x: \"0\" }` would place a spawn at a string coordinate, and the parity suite does not compare entry points.",
"type": "object",
"additionalProperties": false,
"required": ["x"],
"properties": {
"x": { "type": "number" }
}
},
"profilePoint": {
"type": "object",
"additionalProperties": false,
"required": ["x", "d"],
"properties": {
"x": { "type": "number" },
"d": { "type": "number", "minimum": 0 }
}
},
"profile": {
"type": "array",
"minItems": 2,
"items": { "$ref": "#/$defs/profilePoint" }
},
"structure": {
"type": "object",
"required": ["x1", "x2", "dTop", "dBottom"],
"properties": {
"x1": { "type": "number" },
"x2": { "type": "number" },
"dTop": { "type": "number", "minimum": 0 },
"dBottom": { "type": "number", "minimum": 0 },
"kind": { "type": "string" }
}
},
"badAirDome": {
"type": "object",
"required": ["x1", "x2"],
"properties": {
"x1": { "type": "number" },
"x2": { "type": "number" },
"d": { "type": "number", "minimum": 0 }
}
},
"site": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"maxDepth",
"hasOverhead",
"entry",
"floor",
"ceiling",
"structures",
"badAir"
],
"properties": {
"id": { "type": "string", "minLength": 1 },
"maxDepth": { "type": "number", "exclusiveMinimum": 0 },
"hasOverhead": { "type": "boolean" },
"entry": { "$ref": "#/$defs/entryPoint" },
"boatX": { "type": "number" },
"floor": { "$ref": "#/$defs/profile" },
"ceiling": {
"description": "Overhead profile, or explicit null for open water. Required, and null must be spelled out: an absent field makes ceilingAt() fall through to 0, which turns the cave roof into open water (14 m becomes 0 m at x=50) and lets the diver swim up through it. Parity would not catch that, because legacy and generated data lose the field together and agree on 0.",
"oneOf": [{ "$ref": "#/$defs/profile" }, { "type": "null" }]
},
"structures": {
"type": "array",
"items": { "$ref": "#/$defs/structure" }
},
"badAir": {
"type": "array",
"items": { "$ref": "#/$defs/badAirDome" }
},
"currentBias": { "type": "number", "minimum": 0 },
"noShark": { "type": "boolean" }
}
}
}
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
"wp06:perf": "npm run build && node scripts/capture-wp06-performance.mjs",
"license-check": "license-checker --excludePrivatePackages --onlyAllow \"MIT;ISC;BSD-2-Clause;BSD-3-Clause;Apache-2.0;MPL-2.0;0BSD;CC0-1.0;CC-BY-3.0;Python-2.0;WTFPL;Unlicense;BlueOak-1.0.0\"",
"prepare": "husky",
"baseline:visual-compare": "node scripts/compare-rendering.mjs"
"baseline:visual-compare": "node scripts/compare-rendering.mjs",
"sites:generate": "node scripts/generate-site-resources.mjs",
"sites:check": "node scripts/generate-site-resources.mjs --check"
},
"devDependencies": {
"@playwright/test": "^1.62.0",
Expand Down
Loading