Quiet a few years ago there was a game that caught my attention. It’s called GimmieFrictionBaby.
The game has just one control and that is a fire button.
Its not fast paced but strangely addictive.
Search for the name and you will find the original.
This is my simple copy.
Enjoy! and if you don’t enjoy then take the code and make it enjoyable.
<html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> <title>Gimmie Friction Baby</title> <style> Body { background-color: #181818; color: #ffffff; margin: 0px; } Canvas { position:fixed; } #counter { position:absolute; bottom:0; right:0; color:#ffffff; display:none; } </style> </head> <body> <canvas id="myCanvas">Sorry... your browser does not support the canvas element.</canvas> <p id="counter">counter</p> </body> <script> // Description : This is a clone of the winner of a "Casual Gampley Design Competition" // "Gimme Friction Baby" was written by Wouter Visser of The Netherlands // Global variables // used for calculating and showing the frames per second var dwFrames=0; var dwCurrentTime=0; var dwLastUpdateTime=0; var dwElapsedTime=0; var fpsText; // The game engine needs to persist var GE; var currentStage; init = function() { var canvas = document.getElementById('myCanvas'); if (canvas.getContext) { // got to have the game engine GE = GameEngine16Colour(canvas,update,draw); GE.PortraitMode(); currentStage = StageGame().Init(); // currentStage = StageGame().MainMenu(); // when the window resizes we want to resize the canvas window.onresize = function() { doResize(); }; doResize(); } } // This gets called every time the engine redraws the canvas // For slow machines this may be less that the actual fps draw = function() { currentStage.draw(); // calculate and show the fps dwFrames++; dwCurrentTime = (new Date).getTime(); dwElapsedTime = dwCurrentTime - dwLastUpdateTime; if(dwElapsedTime >= 1000) { fpsText = "FPS " + dwFrames; dwLastUpdateTime = dwCurrentTime; dwFrames = 0; } var l = document.getElementById('counter'); l.textContent = fpsText; } // This gets called every game update frame update = function() { currentStage.update(); if ( currentStage.finished ) currentStage = currentStage.nextStage(); } // This just makes sure the canvas is as big as possible // given the window size. function doResize() { var canvas = document.getElementById('myCanvas'); var screenHeight = window.innerHeight; var screenWidth = window.innerWidth; if ( GE.CanvasWidth > GE.CanvasHeight) { if (screenWidth*3/4 < screenHeight) { screenHeight = Math.floor(screenWidth*3/4); } else { screenWidth = Math.floor(screenHeight*4/3); } } else { if (screenHeight*3/4 < screenWidth) { screenWidth = Math.floor(screenHeight*3/4); } else { screenHeight = Math.floor(screenWidth*4/3); } } canvas.style.height = screenHeight+"px"; canvas.style.width = screenWidth+"px"; canvas.style.marginLeft = (window.innerWidth-screenWidth)/2; canvas.style.marginTop = Math.floor((window.innerHeight-screenHeight)/2)+1; // fudge to remove adddress bar on mobile browsers window.scrollTo(0, 0); window.scrollTo(0, 1); } var soundBounce; StageGame = function () { var obj = {}; obj.finished = false; var data; obj.Data = function() { var obj = {}; obj.score = 0; obj.hiscore = 0; obj.useKeyboard = false; obj.cannonVectors = new Array(); obj.ballNum = 0; return obj; } obj.Init = function (dataIn) { var obj = {}; var data; if ( dataIn == undefined ) data = new Array(); else data = dataIn; data["game"] = StageGame().Data(); obj.finished = false; obj.init = function() { data["game"].score = 0; data["game"].hiscore = 0; data["game"].cannonVectors = Array(); data["game"].ballsInCourt = Array(); data["game"].ballNum = 0; } obj.update = function() { obj.finished = true; } obj.draw = function() { } obj.nextStage = function() { return StageSplash(data); // return MainMenu(data); } obj.init(); return obj; } function StageSplash (dataIn) { var obj = {}; obj.finished = false; var currentStage; var data = dataIn; obj.init=function() { data = dataIn; if ( dataIn == undefined ) data = new Array(); currentStage = Static(data); currentStage.init(); } obj.draw = function() { currentStage.draw(); } obj.update = function() { currentStage.update(); if ( currentStage.finished ) { if ( currentStage.nextStage() == null ) obj.finished = true; else currentStage = currentStage.nextStage(); } } obj.nextStage = function() { return InitGame(data); } // Splash Stages function Static(data) { var obj = {}; var startTime; obj.finished = false; var soundStatic = null; var soundBeep = null; var started = false; obj.init=function() { startTime = new Date().getTime(); soundStatic = new customSound(8000); soundStatic.add(2.5, 4000, 4000, 80, 80, 1, 1 ); soundStatic.createSound().play(); soundBeep = new customSound(8000); soundBeep.add(.3, 550,550,127,127,0,0); started = false; } obj.update=function() { if ( new Date().getTime() - startTime > 2750 ) { if ( started == false ) { soundBeep.createSound().play(); started = true; } GE.DrawRect(0,0,GE.CanvasWidth, GE.CanvasHeight,15); } else { for ( var y = 0; y < GE.CanvasHeight; y++) { for ( var x = 0; x < GE.CanvasWidth; x++ ) { GE.DrawPixel(x,y,Math.floor(Math.random()*16)); } } } if ( new Date().getTime() - startTime > 3000 ) obj.finished = true; } obj.draw=function() { } obj.nextStage = function() { return LoadScreen(data); } obj.init(); return obj; } function LoadScreen(data) { var obj = {}; obj.finished = false; var currentStage; var soundClick; var loadData = [ ["QB 8bit Game Engine V1.0",0,0,false], ["",0,0], ["Ready",0,0], ["",0,0], ["load \"GimmieFrictBby\"",30,6], ["Ready",100,0], ["",0,0], ["run",30,6], ["",50,0]]; var MSG_TEXT=0; var MSG_LINE_PAUSE=1; var MSG_CHAR_PAUSE=2; var msgNum = 0; var msgPos = 0; var keyboardtimer = 0; var xpos = 0; var ypos = 0; var cursorstate = 0; obj.init=function() { GE.DrawRect ( 0,0,GE.CanvasWidth, GE.CanvasHeight, 0); startTime = new Date().getTime(); soundClick = new customSound(8000); soundClick.add(.005, 1000,2000,127,127,0,0); } obj.update=function() { keyboardtimer--; if ( keyboardtimer <= 0 ) { GE.DrawRect(xpos,ypos,8,8,0); if ( msgPos < loadData[msgNum][MSG_TEXT].length ) { if ( loadData[msgNum][MSG_CHAR_PAUSE] > 0 ) soundClick.createSound().play(); GE.WriteString(loadData[msgNum][MSG_TEXT][msgPos],xpos,ypos,15); msgPos+=1; xpos+=8; keyboardtimer = loadData[msgNum][MSG_CHAR_PAUSE]+Math.floor(Math.random()*(loadData[msgNum][MSG_CHAR_PAUSE])); } else { msgNum++; msgPos=0; xpos=0; ypos+=8; if ( msgNum >= loadData.length ) { obj.finished = true; return; } if ( loadData[msgNum][MSG_CHAR_PAUSE] != 0 ) { GE.WriteString(">",xpos,ypos,15); xpos+=8; } keyboardtimer = loadData[msgNum][MSG_LINE_PAUSE]; } } if ( loadData[msgNum][MSG_CHAR_PAUSE] != 0 ) cursorstate = (cursorstate + 1) % 2; else cursorSate = 0; } obj.draw=function() { GE.DrawRect(xpos,ypos,8,8,cursorstate * 15); } obj.nextStage = function() { return Logo(data); } obj.init(); return obj; } function Logo (data) { var obj = {}; var startTime; obj.finished = false; var currentStage; obj.init=function() { startTime = new Date().getTime(); GE.DrawRect(0,0,GE.CanvasWidth, GE.CanvasHeight,0); GE.WriteStringCentered("QuietBloke Productions",GE.CanvasHeight/2-32-8,15,false,true); GE.WriteStringCentered("Presents",GE.CanvasHeight/2,15); GE.WriteStringCentered("Gimmie Friction Baby",GE.CanvasHeight/2+32,15,false,true); GE.WriteStringCentered("I liked the game so much",GE.CanvasHeight/2+56,9); GE.WriteStringCentered("I copied it",GE.CanvasHeight/2+64,9); } obj.update=function() { if ( new Date().getTime() - startTime > 3000 ) obj.finished = true; } obj.draw=function() { } obj.nextStage = function() { return null; } obj.init(); return obj; } obj.init(); return obj; } function InitGame(data) { var obj = {} obj.finished = false; var shotVectors = new Array(); obj.init=function() { if ( data == undefined ) data = new Array(); soundBounce = new customSound(8000); soundBounce.add(.005, 1000,2000,127,127,0,0); GE.DrawRect(0,0,GE.CanvasWidth, GE.CanvasHeight,0); for ( var x=0; x <= GE.CanvasWidth; x+=16) { GE.DrawLine(x,GE.CanvasWidth,x+8,GE.CanvasWidth,12); } if ( GE.SpriteGet("turret") == null ) { CreateTurret(); } DrawScore(data["game"].score, data["game"].hiscore); obj.finished = true; } function CreateTurret() { // create the turret sprite and store all the possible vectors var startangle = Math.PI + Math.PI / 16; var endangle = startangle + Math.PI - Math.PI / 16 *2; var angleinc = (endangle-startangle) /64; var cs = Costume(); var sprNum = 0; for ( var angle = startangle; angle < endangle + 0.001; angle += angleinc ) { var bm = Bitmap(80,80); var endx = 40+30*Math.cos(angle); var endy = 75+30*Math.sin(angle); var x = Math.sin(angle); var y = -Math.cos(angle); shotVectors.push(Vector(Math.cos(angle),Math.sin(angle))); // Draw a wide turret.. sorry... game engine does not support line thickness for ( var off = -6; off < 6; off+=0.2) { bm.DrawLine(endx+x*off,endy+y*off,40+x*off,70+y*off,15); bm.DrawLine(40+x*off,75+y*off,endx+x*off,endy+y*off,15); } bm.DrawCircle(40,75,15,7); bm.DrawRect(40-15,75,30,5,7); cs.SetItem(sprNum,bm); sprNum++; } var sp = Sprite(GE.CanvasWidth/2-40,GE.CanvasHeight-80,cs); sp.SetAnimatorBounce(); GE.SpriteAdd("turret",sp,1); data["game"].cannonVectors = shotVectors; } obj.update = function() { } obj.draw = function () { } obj.nextStage = function() { return MainMenu(data); } obj.init(); return obj; } function MainMenu (data) { var obj = {}; obj.finished = false; var spaceDown = false; obj.init=function() { if ( data == undefined ) data = new Array(); obj.finished = false; for ( var x=0; x <= GE.CanvasWidth; x+=16) { GE.DrawLine(x,GE.CanvasWidth,x+8,GE.CanvasWidth,12); } DrawScore(data["game"].score, data["game"].hiscore); } obj.update = function() { if ( GE.GetKeyState(GE.KEY_SPACE)) { data["game"].useKeyboard = true; } if ( GE.mouseClicked) { data["game"].useKeyboard = false; } if (GE.mouseClicked ) { GE.DrawRect(0,GE.CanvasHeight/2-4,GE.CanvasWidth,8,0); obj.finished = true; } if ( GE.GetKeyState(GE.KEY_SPACE) ) spaceDown = true; if (( !GE.GetKeyState(GE.KEY_SPACE) && spaceDown )) { GE.DrawRect(0,GE.CanvasHeight/2-4,GE.CanvasWidth,8,0); obj.finished = true; } if ( obj.finished ) { data["game"].score = 0; DrawScore(data["game"].score, data["game"].hiscore); } } obj.draw = function () { GE.WriteStringCentered("Fire using : ",40,14); GE.WriteStringCentered(" Space Key ",48,14); GE.WriteStringCentered(" Left Mouse Button",56,14); GE.WriteStringCentered(" Touch Screen ",64,14); GE.WriteStringCentered("Press Fire to Begin",GE.CanvasWidth-12,10); } obj.nextStage = function() { return WaitForShot(data); } obj.init(); return obj; } function WaitForShot (data) { var obj = {}; obj.finished = false; obj.init=function() { } obj.update = function() { var ball = GE.SpriteGet("ball"+data["game"].ballNum); if ( (GE.GetKeyState(GE.KEY_SPACE) || GE.mouseClicked))// && (ball == null || (ball.vel.x == 0 && ball.vel.y == 0 ))) { var animframe = GE.SpriteGet("turret").costumeAnimator.frame; var frame = GE.SpriteGet("turret").costumeAnimator.Get(animframe); var ballVector = data["game"].cannonVectors[frame]; data["game"].ballNum++; NewBall(data["game"].ballNum,ballVector); obj.finished = true; } } function NewBall(ballNum,vector) { var bm = Bitmap(12,12); bm.DrawCircle(5,5,6,15); var bx = GE.CanvasWidth / 2; var by = GE.CanvasHeight-5; var sp = Sprite(bx-6+25*vector.x,by-6+25*vector.y,bm); sp.vel.x = vector.x*2.7; sp.vel.y = vector.y*2.7; GE.SpriteAdd("ball"+ballNum,sp,2); } obj.draw = function() { DrawBalls(data["game"].ballsInCourt); } obj.nextStage = function() { return BallMoving(data); } obj.init(); return obj; } function BallMoving (data) { var obj = {}; obj.finished = false; var gameOver = false; obj.init=function() { } obj.update = function() { var ball = GE.SpriteGet("ball"+data["game"].ballNum); var mag = ball.vel.Magnitude()-.01; if ( mag < 0 ) mag = 0; ball.vel.SetMagnitude(mag); var bounced = false; // check collisions in court if ( ball.pos.x + ball.vel.x < 0 ) { ball.vel.x *= -1; bounced = true; } if ( ball.pos.y + ball.vel.y < 0 ) { ball.vel.y *= -1; bounced = true; } if ( ball.pos.x + 12 + ball.vel.x >= GE.CanvasWidth-1 ) { ball.vel.x *= -1; bounced = true; } if ( ball.pos.y + ball.vel.y < 0 ) { ball.vel.y *= -1; bounced = true; } if ( mag < .1 ) { ball.vel.x = 0; ball.vel.y = 0; obj.finished = true; } if ( ball.pos.y + ball.vel.y + 12 >= GE.CanvasWidth-1 && ball.vel.y >= 0) { obj.finished = true; gameOver = true; } var ballsInCourt = data["game"].ballsInCourt; for(var ballInfo in ballsInCourt) { var targetBall = ballsInCourt[ballInfo]; var ballX = ball.pos.x + ball.vel.x + 6;//ball.bitmap.width/2; var ballY = ball.pos.y + ball.vel.y + 6;//ball.bitmap.width/2; var xdist = targetBall.xpos - ballX; var ydist = targetBall.ypos - ballY; var distance = Math.sqrt((ballX+-targetBall.xpos)*(ballX+-targetBall.xpos) + (ballY+-targetBall.ypos)*(ballY-targetBall.ypos)) var minDistance = 6 + targetBall.radius; if (distance < minDistance) { // first shift our sphere back until we are not overlapping the other sphere var m = Vector(targetBall.xpos - ball.pos.x, targetBall.ypos - ball.pos.y); m.Normalise(); m.ScalarMultiply(minDistance-distance); ball.pos.x -= m.x; ball.pos.y -= m.y; // find the collision normal var collisionNormal = Vector(ballX - targetBall.xpos, ballY - targetBall.ypos); collisionNormal.Normalise() var j = (-2 * (ball.vel.DotProduct(collisionNormal)) / collisionNormal.DotProduct(collisionNormal)) collisionNormal.x *= j collisionNormal.y *= j ball.vel.x += collisionNormal.x ball.vel.y += collisionNormal.y targetBall.lives -= 1; if ( targetBall.lives == 0 ) { data["game"].ballsInCourt[ballInfo] = null; data["game"].ballsInCourt[ballInfo] = CleanUpArray(data["game"].ballsInCourt[ballInfo]); data["game"].score += 1; if ( data["game"].score >data["game"].hiscore ) data["game"].hiscore = data["game"].score; DrawScore(data["game"].score, data["game"].hiscore); } bounced = true; } } if ( bounced ) soundBounce.createSound().play(); } obj.draw = function() { DrawBalls(data["game"].ballsInCourt); } obj.nextStage = function() { if ( gameOver ) { return GameOver(data); } else { return BallExpanding(data); } } function CleanUpArray(arrayObj) { var returnArray = new Array(); for ( var i in arrayObj) { if ( arrayObj[i] != null ) { returnArray[i] = arrayObj[i]; } } return returnArray; } obj.init(); return obj; } function BallExpanding (data) { var obj = {}; obj.finished = false; var gameOver = false; obj.init=function() { var ballsInCourt = data["game"].ballsInCourt; var ball = GE.SpriteGet("ball"+data["game"].ballNum); // ballsInCourt[data["game"].ballNum] = [data["game"].ballNum,ball.pos.x+ball.bitmap.width/2, ball.pos.y+ball.bitmap.width/2, 6, 3]; ballsInCourt[data["game"].ballNum] = Sphere(data["game"].ballNum,ball.pos.x+ball.bitmap.width/2, ball.pos.y+ball.bitmap.width/2, 6, 3); ball.died = true; } obj.update = function() { var ballsInCourt = data["game"].ballsInCourt; var ball = ballsInCourt[data["game"].ballNum]; ball.radius += .5; if ( ball.xpos-ball.radius < 0 ) obj.finished = true; if ( ball.xpos+ball.radius >= GE.CanvasWidth-1 ) obj.finished = true; if ( ball.ypos-ball.radius < 0 ) obj.finished = true; if ( ball.ypos+ball.radius >= GE.CanvasWidth-1 ) obj.finished = true; var ballsInCourt = data["game"].ballsInCourt; for(var ballInfo in ballsInCourt) { var targetBall = ballsInCourt[ballInfo]; var xdist = targetBall.xpos - ball.xpos; var ydist = targetBall.ypos - ball.ypos; if ( targetBall.ballNumber != ball.ballNumber ) if ( xdist * xdist + ydist * ydist <= (ball.radius + targetBall.radius) * (ball.radius + targetBall.radius) ) { obj.finished = true; } } } obj.draw = function() { DrawBalls(data["game"].ballsInCourt); } obj.nextStage = function() { if ( gameOver ) { return GameOver(data); } else { return WaitForShot(data); } } obj.init(); return obj; } function GameOver (data) { var obj = {}; obj.finished = false; var spaceDown = false; obj.init=function() { GE.WriteStringCentered("Game Over",GE.CanvasHeight/2,9,true,true); data["game"].ballsInCourt = Array(); data["game"].ballNum = 0; GE.DeleteSpritesInLayer(2) obj.finished = true; } obj.update = function() { } obj.draw = function() { } obj.nextStage = function() { return MainMenu(data); } obj.init(); return obj; } function DrawScore ( score, hiscore ) { GE.DrawRect(0,GE.CanvasHeight-30,3*16,30,0); GE.DrawRect(GE.CanvasWidth-3*16,GE.CanvasHeight-30,3*16,30,0); var tmpStr = "000" + score; tmpStr = tmpStr.substring(tmpStr.length-3); GE.WriteString("Score",0,GE.CanvasHeight-30,7); GE.WriteString(tmpStr,0,GE.CanvasHeight-16,7,true,true); tmpStr = "000" + hiscore; tmpStr = tmpStr.substring(tmpStr.length-3); GE.WriteString("Hi",GE.CanvasWidth-48,GE.CanvasHeight-30,7); GE.WriteString(tmpStr,GE.CanvasWidth-48,GE.CanvasHeight-16,7,true,true); } function DrawBalls(ballsInCourt) { GE.DrawRect(0,0,GE.CanvasWidth, GE.CanvasWidth,0 ); var colours = [0,8,7,15]; for(var ballInfo in ballsInCourt) { var ball = ballsInCourt[ballInfo]; // GE.DrawCircle(targetBall[1],targetBall[2],targetBall[3],colours[targetBall[4]]); GE.DrawCircle(ball.xpos, ball.ypos, ball.radius, colours[ball.lives]); } } return obj; } Sphere = function(ballNumber, xpos, ypos, radius, lives) { var obj = {}; obj.ballNumber = ballNumber; obj.xpos = xpos; obj.ypos = ypos; obj.radius = radius; obj.lives = lives; return obj; } GameEngine16Colour = function(canvasIn,updateFunction,drawFunction) { "use strict"; var obj = {}; if ( typeof(updateFunction) != "function" ) updateFunction = function(){}; if ( typeof(drawFunction) != "function" ) drawFunction = function(){}; var CANVAS_WIDTH = 320; //320; var CANVAS_HEIGHT = 240; // 240; var FPS = 30; var updateRate = Math.floor(1000 / FPS); var canvas = canvasIn; var colours = new Array(); var charSet = {}; obj.mousePos = new Vector(); obj.mouseClicked = false; obj.mouseTouchFire = false; obj.mouseUnclicked = false; obj.mouseDown = false; obj.CanvasWidth = CANVAS_WIDTH; obj.CanvasHeight = CANVAS_HEIGHT; canvas.width = CANVAS_WIDTH; canvas.height = CANVAS_HEIGHT; obj.sprites = new Array(); // key = spritename, value = layer obj.layers = new Array(); // key = spritename, value = sprite obj.collisionRules = new Array(); var ctx = null; if(canvas.getContext ) { ctx = canvas.getContext('2d'); } if (ctx === null ) { return null; } var screenImageData = ctx.getImageData(0,0,CANVAS_WIDTH,CANVAS_HEIGHT); for ( var pixel = 0; pixel < CANVAS_WIDTH*CANVAS_HEIGHT; pixel+=1) { screenImageData.data[pixel*4+3] = 255; } var screenBitmap = Bitmap(CANVAS_WIDTH,CANVAS_HEIGHT); screenBitmap.DrawRect(0,0,CANVAS_WIDTH,CANVAS_HEIGHT,0); var tempScreenBitmap= Bitmap(CANVAS_WIDTH,CANVAS_HEIGHT); var prevScreenBitmap= Bitmap(CANVAS_WIDTH,CANVAS_HEIGHT); function InitColours() { colours[0] = Colour(0,0,0); colours[1] = Colour(128,0,0); colours[2] = Colour(0,128,0); colours[3] = Colour(0,0,128); colours[4] = Colour(128,128,0); colours[5] = Colour(128,0,128); colours[6] = Colour(0,128,128); colours[7] = Colour(128,128,128); colours[8] = Colour(64,64,64); colours[9] = Colour(255,0,0); colours[10] = Colour(0,255,0); colours[11] = Colour(0,0,255); colours[12] = Colour(255,255,0); colours[13] = Colour(255,0,255); colours[14] = Colour(0,255,255); colours[15] = Colour(255,255,255); // experimental colours // mid black and dark primary colours[16] = TertiaryMix(colours[7],colours[7],32); colours[17] = TertiaryMix(colours[1],colours[1],64); colours[18] = TertiaryMix(colours[2],colours[2],64); colours[19] = TertiaryMix(colours[3],colours[3],64); // secondary colours[20] = TertiaryMix(colours[4],colours[4],64); colours[21] = TertiaryMix(colours[5],colours[5],64); colours[22] = TertiaryMix(colours[6],colours[6],64); // tertiary colours colours[23] = TertiaryMix(colours[1],colours[4],128); colours[24] = TertiaryMix(colours[1],colours[5],128); // colours[18] = TertiaryMix(colours[1],colours[6],128); colours[25] = TertiaryMix(colours[2],colours[4],128); // colours[20] = TertiaryMix(colours[2],colours[5],128); colours[26] = TertiaryMix(colours[2],colours[6],128); // colours[22] = TertiaryMix(colours[3],colours[4],128); colours[27] = TertiaryMix(colours[3],colours[5],128); colours[28] = TertiaryMix(colours[3],colours[6],128); colours[29] = TertiaryMix(colours[4],colours[5],128); colours[30] = TertiaryMix(colours[4],colours[6],128); // colours[28] = TertiaryMix(colours[5],colours[4],128); // colours[29] = TertiaryMix(colours[5],colours[6],128); // colours[30] = TertiaryMix(colours[6],colours[4],128); colours[31] = TertiaryMix(colours[6],colours[5],128); // bright colours[32] = TertiaryMix(colours[15],colours[15],192); colours[33] = TertiaryMix(colours[9],colours[9],192); colours[34] = TertiaryMix(colours[10],colours[10],192); colours[35] = TertiaryMix(colours[11],colours[11],192); // secondary colours[36] = TertiaryMix(colours[12],colours[12],192); colours[37] = TertiaryMix(colours[13],colours[13],192); colours[38] = TertiaryMix(colours[14],colours[14],192); // colours[31] = TertiaryMix(colours[7],colours[7],96); colours[39] = TertiaryMix(colours[9],colours[12],255); colours[40] = TertiaryMix(colours[9],colours[13],255); // colours[34] = TertiaryMix(colours[9],colours[14],255); colours[41] = TertiaryMix(colours[10],colours[12],255); // colours[36] = TertiaryMix(colours[10],colours[13],255); colours[42] = TertiaryMix(colours[10],colours[14],255); // colours[38] = TertiaryMix(colours[11],colours[12],255); colours[43] = TertiaryMix(colours[11],colours[13],255); colours[44] = TertiaryMix(colours[11],colours[14],255); colours[45] = TertiaryMix(colours[12],colours[13],255); colours[46] = TertiaryMix(colours[12],colours[14],255); // colours[44] = TertiaryMix(colours[13],colours[12],255); // colours[45] = TertiaryMix(colours[13],colours[14],255); // colours[46] = TertiaryMix(colours[14],colours[12],255); colours[47] = TertiaryMix(colours[14],colours[13],255); // colours[47] = TertiaryMix(colours[15],colours[15],192); function TertiaryMix (col1,col2,maxValue) { var red = col1.red + col2.red; var green = col1.green + col2.green; var blue = col1.blue + col2.blue; var br = red; if(green > br) br = green; if(blue > br) br = blue; red = Math.floor(red / br * maxValue); green = Math.floor(green / br * maxValue); blue = Math.floor(blue / br * maxValue); return Colour(red,green,blue); } } InitColours(); function InitCharSet() { charSet = CharSet(8,8); charSet.SetChar("\\",[ "* ", " * ", " * ", " * ", " * ", " * ", " * ", " "]); charSet.SetChar("\"",[ " * * ", " * * ", " ", " ", " ", " ", " ", " "]); charSet.SetChar("#",[ " * * ", "******* ", " * * ", " * * ", " * * ", "******* ", " * * ", " "]); charSet.SetChar("$",[ " * ", " ***** ", "* * ", " ***** ", " * * ", " ***** ", " * ", " "]); charSet.SetChar("%",[ " * * ", "* * * ", " * * ", " * ", " * * ", " * * * ", "* * ", " "]); charSet.SetChar("&",[ " *** ", " * * ", " * * ", " ** ", " * * ", "* * ", " **** * ", " "]); charSet.SetChar("'",[ " * ", " * ", " ", " ", " ", " ", " ", " "]); charSet.SetChar("(",[ " * ", " * ", " * ", " * ", " * ", " * ", " * ", " "]); charSet.SetChar(")",[ " * ", " * ", " * ", " * ", " * ", " * ", " * ", " "]); charSet.SetChar("*",[ " ", "* * ", " * * ", " * ", " * * ", "* * ", " ", " "]); charSet.SetChar("+",[ " ", " * ", " * ", " ***** ", " * ", " * ", " ", " "]); charSet.SetChar(",",[ " ", " ", " ", " ", " ", " * ", " * ", " * "]); charSet.SetChar("-",[ " ", " ", " ", " ***** ", " ", " ", " ", " "]); charSet.SetChar(".",[ " ", " ", " ", " ", " ", " ", " * ", " "]); charSet.SetChar("/",[ " * ", " * ", " * ", " * ", " * ", " * ", "* ", " "]); charSet.SetChar("0",[ " ***** ", "* * ", "* * * ", "* * *", "* * * ", "* * ", " ***** ", " "]); charSet.SetChar("1",[ " * ", " ** ", " * ", " * ", " * ", " * ", " *** ", " "]); charSet.SetChar("2",[ " ***** ", "* * ", " * ", " * ", " ** ", " * ", "******* ", " "]); charSet.SetChar("3",[ " ***** ", "* * ", " * ", " **** ", " * ", "* * ", " ***** ", " "]); charSet.SetChar("4",[ " * ", " * ", " * ", " * * ", " ****** ", " * ", " * ", " "]); charSet.SetChar("5",[ "******* ", "* ", "* ", "****** ", " * ", "* * ", " ***** ", " "]); charSet.SetChar("6",[ " ***** ", "* * ", "* ", "****** ", "* * ", "* * ", " ***** ", " "]); charSet.SetChar("7",[ "******* ", " * ", " * ", " * ", " * ", " * ", " * ", " "]); charSet.SetChar("8",[ " ***** ", "* * ", "* * ", " ***** ", "* * ", "* * ", " ***** ", " "]); charSet.SetChar("9",[ " ***** ", "* * ", "* * ", " ****** ", " * ", "* * ", " ***** ", " "]); charSet.SetChar(":",[ " ", " ", " * ", " ", " * ", " ", " ", " "]); charSet.SetChar(";",[ " ", " ", " * ", " ", " * ", " * ", " ", " "]); charSet.SetChar("<",[ " ", " ** ", " ** ", " * ", " ** ", " ** ", " ", " "]); charSet.SetChar("=",[ " ", " ", " ***** ", " ", " ***** ", " ", " ", " "]); charSet.SetChar(">",[ " ", " ** ", " ** ", " * ", " ** ", " ** ", " ", " "]); charSet.SetChar("A",[ " ***** ", "* * ", "* * ", "******* ", "* * ", "* * ", "* * ", " "]); charSet.SetChar("B",[ "****** ", "* * ", "* * ", "******* ", "* * ", "* * ", "****** ", " "]); charSet.SetChar("C",[ " ***** ", "* * ", "* ", "* ", "* ", "* * ", " ***** ", " "]); charSet.SetChar("D",[ "****** ", "* * ", "* * ", "* * ", "* * ", "* * ", "****** ", " "]); charSet.SetChar("E",[ "******* ", "* ", "* ", "***** ", "* ", "* ", "******* ", " "]); charSet.SetChar("F",[ "******* ", "* ", "* ", "***** ", "* ", "* ", "* ", " "]); charSet.SetChar("G",[ " ***** ", "* * ", "* ", "* **** ", "* * ", "* * ", " ***** ", " "]); charSet.SetChar("H",[ "* * ", "* * ", "* * ", "******* ", "* * ", "* * ", "* * ", " "]); charSet.SetChar("I",[ " *** ", " * ", " * ", " * ", " * ", " * ", " *** ", " "]); charSet.SetChar("J",[ "******* ", " * ", " * ", " * ", "* * ", "* * ", " ***** ", " "]); charSet.SetChar("K",[ "* * ", "* ** ", "* ** ", "** ", "* ** ", "* ** ", "* * ", " "]); charSet.SetChar("L",[ "* ", "* ", "* ", "* ", "* ", "* ", "******* ", " "]); charSet.SetChar("M",[ "* * ", "** ** ", "* * * * ", "* * * ", "* * ", "* * ", "* * ", " "]); charSet.SetChar("N",[ "* * ", "** * ", "* * * ", "* * * ", "* * * ", "* ** ", "* * ", " "]); charSet.SetChar("O",[ " ***** ", "* * ", "* * ", "* * ", "* * ", "* * ", " ***** ", " "]); charSet.SetChar("P",[ " ***** ", "* * ", "* * ", "****** ", "* ", "* ", "* ", " "]); charSet.SetChar("Q",[ " ***** ", "* * ", "* * ", "* * ", "* * * ", "** * ", " **** * ", " "]); charSet.SetChar("R",[ "****** ", "* * ", "* * ", "****** ", "* * ", "* * ", "* * ", " "]); charSet.SetChar("S",[ " ***** ", "* * ", "* ", " ***** ", " * ", "* * ", " ***** ", " "]); charSet.SetChar("T",[ "******* ", " * ", " * ", " * ", " * ", " * ", " * ", " "]); charSet.SetChar("U",[ "* * ", "* * ", "* * ", "* * ", "* * ", "* * ", "* * ", " ****** "]); charSet.SetChar("V",[ "* * ", "* * ", " * * ", " * * ", " * * ", " * * ", " * ", " "]); charSet.SetChar("W",[ "* * ", "* * ", "* * ", "* * ", "* * * ", "* * * * ", "** ** ", " "]); charSet.SetChar("X",[ "* * ", " * * ", " * * ", " * ", " * * ", " * * ", "* * ", " "]); charSet.SetChar("Y",[ "* * ", " * * ", " * * ", " * ", " * ", " * ", " * ", " "]); charSet.SetChar("Z",[ "******* ", " * ", " * ", " * ", " * ", " * ", "******* ", " "]); // [\]^_`abcdefg.....z{|}~ charSet.SetChar("[",[ " *** ", " * ", " * ", " * ", " * ", " * ", " *** ", " "]); charSet.SetChar("\\",[ "* ", " * ", " * ", " * ", " * ", " * ", " * ", " "]); charSet.SetChar("]",[ " *** ", " * ", " * ", " * ", " * ", " * ", " *** ", " "]); charSet.SetChar("^",[ " * ", " * * ", " * * ", " ", " ", " ", " ", " "]); charSet.SetChar("_",[ " ", " ", " ", " ", " ", " ", " ", "********"]); charSet.SetChar("`",[ " * ", " * ", " ", " ", " ", " ", " ", " "]); charSet.SetChar("a",[ " ", " ", " **** ", " * ", " ***** ", "* * ", " **** * ", " "]); charSet.SetChar("b",[ "* ", "* ", "***** ", "* * ", "* * ", "* * ", "***** ", " "]); charSet.SetChar("c",[ " ", " ", " **** ", "* ", "* ", "* ", " **** ", " "]); charSet.SetChar("d",[ " * ", " * ", " ***** ", "* * ", "* * ", "* * ", " ***** ", " "]); charSet.SetChar("e",[ " ", " ", " **** ", "* * ", "****** ", "* ", " **** ", " "]); charSet.SetChar("f",[ " *** ", " * ", " * ", "**** ", " * ", " * ", " * ", " "]); charSet.SetChar("g",[ " ", " ", " **** ", "* * ", "* * ", " ***** ", " * ", " **** "]); charSet.SetChar("h",[ "* ", "* ", "* ", "***** ", "* * ", "* * ", "* * ", " "]); charSet.SetChar("i",[ " ", " * ", " ", " * ", " * ", " * ", " * ", " "]); charSet.SetChar("j",[ " ", " * ", " ", " ** ", " * ", " * ", " * ", " **** "]); charSet.SetChar("k",[ "* ", "* ", "* * ", "* * ", "*** ", "* * ", "* * ", " "]); charSet.SetChar("l",[ "* ", "* ", "* ", "* ", "* ", "* ", " *** ", " "]); charSet.SetChar("m",[ " ", " ", "*** ** ", "* * * ", "* * * ", "* * * ", "* * * ", " "]); charSet.SetChar("n",[ " ", " ", "***** ", "* * ", "* * ", "* * ", "* * ", " "]); charSet.SetChar("o",[ " ", " ", " **** ", "* * ", "* * ", "* * ", " **** ", " "]); charSet.SetChar("p",[ " ", " ", "***** ", "* * ", "* * ", "***** ", "* ", "* "]); charSet.SetChar("q",[ " ", " ", " ***** ", "* * ", "* * ", " ***** ", " * ", " * "]); charSet.SetChar("r",[ " ", " ", " **** ", "* ", "* ", "* ", "* ", " "]); charSet.SetChar("s",[ " ", " ", " **** ", "* ", " **** ", "* * ", " **** ", " "]); charSet.SetChar("t",[ " ", " * ", "*** ", " * ", " * ", " * ", " *** ", " "]); charSet.SetChar("u",[ " ", " ", "* * ", "* * ", "* * ", "* * ", " ***** ", " "]); charSet.SetChar("v",[ " ", " ", "* * ", "* * ", " * * ", " * * ", " ** ", " "]); charSet.SetChar("w",[ " ", " ", "* * * ", "* * * ", "* * * ", "* * * ", " ** ** ", " "]); charSet.SetChar("x",[ " ", " ", "* * ", " * * ", " ** ", " * * ", "* * ", " "]); charSet.SetChar("y",[ " ", " ", "* * ", "* * ", "* * ", " ***** ", " * ", " **** "]); charSet.SetChar("z",[ " ", " ", "***** ", " * ", " * ", " * ", "***** ", " "]); charSet.SetChar("{",[ " * ", " * ", " * ", " * ", " * ", " * ", " * ", " "]); charSet.SetChar("|",[ " * ", " * ", " * ", " * ", " * ", " * ", " * ", " "]); charSet.SetChar("}",[ " * ", " * ", " * ", " * ", " * ", " * ", " * ", " "]); charSet.SetChar("~",[ " ", " ", " * ", " ***** ", "* ", " ", " ", " "]); } obj.CharSet = function() { return charSet; } InitCharSet(); var dwCurrentTime=0; var dwLastUpdateTime=(new Date).getTime(); var dwTimeCarriedOver = 0; var dwElapsedTime=0; obj.Main = function() { dwCurrentTime = (new Date).getTime(); dwElapsedTime = dwCurrentTime - dwLastUpdateTime + dwTimeCarriedOver; if(dwElapsedTime >= updateRate) { var frames = Math.floor(dwElapsedTime / updateRate ); dwTimeCarriedOver = dwElapsedTime % updateRate; dwLastUpdateTime = dwCurrentTime; for ( var f = 0; f < frames; f++) { updateFunction(); obj.Update(); obj.mouseClicked = false; obj.mouseUnclicked = false; obj.mouseTouchFire = false; obj.CheckCollisions(); } drawFunction(); obj.Draw(); } } var timer = Timer(FPS, obj.Main); obj.Stop = function() { timer.Stop(); } obj.writeToCanvas = function(bitmap) { var sourceArray = bitmap.GetPixelArray(); var prevArray = prevScreenBitmap.GetPixelArray(); var offset = 0; for ( var p = 0; p < sourceArray.length; p++ ) { if ( sourceArray[p] != prevArray[p]) { prevArray[p] = sourceArray[p]; var colour = colours[sourceArray[p]]; screenImageData.data[offset] = colour.red; screenImageData.data[offset+1] = colour.green; screenImageData.data[offset+2] = colour.blue; } // else // { // screenImageData.data[offset] = 0; // screenImageData.data[offset+1] = 0; // screenImageData.data[offset+2] = 0; // } offset+=4; } ctx.putImageData(screenImageData,0,0); } obj.Update = function() { for ( var l in obj.layers ) { var currentLayer = obj.layers[l]; for ( var i in currentLayer ) { var s = currentLayer[i]; if ( s ) { s.Update(tempScreenBitmap.width, tempScreenBitmap.height); if( s.died ) SpriteDeleteForReal(s.name); } } } } obj.PortraitMode = function() { if (CANVAS_WIDTH > CANVAS_HEIGHT ) { var tmp = CANVAS_WIDTH; CANVAS_WIDTH = CANVAS_HEIGHT; CANVAS_HEIGHT = tmp; obj.CanvasWidth = CANVAS_WIDTH; obj.CanvasHeight = CANVAS_HEIGHT; canvas.width = CANVAS_WIDTH; canvas.height = CANVAS_HEIGHT; screenImageData = ctx.getImageData(0,0,CANVAS_WIDTH,CANVAS_HEIGHT); for ( var pixel = 0; pixel < CANVAS_WIDTH*CANVAS_HEIGHT; pixel+=1) { screenImageData.data[pixel*4+3] = 255; } screenBitmap = Bitmap(CANVAS_WIDTH,CANVAS_HEIGHT); screenBitmap.DrawRect(0,0,CANVAS_WIDTH,CANVAS_HEIGHT,0); tempScreenBitmap= Bitmap(CANVAS_WIDTH,CANVAS_HEIGHT); prevScreenBitmap= Bitmap(CANVAS_WIDTH,CANVAS_HEIGHT); } } obj.CheckCollisions = function() { for ( var r in obj.collisionRules ) { var currentLayer = obj.layers[r]; CheckCollisionsForLayer(currentLayer, obj.collisionRules[r]); } } function CheckCollisionsForLayer(layer, collisionRule) { for ( var spritePos in layer ) { var sprite = layer[spritePos]; if ( sprite ) { sprite.collided = false; for ( var targetLayer in collisionRule) { CheckSpriteCollisionWithLayer(sprite, obj.layers[targetLayer]); } } } } function CheckSpriteCollisionWithLayer(sprite, layer) { if ( layer ) { for ( var spritePos in layer ) { var targetSprite = layer[spritePos]; if( sprite.IsCollidedWith(targetSprite.name)) { sprite.collided = true; targetSprite.collided = true; } } } } obj.CountSpritesInLayer=function(layerName) { var count = 0; var layer = obj.layers[layerName]; for ( var spritePos in layer ) { var sprite = layer[spritePos]; if ( sprite ) { count++; } } return count; } obj.Draw = function() { // copy the screenBitmap and place all the sprites on it // Cloning has been replaced with a permanent temp bitmap and // contents just copied over. This seems to yield significant // speed improvement in chrome. // var bitmap = screenBitmap.Clone(); var size = screenBitmap.width * screenBitmap.height; var targetArray = tempScreenBitmap.GetPixelArray(); var sourceArray = screenBitmap.GetPixelArray(); for ( var p = 0; p < size; p++) { targetArray[p] = sourceArray[p]; } for ( var l in obj.layers ) { var currentLayer = obj.layers[l]; for ( var i in currentLayer ) { var spr = currentLayer[i]; if ( spr ) tempScreenBitmap.Paste(spr.pos.x,spr.pos.y, spr.bitmap ) // bitmap.Paste(s.pos.x,s.pos.y, s.bitmap ) } } obj.writeToCanvas(tempScreenBitmap); // obj.writeToCanvas(bitmap); } obj.DrawPixel = function(pixelX, pixelY,colour) { screenBitmap.SetPixel(Math.floor(pixelX),Math.floor(pixelY),colour); } obj.Clear = function(colour) { screenBitmap.Clear(colour); } obj.DrawLine = function(startX, startY, endX, endY, colour) { screenBitmap.DrawLine(startX, startY, endX, endY, colour) } obj.DrawRect = function(xPos,yPos,width,height,colour) { screenBitmap.DrawRect(xPos, yPos, width, height, colour) } obj.DrawCircle = function(xPos,yPos,radius,colour) { screenBitmap.DrawCircle(xPos,yPos,radius,colour) } obj.Scroll = function(xMovement,yMovement) { screenBitmap.Scroll(xMovement,yMovement); } obj.FlipX = function() { screenBitmap = screenBitmap.FlipX(); } obj.FlipY = function() { screenBitmap = screenBitmap.FlipY(); } obj.Paste = function(xPos,yPos,bitmap) { screenBitmap.Paste(xPos,yPos,bitmap); } obj.DefineColour = function(colour,r,g,b) { colours[colour] = Colour(r,g,b); } function CleanUpArray(arrayObj) { var returnArray = new Array(); for ( var i in arrayObj) { if ( arrayObj[i] != null ) { returnArray[i] = arrayObj[i]; } } return returnArray; } function CountItemsInArray(arrayObj) { var count = 0; for ( var i in arrayObj ) { count++; } return count; } obj.SpriteAdd = function(name,sprite, layer) { obj.SpriteDelete(name); obj.sprites[name] = layer; if (obj.layers[layer] == null ) { obj.layers[layer] = new Array(); } var newLayer = obj.layers[layer]; newLayer[name] = sprite; sprite.name = name; } function SpriteDeleteForReal(name) { var spriteLayer = obj.sprites[name]; if ( spriteLayer ) { obj.layers[spriteLayer][name] = null; obj.layers[spriteLayer] = CleanUpArray(obj.layers[spriteLayer]); if ( CountItemsInArray(obj.layers[spriteLayer]) == 0 ) { obj.layers[spriteLayer] = null; obj.layers = CleanUpArray(obj.layers); } obj.sprites[name] = null; obj.sprites = CleanUpArray(obj.sprites); } } obj.SpriteDelete=function(name) { var spriteLayer = obj.sprites[name]; if ( spriteLayer ) { if ( obj.layers[spriteLayer] ) obj.layers[spriteLayer][name].died = true; } } obj.DeleteSpritesInLayer=function(spriteLayer) { if ( spriteLayer ) { if ( obj.layers[spriteLayer] ) { for ( var i in obj.layers[spriteLayer] ) { obj.layers[spriteLayer][i].died = true; } } } } // obj.GetSpritesInLayer = function(spriteLayer) // { // if ( spriteLayer ) // { // if ( obj.layers[spriteLayer] ) // { // return obj.layers[spriteLayer] // } // } // return null; // } /* obj.WriteChar = function(char, charset, xPos,yPos,colour) { screenBitmap.WriteChar(char, charset, xPos,yPos,colour); } obj.WriteString = function(text,charset, xPos,yPos,colour) { screenBitmap.WriteString(text,charset, xPos,yPos,colour); } */ obj.SpriteGet = function(name) { var spriteLayer = obj.layers[obj.sprites[name]]; if ( spriteLayer ) return spriteLayer[name]; return null; } obj.CollisionRuleAdd = function(sourceLayer, targetLayer) { if ( obj.collisionRules[sourceLayer] == null ) { obj.collisionRules[sourceLayer] = new Array(); } if (obj.collisionRules[sourceLayer][targetLayer] == null ) { obj.collisionRules[sourceLayer][targetLayer] = targetLayer; } } obj.CollisionRuleDelete = function(sourceLayer, targetLayer) { if ( obj.collisionRules[sourceLayer] ) { if ( obj.collisionRules[sourceLayer][targetLayer] ) { obj.collisionRules[sourceLayer][targetLayer] = null; obj.collisionRules[sourceLayer] = CleanUpArray(obj.collisionRules[sourceLayer]); } if ( CountItemsInArray(obj.collisionRules[sourceLayer]) == 0 ) { obj.collisionRules[sourceLayer] = null; obj.collisionRules = CleanUpArray(obj.collisionRules); } } } obj.WriteChar = function(char,xPos,yPos,colour,xbig,ybig) { charSet.WriteChar(char,screenBitmap,xPos,yPos,colour,xbig,ybig) } obj.WriteString = function(text,xPos,yPos,colour,xbig,ybig) { charSet.WriteString(text,screenBitmap,xPos,yPos,colour,xbig,ybig) } obj.WriteStringCentered=function(text,yPos,colour,xbig,ybig) { screenBitmap.WriteStringCentered(text,charSet, yPos,colour,xbig,ybig); } var mouseDownPos = Vector(0,0); var mouseUpPos = Vector(0,0); obj.mouseSwipe = Vector(0,0); function SetMousePos(xp,yp) { var bbox = canvas.getBoundingClientRect(); obj.mousePos.x = (xp - bbox.left) * ( canvas.width / bbox.width); obj.mousePos.y = (yp - bbox.top) * ( canvas.height / bbox.height); } canvas.addEventListener("mousemove", function(evt){ SetMousePos(evt.clientX,evt.clientY); evt.preventDefault(); }); canvas.addEventListener("touchmove", function(evt){ SetMousePos(evt.targetTouches[0].pageX,evt.targetTouches[0].pageY); evt.preventDefault(); }); canvas.addEventListener("mousedown", function(evt){ obj.mouseClicked = true; obj.mouseDown = true; mouseDownPos.x = obj.mousePos.x; mouseDownPos.y = obj.mousePos.y; evt.preventDefault(); }); canvas.addEventListener("touchstart", function(evt){ SetMousePos(evt.targetTouches[0].pageX,evt.targetTouches[0].pageY); obj.mouseClicked = true; obj.mouseDown = true; mouseDownPos.x = obj.mousePos.x; mouseDownPos.y = obj.mousePos.y; if ( evt.targetTouches > 1 ) obj.mouseTouchFire = true; evt.preventDefault(); }); canvas.addEventListener("mouseup", function(evt){ obj.mouseDown = false; obj.mouseUnclicked = true; mouseUpPos.x = obj.mousePos.x; mouseUpPos.y = obj.mousePos.y; obj.mouseSwipe.x = mouseUpPos.x - mouseDownPos.x; obj.mouseSwipe.y = mouseUpPos.y - mouseDownPos.y; evt.preventDefault(); }); canvas.addEventListener("touchend", function(evt){ obj.mouseDown = false; obj.mouseUnclicked = true; mouseUpPos.x = obj.mousePos.x; mouseUpPos.y = obj.mousePos.y; obj.mouseSwipe.x = mouseUpPos.x - mouseDownPos.x; obj.mouseSwipe.y = mouseUpPos.y - mouseDownPos.y; evt.preventDefault(); }); var tiltable = false; obj.tiltX = null; obj.tiltY = null; obj.tiltZ = null; var tiltXBase = 0; var tiltYBase = 0; var tiltZBase = 0; obj.Tilt=function(values) { obj.tiltX = values[0]; obj.tiltY = values[1]; obj.tiltZ = values[2]; if ( tiltable == false && obj.tiltX != null ) { tiltable = true; obj.TiltCalibrate(); } } obj.TiltCalibrate = function() { tiltXBase = obj.tiltX; tiltYBase = obj.tiltY; tiltZBase = obj.tiltZ; } obj.Tiltable = function() { return tiltable; } obj.GetTiltX=function() { var upsidedown = 1; if(obj.tiltZ > 0 ) upsidedown = -1; if ( window.innerWidth < window.innerHeight ) return (obj.tiltX-tiltXBase); else return ( obj.tiltY - tiltYBase )*upsidedown; } // Tilt events if (window.DeviceOrientationEvent) { window.addEventListener("deviceorientation", function () { obj.Tilt([event.gamma,event.beta, event.gamma]); }, true); } // keyboard handler var keycodes = []; obj.GetKeyState=function(keycode) { return keycodes[keycode]; } document.addEventListener("keydown", function(evt){ keycodes[evt.keyCode] = true; }); document.addEventListener("keyup", function(evt){ keycodes[evt.keyCode] = false; }); obj.KEY_ENTER = 13; obj.KEY_ESCAPE = 27; obj.KEY_SPACE = 32; obj.KEY_LEFT = 37; obj.KEY_UP = 38; obj.KEY_RIGHT = 39; obj.KEY_DOWN = 40; obj.KEY_0 = 48; obj.KEY_1 = 49; obj.KEY_2 = 50; obj.KEY_3 = 51; obj.KEY_4 = 52; obj.KEY_5 = 53; obj.KEY_6 = 54; obj.KEY_7 = 55; obj.KEY_8 = 56; obj.KEY_9 = 57; obj.KEY_A = 65; obj.KEY_B = 66; obj.KEY_C = 67; obj.KEY_D = 68; obj.KEY_E = 69; obj.KEY_F = 70; obj.KEY_G = 71; obj.KEY_H = 72; obj.KEY_I = 73; obj.KEY_J = 74; obj.KEY_K = 75; obj.KEY_L = 76; obj.KEY_M = 77; obj.KEY_N = 78; obj.KEY_O = 79; obj.KEY_P = 80; obj.KEY_Q = 81; obj.KEY_R = 82; obj.KEY_S = 83; obj.KEY_T = 84; obj.KEY_U = 85; obj.KEY_V = 86; obj.KEY_W = 87; obj.KEY_X = 88; obj.KEY_Y = 89; obj.KEY_Z = 90; return obj; } Timer = function(fps,callFunction) { var obj = {} var fps = fps; var callFunction = callFunction var timerId = setInterval ( callFunction, Math.floor(1000/fps)); obj.Stop = function() { clearInterval(timerId); } return obj; } Colour = function(red,green,blue) { obj = {} obj.red = red; obj.green = green; obj.blue = blue; return obj; } var BaseBitmap = {}; Bitmap = function(width,height) { var obj = Object.create(BaseBitmap); obj.width = width; obj.height = height; var pixels = new Array() var scrollXFraction = 0.0; var scrollYFraction = 0.0; obj.GetPixelArray = function () { return pixels; } obj.SetPixelArray = function (newArray) { pixels = newArray; } obj.GetPixel = function (xpos, ypos) { if (xpos >= 0 && xpos < obj.width && ypos >= 0 && ypos < obj.height) { return pixels[ypos*width+xpos]; } return 0; } obj.SetPixel = function (xpos,ypos,colour) { if (xpos >= 0 && xpos < obj.width && ypos >= 0 && ypos < obj.height) { pixels[Math.floor(ypos)*width+Math.floor(xpos)] = colour; } } obj.Clear = function (colour) { for ( var ypos = 0; ypos < obj.height; ypos++) { for ( var xpos = 0; xpos < obj.width; xpos++) { obj.SetPixel(xpos,ypos,colour); } } } obj.PixelsFromArray = function(pixelsIn) { var minPixels = Math.min(obj.width*obj.height,pixelsIn.length); for(var p = 0; p < minPixels; p++) { pixels[p] = pixelsIn[p]; } return obj; } obj.DrawLine = function(startX, startY, endX, endY, colour) { var xLen = endX - startX; var yLen = endY - startY; var maxPoints = Math.max(Math.abs(xLen), Math.abs(yLen)) var xDisp = xLen / maxPoints; var yDisp = yLen / maxPoints; for ( var p = 0; p <= maxPoints; p++) { obj.SetPixel(startX + p*xDisp+0.5, startY + p*yDisp+0.5, colour) } } obj.DrawRect = function(xPos,yPos,width,height,colour) { for(var yDisp = 0; yDisp < height; yDisp++ ) { obj.DrawLine(xPos, yPos+yDisp, xPos+width-1, yPos+yDisp,colour) } } obj.DrawCircle = function(xPos,yPos,radius,colour) { var radiusSquared = radius * radius; var xOff; for( var yOff = 0.0; yOff < radius; yOff+= 0.5) { xOff = Math.sqrt(radiusSquared-(yOff*yOff)); obj.DrawLine(xPos-xOff+.5,yPos+yOff+.5,xPos+xOff+.5,yPos+yOff,colour) obj.DrawLine(xPos-xOff+.5,yPos-yOff+.5,xPos+xOff+.5,yPos-yOff,colour) } } obj.Scroll = function(xMovement,yMovement) { scrollXFraction += xMovement; scrollYFraction += yMovement; var xMove = Math.round(scrollXFraction); var yMove = Math.round(scrollYFraction); if(xMove == 0 && yMove == 0) { return; } scrollXFraction -= xMove; scrollYFraction -= yMove; var newPixels = new Array(); var targetXPos = xMove; if(targetXPos < 0) { targetXPos += obj.width; } var targetYPos = yMove if(targetYPos < 0) { targetYPos += obj.height; } for(var yPos = 0; yPos < obj.height; yPos++) { for(var xPos = 0; xPos < obj.width; xPos++) { newPixels[yPos*obj.width+xPos] = pixels[targetYPos*obj.width+targetXPos]; targetXPos += 1 if(targetXPos >= obj.width) { targetXPos -= obj.width; } } targetYPos += 1 if(targetYPos >= obj.height) { targetYPos -= obj.height; } } pixels = newPixels; } // Not ready for relacing scroll function yet. Does not wrap pixels. obj.ScrollNew = function(xMovement,yMovement) { scrollXFraction -= xMovement; scrollYFraction -= yMovement; var xMove = Math.round(scrollXFraction); var yMove = Math.round(scrollYFraction); if(xMove == 0 && yMove == 0) { return; } scrollXFraction -= xMove; scrollYFraction -= yMove; var newBitmap = obj.Clone(); obj.Clear(-1); var targetXPos = xMove; if(targetXPos < 0) { targetXPos += obj.width; } var targetYPos = yMove if(targetYPos < 0) { targetYPos += obj.height; } obj.Paste(xMove,yMove,newBitmap); if(targetYPos > 0 ) obj.Paste(xMove,yMove-obj.height,newBitmap); if(targetYPos < 0 ) obj.Paste(xMove,yMove+obj.height,newBitmap); // obj.Paste(0,0,newBitmap); } obj.FlipX = function () { var newBitmap = Bitmap(obj.width,obj.height); for(var yPos = 0; yPos < obj.height; yPos++) { for(var xPos = 0; xPos < obj.width; xPos++) { newBitmap.SetPixel(xPos,yPos,obj.GetPixel(obj.width-1-xPos,yPos)); } } return newBitmap; } obj.FlipY = function() { var newBitmap = Bitmap(obj.width,obj.height); for(var yPos = 0; yPos < obj.height; yPos++) { for(var xPos = 0; xPos < obj.width; xPos++) { newBitmap.SetPixel(xPos,yPos,obj.GetPixel(xPos,obj.height-1-yPos)); } } return newBitmap; } obj.RotateRight = function() { var newBitmap = Bitmap(obj.width,obj.height); for(var yPos = 0; yPos < obj.height; yPos++) { for(var xPos = 0; xPos < obj.width; xPos++) { newBitmap.SetPixel(yPos,xPos,obj.GetPixel(xPos,obj.height-1-yPos)); } } return newBitmap; } obj.RotateLeft = function() { var newBitmap = Bitmap(obj.width,obj.height); for(var yPos = 0; yPos < obj.height; yPos++) { for(var xPos = 0; xPos < obj.width; xPos++) { newBitmap.SetPixel(yPos,xPos,obj.GetPixel(obj.width-1-xPos,yPos)); } } return newBitmap; } obj.Clone = function() { var newBitmap = Bitmap(obj.width,obj.height); newBitmap.SetPixelArray([].concat(pixels)); // for(var yPos = 0; yPos < obj.height; yPos++) // { // for(var xPos = 0; xPos < obj.width; xPos++) // { // newBitmap.SetPixel(xPos,yPos,obj.GetPixel(xPos,yPos)); // } // } return newBitmap; } obj.Paste = function(xp,yp,bitmap) { var xdisp = Math.round(xp); var ydisp = Math.round(yp); // find the area of overlap // calculate the start y pixel in source and target var srcY = 0; var destY = 0; if (ydisp>0) destY = ydisp; if (ydisp<0) srcY = -ydisp; // calculate the start x pixel in source and target var srcX = 0; var destX = 0; if (xdisp>0) destX = xdisp; if (xdisp<0) srcX = -xdisp; // calculate the height of the overlap var ydist = obj.height-destY; if(bitmap.height-srcY < ydist) ydist = bitmap.height-srcY; // calculate the width of the overlap var xdist = obj.width-destX; if(bitmap.width-srcX < xdist) xdist = bitmap.width-srcX; if ( ydist > 0 && xdist > 0) { for ( var y = 0; y < ydist; y++) { for(var xPos = 0; xPos < bitmap.width; xPos++) { if ( bitmap.GetPixel(xPos,srcY) >= 0 ) { obj.SetPixel(xPos+xdisp,destY,bitmap.GetPixel(xPos,srcY)); } } srcY++; destY++; } } /* for(var yPos = 0; yPos < bitmap.height; yPos++) { if ( yPos +ydisp >= 0 && yPos+ydisp < obj.height) for(var xPos = 0; xPos < bitmap.width; xPos++) { if ( bitmap.GetPixel(xPos,yPos) >= 0 ) obj.SetPixel(xPos+xdisp,yPos+ydisp,bitmap.GetPixel(xPos,yPos)); } } */ } obj.WriteChar = function(char, charset, xPos,yPos,colour,xbig,ybig) { charSet.WriteChar(char,obj,xPos,yPos,colour,xbig,ybig); } obj.WriteString = function(text,charSet, xPos,yPos,colour,xbig,ybig) { charSet.WriteString(text,obj,xPos,yPos,colour,xbig,ybig); } obj.WriteStringCentered = function(text, charset, yPos,colour,xbig,ybig) { if (xbig == undefined) xbig = false; if ( xbig ) charset.WriteString(text,obj,width/2-charset.GetWidth()/2*text.length*2,yPos, colour,xbig,ybig); else charset.WriteString(text,obj,width/2-charset.GetWidth()/2*text.length,yPos, colour,xbig,ybig); } obj.Clear(-1); return obj; } CharSet = function (width, height) { if (width == undefined) width = 8; if (height == undefined) height = 8; var obj = {}; var charData = {}; obj.GetWidth = function () { return width; } obj.GetHeight = function () { return height; } obj.SetChar = function(char, chardata ) { charData[char] = chardata; } obj.WriteChar = function(char,bitmap,xPos,yPos,colour,xbig,ybig) { if(typeof(xbig)==='undefined') xbig = false; if(typeof(ybig)==='undefined') ybig = false; var rowData = charData[char]; if ( rowData != undefined ) { for ( var row = 0; row < rowData.length; row++) { var line = rowData[row]; for ( var charPos = 0; charPos < line.length; charPos++) { var chr = line.charAt(charPos); if ( chr != " " ) { if ( !xbig && !ybig) bitmap.SetPixel(charPos+xPos, row+yPos, colour ); if ( xbig && !ybig) { bitmap.SetPixel(charPos*2+xPos, row+yPos, colour ); bitmap.SetPixel(charPos*2+xPos+1, row+yPos, colour ); } if ( !xbig && ybig) { bitmap.SetPixel(charPos+xPos, row*2+yPos, colour ); bitmap.SetPixel(charPos+xPos, row*2+yPos+1, colour ); } if ( xbig && ybig) { bitmap.SetPixel(charPos*2+xPos, row*2+yPos, colour ); bitmap.SetPixel(charPos*2+xPos+1, row*2+yPos, colour ); bitmap.SetPixel(charPos*2+xPos, row*2+yPos+1, colour ); bitmap.SetPixel(charPos*2+xPos+1, row*2+yPos+1, colour ); } } } } } } obj.WriteString = function(text,bitmap,xPos,yPos,colour,xbig,ybig) { if(typeof(xbig)==='undefined') xbig = false; for ( var charPos = 0; charPos < text.length; charPos++) { var chr = text.charAt(charPos); if ( xbig) obj.WriteChar(chr,bitmap,xPos+charPos*2*width, yPos, colour,xbig,ybig ); else obj.WriteChar(chr,bitmap,xPos+charPos*width, yPos, colour,xbig,ybig ); } } return obj; } Sprite = function (xp,yp,costumeOrBitmap) { if (costumeOrBitmap === null ) return null; var costume = costumeOrBitmap; if ( BaseBitmap.isPrototypeOf(costumeOrBitmap)) { costume = Costume(); costume.SetItem(0,costumeOrBitmap); } else { if ( !BaseCostume.isPrototypeOf(costumeOrBitmap)) return null; } var obj = {}; var lifeSpan = -1; obj.died = false; obj.name = ""; obj.pos = Vector(xp,yp); obj.vel = Vector(0,0); obj.costume = costume; obj.costumeAnimator = Animator([0]); obj.bitmap = costume.GetItem(0); obj.wrap = false; obj.collided = false; obj.collidedList = new Array(); obj.SetLifeSpan = function(newLifeSpan) { lifeSpan = newLifeSpan; } obj.SetCostume = function(costume) { obj.costume = costume; } obj.SetWrapOn = function() { obj.wrap = true; } obj.SetWrapOff = function() { obj.wrap = false; } obj.SetAnimator = function(frameList) { obj.costumeAnimator = Animator(frameList); } obj.SetAnimatorBounce = function () { obj.costumeAnimator.Bounce(obj.costume.Length()); } obj.SetAnimatorLoop = function () { obj.costumeAnimator.Loop(obj.costume.Length()); } obj.SetAnimatorFrame = function(frame) { obj.costumeAnimator.Set(frame); obj.bitmap = obj.costume.GetItem(obj.costumeAnimator.Get()); } obj.GetAnimatorFrame = function(frame) { return obj.costumeAnimator.Get(); } obj.SetCostumeAnimator = function(animator) { obj.costumeAnimator = animator; } obj.SetAnimatorUpdateRate = function (updateRate) { obj.costumeAnimator.SetUpdateRate(updateRate); } obj.SetMovement = function(xm,ym) { obj.vel.x = xm; obj.vel.y = ym; return obj; } obj.IsCollidedWith = function(targetSpriteName) { var target = GE.SpriteGet(targetSpriteName); if ( target == undefined ) return false; // simple bounding box check will do for now if ( obj.pos.x < target.pos.x + target.bitmap.width && obj.pos.x + obj.bitmap.width > target.pos.x && obj.pos.y < target.pos.y + target.bitmap.height && obj.pos.y + obj.bitmap.height > target.pos.y ) { // return true; var collided = false; // now lets do a pixel perfect collision check // get the biggest left var boxLeftPos = obj.pos.x; if ( boxLeftPos < target.pos.x ) boxLeftPos = target.pos.x; // get the smallest right var boxRightPos = obj.pos.x + obj.bitmap.width; if ( boxRightPos > target.pos.x + target.bitmap.width) boxRightPos = target.pos.x + target.bitmap.width; // get the biggest top var boxTopPos = obj.pos.y; if ( boxTopPos < target.pos.y ) boxTopPos = target.pos.y; // get the smallest bottom var boxBottomPos = obj.pos.y + obj.bitmap.height; if ( boxBottomPos > target.pos.y + target.bitmap.height) boxBottomPos = target.pos.y + target.bitmap.height; var overlapWidth = boxRightPos - boxLeftPos; var overlapHeight = boxBottomPos - boxTopPos; var sx = Math.floor(boxLeftPos - obj.pos.x); var sy = Math.floor(boxTopPos - obj.pos.y); var tx = Math.floor(boxLeftPos - target.pos.x); var ty = Math.floor(boxTopPos - target.pos.y); for ( var y = 0; y < overlapHeight; y++) { for ( var x = 0; x < overlapWidth; x++ ) { var p1 = obj.bitmap.GetPixel(sx+x,sy+y); var p2 = target.bitmap.GetPixel(tx+x,ty+y); if (p1 != -1 && p2 != -1 ) collided = true; } } return collided; } return false; } obj.Update = function (scrWidth, scrHeight) { obj.pos.x += obj.vel.x; obj.pos.y += obj.vel.y; if ( obj.wrap ) { if ( obj.pos.x < 0 ) obj.pos.x += scrWidth; if ( obj.pos.y < 0 ) obj.pos.y += scrHeight; if ( obj.pos.x >= scrWidth ) obj.pos.x -= scrWidth; if ( obj.pos.y >= scrHeight ) obj.pos.y -= scrHeight; } obj.costumeAnimator.Update(); obj.bitmap = obj.costume.GetItem(obj.costumeAnimator.Get()); obj.layerChanged = false; if ( lifeSpan != -1 ) { lifeSpan--; if (lifeSpan == 0 ) obj.died = true; } } // obj.Draw = function () // { // // } return obj; } Vector = function(x,y) { var obj = {}; obj.x = x; obj.y = y; obj.Add = function(v1) { return Vector(obj.x+v1.x, obj.y+v1.y); } obj.Subtract = function(v1) { return Vector(obj.x-v1.x, obj.y-v1.y); } obj.Multiply = function(v1) { return Vector(obj.x*v1.x, obj.y*v1.y); } obj.Magnitude = function() { return Math.sqrt(obj.x*obj.x + obj.y*obj.y); } obj.Normalise = function() { var mag = obj.Magnitude(); obj.x /= mag; obj.y /= mag; } obj.SetMagnitude = function(newMagnitude) { obj.Normalise(); obj.x *= newMagnitude; obj.y *= newMagnitude; } obj.DotProduct = function(v) { return obj.x * v.x + obj.y * v.y; } obj.ScalarMultiply = function(scalar) { obj.x *= scalar; obj.y *= scalar; } return obj; } var BaseCostume = {}; Costume = function() { var obj = Object.create(BaseCostume); obj.bitmaps = Array(); obj.SetItem = function (frame, bitmap) { // try to set a frame to something other than a bitmap will result in nothing happening if ( !BaseBitmap.isPrototypeOf(bitmap)) return; obj.bitmaps[frame] = bitmap; }; obj.SetItems = function (bitmaps,startFrame) { var frame = startFrame; for(var bitmapPos = 0; bitmapPos < bitmaps.length; bitmapPos++) { obj.SetItem(frame, bitmaps[bitmapPos]); frame++; } }; obj.Length = function () { return obj.bitmaps.length; } obj.GetItem = function (frame) { return obj.bitmaps[frame]; } obj.Update = function() { } return obj; } Animator = function (frameList) { var obj = {}; obj.frame = 0; obj.updateRate = 1; obj.repeat = true; if (frameList == null ) { obj.frameList = new Array(); } else { obj.frameList = frameList; } var paused = false; obj.Bounce = function (frames) { var list = new Array(); var f = 0; for ( var i = 0; i < frames; i++) { list[f] = i; f++; } for ( var i = frames-2; i > 0; i--) { list[f] = i; f++; } obj.frameList = list; } obj.Loop = function (frames) { var list = new Array(); var f = 0; for ( var i = 0; i < frames; i++) { list[f] = i; f++; } obj.frameList = list; } obj.Update = function () { if ( paused == false ) { var prevFrame = obj.frame; obj.frame = (obj.frame + obj.updateRate) % obj.frameList.length; if ( obj.frame < prevFrame ) if ( !obj.repeat ) { obj.frame = prevFrame; obj.Pause(); } } } obj.Get = function() { return obj.frameList[Math.floor(obj.frame)]; } obj.Set = function(frame) { obj.frame = Math.floor(frame % obj.frameList.length); } obj.SetUpdateRate = function(updateRate) { obj.updateRate = updateRate; obj.waitTime = 0; } obj.Start = function () { frame = 0; paused = false; } obj.Resume = function () { paused = false; } obj.Stop = function () { frame = 0; paused = true; } obj.Pause = function () { paused = true; } obj.Resume = function () { paused = false; } return obj; } function customSound(bitrate) { this.bitrate = bitrate; this.angle = 0; this.customSoundData=[]; this.totalDuration = 0; this.dataPos = 0; customSound.prototype.add = function(duration, startFreq, endFreq, startVol, endVol, startNoise, endNoise ) { var vol = startVol; var freq = startFreq; var angleInc = (2*Math.PI)/this.bitrate * freq; var noise = startNoise; for ( var x=0; x < this.bitrate*duration; x++) { vol = (startVol + (endVol - startVol)*x/(this.bitrate*duration)); this.customSoundData[this.dataPos] = (Math.sin(this.angle) + (Math.random()*noise)) * vol; freq = startFreq + (endFreq - startFreq)*x/(this.bitrate*duration); noise = startNoise + (endNoise - startNoise)*x/(this.bitrate*duration); angleInc = (2*Math.PI)/this.bitrate * freq; this.angle+=angleInc; if ( this.angle > Math.PI * 2 ) { this.angle -= Math.PI * 2; } this.dataPos++; } } customSound.prototype.createSound = function() { var n = this.customSoundData.length; var header = "RIFF****WAVEfmt \x10\x00\x00\x00\x01\x00\x01\x00********\x01\x00\x08\x00data****"; // ChunkSize var numval = n + 36; header=insertLong(header,4,numval); numval = this.bitrate; // byterate header = insertLong(header,24,numval); // BitRate numval = this.bitrate * 8; //44100; header = insertLong(header,28,numval); // Subchunk2Size // insertLong(n); numval = n; header = insertLong(header,40,numval); // Output sound data for (var i = 0; i < n; ++i) { var charCode = Math.round(Math.min(127, Math.max(-127, this.customSoundData[i]))+127); header += String.fromCharCode(charCode); } // var o= document.getElementById('output'); var h = btoa(header); h = 'data:audio/wav;base64,' + h; var a = new Audio(); a.src = h; return a; } } function insertLong(inString, index, inValue) { var retString = inString.substr(0,index); for (i = 0; i < 4; ++i) { retString += String.fromCharCode(inValue & 255); inValue = inValue >> 8; } retString += inString.substr(index+4); return retString; } // last piece of code is to start the game init(); </script> </html>