@2003scape/rsc-landscape
Advanced tools
Comparing version 1.0.0 to 2.0.0
@@ -14,2 +14,9 @@ const fs = require('fs'); | ||
const lumbridge = landscape.sectors[50][50][0]; | ||
const tile = lumbridge.tiles[0][0]; | ||
console.log(tile.colour, tile.getGameCoords()); | ||
const tile2 = landscape.getTileAtGameCoords(126, 1468); | ||
console.log(tile2.getTileDef()); | ||
process.stdout.write(lumbridge.toString(true)); | ||
@@ -19,3 +26,3 @@ fs.writeFileSync(`./sector-lumbridge.png`, lumbridge.toCanvas().toBuffer()); | ||
(async () => { | ||
fs.writeFileSync(`./worldmap.png`, (await landscape.toCanvas({ | ||
fs.writeFileSync('./worldmap.png', (await landscape.toCanvas({ | ||
points: require('./map-points.json'), | ||
@@ -22,0 +29,0 @@ objects: require('./object-locs.json'), |
{ | ||
"name": "@2003scape/rsc-landscape", | ||
"version": "1.0.0", | ||
"version": "2.0.0", | ||
"description": "(de)serialize runescape classic landscape files", | ||
"main": "./src/index.js", | ||
"bin": { | ||
"rsc-landscape": "./src/bin.js" | ||
}, | ||
"repository": { | ||
@@ -25,3 +28,3 @@ "type": "git", | ||
"dependencies": { | ||
"@2003scape/rsc-archiver": "^1.0.1", | ||
"@2003scape/rsc-archiver": "^1.1.0", | ||
"canvas": "^2.6.0", | ||
@@ -31,5 +34,4 @@ "chalk": "^3.0.0", | ||
"mkdirp-promise": "^5.0.1", | ||
"nearest-color": "^0.4.4", | ||
"yargs": "^15.0.2" | ||
} | ||
} |
109
README.md
@@ -23,3 +23,3 @@ # rsc-landscape | ||
$ npm install @2003scape/rsc-landscape | ||
$ npm install @2003scape/rsc-landscape # -g for CLI program | ||
@@ -31,6 +31,7 @@ ## cli usage | ||
Commands: | ||
rsc-landscape generate-map <archives> generate a world map image | ||
rsc-landscape dump-json <archives> dump JSON files of each sector | ||
rsc-landscape pack-json <directory> generate land and maps archives from a | ||
directory of JSON files | ||
rsc-landscape generate-map <archives..> generate world map png | ||
rsc-landscape dump-json <archives..> dump JSON files of each sector | ||
rsc-landscape pack-json <directory> generate land and maps archives from | ||
directory of JSON files | ||
rsc-landscape print-sector <archives..> print coloured sector to terminal | ||
@@ -42,2 +43,7 @@ Options: | ||
$ rsc-landscape generate-map land* maps* -O object-locs.json \ | ||
-p map-points.json -l map-labels.json # generate worldmap.png | ||
$ rsc-landscape generate-map land* maps* --plane 3 -o dungeons.png | ||
$ rsc-landscape print-sector land* maps* -x 50 -y 50 -z 0 -c 2 # lumbridge | ||
## example | ||
@@ -58,2 +64,9 @@ ```javascript | ||
const lumbridge = landscape.sectors[50][50][0]; | ||
const tile = lumbridge.tiles[0][0]; | ||
console.log(tile.colour, tile.getGameCoords()); | ||
const tile2 = landscape.getTileAtGameCoords(126, 1468); | ||
console.log(tile2.getTileDef()); | ||
process.stdout.write(lumbridge.toString(true)); | ||
@@ -63,3 +76,3 @@ fs.writeFileSync(`./sector-lumbridge.png`, lumbridge.toCanvas().toBuffer()); | ||
(async () => { | ||
fs.writeFileSync(`./worldmap.png`, (await landscape.toCanvas({ | ||
fs.writeFileSync('./worldmap.png', (await landscape.toCanvas({ | ||
points: require('./map-points.json'), | ||
@@ -74,13 +87,14 @@ objects: require('./object-locs.json'), | ||
the runescape classic world is separated into sectors, each containing 48x48 | ||
(2304) tiles. each sector has at least 1 file associated with it: | ||
* a `.hei` file in the *land* archive which stores the elevation and colour of | ||
each tile | ||
* a `.dat` file in the *maps* archive which stores the wall and object direction | ||
of each tile | ||
* an optional `.loc` file in the *maps* archive which stores object IDs (used | ||
for the login screen previews) | ||
(2304) tiles. | ||
overworld and dungeon sectors contain both a `.hei` and `.dat` file, sectors | ||
upstairs only contain `.dat` files, and any sector with object locations will | ||
have a `.loc` file. | ||
* `.hei` file in *land* archive which stores elevation and colour of tiles | ||
* `.dat` file in *maps* archive which stores walls and object direction of tiles | ||
* `.loc` file in the *maps* archive which stores object IDs (used for the login | ||
screen previews) | ||
## api | ||
### .terrainColours.integer | ||
array of the original, undarkened 256 colours the client uses for each tile. | ||
array of original, undarkened 256 colours client uses to colour tiles. | ||
@@ -91,11 +105,7 @@ ### .terrainColours.rgb | ||
### .overlays | ||
map of overlay names to overlay IDs. | ||
### .tileOverlays | ||
map of IDs to tile overlay information. | ||
### .overlayColours | ||
map of overlay names corresponding to the colour that should be used to | ||
represent it on a map. | ||
### tile = new Tile({ sector, x, y, ... }) | ||
create a new sector tile. accepts all of the properties below in object. | ||
create new sector tile. accepts all of the properties listed below. | ||
@@ -106,12 +116,12 @@ ### tile.colour | ||
### tile.elevation | ||
number from 0-255 describing the height of the tile. | ||
number from 0-255 describing height of tile. | ||
### tile.direction | ||
number from 0-6 describing the direction objects should face on this tile. | ||
number from 0-6 describing direction objects should face on tile. | ||
### tile.overlay | ||
overlay type index. the corresponding names are stored in `.overlays`. | ||
overlay type index. corresponding names are stored in `.overlays`. | ||
### tile.wall | ||
object with the following potential properties: | ||
object with following potential properties: | ||
@@ -131,3 +141,3 @@ ```javascript | ||
### tile.objectId | ||
store an object here for the login screen previews. | ||
store object here for login screen previews. | ||
@@ -137,13 +147,33 @@ ### tile.populate() | ||
### tile.getTerrainColour() | ||
return base colour of this tile for maps. | ||
### tile.getTileDef() | ||
return object describing attributes of tile's overlay (from | ||
`./res/tile-overlays.json`): | ||
```javascript | ||
{ | ||
name: 'road', | ||
blocked: false, | ||
bridge: false, | ||
indoors: false, | ||
antialias: true, | ||
colour: 'rgb(64, 64, 64)' | ||
} | ||
``` | ||
### tile.getGameCoords() | ||
return `{ x, y }` game uses for this tile. | ||
### sector = new Sector({ x, y, plane, members?, tiles? }) | ||
create a new sector instance. | ||
create new sector instance. | ||
### sector.members | ||
should this be stored in the `.jag` or `.mem`? | ||
store in `.jag` or `.mem` file? | ||
### sector.width | ||
amount of tiles on the x axis (48). | ||
amount of tiles on x axis (48). | ||
### sector.height | ||
amount of tiles on the y axis (48). | ||
amount of tiles on y axis (48). | ||
@@ -159,3 +189,3 @@ ### sector.terrainHeight | ||
sector's tile objects with `sector.populateBuffers()`. these buffers are | ||
encoded + compressed into final file buffers. | ||
encoded + compressed into archives. | ||
@@ -209,3 +239,3 @@ ### sector.wallsDiagonal | ||
### sector.toString(terminal = false) | ||
### sector.toString(terminal = false, colourLevel = -1) | ||
if `terminal` is true, return a nethack-esque terminal rendering of the sector: | ||
@@ -215,6 +245,10 @@ | ||
`colourLevel` describes the | ||
[chalk level of colours to use](https://github.com/chalk/chalk#chalklevel). | ||
`-1` automatically detects the maximum support. | ||
...otherwise just return the name and size of the sector. | ||
### landscape = new Landscape() | ||
generate a new landscape (de)serializer instance. | ||
create new landscape (de)serializer instance. | ||
@@ -230,3 +264,3 @@ ### landscape.loadJag(landBuffer, mapBuffer) | ||
### \*landscape.getPopulatedSectors() | ||
return an iterator of all the non-empty sectors. | ||
return iterator of all the non-empty sectors. | ||
@@ -236,2 +270,5 @@ ### landscape.getSectorNeighbours(x, y, plane) | ||
### landscape.getTileAtGameCoords(x, y) | ||
get the tile at coordinates used in game. | ||
### async landscape.toCanvas({ objects, points, labels }) | ||
@@ -266,5 +303,5 @@ create a world map image from all of the non-empty sectors. | ||
size: 10, // 8 is the smallest in use, while 14 is the largest | ||
align: 'center', | ||
align: 'center' || 'left', | ||
bold: true || undefined, | ||
colour: 'rgb(254, 165, 0)' || undefined | ||
colour: 'rgb(254, 165, 0)' || '#ff00ff' || undefined | ||
} | ||
@@ -271,0 +308,0 @@ ``` |
@@ -54,3 +54,3 @@ #!/usr/bin/env node | ||
'generate-map <archives..>', | ||
'generate a world map image', | ||
'generate world map png', | ||
yargs => { | ||
@@ -62,2 +62,10 @@ yargs.positional('archives', { | ||
yargs.option('plane', { | ||
alias: 'z', | ||
description: 'change map depth (0 = overworld, 1 = upstairs, ' + | ||
'etc.)', | ||
type: 'number', | ||
default: 0 | ||
}); | ||
yargs.option('objects', { | ||
@@ -108,2 +116,4 @@ alias: 'O', | ||
options.plane = argv.plane; | ||
const canvas = await landscape.toCanvas(options); | ||
@@ -127,3 +137,3 @@ await fs.writeFile(argv.output, canvas.toBuffer()); | ||
alias: 'p', | ||
description: 'pretty-print the JSON files', | ||
description: 'pretty-print JSON files', | ||
type: 'boolean', | ||
@@ -164,3 +174,3 @@ default: false | ||
'pack-json <directory>', | ||
'generate land and maps archives from a directory of JSON files', | ||
'generate land and maps archives from directory of JSON files', | ||
yargs => { | ||
@@ -240,3 +250,48 @@ yargs.positional('directory', { | ||
}) | ||
.command( | ||
'print-sector <archives..>', | ||
'print coloured sector to terminal', | ||
yargs => { | ||
yargs.positional('archives', { | ||
description: 'landscape and map .jag and .mem archives', | ||
type: 'array' | ||
}); | ||
yargs.option('x', { | ||
type: 'number', | ||
default: 50 | ||
}); | ||
yargs.option('y', { | ||
type: 'number', | ||
default: 50 | ||
}); | ||
yargs.option('plane', { | ||
alias: 'z', | ||
type: 'number', | ||
default: 0 | ||
}); | ||
yargs.option('colours', { | ||
alias: 'c', | ||
description: 'amount of colours to use, or -1 to autodetect', | ||
type: 'number', | ||
default: -1 | ||
}); | ||
}, | ||
async argv => { | ||
const landscape = new Landscape(); | ||
try { | ||
await parseArchives(argv, landscape); | ||
const sector = landscape.sectors[argv.x][argv.y][argv.plane]; | ||
process.stdout.write(sector.toString(true, argv.colours) + | ||
'\n'); | ||
} catch (e) { | ||
process.errorCode = 1; | ||
console.error(e); | ||
} | ||
}) | ||
.demandCommand() | ||
.argv; |
@@ -1,9 +0,5 @@ | ||
const Landscape = require('./landscape'); | ||
const Tile = require('./tile'); | ||
const overlayColours = require('./overlay-colours'); | ||
const overlays = require('./overlays'); | ||
module.exports.Landscape = Landscape; | ||
module.exports.Tile = Tile; | ||
module.exports.overlayColours = overlayColours; | ||
module.exports.overlays = overlays; | ||
module.exports.Landscape = require('./landscape'); | ||
module.exports.Sector = require('./sector'); | ||
module.exports.Tile = require('./tile'); | ||
module.exports.key = require('../res/key'); | ||
module.exports.tileOverlays = require('../res/tile-overlays'); |
@@ -17,4 +17,4 @@ const MapPainter = require('./map-painter'); | ||
this.minRegionX = null; | ||
this.minRegionY = null; | ||
this.minRegionX = 48; | ||
this.minRegionY = 37; | ||
this.maxRegionX = null; | ||
@@ -70,5 +70,5 @@ this.maxRegionY = null; | ||
parseArchives() { | ||
for (let plane = 0; plane < MAX_PLANES; plane+= 1) { | ||
for (let y = 37; y < MAX_Y_SECTORS; y+= 1) { | ||
for (let x = 48; x < MAX_X_SECTORS; x+= 1) { | ||
for (let plane = 0; plane < MAX_PLANES; plane += 1) { | ||
for (let y = this.minRegionY; y < MAX_Y_SECTORS; y += 1) { | ||
for (let x = this.minRegionX; x < MAX_X_SECTORS; x += 1) { | ||
const sector = new Sector({ x, y, plane }); | ||
@@ -106,15 +106,7 @@ const entry = sector.getEntryName(); | ||
if (!this.minRegionX || x < this.minRegionX) { | ||
this.minRegionX = x; | ||
} | ||
if (!this.minRegionX || x > this.maxRegionX) { | ||
if (x > this.maxRegionX) { | ||
this.maxRegionX = x; | ||
} | ||
if (!this.minRegionY || y < this.minRegiony) { | ||
this.minRegionY = y; | ||
} | ||
if (!this.minRegionY || y > this.maxRegionY) { | ||
if (y > this.maxRegionY) { | ||
this.maxRegionY = y; | ||
@@ -167,2 +159,28 @@ } | ||
// convert game coordinates to tile | ||
getTileAtGameCoords(x, y) { | ||
let plane = 0; | ||
if (y >= 0 && y <= 1007) { | ||
plane = 0; // overworld | ||
} else if (y >= 1007 && y <= 1007 + 943) { | ||
plane = 1; // first floor | ||
y -= 943; | ||
} else if (y >= 1008 + 943 && y <= 1007 + (2 * 943)) { | ||
plane = 2; // second floor | ||
y -= 943 * 2; | ||
} else { | ||
plane = 3; // dungeon | ||
y -= 943 * 3; | ||
} | ||
const sectorX = Math.floor(x / 48) + this.minRegionX; | ||
const sectorY = Math.floor(y / 48) + this.minRegionY; | ||
const sector = this.sectors[sectorX][sectorY][plane]; | ||
const tile = sector.tiles[47 - (x % 48)][y % 48]; | ||
return tile; | ||
} | ||
async toCanvas(options = {}) { | ||
@@ -174,4 +192,9 @@ const painter = new MapPainter(this, options); | ||
} | ||
toString() { | ||
return `[object ${this.constructor.name} ${this.width}x` + | ||
`${this.height}x${this.depth}]`; | ||
} | ||
} | ||
module.exports = Landscape; |
// paint the final world map images, combining sector images | ||
const key = require('./key'); | ||
const overlayColours = require('./overlay-colours'); | ||
const key = require('../res/key'); | ||
const path = require('path'); | ||
@@ -19,2 +18,4 @@ const { Image, createCanvas } = require('canvas'); | ||
const WATER_COLOUR = 'rgb(36, 64, 127)'; | ||
const FONT = 'Arial'; | ||
@@ -31,14 +32,14 @@ | ||
this.options = {}; | ||
this.options.scale = isNaN(+options.scale) ? 1 : +options.scale; | ||
this.options.points = options.points && options.points.length ? | ||
this.scale = isNaN(+options.scale) ? 1 : +options.scale; | ||
this.points = options.points && options.points.length ? | ||
options.points : []; | ||
this.options.objects = options.objects && options.objects.length ? | ||
this.objects = options.objects && options.objects.length ? | ||
options.objects : []; | ||
this.options.labels = options.labels && options.labels.length ? | ||
this.labels = options.labels && options.labels.length ? | ||
options.labels : []; | ||
this.plane = options.plane || 0; | ||
const width = this.landscape.maxRegionX - this.landscape.minRegionX + 1; | ||
const height = | ||
(this.landscape.maxRegionY - this.landscape.minRegionY + 1) * 1; | ||
//this.landscape.depth; | ||
this.landscape.maxRegionY - this.landscape.minRegionY + 1; | ||
@@ -51,7 +52,16 @@ const firstSector = landscape.getPopulatedSectors().next().value; | ||
this.imageWidth = (this.sectorWidth * TILE_SIZE) * width; | ||
this.imageHeight = (this.sectorHeight * TILE_SIZE * height) + | ||
/*(this.landscape.depth * 240) - 96*/0; | ||
this.imageHeight = (this.sectorHeight * TILE_SIZE * height); | ||
this.canvas = createCanvas(this.imageWidth, this.imageHeight); | ||
this.ctx = this.canvas.getContext('2d'); | ||
// change the offset for positions of labels, objects and points based | ||
// on plane | ||
const maxY = this.landscape.maxRegionY; | ||
const minY = this.landscape.minRegionY; | ||
this.yOffset = | ||
(this.plane * this.sectorHeight * TILE_SIZE * (maxY - minY)) + | ||
(this.plane * 240); | ||
} | ||
@@ -86,36 +96,28 @@ | ||
//for (let i = 0; i < this.landscape.depth; i += 1) { | ||
for (let i = 0; i < 1; i += 1) { | ||
y = (i * this.sectorHeight * TILE_SIZE * (maxY - minY)) + (i * 240); | ||
for (let i = maxX; i >= minX; i -= 1) { | ||
for (let j = minY; j <= maxY; j += 1) { | ||
const sector = this.landscape.sectors[i][j][this.plane]; | ||
for (let j = maxX; j >= minX; j -= 1) { | ||
for (let k = minY; k <= maxY; k += 1) { | ||
const sector = this.landscape.sectors[j][k][i]; | ||
if (!sector || sector.empty) { | ||
this.ctx.fillStyle = | ||
this.plane === 0 ? WATER_COLOUR : '#000'; | ||
this.ctx.fillRect(x, y, this.sectorWidth * TILE_SIZE, | ||
this.sectorHeight * TILE_SIZE); | ||
} else { | ||
const sectorCanvas = sector.toCanvas(this.options, | ||
this.landscape.getSectorNeighbours(i, j, this.plane)); | ||
this.ctx.drawImage(sectorCanvas, x, y); | ||
if (!sector || sector.empty) { | ||
this.ctx.fillStyle = | ||
overlayColours[i === 0 ? 'WATER' : 'BLACK']; | ||
this.ctx.fillRect(x, y, this.sectorWidth * TILE_SIZE, | ||
this.sectorHeight * TILE_SIZE); | ||
} else { | ||
const sectorCanvas = sector.toCanvas(this.options, | ||
this.landscape.getSectorNeighbours(j, k, i)); | ||
this.ctx.drawImage(sectorCanvas, x, y); | ||
/*const text = `${sector.x},${sector.y},${sector.plane}`; | ||
this.ctx.fillStyle = '#000'; | ||
this.ctx.fillText(text, x + 2, y + 2); | ||
this.ctx.fillStyle = '#fff'; | ||
this.ctx.fillText(text, x + 3, y + 3);*/ | ||
} | ||
y += this.sectorHeight * TILE_SIZE; | ||
/*const text = `${sector.x},${sector.y},${sector.plane}`; | ||
this.ctx.fillStyle = '#000'; | ||
this.ctx.fillText(text, x + 2, y + 2); | ||
this.ctx.fillStyle = '#fff'; | ||
this.ctx.fillText(text, x + 3, y + 3);*/ | ||
} | ||
x += this.sectorWidth * TILE_SIZE; | ||
y = (i * this.sectorHeight * TILE_SIZE * (maxY - minY)) + | ||
(i * 240); | ||
y += this.sectorHeight * TILE_SIZE; | ||
} | ||
x = 0; | ||
x += this.sectorWidth * TILE_SIZE; | ||
y = 0; | ||
} | ||
@@ -126,3 +128,6 @@ } | ||
drawObjects() { | ||
for (let { id, position: [x, y]} of this.options.objects) { | ||
const maxY = this.landscape.maxRegionY; | ||
const minY = this.landscape.minRegionY; | ||
for (let { id, position: [x, y]} of this.objects) { | ||
x *= 3; | ||
@@ -133,2 +138,4 @@ x = this.imageWidth - x - 2; | ||
y -= this.yOffset; | ||
if (inWilderness(x, y)) { | ||
@@ -153,6 +160,8 @@ if (WILD_SCENERY.indexOf(id) > -1) { | ||
for (let { type, x, y } of this.options.points) { | ||
for (let { type, x, y } of this.points) { | ||
x -= this.landscape.minRegionX * this.sectorWidth * TILE_SIZE; | ||
y -= this.landscape.minRegionY * this.sectorHeight * TILE_SIZE; | ||
y -= this.yOffset; | ||
this.ctx.drawImage(this.keyImages.get(type), x, y); | ||
@@ -164,3 +173,3 @@ } | ||
drawLabels() { | ||
for (const label of this.options.labels) { | ||
for (const label of this.labels) { | ||
let [x, y] = [label.x, label.y]; | ||
@@ -171,2 +180,4 @@ | ||
y -= this.yOffset; | ||
let sizeInc = 3; | ||
@@ -173,0 +184,0 @@ |
// paint a 2d map of an individual landscape sector | ||
const chalk = require('chalk'); | ||
const overlayColours = require('./overlay-colours'); | ||
const overlays = require('./overlays'); | ||
const terrainColours = require('./terrain-colours'); | ||
const { createCanvas } = require('canvas'); | ||
const { cssColor, rgb2hex } = require('color-functions'); | ||
chalk.level = 3; | ||
// size of a square tile in pixels | ||
@@ -18,21 +13,4 @@ const TILE_SIZE = 3; | ||
const OVERLAY_COLOURS = {}; | ||
const WALL_COLOUR = 'rgb(97, 97, 97)'; | ||
for (const [name, colour] of Object.entries(overlayColours)) { | ||
OVERLAY_COLOURS[overlays[name]] = colour; | ||
} | ||
const FLOOR_TILES = [ | ||
'BROWN_FLOOR', 'STONE_FLOOR', 'MAROON_FLOOR', 'BLACK_FLOOR','BLUE_FLOOR', | ||
'PURPLE_FLOOR', 'LIGHT_STONE_FLOOR', 'SAND_FLOOR', 'MUD_FLOOR' | ||
].map(name => overlays[name]); | ||
const BRIDGE_TILES = [ | ||
'BRIDGE', 'BRIDGE_2', 'LOG', 'LOG_2' | ||
].map(name => overlays[name]); | ||
const ANTIALIAS_OVERLAYS = [ | ||
'ROAD', 'WATER', 'SWAMP_WATER', 'MOUNTAIN', 'LAVA' | ||
].map(name => overlays[name]); | ||
class SectorPainter { | ||
@@ -53,3 +31,3 @@ constructor(sector, options = {}, neighbours = []) { | ||
drawVerticalWall(x, y) { | ||
this.ctx.fillStyle = OVERLAY_COLOURS[9]; | ||
this.ctx.fillStyle = WALL_COLOUR; | ||
this.ctx.fillRect(x + 2, y, 1, TILE_SIZE); | ||
@@ -60,3 +38,3 @@ } | ||
drawHorizontalWall(x, y) { | ||
this.ctx.fillStyle = OVERLAY_COLOURS[9]; | ||
this.ctx.fillStyle = WALL_COLOUR; | ||
this.ctx.fillRect(x, y, TILE_SIZE, 1); | ||
@@ -68,4 +46,5 @@ } | ||
drawDiagonalWall(direction, x, y) { | ||
this.ctx.fillStyle = WALL_COLOUR; | ||
if (direction === '/') { | ||
this.ctx.fillStyle = OVERLAY_COLOURS[9]; | ||
this.ctx.fillRect(x + 2, y, 1, 1); | ||
@@ -75,3 +54,2 @@ this.ctx.fillRect(x + 1, y + 1, 1, 1); | ||
} else if (direction === '\\') { | ||
this.ctx.fillStyle = OVERLAY_COLOURS[9]; | ||
this.ctx.fillRect(x, y, 1, 1); | ||
@@ -92,11 +70,5 @@ this.ctx.fillRect(x + 1, y + 1, 1, 1); | ||
// direction contains a matching overlay | ||
drawOverlay(overlay, x, y, [ north, east, south, west ]) { | ||
const overlayColour = OVERLAY_COLOURS[overlay]; | ||
drawOverlay(colour, x, y, [ north, east, south, west ]) { | ||
this.ctx.fillStyle = colour; | ||
if (!overlayColour) { | ||
return; | ||
} | ||
this.ctx.fillStyle = overlayColour; | ||
// the order of these matter for accuracy | ||
@@ -168,3 +140,5 @@ if (!north && !east && !south && !west) { | ||
for (let j = 0; j < this.sector.height; j += 1) { | ||
const tile = this.sector.tiles[i][j]; | ||
let tile = this.sector.tiles[i][j]; | ||
let tileDef = tile.getTileDef(); | ||
let overlay = tile.overlay; | ||
@@ -174,20 +148,8 @@ // TRBL/NESW | ||
const colour = terrainColours.rgb[tile.colour]; | ||
if (this.sector.plane === 0 || this.sector.plane === 3) { | ||
this.drawTile(colour, x, y); | ||
} else { | ||
this.drawTile('#000', x, y); | ||
} | ||
let overlay = tile.overlay; | ||
const diagonal = tile.wall.diagonal; | ||
// add extra bridge tiles on land | ||
if (overlay !== overlays.WATER && | ||
BRIDGE_TILES.indexOf(overlay) === -1) { | ||
if (!/^(water|lava)$/.test(tileDef.name) && !tileDef.bridge) { | ||
for (const neighbour of neighbours) { | ||
if (neighbour && | ||
BRIDGE_TILES.indexOf(neighbour.overlay) >= 0) { | ||
if (neighbour && neighbour.getTileDef().bridge) { | ||
overlay = neighbour.overlay; | ||
tileDef = neighbour.getTileDef(); | ||
break; | ||
@@ -198,15 +160,19 @@ } | ||
const diagonal = tile.wall.diagonal; | ||
this.drawTile(tile.getTerrainColour(), x, y); | ||
if (overlay !== 0) { | ||
let overlayNeighbours = SURROUNDED.slice(); | ||
if (diagonal && FLOOR_TILES.indexOf(overlay) > -1) { | ||
if (diagonal && tileDef.indoors) { | ||
// this fixes antialiasing for checkered-floor patterns | ||
overlayNeighbours = neighbours.map(neighbour => { | ||
return !!neighbour && | ||
FLOOR_TILES.indexOf(neighbour.overlay) > -1; | ||
neighbour.getTileDef().indoors; | ||
}); | ||
} else if (diagonal || | ||
ANTIALIAS_OVERLAYS.indexOf(overlay) > -1) { | ||
} else if (diagonal || tileDef.antialias) { | ||
overlayNeighbours = neighbours.map(neighbour => { | ||
return !!neighbour && ( | ||
neighbour.overlay === overlays.HOLE || | ||
neighbour.getTileDef().name === 'hole' || | ||
neighbour.overlay === overlay); | ||
@@ -216,7 +182,6 @@ }); | ||
if (overlay === overlays.WATER) { | ||
// if water is touching a bridge or log, don't antialias | ||
// if water is touching a bridge or log, don't antialias | ||
if (/^(water|lava)$/.test(tileDef.name)) { | ||
neighbours.forEach((neighbour, direction) => { | ||
if (neighbour && | ||
BRIDGE_TILES.indexOf(neighbour.overlay) >= 0) { | ||
if (neighbour && neighbour.getTileDef().bridge) { | ||
overlayNeighbours[direction] = true; | ||
@@ -227,3 +192,17 @@ } | ||
this.drawOverlay(overlay, x, y, overlayNeighbours); | ||
// fix diagonal tiles surrounded by different overlays | ||
if (diagonal) { | ||
if (!overlayNeighbours[SOUTH] && neighbours[SOUTH]) { | ||
this.drawTile( | ||
neighbours[SOUTH].getTileDef().colour, | ||
x, y); | ||
} else if (!overlayNeighbours[NORTH] && | ||
neighbours[NORTH]) { | ||
this.drawTile( | ||
neighbours[NORTH].getTileDef().colour, | ||
x, y); | ||
} | ||
} | ||
this.drawOverlay(tileDef.colour, x, y, overlayNeighbours); | ||
} | ||
@@ -243,6 +222,2 @@ | ||
if (diagonal === 48020) { | ||
this.drawTile('#f0f', x, y); | ||
} | ||
y += TILE_SIZE; | ||
@@ -257,3 +232,7 @@ } | ||
// draw the map for in characters for terminals | ||
write() { | ||
write(colourLevel = -1) { | ||
if (colourLevel !== -1) { | ||
chalk.level = colourLevel; | ||
} | ||
const output = []; | ||
@@ -266,6 +245,6 @@ | ||
const tile = this.sector.tiles[tileX][tileY]; | ||
let colour = terrainColours.rgb[tile.colour]; | ||
let colour = tile.getTerrainColour(); | ||
if (tile.overlay) { | ||
const {r, g, b} = cssColor(OVERLAY_COLOURS[tile.overlay]); | ||
const {r, g, b} = cssColor(tile.getTileDef().colour); | ||
colour = rgb2hex(r, g, b); | ||
@@ -272,0 +251,0 @@ } |
@@ -161,14 +161,10 @@ const SectorPainter = require('./sector-painter'); | ||
require('fs').writeFileSync( | ||
'./compressed-elevation.json', | ||
JSON.stringify(Array.from(mapData).slice(0, offset).map(i => i&255), null, ' ')); | ||
lastVal = 64; | ||
for (let tileY = 0; tileY < SECTOR_HEIGHT; tileY++) { | ||
for (let tileX = 0; tileX < SECTOR_WIDTH; tileX++) { | ||
for (let tileX = 0; tileX < SECTOR_WIDTH; tileX++) { | ||
const index = tileX * SECTOR_WIDTH + tileY; | ||
lastVal = this.terrainHeight[index] + (lastVal & 0x7f); | ||
this.terrainHeight[index] = (lastVal * 2) & 0xff; | ||
lastVal = this.terrainHeight[index] + (lastVal & 0x7f); | ||
this.terrainHeight[index] = (lastVal * 2) & 0xff; | ||
@@ -178,21 +174,21 @@ if (this.terrainHeight[index] > 0) { | ||
} | ||
} | ||
} | ||
} | ||
} | ||
lastVal = 0; | ||
for (let tile = 0; tile < MAX_TILES; ) { | ||
let val = mapData[offset++] & 0xff; | ||
for (let tile = 0; tile < MAX_TILES; ) { | ||
let val = mapData[offset++] & 0xff; | ||
if (val < 128) { | ||
this.terrainColour[tile++] = val & 0xff; | ||
lastVal = val; | ||
} | ||
if (val < 128) { | ||
this.terrainColour[tile++] = val & 0xff; | ||
lastVal = val; | ||
} | ||
if (val >= 128) { | ||
for (let i = 0; i < val - 128; i++) { | ||
this.terrainColour[tile++] = lastVal & 0xff; | ||
} | ||
} | ||
} | ||
if (val >= 128) { | ||
for (let i = 0; i < val - 128; i++) { | ||
this.terrainColour[tile++] = lastVal & 0xff; | ||
} | ||
} | ||
} | ||
@@ -218,3 +214,3 @@ lastVal = 35; | ||
parseDat(mapData) { | ||
let offset = 0; | ||
let offset = 0; | ||
@@ -341,9 +337,2 @@ for (let tile = 0; tile < MAX_TILES; tile++) { | ||
const tile = this.tiles[SECTOR_WIDTH - 1 - tileX][tileY]; | ||
if (!tile) { | ||
console.log(this.tiles[tileX], tileX, tileY); | ||
console.log('tile not found'); | ||
process.exit(1); | ||
} | ||
const diagonal = tile.wall ? tile.wall.diagonal : null; | ||
@@ -438,7 +427,3 @@ | ||
if (empty) { | ||
return null; | ||
} | ||
return compressedObjects; | ||
return empty ? null : compressedObjects; | ||
} | ||
@@ -453,5 +438,5 @@ | ||
toString(terminal = false) { | ||
toString(terminal = false, colourLevel = -1) { | ||
if (!terminal) { | ||
return `[${this.constructor.name} ${this.toEntryName()} ` + | ||
return `[object ${this.constructor.name} ${this.getEntryName()} ` + | ||
`${this.width}x${this.height}]`; | ||
@@ -461,3 +446,3 @@ } | ||
const painter = new SectorPainter(this); | ||
return painter.write(); | ||
return painter.write(colourLevel); | ||
} | ||
@@ -464,0 +449,0 @@ |
@@ -1,5 +0,8 @@ | ||
/*const BLOCKED_OVERLAYS = [ | ||
'WATER', 'SWAMP_WATER', 'MOUNTAIN', 'BLACK', 'LAVA', 'BLACK_2' | ||
];*/ | ||
const terrainColours = require('./terrain-colours'); | ||
const tileOverlays = require('../res/tile-overlays'); | ||
const BLACK = 'rgb(0, 0, 0)'; | ||
const PLANE_HEIGHT = 944; | ||
class Tile { | ||
@@ -59,2 +62,27 @@ constructor(tile) { | ||
// return the map colour for this tile | ||
getTerrainColour() { | ||
const plane = this.sector.plane; | ||
if (plane === 0 || plane === 3) { | ||
return terrainColours.rgb[this.colour]; | ||
} else { | ||
return BLACK; | ||
} | ||
} | ||
// get the overlay tile def | ||
getTileDef() { | ||
return tileOverlays[this.overlay] || {}; | ||
} | ||
// return the {x, y} the game would use for this tile | ||
getGameCoords() { | ||
const x = this.x + (this.sector.x - 48) * 48; | ||
const y = ((((this.sector.y - 36) * 48) + this.y + 96) - 144) + | ||
(this.sector.plane * PLANE_HEIGHT); | ||
return { x, y }; | ||
} | ||
toJSON() { | ||
@@ -70,4 +98,9 @@ return { | ||
} | ||
toString() { | ||
return `[object ${this.constructor.name} ` + | ||
`${this.sector.getEntryName()} ${this.index}]`; | ||
} | ||
} | ||
module.exports = Tile; |
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
4992161
6
68
219075
308
2
5
- Removednearest-color@^0.4.4
- Removednearest-color@0.4.4(transitive)