Initial commit; Can render maps, AABBs and POIs
This commit is contained in:
commit
592bacf2f8
4 changed files with 110081 additions and 0 deletions
133
app.js
Normal file
133
app.js
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
'use strict';
|
||||
|
||||
let fileInput;
|
||||
let levelSelector;
|
||||
let trackSelector;
|
||||
let debugRenderCheckbox;
|
||||
|
||||
/**
|
||||
* @type MRGFile?
|
||||
*/
|
||||
let parsedFile = null;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {P5File} file
|
||||
*/
|
||||
const handleFile = function (file) {
|
||||
if (file.name.endsWith(".mrg")) {
|
||||
loadBytes(file.data, (file) => {
|
||||
parsedFile = MRGFile.parse(file.bytes);
|
||||
print(parsedFile);
|
||||
fileInput.hide();
|
||||
levelSelector.show();
|
||||
}, (error) => {
|
||||
print(error);
|
||||
})
|
||||
} else {
|
||||
alert("unknown file type");
|
||||
}
|
||||
}
|
||||
|
||||
const levelSelected = function () {
|
||||
let item = ~~levelSelector.value();
|
||||
if (item === -1) {
|
||||
trackSelector.hide();
|
||||
} else {
|
||||
while (trackSelector.elt.length > 0) {
|
||||
trackSelector.elt.remove(0);
|
||||
}
|
||||
|
||||
trackSelector.option("Select track", -1);
|
||||
for (let i = 0; i < parsedFile.levels[item].count; i++) {
|
||||
trackSelector.option(parsedFile.getTrack(item, i).name, i);
|
||||
}
|
||||
|
||||
trackSelector.show();
|
||||
trackSelector.removeAttribute("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
function setup() {
|
||||
fileInput = createFileInput(handleFile);
|
||||
fileInput.position(20, 50);
|
||||
|
||||
levelSelector = createSelect();
|
||||
levelSelector.position(20, 50);
|
||||
levelSelector.hide();
|
||||
|
||||
levelSelector.option("Select level", -1);
|
||||
levelSelector.option("Level 1", 0);
|
||||
levelSelector.option("Level 2", 1);
|
||||
levelSelector.option("Level 3", 2);
|
||||
|
||||
levelSelector.changed(levelSelected);
|
||||
|
||||
trackSelector = createSelect();
|
||||
trackSelector.position(20, 80);
|
||||
trackSelector.disable();
|
||||
trackSelector.hide();
|
||||
|
||||
debugRenderCheckbox = createCheckbox("Do debug rendering?", true);
|
||||
debugRenderCheckbox.position(20, 20);
|
||||
|
||||
createCanvas(document.body.clientWidth, document.body.clientHeight);
|
||||
}
|
||||
|
||||
let x = 100;
|
||||
let y = 100;
|
||||
|
||||
function draw() {
|
||||
translate(width / 2 - x, height / 2 - y);
|
||||
background(55);
|
||||
|
||||
let lev = ~~levelSelector.value();
|
||||
let trk = ~~trackSelector.value();
|
||||
if (lev !== -1 && trk !== -1) {
|
||||
push();
|
||||
{
|
||||
strokeCap(SQUARE);
|
||||
|
||||
strokeWeight(2);
|
||||
stroke("lime");
|
||||
const track = parsedFile.getTrack(lev, trk);
|
||||
for (let i = 1; i < track.trackData.mapPoints.length; i++) {
|
||||
const pp = track.trackData.mapPoints[i-1];
|
||||
const cp = track.trackData.mapPoints[i];
|
||||
line(pp.x, -pp.y, cp.x, -cp.y);
|
||||
}
|
||||
|
||||
const taabb = track.trackData.getAABB();
|
||||
strokeWeight(6);
|
||||
stroke("yellow");
|
||||
point(track.trackData.playerStartPos.x, -track.trackData.playerStartPos.y);
|
||||
stroke("red");
|
||||
line(track.trackData.finishPos.x, -taabb.topLeft.y, track.trackData.finishPos.x, -taabb.bottomRight.y);
|
||||
|
||||
if (debugRenderCheckbox.checked()) {
|
||||
strokeWeight(1);
|
||||
noFill();
|
||||
rectMode(CORNERS);
|
||||
stroke("cyan");
|
||||
rect(taabb.topLeft.x, -taabb.topLeft.y, taabb.bottomRight.x, -taabb.bottomRight.y);
|
||||
}
|
||||
}
|
||||
pop();
|
||||
}
|
||||
|
||||
if (keyIsDown(LEFT_ARROW)) {
|
||||
x -= 5;
|
||||
}
|
||||
|
||||
if (keyIsDown(RIGHT_ARROW)) {
|
||||
x += 5;
|
||||
}
|
||||
|
||||
if (keyIsDown(UP_ARROW)) {
|
||||
y -= 5;
|
||||
}
|
||||
|
||||
if (keyIsDown(DOWN_ARROW)) {
|
||||
y += 5;
|
||||
}
|
||||
}
|
||||
27
index.html
Normal file
27
index.html
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>GD: p5js</title>
|
||||
<script src="p5.js"></script>
|
||||
<script src="mrgfile.js"></script>
|
||||
<script src="app.js"></script>
|
||||
|
||||
<style>
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
width: 99vw;
|
||||
height: 99vh;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
270
mrgfile.js
Normal file
270
mrgfile.js
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
const MAP_START_MARKER = 0x33;
|
||||
|
||||
class P5File {
|
||||
/** @type {String} */ data
|
||||
/** @type {File} */ file
|
||||
/** @type {String} */ name
|
||||
/** @type {Number} */ size
|
||||
/** @type {String} */ type
|
||||
/** @type {String?} */ subtype
|
||||
}
|
||||
|
||||
class BinUtils {
|
||||
/**
|
||||
*
|
||||
* @param {DataView} view
|
||||
*/
|
||||
constructor(view) {
|
||||
this.offset = 0;
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} offset
|
||||
*/
|
||||
seek(offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Number}
|
||||
*/
|
||||
readInt() {
|
||||
const out = this.view.getInt32(this.offset);
|
||||
this.offset += 4;
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Number}
|
||||
*/
|
||||
readShort() {
|
||||
const out = this.view.getInt16(this.offset);
|
||||
this.offset += 2;
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Number}
|
||||
*/
|
||||
readByte() {
|
||||
const out = this.view.getInt8(this.offset);
|
||||
this.offset += 1;
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Number}
|
||||
*/
|
||||
peekInt() {
|
||||
return this.view.getInt32(this.offset);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Number}
|
||||
*/
|
||||
peekShort() {
|
||||
return this.view.getInt16(this.offset);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Number}
|
||||
*/
|
||||
peekByte() {
|
||||
return this.view.getInt8(this.offset);
|
||||
}
|
||||
}
|
||||
|
||||
class Point {
|
||||
/** @type {Number} */ x;
|
||||
/** @type {Number} */ y;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
*/
|
||||
constructor(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
class AABB {
|
||||
/** @type {Point} */ topLeft;
|
||||
/** @type {Point} */ bottomRight;
|
||||
}
|
||||
|
||||
class TrackData {
|
||||
/** @type {Point} */ playerStartPos;
|
||||
/** @type {Point} */ finishPos;
|
||||
/** @type {Number} */ pointsCount;
|
||||
/** @type {Point[]} */ mapPoints;
|
||||
/** @type {AABB?} */ _trackAABB = null;
|
||||
|
||||
/**
|
||||
* @returns {AABB}
|
||||
*/
|
||||
getAABB() {
|
||||
if (this._trackAABB === null) {
|
||||
let lx, mx = lx = 0;
|
||||
let ly, my = ly = 0;
|
||||
|
||||
this.mapPoints.forEach(pt => {
|
||||
if (pt.x < lx) {
|
||||
lx = pt.x;
|
||||
} else if (pt.x > mx) {
|
||||
mx = pt.x;
|
||||
}
|
||||
|
||||
if (pt.y < ly) {
|
||||
ly = pt.y;
|
||||
} else if (pt.y > my) {
|
||||
my = pt.y;
|
||||
}
|
||||
});
|
||||
|
||||
this._trackAABB = new AABB();
|
||||
this._trackAABB.topLeft = new Point(lx, my);
|
||||
this._trackAABB.bottomRight = new Point(mx, ly);
|
||||
}
|
||||
|
||||
return this._trackAABB;
|
||||
}
|
||||
}
|
||||
|
||||
class TrackName {
|
||||
/** @type {Number} */ offset;
|
||||
/** @type {String} */ name;
|
||||
/** @type {TrackData} */ trackData;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Number} offset
|
||||
*/
|
||||
constructor(name, offset) {
|
||||
this.name = name;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {TrackData} trackData
|
||||
*/
|
||||
setTrackData(trackData) {
|
||||
this.trackData = trackData;
|
||||
}
|
||||
}
|
||||
|
||||
class Level {
|
||||
/** @type {Number} */ count;
|
||||
/** @type {TrackName[]} */ tracks;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} tracksCount
|
||||
*/
|
||||
constructor(tracksCount) {
|
||||
this.count = tracksCount;
|
||||
this.tracks = Array(tracksCount);
|
||||
}
|
||||
}
|
||||
|
||||
class MRGFile {
|
||||
/** @type {Level[]} */ levels;
|
||||
|
||||
constructor() {
|
||||
this.levels = Array(3);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} levelNum
|
||||
* @param {Number} trackNum
|
||||
* @returns {TrackName}
|
||||
*/
|
||||
getTrack(levelNum, trackNum) {
|
||||
return this.levels[levelNum].tracks[trackNum];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Uint8Array} bytes
|
||||
* @returns MRGFile
|
||||
*/
|
||||
static parse(bytes) {
|
||||
const view = new DataView(bytes.buffer, 0);
|
||||
const reader = new BinUtils(view);
|
||||
let mrgFile = new MRGFile();
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const tn = reader.readInt();
|
||||
|
||||
const level = new Level(tn);
|
||||
for (let j = 0; j < tn; j++) {
|
||||
const trackOffset = reader.readInt();
|
||||
let titleBytes = [];
|
||||
while (reader.peekByte() !== 0) {
|
||||
let byte = reader.readByte();
|
||||
titleBytes.push(byte);
|
||||
}
|
||||
reader.readByte();
|
||||
let trackName = String.fromCharCode(...titleBytes);
|
||||
|
||||
level.tracks[j] = new TrackName(trackName, trackOffset);
|
||||
}
|
||||
mrgFile.levels[i] = level;
|
||||
}
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
for (let j = 0; j < mrgFile.levels[i].count; j++) {
|
||||
let track = mrgFile.levels[i].tracks[j];
|
||||
reader.seek(track.offset);
|
||||
if (reader.peekByte() !== MAP_START_MARKER) {
|
||||
console.warn(`Corrupted map data at offset ${track.offset}`);
|
||||
continue;
|
||||
}
|
||||
reader.readByte();
|
||||
|
||||
let trackData = new TrackData();
|
||||
trackData.playerStartPos = new Point(
|
||||
reader.readInt() >> 16 << 3, reader.readInt() >> 16 << 3
|
||||
);
|
||||
trackData.finishPos = new Point(
|
||||
reader.readInt() >> 16 << 3, reader.readInt() >> 16 << 3
|
||||
);
|
||||
trackData.pointsCount = reader.readShort();
|
||||
trackData.mapPoints = Array(trackData.pointsCount);
|
||||
trackData.mapPoints[0] = new Point(
|
||||
reader.readInt(), reader.readInt()
|
||||
);
|
||||
for (let i = 1; i < trackData.pointsCount; i++) {
|
||||
let ppx = reader.readByte();
|
||||
let ppy = 0;
|
||||
if (ppx === -1) {
|
||||
ppx = reader.readInt();
|
||||
ppy = reader.readInt();
|
||||
} else {
|
||||
ppx += trackData.mapPoints[i-1].x;
|
||||
ppy = trackData.mapPoints[i-1].y + reader.readByte();
|
||||
}
|
||||
trackData.mapPoints[i] = new Point(
|
||||
ppx, ppy
|
||||
);
|
||||
}
|
||||
|
||||
track.setTrackData(trackData);
|
||||
}
|
||||
}
|
||||
|
||||
return mrgFile;
|
||||
}
|
||||
}
|
||||
109651
p5.js
Normal file
109651
p5.js
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue