/* GL */
var GL = function(){};
GL.loadLibrary = function(library){
this.library = library;
};
GL.translate = function(key,spacers){
if ( this.library && this.library[key] ) {
var text = this.library[key];
if ( spacers ) {
for ( var spacer in spacers ) {
var value = spacers[spacer];
var expression = new RegExp('\\['+ spacer +'\\]','g');
text = text.replace(expression,value);
}
}
return text;
}
return key;
};
GL.centerElement = function(element,frame){
element.css({
'left':((frame[0]-element.outerWidth())/2)+'px',
'top':((frame[1]-element.outerHeight())/2)+'px'
});
};
/* GL.Config */
(function(){
//Class Definition
var ClassStatic = GL.Config = function(){};
var ClassPrototype = ClassStatic.prototype;
//--
ClassStatic.createImage = function(name,extension,path){
if ( !path ) path = 'img/';
if ( !extension ) extension = '.png';
var image = new Image();
image.src = path + name + extension;
return image;
};
ClassStatic.createAnimation = function(className,suffixes,speed){
var images = new Array();
for ( var i=0; i < suffixes.length; i++ ){
images.push(ClassStatic.createImage(className +'.'+ suffixes[i]));
}
return {
'speed':speed,
'images':images
};
};
ClassStatic.SoundControl = {
'sounds':{
'died':'sounds/died.mp3',
'finish':'sounds/finish.1.mp3',
'complete':'sounds/complete.mp3',
'gameover':'sounds/gameover.mp3'
},
'music':{
'groovesalad':'http://somafm.com/goovesalad.pls',
'spacestation':'http://somafm.com/spacestation.pls',
'lush':'http://somafm.com/lush.pls',
'secretagent':'http://somafm.com/secretagent.pls',
'cliqhop':'http://somafm.com/cliqhop.pls'
}
};
ClassStatic.TotalScreen = {
'size':[2,148]
};
ClassStatic.Control = {
'maxLives':5
};
ClassStatic.Control.GamePanel = {
'buttons':[
'Music',
'Sound',
'NewGame',
'Pause'
]
};
ClassStatic.Level = {
'liveWidth':10,
'maxTime':5000
};
ClassStatic.Enemy = {};
ClassStatic.Enemy.Shark = {
'animation':ClassStatic.createAnimation('shark',['1','2','3','4','3','2'],5)
};
ClassStatic.Player = {
'animation':ClassStatic.createAnimation('player',['1','aura.2','aura.3','aura.4','aura.3','aura.2'],7),
'moveVectorMap':{
37:[-1,0], //left
38:[0,-1], //up
39:[1,0], //right
40:[0,1] //down
}
};
ClassStatic.Powerup = {
'maxCountdown':500,
'lifetime':1000,
'types':{
'timeout':{
'duration':300,
'frequency':20
},
'bonus':{
'value':100,
'frequency':70
},
'live':{
'frequency':10
}
}
};
})();
/* GL.Control */
(function(){
//Class Definition
var ClassStatic = GL.Control = function(){};
var ClassPrototype = ClassStatic.prototype;
//--
ClassStatic.colors = {
'path':[150,220,150,255],
'occupied':[180,220,180,255],
'deadly':[240,80,80,255],
'impassable':[90,90,90,255],
'free':[255,255,255,255]
};
ClassStatic.states = {
'path':ClassStatic.colors['path'][0],
'occupied':ClassStatic.colors['occupied'][0],
'deadly':ClassStatic.colors['deadly'][0],
'impassable':ClassStatic.colors['impassable'][0],
'free':ClassStatic.colors['free'][0]
};
ClassStatic.getCookieValue = function(cookieName){
var cookies = document.cookie.split(';');
for ( var i=0;i');
this.initGamePanel();
$(document.body).append(this.element);
this.helpCycleIdx = -1;
this.sound = new GL.SoundControl().__construct(this,'sound');
this.level = new GL.Level.Intro().__construct(this);
GL.centerElement(this.element,[1024,400]);
this.initHelp();
return this;
};
ClassPrototype.createGamePanelButton = function(buttonName){
var self = this;
var buttonElement = $('
');
buttonElement.click(function(event){
self.processButtonEvent(event,buttonName);
});
return buttonElement;
};
ClassPrototype.initGamePanel = function(){
var buttonMap = $('
');
var buttons = GL.Config.Control.GamePanel.buttons;
for( var i=0; i
');
this.element.append(buttonMap);
};
ClassPrototype.adjustLives = function(liveDelta){
this.lives += Math.min(GL.Config.Control.maxLives,liveDelta);
if ( this.lives < 1 ) this.level.setGameover();
};
ClassPrototype.score = function(score){
this.scoreValue += parseInt(score);
if ( this.scoreValue > this.highScoreValue ) {
this.highScoreValue = this.scoreValue;
var a = new Date();
a = new Date(a.getTime() +1000*60*60*24*365);
document.cookie = 'GL.HighScore='+this.highScoreValue +'; expires='+a.toGMTString()+';';
}
};
ClassPrototype.startNewGame = function(){
var GET = {};
if ( window.location.search ) {
var pieces = window.location.search.substr(1).split('&');
for ( var i=0; i
');
this.musicCurrent = $('

');
this.musicBoxOpener = $('

');
this.musicBox.append('

');
this.musicBox.append(this.musicCurrent);
this.musicBox.append(this.musicBoxOpener);
//Entries
this.musicEntries = $('
');
this.musicEntries.hide();
for ( var musicName in GL.Config.SoundControl.music ) this.initEntry(musicName);
this.musicBox.append(this.musicEntries);
//--
$(document).bind('click',function(){
self.closeMusicEntries();
});
this.musicCurrent.bind('click',function(){
self.toggleMusic();
});
this.musicBoxOpener.bind('click',function(event){
event.stopPropagation();
self.openMusicEntries();
});
};
ClassPrototype.initEntry = function(musicName){
if ( !this.currentMusic ) this.setCurrentMusic(musicName,true);
var self = this;
var musicBoxEntry = $('

');
musicBoxEntry.bind('click',function(){
self.setCurrentMusic(musicName);
});
this.musicEntries.append(musicBoxEntry);
};
ClassPrototype.openMusicEntries = function(){
if ( this.musicEntriesOpen ) return;
this.musicEntriesOpen = true;
this.musicBoxOpener.hide();
this.musicBox.addClass('open');
this.musicEntries.slideDown('slow');
};
ClassPrototype.closeMusicEntries = function(){
if ( !this.musicEntriesOpen ) return;
this.musicEntriesOpen = false;
var self = this;
this.musicEntries.slideUp('slow',function(){
self.musicBoxOpener.show();
self.musicBox.removeClass('open');
});
};
ClassPrototype.setCurrentMusic = function(musicName,init){
if ( this.currentMusic && this.playingMusic ) this.toggleMusic();
this.currentMusic = musicName;
this.musicCurrent.css({'background-image':'url(img/music/'+ musicName +'.png)'});
if ( init ) return;
this.closeMusicEntries();
this.toggleMusic();
};
ClassPrototype.restartMusic = function(){
if ( !this.pausedMusic ) return;
this.pausedMusic = false;
if ( this.playingMusic ) return;
this.toggleMusic();
};
ClassPrototype.pauseMusic = function(){
if ( this.pausedMusic ) return;
if ( !this.playingMusic ) return;
this.pausedMusic = true;
this.toggleMusic();
};
ClassPrototype.updateLevel = function(){
this.control.level.setIconVisibility('sound',this.soundOff);
this.control.level.setIconVisibility('music',!this.playingMusic);
};
ClassPrototype.toggleSound = function(){
this.soundOff = !this.soundOff;
this.updateLevel();
};
ClassPrototype.toggleMusic = function(){
this.playingMusic = !this.playingMusic;
this.updateLevel();
if ( this.playingMusic ) this.play('music');
else this.stop('music');
this.musicCurrent[0].src = 'img/music.'+ (this.playingMusic?'on':'off') +'.png';
};
ClassPrototype.play = function(key){
if ( !this.player ) this.player = document.getElementById(this.id);
try{
if ( !this.player ) throw {'message':'player not ready','key':key};
var url;
if ( key == 'music' ) {
key = this.currentMusic;
url = GL.Config.SoundControl.music[key];
} else {
if ( this.soundOff ) return;
url = GL.Config.SoundControl.sounds[key];
}
this.player.stop(key);
if ( !url ) throw {'message':'no url for key','key':key};
var error = this.player.play(key,url);
if ( error ) throw error;
} catch( error ) {
if ( window.console ) window.console.log(error,key,url);
}
};
ClassPrototype.stop = function(key){
if ( !this.player ) return;
if ( key == 'music' ) key = this.currentMusic;
var error = this.player.stop(key);
if ( error && window.console ) window.console.log(error);
};
})();
/* GL.Sprite */
(function(){
//Class Definition
var ClassStatic = GL.Sprite = function(){};
var ClassPrototype = ClassStatic.prototype;
//--
ClassPrototype.__construct = function(className, animation){
this.size = 20;
this.element = $('
');
this.context = this.element[0].getContext('2d');
this.background = this.context.getImageData(0,0,this.size,this.size);
this.animation = animation;
this.animationTimeout = 0;
this.imageIndex = 0;
this.image = this.animation.images[this.imageIndex];
return this;
};
ClassPrototype.setImage = function(imageIndex){
this.imageIndex = imageIndex;
if ( this.imageIndex >= this.animation.images.length ) this.imageIndex = 0;
else if ( this.imageIndex < 0 ) this.imageIndex = 0;
this.image = this.animation.images[this.imageIndex];
};
ClassPrototype.animate = function(rotation){
if ( --this.animationTimeout >= 0 ) return;
this.animationTimeout = this.animation.speed;
this.setImage(this.imageIndex+1);
this.update(rotation);
};
ClassPrototype.update = function(rotation){
if ( !this.context ) return;
this.context.putImageData(this.background,0,0);
try{
if ( rotation ) {
var center = this.size/2;
this.context.save();
this.context.translate(center,center);
this.context.rotate(-rotation);
this.context.drawImage(this.image,-center,-center);
this.context.restore();
} else {
this.context.drawImage(this.image,0,0);
}
} catch ( error ) {
if ( window.console ) window.console.log(error);
}
};
})();
/* GL.Level */
(function(){
//Class Definition
var ClassStatic = GL.Level = function(){};
var ClassPrototype = ClassStatic.prototype;
//--
ClassStatic.types = new Array();
ClassStatic.helpTypes = {};
ClassStatic.register = function(type){
ClassStatic.types.push(type);
};
ClassPrototype.__construct = function(control,level){
this.control = control;
this.level = level;
this.typeLevel = Math.ceil(level / ClassStatic.types.length);
this.complete = false;
this.gameover = false;
this.size = control.size;
this.maxArea = this.size[0]*this.size[1];
this.area = 0;
this.areaPercentage = 0;
this.powerup = null;
this.initWinCondition();
this.initActions();
this.initElement();
this.initScreen();
this.initPlayer([0,0]);
this.initEnemies();
this.initScore();
GL.Powerup.reset(this);
this.start();
return this;
};
ClassPrototype.hideMessage = function(){
this.messageElement.fadeOut(function(){
this.innerHTML = '';
});
};
ClassPrototype.showHelpMessage = function(Type){
if ( ClassStatic.helpTypes[Type] ) return; //already shown
ClassStatic.helpTypes[Type] = true;
this.stop();
this.showMessage('Help',GL.translate(Type+' Help Message')+'
'+GL.translate('Help Message'));
};
ClassPrototype.showMessage = function(title,message){
if ( !message ) message = GL.translate(title + ' Message');
title = GL.translate(title);
var msg = this.messageElement;
msg.append($('
'+title+'
'+message+'
'));
GL.centerElement(msg,[this.size[0],this.size[1]]);
msg.show();
};
ClassPrototype.initActions = function(){
var self = this;
this.actionMap = {
32:function(){ self.toggleRunningState(); }, //[space]
70:function(){ self.toggleFramemeter(); } //f
};
};
ClassPrototype.initWinCondition = function(){
this.winAreaPercentage = Math.min(10,this.typeLevel)*4.5+50;
};
ClassPrototype.initElement = function(){
this.element = $('
');
this.messageElement = $('
');
this.messageElement.hide();
this.element.append(this.messageElement);
this.control.element.append(this.element);
};
ClassPrototype.initScreen = function(){
this.screen = new GL.Screen().__construct(this,GL.Control.colors['occupied']);
};
ClassPrototype.initPlayer = function(startPos){
this.player = new GL.Player().__construct(this,startPos);
};
ClassPrototype.initEnemies = function(){
};
ClassPrototype.initScore = function(){
this.livesElement = $('
')[0];
this.element.append(this.livesElement);
this.icons = {
'music':$('
'),
'sound':$('
')
};
this.element.append(this.icons.music);
this.element.append(this.icons.sound);
var self = this;
var addDisplay = function(name,digits,value){
self.displays[name] = new GL.DigitalDisplay().__construct(self.element[0],name,digits,value);
};
this.displays = {};
addDisplay('timer',3);
addDisplay('levelNr',2,this.level);
addDisplay('score',5);
addDisplay('highscore',5);
this.totalScreen = new GL.TotalScreen().__construct(this,'totalScreen',this.winAreaPercentage);
this.timeScreen = new GL.TotalScreen().__construct(this,'timeScreen');
this.maxTime = GL.Config.Level.maxTime;
this.time = 0;
this.adjustLives(0);
this.updateScore();
};
ClassPrototype.remove = function(){
this.stop();
this.element.remove();
};
ClassPrototype.debug = function(){
this.stop();
this.update();
};
ClassPrototype.stop = function(){
if ( this.interval ) {
window.clearInterval(this.interval);
this.interval = null;
}
};
ClassPrototype.start = function(){
this.stop();
var self=this;
this.interval = window.setInterval(function(){
try {
self.update();
} catch ( e ) {
Debug.log(e);
self.stop();
}
},20);
};
ClassPrototype.setIconVisibility = function(iconName,hide){
if ( !this.icons ) return;
var icon = this.icons[iconName];
if ( !icon ) return;
if ( hide ) icon.hide();
else icon.show();
};
ClassPrototype.adjustLives = function(delta){
this.control.adjustLives(delta);
this.livesElement.innerHTML = '
';
if ( delta < 0 ) {
this.resetTime();
if ( this.gameover ) this.control.sound.play('gameover');
else this.control.sound.play('died');
}
};
ClassPrototype.updateArea = function(area){
this.area += area;
this.areaPercentage = this.area/this.maxArea*100;
this.totalScreen.set(this.areaPercentage,GL.Control.colors.path);
};
ClassPrototype.getScoreValue = function(score){
var scoreValue = score / this.maxArea * 100;
return Math.round(scoreValue * this.level);
};
ClassPrototype.showScore = function(pos,scoreValue){
var scoreElement = $('
'+ scoreValue +'
').insertAfter(this.screen.element);
scoreElement.css({'left':pos[0]+'px','top':pos[1]+'px'});
scoreElement.fadeOut('slow',function(){ $(this).remove(); });
};
ClassPrototype.updateScore = function(scoreValue,position){
if ( scoreValue > 0 ) {
this.control.score(scoreValue);
if ( position ) this.showScore(position,scoreValue);
}
this.displays.score.setValue(this.control.scoreValue);
this.displays.highscore.setValue(this.control.highScoreValue);
};
ClassPrototype.timeout = function(duration){
if ( this.timeoutCountdown > 0 ) return;
this.timeoutCountdown = duration;
};
ClassPrototype.score = function(score){
var scoreValue = this.getScoreValue(score);
this.control.sound.play('finish');
this.updateArea(score);
this.updateScore(scoreValue,this.player.position);
if ( this.areaPercentage > this.winAreaPercentage ) {
this.startNextLevel();
}
};
ClassPrototype.startNextLevel = function(){
this.complete = true;
this.completing = true;
this.control.sound.play('complete');
var self = this;
var timeInc = this.maxTime/100;
var interval = window.setInterval(function(){
self.time += timeInc;
self.updateScore(1);
if ( self.updateTime() ){
self.completing = false;
self.showMessage('Level complete');
window.clearInterval(interval);
}
},20);
this.stop();
};
ClassPrototype.resetTime = function(){
this.time = 0;
this.timeScreen.reset();
};
ClassPrototype.updateTime = function(){
var percentage = this.time/this.maxTime*100;
this.timeScreen.set(percentage,GL.Control.colors.deadly);
return (percentage >= 100);
};
ClassPrototype.update = function(){
if ( this.framemeter ) this.framemeter.update();
if ( !this.gameover ) {
if ( this.player ) {
this.player.update();
}
this.time++;
if ( this.updateTime() ) {
this.adjustLives(-1);
}
}
if ( this.enemies ) {
if ( this.timeoutCountdown > 0 ) {
this.timeoutCountdown--;
if ( this.timeoutCountdown > 0 ){
this.displays.timer.setValue(this.timeoutCountdown);
return;
}
this.displays.timer.setValue(null);
}
if ( this.player ) {
if ( this.powerup ) this.powerup = this.powerup.update();
else this.powerup = GL.Powerup.createPowerup();
}
for( var i=0;i
= this.control.highScoreValue ) {
this.showMessage('New Highscore');
try {
if ( FB.getLoginStatus ) {
var message = GL.translate('FB Highscore Message',{'score':this.control.scoreValue,'level':this.level});
FB.getLoginStatus(function(response){
if ( response.session ) {
FB.ui(
{
method: 'stream.publish',
message: message,
user_message_prompt: GL.translate('FB Highscore Message Prompt')
}
);
}
});
}
} catch ( e ) { }
} else {
this.showMessage('Game Over');
}
this.player.remove();
};
ClassPrototype.pause = function(){
if ( !this.interval ) return;
if ( this.gameover ) return;
if ( this.complete ) return;
this.control.sound.pauseMusic();
this.showMessage('Game Paused');
this.stop();
};
ClassPrototype.toggleFramemeter = function(){
if ( this.framemeter ) {
this.framemeter.remove();
this.framemeter = null;
} else {
this.framemeter = new Speedometer.Framemeter().__construct(this.element[0],100,50);
}
};
ClassPrototype.toggleRunningState = function(){
if ( this.complete ) {
if ( this.completing ) return; //wait
this.hideMessage();
this.control.startNextLevel();
} else {
if ( this.interval ) this.pause();
else this.play();
}
};
ClassPrototype.processKeyEvent = function(event){
if ( this.gameover ) return;
if ( this.actionMap[event.keyCode] ) {
event.preventDefault();
this.actionMap[event.keyCode](event);
} else {
if ( !this.complete ) this.player.processKeyEvent(event);
}
};
})();
/* GL.Level.Intro */
(function(){
//Class Definition
var ClassStatic = GL.Level.Intro = function(){};
var ParentClass = GL.Level;
var ParentPrototype = ParentClass.prototype;
var ClassPrototype = ClassStatic.prototype = new ParentClass;
//--
ClassPrototype.__construct = function(control){
ParentPrototype.__construct.call(this,control,10);
this.gameover = true;
this.showMessage('Ready');
return this;
};
ClassPrototype.initScreen = function(){
ParentPrototype.initScreen.call(this);
this.screen.drawBorder(GL.Control.colors.impassable);
this.screen.commitState();
};
ClassPrototype.initPlayer = function(){};
ClassPrototype.initScore = function(){};
})();
/* GL.Level.Classic */
(function(){
//Class Definition
var ClassStatic = GL.Level.Classic = function(){};
var ParentClass = GL.Level;
var ParentPrototype = ParentClass.prototype;
var ClassPrototype = ClassStatic.prototype = new ParentClass;
//--
GL.Level.register('Classic');
ClassPrototype.__construct = function(control,level){
ParentPrototype.__construct.call(this,control,level);
this.showHelpMessage('Classic');
return this;
};
ClassPrototype.initScreen = function(){
ParentPrototype.initScreen.call(this);
this.screen.drawBorder(GL.Control.colors.occupied);
this.screen.commitState();
};
ClassPrototype.initEnemies = function(){
this.enemies = new Array();
for(var i=0;i room2 ) {
room1 = room2;
room1 = grid[y2][x2];
}
var key = room1+':'+room2;
if ( !passages[key] ) passages[key] = new Array();
passages[key].push([x,y]);
};
this.coveredFields = 0;
for ( y=1; y= maxY ) return false; //out of range
if ( x <= 0 || x >= maxX ) return false; //out of range
if ( grid[y][x] ) return false; //already taken
var self = this;
var check = function(x,y){
var isTaken = function(y){
if ( y <= 0 || y >= maxY ) return false;
var nr = grid[y][x];
if (nr == 0 || nr == self.nr) return false;
return true;
};
x-=1;
if ( x > 0 ){
if ( isTaken(y-1) || isTaken(y) || isTaken(y+1) ) return false;
}
x+=1;
if ( isTaken(y-1) || isTaken(y+1) ) return false;
x+=1;
if ( x < maxX ){
if ( isTaken(y-1) || isTaken(y) || isTaken(y+1) ) return false;
}
return true;
};
if ( !check(x,y) ) return false;
var size = this.creator.minSize;
this.creator.screen.fillRect(x*size,y*size,size,size,this.color);
this.grid[y][x] = this.nr;
this.open.push([x,y]);
return true;
};
ClassPrototype.expand = function(){
if ( this.open.length <= 0 ) return false;
var field = this.open.shift();
var x = field[0];
var y = field[1];
x-=1;
if ( x > 0 ){
this.occupy(x,y-1);
this.occupy(x,y);
this.occupy(x,y+1);
}
x+=1;
this.occupy(x,y-1);
this.occupy(x,y+1);
x+=1;
if ( x < this.creator.gridSize.x ){
this.occupy(x,y-1);
this.occupy(x,y);
this.occupy(x,y+1);
}
return true;
};
})();
/* GL.Level.Bridging */
(function(){
//Class Definition
var ClassStatic = GL.Level.Bridging = function(){};
var ParentClass = GL.Level;
var ParentPrototype = ParentClass.prototype;
var ClassPrototype = ClassStatic.prototype = new ParentClass;
//--
GL.Level.register('Bridging');
ClassPrototype.__construct = function(control,level){
ParentPrototype.__construct.call(this,control,level);
this.showHelpMessage('Bridging');
return this;
};
ClassPrototype.initScreen = function(){
ParentPrototype.initScreen.call(this);
this.screen.drawBorder(GL.Control.colors.impassable);
this.maxArea = 0;
this.areas = new Array();
var max = this.typeLevel+1;
for( var i=0; i');
this.offset = {
'col':4,
'row':this.size[0]*4
};
this.init();
return this;
};
ClassStatic.color2string = function(color){
return 'rgba('
+color[0]
+','+ color[1]
+','+ color[2]
+','+ (color[3]?color[3]:255)
+')';
};
ClassPrototype.fillRect = function(x,y,width,height,color){
this.context.fillStyle = ClassStatic.color2string(color);
this.context.fillRect(x,y,width,height);
};
ClassPrototype.reset = function(){
this.fillRect(0,0,this.size[0],this.size[1],GL.Control.colors['free']); //draw rect on the real image
this.revertImage(); //make sure the buffer looks the same as the real image
};
ClassPrototype.init = function(){
this.context = this.element[0].getContext('2d');
this.reset();
this.pixelImage = this.context.createImageData(1,1);
};
ClassPrototype.getEmptyImage = function(){
return this.context.createImageData(this.size[0],this.size[1]);
};
ClassPrototype.getImage = function(){
return this.context.getImageData(0,0,this.size[0],this.size[1]);
};
ClassPrototype.revertImage = function(){
this.image = this.getImage(); //align buffer with real image
};
ClassPrototype.applyImage = function(){
this.context.putImageData(this.image, 0,0); //apply buffer to real image
};
ClassPrototype.getPixelState = function(pos){
return this.image.data[this.getPosIdx(pos)];
};
ClassPrototype.getCenter = function(){
return [this.size[0]/2,this.size[1]/2];
};
ClassPrototype.getPosIdx = function(pos){
return (pos[1]*this.size[0]+pos[0])*4; //calculate a linear position from the given 2d-position
};
ClassPrototype.applyColor = function(idx,color){
var imageData = this.image.data;
imageData[idx++] = color[0];
imageData[idx++] = color[1];
imageData[idx++] = color[2];
imageData[idx++] = color[3];
};
ClassPrototype.drawLine = function(pos,width,height,color){
var idx = this.getPosIdx(pos);
var len = (width?width:height);
var offset = (width?this.offset.col:this.offset.row);
for( var i=0;i')[0];
this.digitElements = [];
for ( var i=0;i')[0];
this.element.appendChild(digit);
this.digitElements.push(digit);
}
this.setValue(value);
parentElement.appendChild(this.element);
return this;
};
ClassPrototype.setValue = function(value){
var strValue = '';
if ( !isNaN(value) ) strValue = String(value);
var start = this.digits-strValue.length;
for ( var i=0; i=start?strValue[i-start]:'off');
}
};
})();
/* GL.Player */
(function(){
//Class Definition
var ClassStatic = GL.Player = function(){};
var ClassPrototype = ClassStatic.prototype;
//--
ClassPrototype.__construct = function(level,startPos){
this.level = level;
this.control = level.control;
this.screen = level.screen;
this.score = 0;
this.initPos = startPos;
this.moveVectorMap = GL.Config.Player.moveVectorMap;
this.sprite = new GL.Sprite().__construct('player',GL.Config.Player.animation);
this.sprite.update(0);
this.element = this.sprite.element;
this.reset();
this.level.element.append(this.element);
return this;
};
ClassPrototype.mark = function(pixelIdx){
var colorOccupied = GL.Control.colors.occupied;
var imageData = this.screen.image.data;
imageData[pixelIdx] = colorOccupied[0];
imageData[pixelIdx+1] = colorOccupied[1];
imageData[pixelIdx+2] = colorOccupied[2];
imageData[pixelIdx+3] = colorOccupied[3];
};
ClassPrototype.remove = function(){
this.element.remove();
};
ClassPrototype.fillPath = function(axis,move){
var imageData = this.screen.image.data;
var stateOccupied = GL.Control.states.occupied;
var statePath = GL.Control.states.path;
var stateFree = GL.Control.states.free;
var colOffset = this.screen.offset.col;
var rowOffset = this.screen.offset.row;
var pathOffset,moveOffset,queueOffset;
var turn = function(moveChange){
axis = (axis+1)%2;
move = moveChange;
if ( axis == 1 ) {
pathOffset = colOffset;
moveOffset = rowOffset*move;
queueOffset = (move == 1?-colOffset:colOffset);
} else {
pathOffset = rowOffset;
moveOffset = colOffset*move;
queueOffset = (move == -1?-rowOffset:rowOffset);
}
};
axis++;
turn(move); //init
var pixelIdx = this.screen.getPosIdx(this.position);
this.rightQueue = new Array();
this.leftQueue = new Array();
var counter = 0;
while ( imageData[pixelIdx] == statePath ){
//mark as occupied
this.mark(pixelIdx);
counter++;
if ( imageData[pixelIdx+pathOffset] == statePath ) { //turn right
turn(1);
} else if ( imageData[pixelIdx-pathOffset] == statePath ) { //turn left
turn(-1);
}
if ( imageData[pixelIdx+queueOffset] == stateFree ) {
this.rightQueue.push(pixelIdx+queueOffset);
}
if ( imageData[pixelIdx-queueOffset] == stateFree ) {
this.leftQueue.push(pixelIdx-queueOffset);
}
//continue on the path
pixelIdx += moveOffset;
};
return counter;
};
ClassPrototype.fill = function(queue,max){
var imageData = this.screen.image.data;
var colorOccupied = GL.Control.colors.occupied;
var stateOccupied = GL.Control.states.occupied;
var stateFree = GL.Control.states.free;
var counter=0;
var colOffset = this.screen.offset.col;
var rowOffset = this.screen.offset.row;
var width = this.screen.size[0]*colOffset;
var height = this.screen.size[0];
/*
var maxFillX = 0;
var maxFillY = 0;
var minFillX = width;
var minFillY = height;
*/
var self = this;
var colorize = function(pixelIdx,add){
counter++;
self.mark(pixelIdx);
/*
var x = (pixelIdx % width);
var y = (pixelIdx-x) / height;
x >>= 2;
y >>= 2;
if ( maxFillX < x ) maxFillX = x;
if ( minFillX > x ) minFillX = x;
if ( maxFillY < y ) maxFillY = y;
if ( minFillY > y ) minFillY = y;
*/
if ( add ) queue.push(pixelIdx);
};
while ( queue.length > 0 ) {
var pixelIdx = queue.pop();
if ( imageData[pixelIdx] == stateFree ) colorize(pixelIdx);
if ( imageData[pixelIdx+colOffset] == stateFree ) colorize(pixelIdx+colOffset,true);
if ( imageData[pixelIdx-colOffset] == stateFree ) colorize(pixelIdx-colOffset,true);
if ( imageData[pixelIdx+rowOffset] == stateFree ) colorize(pixelIdx+rowOffset,true);
if ( imageData[pixelIdx-rowOffset] == stateFree ) colorize(pixelIdx-rowOffset,true);
if ( max && counter > max ) break;
}
return counter;
};
ClassPrototype.updatePos = function(axis){
var nextPos = Math.max(0,Math.min(this.screen.size[axis]-1,this.position[axis]+this.moveVector[axis]));
if ( nextPos == this.position[axis] ) return;
var oldPos = this.position[axis];
var self = this;
var stopMoving = function(){
self.moveVector = [0,0]; //stop moving
};
var drawPath = function(){
applyMove();
self.screen.drawPixel(self.position,GL.Control.colors['path']);
};
var applyMove = function(){
self.position[axis] = nextPos;
self.element[0].style[(axis == 0?'left':'top')] = nextPos +'px';
};
this.position[axis] = nextPos;
var pixelState = this.screen.getPixelState(this.position);
this.position[axis] = oldPos; //revert
if ( this.startPos ) {
switch( pixelState ) {
case GL.Control.states.free:
drawPath();
break;
case GL.Control.states.deadly:
this.die();
break;
case GL.Control.states.occupied:
this.closeArea(axis);
applyMove();
stopMoving();
break;
default:
stopMoving();
break; //not a possible move
}
} else {
switch ( pixelState ) {
case GL.Control.states.free:
this.startPos = [this.position[0],this.position[1]];
this.startPos[axis] = oldPos;
drawPath();
break;
case GL.Control.states.occupied:
applyMove();
break;
default:
stopMoving();
break;
}
}
};
ClassPrototype.closeArea = function(axis){
var pathLength = this.fillPath(axis,this.moveVector[axis]*-1);
this.screen.applyImage();
var left = this.fill(this.leftQueue); //floodfill on the left side of the path
var score = 0;
if ( this.level.checkEnemies() ) { //check wether any enemies have been encountered while filling the left side
this.screen.revertImage(); //revert to previous image if there was indeed an enemy
left = 0;
} else {
score += left;
//copying left data to be able to reuse it later, when the floodfill on the right side needs to be undone
this.screen.applyImage();
leftImage = this.screen.getImage();
}
var right = this.fill(this.rightQueue); //floodfill on the right side of the path
if ( this.level.checkEnemies() ) { //if an enemy is on the right side, just use the left floodfill
if ( left ) {
this.screen.image = leftImage; //undo any floodfilling done on the right side of the path
this.screen.applyImage();
}
} else {
score += right;
this.screen.applyImage();
}
if ( score ) {
score += pathLength;
this.screen.commitState();
this.level.checkPowerup();
this.level.score(score);
} else {
this.screen.revertState();
}
this.startPos = null;
};
ClassPrototype.reset = function(position){
this.position = (position?position:this.initPos);
this.fillPosition = [0,0];
this.moveVector = [0,0];
this.startPos = null;
this.element.css({'left':this.position[0]+'px','top':this.position[1]+'px'});
};
ClassPrototype.die = function(){
this.level.adjustLives(-1);
this.reset(this.startPos);
this.screen.revertState();
};
ClassPrototype.update = function(){
if ( this.moveVector[0] ) this.updatePos(0);
else if ( this.moveVector[1] ) this.updatePos(1);
else {
this.sprite.animate();
}
};
ClassPrototype.processKeyEvent = function(event){
var newVector = this.moveVectorMap[event.keyCode];
if ( newVector ){
event.preventDefault();
if ( this.moveVector != newVector ) {
this.moveVector = newVector;
this.sprite.setImage(0);
this.sprite.update();
}
return;
}
};
})();
/* GL.Enemy */
(function(){
//Class Definition
var ClassStatic = GL.Enemy = function(){};
var ClassPrototype = ClassStatic.prototype;
//--
ClassPrototype.__construct = function(level,position){
this.level = level;
this.screen = level.screen;
this.speed = 1;
if ( position ) {
this.position = [position[0],position[1]];
this.realPosition = [position[0],position[1]];
} else {
this.position = this.screen.getCenter();
this.realPosition = this.screen.getCenter();
}
this.prev = {
'real':[0,0],
'position':[0,0],
'idx':0
};
this.adjustAngle(Math.random()*2*Math.PI);
this.initElement();
level.element.append(this.element);
this.adjustVisual();
return this;
};
ClassPrototype.adjustAngle = function(newAngle){
var full = 2*Math.PI;
while ( newAngle < 0 ) newAngle += full;
while ( newAngle >= full ) newAngle -= full;
this.moveAngle = newAngle;
this.moveVector = [this.speed*Math.sin(this.moveAngle),this.speed*Math.cos(this.moveAngle)];
};
ClassPrototype.initElement = function(){
this.element = $('');
};
ClassPrototype.adjustVisual = function(){
this.element[0].style.left = this.position[0] +'px';
this.element[0].style.top = this.position[1] +'px';
};
ClassPrototype.check = function(){
return (this.screen.image.data[this.posIdx] != GL.Control.states.free);
};
ClassPrototype.revertPosition = function(){
this.realPosition[0] = this.prev.real[0];
this.realPosition[1] = this.prev.real[1];
this.position[0] = this.prev.position[0];
this.position[1] = this.prev.position[1];
this.posIdx = this.prev.idx;
};
ClassPrototype.backupPosition = function(){
this.prev.real[0] = this.realPosition[0];
this.prev.real[1] = this.realPosition[1];
this.prev.position[0] = this.position[0];
this.prev.position[1] = this.position[1];
this.prev.idx = this.posIdx;
};
ClassPrototype.move = function(){
this.realPosition[0] += this.moveVector[0];
this.realPosition[1] += this.moveVector[1];
this.position[0] = Math.round(this.realPosition[0]);
this.position[1] = Math.round(this.realPosition[1]);
this.posIdx = this.screen.getPosIdx(this.position);
};
ClassPrototype.update = function(){
this.backupPosition();
this.move();
var imageData = this.screen.image.data;
var state = imageData[this.posIdx];
var isOccupied = GL.Control.states.occupied;
var isFree = GL.Control.states.free;
var isPath = GL.Control.states.path;
if ( state == isFree ){
this.previouslyReflected = false;
this.adjustVisual();
return true;
} else {
if ( this.previouslyReflected ) {
this.moveAngle = Math.random()*2*Math.PI;
}
this.previouslyReflected = true;
if ( state == isPath ) {
this.level.player.die();
}
var offset = this.screen.offset;
var leftState = isOccupied;
if ( this.position[0] > 0 ) leftState = imageData[this.posIdx-offset.col];
var rightState = isOccupied;
if ( this.position[0] < this.screen.size[0]-1 ) var rightState = imageData[this.posIdx+offset.col];
this.revertPosition();
var newAngle = Math.random()/2-0.25 - this.moveAngle;
if ( leftState != isFree && rightState != isFree ) newAngle += Math.PI;
this.adjustAngle(newAngle);
}
};
})();
/* GL.Enemy.Shark */
(function(){
//Class Definition
var ClassStatic = GL.Enemy.Shark = function(){};
var ParentClass = GL.Enemy;
var ParentPrototype = ParentClass.prototype;
var ClassPrototype = ClassStatic.prototype = new ParentClass;
//--
ClassPrototype.initElement = function(newAngle){
this.sprite = new GL.Sprite().__construct('shark',GL.Config.Enemy.Shark.animation);
this.sprite.update(0);
this.element = this.sprite.element;
this.speed = 2;
this.adjustAngle(this.moveAngle);
};
ClassPrototype.preventCollision = function(){
this.backupPosition();
var imageData = this.screen.image.data;
var radius = 50;
var state;
var isFree = GL.Control.states.free;
var isPath = GL.Control.states.path;
var collision = 1;
for ( ; collision= full ) angle -= full;
while ( angle < 0 ) angle += full;
var rotation = angle - this.moveAngle;
if ( rotation > half ) rotation -= full;
else if ( rotation < -half ) rotation += full;
var maxRotation = half / 50;
if ( rotation > maxRotation ) rotation = maxRotation;
else if ( rotation < -maxRotation ) rotation = -maxRotation;
this.adjustAngle(this.moveAngle+rotation);
};
ClassPrototype.followPlayer = function(){
if ( !this.level.player || !this.level.player.startPos ) return false;
this.swerveTo(this.level.player.position);
return true;
};
ClassPrototype.update = function(){
if ( !this.preventCollision() ) this.followPlayer();
ParentPrototype.update.call(this);
this.sprite.animate(this.moveAngle);
};
})();
/* GL.Enemy.Wanderer */
(function(){
//Class Definition
var ClassStatic = GL.Enemy.Wanderer = function(){};
var ParentClass = GL.Enemy;
var ParentPrototype = ParentClass.prototype;
var ClassPrototype = ClassStatic.prototype = new ParentClass;
//--
ClassPrototype.__construct = function(control,startPos){
ParentPrototype.__construct.call(this,control,startPos);
this.element.addClass('wanderer');
return this;
};
ClassPrototype.adjustAngle = function(newAngle){
/*
quarter = Math.PI/2;
newAngle = Math.round(newAngle / quarter);
newAngle *= quarter;
*/
ParentPrototype.adjustAngle(newAngle);
};
ClassPrototype.update = function(){
this.adjustAngle(this.moveAngle+(Math.random()-Math.random())*Math.PI/10);
ParentPrototype.update.call(this);
};
})();
/* GL.Powerup */
(function(){
//Class Definition
var ClassStatic = GL.Powerup = function(){};
var ClassPrototype = ClassStatic.prototype;
var Config = GL.Config.Powerup;
//--
(function(){
ClassStatic.typeTotalFrequency = 0;
for ( var i in Config.types ) {
ClassStatic.typeTotalFrequency += Config.types[i].frequency;
}
}());
ClassStatic.getRandomType = function(){
var typeRange = Math.round(Math.random()*ClassStatic.typeTotalFrequency);
var sum = 0;
var type = null;
for ( type in Config.types ) {
sum += Config.types[type].frequency;
if ( sum > typeRange ) break;
}
return type;
};
ClassStatic.resetCountdown = function(){
ClassStatic.countdown = Math.round(Math.random()*Config.maxCountdown);
};
ClassStatic.reset = function(level){
ClassStatic.level = level;
ClassStatic.count = 0;
ClassStatic.resetCountdown();
};
ClassStatic.createPowerup = function(){
if ( --ClassStatic.countdown > 0 ) return null; //only add one at a certain chance
ClassStatic.resetCountdown();
return new GL.Powerup().__construct(ClassStatic.level,++ClassStatic.count);
};
ClassPrototype.__construct = function(level,nr){
this.level = level;
this.nr = nr;
this.screen = level.screen;
this.lifetime = Config.lifetime;
this.type = ClassStatic.getRandomType();
var enemies = this.level.enemies;
var enemyIdx = Math.round(Math.random()*(enemies.length-1));
var position = enemies[enemyIdx].position;
this.position = [position[0],position[1]];
this.posIdx = this.screen.getPosIdx(this.position);
this.initElement();
level.element.append(this.element);
return this;
};
ClassPrototype.initElement = function(){
this.element = $('');
};
ClassPrototype.check = function(){
if ( this.screen.image.data[this.posIdx] == GL.Control.states.free ) return this;
this.consume();
return null;
};
ClassPrototype.consume = function(){
this.element.remove();
switch ( this.type ) {
case 'live':
this.level.adjustLives(+1);
break;
case 'timeout':
this.level.timeout(Config.types.timeout.duration);
break;
case 'bonus':
this.level.updateScore(Config.types.bonus.value,this.position);
break;
}
};
ClassPrototype.update = function(){
this.lifetime--;
if ( this.lifetime < 0 ) {
this.element.remove();
return null;
}
return this;
};
})();
/* Speedometer */
var Speedometer;
(function(){
//Class Definition
var ClassStatic = Speedometer = function(){};
var ClassPrototype = ClassStatic.prototype;
//--
ClassPrototype.__construct = function(parentElement,max,size){
var parent = document.createElement('div');
parent.innerHTML = '