I am trying to replicate the game mechanic of displaying the complete map of a region when the player gets their hands on a map, like in early survival horror games.
Here is how I managed it.
Please note that my method uses the taking of the item, but this is also doable with the examine command and a flag in the item.
Step 1 - Your map item
createItem("b1Map", TAKEABLE(), {
loc: "cellA",
alias: "basement map",
examine: "A map of the basement. It is labeled \"B1 - Detention\"",
afterMove: function(toLoc, fromLoc) {
msg("You note the locations.")
map.update();
}
})
Step 2 - Setting preparations
settings.mapShowNotVisited = false
settings.mapGetStartingLocations = function() {
const start1 = w.cellA
start1.mapX = 0
start1.mapY = 0
start1.mapZ = 0
start1.mapRegion = 0
return [start1]
}
settings.maps = {0 : "b1Map"}
settings.maps is a dictionary with the mapRegion number as key, and the name of the map item as value.
Step 3 - Customize mapUpdate to check for the map
// Draw the map
// It collects all the SVG in five lists, which are effectively layers.
// This means all the exits appear in one layer, all the labels in another
// and so labels are always on top of exits
map.update = function() {
// grab the current room region and level. If the room is missing either, give up now!
const level = w[player.loc].mapZ
const region = w[player.loc].mapRegion
if (level === undefined || region === undefined) return
if (w[player.loc].mapIgnore) return
// Search for a map item corresponding to the current region.
const mapItem = w[settings.maps[region]];
// Stuff gets put in any of several layers, which will be displayed in this order
const lists = {}
for (let el of map.layers) lists[el.name] = ['', '<g id="otherLevels-layer" ' + el.attrs + '>']
// Loop through every room
for (let key in w) {
const room = w[key]
// Do not map if in another region (if region is true, the room can handle it)
// Only show if visited unless mapShowNotVisited or the player has the map of the region
if (room.mapRegion !== region && room.mapRegion !== true) continue
if ((mapItem === undefined || mapItem.loc !== player.name) && !settings.mapShowNotVisited && !room.visited) continue
// Call mapDraw on the room if it has that, otherwise the default version
(room.mapDraw ? room : map).mapDraw(lists, region, level, room)
}
// Add it all together
const result = settings.mapDefs ? settings.mapDefs() : []
for (let key in lists) {
for (let el of lists[key]) result.push(el)
result.push('</g>')
}
//console.log(result)
if (settings.mapExtras) result.push(...settings.mapExtras())
result.push(settings.mapMarker ? settings.mapMarker(w[player.loc]) : map.marker(w[player.loc].mapX, w[player.loc].mapY))
// Centre the view on the player, and draw it
const x = w[player.loc].mapX - settings.mapWidth/2
const y = -settings.mapHeight/2 + w[player.loc].mapY
draw(settings.mapWidth, settings.mapHeight, result, {destination:'quest-map', x:x, y:y})
}
Why am I using a specific dictionary ? Because iterating over the keys of a dictionary is time-consuming, while getting a specific value with the corresponding key is near-instantaneous. So we spare ourselves from doing two loops inside the world dictionary.