Gate random encounters on the 'Special Delivery' quest claim

- Add `_encountersLockedByQuest()` to GameScene: while the delivery
  chain is live (not yet completed AND claimed), keep the encounter
  director from rolling. Quests disabled = gate off too.
- AND the new scene-side hold into `encounters.update`'s `allowed` so
  the pure clock stays untouched; document the contract in Encounters.js.
- Ship new transport sprite assets (ships-transport.png/.psd).
This commit is contained in:
Brian Fertig 2026-09-22 20:48:22 -06:00
parent 81a1589307
commit 6f77ada8ad
4 changed files with 25 additions and 4 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

View File

@ -8,8 +8,12 @@
*
* WHEN — while the system allows encounters (NEUTRAL — faction null —,
* not home, no wave run in flight, the player upright; the scene
* passes `ctx.allowed`, `encounterAllowed` is the pure gate), a
* ROLL lands on the interval schedule (data/combat.json →
* passes `ctx.allowed`, `encounterAllowed` is the pure gate) AND the
* scene's own quest hold stands off — GameScene ANDs in
* `_encountersLockedByQuest`, the 'Special Delivery' gate: the rolls
* stay off until that quest is completed and claimed (a scene-side
* state, so it never crosses this module's pure contract). A roll then
* lands on the interval schedule (data/combat.json →
* encounters.rollIntervalMin..rollIntervalMax, seconds); each roll
* fires at chanceMin..chanceMax probability. ONE AT A TIME: the
* schedule holds while an encounter is engaged.

View File

@ -2621,7 +2621,8 @@ export class GameScene extends Phaser.Scene {
// returns the composition to spawn — off-screen, streamed in by
// their own AI (the clock is pure; the scene stays the spawner).
const comp = this.encounters.update(_time, {
allowed: encounterAllowed({
allowed:
encounterAllowed({
ship: this.ship,
isHomeSystem: this.isHomeSystem,
faction: this.systemRecord ? this.systemRecord.faction : null,
@ -2630,7 +2631,7 @@ export class GameScene extends Phaser.Scene {
this.combat.waveRun.state !== 'done' &&
this.combat.waveRun.state !== 'cancelled',
playerDown: this.combat.playerDown,
}),
}) && !this._encountersLockedByQuest(), // the 'Special Delivery' hold
live: this.combat.active,
xpLevel: this.combat.xp.level,
});
@ -3754,6 +3755,22 @@ export class GameScene extends Phaser.Scene {
return cap !== '' && cap === name;
}
/**
* The random-encounter hold (the 'Special Delivery' gate): the run's
* first flights into neutral space stay calm while the delivery chain
* is live — the director rolls (EncounterDirector, the pure clock)
* only once 'Special Delivery' is COMPLETED AND CLAIMED (the same
* witness the run's chain uses — QuestState.isClaimed; the claim is
* the scene's own, paid out only on a fully satisfied quest). Quests
* off (the config kill-switch, data/game.json) = the gate is off too:
* with no quest there is nothing to wait on.
* @returns {boolean} true while random encounters stay locked
*/
_encountersLockedByQuest() {
if (config.get('quests.enabled', true) === false) return false;
return this.questState?.isClaimed?.('special_delivery') !== true;
}
/**
* LAND (commsAction) — the surface sequence: SurfaceScene starts ON
* TOP of this scene, handed the world's planets.png sheet frame so it