JavaScript Source Code
/* canvas game library */
var
paintInterval=30; /* roughly 30fps after 'overhead' */
timeReport=document.getElementById('timeReport'),
upSize=document.getElementById('upSize'),
cCanvas=document.createElement('canvas'),
frameControl=0,
frameAdjust=1,
mouseX=0,
mouseY=0,
bobbing1=0,
bobbing2=0.5,
bobbing1dir=0.03,
bobbing2dir=0.04,
resX=640, resY=400,
aspect=resY/resX;
maxX=resX-1, maxY=resY-1,
renderScale=1,
throttleCount=0,
throttleTime=0,
skyGradient=0,
waterLines=[],
destroyers=[],
subs=[],
oceanOffset=maxY-72;
landGradient=0,
landStartX=resX/4
landEndX=landStartX*3,
landOffsetY=oceanOffset-3,
cities=[],
hexRadians=Math.PI/16;
function cityScape(offsetX,offsetY) {
this.buildingHeight=[];
this.buildingX=[];
this.buildingColor=[];
this.offsetY=offsetY;
for (var n=0; n<16; n++) {
this.buildingHeight[n]=10+Math.floor(Math.random()*14)*Math.sin(n*hexRadians);
this.buildingX[n]=n*4+offsetX;
var cc=128+Math.floor(Math.random()*96);
this.buildingColor[n]='rgb('+cc+','+cc+','+cc+')';
}
for (var n=0; n<8; n++) {
this.buildingHeight[n+16]=6+Math.floor(Math.random()*8)*Math.sin((n*2)*hexRadians);
this.buildingX[n+16]=n*8+offsetX+2;
var cc=96+Math.floor(Math.random()*64);
this.buildingColor[n+16]='rgb('+cc+','+cc+','+cc+')';
}
this.paint=function() {
for (var n=0; n<24; n++) {
cContext.fillStyle=this.buildingColor[n];
cContext.fillRect(this.buildingX[n],this.offsetY-this.buildingHeight[n],4,this.buildingHeight[n]);
}
}
} // cityScape
function waterLine(count,dirSpeed,maxY,startColor,startY,endColor,endY) {
this.x=[];
this.y=[];
this.dir=[];
this.count=count;
this.inc=Math.ceil(resX/count);
this.randomSkew=this.inc*0.5;
this.offsetX=(this.inc-this.randomSkew)/2;
this.half=Math.floor(this.inc/2);
this.count+=2;
this.maxY=-maxY; // n-1
this.startY=startY;
this.endY=endY;
this.dirSpeed=dirSpeed;
this.gradient=cContext.createLinearGradient(0,startY,0,endY);
this.gradient.addColorStop(0,startColor);
this.gradient.addColorStop(1,endColor);
for (var t=0; t=0) {
x[t]=cpx+half+Math.floor((Math.random()*randomSkew-offsetX));
dir[t]=-(0.1+Math.random()/dirSpeed);
} else if (y[t]3) || (tb<0)) {
this.bobRate=-this.bobRate;
} else this.bob=tb;
with (cContext) {
save();
translate(this.x,this.y+this.bob);
scale(this.direction,1);
save();
translate(-10.5,-1.5);
rotate(this.aim);
fillStyle='#248';
fillRect(-0.5,-4,0.5,4);
restore();
fillStyle='#888';
fillRect(-3,-9,1,5);
fillStyle='#AAA';
beginPath();
moveTo(-6,-1);
lineTo(-5,-5);
lineTo(-1,-5);
lineTo(0,-2);
lineTo(1,-2);
lineTo(1,-4);
lineTo(3,-4);
lineTo(4,-1);
closePath();
fill();
beginPath();
moveTo(4.5,-1);
lineTo(4.5,-3);
lineTo(7,-3);
lineTo(8,-2);
lineTo(10,-2);
lineTo(11,-1);
closePath();
fill();
fillStyle='#BBB';
beginPath();
moveTo(-8,-1);
lineTo(-7,-2);
lineTo(-5,-2);
lineTo(-4,-4);
lineTo(-2,-4);
lineTo(-2,-3);
lineTo(1,-3);
lineTo(1,-1);
closePath();
fill();
fillStyle='#666';
fillRect(-5,-5.5,4,0.5);
fillRect(-4,-4.5,2,0.5);
fillRect(1,-4.5,2,0.5);
fillRect(4.5,-3.5,2.5,0.5);
fillRect(4.5,-2.5,5.5,0.5);
fillStyle='#999';
fillRect(6,-1.5,1,0.5);
fillRect(8,-1.5,1,0.5);
fillRect(-7,-1.5,1,0.5);
fillRect(-5,-1.5,1,0.5);
fillRect(-3,-1.5,1,0.5);
fillRect(-1,-1.5,1,0.5);
desGradient=createLinearGradient(0,-3,0,2);
with (desGradient) {
addColorStop(0,'#888');
addColorStop(0.7,'#CCC');
}
fillStyle=desGradient;
beginPath()
moveTo(-16,2);
quadraticCurveTo(-18,-1,-22,-2.5);
quadraticCurveTo(-18,-1,-10,-1);
lineTo(21,-1);
lineTo(21,0);
lineTo(20,0);
lineTo(19,2);
closePath();
fill();
desRadial=createRadialGradient(-11.7,-2,0.1,-11,-0.5,3);
with (desRadial) {
addColorStop(0,'#FFF');
addColorStop(1,'#666');
}
fillStyle=desRadial;
beginPath();
moveTo(-9,-1);
quadraticCurveTo(-9,-2.5,-10.5,-2.5);
quadraticCurveTo(-12,-2.5,-12,-1);
closePath();
fill();
restore();
}
}
} // destroyer
function submarine(x,y,rotate) {
this.x=x;
this.y=y;
this.rotate=rotate;
this.bob=0;
this.bobRate=-0.05+Math.random()*0.05;
this.paint=function() {
var tb=this.bob+this.bobRate;
if ((tb>1.5) || (tb<0)) {
this.bobRate=-this.bobRate;
} else this.bob=tb;
with (cContext) {
subGradient=createLinearGradient(0,-3,0,2);
with (subGradient) {
addColorStop(0,'#8AD');
addColorStop(0.5,'#78B');
addColorStop(1,'#346');
}
save();
translate(this.x,this.y+this.bob);
if (this.rotate==0) {
fillStyle='#579';
fillRect(-0.5,-8,1,6);
} else {
save();
scale(this.rotate,1);
fillStyle='#579';
fillRect(15,-3,4,4.5);
beginPath();
moveTo(-16,-7);
lineTo(-12,-7);
lineTo(-9,-3);
lineTo(-16,-3);
fill();
fillStyle='#67A';
fillRect(-16,-5,3,1);
fillStyle=subGradient;
beginPath();
moveTo(-19,2);
quadraticCurveTo(-24,2,-24,0);
quadraticCurveTo(-24,-3,-20,-3);
lineTo(-9,-3);
quadraticCurveTo(6,-3,24,-1);
quadraticCurveTo(6,2,0,2)
closePath();
fill();
restore(); // scale
}
fillStyle=subGradient;
beginPath();
arc(0,-0.5,2.5,0,Math.PI*2,false);
fill();
var plane=Math.abs(1-this.rotate)*2;
fillStyle='#67A';
fillRect(-13*this.rotate-3,-5,3+plane,1);
if (!rotate==0) {
save();
scale(this.rotate,1);
fillStyle='#346';
fillRect(-15,-10,1,3);
fillStyle='#45B';
fillRect(-22,-1,2,1);
fillRect(-8,-3,2,1);
fillRect(-5,-3,2,1);
fillRect(-2,-3,2,1);
fillRect(1,-3,2,1);
fillStyle='#89A';
fillRect(-22,0,2,1);
fillRect(-22,-2,2,1);
restore(); // scale
}
restore(); // translate
}
}
}
function world_prepare() {
var skyBreak=(oceanOffset+4)/(maxY-32);
skyGradient=cContext.createLinearGradient(0,0,0,maxY-32);
with (skyGradient) {
addColorStop(0,'#0AF');
addColorStop(skyBreak-0.01,'#DEF');
addColorStop(skyBreak,'#6BF');
addColorStop(skyBreak+0.02,'#08F');
}
landGradient=cContext.createLinearGradient(0,landOffsetY,0,landOffsetY+22);
with (landGradient) {
addColorStop(0,'#4B2');
addColorStop(0.4,'#0C0');
addColorStop(0.8,'#FF8');
}
waterLine[0]=new waterLine(48,6,2,'#08F',oceanOffset+8, '#06F',oceanOffset+30);
waterLine[1]=new waterLine(32,5,3,'#05F',oceanOffset+16,'#08F',oceanOffset+35);
waterLine[2]=new waterLine(24,4,4,'#008',oceanOffset+32,'#002',maxY);
cities[0]=new cityScape(18,landOffsetY+13);
cities[1]=new cityScape(190,landOffsetY+13);
cities[2]=new cityScape(380,landOffsetY+13);
cities[3]=new cityScape(552,landOffsetY+13);
destroyers[0]=new destroyer(50,oceanOffset+24,-1);
destroyers[1]=new destroyer(maxX-50,oceanOffset+24,1);
subs[0]=new submarine(135,oceanOffset+47,1);
subs[1]=new submarine(maxX-135,oceanOffset+47,-1);
}
function canvas_landMasses() {
with (cContext) {
fillStyle=landGradient;
beginPath();
moveTo(-20,landOffsetY+20);
quadraticCurveTo(60,landOffsetY,120,landOffsetY+20);
fill();
beginPath();
moveTo(140,landOffsetY+20);
quadraticCurveTo(220,landOffsetY+5,260,landOffsetY+5);
quadraticCurveTo(300,landOffsetY+15,320,landOffsetY+7);
quadraticCurveTo(360,landOffsetY+5,380,landOffsetY+10);
quadraticCurveTo(440,landOffsetY+5,maxX-140,landOffsetY+20);
fill();
beginPath();
moveTo(maxX-120,landOffsetY+20);
quadraticCurveTo(maxX-60,landOffsetY,maxX+20,landOffsetY+20);
fill();
}
}
function canvas_paint() {
with (cContext) {
clearRect(0,0,maxX,maxY);
fillStyle=skyGradient;
fillRect(0,0,maxX,maxY);
}
for (var n=0; n<3; n++) {
switch (n) {
case 0:
// aircraft go here
break;
case 1:
canvas_landMasses();
for (var t=0; t=10) {
throttleCount=1;
throttleTime=throttleAverage;
}
frameAdjust=paintInterval/throttleAverage;
timeReport.innerHTML=Math.floor(1000/throttleAverage)+' fps - '+mouseX+':'+mouseY;
}
function canvas_onLoad() {
var d=new Date();
oldTime=d.getTime();
frameControl=setInterval(canvas_paint,paintInterval);
}
function canvas_fixAspect() {
with (document) {
var calcWidth=body.clientWidth-32;
var testHeight=body.clientHeight-60;
var container=getElementById('canvasHolder');
}
if (calcWidth<256) calcWidth=256;
if (!upSize.checked) {
if (resX>calcWidth) {
var calcHeight=Math.floor(calcWidth*aspect);
} else {
calcWidth=resX;
var calcHeight=resY;
}
} else {
var calcHeight=Math.floor(calcWidth*aspect);
}
if (calcHeight>testHeight) {
calcHeight=testHeight;
calcWidth=Math.floor(calcHeight/aspect);
}
cCanvas.style.width=calcWidth+'px';
cCanvas.style.height=calcHeight+'px';
renderScale=resX/(calcWidth);
}
function canvas_mouseMove(nonIE) {
if (nonIE) {
mouseX=nonIE.clientX;
mouseY=nonIE.clientY;
} else {
mouseX=event.clientX;
mouseY=event.clientY;
}
mouseX=Math.floor((mouseX-cCanvas.offsetLeft)*renderScale);
mouseY=Math.floor((mouseY-cCanvas.offsetTop)*renderScale);
}
if (cCanvas.getContext) {
var container=document.getElementById('canvasHolder');
container.replaceChild(cCanvas,container.firstChild);
cCanvas.width=maxX;
cCanvas.height=maxY;
cCanvas.onmousemove=canvas_mouseMove;
var cContext=cCanvas.getContext('2d');
world_prepare();
if (window.addEventListener){
window.addEventListener('resize',canvas_fixAspect,false);
window.addEventListener('load',canvas_onLoad,false);
} else if (window.attachEvent){
window.attachEvent("onresize",canvas_fixAspect);
window.attachEvent("onload",canvas_onLoad);
} else {
window.onresize=canvas_fixAspect;
window.onload=canvas_onLoad;
}
upSize.onchange=canvas_fixAspect;
canvas_fixAspect();
}