Skip to content
Open
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
50 changes: 48 additions & 2 deletions lib/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -1163,7 +1163,52 @@ const DUNGEON_ROWS = [
'#..........................................................#',
'#..........................~~~~~~...........................#',
'############################################################'
].map((row) => row.padEnd(62, '#'))
// The ruins were drawn a few columns short on the right and every row ended
// at a different wall column; padEnd used to hide that from defineMap()'s
// rectangularity check (#10). Normalizing to one fixed width gives the ruins
// a straight east wall, so the check means something again.
].map((row) => (row + '###').slice(0, 60) + '#')
Comment on lines +1166 to +1170

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Quality: Row normalization still hides ragged art from the check

.map((row) => (row + '###').slice(0, 60) + '#') forces every dungeon row to exactly 61 chars, so defineMap()'s rectangularity check can never throw for the dungeon — the same masking behavior the comment criticizes about the old padEnd, not a fix for it. It also silently drops the rightmost column of any row longer than 61 (rows here are 62 wide, so their real east-wall column is discarded), and 60-wide rows gain a redundant double wall, leaving the interior right edge jagged by one cell despite the 'straight east wall' claim. Consider fixing the source art to a uniform width and letting defineMap() validate it, rather than reshaping rows at load time.

Was this helpful? React with 👍 / 👎


/**
* The ruins are not empty anymore: three residents camp different chambers,
* reusing the exact NPC machinery the city already runs (nearbyNpc header,
* e-to-talk, interactNpc dialogue) - issue #10's minimum fix.
*/
const DUNGEON_NPCS = [
{
id: 'eco',
name: 'Eco de las ruinas',
role: 'vidente',
x: 10,
y: 7,
sprite: NPC_SPRITES.resident,
anchorY: NPC_SPRITES.resident.length - 1,
color: 'cyan',
line: 'los muros recuerdan mas pasos que los vivos'
},
{
id: 'tadeo',
name: 'Tadeo',
role: 'minero',
x: 30,
y: 13,
sprite: NPC_SPRITES.resident,
anchorY: NPC_SPRITES.resident.length - 1,
color: 'yellow',
line: 'la V fue salida, no entrada: alguien selló el resto'
},
{
id: 'gata',
name: 'Gata de las ruinas',
role: 'gato',
x: 40,
y: 17,
sprite: NPC_SPRITES.resident,
anchorY: NPC_SPRITES.resident.length - 1,
color: 'gray',
line: 'miau'
}
]

/**
* Turn art plus metadata into a map, checking the art is rectangular.
Expand Down Expand Up @@ -1212,7 +1257,8 @@ const MAPS = {
name: 'las ruinas bajo el castillo',
rows: DUNGEON_ROWS,
spawn: { x: 3, y: 2 },
arrive: { x: 3, y: 2 }
arrive: { x: 3, y: 2 },
npcs: DUNGEON_NPCS
}),
coliseum: defineMap({
id: 'coliseum',
Expand Down
49 changes: 49 additions & 0 deletions test/dungeon-content.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const { test } = require('brittle')
const { Runa } = require('../lib/game.js')
const M = require('../lib/map.js')

function press(game, name) {
return game.onKey({ type: 'key', is: (...keys) => keys.includes(name) })
}

function startGame(game, name = 'Tomas') {
press(game, 'enter')
for (const ch of String(name)) {
game.onKey({ type: 'key', sequence: ch, ctrl: false, meta: false, is: () => false })
}
press(game, 'enter')
}

test('the ruins host three residents on walkable ground (#10)', (t) => {
const npcs = M.MAPS.dungeon.npcs
t.ok(Array.isArray(npcs) && npcs.length >= 3, 'dungeon defines its own residents')
for (const n of npcs) {
t.ok(!M.isSolid(M.MAPS.dungeon, n.x, n.y), `${n.name} stands on open floor at ${n.x},${n.y}`)
}
})

test('the east wall of the ruins is one straight line now (#10)', (t) => {
const city = M.MAPS.dungeon
const w = city.width
let aligned = true
for (let y = 0; y < city.height; y++) {
if (city.rows[y].length !== w || city.rows[y][w - 1] !== '#') aligned = false
if (!M.isSolid(city, w - 1, y)) aligned = false
}
t.is(w, 61, 'normalized to a single declared width')
t.ok(aligned, 'column 60 is wall on every row - no more jagged mouth')
})

test('talking inside the ruins reaches a resident instead of void (#10)', (t) => {
const game = new Runa({ presence: false })
startGame(game)

const npc = M.MAPS.dungeon.npcs[0]
game.walker.placeAt('dungeon', npc.x + 1, npc.y)
press(game, 'e')

t.ok(
!game.log.some((line) => String(line).includes('aca no hay nada')),
'the resident answers instead of the void'
)
})
1 change: 1 addition & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const {
const render = require('../lib/render.js')

require('./sage.test.js')
require('./dungeon-content.test.js')

function press(game, name) {
return game.onKey({ type: 'key', is: (...keys) => keys.includes(name) })
Expand Down
Loading