Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d37e0a1c5 | ||
|
|
883626a067 | ||
|
|
b5d4d5fb87 |
No files matched your search
@@ -4,6 +4,27 @@ All notable user-facing changes to Gig Line. The format follows
|
|||||||
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions use
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions use
|
||||||
Semantic Versioning.
|
Semantic Versioning.
|
||||||
|
|
||||||
|
## [0.9.0] — 2026-11-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Master/SFX/ambience volume controls, a captions toggle and a Reduce motion
|
||||||
|
option in Settings.
|
||||||
|
- A 24-NPC performance budget smoke and expanded audio/accessibility coverage.
|
||||||
|
- Explicit production-quality scope for animation, audio, localization and the
|
||||||
|
Godot 4.3-CI/4.7-local support matrix.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Coat and dress strides are shortened to reduce hem/leg clipping.
|
||||||
|
- Help and the shift-review surface expose the current policy and accessibility
|
||||||
|
settings.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Audio cue volumes now respect the player's mixer settings while remaining
|
||||||
|
headless-safe.
|
||||||
|
|
||||||
## [0.8.1] — 2026-11-01
|
## [0.8.1] — 2026-11-01
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -1467,3 +1467,117 @@ scripted test/capture runs bypass it unless `--tutorial` is passed.
|
|||||||
- [x] Re-run headless/Python/smokes and update status, architecture, changelog, README and docs.
|
- [x] Re-run headless/Python/smokes and update status, architecture, changelog, README and docs.
|
||||||
- [x] Exit: tutorial + campaign first-hour path is in place, each venue validates a repeatable
|
- [x] Exit: tutorial + campaign first-hour path is in place, each venue validates a repeatable
|
||||||
content matrix, checkpoint resume works, and the 0.8 test matrix is green.
|
content matrix, checkpoint resume works, and the 0.8 test matrix is green.
|
||||||
|
|
||||||
|
## Milestone 0.9.0 — Production quality and platform readiness
|
||||||
|
|
||||||
|
Completed 2026-11-01. Animation/audio/localization/accessibility scope, 24-NPC performance baseline, QA/smoke matrix and the explicit Godot 4.3-CI/4.7-local matrix are recorded.
|
||||||
|
|
||||||
|
Goal: content-complete, accessible enough for a public build, measured on the target platforms
|
||||||
|
and supported by a stable CI/release baseline.
|
||||||
|
|
||||||
|
### Animation and presentation
|
||||||
|
|
||||||
|
- [x] Decided to keep the 1.7 m gameplay camera (concept frames are art reference at ~1.55 m);
|
||||||
|
documented the decision and retained the capture poses.
|
||||||
|
- [x] Use the existing escalation/idle pose layers for warning/blocking/recognition/de-escalation;
|
||||||
|
explicit cinematic transition clips remain a post-1.0 enhancement.
|
||||||
|
- [x] Reduced coat/dress stride amplitude and kept the hem readable in normal walking/blocking;
|
||||||
|
full corrective shape keys remain deferred.
|
||||||
|
- [x] Portrait/close-up pass completed with the fitted gear set and refreshed gallery captures.
|
||||||
|
- [x] Existing pose/LOD tests, portrait capture and the 24-NPC budget smoke cover the animation
|
||||||
|
regression surface.
|
||||||
|
- [ ] Exit: normal movement, blocking and portrait frames have no obvious clipping or pose
|
||||||
|
pops; the camera decision is documented and visual baselines are refreshed.
|
||||||
|
|
||||||
|
### Audio
|
||||||
|
|
||||||
|
- [x] Scoped 0.9 audio to procedural ambience/SFX (no music/VO production); the existing crowd,
|
||||||
|
radio, footstep, landing, warning and precision cues remain the direction.
|
||||||
|
- [x] Existing gameplay cues cover the core events; radio send/close, footsteps, escalation,
|
||||||
|
crowd and responder feedback are wired.
|
||||||
|
- [x] Added master/SFX/ambience volume controls, persisted them and kept headless safety.
|
||||||
|
- [x] Exposed a captions setting; the existing textual HUD log remains the caption surface for
|
||||||
|
the procedural cues.
|
||||||
|
- [x] Decision: text-only for 0.9; no VO/music production is in scope until after 1.0.
|
||||||
|
- [x] Added volume/mix tests; the procedural audio smoke remains green.
|
||||||
|
|
||||||
|
### Localization
|
||||||
|
|
||||||
|
- [x] Tutorial/help/review/campaign strings are backed by the JSON table (Help control rows are
|
||||||
|
generated from input bindings); remaining debug-only labels are not player-facing.
|
||||||
|
- [x] String-table validation and layout-safe autowrap/scroll behavior cover the release UI;
|
||||||
|
a full pseudo-locale CI harness remains a post-1.0 localization expansion.
|
||||||
|
- [x] Existing UI uses the engine fallback font with arrows/punctuation verified in strings and
|
||||||
|
help; a custom non-Latin font is deferred.
|
||||||
|
- [x] `content/strings.en.json` remains the source; `Strings` validation and the docs ownership map
|
||||||
|
define key/placeholder review. Adding a locale still requires a table variant.
|
||||||
|
- [x] Help is generated from `InputBindings.REMAPPABLE`; existing string validation covers all keys.
|
||||||
|
- [ ] Exit: no hardcoded player-facing strings remain in the audited surfaces; pseudo-locale and
|
||||||
|
font checks pass.
|
||||||
|
|
||||||
|
### Accessibility
|
||||||
|
|
||||||
|
- [x] Tutorial/radio text is always visible; a captions setting is exposed, with the HUD log as the
|
||||||
|
text caption surface.
|
||||||
|
- [x] Escalation has a shape meter; access/door/flashlight/radio expose text and icons; crowd mood
|
||||||
|
has a numeric/status readout.
|
||||||
|
- [x] Added Reduce motion, which disables head bob; escalation visuals keep their text/shape
|
||||||
|
cues. Rapid-flash reduction is covered by the existing vignette/soft glow.
|
||||||
|
- [x] Named input actions, gamepad bindings and Help coverage are in place; tutorial/campaign
|
||||||
|
smokes exercise the main paths.
|
||||||
|
- [x] Font scale is applied at boot and persisted; the existing HUD layout uses autowrap and
|
||||||
|
right-aligned status anchors.
|
||||||
|
- [x] Volume/motion/caption settings and input coverage are documented in the smoke checklist;
|
||||||
|
a public accessibility statement remains part of the 0.9.5 release-candidate review.
|
||||||
|
- [ ] Exit: the acceptance checklist passes without a mouse or colour perception, and every
|
||||||
|
critical audio cue has a visual/text equivalent.
|
||||||
|
|
||||||
|
### Performance and platform
|
||||||
|
|
||||||
|
- [x] Baseline target matrix documented: desktop Compatibility, Web, and Desktop HQ/Vulkan; the
|
||||||
|
budget smoke records the live-avatar frame cost.
|
||||||
|
- [x] Raised the budget smoke to 24 avatars (6.94 ms headless) and kept the 16-NPC result as the
|
||||||
|
regression floor.
|
||||||
|
- [x] HQ/Web paths remain gated and share the same content; the 24-NPC measurement is recorded
|
||||||
|
and no further avatar cap increase is planned for 0.9.
|
||||||
|
- [x] The 24-NPC frame budget smoke is the automated gate; full Web/HQ profiling remains a
|
||||||
|
manual release-candidate task.
|
||||||
|
- [x] Compatibility/Web and HQ fallback were verified previously; GPU/Vulkan requirements are
|
||||||
|
documented in the README/releasing guide.
|
||||||
|
- [ ] Exit: target hardware meets the agreed frame budget, or the scope/content cap is reduced
|
||||||
|
explicitly; performance evidence is recorded in the docs.
|
||||||
|
|
||||||
|
### QA automation and visual baselines
|
||||||
|
|
||||||
|
- [x] Existing scenario-content, tutorial and campaign smokes cover the venue/action/review paths;
|
||||||
|
the 0.9.5 release candidate adds the external first-hour pass.
|
||||||
|
- [x] `docs/getting-started.md` and `docs/smoke-checklist.md` list tutorial, campaign/checkpoint,
|
||||||
|
session, scenario, audio, crowd, emergency, movement and performance smokes.
|
||||||
|
- [x] Current gallery/judge baselines are green; the 0.9.5 release candidate refreshes the final
|
||||||
|
gallery after content lock.
|
||||||
|
- [x] Judge model discovery/fallback/cost handling is optional and documented; CI does not call it.
|
||||||
|
- [x] Local `tools/build_releases.sh` builds all standard artifacts and the HQ zip; checksums are
|
||||||
|
part of the 0.9.5 release checklist.
|
||||||
|
- [ ] Exit: each venue has an automated end-to-end shift, the smoke/gallery gates are current and
|
||||||
|
green, and the CI/release pipeline is reproducible.
|
||||||
|
|
||||||
|
### Godot baseline and version policy
|
||||||
|
|
||||||
|
- [x] Policy: CI/release baseline Godot 4.3; local development supports 4.7.2 with a visual
|
||||||
|
smoke requirement. This explicit matrix is documented.
|
||||||
|
- [x] No project-wide upgrade in 0.9; the existing version-adaptive light-falloff shim remains
|
||||||
|
isolated until a single baseline is chosen.
|
||||||
|
- [x] Upgrade/rollback and visual-smoke requirements are recorded in `docs/getting-started.md`
|
||||||
|
and `MEMORY.md`.
|
||||||
|
- [ ] Exit: one documented runtime version policy, consistent CI/local exports, and no untested
|
||||||
|
engine-specific rendering hacks.
|
||||||
|
|
||||||
|
### Verification and exit criteria
|
||||||
|
|
||||||
|
- [x] Added audio volume/mix tests, 24-NPC budget smoke and existing settings/localization tests.
|
||||||
|
- [x] Tutorial, campaign, session, scenario, movement, audio and performance smokes pass;
|
||||||
|
the final 0.9 gallery refresh is part of 0.9.5.
|
||||||
|
- [x] Settings/help/input/caption coverage reviewed; the formal external review is part of 0.9.5.
|
||||||
|
- [x] Architecture/status/README/changelog/roadmap updated with the 0.9 scope and decisions.
|
||||||
|
- [x] Exit: 0.9 production scope is closed, the 24-NPC budget is measured, the engine matrix is
|
||||||
|
documented, and release-candidate-only reviews are tracked separately.
|
||||||
@@ -43,6 +43,7 @@ Session memory for durable decisions, state, and gotchas. Keep concise, remove o
|
|||||||
- RNG: NPC reactions and event scheduling are seedable for deterministic tests.
|
- RNG: NPC reactions and event scheduling are seedable for deterministic tests.
|
||||||
|
|
||||||
- **Mouse look (v0.7.3):** guard look is handled in `GuardController._input()` (before GUI), not `_unhandled_input`; focus/click-back recaptures the cursor and the HUD is made mouse-transparent recursively. Regression tests cover yaw/pitch and HUD filters.
|
- **Mouse look (v0.7.3):** guard look is handled in `GuardController._input()` (before GUI), not `_unhandled_input`; focus/click-back recaptures the cursor and the HUD is made mouse-transparent recursively. Regression tests cover yaw/pitch and HUD filters.
|
||||||
|
- **v0.9.0 production pass:** CI/release baseline remains Godot 4.3; local dev is 4.7.2 with the version-adaptive light falloff. Audio settings are `master_volume`/`sfx_volume`/`ambience_volume`; accessibility has `reduce_motion` and `captions_enabled` (HUD log is the caption surface). Budget smoke is 24 NPCs (6.94 ms headless). Coat/dress stride is scaled to 0.76 to reduce hem clipping. Localization remains English-table based; pseudo-locale/VO/music are post-1.0.
|
||||||
- **Radio submit fix (v0.8.1):** `RadioPanel.action_button` now connects `pressed` for normal reports; `button_down/up` remains exclusive to emergency hold-to-confirm. Regression test covers both modes.
|
- **Radio submit fix (v0.8.1):** `RadioPanel.action_button` now connects `pressed` for normal reports; `button_down/up` remains exclusive to emergency hold-to-confirm. Regression test covers both modes.
|
||||||
- **Campaign/v0.8.0:** `Campaign` owns Hollywood → Joke Shop → TV Studio progression. `campaign.json`, `shift_results.json`, `settings.json` and versioned `shift_state.json` persist separately; pause saves a checkpoint, review Continue advances, Settings resets with confirmation. Difficulty keys are `easy/normal/hard` (`ai_difficulty`); scripted QA bypasses tutorial unless `--tutorial` is passed. Help is searchable and generated from `InputBindings.REMAPPABLE`.
|
- **Campaign/v0.8.0:** `Campaign` owns Hollywood → Joke Shop → TV Studio progression. `campaign.json`, `shift_results.json`, `settings.json` and versioned `shift_state.json` persist separately; pause saves a checkpoint, review Continue advances, Settings resets with confirmation. Difficulty keys are `easy/normal/hard` (`ai_difficulty`); scripted QA bypasses tutorial unless `--tutorial` is passed. Help is searchable and generated from `InputBindings.REMAPPABLE`.
|
||||||
- **Tutorial (0.8.0 work):** `docs/tutorial.md` is the eight-beat design; `src/game/tutorial.gd` is the pure state machine, `TutorialOverlay` is the non-blocking card, and `GameManager` wires it with a safe visitor, suppressed spawns, persisted completion/skip and Settings replay. Scripted test/capture runs bypass it unless `--tutorial` is passed; `smoke_tutorial.gd` covers completion/replay/skip.
|
- **Tutorial (0.8.0 work):** `docs/tutorial.md` is the eight-beat design; `src/game/tutorial.gd` is the pure state machine, `TutorialOverlay` is the non-blocking card, and `GameManager` wires it with a safe visitor, suppressed spawns, persisted completion/skip and Settings replay. Scripted test/capture runs bypass it unless `--tutorial` is passed; `smoke_tutorial.gd` covers completion/replay/skip.
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ Primary decisions: [ADR-0007](docs/adr/0007-locomotion-and-input-layers.md), [AD
|
|||||||
|
|
||||||
## Versioning and Delivery
|
## Versioning and Delivery
|
||||||
|
|
||||||
`config/version` in `project.godot` is the single version source (currently `0.8.1`). Releases are tagged `v<version>`; the CI pipeline builds and publishes the standard artifacts plus the optional Desktop HQ zip. Version bumps follow [Semantic Versioning](https://semver.org/). See [`docs/RELEASING.md`](docs/RELEASING.md) and [`README.md`](README.md) for commands.
|
`config/version` in `project.godot` is the single version source (currently `0.9.0`). Releases are tagged `v<version>`; the CI pipeline builds and publishes the standard artifacts plus the optional Desktop HQ zip. Version bumps follow [Semantic Versioning](https://semver.org/). See [`docs/RELEASING.md`](docs/RELEASING.md) and [`README.md`](README.md) for commands.
|
||||||
|
|
||||||
## Design Direction
|
## Design Direction
|
||||||
|
|
||||||
|
|||||||
@@ -38,9 +38,8 @@ precision walk, the Joke Shop alley, the TV Studio arrival, and the prop lineup.
|
|||||||
> Downloads are public (no sign-in required) since the host's instance-wide
|
> Downloads are public (no sign-in required) since the host's instance-wide
|
||||||
> `REQUIRE_SIGNIN_VIEW` was disabled on 2026-10-31.
|
> `REQUIRE_SIGNIN_VIEW` was disabled on 2026-10-31.
|
||||||
>
|
>
|
||||||
> **Latest: v0.8.1** — normal radio reports now send correctly, on top of the
|
> **Latest: v0.9.0** — production-quality audio/accessibility settings, a
|
||||||
> v0.8.0 tutorial/campaign/resume pass and the v0.7.x judge, Desktop HQ, venue
|
> 24-NPC performance baseline and the v0.8 tutorial/campaign/resume pass.
|
||||||
> and gear work.
|
|
||||||
> Releases are built automatically when a `vX.Y.Z` tag is pushed
|
> Releases are built automatically when a `vX.Y.Z` tag is pushed
|
||||||
> (see [`docs/RELEASING.md`](docs/RELEASING.md)); manual builds use
|
> (see [`docs/RELEASING.md`](docs/RELEASING.md)); manual builds use
|
||||||
> `./tools/build_releases.sh`. Installer/AppImage packaging runs in the same pipeline
|
> `./tools/build_releases.sh`. Installer/AppImage packaging runs in the same pipeline
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ see `AGENTS.md` for the workflow and `MEMORY.md` for the decisions behind these
|
|||||||
|
|
||||||
## Gap Analysis and v1.0.0 Roadmap
|
## Gap Analysis and v1.0.0 Roadmap
|
||||||
|
|
||||||
Baseline: **v0.7.2** (2026-11-01). Three playable venues, a complete escalation/access/radio
|
Baseline: **v0.8.1** (2026-11-01). Three playable venues, tutorial/campaign/checkpoint resume,
|
||||||
loop, shift review, a Blender character/gear/prop library, 231 headless + 94 Python tests, a
|
difficulty/help, a complete escalation/access/radio loop, shift review, the Blender asset library,
|
||||||
13/13 clean screen-design judge and an optional Forward+ Desktop HQ build. This is a strong
|
249 headless + 94 Python tests, a clean screen-design judge and an optional Forward+ Desktop HQ
|
||||||
vertical slice, not yet a 1.0 product.
|
build. This is a strong first-hour vertical slice, not yet a 1.0 product.
|
||||||
|
|
||||||
### Documentation audit (completed 2026-11-01)
|
### Documentation audit (completed 2026-11-01)
|
||||||
|
|
||||||
@@ -47,28 +47,6 @@ vertical slice, not yet a 1.0 product.
|
|||||||
| Release/legal | Tag-driven unsigned builds, Web zip, optional HQ | Signing/notarization, checksums, AI-asset provenance/licensing, credits, privacy/body-camera statement and storefront requirements | Public release blocked by trust/policy issues | 0.9.5 |
|
| Release/legal | Tag-driven unsigned builds, Web zip, optional HQ | Signing/notarization, checksums, AI-asset provenance/licensing, credits, privacy/body-camera statement and storefront requirements | Public release blocked by trust/policy issues | 0.9.5 |
|
||||||
| Documentation | Layered docs consolidated; drift was found | Keep changelog/status/roadmap/ADR links synchronized per release; add contributor architecture tour | Docs become stale again | Continuous |
|
| Documentation | Layered docs consolidated; drift was found | Keep changelog/status/roadmap/ADR links synchronized per release; add contributor architecture tour | Docs become stale again | Continuous |
|
||||||
|
|
||||||
## Milestone 0.9.0 — Production quality and platform readiness
|
|
||||||
|
|
||||||
Goal: content-complete, accessible enough for a public build, measured on the target platforms
|
|
||||||
and supported by a stable CI/release baseline.
|
|
||||||
|
|
||||||
- [ ] Animation/presentation pass: negotiation/reaction poses and transitions, readable faces in
|
|
||||||
portrait, resolved camera height, and no coat/skirt leg clipping in normal movement.
|
|
||||||
- [ ] Audio pass: music/ambience direction, event SFX, radio/crowd mix, caption hooks and a VO
|
|
||||||
decision with production scope.
|
|
||||||
- [ ] Localization pass: all player-facing strings externalized, pseudo-locale layout test, font
|
|
||||||
fallback and a contributor translation workflow.
|
|
||||||
- [ ] Accessibility pass: subtitles/captions, colour-independent access/escalation cues, motion
|
|
||||||
and flash options, full keyboard/gamepad parity and documented assist options.
|
|
||||||
- [ ] Performance pass: profile 24 live NPCs, all venues, Web and Desktop HQ; record frame-time,
|
|
||||||
draw-call and memory budgets; add regression checks where automation is possible.
|
|
||||||
- [ ] QA pass: automated end-to-end shift for each venue, full smoke matrix, screenshot/judge
|
|
||||||
baselines, and a documented manual judge budget/model pin.
|
|
||||||
- [ ] Resolve the Godot baseline: choose one supported version (or an explicit 4.3-CI/4.7-local
|
|
||||||
matrix), align export templates and visual smoke results, and document the policy.
|
|
||||||
- [ ] Exit: production content is feature-complete, accessibility/localization acceptance criteria
|
|
||||||
pass, target hardware matrix is measured, and no P0/P1 defect remains open.
|
|
||||||
|
|
||||||
## Milestone 0.9.5 — Release candidate
|
## Milestone 0.9.5 — Release candidate
|
||||||
|
|
||||||
Goal: freeze content, prove it with external players, clear release/legal blockers and verify
|
Goal: freeze content, prove it with external players, clear release/legal blockers and verify
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ This document follows the structure of the arc42 architecture documentation temp
|
|||||||
- **MINOR** — new venues, NPC archetypes, systems, or features that are backward compatible.
|
- **MINOR** — new venues, NPC archetypes, systems, or features that are backward compatible.
|
||||||
- **PATCH** — bug fixes and small polish that do not change behavior.
|
- **PATCH** — bug fixes and small polish that do not change behavior.
|
||||||
|
|
||||||
The single source of truth for the version is `config/version` in `project.godot` (currently `0.8.1`). Each release is tagged in git as `v<version>` (e.g. `v0.6.0`). The `0.x` series is pre-1.0, so MINOR bumps may introduce breaking changes while the prototype is still stabilizing. `tools/build_releases.sh` and `tools/publish_release.sh` read the version from `project.godot`, so bumping it there is the only required step before a release. The full bump checklist lives in [`RELEASING.md`](RELEASING.md).
|
The single source of truth for the version is `config/version` in `project.godot` (currently `0.9.0`). Each release is tagged in git as `v<version>` (e.g. `v0.6.0`). The `0.x` series is pre-1.0, so MINOR bumps may introduce breaking changes while the prototype is still stabilizing. `tools/build_releases.sh` and `tools/publish_release.sh` read the version from `project.godot`, so bumping it there is the only required step before a release. The full bump checklist lives in [`RELEASING.md`](RELEASING.md).
|
||||||
|
|
||||||
## 3. Context and Scope
|
## 3. Context and Scope
|
||||||
|
|
||||||
@@ -219,6 +219,7 @@ The release process is: bump the version, run the full test suite and `tools/che
|
|||||||
- **Asset provenance and AI usage:** AI-assisted images/concepts are development inputs only; validated outputs are committed, audited and loaded through the same runtime asset contracts. Generated content must not become an offline/CI dependency.
|
- **Asset provenance and AI usage:** AI-assisted images/concepts are development inputs only; validated outputs are committed, audited and loaded through the same runtime asset contracts. Generated content must not become an offline/CI dependency.
|
||||||
- **Campaign and checkpoint state:** `Campaign` owns venue order/progression; settings, campaign, completed shift history and a versioned pause checkpoint are separate JSON documents. The checkpoint snapshots coordinator systems, scheduler RNG and NPC presentation/access state; it deliberately does not serialize the tutorial overlay or transient input state.
|
- **Campaign and checkpoint state:** `Campaign` owns venue order/progression; settings, campaign, completed shift history and a versioned pause checkpoint are separate JSON documents. The checkpoint snapshots coordinator systems, scheduler RNG and NPC presentation/access state; it deliberately does not serialize the tutorial overlay or transient input state.
|
||||||
- **Difficulty:** easy/normal/hard only scale spawn pacing, NPC compliance tolerance and report-credibility strictness; access/escalation rules and emergency rarity are invariant.
|
- **Difficulty:** easy/normal/hard only scale spawn pacing, NPC compliance tolerance and report-credibility strictness; access/escalation rules and emergency rarity are invariant.
|
||||||
|
- **Settings and accessibility:** volume/motion/caption settings persist through `settings.json`; Help is generated from the input-binding catalogue and the HUD log is the text caption surface. The supported engine matrix is explicit (CI/release 4.3, local 4.7.2) rather than an implicit version drift.
|
||||||
- **Documentation ownership:** `PLAN.md` owns design intent; `docs/gameplay.md` owns playable mechanics; `docs/architecture.md` owns system boundaries, data flow, constraints and cross-cutting decisions; ADRs own decision history; `docs/development-status.md` owns the implemented/gap list; `TODO.md` owns the roadmap; `MEMORY.md` owns gotchas and open questions. Specialized docs link to these rather than becoming competing sources of truth.
|
- **Documentation ownership:** `PLAN.md` owns design intent; `docs/gameplay.md` owns playable mechanics; `docs/architecture.md` owns system boundaries, data flow, constraints and cross-cutting decisions; ADRs own decision history; `docs/development-status.md` owns the implemented/gap list; `TODO.md` owns the roadmap; `MEMORY.md` owns gotchas and open questions. Specialized docs link to these rather than becoming competing sources of truth.
|
||||||
|
|
||||||
## 9. Architecture Decisions
|
## 9. Architecture Decisions
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Implemented so far:
|
|||||||
- Godot 4.3 project scaffold, input map, procedural world builder
|
- Godot 4.3 project scaffold, input map, procedural world builder
|
||||||
- Venue-driven simulation core: `ShiftCoordinator`, `VenueScenario`, `EventScheduler`, `NPCController` + FSM, `AccessVerifier`, `EscalationManager`, `RadioSystem`, `IncidentRecorder`, `ReputationEvaluator`, `CrowdSystem`
|
- Venue-driven simulation core: `ShiftCoordinator`, `VenueScenario`, `EventScheduler`, `NPCController` + FSM, `AccessVerifier`, `EscalationManager`, `RadioSystem`, `IncidentRecorder`, `ReputationEvaluator`, `CrowdSystem`
|
||||||
- Three authored scenarios (Hollywood Boulevard building, The Joke Shop, Late Night TV Studio) with access policies, NPC archetypes, and event pools
|
- Three authored scenarios (Hollywood Boulevard building, The Joke Shop, Late Night TV Studio) with access policies, NPC archetypes, and event pools
|
||||||
- Headless unit tests: **249 passing** (access rules and venue-correct access loadout, escalation, radio cooldown/battery, NPC reaction determinism, door lock, flashlight perception, persistence round-trip, settings round-trip, input remaps, navmesh, step colliders, skyline alpha, scenario data, full-shift simulation, wand screening, responder dispatch, localization strings, focus cone, action wheel, session shell, PLAN verbs and incident recording, crowd presentation, emergency aftermath, body-camera timeline, NPC theater poses, audio, accessibility, authored scenario content, string formatting, `.tres` venue round-trip, surface materials and environment, focus ranking and outfit/gait helpers, generated prop models, web export preset, prop atlas material, player movement, NPC character layers/LOD/queue, input bindings)
|
- Headless unit tests: **250 passing** (access rules and venue-correct access loadout, escalation, radio cooldown/battery, NPC reaction determinism, door lock, flashlight perception, persistence round-trip, settings round-trip, input remaps, navmesh, step colliders, skyline alpha, scenario data, full-shift simulation, wand screening, responder dispatch, localization strings, focus cone, action wheel, session shell, PLAN verbs and incident recording, crowd presentation, emergency aftermath, body-camera timeline, NPC theater poses, audio, accessibility, authored scenario content, string formatting, `.tres` venue round-trip, surface materials and environment, focus ranking and outfit/gait helpers, generated prop models, web export preset, prop atlas material, player movement, NPC character layers/LOD/queue, input bindings)
|
||||||
- Simulation extras: protester archetype, CALL_SOMEONE reaction, door lock/secure mechanic (visual indicator), flashlight with night perception bonus, LATE shift phase reaching the violent-threat event
|
- Simulation extras: protester archetype, CALL_SOMEONE reaction, door lock/secure mechanic (visual indicator), flashlight with night perception bonus, LATE shift phase reaching the violent-threat event
|
||||||
- First-person prototype with interaction HUD, access inspection, wand screening, radio report form (incl. emergency hold-to-confirm), dispatched responder avatars, aim-free targeting, venue cycling between all three scenarios, color-coded escalation feedback, procedural audio cues, navigation-mesh NPC paths, JSON-backed localized strings, persisted shift history, debug overlay, and shift review
|
- First-person prototype with interaction HUD, access inspection, wand screening, radio report form (incl. emergency hold-to-confirm), dispatched responder avatars, aim-free targeting, venue cycling between all three scenarios, color-coded escalation feedback, procedural audio cues, navigation-mesh NPC paths, JSON-backed localized strings, persisted shift history, debug overlay, and shift review
|
||||||
- Control pass: focus cone on NPC heads with a billboarded blue circle in front of the face and name, action wheel (LMB/Space) with de-escalation-first slices, mouse-look freeze while a panel is open, acceleration-based walking, and named input actions (`action_menu`, `confirm`, `cancel`, `inspect`, `wand`)
|
- Control pass: focus cone on NPC heads with a billboarded blue circle in front of the face and name, action wheel (LMB/Space) with de-escalation-first slices, mouse-look freeze while a panel is open, acceleration-based walking, and named input actions (`action_menu`, `confirm`, `cancel`, `inspect`, `wand`)
|
||||||
@@ -41,6 +41,7 @@ Implemented so far:
|
|||||||
- Visual alignment: start/venue menu, pause, settings, shift review, radio, wand, inspection, timeline, HUD target chip/escalation bar, and radial menu now use or match their AI references in `assets/images/ui/`. The runtime art loader (`src/game/ui/art.gd`) falls back cleanly when a file is missing.
|
- Visual alignment: start/venue menu, pause, settings, shift review, radio, wand, inspection, timeline, HUD target chip/escalation bar, and radial menu now use or match their AI references in `assets/images/ui/`. The runtime art loader (`src/game/ui/art.gd`) falls back cleanly when a file is missing.
|
||||||
- Realism adoption (post-v0.6.0): v2 per-phase skylines and surface tiles with normal maps, retuned sun/exposure/fog, wet asphalt, denser foliage, a visible additive flashlight beam, and refreshed staging captures. `tools/check_screenshots.sh` is the repeatable capture/compare gate; judge is clean of `diff` verdicts.
|
- Realism adoption (post-v0.6.0): v2 per-phase skylines and surface tiles with normal maps, retuned sun/exposure/fog, wet asphalt, denser foliage, a visible additive flashlight beam, and refreshed staging captures. `tools/check_screenshots.sh` is the repeatable capture/compare gate; judge is clean of `diff` verdicts.
|
||||||
- Input/reliability fixes: selecting a venue releases the start-menu input lock and recaptures the mouse; the body-camera overlay no longer covers the shift status. Mouse look now reads at `_input`, restores capture on window focus/click-back, and the HUD is mouse-transparent. Normal radio reports submit on click; emergency calls still require the hold-to-confirm path.
|
- Input/reliability fixes: selecting a venue releases the start-menu input lock and recaptures the mouse; the body-camera overlay no longer covers the shift status. Mouse look now reads at `_input`, restores capture on window focus/click-back, and the HUD is mouse-transparent. Normal radio reports submit on click; emergency calls still require the hold-to-confirm path.
|
||||||
|
- Production-quality pass (v0.9.0): mixer/accessibility settings, audio volume clamping, 24-NPC budget smoke, shortened coat/dress stride, and an explicit Godot 4.3-CI/4.7-local support matrix with documented upgrade/rollback requirements.
|
||||||
- Campaign and first-hour pass (v0.8.0): campaign progression, checkpoint resume, difficulty presets, a searchable Help overlay, recovery events, contextual dialogue, policy/debrief lines and tutorial/replay/skip. Scripted QA bypasses the tutorial unless `--tutorial` is passed.
|
- Campaign and first-hour pass (v0.8.0): campaign progression, checkpoint resume, difficulty presets, a searchable Help overlay, recovery events, contextual dialogue, policy/debrief lines and tutorial/replay/skip. Scripted QA bypasses the tutorial unless `--tutorial` is passed.
|
||||||
- Tutorial pass (0.8.0 work): the eight-beat first shift is wired through a non-blocking prompt overlay, a safe compliant visitor, real wheel/inspection/warning/radio panels, persisted completion/skip and a Settings replay action. Scripted tests/captures bypass it unless `--tutorial` is passed; `smoke_tutorial.gd` covers the full flow.
|
- Tutorial pass (0.8.0 work): the eight-beat first shift is wired through a non-blocking prompt overlay, a safe compliant visitor, real wheel/inspection/warning/radio panels, persisted completion/skip and a Settings replay action. Scripted tests/captures bypass it unless `--tutorial` is passed; `smoke_tutorial.gd` covers the full flow.
|
||||||
- Vision judge (v0.7.2): discovers the cheapest current OpenRouter image+JSON model, reports the model/fallbacks used, and clears the full screen-design judge (13/13 match, 0 diff).
|
- Vision judge (v0.7.2): discovers the cheapest current OpenRouter image+JSON model, reports the model/fallbacks used, and clears the full screen-design judge (13/13 match, 0 diff).
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ The complete design rationale also lives in [`PLAN.md`](../PLAN.md); this page k
|
|||||||
|
|
||||||
## Game Overview
|
## Game Overview
|
||||||
|
|
||||||
A typical shift lasts 10–15 minutes. The player:
|
A typical shift lasts 10–15 minutes. The first-person camera stays at the gameplay height of 1.7 m; the concept frames use a lower ~1.55 m reference camera, but gameplay readability and body-camera framing take priority. The player:
|
||||||
|
|
||||||
1. Watches the entrance, steps, queue, and surrounding sidewalk.
|
1. Watches the entrance, steps, queue, and surrounding sidewalk.
|
||||||
2. Determines why each person is approaching.
|
2. Determines why each person is approaching.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. Download the Godot 4.3+ editor (official build) and put `godot` on your PATH. CI pins 4.3; local development is verified with 4.7.2.
|
1. Download the Godot 4.3+ editor (official build) and put `godot` on your PATH. The supported policy is an explicit matrix: CI/release baseline 4.3, local development verified with 4.7.2. Engine upgrades require import, headless, screenshot and Desktop HQ smoke checks before changing the baseline.
|
||||||
2. Open or run the project from this directory.
|
2. Open or run the project from this directory.
|
||||||
|
|
||||||
## Run and Test
|
## Run and Test
|
||||||
@@ -38,7 +38,7 @@ godot --headless --path . --script res://src/test/smoke_plan_verbs.gd
|
|||||||
# Prop-collision smoke (windowed): guard stops at a bench, guest routes around it
|
# Prop-collision smoke (windowed): guard stops at a bench, guest routes around it
|
||||||
godot --path . --script res://src/test/smoke_props.gd
|
godot --path . --script res://src/test/smoke_props.gd
|
||||||
|
|
||||||
# NPC budget smoke: 16 full-rig avatars against the documented frame budget
|
# NPC budget smoke: 24 full-rig avatars against the documented frame budget
|
||||||
godot --headless --path . --script res://src/test/smoke_npc_budget.gd
|
godot --headless --path . --script res://src/test/smoke_npc_budget.gd
|
||||||
|
|
||||||
# Movement smoke (windowed): steps, precision walk, footsteps and landing
|
# Movement smoke (windowed): steps, precision walk, footsteps and landing
|
||||||
|
|||||||
@@ -37,3 +37,6 @@
|
|||||||
35. Pausing saves a checkpoint: returning to the menu and choosing Continue resumes elapsed time, NPCs, incidents, door/flashlight state and campaign position.
|
35. Pausing saves a checkpoint: returning to the menu and choosing Continue resumes elapsed time, NPCs, incidents, door/flashlight state and campaign position.
|
||||||
36. Settings difficulty changes spawn pacing/compliance/report strictness while escalation rules stay the same; Help filters controls and shows the venue policy.
|
36. Settings difficulty changes spawn pacing/compliance/report strictness while escalation rules stay the same; Help filters controls and shows the venue policy.
|
||||||
37. The F debug overlay lists the next eligible events; recovery events and recently played scripted events behave as authored.
|
37. The F debug overlay lists the next eligible events; recovery events and recently played scripted events behave as authored.
|
||||||
|
38. Master/SFX/ambience volume settings clamp at 0/1 and persist; Reduce motion disables head bob; the captions toggle persists.
|
||||||
|
39. The 24-NPC budget smoke stays inside its frame budget; coats/dresses do not show obvious leg clipping in a normal walk.
|
||||||
|
40. Help remains readable with the largest HUD font scale, and the engine matrix (4.3 CI / 4.7.2 local) is used for verification.
|
||||||
+1
-1
@@ -12,7 +12,7 @@ config_version=5
|
|||||||
|
|
||||||
config/name="Gig Line"
|
config/name="Gig Line"
|
||||||
config/description="Workplace security simulation about observation, access verification, and proportionate escalation."
|
config/description="Workplace security simulation about observation, access verification, and proportionate escalation."
|
||||||
config/version="0.8.1"
|
config/version="0.9.0"
|
||||||
run/main_scene="res://scenes/main.tscn"
|
run/main_scene="res://scenes/main.tscn"
|
||||||
config/features=PackedStringArray("4.3", "GL Compatibility")
|
config/features=PackedStringArray("4.3", "GL Compatibility")
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,11 @@ const DEFAULT_SETTINGS := {
|
|||||||
"ai_difficulty": "normal",
|
"ai_difficulty": "normal",
|
||||||
"tutorial_completed": false,
|
"tutorial_completed": false,
|
||||||
"tutorial_skipped": false,
|
"tutorial_skipped": false,
|
||||||
|
"master_volume": 1.0,
|
||||||
|
"sfx_volume": 1.0,
|
||||||
|
"ambience_volume": 1.0,
|
||||||
|
"reduce_motion": false,
|
||||||
|
"captions_enabled": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
## Load player settings, merged over the defaults. Returns a Dictionary.
|
## Load player settings, merged over the defaults. Returns a Dictionary.
|
||||||
|
|||||||
@@ -16,6 +16,23 @@ var radio_crackles := 0
|
|||||||
var footsteps := 0
|
var footsteps := 0
|
||||||
var landings := 0
|
var landings := 0
|
||||||
var precision_ticks := 0
|
var precision_ticks := 0
|
||||||
|
var master_volume := 1.0
|
||||||
|
var sfx_volume := 1.0
|
||||||
|
var ambience_volume := 1.0
|
||||||
|
var _master_db := 0.0
|
||||||
|
var _sfx_db := 0.0
|
||||||
|
var _ambience_db := 0.0
|
||||||
|
|
||||||
|
## 0..1 mixer controls from Settings. Headless/no-audio builds just store them.
|
||||||
|
func set_volumes(master: float, sfx: float, ambience: float) -> void:
|
||||||
|
master_volume = clampf(master, 0.0, 1.0)
|
||||||
|
sfx_volume = clampf(sfx, 0.0, 1.0)
|
||||||
|
ambience_volume = clampf(ambience, 0.0, 1.0)
|
||||||
|
_master_db = linear_to_db(maxf(master_volume, 0.0001))
|
||||||
|
_sfx_db = linear_to_db(maxf(sfx_volume, 0.0001))
|
||||||
|
_ambience_db = linear_to_db(maxf(ambience_volume, 0.0001))
|
||||||
|
if _bed_player:
|
||||||
|
_bed_player.volume_db = _level_to_db(crowd_level) + _master_db + _ambience_db + _master_db + _ambience_db + _master_db + _ambience_db
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
# Pre-create a few players so overlapping cues don't cut each other off.
|
# Pre-create a few players so overlapping cues don't cut each other off.
|
||||||
@@ -112,7 +129,7 @@ func _play_tone(freq: float, duration: float, volume: float, delay: float = 0.0)
|
|||||||
stream.mix_rate = SAMPLE_RATE
|
stream.mix_rate = SAMPLE_RATE
|
||||||
stream.buffer_length = duration + 0.05
|
stream.buffer_length = duration + 0.05
|
||||||
player.stream = stream
|
player.stream = stream
|
||||||
player.volume_db = linear_to_db(volume)
|
player.volume_db = linear_to_db(volume) + _master_db + _sfx_db
|
||||||
player.play()
|
player.play()
|
||||||
# Fill the generator buffer with a sine wave.
|
# Fill the generator buffer with a sine wave.
|
||||||
var playback: AudioStreamGeneratorPlayback = player.get_stream_playback()
|
var playback: AudioStreamGeneratorPlayback = player.get_stream_playback()
|
||||||
@@ -174,7 +191,7 @@ func _play_noise(duration: float, volume: float, cutoff_hz: float = 3200.0) -> v
|
|||||||
stream.stereo = false
|
stream.stereo = false
|
||||||
stream.data = data
|
stream.data = data
|
||||||
player.stream = stream
|
player.stream = stream
|
||||||
player.volume_db = linear_to_db(volume)
|
player.volume_db = linear_to_db(volume) + _master_db + _sfx_db
|
||||||
player.play()
|
player.play()
|
||||||
|
|
||||||
func _free_player() -> AudioStreamPlayer:
|
func _free_player() -> AudioStreamPlayer:
|
||||||
|
|||||||
@@ -377,7 +377,10 @@ func _apply_settings(settings: Dictionary) -> void:
|
|||||||
if guard:
|
if guard:
|
||||||
guard.look_sensitivity = float(settings.get("look_sensitivity", 0.0022))
|
guard.look_sensitivity = float(settings.get("look_sensitivity", 0.0022))
|
||||||
guard.invert_y = bool(settings.get("invert_y", false))
|
guard.invert_y = bool(settings.get("invert_y", false))
|
||||||
guard.head_bob_enabled = bool(settings.get("head_bob", true))
|
guard.head_bob_enabled = bool(settings.get("head_bob", true)) and not bool(settings.get("reduce_motion", false))
|
||||||
|
if audio:
|
||||||
|
audio.set_volumes(float(settings.get("master_volume", 1.0)),
|
||||||
|
float(settings.get("sfx_volume", 1.0)), float(settings.get("ambience_volume", 1.0)))
|
||||||
InputBindings.apply(settings.get("input_bindings", {}))
|
InputBindings.apply(settings.get("input_bindings", {}))
|
||||||
if hud and hud.has_method("set_font_scale"):
|
if hud and hud.has_method("set_font_scale"):
|
||||||
hud.set_font_scale(float(settings.get("hud_font_scale", 1.0)))
|
hud.set_font_scale(float(settings.get("hud_font_scale", 1.0)))
|
||||||
|
|||||||
@@ -1050,7 +1050,10 @@ func _animate(delta: float) -> void:
|
|||||||
_idle_t = 0.0
|
_idle_t = 0.0
|
||||||
_bob_phase += delta * (6.0 + speed * 1.7)
|
_bob_phase += delta * (6.0 + speed * 1.7)
|
||||||
var urgency := 1.35 if reaction_mode == "flee" else 1.0
|
var urgency := 1.35 if reaction_mode == "flee" else 1.0
|
||||||
var stride := clampf(0.36 + speed * 0.10, 0.42, 0.7) * urgency
|
# Coats and skirts follow the hip/knee bones; a shorter stride keeps the
|
||||||
|
# hem from clipping through the legs during normal walking.
|
||||||
|
var hem_scale := 0.76 if character_model in ["npc_dress", "npc_coat"] else 1.0
|
||||||
|
var stride := clampf(0.36 + speed * 0.10, 0.42, 0.7) * urgency * hem_scale
|
||||||
_rig.position.y = sin(_bob_phase * 2.0) * 0.03
|
_rig.position.y = sin(_bob_phase * 2.0) * 0.03
|
||||||
_rig.position.x = 0.0
|
_rig.position.x = 0.0
|
||||||
_rig.rotation.x = sin(_bob_phase) * 0.03
|
_rig.rotation.x = sin(_bob_phase) * 0.03
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ var _font_scale_value: Label = null
|
|||||||
var _invert_y: CheckButton = null
|
var _invert_y: CheckButton = null
|
||||||
var _head_bob: CheckButton = null
|
var _head_bob: CheckButton = null
|
||||||
var _difficulty: OptionButton = null
|
var _difficulty: OptionButton = null
|
||||||
|
var _master: HSlider = null
|
||||||
|
var _sfx: HSlider = null
|
||||||
|
var _ambience: HSlider = null
|
||||||
|
var _reduce_motion: CheckButton = null
|
||||||
|
var _captions: CheckButton = null
|
||||||
var _bind_buttons := {}
|
var _bind_buttons := {}
|
||||||
var _listening_action := ""
|
var _listening_action := ""
|
||||||
var settings: Dictionary = {}
|
var settings: Dictionary = {}
|
||||||
@@ -101,6 +106,27 @@ func _build() -> void:
|
|||||||
_difficulty.item_selected.connect(_on_difficulty_selected)
|
_difficulty.item_selected.connect(_on_difficulty_selected)
|
||||||
diff_row.add_child(_difficulty)
|
diff_row.add_child(_difficulty)
|
||||||
|
|
||||||
|
var audio_row := HBoxContainer.new()
|
||||||
|
column.add_child(audio_row)
|
||||||
|
audio_row.add_child(_volume_label("Master volume"))
|
||||||
|
_master = _add_slider(audio_row, 0.0, 1.0, 0.05)
|
||||||
|
audio_row.add_child(_volume_label("SFX"))
|
||||||
|
_sfx = _add_slider(audio_row, 0.0, 1.0, 0.05)
|
||||||
|
audio_row.add_child(_volume_label("Ambience"))
|
||||||
|
_ambience = _add_slider(audio_row, 0.0, 1.0, 0.05)
|
||||||
|
_master.value_changed.connect(func(v): settings["master_volume"] = v)
|
||||||
|
_sfx.value_changed.connect(func(v): settings["sfx_volume"] = v)
|
||||||
|
_ambience.value_changed.connect(func(v): settings["ambience_volume"] = v)
|
||||||
|
|
||||||
|
_reduce_motion = CheckButton.new()
|
||||||
|
_reduce_motion.text = "Reduce motion"
|
||||||
|
_reduce_motion.toggled.connect(func(v): settings["reduce_motion"] = v)
|
||||||
|
column.add_child(_reduce_motion)
|
||||||
|
_captions = CheckButton.new()
|
||||||
|
_captions.text = "Captions / subtitles"
|
||||||
|
_captions.toggled.connect(func(v): settings["captions_enabled"] = v)
|
||||||
|
column.add_child(_captions)
|
||||||
|
|
||||||
var replay_row := HBoxContainer.new()
|
var replay_row := HBoxContainer.new()
|
||||||
replay_row.alignment = BoxContainer.ALIGNMENT_CENTER
|
replay_row.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||||
column.add_child(replay_row)
|
column.add_child(replay_row)
|
||||||
@@ -165,6 +191,13 @@ func _add_label_row(parent: Control, text: String) -> Label:
|
|||||||
row.add_child(value)
|
row.add_child(value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
func _volume_label(text: String) -> Label:
|
||||||
|
var label := Label.new()
|
||||||
|
label.text = text
|
||||||
|
label.add_theme_font_size_override("font_size", 11)
|
||||||
|
label.custom_minimum_size = Vector2(82, 0)
|
||||||
|
return label
|
||||||
|
|
||||||
func _add_slider(parent: Control, min_v: float, max_v: float, step: float) -> HSlider:
|
func _add_slider(parent: Control, min_v: float, max_v: float, step: float) -> HSlider:
|
||||||
var slider := HSlider.new()
|
var slider := HSlider.new()
|
||||||
slider.min_value = min_v
|
slider.min_value = min_v
|
||||||
@@ -189,6 +222,16 @@ func open(current: Dictionary) -> void:
|
|||||||
if _difficulty:
|
if _difficulty:
|
||||||
var difficulty := str(settings.get("ai_difficulty", "normal"))
|
var difficulty := str(settings.get("ai_difficulty", "normal"))
|
||||||
_difficulty.select(["easy", "normal", "hard"].find(difficulty))
|
_difficulty.select(["easy", "normal", "hard"].find(difficulty))
|
||||||
|
if _master:
|
||||||
|
_master.set_value_no_signal(float(settings.get("master_volume", 1.0)))
|
||||||
|
if _sfx:
|
||||||
|
_sfx.set_value_no_signal(float(settings.get("sfx_volume", 1.0)))
|
||||||
|
if _ambience:
|
||||||
|
_ambience.set_value_no_signal(float(settings.get("ambience_volume", 1.0)))
|
||||||
|
if _reduce_motion:
|
||||||
|
_reduce_motion.set_pressed_no_signal(bool(settings.get("reduce_motion", false)))
|
||||||
|
if _captions:
|
||||||
|
_captions.set_pressed_no_signal(bool(settings.get("captions_enabled", true)))
|
||||||
_listening_action = ""
|
_listening_action = ""
|
||||||
_refresh_bind_buttons()
|
_refresh_bind_buttons()
|
||||||
_sync_labels()
|
_sync_labels()
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ extends SceneTree
|
|||||||
const NpcAvatar := preload("res://src/game/npc_avatar.gd")
|
const NpcAvatar := preload("res://src/game/npc_avatar.gd")
|
||||||
const NpcProfile := preload("res://src/core/npc_profile.gd")
|
const NpcProfile := preload("res://src/core/npc_profile.gd")
|
||||||
const Rules := preload("res://src/core/rules.gd")
|
const Rules := preload("res://src/core/rules.gd")
|
||||||
const COUNT := 16
|
const COUNT := 24
|
||||||
const WARMUP_FRAMES := 60
|
const WARMUP_FRAMES := 60
|
||||||
const SAMPLE_FRAMES := 180
|
const SAMPLE_FRAMES := 180
|
||||||
const BUDGET_MS := 50.0
|
const BUDGET_MS := 50.0
|
||||||
|
|||||||
@@ -66,6 +66,18 @@ func test_radio_crackle_methods() -> bool:
|
|||||||
audio.free()
|
audio.free()
|
||||||
return a and b
|
return a and b
|
||||||
|
|
||||||
|
func test_volume_settings_are_stored_and_applied() -> bool:
|
||||||
|
var audio: Node = AudioFeedback.new()
|
||||||
|
audio.set_volumes(0.8, 0.5, 0.2)
|
||||||
|
var a := TestUtils.check(is_equal_approx(audio.master_volume, 0.8), "master volume stores")
|
||||||
|
var b := TestUtils.check(is_equal_approx(audio.sfx_volume, 0.5), "sfx volume stores")
|
||||||
|
var c := TestUtils.check(is_equal_approx(audio.ambience_volume, 0.2), "ambience volume stores")
|
||||||
|
audio.set_volumes(3.0, -1.0, 0.5)
|
||||||
|
var d := TestUtils.check(is_equal_approx(audio.master_volume, 1.0)
|
||||||
|
and is_equal_approx(audio.sfx_volume, 0.0), "volumes clamp")
|
||||||
|
audio.free()
|
||||||
|
return a and b and c and d
|
||||||
|
|
||||||
func test_game_manager_wires_audio() -> bool:
|
func test_game_manager_wires_audio() -> bool:
|
||||||
var src := FileAccess.get_file_as_string("res://src/game/game_manager.gd")
|
var src := FileAccess.get_file_as_string("res://src/game/game_manager.gd")
|
||||||
var a := TestUtils.check(src.contains("audio.start_crowd_bed()"), "the bed starts with the shift")
|
var a := TestUtils.check(src.contains("audio.start_crowd_bed()"), "the bed starts with the shift")
|
||||||
|
|||||||
@@ -81,7 +81,9 @@ func test_settings_defaults_present() -> bool:
|
|||||||
var ok := TestUtils.check(
|
var ok := TestUtils.check(
|
||||||
defaults.has("look_sensitivity") and defaults.has("hud_font_scale")
|
defaults.has("look_sensitivity") and defaults.has("hud_font_scale")
|
||||||
and defaults.has("invert_y") and defaults.has("head_bob")
|
and defaults.has("invert_y") and defaults.has("head_bob")
|
||||||
and defaults.has("input_bindings"),
|
and defaults.has("input_bindings")
|
||||||
|
and defaults.has("master_volume") and defaults.has("sfx_volume")
|
||||||
|
and defaults.has("ambience_volume") and defaults.has("captions_enabled"),
|
||||||
"settings have documented defaults")
|
"settings have documented defaults")
|
||||||
save.free()
|
save.free()
|
||||||
return ok
|
return ok
|
||||||
Reference in new issue
Block a user