From 799f44d59e5012d3aedbcadbffc7dc8a9f1d775c Mon Sep 17 00:00:00 2001 From: blippip69 Date: Tue, 25 Aug 2026 20:30:43 +0300 Subject: [PATCH] fix: derive a distinct field seed per excursion (#3) --- lib/game.js | 11 +++++++++-- test/index.js | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/lib/game.js b/lib/game.js index d781969..1975056 100644 --- a/lib/game.js +++ b/lib/game.js @@ -196,6 +196,7 @@ class Runa { this.dungeonReturn = null this.player = new Player() this.field = null + this.excursion = 0 this.shop = null this.cursor = 0 this.title = true @@ -774,7 +775,10 @@ class Runa { this.pending = null if (location.kind === 'field') { - this.field = new Field({ script: this.scriptSource, player: this.player }) + this.excursion++ + const seed = + (this.excursion * 2654435761 + this.player.xp * 40503 + this.player.gold) >>> 0 + this.field = new Field({ script: this.scriptSource, player: this.player, seed }) this.field.player.x = clampNumber(location.x, 0, this.field.width - 1) this.field.player.y = clampNumber(location.y, 0, this.field.height - 1) } else { @@ -1177,7 +1181,10 @@ class Runa { switch (action.kind) { case 'travel': if (action.to === 'field') { - this.field = new Field({ player: this.player }) + this.excursion++ + const seed = + (this.excursion * 2654435761 + this.player.xp * 40503 + this.player.gold) >>> 0 + this.field = new Field({ player: this.player, seed }) this.field.setScript(this.scriptSource) this.say('salis de la ciudad. pulsa t para volver cuando no estes peleando') } else if (action.to === 'dungeon') { diff --git a/test/index.js b/test/index.js index 44d13eb..2676c22 100644 --- a/test/index.js +++ b/test/index.js @@ -1098,6 +1098,40 @@ test('hitbox combat stays on the field and advances on attack input', (t) => { ) }) +test('two excursions are never the same field (#3)', (t) => { + const game = new Runa({ presence: false }) + startGame(game) + + const openField = () => { + const gateTile = { x: 0, y: 0 } + // find the '>' exit and step on it via placeAt + enter + for (let y = 0; y < MAPS.city.rows.length; y++) { + const x = MAPS.city.rows[y].indexOf('>') + if (x !== -1) { + gateTile.x = x + gateTile.y = y + break + } + } + game.walker.placeAt('city', gateTile.x, gateTile.y) + press(game, 'e') + } + + openField() + t.ok(game.field, 'first excursion opens the field') + const first = game.field.seed + + press(game, 't') + t.absent(game.field, 't closes the excursion') + game.player.gold += 7 + + openField() + t.ok(game.field, 'second excursion opens a fresh field') + const second = game.field.seed + + t.not(first, second, 'the seed differs between excursions') +}) + test('t returns from the field to the city outside combat', (t) => { const game = new Runa({ presence: false }) startGame(game)