Hex Maps - ThePix/QuestJS GitHub Wiki
Quest 6 allows you to add a hex map - that is a map where each cell is a hexagon. These are popular in more recent strategy games, so not an obvious fit for a text adventure, but the facilities are there! No reason why you could not add a strategy game element to your text adventure. In my opinion, this works best for a relatively small-scale map, where each hexagon is maybe a mile across or even bigger, and you are mapping out a large wilderness area.
Coordinate System
There are various ways to draw and arrange a hex map; Quest has a specific system. Each hexagon is lying on its side, and is identified by its x and y coordinates, with x increasing as you go to the right, and rising, while y increases as you go up the map.
Setting up
By default, the map library is not included in a Quest 6 game (though the file is present). The first step, then, is to tell Quest to include this library. To do that, add this line to your settings.js file (and indeed all options that start "settings" should go in that file):
settings.libraries.push('hex-map')
There are various options you can use to control the map. These are the basics.
settings.mapHexStroke = 'grey'
settings.mapDefaultColour = 'lightgrey'
settings.mapXOffset1 = 12
settings.mapXOffset2 = 25
settings.mapYOffset = 20
settings.mapStyle = {
right:'0',
top:'0',
width:'390px',
height:'400px',
border:'3px black solid',
}
The mapYOffset is the number of pixels from the centre of a hexagon to the flat side above and below it; mapXOffset1 is the number of pixels from the centre of a flat side to the end of the flat side; mapXOffset2 is the number of pixels from the centre of a hexagon to the corner on either side.
The mapDefaultColour is the default colour of a hex that is not an actual location, while mapHexStroke is the colour of the outlines.
The mapStyle sets the map itself, its size and place on the screen, plus a border.
Hex locations
A hex only exists if you create a special location for it. In the image above, although the map is filled with hexes, the only ones that actually exist - that the player can get too - are the three coloured ones.
Use the createHex
function to do that, passing it the x and y coordinates. This example also sets the colour of the hex. Note that inside createHex
the createRoom
function will be used; this is creating a special room, and most of what applies to a room also applies to a hex - the data here is the same as for a room, but with some extra options.
createHex(4, 1, {
desc:"The middle of the desert.",
getCellColour:function() { return 'yellow' },
})
Quest will automatically create exits to adjacent hexes, if they have rooms (it does this on the fly, i.e., when the player uses the exit, discarding it afterwards). You can add your own too, as shown here:
createHex(5, 1, {
desc:"A grassy hill with a tower on it.",
getCellColour:function() { return 'green' },
in:new Exit('tower'),
})
Quest generates special names for these rooms, based on the coordinates. To ensure exits going to a hex get the name right, use the map.coordToCellName
function, as shown here. This will return the player to the correct hex.
createRoom("tower", {
desc:'In a tower',
out:new Exit(map.coordToCellName(5, 1)),
})
Note that the map does not get updated when the player is in a location that is not a hex. Generally this will be fine; the old map is still displayed, while the player is in the tower. This can cause problems in two situations. If the player goes to another location on another hex, the map will not update. Perhaps there is a portal to another tower the other side of the continent. After using it, the map will still show the location of the first tower. The other situation is when the player loads a saved game, saved in a location that is not a hex; Quest will continue to show the original map. Whether this is a big deal is up to you to decide.
Options for locations
As seen above, you can give your hex a colour. You can also give it a border, a symbol and a label. There are up to six border functions you can add to your location, numbered from 0 to 5, with 0 being the top border, and counting clockwise from there. The function should return a colour or a dictionary.
If it just returns a colour, the width used is settings.mapHexStrokeWidth
, while the dictionay allows you to set the width and cap used to terminate the line too. The function will be sent the side number in case that is useful.
getHexBorder0:function() { return 'blue' },
getHexBorder2:function(side) {
return {
stroke:'blue',
'stroke-width':10,
'stroke-linecap':'round',
}
},
This example uses the border feature to make the hexagon appear raised.
getHexColour:function() { return 'red' },
getHexBorder0:function() { return 'pink' },
getHexBorder1:function() { return 'red' },
getHexBorder2:function() { return 'darkred' },
getHexBorder3:function() { return 'darkred' },
getHexBorder4:function() { return 'red' },
getHexBorder5:function() { return 'pink' },
Creating a label; the function just returns the text.
getHexLabel:function() { return 'Desert' },
Labels will be centred, but you can adjust the vertical position with settings.mapLabelOffset, the text colour with settings.mapLabelColour and, should you want to, the rotation with settings.mapLabelRotate
settings.mapLabelColour = 'blue'
settings.mapLabelOffset = 15
settings.mapLabelRotate = -20
You can also add a symbol. This should be a small image file, I would recommend in PNG format. You can set the position within the hex with "getHexSymbolOffset", relative to the centre of the hex.
getHexSymbol:function() { return 'assets/icons/houseicon.png' },
getHexSymbolOffset:function() { return [-8, -8] },
Quest automatically creates exits between adjacent hexes, but you can stop the player accessing another hex from the current one by giving it a prohibiting flag. This example stops the player going northeast from this hex. It does not stop the playing going the reverse, travelling to this hex from the one to the northeast.
northeastProhibited:true,
Responding to Mouse Clicks
If the player clicks on the map, you can capture that, and have the game react as you like. This example just reports which cell was clicked to the console, but you could give the player information about the region, or teleport her straight there.
settings.mapClick = function(x, y) {
log(x + ',' + y)
}
Biome hexes
One hex is generally going to be used to represent a large chunk of land that is assumed to be of the same type - forest, marsh, etc. It is therefore often convenient to associate your hex with a biome. The advantage of doing this is that you set up the biome once and apply it to several hexes.
First, then, we set up the biomes. Each is identified by a single character, and has a name and colour.
settings.mapBiomes = {
G:{name:'grassland', colour:'palegreen',},
S:{name:'savanna', colour:'yellowgreen',},
D:{name:'deciduous forest', colour:'olivedrab',},
C:{name:'coniferous forest', colour:'darkgreen',},
R:{name:'rain forest', colour:'olive',},
M:{name:'marsh', colour:'teal',},
C:{name:'chaparral', colour:'burlywood',},
H:{name:'heath', colour:'sandybrown',},
s:{name:'sea', colour:'powderblue',},
m:{name:'mountains', colour:'darkgrey',},
l:{name:'lavafield', colour:'orange',},
d:{name:'desert', colour:'khaki',},
t:{name:'tundra', colour:'blanchedalmond',},
a:{name:'artic', colour:'white',},
r:{name:'reef', colour:'pink',},
}
Creating a biome hex is then simply a case of using the createBiome
function, and sending the relevant character as well as the other data.
createBiome(5, -4, 'G', {
desc:"There is a tower here.",
getHexSymbol:function() { return 'assets/icons/houseicon.png' },
getHexSymbolOffset:function() { return [-8, -8] },
in:new Exit('tower'),
})
Where this really excels is when you want to create a large map. The map.generate
function will create a large number of biome hexes in one go. The first two parameters position the hexes on the map, then there is an array of strings. Each string is a rising row of hexes, and each character is one hex along that row. Spaces are used to align the rows, but will not lead to hexes in those positions. Note also the space in the last-but-three row; this is where the hex we created before will be.
map.generate(-5, 8, [
' ss ',
' sssms ',
' sSSGms',
' ssGGGms',
' sGGGmms',
'ssddGGms',
'sdddGGms',
'sdddGGGms',
'sddddGGms',
' sddddGmss',
' sdddmssms',
' sddmss ms',
' smmsssss',
' smss',
' ss',
])
Here is the result:
You can add a river to a hex using map.river
. It should be sent the x and y coordinates, plus the width of each side in turn, starting from the top and going clockwise. I suggest only adding the river to the first three sides of each hex, but you can add it to all six if desired.
map.river(-1, 4, 5, 5)
map.river(0, 5, 0, 2, 3)
map.river(0, 4, 0, 3, 3)
map.river(0, 3, 5, 4)