diff --git a/data/sidequests.json b/data/sidequests.json index e2369ac..906ae84 100644 --- a/data/sidequests.json +++ b/data/sidequests.json @@ -200,7 +200,7 @@ } }, "escort": { - "_comment": "The ESCORT category's rules (the third live type — the model: js/quests/SideQuests.js → escortDestinationFor / deriveEscortPlan; the ship: js/combat/TransportShip.js + data/combat.json → transport; the scene: GameScene._establishEscort + updateEscort). An escort is a CONVOY JOB: a neutral TRANSPORT (its own ship class — ~2.5x the fighter's size, far heavier armor, a heavy lance) plus 1–2 escort fighters stands at a single RENDEZVOUS waypoint in the posting's own system (waypoints [1,1] — the route builder's shared placement law, the 'escort' fork). The player flies to the waypoint within visitRadius and the convoy LINKS (phase 'rendezvous' → 'transit') — from then on the transport + fighters hold their escort ring around the player through EVERY remaining leg, re-forming across jumps (the route record carries the transport's armor — a signed contract is a signed contract: a jump or a save never re-rolls a standing convoy). The DESTINATION is a planet or a free-space station 1–2 gate hops away (destinationHops [min,max], a seeded walk of the jump network — js/galaxy/JumpTravel.js's gate graph, the same links the player can actually fly), fixed at the accept and never re-rolled; the player flies the legs (1–2 jumps) and arrives at the destination object with the convoy intact. ENCOUNTERS — the lane is HOT: while the convoy is linked, the escort director (its own EncounterDirector instance with these tighter numbers — a shorter roll interval, a near-certain chance, a short quiet) fires WAVES of mixed fighter types (bands, by the player's combat level — a weighted roster of enemyTypes ids, validated like the patrol's) with LARGER concurrent counts than a normal encounter (wave.total / maxConcurrent) — one at a time, the wave run's completion the quiet's gate, exactly the patrol contact's law. The TRANSPORT is the job: it fights alongside (its own lance, js/combat/TransportShip.js), it is a legal enemy target (the allies roster — pickEnemyTarget's escort aggro), and if it is DESTROYED the contract fails (phase 'failed', the convoy breaks off) — the player must LAND ON A PLANET to reset it (GameScene.startLanding's escort reset — a fresh rendezvous, a fresh transport, the same destination: retry the run). COMPLETION — the destination world (planet/free-space station) is VISIBLE ON SCREEN (circleInView against the camera's world rect — the player has flown it into view, not merely near it) with NO enemy fighter up (the craftVisible roster rule — the bounty field-clear's law): the contract enters its DELIVERY (phase 'delivering') — the convoy BREAKS OFF the wing, flies in beside the world, and FADES OUT (the touchdown: the transport + its fighters land, they are through, not destroyed; a fixed time-driven fly-in then fade, GameScene._runEscortDelivery), and on the fade's close the contract completes, the reward stands ready to claim. compass + marker = the escort's in-game face: the green next-stop arrow (the RENDEZVOUS waypoint pre-link, then the DESTINATION gate, then the destination object itself) and the in-world beacons (rendezvous = pulsing until linked, then the destination object's mark).", + "_comment": "The ESCORT category's rules (the third live type — the model: js/quests/SideQuests.js → escortDestinationFor / deriveEscortPlan; the ship: js/combat/TransportShip.js + data/combat.json → transport; the scene: GameScene._establishEscort + updateEscort). An escort is a CONVOY JOB: a neutral TRANSPORT (its own ship class — ~2.5x the fighter's size, far heavier armor, a heavy lance) plus 1–2 escort fighters stands at a single RENDEZVOUS waypoint in the posting's own system (waypoints [1,1] — the route builder's shared placement law, the 'escort' fork). The player flies to the waypoint within visitRadius and the convoy LINKS (phase 'rendezvous' → 'transit') — from then on the transport + fighters hold their escort ring around the player through EVERY remaining leg, re-forming across jumps (the route record carries the transport's armor — a signed contract is a signed contract: a jump or a save never re-rolls a standing convoy). The DESTINATION is a planet or a free-space station 1–2 gate hops away (destinationHops [min,max], a seeded walk of the jump network — js/galaxy/JumpTravel.js's gate graph, the same links the player can actually fly), fixed at the accept and never re-rolled; the player flies the legs (1–2 jumps) and arrives at the destination object with the convoy intact. ENCOUNTERS — the lane is HOT: while the convoy is linked, the escort director (its own EncounterDirector instance — a short roll interval every 20–30 s at a middling chance, a long quiet) fires WAVES of mixed fighter types (bands, by the player's combat level — a weighted roster of enemyTypes ids, validated like the patrol's) with LARGER concurrent counts than a normal encounter (wave.total / maxConcurrent) — one at a time, the wave run's completion the quiet's gate, exactly the patrol contact's law. The TRANSPORT is the job: it fights alongside (its own lance, js/combat/TransportShip.js), it is a legal enemy target (the allies roster — pickEnemyTarget's escort aggro), and if it is DESTROYED the contract fails (phase 'failed', the convoy breaks off) — the player must LAND ON A PLANET to reset it (GameScene.startLanding's escort reset — a fresh rendezvous, a fresh transport, the same destination: retry the run). COMPLETION — the destination world (planet/free-space station) is VISIBLE ON SCREEN (circleInView against the camera's world rect — the player has flown it into view, not merely near it) with NO enemy fighter up (the craftVisible roster rule — the bounty field-clear's law): the contract enters its DELIVERY (phase 'delivering') — the convoy BREAKS OFF the wing, flies in beside the world, and FADES OUT (the touchdown: the transport + its fighters land, they are through, not destroyed; a fixed time-driven fly-in then fade, GameScene._runEscortDelivery), and on the fade's close the contract completes, the reward stands ready to claim. compass + marker = the escort's in-game face: the green next-stop arrow (the RENDEZVOUS waypoint pre-link, then the DESTINATION gate, then the destination object itself) and the in-world beacons (rendezvous = pulsing until linked, then the destination object's mark).", "enabled": true, "minLevel": 3, "contexts": [ @@ -224,14 +224,14 @@ 3 ], "encounters": { - "_comment": "The escort's LANE COMBAT (the director's numbers + the wave's shape + the roster's bands): rollIntervalMin/Max + chanceMin/Max + cooldownMin/Max are the EscortDirector's own EncounterDirector config (data/combat.json → encounters' shape — seconds); wave = one rolling wave of `total` mixed fighters, `maxConcurrent` on screen at a time, each kill replaced (replacePerKill) — the patrol contact's rolling shape, bigger; bands = the roster by the player's COMBAT level (levels [lo,hi], roster = a weighted { type: weight } table over combat.enemyTypes ids — the unknown-id rule is the patrol's: a type that isn't in combat.enemyTypes never ships, a roster that empties takes the established type); no band standing → the fallback band's roster.", + "_comment": "The escort's LANE COMBAT (the director's numbers + the wave's shape + the roster's bands): rollIntervalMin/Max + chanceMin/Max + cooldownMin/Max are the EscortDirector's own EncounterDirector config (data/combat.json → encounters' shape — seconds), tuned to a HOT lane: a short 20–30 s roll interval × a middling chance, a 15–30 s quiet after each fight — frequent engagements while the convoy is linked (roughly 2–8 per run, scaling with how long the legs take); wave = one rolling wave of `total` mixed fighters, `maxConcurrent` on screen at a time, each kill replaced (replacePerKill) — the patrol contact's rolling shape, bigger; bands = the roster by the player's COMBAT level (levels [lo,hi], roster = a weighted { type: weight } table over combat.enemyTypes ids — the unknown-id rule is the patrol's: a type that isn't in combat.enemyTypes never ships, a roster that empties takes the established type); no band standing → the fallback band's roster.", "enabled": true, - "rollIntervalMin": 9, - "rollIntervalMax": 18, - "chanceMin": 0.85, - "chanceMax": 1, - "cooldownMin": 5, - "cooldownMax": 10, + "rollIntervalMin": 20, + "rollIntervalMax": 30, + "chanceMin": 0.4, + "chanceMax": 0.6, + "cooldownMin": 15, + "cooldownMax": 30, "wave": { "kind": "rolling", "total": 8, diff --git a/js/scenes/GameScene.js b/js/scenes/GameScene.js index aff2c2d..bc6ec6d 100644 --- a/js/scenes/GameScene.js +++ b/js/scenes/GameScene.js @@ -1130,9 +1130,9 @@ export class GameScene extends Phaser.Scene { // AI streams them in). this.encounters = new EncounterDirector(config.get('combat.encounters', {})); // THE ESCORT LANE'S DIRECTOR (data/sidequests.json → escort.encounters — - // tighter numbers than the normal director: a shorter roll interval, - // a near-certain chance, a short quiet). It owns no ships either: on a - // hit it's just a "go", and updateEscort fires the escort's mixed-fighters + // a HOT lane: a short 20–30 s roll interval at a middling chance, a + // 15–30 s quiet after each fight). It owns no ships either: + // on a hit it's just a "go", and updateEscort fires the escort's mixed-fighters // wave (escortWaveRoster's roster, bigger concurrent counts). One at a // time (the wave run's completion is the quiet's gate — the patrol's law). this.escortDirector = new EncounterDirector(config.get('sidequests.escort.encounters', {}));