Skip to content

Instantly share code, notes, and snippets.

@Bolloxim
Created August 1, 2014 09:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Bolloxim/b9d864b23b58e7504411 to your computer and use it in GitHub Desktop.
Save Bolloxim/b9d864b23b58e7504411 to your computer and use it in GitHub Desktop.
A Pen by Andi Smithers.
j<body>
<canvas id='test'></canvas>
</body>

old school rotation and projection

Rotation and projection without using a matrix. However old school days those sin,cos, tan functions would be fast giving just 8 mults and 3 lookups. These days a good matrix with projection built in will be faster and more verstatile. Although I do infinite loop through the asteriods regardless of position.

version 0.3 added NME's

version 0.2 resorted to adding matrices, couldnt solve ypr gimbal lock issues otherwise.

A Pen by Andi Smithers on CodePen.

License.

// constant options
const focalDepth = 80;
const focalPoint = 256;
const eNone = 0;
const eGalacticScanner = 1;
const eLongRange = 2;
// variables
var centreX;
var centreY;
var mouseX=0;
var mouseY=0;
var frameCount=0;
var gameStart;
var context;
var canvas;
var fontsize = 20;
var overlayMode = eNone;
// test multiple groups
var boardPieces = [];
var mapScale = {x:0, y:0};
var border = {x:0, y:0};
var galaxyMapSize = {x:16, y:8};
var shipLocation = {x:0, y:0};
var shipPosition = {x:0, y:0};
var localPosition = {x:0, y:0, z:0};
var shipPing = {x:0, y:0};
var pingRadius = 100;
var lastCycle = 0;
var cycleScalar = 1; // 3 = 30 seconds.
var targetBase = 0;
var localSpaceCubed = 1024;
var lrScale = {x:0, y:0};
var warpLocation = {x:0, y:0};
var warpAnim = 0;
var warpLocked = false;
// difficulty settings
var maxAsteriods = 32;
var maxNMEs = 8;
var orientation = new matrix3x3();
// basic matrices 3x3
function matrix3x3()
{
// set as identiy
this.m = [1,0,0,0,1,0,0,0,1];
}
matrix3x3.prototype.clone = function(matrix)
{
this.m = matrix.m.slice(0);
}
matrix3x3.prototype.rotateX = function(angle)
{
var c = Math.cos(angle);
var s = Math.sin(angle);
this.m = [1,0,0,0,c,-s,0, s, c];
}
matrix3x3.prototype.rotateY = function(angle)
{
var c = Math.cos(angle);
var s = Math.sin(angle);
this.matrix = [c,0,s,0,1,0,-s, 0, c];
}
matrix3x3.prototype.rotateZ = function(angle)
{
var c = Math.cos(angle);
var s = Math.sin(angle);
this.m = [c,-s,0,s,c,0,0,0,1];
}
matrix3x3.prototype.multiply = function(matrix)
{
var m1 = this.m;
var m2 = matrix.m;
return {m:[m1[0] * m2[0] + m1[1] * m2[3] + m1[2] * m2[6],
m1[0] * m2[1] + m1[1] * m2[4] + m1[2] * m2[7],
m1[0] * m2[2] + m1[1] * m2[5] + m1[2] * m2[8],
m1[3] * m2[0] + m1[4] * m2[3] + m1[5] * m2[6],
m1[3] * m2[1] + m1[4] * m2[4] + m1[5] * m2[7],
m1[3] * m2[2] + m1[4] * m2[5] + m1[5] * m2[8],
m1[6] * m2[0] + m1[7] * m2[3] + m1[8] * m2[6],
m1[6] * m2[1] + m1[7] * m2[4] + m1[8] * m2[7],
m1[6] * m2[2] + m1[7] * m2[5] + m1[8] * m2[8]] };
}
matrix3x3.prototype.transform = function(tx, ty, tz)
{
return {x:tx*this.m[0]+ty*this.m[1]+tz*this.m[2],
y:tx*this.m[3]+ty*this.m[4]+tz*this.m[5],
z:tx*this.m[6]+ty*this.m[7]+tz*this.m[8]};
}
var nmes = [];
function SetupNMEs()
{
for (var i=0; i<maxNMEs; i++)
{
var nme = new NME();
nme.randomize(0);
nmes.push(nme);
}
}
function UpdateNMEs()
{
for (var i=0; i<nmes.length; i++)
{
nmes[i].pos.x = modulo( nmes[i].pos.x + nmes[i].vel.x * nmes[i].speed );
nmes[i].pos.y = modulo( nmes[i].pos.y + nmes[i].vel.y * nmes[i].speed );
nmes[i].pos.z = modulo( nmes[i].pos.z + nmes[i].vel.z * nmes[i].speed );
}
}
// nme objects
function NME()
{
this.pos = {x:0, y:0, z:0};
this.rot = {y:0, p:0, r:0};
this.vel = {x:0, y:0, z:0};
this.speed = 0;
this.damage = 0;
this.type = 0; // 0 = fighter, 1 = cruiser, 2 = basestar
}
NME.prototype.randomize = function(type)
{
this.pos.x = Math.random()*localSpaceCubed;
this.pos.y = Math.random()*localSpaceCubed;
this.pos.z = Math.random()*localSpaceCubed;
this.vel.x = Math.random()*2-1;
this.vel.y = Math.random()*2-1;
this.vel.z = Math.random()*2-1;
this.speed = 1.2;
}
// depth modulo fucntion. custom
function modulo(a)
{
// depth range is 1024
const b = 1024;
return a-b * Math.floor(a/b);
}
// board pieces
function GetBoardPiece(x, y, useKnown)
{
if (useKnown)
{
for (var i=0; i<boardPieces.length; i++)
{
if (boardPieces[i].lastknown.x == x && boardPieces[i].lastknown.y==y) return i;
}
}
else
{
for (var i=0; i<boardPieces.length; i++)
{
if (boardPieces[i].location.x == x && boardPieces[i].location.y==y) return i;
}
}
return -1;
}
function GetBoardPieceScreen(sx, sy, lastKnown)
{
x = Math.floor(sx/mapScale.x) - 1;
y = Math.floor(sy/mapScale.y) - 1;
return GetBoardPiece(x, y, lastKnown);
}
// creates a piece
function BoardPiece(x, y, type)
{
this.init(x, y, type);
}
// initializer
BoardPiece.prototype.init = function(fx, fy, type)
{
// type 0 = base
// type 1 = patrol
// type 2 = group
// type 3 = fleet
// status = 0 dead
// status = 1 alive
this.type = type;
this.laststate = 0;
this.lastknown = {x:fx, y:fy};
this.location = {x:fx, y:fy};
this.status = 2;
this.moveRate = type==3 ? 8 : type;
this.nextMove = this.moveRate;
this.numTargets = type==0 ? 0 : type+1;
}
// render pieces
BoardPiece.prototype.render = function(x, y, radius)
{
var trailRad2 = radius*radius*0.25;
var leadRad2 = radius*radius*4;
var pos = this.screen(this.lastknown.x, this.lastknown.y);
var dx = pos.x - x;
var dy = pos.y - y;
var distance2 = dx*dx+dy*dy;
var fade=1;
if (distance2<leadRad2) // update lastknow
{
fade = (distance2 - trailRad2) / (leadRad2-trailRad2);
if (fade>0 && this.laststate!=0)
{
this.drawitem(pos, fade);
}
}
else if (this.laststate!=0)
{
this.drawitem(pos, 1);
}
var pos2 = this.screen(this.location.x, this.location.y);
var dx2 = pos2.x-x;
var dy2 = pos2.y-y;
var distance22 = dx2*dx2+dy2*dy2;
if (distance22<leadRad2 && this.status>0) // update lastkno
{
fade = ((distance22 - trailRad2) / (leadRad2-trailRad2))+1.0;
this.drawitem(pos2, fade);
}
else if (this.status == 1)
{
this.drawitem(pos2, fade);
}
}
// update to known location
BoardPiece.prototype.updateKnowns = function()
{
if(this.status==2) this.status=1;
this.lastknown.x = this.location.x;
this.lastknown.y = this.location.y;
this.laststate = this.status;
}
// game piece movement logic - pretty rough randomizer
BoardPiece.prototype.move = function(gameCycle)
{
this.updateKnowns();
// scale game cycle time (radar beats every 10s)
aiCycle = Math.floor(gameCycle/cycleScalar);
if (this.nextMove<=aiCycle && this.type > 0) // new position except bases
{
var x, y;
var target = boardPieces[targetBase].location;
// bee-line
var dx = target.x - this.location.x;
var dy = target.y - this.location.y;
x = Math.max(Math.min(dx, 1), -1) + this.location.x;
y = Math.max(Math.min(dy, 1), -1) + this.location.y;
var p = GetBoardPiece(x, y);
if ((p>=0 || x<0 || y<0 || x>=galaxyMapSize.x || y>=galaxyMapSize.y) && p.type!=0)
{
if (dx==0) x = Math.floor(Math.random()*2)*2-1 + this.location.x;
else y = Math.floor(Math.random()*2)*2-1 + this.location.y;
p = GetBoardPiece(x, y);
// off board or occupied
if (p>=0 || x<0 || y<0 || x>=galaxyMapSize.x || y>=galaxyMapSize.y) return;
}
// update movement time
this.nextMove += this.moveRate;
this.location.x = x;
this.location.y = y;
this.status = 2;
}
}
// help functoin to convert from map to screen space
BoardPiece.prototype.screen = function(x, y)
{
return {x:x*mapScale.x+mapScale.x*1.5, y:y*mapScale.y+mapScale.y*1.5};
}
// DRAW Board piece item with label text
BoardPiece.prototype.drawitem = function( pos, fading)
{
var ascii = ["starbase", "patrol", "group", "fleet"];
var light = (fading-1)*255;
light = Math.floor(Math.max(Math.min(light, 255), 0));
font = fontsize + (((fading-1.5)*40));
if (this.status < 2 || fading <=1.5) font = fontsize;
font*=0.75;
context.globalAlpha = fading>1 ? 1.0: fading;
context.font = font + 'pt Calibri';
context.fillStyle = 'rgb('+light+',255,'+light+')';
context.textAlign = "center";
context.fillText(ascii[this.type], pos.x, pos.y+font*1.5);
pos.y-=font;
switch(this.type)
{
case 0: // base
context.beginPath();
context.arc(pos.x, pos.y, font, 0, Math.PI, true);
context.moveTo(pos.x+font, pos.y);
context.quadraticCurveTo(pos.x, pos.y+font*0.75, pos.x-font, pos.y);
context.fill();
context.strokeStyle='#80ff80';
context.stroke();
break;
case 1: // patrol fighter
context.beginPath();
context.arc(pos.x, pos.y, font*0.75, 0, Math.PI*2, false);
context.fill();
context.moveTo(pos.x-font, pos.y-font);
context.lineTo(pos.x-font, pos.y+font);
context.moveTo(pos.x+font, pos.y-font);
context.lineTo(pos.x+font, pos.y+font);
context.strokeStyle='#80ff80';
context.stroke();
break;
case 2: // group cruiser
context.beginPath();
context.moveTo(pos.x-font, pos.y);
context.lineTo(pos.x-font/2, pos.y-font/2);
context.lineTo(pos.x+font*2, pos.y);
context.lineTo(pos.x-font/2, pos.y+font/2);
context.closePath();
context.fill();
context.lineTo(pos.x+font*2, pos.y);
context.strokeStyle='#80ff80';
context.stroke();
context.beginPath();
context.arc(pos.x-font*1.5, pos.y-font*0.9, font*0.25, 0, Math.PI*2, false);
context.arc(pos.x-font*2, pos.y+font*0.75, font*0.25, 0, Math.PI*2, false);
context.fill();
break;
case 3: // fleet / basestar
context.beginPath();
context.moveTo(pos.x-font, pos.y-font/2);
context.quadraticCurveTo(pos.x, pos.y-font, pos.x+font, pos.y-font/2);
context.lineTo(pos.x-font, pos.y+font/2);
context.quadraticCurveTo(pos.x, pos.y+font, pos.x+font, pos.y+font/2);
context.closePath();
context.fill();
context.strokeStyle='#80ff80';
context.stroke();
break;
}
}
function CountHostiles(loc)
{
var count = 0;
for (var i=-1; i<2; i++)
{
for (var j=-1; j<2; j++)
{
var p = GetBoardPiece(loc.x+i, loc.y+j);
if (p>=0 && p.type!=0) count++;
}
}
return count;
}
function BoardSetup()
{
// clear board
targetBase = 0;
boardPieces = [];
var x, y, border = 1;
for (var i =0; i<4; i++) // types
{
for (var j=0; j<4; j++) // counts
{
do
{
x = Math.floor(Math.random() * (galaxyMapSize.x-border*2))+border;
y = Math.floor(Math.random() * (galaxyMapSize.y-border*2))+border;
var p = GetBoardPiece(x, y);
} while(p>=0);
boardPieces.push(new BoardPiece(x, y, i));
}
border = 0;
}
}
function SetShipLocation(x, y)
{
shipLocation.x = Math.floor(x);
shipLocation.y = Math.floor(y);
shipPosition.x = x;
shipPosition.y = y;
}
function SetWarpPoint(x, y, lock)
{
if (warpLocked == true && lock == false) return;
warpLocked = lock;
warpLocation.x = x;
warpLocation.y = y;
warpAnim = 0;
}
function ClearWarpPoint()
{
warpLocked = false;
}
// many thanks to http://www.sonic.net/~nbs/star-raiders/docs/v.html
var distanceTable =[100,130,160,200,230,500,700,800,900,1200,1250,1300,1350,1400,1550,1700,1840,2000,2080,2160,2230,2320,2410, 2500,9999];
function ShipCalculateWarpEnergy(sx, sy)
{
// convert sx and sy into board-coords
var x = (sx/mapScale.x) - 1;
var y = (sy/mapScale.y) - 1;
var dx = shipPosition.x - x;
var dy = shipPosition.y - y;
// location
var distance = Math.sqrt(dx*dx+dy*dy);
var di = Math.floor(distance);
if (di>23) di=23;
var energy = distanceTable[di];
energy+= (distanceTable[di+1]-energy) * (distance-di);
return Math.floor(energy);
}
// asteriods
var asteriods = [];
function SetupAsteriods()
{
asteriods = [];
for (var i=0; i<maxAsteriods; i++)
{
asteriods.push(new Asteriod())
}
}
function Asteriod()
{
this.init();
}
Asteriod.prototype.init = function()
{
this.x = Math.random()*localSpaceCubed;
this.y = Math.random()*localSpaceCubed;
this.z = Math.random()*localSpaceCubed;
}
// initialization
function init()
{
// setup canvas and context
canvas = document.getElementById('test');
context = canvas.getContext('2d');
// set canvas to be window dimensions
resize();
// create event listeners
canvas.addEventListener('mousemove', mouseMove);
canvas.addEventListener('click', mouseClick);
canvas.addEventListener('mousedown', mouseDown); canvas.addEventListener('mouseup', mouseUp);
window.addEventListener('resize', resize);
// initialze variables
SetShipLocation(galaxyMapSize.x*0.5, galaxyMapSize.y*0.5);
// initial ping
shipPing.x = shipPosition.x;
shipPing.y = shipPosition.y;
// buttons
SetupButtons();
// populate map
BoardSetup();
// populate local space
SetupAsteriods();
SetupNMEs();
var d = new Date();
gameStart = d.getTime();
// testing this
PressButton("Long Range");
}
// long range scan
var shipPhi = 0.0;
var shipTheta = 0;
var strobe = 0;
function renderLongRangeScanner()
{
var radius = lrScale.x<lrScale.y ? lrScale.x : lrScale.y;
context.beginPath();
context.arc(centreX, centreY, radius/2, 0, Math.PI*2.0, 0);
context.fillStyle = 'rgba(0,64,0,0.5)';
context.fill();
context.beginPath();
context.arc(centreX, centreY, radius/2, 0, Math.PI*2.0, 0);
context.strokeStyle = 'rgba(0,255,0,0.5)';
context.linewidth = 1;
context.stroke();
context.beginPath();
context.arc(centreX, centreY, radius/8, 0, Math.PI*2.0, 0);
context.stroke();
context.beginPath();
context.arc(centreX, centreY, radius/2, Math.PI*8.5/6, Math.PI*9.5/6, false);
context.lineTo(centreX, centreY);
context.closePath();
context.stroke();
context.strokeStyle = 'rgba(0,64,0,0.5)';
for (var a=1; a<8; a++)
{
context.beginPath();
context.arc(centreX, centreY, (a/8) * radius*0.5, Math.PI*8.5/6, Math.PI*9.5/6, 0);
context.stroke();
}
// render asteriods
// localPosition.x+=2;
// localPosition.y+=1;
// localPosition.y+=10;
var cphi = Math.cos(shipPhi);
var sphi = Math.sin(shipPhi);
var ctheta = Math.cos(shipTheta);
var stheta = Math.sin(shipTheta);
var s = radius/canvas.width;
// I think a matrix is now going to be faster..
for (var i=0; i<asteriods.length; i++)
{
var x = modulo(localPosition.x - asteriods[i].x)-512;
var y = modulo(localPosition.y - asteriods[i].y)-512;
var z = modulo(localPosition.z - asteriods[i].z)-512;
/*
// rotate around theta plane
var tx = x * cphi - y * sphi;
var ty = y * cphi + x * sphi;
// rotate phi
var tz = z * ctheta - ty * stheta;
ty = ty * ctheta + z * stheta;
// pixel depth using a long focal distance to ensure all set is in focus
*/
var t = orientation.transform(x,y,z);
var depth = focalPoint*5 / ((t.z + 1000) +1);
var sz = 4 * depth;
// draw a blob
var grey = Math.floor(depth*128);
context.beginPath();
context.rect(t.x*depth*s+centreX, t.y*depth*s+centreY, sz, sz);
context.fillStyle = 'rgba('+grey+','+grey+','+grey+',1)';
context.fill();
}
strobe+=1;
for (var i=0; i<nmes.length; i++)
{
var x = modulo(localPosition.x - nmes[i].pos.x)-512;
var y = modulo(localPosition.y - nmes[i].pos.y)-512;
var z = modulo(localPosition.z - nmes[i].pos.z)-512;
var t = orientation.transform(x,y,z);
var depth = focalPoint*5 / ((t.z + 1000) +1);
var sz = 6 * depth;
// draw a blob
var red = Math.floor(depth*128);
context.beginPath();
context.rect(t.x*depth*s+centreX, t.y*depth*s+centreY, sz, sz);
context.fillStyle = 'rgba('+red+','+red*(strobe&4)+','+red*(strobe&8)+',1)';
context.fill();
}
// context.fillStyle = 'rgba(255,255,255,1)';
// context.fill();
// render targets
}
// galactic scan
function renderGalacticScanner()
{
var scaleX = mapScale.x;
var scaleY = mapScale.y;
context.beginPath();
for (var i=0; i<=galaxyMapSize.x; i++)
{
context.moveTo(scaleX*(i+1), scaleY);
context.lineTo(scaleX*(i+1), scaleY*(galaxyMapSize.y+1));
}
context.strokeStyle = '#c0c0c0';
context.lineWidth = 4;
context.stroke();
context.beginPath();
for (var j=0; j<=galaxyMapSize.y; j++)
{
context.moveTo(scaleX, scaleY*(j+1));
context.lineTo(scaleX*(galaxyMapSize.x+1), scaleY*(j+1));
}
context.strokeStyle = '#c0c0c0';
context.lineWidth = 4;
context.stroke();
// ping every 5 seconds
var d = new Date();
var currentTime = d.getTime();
var distance = ((currentTime - gameStart)%10000)/10000;
pingRadius = distance * canvas.width/2;
context.globalCompositeOperation='source-over';
var shipX = shipPing.x * scaleX + scaleX;
var shipY = shipPing.y * scaleY + scaleY;
// update map with locations of ships
for (var b=0; b<boardPieces.length; b++)
{
// fade in and out board pieces as the ping passes over them
boardPieces[b].render(shipX, shipY, pingRadius);
}
context.globalCompositeOperation='lighter';
context.globalAlpha = 1;
context.beginPath();
var gradient = context.createRadialGradient(shipX, shipY, pingRadius*0.5, shipX, shipY, pingRadius*2);
gradient.addColorStop(0, 'rgba(128, 255, 128,0)');
gradient.addColorStop(1, 'rgba(128, 255, 128,'+(1-distance)+')');
context.fillStyle = gradient;
context.arc(shipX, shipY, pingRadius*2, Math.PI*2, false);
context.fill();
// render hyperspace location
context.beginPath();
clampX = Math.max(Math.min(warpLocation.x, scaleX*(galaxyMapSize.x+1)), scaleX);
clampY = Math.max(Math.min(warpLocation.y, scaleY*(galaxyMapSize.y+1)), scaleY);
context.moveTo(clampX, scaleY)
context.lineTo(clampX, scaleY*(galaxyMapSize.y+1));
context.moveTo(scaleX, clampY);
context.lineTo(scaleX*(galaxyMapSize.x+1), clampY);
if (warpLocked)
{
warpAnim=(warpAnim+1)%(Math.sqrt(scaleX*scaleX+scaleY*scaleY)*0.5);
context.arc(clampX, clampY, warpAnim, 0, Math.PI*2, false);
}
context.strokeStyle = '#c0ffc0';
context.lineWidth = 1;
context.stroke();
}
// input functions
function mouseMove(event)
{
var rect = canvas.getBoundingClientRect();
mouseX = event.clientX - rect.left;
mouseY = event.clientY - rect.top;
SetWarpPoint(mouseX, mouseY, false);
if (event.which&1 && dragging)
{
DragEvent(event);
}
}
function mouseDown(event)
{
console.log("mousedown" + event.which);
if (event.which&1)
{
dragging = true;
DragEventStart(event);
}
}
function mouseUp(event)
{
if (event.which&1 && dragging)
{
DragEventDone(event);
}
}
function mouseClick()
{
var scaleX = mapScale.x;
var scaleY = mapScale.y;
var x = (mouseX/scaleX)-1;
var y = (mouseY/scaleY)-1;
//SetShipLocation(x, y);
buttonpressed = CheckButtons(mouseX, mouseY);
if (buttonpressed) return;
// lock in warp point
if (overlayMode == eGalacticScanner)
{
if (!warpLocked)
SetWarpPoint(mouseX, mouseY, true);
else
ClearWarpPoint();
}
}
function resize()
{
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// compute centre of screen
centreX = canvas.width/2;
centreY = canvas.height/2;
fontsize = 21;
// resize font based on canvas size
mapScale.x = canvas.width/(galaxyMapSize.x+2);
mapScale.y = canvas.height/(galaxyMapSize.y+2);
border.x = mapScale.x;
border.y = mapScale.y;
lrScale.x = canvas.width*16/18;
lrScale.y = canvas.height*16/18;
do
{
fontsize-=0.1;
context.font = fontsize + 'pt Calibri';
var fits = context.measureText('group');
}while((fits.width+20>mapScale.x || mapScale.y<fontsize*4) && fontsize >5);
// reset buttons
SetupButtons();
}
function renderInformation()
{
switch (overlayMode)
{
case eGalacticScanner:
renderGalaxyInformation();
break;
case eLongRange:
renderLongRangeInformation();
break;
}
}
function renderGalaxyInformation()
{
var scaleX = mapScale.x;
var scaleY = mapScale.y;
var i = GetBoardPieceScreen(mouseX, mouseY);
var targets = 'empty';
if (i>0)
{
targets = boardPieces[i].numTargets;
if (targets == 0) targets = 'starbase';
}
context.globalAlpha = 1.0;
context.font = '20pt Calibri';
context.fillStyle = 'rgb(255,255,0)';
context.textAlign = "left";
context.fillText('Targets: ' + targets, mapScale.x, canvas.height-15);
var fuel = ShipCalculateWarpEnergy(mouseX, mouseY);
context.font = '20pt Calibri';
context.fillStyle = 'rgb(255,255,0)';
context.textAlign = "center";
context.fillText('Warp Energy: ' + fuel, canvas.width/2, canvas.height-15);
if (warpLocked)
{
var clampX = Math.max(Math.min(warpLocation.x, mapScale.x*(galaxyMapSize.x+1)), mapScale.x);
var clampY = Math.max(Math.min(warpLocation.y, mapScale.y*(galaxyMapSize.y+1)), mapScale.y);
var fuel = ShipCalculateWarpEnergy(clampX, clampY);
context.textAlign = "centre";
context.font = '11pt Calibri';
context.fillText('Energy: ' + fuel, clampX, clampY-12);
}
var x = shipPosition.x*scaleX+scaleX;
var y = shipPosition.y*scaleY+scaleY;
renderIconShip(x, y, fontsize);
context.globalAlpha = 1.0;
context.font = '20pt Calibri';
context.fillStyle = 'rgb(255,255,255)';
context.textAlign = "center";
context.fillText('Galactic Scanner', canvas.width/2, 30);
}
function renderLongRangeInformation()
{
context.globalAlpha = 1.0;
renderIconShip(centreX, centreY, 10);
context.globalAlpha = 1.0;
context.font = '20pt Calibri';
context.fillStyle = 'rgb(255,255,255)';
context.textAlign = "center";
context.fillText('Long Range Scanner', canvas.width/2, 20);
context.font = '14pt Calibri';
context.fillText('(hold left mouse down and DRAG to YAW and PITCH ship)', canvas.width/2, canvas.height-5);
}
function renderIconShip(posx, posy, scale)
{
// render our ship
var x = posx;
var y = posy;
context.fillStyle = 'rgb(255,255,0)';
context.beginPath();
context.arc(x, y, scale*0.5, 0, Math.PI*2);
context.fill();
context.moveTo(x, y-scale*1.5);
context.lineTo(x-scale, y+scale*1.5);
context.lineTo(x, y);
context.lineTo(x+scale, y+scale*1.5);
context.closePath();
context.fill();
}
function renderStarDate()
{
var d = new Date();
var currentTime = d.getTime();
var gameTime = currentTime - gameStart;
var decimalTime = gameTime / 60000;
context.font = '20pt Calibri';
context.fillStyle = 'rgb(255,255,0)';
context.textAlign = "right";
leadingzero = decimalTime<10 ? '0':'';
context.fillText('StarDate: ' + leadingzero + decimalTime.toFixed(2), canvas.width-mapScale.x, canvas.height-15);
}
// rendering functions
function render()
{
context.fillStyle = 'black';
context.clearRect(0, 0, canvas.width, canvas.height);
switch (overlayMode)
{
case eGalacticScanner:
renderGalacticScanner();
break;
case eLongRange:
renderLongRangeScanner();
break;
}
renderInformation();
renderStarDate();
RenderButtons();
}
// movement functions
function update()
{
var d = new Date();
var currentTime = d.getTime();
var gameCycle = Math.floor((currentTime - gameStart)/10000);
if (gameCycle!=lastCycle)
{
for (var b=0; b<boardPieces.length; b++)
{
boardPieces[b].move(gameCycle);
}
shipPing.x = shipPosition.x;
shipPing.y = shipPosition.y;
}
lastCycle = gameCycle;
// check starbase health and trigger destruction
var base = boardPieces[targetBase];
var count = CountHostiles(base.location);
if (count>=4) // trigger kaboom
{
if (base.nextMove==0) base.nextMove = gameCycle + 2;
if (base.nextMove<gameCycle)
{
targetBase++;
base.status = 0;
boardPieces.push(new BoardPiece(base.location.x, base.location.y, 1));
}
}
else
{
base.nextMove = 0;
}
if (targetBase == 4 ) // zylons win
{
BoardSetup();
}
UpdateShipControls();
UpdateNMEs();
}
// per frame tick functions
function animate()
{
frameCount++;
// movement update
update();
// render update
render();
// trigger next frame
requestAnimationFrame(animate);
}
// array to hold all buttons
var buttons = [];
// setup buttons
function SetupButtons()
{
// erase old buttons
buttons = [];
var b = border;
var ms = mapScale;
new Button(b.x*0.05, b.y*1.2, b.x*0.8, ms.y*0.6, "Long Range", SwitchToLongRange);
new Button(b.x*0.05, b.y*2.2, b.x*0.8, ms.y*0.6, "Galaxy Chart", SwitchToGalaxyChart);
new Button(b.x*0.05, b.y*3.2, b.x*0.8, ms.y*0.6, "Warp", ToggleWarp);
new Button(b.x*0.05, b.y*4.2, b.x*0.8, ms.y*0.6, "Shields", ToggleShields);
new Button(b.x*0.05, b.y*5.2, b.x*0.8, ms.y*0.6, "Target Comp", ToggleTargetComp);
}
// checks hits against the button list
function CheckButtons(x, y)
{
var buttonhit=false;
for (var i=0; i<buttons.length; i++)
{
buttonhit |= buttons[i].hit(x,y);
}
return buttonhit;
}
// renders the buttons
function RenderButtons()
{
for (var i=0; i<buttons.length; i++)
{
buttons[i].render();
}
}
function ClearButtonState(name)
{
for (var i=0; i<buttons.length; i++)
{
if (name == buttons[i].name)
{
buttons[i].state = 0;
}
}
}
// debug function to emumlate button presses
function PressButton(name)
{
for (var i=0; i<buttons.length; i++)
{
if (name == buttons[i].name)
{
buttons[i].callback(buttons[i]);
}
}
}
function Button(x, y, w, h, name, callback)
{
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.name = name;
this.callback = callback;
this.state = 0;
// add button to stack
buttons.push(this);
}
Button.prototype.render = function()
{
// fill a rect
context.beginPath();
context.rect(this.x, this.y, this.w, this.h);
if (this.state==0)
context.fillStyle = 'rgba(0, 0, 0, 0.5)';
else
context.fillStyle = 'rgba(0, 128, 0, 0.5)';
context.fill();
context.lineWidth = 2;
context.strokeStyle = 'rgba(120, 240, 120, 0.5)';
context.stroke();
context.moveTo(this.x, this.y);
context.font = '10pt Calibri';
context.fillStyle = 'rgba(127,255,127, 1)';
context.textAlign = "center";
context.fillText(this.name, this.x+this.w/2, this.y+this.h-9);
}
Button.prototype.hit = function(x, y)
{
if (x>=this.x && x<this.x+this.w && y>this.y && y<this.y+this.h)
{
// hit
this.callback(this);
return true;
}
return false;
}
function SwitchToLongRange(button)
{
console.log("switching to long range");
if (overlayMode == eLongRange) overlayMode = eNone;
else overlayMode = eLongRange;
button.state = overlayMode == eLongRange;
ClearButtonState("Galaxy Chart");
}
function SwitchToGalaxyChart(button)
{
console.log("switching to galatic range");
if (overlayMode == eGalacticScanner) overlayMode = eNone;
else overlayMode = eGalacticScanner;
button.state = overlayMode == eGalacticScanner;
ClearButtonState("Long Range");
}
function ToggleWarp(button)
{
button.state^=1;
}
function ToggleShields(button)
{
button.state^=1;
}
function ToggleTargetComp(button)
{
button.state^=1;
}
// flight controls
var dragStart = {x:0,y:0};
var dragging = false;
var dragCurr = {x:0, y:0};
var dragmatrix = new matrix3x3();
function DragEvent(event)
{
dragCurr.x = event.clientX;
dragCurr.y = event.clientY;
}
function DragEventStart(event)
{
dragStart.x = event.clientX;
dragStart.y = event.clientY;
dragCurr.x = event.clientX;
dragCurr.y = event.clientY;
dragmatrix.clone(orientation);
shipPhi = 0;
shipTheta = 0;
dragging = true;
}
function DragEventDone(event)
{
dragging =false;
}
var rotateVelocity = 0;
var pitchVelocity = 0;
function UpdateShipControls()
{
var rotationForce = (dragCurr.x - dragStart.x) / canvas.width;
var pitchForce = (dragCurr.y - dragStart.y) / canvas.height;
if (dragging==true)
{
rotateVelocity+=rotationForce;
pitchVelocity+=pitchForce;
}
rotateVelocity*=0.9;
pitchVelocity*=0.9;
shipTheta+=(pitchVelocity*Math.PI*0.5) / 60;
shipPhi+=(rotateVelocity*Math.PI*0.5) / 60;
// build rotation matrix
var rx = new matrix3x3();
var rz = new matrix3x3();
rz.rotateZ(shipPhi);
rx.rotateX(shipTheta);
orientation.clone( rz.multiply(rx.multiply(dragmatrix)) );
}
// entry point
init();
animate();
body{
background:#000000
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment