Use real map data in Javascript raycaster, so I can more closely simulate the Apple II and thus debug problems more easily.

This commit is contained in:
Martin Haye 2014-05-30 08:33:11 -07:00
parent 4cd450ecc9
commit c8d8c22478
2 changed files with 157 additions and 58 deletions

View File

@ -40,6 +40,8 @@ class PackPartitions
def debugCompression = false
def javascriptOut = null
def parseMap(map, tiles)
{
// Parse each row of the map
@ -348,11 +350,59 @@ class PackPartitions
}
}
}
/**
* Dump map data to Javascript code, to help in debugging the raycaster. This way,
* the Javascript version can run the same map, and we can compare its results to
* the 6502 results.
*/
def dumpJsMap(rows, texMap)
{
def width = rows[0].size()
// Write the map data. First comes the sentinel row.
javascriptOut.println("var map = [")
javascriptOut.print(" [")
(0..width).each { javascriptOut.print("-1,") }
javascriptOut.println("],")
// Now the real map data
rows.each { row ->
javascriptOut.print(" [")
row.each { tile ->
def b = texMap[tile?.@id]
if ((b & 0x80) == 0)
javascriptOut.format("%2d,", b)
else
javascriptOut.print(" 0,")
}
javascriptOut.println("-1,],")
}
// Finish the map data with another sentinel row
javascriptOut.print(" [")
(0..width).each { javascriptOut.print("-1,") }
javascriptOut.println("]")
javascriptOut.println("];\n")
// Then write out the sprites
javascriptOut.println("var allSprites = [")
rows.eachWithIndex { row, y ->
row.eachWithIndex { tile, x ->
def b = texMap[tile?.@id]
if ((b & 0x80) != 0) {
// y+1 below to account for initial sentinel row
javascriptOut.format(" {type:%2d, x:%2d.5, y:%2d.5},\n", b & 0x7f, x, y+1)
}
}
}
javascriptOut.println("];\n")
}
def write3DMap(buf, mapName, rows) // [ref BigBlue1_50]
{
def width = rows[0].size()
def height = rows.size()
def width = rows[0].size() + 1 // Sentinel $FF at end of each row
def height = rows.size() + 2 // Sentinel rows of $FF's at start and end
// Determine the set of all referenced textures, and assign numbers to them.
def texMap = [:]
@ -389,12 +439,22 @@ class PackPartitions
texList.each { buf.put((byte)it) }
buf.put((byte)0)
// Sentinel row of $FF at start of map
(0..width).each { buf.put((byte)0xFF) }
// After the header comes the raw data
rows.each { row ->
row.each { tile ->
buf.put((byte)texMap[tile?.@id])
}
buf.put((byte)0xFF) // sentinel at end of row
}
// Sentinel row of $FF at end of map
(0..width).each { buf.put((byte)0xFF) }
if (javascriptOut)
dumpJsMap(rows, texMap)
}
// The renderer wants bits of the two pixels interleaved in a special way.
@ -697,7 +757,7 @@ class PackPartitions
}
}
def pack(xmlPath, binPath)
def pack(xmlPath, binPath, javascriptPath)
{
// Read in code chunks. For now these are hard coded, but I guess they ought to
// be configured in a config file somewhere...?
@ -731,6 +791,10 @@ class PackPartitions
packTexture(image)
}
// If doing Javascript debugging, open that output file too.
if (javascriptPath)
javascriptOut = new File(javascriptPath).newPrintWriter()
// Pack each map This uses the image and tile maps filled earlier.
println "Packing maps."
dataIn.map.each { map ->
@ -746,6 +810,11 @@ class PackPartitions
println "Writing output file."
new File(binPath).withOutputStream { stream -> writePartition(stream) }
// Finish up Javacript if necessary
if (javascriptPath)
javascriptOut.close()
// Lastly, print stats
println "Compression saved $compressionSavings bytes."
if (compressionSavings > 0) {
def endSize = new File(binPath).length()
@ -773,13 +842,14 @@ class PackPartitions
}
// Check the arguments
if (args.size() != 2) {
println "Usage: convert yourOutlawFile.xml DISK.PART.0.bin"
if (args.size() != 2 && args.size() != 3) {
println "Usage: convert yourOutlawFile.xml game.part.0.bin [intcastMap.js]"
println " (where intcastMap.js is to aid in debugging the Javascript raycaster)"
System.exit(1);
}
// Go for it.
new PackPartitions().pack(args[0], args[1])
new PackPartitions().pack(args[0], args[1], args.size() > 2 ? args[2] : null)
}
}

View File

@ -4,55 +4,77 @@ var $ = function(id) { return document.getElementById(id); };
var dc = function(tag) { return document.createElement(tag); };
var map = [
[1,4,3,4,2,3,2,4,3,2,4,3,4],
[1,0,0,1,0,0,0,3,0,0,2,0,3],
[1,1,0,1,1,0,0,1,0,0,3,0,2],
[1,0,0,1,2,3,0,4,0,0,4,0,3],
[1,0,0,0,0,4,0,0,0,0,0,0,4],
[2,0,0,0,0,2,0,0,0,0,0,0,4],
[0,2,2,2,0,0,0,3,0,0,3,0,1],
[3,0,0,2,0,0,0,3,0,0,2,0,3],
[1,0,0,2,0,3,0,2,0,0,4,0,3],
[1,0,0,0,0,2,0,0,0,0,0,0,1],
[3,0,0,0,0,1,0,0,0,0,0,0,3],
[1,0,0,4,0,4,0,3,1,2,4,0,2],
[4,0,0,0,0,0,0,0,0,0,0,0,3],
[1,2,3,3,3,2,2,1,2,4,2,2,2]
[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,],
[ 0, 1, 2, 0, 1, 0, 0, 3, 0, 0, 1, 4, 5, 1, 0, 0, 0,-1,],
[ 4, 0, 0, 7, 0, 2, 1, 0, 1, 2, 0, 0, 0, 0, 0, 1, 0,-1,],
[ 0, 5, 0, 0, 0, 0, 0, 0, 7, 1, 8, 9, 0, 2, 1, 0, 0,-1,],
[ 1, 0, 0, 2, 0,10,11, 0, 2, 0, 0, 8, 0, 7, 1, 0, 0,-1,],
[ 0, 0, 0, 1, 9, 0,12, 0, 0, 0, 4, 5, 0, 0, 0, 3, 0,-1,],
[ 3, 0, 0, 0, 0,12,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-1,],
[ 0, 4, 5, 2, 0, 0, 0, 0,14,15, 0, 7, 2, 0, 0, 0, 0,-1,],
[ 2, 0, 0, 7, 0, 4, 5, 0, 2,14, 0, 9, 7, 0, 0, 2, 0,-1,],
[ 0, 1, 0, 1, 0,11,10, 0, 8, 0, 0, 4, 0,10,11, 0, 0,-1,],
[ 0, 8, 0, 0, 0,10,11, 0, 9, 0, 0, 5, 0, 0, 0, 0, 0,-1,],
[ 0, 8, 9, 0, 1, 1, 0, 3, 0, 0, 0, 0, 0,10, 0, 0, 0,-1,],
[ 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0,-1,],
[ 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0,-1,],
[ 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0,-1,],
[ 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0,-1,],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-1,],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-1,],
[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,]
];
var itemTypes = [
{ img : "sprites/tablechairs.png", block : true }, // 0
{ img : "sprites/armor.png", block : true }, // 1
{ img : "sprites/plantgreen.png", block : true }, // 2
{ img : "sprites/lamp.png", block : false } // 3
{ img : "sprites/lamp.png", block : false }, // 3
{ img : "sprites/lamp.png", block : false }, // 4
{ img : "sprites/lamp.png", block : false }, // 5
{ img : "sprites/lamp.png", block : false } // 6
];
var allSprites = [
// lamp where tree would be
{type:3, x:1.5, y:4.5},
// lamps in center area
{type:3, x:9.5, y:7.5},
{type:3, x:15.5, y:7.5},
// lamps in bottom corridor
{type:3, x:5.5, y:12.5},
{type:3, x:11.5, y:12.5},
{type:3, x:11.5, y:12.5},
// tables in long bottom room
{type:0, x:10.5, y:10.5},
{type:1, x:11.5, y:10.5},
// lamps in long bottom room
{type:3, x:8.5, y:10.5},
{type:3, x:9.5, y:10.5}
{type: 6, x: 1.5, y: 5.5},
{type: 6, x:14.5, y: 1.5},
{type: 6, x: 4.5, y: 4.5},
{type: 6, x: 8.5, y: 5.5},
{type: 6, x:14.5, y: 6.5},
{type: 6, x:15.5, y: 7.5},
{type: 6, x: 9.5, y: 9.5},
{type: 6, x:12.5, y:10.5},
{type: 6, x:14.5, y:10.5},
{type: 6, x: 8.5, y:11.5},
{type: 6, x:10.5, y:11.5},
{type: 6, x:12.5, y:11.5},
{type: 6, x: 6.5, y:12.5},
{type: 6, x: 8.5, y:12.5},
{type: 6, x: 9.5, y:12.5},
{type: 6, x:10.5, y:12.5},
{type: 6, x:11.5, y:12.5},
{type: 6, x: 7.5, y:13.5},
{type: 6, x: 9.5, y:13.5},
{type: 6, x: 6.5, y:14.5},
{type: 6, x:10.5, y:14.5},
{type: 6, x: 8.5, y:15.5},
{type: 6, x:12.5, y:15.5},
{type: 6, x: 1.5, y:16.5},
{type: 6, x: 3.5, y:16.5},
{type: 6, x: 5.5, y:16.5},
{type: 6, x: 6.5, y:16.5},
{type: 6, x: 7.5, y:16.5},
{type: 6, x: 9.5, y:16.5},
{type: 6, x:10.5, y:16.5},
{type: 6, x:11.5, y:16.5},
{type: 6, x:13.5, y:16.5},
{type: 6, x:14.5, y:16.5},
];
// Player attributes [ref BigBlue2_10]
var player = {
x : 1.5, // current x, y position
y : 2.5,
y : 3.5,
dir : 0, // the direction that the player is turning, either -1 for left or 1 for right.
angleNum : 4, // the current angle of rotation
speed : 0, // is the playing moving forward (speed = 1) or backwards (speed = -1).
@ -100,9 +122,6 @@ var wallTextures = [
var userAgent = navigator.userAgent.toLowerCase();
var isGecko = userAgent.indexOf("gecko") != -1 && userAgent.indexOf("safari") == -1;
// enable this to use a single image file containing all wall textures. This performs better in Firefox. Opera likes smaller images.
var useSingleTexture = isGecko;
var screenStrips = [];
var overlay;
@ -413,6 +432,15 @@ function intRenderSprites()
// translate position to viewer space
var dx = sprite.x - player.x;
var dy = sprite.y - player.y;
// Prevent things from overflowing
if (dx > 8 || dy > 8) {
if (sprite.index == debugSprite)
console.log(" too far away (dx=" + dx + ", dy=" + dy + ")");
sprite.visible = false;
sprite.img.style.display = "none";
continue;
}
// Apply rotation to the position
var bSgnDx = (dx < 0) ? -1 : 1;
@ -470,7 +498,7 @@ function intRenderSprites()
var wSpriteTop = 32 - (wSize >> 1);
var wSpriteLeft = wX + wSpriteTop;
if (sprite.index == debugSprite)
console.log(" wX=$" + wordToHex(wX & 0xFFFF) + ", wSpriteTop=$" + wordToHex(wSpriteTop) + ", wSpriteLeft=$" + wordToHex(wSpriteLeft & 0xFFFF));
console.log(" wX=$" + wordToHex(wX) + ", wSpriteTop=$" + wordToHex(wSpriteTop) + ", wSpriteLeft=$" + wordToHex(wSpriteLeft));
var bStartTx = 0;
if (wSpriteLeft < 0) {
if (wSpriteLeft < -wSize) {
@ -579,10 +607,6 @@ function initScreen() {
strip.style.left = 0 + "px";
strip.style.height = "0px";
if (useSingleTexture) {
strip.src = "walls/walls.png";
}
strip.oldStyles = {
left : 0,
top : 0,
@ -728,14 +752,16 @@ function assert(flg, msg) {
}
function byteToHex(d) {
assert(d >= 0 && d <= 255, "byte out of range");
var hex = Number(d).toString(16).toUpperCase();
assert(d >= -127 && d <= 255, "byte out of range");
d = Number(d) & 0xFF;
var hex = d.toString(16).toUpperCase();
return "00".substr(0, 2 - hex.length) + hex;
}
function wordToHex(d) {
assert(d >= 0 && d <= 65535, "word out of range");
var hex = Number(d).toString(16).toUpperCase();
assert(d >= -32767 && d <= 65535, "word out of range");
d = Number(d) & 0xFFFF;
var hex = d.toString(16).toUpperCase();
return "0000".substr(0, 4 - hex.length) + hex;
}
@ -976,6 +1002,10 @@ function intCast(x)
// Perform DDA - digital differential analysis
while (true)
{
// Return immediately if we hit the edge of the map.
if (bMapX < 0 || bMapY < 0 || map[bMapY][bMapX] < 0)
return { wallType: -1, textureX: 0, height: 0, logHeight: 1, xWallHit: 0, yWallHit: 0 }
// Jump to next map square in x-direction, OR in y-direction
if (uless_bb(bSideDistX, bSideDistY)) {
bMapX += bStepX;
@ -1060,12 +1090,11 @@ function drawStrip(stripIdx, lineData)
var imgTop = 0;
var styleHeight;
if (useSingleTexture) {
// then adjust the top placement according to which wall texture we need
imgTop = Math.floor(height * (lineData.wallType-1));
var styleHeight = Math.floor(height * numTextures);
if (lineData.wallType < 0) {
var styleSrc = wallTextures[0];
var styleHeight = 0;
} else {
var styleSrc = wallTextures[lineData.wallType-1];
var styleSrc = wallTextures[(lineData.wallType-1) % wallTextures.length];
if (strip.oldStyles.src != styleSrc) {
strip.src = styleSrc;
strip.oldStyles.src = styleSrc