@2003scape/rsc-landscape 中文文档教程
rsc-landscape
(反)序列化 runescape 经典景观文件。 解析原始的 land
和 maps
归档到 tile 对象,转储 PNG,进行更改和编码 + 将它们压缩回原始档案。
用 rsc-landscape 生成的世界地图
与 jagex 的世界地图比较
由于 GIF,jagex 生成的官方世界地图包含的细节较少 调色板压缩,以及扇区之间的剪切对象符号。 它是 与最新版本相比也缺少一些区域(格特鲁德的房子, digsite、shilo 村等)。
install
$ npm install @2003scape/rsc-landscape # -g for CLI program
cli usage
rsc-landscape <command>
Commands:
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
Options:
--help Show help [boolean]
--version Show version number [boolean]
$ 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
const fs = require('fs');
const { Landscape } = require('./src');
const landscape = new Landscape();
landscape.loadJag(fs.readFileSync('./land63.jag'),
fs.readFileSync('./maps63.jag'));
landscape.loadMem(fs.readFileSync('./land63.mem'),
fs.readFileSync('./maps63.mem'));
landscape.parseArchives();
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));
fs.writeFileSync(`./sector-lumbridge.png`, lumbridge.toCanvas().toBuffer());
(async () => {
fs.writeFileSync('./worldmap.png', (await landscape.toCanvas({
points: require('./map-points.json'),
objects: require('./object-locs.json'),
labels: require('./map-labels.json')
})).toBuffer());
})();
file formats
runescape classic 世界被分成扇区,每个扇区包含 48x48 (2304) 瓷砖。 overworld 和 dungeon 扇区包含一个 .hei
和 .dat
文件,扇区 楼上只包含.dat
文件,任何有对象位置的扇区都会 有一个 .loc
文件。
.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 maps archive which stores object IDs (used for the login screen previews)
api
.terrainColours.integer
一组原始的、未加深的 256 种颜色,客户使用这些颜色来为瓷砖着色。
.terrainColours.rgb
用于每个图块的 256 种地图颜色的数组,变暗 50% 并转换为 rgb(r, g, b)
格式。
.tileOverlays
ID 到图块覆盖信息的映射。
tile = new Tile({ sector, x, y, … })
创建新的部门瓷砖。 接受下面列出的所有属性。
tile.colour
0-255 中的数字对应于 .terrainColours
中的颜色。
tile.elevation
0-255 之间的数字描述了 tile 的高度。
tile.direction
0-6 中的数字描述对象在图块上应面对的方向。
tile.overlay
叠加类型索引。 相应的名称存储在 .overlays
中。
tile.wall
具有以下潜在属性的对象:
{
diagonal: {
direction: '/' || '\\',
overlay: overlay
} || null,
vertical: overlay || 0,
horizontal: overlay || 0,
roof: roofOverlay || 0
}
tile.objectId
在此处存储对象以供登录屏幕预览。
tile.populate()
从 tile 的扇区读取缓冲区并填充其属性。
tile.getTerrainColour()
为地图返回此图块的基色。
tile.getTileDef()
返回描述图块覆盖属性的对象(来自 ./res/tile-overlays.json
):
{
name: 'road',
blocked: false,
bridge: false,
indoors: false,
antialias: true,
colour: 'rgb(64, 64, 64)'
}
tile.getGameCoords()
返回 { x, y }
游戏用于此图块。
sector = new Sector({ x, y, plane, members?, tiles? })
创建新的扇区实例。
sector.members
存储在 .jag
或 .mem
文件中?
sector.width
x 轴 (48) 上的瓷砖数量。
sector.height
y 轴上的瓷砖数量 (48)。
sector.terrainHeight
sector.terrainColour
sector.wallsVertical
sector.wallsHorizontal
sector.wallsRoof
sector.tileDecoration
sector.tileDirection
使用 sector.parse*
或从存档文件填充的 Int8Array 缓冲区 使用 sector.populateBuffers()
的扇区磁贴对象。 这些缓冲器是 编码+压缩成档案。
sector.wallsDiagonal
Int32Array 缓冲区,与上面类似但 32 位存储值 > 255 ( 如果存储对象,则可能大于 48000)。
sector.tiles[width][height]
二维图块对象数组。 从存档缓冲区填充此字段 sector.populateTiles()
,或者用 sector.populateBuffers()
。
sector.parseHei(buffer)
从 .hei
文件填充 sector.terrainHeight
和 sector.terrainColour
。
sector.parseDat(buffer)
填充 sector.walls*
、sector.tileDecoration
和 sector.tileDirection
来自 .dat
文件。
sector.parseLoc(buffer)
使用 .loc
文件中的对象 ID 填充 sector.wallsDiagonal
。
sector.populateTiles()
使用基于缓冲区的 tile 对象的二维数组 (48x48) 填充 sector.tiles
我们从存档文件中解析。
sector.populateBuffers()
填充未来的存档缓冲区(sector.terrain*
、sector.wall*
等) 扇区.tiles
。
sector.getEntryName()
获取横向存档文件名的主要部分。
sector.toHei()
为该扇区获取一个 .hei
文件缓冲区。
sector.toDat()
获取此扇区的 .dat
文件缓冲区。
sector.toLoc()
获取此扇区的 .loc
文件缓冲区(如果未存储对象 ID,则为 null)。
sector.toCanvas(options, [ north, east, south, west ])
将单个扇区渲染到画布上。 第二个参数是可选的,如果你 想要使用相邻扇区(世界地图 生成会自动执行此操作)。
在节点中,您可以将其转换为 PNG 使用 .toBuffer()
。
sector.toString(terminal = false, colourLevel = -1)
如果 terminal
为真,则返回该扇区的 nethack 式终端渲染:
colourLevel< /code> 描述了 要使用的粉笔颜色级别。
-1
自动检测最大支持度。
…否则只返回扇区的名称和大小。
landscape = new Landscape()
创建新的景观(反)序列化器实例。
landscape.loadJag(landBuffer, mapBuffer)
landscape.loadMem(landBuffer, mapBuffer)
准备要解析的 .jag
和 .mem
缓冲区。 任何部门加载 landscape.loadMem
将有 sector.members = true
。
landscape.parseArchives()
用加载的缓冲区填充 landscape.sectors
。
*landscape.getPopulatedSectors()
返回所有非空扇区的迭代器。
landscape.getSectorNeighbours(x, y, plane)
将邻居返回到扇区位置作为 [north, east, south, west]
。
landscape.getTileAtGameCoords(x, y)
在游戏中使用的坐标处获取图块。
async landscape.toCanvas({ objects, points, labels })
从所有非空扇区创建世界地图图像。
objects
is an optional array of the following:
{
id: 1,
position: [x, y]
}
它的 x
和 y
乘以图块大小。
points
is an optional array of the following:
{
type: 'altar', // 'general-shop', 'dungeon' etc. see ./res/key/
x, y
}
每个点图像是 15x15。
labels
is an optional array of the following:
{
text: 'label\nfor\nsomething',
x, y,
size: 10, // 8 is the smallest in use, while 14 is the largest
align: 'center' || 'left',
bold: true || undefined,
colour: 'rgb(254, 165, 0)' || '#ff00ff' || undefined
}
license
版权所有 2019 2003Scape Team
本程序是免费软件:您可以重新分发它和/或修改它 GNU Affero 通用公共许可证的条款由 自由软件基金会,许可证的第 3 版,或(由您选择) 任何更高版本。
分发该程序是希望它有用,但没有任何 保修单; 甚至没有适销性或适用性的默示保证 特殊用途。 有关详细信息,请参阅 GNU Affero 通用公共许可证。
您应该已经收到 GNU Affero 通用公共许可证的副本 有了这个程序。 如果没有,请参阅 http://www.gnu.org/licenses/。
rsc-landscape
(de)serialize runescape classic landscape files. parse the original land
and maps
archives into a tile objects, dump PNGs, make changes and encode + compress them back to an original archive.
a world map generated with rsc-landscape
comparison with jagex's world map
the official world map generated by jagex contains less detail due to GIF palette compression, as well as clipped object symbols between sectors. it's also missing some areas compared to the latest revision (gertrude's house, digsite, shilo village, etc.).
install
$ npm install @2003scape/rsc-landscape # -g for CLI program
cli usage
rsc-landscape <command>
Commands:
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
Options:
--help Show help [boolean]
--version Show version number [boolean]
$ 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
const fs = require('fs');
const { Landscape } = require('./src');
const landscape = new Landscape();
landscape.loadJag(fs.readFileSync('./land63.jag'),
fs.readFileSync('./maps63.jag'));
landscape.loadMem(fs.readFileSync('./land63.mem'),
fs.readFileSync('./maps63.mem'));
landscape.parseArchives();
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));
fs.writeFileSync(`./sector-lumbridge.png`, lumbridge.toCanvas().toBuffer());
(async () => {
fs.writeFileSync('./worldmap.png', (await landscape.toCanvas({
points: require('./map-points.json'),
objects: require('./object-locs.json'),
labels: require('./map-labels.json')
})).toBuffer());
})();
file formats
the runescape classic world is separated into sectors, each containing 48x48 (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 maps archive which stores object IDs (used for the login screen previews)
api
.terrainColours.integer
array of original, undarkened 256 colours client uses to colour tiles.
.terrainColours.rgb
array of 256 map colours used for each tile, darkened by 50% and converted to rgb(r, g, b)
format.
.tileOverlays
map of IDs to tile overlay information.
tile = new Tile({ sector, x, y, … })
create new sector tile. accepts all of the properties listed below.
tile.colour
number from 0-255 corresponding to colour in .terrainColours
.
tile.elevation
number from 0-255 describing height of tile.
tile.direction
number from 0-6 describing direction objects should face on tile.
tile.overlay
overlay type index. corresponding names are stored in .overlays
.
tile.wall
object with following potential properties:
{
diagonal: {
direction: '/' || '\\',
overlay: overlay
} || null,
vertical: overlay || 0,
horizontal: overlay || 0,
roof: roofOverlay || 0
}
tile.objectId
store object here for login screen previews.
tile.populate()
read buffers from tile's sector and populate its properties.
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
):
{
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 new sector instance.
sector.members
store in .jag
or .mem
file?
sector.width
amount of tiles on x axis (48).
sector.height
amount of tiles on y axis (48).
sector.terrainHeight
sector.terrainColour
sector.wallsVertical
sector.wallsHorizontal
sector.wallsRoof
sector.tileDecoration
sector.tileDirection
Int8Array buffers populated from archive files with sector.parse*
or from sector's tile objects with sector.populateBuffers()
. these buffers are encoded + compressed into archives.
sector.wallsDiagonal
Int32Array buffer, similar to above but 32-bit to store values > 255 ( potentially larger than 48000 if objects are stored).
sector.tiles[width][height]
2d array of tile objects. populate this field from the archive buffers with sector.populateTiles()
, or populate the future archive buffers with sector.populateBuffers()
.
sector.parseHei(buffer)
populate sector.terrainHeight
and sector.terrainColour
from a .hei
file.
sector.parseDat(buffer)
populate sector.walls*
, sector.tileDecoration
and sector.tileDirection
from a .dat
file.
sector.parseLoc(buffer)
populate sector.wallsDiagonal
with object IDs from a .loc
file.
sector.populateTiles()
populate sector.tiles
with a 2d array (48x48) of tile objects based on buffers we parsed from archived files.
sector.populateBuffers()
populate future archive buffers (sector.terrain*
, sector.wall*
, etc.) with sector.tiles
.
sector.getEntryName()
get the main portion of a landscape archive filename.
sector.toHei()
get a .hei
file buffer for this sector.
sector.toDat()
get a .dat
file buffer for this sector.
sector.toLoc()
get a .loc
file buffer for this sector (or null if no objects ID are stored).
sector.toCanvas(options, [ north, east, south, west ])
render an individual sector to a canvas. the second argument is optional if you want to antialias the edges properly using the neighbouring sectors (world map generation does this automatically).
in node, you can turn this into a PNG with .toBuffer()
.
sector.toString(terminal = false, colourLevel = -1)
if terminal
is true, return a nethack-esque terminal rendering of the sector:
colourLevel
describes the chalk level of colours to use. -1
automatically detects the maximum support.
…otherwise just return the name and size of the sector.
landscape = new Landscape()
create new landscape (de)serializer instance.
landscape.loadJag(landBuffer, mapBuffer)
landscape.loadMem(landBuffer, mapBuffer)
prepare .jag
and .mem
buffers to be parsed. any sectors loaded with landscape.loadMem
will have sector.members = true
.
landscape.parseArchives()
populate landscape.sectors
with loaded buffers.
*landscape.getPopulatedSectors()
return iterator of all the non-empty sectors.
landscape.getSectorNeighbours(x, y, plane)
return neighbours to a sector position as [north, east, south, west]
.
landscape.getTileAtGameCoords(x, y)
get the tile at coordinates used in game.
async landscape.toCanvas({ objects, points, labels })
create a world map image from all of the non-empty sectors.
objects
is an optional array of the following:
{
id: 1,
position: [x, y]
}
its x
and y
are multipled by the tile size.
points
is an optional array of the following:
{
type: 'altar', // 'general-shop', 'dungeon' etc. see ./res/key/
x, y
}
each point image is 15x15.
labels
is an optional array of the following:
{
text: 'label\nfor\nsomething',
x, y,
size: 10, // 8 is the smallest in use, while 14 is the largest
align: 'center' || 'left',
bold: true || undefined,
colour: 'rgb(254, 165, 0)' || '#ff00ff' || undefined
}
license
Copyright 2019 2003Scape Team
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see http://www.gnu.org/licenses/.