Skip to content

Instantly share code, notes, and snippets.

@Rybar
Created April 21, 2018 01:13
Show Gist options
  • Save Rybar/50bbcf7719afa47cef4710578c6f9921 to your computer and use it in GitHub Desktop.
Save Rybar/50bbcf7719afa47cef4710578c6f9921 to your computer and use it in GitHub Desktop.
//--------------Engine.js-------------------
const WIDTH = 320;
const HEIGHT = 180;
const PAGES = 10; //page = 1 screen HEIGHTxWIDTH worth of screenbuffer.
const PAGESIZE = WIDTH*HEIGHT;
const SCREEN = 0;
const BUFFER = PAGESIZE;
const BUFFER2 = PAGESIZE*2;
const BACKGROUND = PAGESIZE*3;
const MIDGROUND = PAGESIZE*4;
const FOREGROUND = PAGESIZE*5;
const COLLISION = PAGESIZE*6;
const SPRITES = PAGESIZE*7;
const UI = PAGESIZE*8;
S=Math.sin;
C=Math.cos;
function cos(x) { // x = 0 - 1
return Math.cos(x*6.28318531);
};
function sin(x) {
return Math.sin(-x*6.28318531);
}
//relative drawing position and pencolor, for drawing functions that require it.
viewX = 0;
viewY = 0;
viewW = WIDTH;
viewH = HEIGHT;
cursorX = 0;
cursorY = 0;
cursorColor = 22;
cursorColor2 = 0;
floodStack = [];
fontString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_!@#.'\"?/<()";
fontBitmap = "11111100011111110001100011111010001111101000111110111111000010000100000111111100100101000110001111101111110000111001000011111111111000"+
"0111001000010000111111000010111100011111110001100011111110001100011111100100001000010011111111110001000010100101111010001100101110010010100011000"+
"0100001000010000111111000111011101011000110001100011100110101100111000101110100011000110001011101111010001100101110010000011101000110001100100111"+
"1111101000111110100011000101111100000111000001111101111100100001000010000100100011000110001100010111010001100011000101010001001000110001101011010"+
"1011101000101010001000101010001100010101000100001000010011111000100010001000111110010001100001000010001110011101000100010001001111111110000010011"+
"0000011111010010100101111100010000101111110000111100000111110011111000011110100010111011111000010001000100001000111010001011101000101110011101000"+
"1011110000101110011101000110001100010111000000000000000000000111110010000100001000000000100111111000110111101011011101010111110101011111010100000"+
"000000000000000000100001100001000100000000000011011010011001000000000000111010001001100000000100000010001000100010001000000010001000100000100000100001000100001000010000010"
dither = [
0b1111111111111111,
0b1111111111110111,
0b1111110111110111,
0b1111110111110101,
0b1111010111110101,
0b1111010110110101,
0b1110010110110101,
0b1110010110100101,
0b1010010110100101,
0b1010010110100001,
0b1010010010100001,
0b1010010010100000,
0b1010000010100000,
0b1010000000100000,
0b1000000000100000,
0b1000000000000000,
0b0000000000000000,
];
pat = 0b1111111111111111;
//default palette index
palDefault = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63];
var
c = document.getElementById('canvas'),
ctx = c.getContext('2d'),
renderTarget = 0x00000,
renderSource = PAGESIZE, //buffer is ahead one screen's worth of pixels
//Adigun Azikiwe Polack's AAP64 Palette.
//ofcourse you can change this to whatever you like, up to 256 colors.
//one GOTCHA: colors are stored 0xAABBGGRR, so you'll have to flop the values from your typical hex colors.
colors =
[
0xff080606,
0xff131014,
0xff25173B,
0xff2D1773,
0xff2A20B4,
0xff233EDF,
0xff0A6AFA,
0xff1BA3F9,
0xff41D5FF,
0xff40FCFF,
0xff64F2D6,
0xff43DB9C,
0xff35C159,
0xff2EA014,
0xff3E7A1A,
0xff3B5224,
0xff202012,
0xff643414,
0xffC45C28,
0xffDE9F24,
0xffC7D620,
0xffDBFCA6,
0xffFFFFFF,
0xffC0F3FE,
0xffB8D6FA,
0xff97A0F5,
0xff736AE8,
0xff9B4ABC,
0xff803A79,
0xff533340,
0xff342224,
0xff1A1C22,
0xff282b32,
0xff3b4171,
0xff4775bb,
0xff63a4db,
0xff9cd2f4,
0xffeae0da,
0xffd1b9b3,
0xffaf938b,
0xff8d756d,
0xff62544a,
0xff413933,
0xff332442,
0xff38315b,
0xff52528e,
0xff6a75ba,
0xffa3b5e9,
0xffffe6e3,
0xfffbbfb9,
0xffe49b84,
0xffbe8d58,
0xff857d47,
0xff4e6723,
0xff648432,
0xff8daf5d,
0xffbadc92,
0xffe2f7cd,
0xffaad2e4,
0xff8bb0c7,
0xff6286a0,
0xff556779,
0xff444e5a,
0xff343942,]
//active palette index. maps to indices in colors[]. can alter this whenever for palette effects.
pal = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63];
//paldrk = [0,0,1,2,3,4,5,6,6,10,11,12,13,14,2,2,15,16,17,18,22,20,23,24,25,26,2,2,27,28,31,13]
ctx.imageSmoothingEnabled = false;
//ctx.mozImageSmoothingEnabled = false;
c.width = WIDTH;
c.height = HEIGHT;
var imageData = ctx.getImageData(0, 0, WIDTH, HEIGHT),
buf = new ArrayBuffer(imageData.data.length),
buf8 = new Uint8Array(buf),
data = new Uint32Array(buf),
ram = new Uint8Array(WIDTH * HEIGHT * PAGES);
//--------------graphics functions----------------
function inView(x,y, viewpad = 64){
return(x >= 0-viewpad && x <= WIDTH+viewpad && y >=0-viewpad && y <= HEIGHT+viewpad);
}
function clear(color = 0, page=renderTarget){
ram.fill(color, page, page + PAGESIZE);
}
function setColors(o){
cursorColor = o[0];
cursorColor2 = o[1];
}
function pset(o) { //an index from colors[], 0-63
//o[0] x
//o[1] y
x = o[0]|0;
y = o[1]|0;
let px = (y % 4) * 4 + (x% 4);
let mask = pat & Math.pow(2, px);
pcolor = mask ? cursorColor : cursorColor2;
if(pcolor == 0)return;
if(x < 0 | x > WIDTH-1) return;
if(y < 0 | y > HEIGHT-1) return;
ram[renderTarget + y * WIDTH + x] = mask ? cursorColor : cursorColor2;
}
function pget(x=cursorX, y=cursorY, page=renderTarget){
return ram[page + x + y * WIDTH];
}
function fillPixel (pixel, prevC, newC){
ram[pixel] = newC;
let up = pixel + WIDTH, down = pixel - WIDTH, left = pixel -1, right = pixel+1
if(ram[up] == prevC)floodStack.push(up)
if(ram[down] == prevC)floodStack.push(down)
if(ram[left] == prevC)floodStack.push(left)
if(ram[right] == prevC)floodStack.push(right)
}
function floodFill(x=cursorX,y=cursorY, newC=cursorColor, page=renderTarget){
let prevC = pget(x,y,page);
floodStack = [];
fillPixel(page + x + y * WIDTH, prevC, newC);
while(floodStack.length > 0){
let fill = floodStack.pop();
fillPixel(fill, prevC, newC);
}
}
function moveTo(x,y){
cursorX = x;
cursorY = y;
}
function lineTo(x,y, color=cursorColor, color2 = cursorColor2){
cursorColor2 = color2;
cursorColor = color;
line(cursorX, cursorY, x, y, color, color2);
cursorX = x;
cursorY = y;
}
function line(x1, y1, x2, y2, color=cursorColor, color2 = cursorColor2) {
cursorColor2 = color2;
cursorColor = color;
x1 = x1|0;
x2 = x2|0;
y1 = y1|0;
y2 = y2|0;
var dy = (y2 - y1);
var dx = (x2 - x1);
var stepx, stepy;
if (dy < 0) {
dy = -dy;
stepy = -1;
} else {
stepy = 1;
}
if (dx < 0) {
dx = -dx;
stepx = -1;
} else {
stepx = 1;
}
dy <<= 1; // dy is now 2*dy
dx <<= 1; // dx is now 2*dx
pset([x1, y1]);
if (dx > dy) {
var fraction = dy - (dx >> 1); // same as 2*dy - dx
while (x1 != x2) {
if (fraction >= 0) {
y1 += stepy;
fraction -= dx; // same as fraction -= 2*dx
}
x1 += stepx;
fraction += dy; // same as fraction -= 2*dy
pset([x1, y1]);
}
;
} else {
fraction = dx - (dy >> 1);
while (y1 != y2) {
if (fraction >= 0) {
x1 += stepx;
fraction -= dy;
}
y1 += stepy;
fraction += dx;
pset([x1, y1]);
}
}
}
function circle(xm=cursorX, ym=cursorY, r=5, color=cursorColor, color2 = cursorColor2) {
cursorColor2 = color2;
cursorColor = color;
xm = xm|0;
ym = ym|0;
r = r|0;
color = color|0;
var x = -r, y = 0, err = 2 - 2 * r;
/* II. Quadrant */
do {
pset([xm - x, ym + y]);
/* I. Quadrant */
pset([xm - y, ym - x]);
/* II. Quadrant */
pset([xm + x, ym - y]);
/* III. Quadrant */
pset([xm + y, ym + x]);
/* IV. Quadrant */
r = err;
if (r <= y) err += ++y * 2 + 1;
/* e_xy+e_y < 0 */
if (r > x || err > y) err += ++x * 2 + 1;
/* e_xy+e_x > 0 or no 2nd y-step */
} while (x < 0);
}
function fillCircle(xm, ym, r=5, color=cursorColor, color2 = cursorColor2) {
cursorColor2 = color2;
cursorColor = color;
xm = xm|0;
ym = ym|0;
r = r|0;
color = color|0;
if(r < 0) return;
xm = xm|0; ym = ym|0, r = r|0; color = color|0;
var x = -r, y = 0, err = 2 - 2 * r;
/* II. Quadrant */
do {
line(xm-x, ym-y, xm+x, ym-y, color);
line(xm-x, ym+y, xm+x, ym+y, color);
r = err;
if (r <= y) err += ++y * 2 + 1;
if (r > x || err > y) err += ++x * 2 + 1;
} while (x < 0);
}
function ellipse(x0=cursorX, y0=cursorY, width, height, color=cursorColor, color2 = cursorColor2){
cursorColor2 = color2;
cursorColor = color;
x0 = x0|0;
let x1 = x0+width|0;
y0 = y0|0;
let y1 = y0+height|0;
let a = Math.abs(x1-x0), b = Math.abs(y1-y0), b1 = b&1; /* values of diameter */
let dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; /* error increment */
let err = dx+dy+b1*a*a, e2; /* error of 1.step */
if (x0 > x1) { x0 = x1; x1 += a; } /* if called with swapped points */
if (y0 > y1) y0 = y1; /* .. exchange them */
y0 += (b+1)/2; y1 = y0-b1; /* starting pixel */
a *= 8*a; b1 = 8*b*b;
do {
pset([x1, y0]); /* I. Quadrant */
pset([x0, y0]); /* II. Quadrant */
pset([x0, y1]); /* III. Quadrant */
pset([x1, y1]); /* IV. Quadrant */
e2 = 2*err;
if (e2 <= dy) { y0++; y1--; err += dy += a; } /* y step */
if (e2 >= dx || 2*err > dy) { x0++; x1--; err += dx += b1; } /* x step */
} while (x0 <= x1);
while (y0-y1 < b) { /* too early stop of flat ellipses a=1 */
pset([x0-1, y0]); /* -> finish tip of ellipse */
pset([x1+1, y0++]);
pset([x0-1, y1]);
pset([x1+1, y1--]);
}
}
function rect(x, y, x2=16, y2=16, color=cursorColor, color2 = cursorColor2) {
cursorColor2 = color2;
cursorColor = color;
x1 = x|0;
y1 = y|0;
x2 = x2|0;
y2 = y2|0;
line(x1,y1, x2, y1, color);
line(x2, y1, x2, y2, color);
line(x1, y2, x2, y2, color);
line(x1, y1, x1, y2, color);
}
function fillRect(x, y, x2=16, y2=16, color=cursorColor, color2 = cursorColor2) {
cursorColor2 = color2;
cursorColor = color;
x1 = x|0;
y1 = y|0;
x2 = x2|0;
y2 = y2|0;
color = color|0;
var i = Math.abs(y2 - y1);
line(x1, y1, x2, y1, color);
if(i > 0){
while (--i) {
line(x1, y1+i, x2, y1+i, color);
}
}
line(x1,y2, x2, y2, color);
}
function rectTo(width,height,color=cursorColor, color2 = cursorColor2, filled=false){
cursorColor2 = color2;
cursorColor = color;
filled ? fillRect(cursorX, cursorY, cursorX+width, cursorY+height, color, color2): rect(cursorX, cursorY, cursorX+width, cursorY+height, color, color2);
cursorX += width;
cursorY += height;
}
function cRect(x,y,w,h,c,color=cursorColor, color2 = cursorColor2){
x = x|0;
y = y|0;
w = w|0;
h = h|0;
c = c|0;
color = color|0;
for(let i = 0; i <= c; i++){
fillRect(x+i,y-i,w-i*2,h+i*2,color);
}
}
function outline(renderSource, renderTarget, color=cursorColor, color2=cursorColor2, color3=color, color4=color){
cursorColor2 = color2;
cursorColor = color;
for(let i = 0; i <= WIDTH; i++ ){
for(let j = 0; j <= HEIGHT; j++){
let left = i-1 + j * WIDTH;
let right = i+1 + j * WIDTH;
let bottom = i + (j+1) * WIDTH;
let top = i + (j-1) * WIDTH;
let current = i + j * WIDTH;
if(ram[renderSource + current]){
if(!ram[renderSource + left]){
ram[renderTarget + left] = color;
};
if(!ram[renderSource + right]){
ram[renderTarget + right] = color3;
};
if(!ram[renderSource + top]){
ram[renderTarget + top] = color2;
};
if(!ram[renderSource + bottom]){
ram[renderTarget + bottom] = color4;
};
}
}
}
}
function triangle(x1, y1, x2, y2, x3, y3, color=cursorColor, color2 = cursorColor2) {
cursorColor2 = color2;
cursorColor = color;
line(x1,y1, x2,y2, color);
line(x2,y2, x3,y3, color);
line(x3,y3, x1,y1, color);
}
function fillTriangle( x1, y1, x2, y2, x3, y3, color=cursorColor , color2 = cursorColor2) {
cursorColor2 = color2;
cursorColor = color;
//I don't pretend to know how this works exactly; I know it uses barycentric coordinates.
//Might replace with simpler line-sweep; haven't perf tested yet.
var canvasWidth = WIDTH;
// http://devmaster.net/forums/topic/1145-advanced-rasterization/
// 28.4 fixed-point coordinates
var x1 = Math.round( 16 * x1 );
var x2 = Math.round( 16 * x2 );
var x3 = Math.round( 16 * x3 );
var y1 = Math.round( 16 * y1 );
var y2 = Math.round( 16 * y2 );
var y3 = Math.round( 16 * y3 );
// Deltas
var dx12 = x1 - x2, dy12 = y2 - y1;
var dx23 = x2 - x3, dy23 = y3 - y2;
var dx31 = x3 - x1, dy31 = y1 - y3;
// Bounding rectangle
var minx = Math.max( ( Math.min( x1, x2, x3 ) + 0xf ) >> 4, 0 );
var maxx = Math.min( ( Math.max( x1, x2, x3 ) + 0xf ) >> 4, WIDTH );
var miny = Math.max( ( Math.min( y1, y2, y3 ) + 0xf ) >> 4, 0 );
var maxy = Math.min( ( Math.max( y1, y2, y3 ) + 0xf ) >> 4, HEIGHT );
// Block size, standard 8x8 (must be power of two)
var q = 8;
// Start in corner of 8x8 block
minx &= ~(q - 1);
miny &= ~(q - 1);
// Constant part of half-edge functions
var c1 = -dy12 * x1 - dx12 * y1;
var c2 = -dy23 * x2 - dx23 * y2;
var c3 = -dy31 * x3 - dx31 * y3;
// Correct for fill convention
if ( dy12 > 0 || ( dy12 == 0 && dx12 > 0 ) ) c1 ++;
if ( dy23 > 0 || ( dy23 == 0 && dx23 > 0 ) ) c2 ++;
if ( dy31 > 0 || ( dy31 == 0 && dx31 > 0 ) ) c3 ++;
c1 = (c1 - 1) >> 4;
c2 = (c2 - 1) >> 4;
c3 = (c3 - 1) >> 4;
// Set up min/max corners
var qm1 = q - 1; // for convenience
var nmin1 = 0, nmax1 = 0;
var nmin2 = 0, nmax2 = 0;
var nmin3 = 0, nmax3 = 0;
if (dx12 >= 0) nmax1 -= qm1*dx12; else nmin1 -= qm1*dx12;
if (dy12 >= 0) nmax1 -= qm1*dy12; else nmin1 -= qm1*dy12;
if (dx23 >= 0) nmax2 -= qm1*dx23; else nmin2 -= qm1*dx23;
if (dy23 >= 0) nmax2 -= qm1*dy23; else nmin2 -= qm1*dy23;
if (dx31 >= 0) nmax3 -= qm1*dx31; else nmin3 -= qm1*dx31;
if (dy31 >= 0) nmax3 -= qm1*dy31; else nmin3 -= qm1*dy31;
// Loop through blocks
var linestep = (canvasWidth-q);
for ( var y0 = miny; y0 < maxy; y0 += q ) {
for ( var x0 = minx; x0 < maxx; x0 += q ) {
// Edge functions at top-left corner
var cy1 = c1 + dx12 * y0 + dy12 * x0;
var cy2 = c2 + dx23 * y0 + dy23 * x0;
var cy3 = c3 + dx31 * y0 + dy31 * x0;
// Skip block when at least one edge completely out
if (cy1 < nmax1 || cy2 < nmax2 || cy3 < nmax3) continue;
// Offset at top-left corner
var offset = (x0 + y0 * canvasWidth);
// Accept whole block when fully covered
if (cy1 >= nmin1 && cy2 >= nmin2 && cy3 >= nmin3) {
for ( var iy = 0; iy < q; iy ++ ) {
for ( var ix = 0; ix < q; ix ++, offset ++ ) {
ram[renderTarget + offset] = color;
}
offset += linestep;
}
} else { // Partially covered block
for ( var iy = 0; iy < q; iy ++ ) {
var cx1 = cy1;
var cx2 = cy2;
var cx3 = cy3;
for ( var ix = 0; ix < q; ix ++ ) {
if ( (cx1 | cx2 | cx3) >= 0 ) {
ram[renderTarget + offset] = color;
}
cx1 += dy12;
cx2 += dy23;
cx3 += dy31;
offset ++;
}
cy1 += dx12;
cy2 += dx23;
cy3 += dx31;
offset += linestep;
}
}
}
}
}
function spr(sx = 0, sy = 0, sw = WIDTH, sh = HEIGHT, x=0, y=0, flipx = false, flipy = false, palette=pal){
sx = sx|0
sy = sy|0
sw = sw|0
sh = sh|0
x = x|0
y = y|0
for(var i = 0; i < sh; i++){
for(var j = 0; j < sw; j++){
if(y+i < HEIGHT && x+j < WIDTH && y+i > -1 && x+j > -1){
if(flipx & flipy){
if(ram[(renderSource + ( ( sy + (sh-i) )*WIDTH+sx+(sw-j)))] >= 0) {
ram[ (renderTarget + ((y+i)*WIDTH+x+j)) ] = palette[ ram[(renderSource + ((sy+(sh-i-1))*WIDTH+sx+(sw-j-1)))] ];
}
}
else if(flipy && !flipx){
if(ram[(renderSource + ( ( sy + (sh-i) )*WIDTH+sx+j))] >= 0) {
ram[ (renderTarget + ((y+i)*WIDTH+x+j)) ] = palette[ ram[(renderSource + ((sy+(sh-i-1))*WIDTH+sx+j))] ];
}
}
else if(flipx && !flipy){
if(ram[(renderSource + ((sy+i)*WIDTH+sx+(sw-j-1)))] >= 0) {
ram[ (renderTarget + ((y+i)*WIDTH+x+j)) ] = palette[ ram[(renderSource + ((sy+i)*WIDTH+sx+(sw-j-1)))] ];
}
}
else if(!flipx && !flipy){
if(ram[(renderSource + ((sy+i)*WIDTH+sx+j))] >= 0) {
ram[ (renderTarget + ((y+i)*WIDTH+x+j)) ] = palette [ ram[(renderSource + ((sy+i)*WIDTH+sx+j))] ] ;
}
}
}
}
}
}
function sspr(sx = 0, sy = 0, sw = 16, sh = 16, x=0, y=0, dw=16, dh=16, palette=pal){
sx = sx|0
sy = sy|0
sw = sw|0
sh = sh|0
x = x|0
y = y|0
dw = dw|0
dh = dh|0
var xratio = sw / dw;
var yratio = sh / dh;
for(var i = 0; i < dh; i++){
for(var j = 0; j < dw; j++){
px = (j*xratio)|0;
py = (i*yratio)|0;
if(y+i < HEIGHT && x+j < WIDTH && y+i > -1 && x+j > -1) {
if (ram[(renderSource + ((sy + py) * WIDTH + sx + px))] > 0) {
ram[(renderTarget + ((y + i) * WIDTH + x + j))] = palette[ ram[(renderSource + ((sy + py) * WIDTH + sx + px))] ]
}
}
}
}
}
function rspr( sx, sy, sw, sh, destCenterX, destCenterY, scale, angle, palette=pal ){
let sourceCenterX = (sw / 2)|0;
let sourceCenterY = (sh / 2)|0;
let destWidth = sw * scale;
let destHeight = sh * scale;
let halfWidth = (destWidth / 2 * 1.41421356237)|0 + 5; //area will always be square, hypotenuse trick
let halfHeight = (destHeight / 2 * 1.41421356237)|0 + 5;
let startX = -halfWidth;
let endX = halfWidth;
let startY = -halfHeight;
let endY = halfHeight;
let scaleFactor = 1.0 / scale;
let cos = Math.cos(-angle) * scaleFactor;
let sin = Math.sin(-angle) * scaleFactor;
for(let y = startY; y < endY; y++){
for(let x = startX; x < endX; x++){
let u = sourceCenterX + Math.round(cos * x + sin * y);
let v = sourceCenterY + Math.round(-sin * x + cos * y);
let drawX = (x + destCenterX)|0;
let drawY = (y + destCenterY)|0;
//screen check. otherwise drawn pix will wrap to next line due to 1D nature of buffer array
if(drawX > 0 && drawX < WIDTH && drawY > 0 && drawY < HEIGHT){
if(u >= 0 && v >= 0 && u < sw && v < sh){
if( ram[renderSource + (u+sx) + (v+sy) * WIDTH] > 0) {
ram[renderTarget + drawX + drawY * WIDTH] = palette[ ram[renderSource + (u+sx) + (v+sy) * WIDTH] ]
}
}
}
} //end x loop
} //end outer y loop
}
function checker(x, y, w, h, nRow=8, nCol=8, color=cursorColor, color2 = cursorColor2) {
cursorColor2 = color2;
cursorColor = color;
w /= nCol; // width of a block
h /= nRow; // height of a block
for (var i = 0; i < nRow; ++i) {
for (var j = 0, col = nCol / 2; j < col; ++j) {
let bx = x + (2 * j * w + (i % 2 ? 0 : w) );
let by = i * h;
fillRect(bx, by, w-1, h-1, color);
}
}
}
function imageToRam(image, address) {
let tempCanvas = document.createElement('canvas');
tempCanvas.width = WIDTH;
tempCanvas.height = HEIGHT;
let context = tempCanvas.getContext('2d');
//draw image to canvas
context.drawImage(image, 0, 0);
//get image data
var imageData = context.getImageData(0,0, WIDTH, HEIGHT);
//set up 32bit view of buffer
let data = new Uint32Array(imageData.data.buffer);
//compare buffer to palette (loop)
for(var i = 0; i < data.length; i++) {
ram[address + i] = colors.indexOf(data[i]);
}
}
function render() {
var i = PAGESIZE; // display is first page of ram
while (i--) {
/*
data is 32bit view of final screen buffer
for each pixel on screen, we look up it's color and assign it
*/
data[i] = colors[pal[ram[i]]];
}
imageData.data.set(buf8);
ctx.putImageData(imageData, 0, 0);
}
//-----------txt.js----------------
//forked from Jack Rugile's text renderer in Radius Raid: https://github.com/jackrugile/radius-raid-js13k/blob/master/js/text.js
//o is an array of options with the following structure:
/*
0: text
1: x
2: y
3: hspacing
4: vspacing
5: halign
6: valign
7: scale
8: color
//options 9-11 are for animating the text per-character. just sin motion
9: per character offset
10: delay, higher is slower
11: frequency
*/
function textLine(o) {
//o 9,10,11 are 6,7,8 here
if(!o[7])o[7]=1
if(!o[8])o[8]=1
var textLength = o[0].length,
size = 5;
for (var i = 0; i < textLength; i++) {
var letter = [];
letter = getCharacter( o[0].charAt(i) );
for (var y = 0; y < size; y++) {
for (var x = 0; x < size; x++) {
if (letter[y*size+x] == 1){
if(o[4] == 1){
let cx = 0;
let cy = 0;
pset([
o[1] + ( x * o[4] ) + ( ( size * o[4] ) + o[3] ) * i,
( o[2] + ( o[6] ? Math.sin((t+i*o[8])*1/o[7])*o[6] : 0 ) + (y * o[4]) )|0
]);
}
else {
let cx = o[1] + ( x * o[4] ) + ( ( size * o[4] ) + o[3] ) * i;
let cy = ( o[2] + ( o[6] ? Math.sin((t+i*o[8])*1/o[7])*o[6] : 0 ) + (y * o[4]) )|0
//pat = 0b1111111111111111;
fillRect( cx, cy, cx+o[4], cy+o[4], o[5]);
}
} //end draw routine
} //end x loop
} //end y loop
} //end text loop
} //end textLine()
function text(o) {
var size = 5,
letterSize = size * o[7],
lines = o[0].split('\n'),
linesCopy = lines.slice(0),
lineCount = lines.length,
longestLine = linesCopy.sort(function (a, b) {
return b.length - a.length;
})[0],
textWidth = ( longestLine.length * letterSize ) + ( ( longestLine.length - 1 ) * o[3] ),
textHeight = ( lineCount * letterSize ) + ( ( lineCount - 1 ) * o[4] );
if(!o[5])o[5] = 'left';
if(!o[6])o[6] = 'bottom';
var sx = o[1],
sy = o[2],
ex = o[1] + textWidth,
ey = o[2] + textHeight;
if (o[5] == 'center') {
sx = o[1] - textWidth / 2;
ex = o[1] + textWidth / 2;
} else if (o[5] == 'right') {
sx = o[1] - textWidth;
ex = o[1];
}
if (o[6] == 'center') {
sy = o[2] - textHeight / 2;
ey = o[2] + textHeight / 2;
} else if (o[6] == 'bottom') {
sy = o[2] - textHeight;
ey = o[2];
}
var cx = sx + textWidth / 2,
cy = sy + textHeight / 2;
for (var i = 0; i < lineCount; i++) {
var line = lines[i],
lineWidth = ( line.length * letterSize ) + ( ( line.length - 1 ) * o[3] ),
x = o[1],
y = o[2] + ( letterSize + o[4] ) * i;
if (o[5] == 'center') {
x = o[1] - lineWidth / 2;
} else if (o[5] == 'right') {
x = o[1] - lineWidth;
}
if (o[6] == 'center') {
y = y - textHeight / 2;
} else if (o[6] == 'bottom') {
y = y - textHeight;
}
textLine([
line,
x,
y,
o[3] || 0,
o[7] || 1,
o[8],
o[9],
o[10],
o[11]
]);
}
return {
sx: sx,
sy: sy,
cx: cx,
cy: cy,
ex: ex,
ey: ey,
width: textWidth,
height: textHeight
}
}
function getCharacter(char){
index = fontString.indexOf(char);
return fontBitmap.substring(index * 25, index*25+25).split('') ;
}
//-----------END txt.js----------------
Number.prototype.clamp = function(min, max) {
return Math.min(Math.max(this, min), max);
};
Number.prototype.map = function(old_bottom, old_top, new_bottom, new_top) {
return (this - old_bottom) / (old_top - old_bottom) * (new_top - new_bottom) + new_bottom;
};
Number.prototype.pad = function(size, char="0") {
var s = String(this);
while (s.length < (size || 2)) {s = char + s;}
return s;
};
//---audio handling-----------------
function playSound(buffer, playbackRate = 1, pan = 0, volume = .5, loop = false) {
var source = audioCtx.createBufferSource();
var gainNode = audioCtx.createGain();
var panNode = audioCtx.createStereoPanner();
source.buffer = buffer;
source.connect(panNode);
panNode.connect(gainNode);
gainNode.connect(audioMaster);
source.playbackRate.value = playbackRate;
source.loop = loop;
gainNode.gain.value = volume;
panNode.pan.value = pan;
source.start();
return {volume: gainNode, sound: source};
}
//--------keyboard input--------------
Key = {
_pressed: {},
_released: {},
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
SPACE: 32,
ONE: 49,
TWO: 50,
THREE: 51,
FOUR: 52,
a: 65,
c: 67,
w: 87,
s: 83,
d: 68,
z: 90,
x: 88,
f: 70,
p: 80,
r: 82,
isDown(keyCode) {
return this._pressed[keyCode];
},
justReleased(keyCode) {
return this._released[keyCode];
},
onKeydown(event) {
this._pressed[event.keyCode] = true;
},
onKeyup(event) {
this._released[event.keyCode] = true;
delete this._pressed[event.keyCode];
},
update() {
this._released = {};
}
};
function LCG(seed = Date.now(), a = 1664525, c = 1013904223, m = Math.pow(2,32) ){
this.seed = seed;
this.a= a;
this.c = c;
this.m = m;
}
LCG.prototype.setSeed = function(seed) {
this.seed = seed;
},
LCG.prototype.nextInt = function() {
// range [0, 2^32)
this.seed = (this.seed * this.a + this.c) % this.m;
return this.seed;
},
LCG.prototype.nextFloat = function() {
// range [0, 1)
return this.nextInt() / this.m;
},
LCG.prototype.nextBool = function(percent) {
// percent is chance of getting true
if(percent == null) {
percent = 0.5;
}
return this.nextFloat() < percent;
},
LCG.prototype.nextFloatRange = function(min, max) {
// range [min, max)
return min + this.nextFloat() * (max - min);
},
LCG.prototype.nextIntRange = function(min, max) {
// range [min, max)
return Math.floor(this.nextFloatRange(min, max));
},
LCG.prototype.nextColor = function() {
// range [#000000, #ffffff]
var c = this.nextIntRange(0, Math.pow(2, 24)).toString(16).toUpperCase();
while(c.length < 6) {
c = "0" + c;
}
return "#" + c;
}
//--------END Engine.js-------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment