/* 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('SomaFM commercial free internet radio'); 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 = ''; this.element = parent.firstChild; parent.innerHTML = '

'; this.textElement = parent.firstChild; parentElement.appendChild(this.element); parentElement.appendChild(this.textElement); this.offset = 3; this.maxSpeed = max; this.dim = {x:size,y:size}; this.radius = (size-2*this.offset)/2; this.innerRadius = this.radius - this.offset; this.center = { x:this.radius+this.offset, y:this.radius+this.offset }; this.fullRad = Math.PI*1.5; this.radOffset = Math.PI*0.75; this.speed = 0; this.init(); this.draw(); this.setTarget(0); return this; }; ClassPrototype.remove = function(){ var parent = this.element.parentNode; parent.removeChild(this.element); parent.removeChild(this.textElement); }; ClassPrototype.init = function(){ this.context = this.element.getContext('2d'); this.context.translate(this.center.x,this.center.y); this.context.fillStyle = '#fff'; this.context.strokeStyle = '#000'; this.context.beginPath(); this.context.arc(0,0,this.radius,0,2*Math.PI,false); // Outer circle this.context.fill(); this.context.stroke(); this.drawSegments(); this.background = this.context.getImageData(0,0,this.dim.x,this.dim.y); }; ClassPrototype.drawSegments = function(){ var segmentLength = this.fullRad/6; var colors = ['#00a000','#33a833','#66b066','#99b899','#bbc0bb','#ccc8cc','#ddd0dd','#eed8ee']; for ( var i=this.radOffset; ithis.speed?1:-1); } }; ClassPrototype.update = function(){ if ( !this.speedOffset ) return; this.speed += this.speedOffset; this.textElement.innerHTML = ''+this.speed; this.draw(); if ( this.speed == this.targetSpeed ) { this.speedOffset = 0; } }; })(); (function(){ //Class Definition var ClassStatic = Speedometer.Framemeter = function(){}; var ParentClass = Speedometer; var ParentPrototype = ParentClass.prototype; var ClassPrototype = ClassStatic.prototype = new ParentClass; //-- ClassPrototype.update = function(){ if ( !this.frame ) { var now = new Date().getTime(); this.frame = { counter:0, start:now }; } else { this.frame.counter++; } if ( this.frame.counter > 50 ){ var now = new Date().getTime(); var time = now-this.frame.start; this.setTarget(Math.round(this.frame.counter/time*1000)); this.frame.counter = 0; this.frame.start = now; } ParentPrototype.update.call(this); }; }()); /* Hourglass */ var Hourglass; (function(){ //Class Definition var ClassStatic = Hourglass = function(){}; var ClassPrototype = ClassStatic.prototype; //-- ClassPrototype.__construct = function(parentElement,count,width,height){ var parent = document.createElement('div'); parent.innerHTML = ''; this.element = parent.firstChild; parentElement.appendChild(this.element); this.pos = {x:10,y:5}; this.dim = { x:width-(this.pos.x*2), y:height-(this.pos.y*2) }; this.dim.full = {x:width,y:height}; this.maxRotation = 30; count -= this.maxRotation; this.maxCount = count; this.count = 0; this.center = {x:this.dim.full.x/2,y:this.dim.full.y/2}; var topDimY = this.dim.y/2; this.ratio = this.dim.x / topDimY; var A = (this.dim.x*topDimY)/2; this.scale = A/(this.maxCount*1.5); this.init(); this.draw(); return this; }; ClassPrototype.init = function(){ this.context = this.element.getContext('2d'); this.clearImage = this.context.getImageData(0,0,this.dim.full.x,this.dim.full.y); this.context.translate(this.center.x,this.center.y); this.drawBackground(); this.background = this.context.getImageData(0,0,this.dim.full.x,this.dim.full.y); }; ClassPrototype.remove = function(){ var parent = this.element.parentNode; parent.removeChild(this.element); }; ClassPrototype.flip = function(){ if ( !this.rotation ) this.rotation = this.maxRotation; }; ClassPrototype.update = function(){ if ( this.rotation > 0 ) { this.rotate(); return true; } if ( this.count <= 0 ) return false; this.count--; this.draw(); return true; }; ClassPrototype.rotate = function(){ this.clear(); this.rotation--; this.context.rotate(Math.PI/this.maxRotation); this.drawBackground(); this.drawSand(); if ( this.rotation <= 0 ) { this.context.rotate(Math.PI); this.count = this.maxCount; this.rotation = 0; } }; ClassPrototype.clear = function(){ var c = this.center; this.context.putImageData(this.clearImage,0,0); }; ClassPrototype.draw = function(){ this.clear(); this.context.putImageData(this.background,0,0); this.drawSand(); }; ClassPrototype.drawBackground = function(){ this.context.lineWidth = 3; this.context.lineCap = 'round'; this.context.lineJoin = 'round'; this.context.fillStyle = 'white'; this.context.strokeStyle = 'black'; var c = { x:-this.pos.x+this.center.x, y:-this.pos.y+this.center.y }; this.context.beginPath(); this.context.moveTo(-c.x,-c.y); this.context.lineTo(c.x,-c.y); this.context.lineTo(-c.x,c.y); this.context.lineTo(c.x,c.y); this.context.lineTo(-c.x,-c.y); this.context.closePath(); this.context.stroke(); this.context.fill(); }; ClassPrototype.drawSand = function(){ if ( this.count >= 0 ) { var h = Math.sqrt((2*this.count*this.scale)/this.ratio); var a = this.ratio*h; this.drawTriangle(a,h,{ x:0, y:0 }); } var r = this.ratio; var h = Math.sqrt((2*(this.maxCount-this.count)*this.scale)/r); var a = r*h; this.drawTriangle(a,-h,{ x:0, y:(this.dim.y/2-h) }); }; ClassPrototype.drawTriangle = function(a,h,center){ this.context.beginPath(); this.context.fillStyle = '#800'; var top = {x: center.x-a/2,y:center.y-h}; this.context.moveTo(top.x,top.y); this.context.lineTo(top.x+a,top.y); this.context.lineTo(center.x,center.y); this.context.lineTo(top.x,top.y); this.context.fill(); this.context.closePath(); }; })(); /* Debug */ var Debug = function(){}; Debug.log = function(data){ /* if ( window.console ) { window.console.log(data); return; } */ if ( !Debug.console ) { var c = document.createElement('DIV'); c.innerHTML = '

Debug Console

'; c.style.fontFamily = 'Courier New'; c.style.fontSize = '9px'; c.style.lineHeight = '10px'; c.style.fontWeight = 'normal'; c.style.position = 'absolute'; c.style.top = '0px'; c.style.right = '0px'; c.style.width = '400px'; c.style.height = '800px'; document.body.appendChild(c); Debug.console = c; } var p = document.createElement('p'); p.innerHTML = Debug.toString(data); Debug.console.appendChild(p); }; Debug.toString = function(data,maxDepth){ if ( isNaN(maxDepth) ) maxDepth = 2; if ( maxDepth <= 0 ) return data; var result; var color = 'blue'; if ( data instanceof Array ) { result = 'Array [
    '; for( var i=0; i < data.length; i++) { result += '
  1. '+ Debug.toString(data[i],maxDepth-1) +'
  2. '; } result += '
]'; } else if ( data instanceof Object ) { result = typeof(data) +' {
    '; for ( var varName in data ) { var value; try { value = data[varName]; } catch( e ) { value = e.toString(); } result += '
  • '+ varName +': '+ Debug.toString(value,maxDepth-1) +'
  • '; } result += '
}'; } else { switch ( typeof(data) ) { case 'string': color = 'green'; result = data; break; case 'number': default: color = 'red'; try{ result = String(data); } catch ( e ){ result = typeof(data) +' ('+ e.toString() +')'; } break; } } return ''+result+'';; };GL.loadLibrary({"Ready":"Ready?","Ready Message":"Press [n] to start a new Game","Help Message":"Hit [space] to start!","Game Paused Message":"Press space to continue","Game Over Message":"Press [n] to start a new game","Level Complete Message":"Press space to start next level","New Highscore Message":"You just topped your personal highscore!","FB Highscore Message Prompt":"Tell your friends about your achievement","FB Highscore Message":"I just scored [score] points and reached level [level]!","Classic Help Message":"Use the arrow-keys to navigate through the dangerous realms, without getting caught while trying to claim area for yourself...","Dungeon Help Message":"Enter the dungeon at your own risk. Use the impassable Walls to block off your enemies...","Bridging Help Message":"To score, cover the highlighted areas. Beware the sharks, they circle round once you enter unknown depths...","Music Button Message":"Toggle music on\/off [m]","Sound Button Message":"Toggle sound on\/off [s]","NewGame Button Message":"Start a new game [n]","Pause Button Message":"Pause [space]","music":"Music is on","sound":"Sound is on","levelNr":"Current level","lives":"Your remaining tries","timer":"Standstill countdown","score":"Your score","highscore":"Your highest score","totalScreen":"Your remaining area to fill","timeScreen":"Your remaining time"});