blob: 6ee7076c7132c78959380c5c4939d40b496702c9 [file] [log] [blame]
/*
* Copyright (c) 2007, Opera Software ASA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Opera Software ASA nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY OPERA SOFTWARE ASA ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL OPERA SOFTWARE ASA BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* $ alias of document.getElementById
*/
function $(id)
{
return document.getElementById(id);
}
/*
* Function bind
*/
Function.prototype.bind = function( object )
{
var method = this;
return function ()
{
return method.apply( object, arguments );
}
}
/*
* on load, instanciate a new BubbleGame
*/
if(window.addEventListener){
window.addEventListener
(
'load',
function(/* load callback */)
{
if (screen.availHeight + screen.availWidth <= 800)
{
window.moveTo(0, 0);
window.resizeTo(screen.availWidth, screen.availHeight);
window.scrollTo(0,0);
}
new BubbleGame( 'gameArea', 'newGame', 'score', 'hiScore' );
},
false
)
}
else{
window.attachEvent
(
'onload',
function(/* load callback */)
{
if (screen.availHeight + screen.availWidth <= 800)
{
window.moveTo(0, 0);
window.resizeTo(screen.availWidth, screen.availHeight);
window.scrollTo(0,0);
}
new BubbleGame( 'gameArea', 'newGame', 'score', 'hiScore' );
}
)
}
/*
* BubbleGame
*/
function BubbleGame( gameAreaId, newGameId, scoreId, hiScoreId )
{
var that = this,
isGameOver = false,
width = 0,
height = 0,
tiles = 0,
score = 0,
map = [],
colors = ['#e00','#fa0','#766','#0cf','#a0f','#3b0'];
/*
* displayScore
*/
function displayScore()
{
hScore.textContent = score;
hHiScore.textContent = hiScore;
$('dock').textContent = "Score: " + score;
}
/*
* newGame
*/
this.newGame = function()
{
score = 0;
width = 0;
height = 0;
map.length = 0;
tiles = 0;
displayScore();
hGameArea.className = '';
hGameArea.innerHTML = '';
if(window.addEventListener){
hGameArea.addEventListener( 'mouseover', hoverTile.bind(this), false );
hGameArea.addEventListener( 'mouseout', hoverTile.bind(this), false );
}
else{
hGameArea.attachEvent( 'onmouseover', hoverTile.bind(this));
hGameArea.attachEvent( 'onmouseout', hoverTile.bind(this));
}
var isFull = false;
while( !isFull )
{
var newTile = document.createElement('span'),
color = colors[Math.random()*colors.length|0];
map.push( color );
newTile.setAttribute( 'style', 'background-color:'+ color );
newTile.setAttribute( 'data-index', tiles );
hGameArea.appendChild( newTile );
if(window.addEventListener){
newTile.addEventListener( 'click', clickTile.bind(this), false );
}
else{
newTile.attachEvent( 'onclick', clickTile.bind(this));
}
//newTile.onclick= clickTile.bind(this);
if( !newTile.offsetTop )
{
width++;
}
isFull = !(++tiles%width) && (newTile.offsetTop+newTile.offsetHeight*2)>=hGameArea.clientHeight;
}
height = hGameArea.childNodes.length/width;
}
/*
* floodFill
*/
function floodFill( x,y )
{
var color = map[ x+y*width ],
floodMap = new Array(map.length),
n = 0;
if( color )
{
n = (function( x, y )
{
var i = x+y*width;
if( map[i]==color && !floodMap[i] )
{
// mark tile
floodMap[i] = 1;
// recurse
var n = 1;
if( x>0 ) n += arguments.callee( x-1, y );
if( x<width-1 ) n += arguments.callee( x+1, y );
if( y>0 ) n += arguments.callee( x, y-1 );
if( y<height-1) n += arguments.callee( x, y+1 );
return n;
}
return 0;
})( x, y );
}
return {'map':floodMap,'tiles':n}
}
/*
* hoverTile
*/
function hoverTile(event)
{
var src = event.srcElement? event.srcElement : event.target;
if( src==event.currentTarget || event.type=='mouseout' )
{
var hoverMap = new Array(map.length);
}
else
{
var index = src.getAttribute('data-index')|0,
color = map[index],
x = index%width,
y = index/width|0,
hoverMap= floodFill( x, y ).map;
}
// update tiles' display
var node = hGameArea.firstChild,
i = 0;
while( node )
{
var className = hoverMap[i++]?'hover':'';
if( node.className != className )
{
node.className = className;
}
node = node.nextSibling;
}
}
/*
* clickTile
*/
function clickTile(event)
{
var src = event.srcElement? event.srcElement : event.target,
index = src.getAttribute('data-index')|0,
x = index%width,
y = index/width|0,
flood = floodFill( x, y );
// a single|empty tile -> exit
if( flood.tiles<2 )
{
return true;
}
// clear tiles
for( var i=flood.map.length;i--; )
{
if( flood.map[i] )
map[i] = 0;
}
// decrease tiles count
tiles -= flood.tiles;
// update score
score+= (1+flood.tiles)*flood.tiles;
if( tiles )
{
// waterfall
var cols=[]
for( var x=0; x<width; x++ )
{
var i = map.length-width+x,
n = height-1;
while( i>=width )
{
var j = i-width;
if( !map[i] )
{
while( j>=0 && !map[j])j-=width;
if( j>=0 )
{
map[i] = map[j];
map[j] = 0;
}
}
if( !map[i] )
{
n--;
}
i -= width;
}
cols.push(n)
}
// collapse empty columns
for( var xx=0; xx<width*width; xx++ )
{
var x = xx%(width-1);
if( cols[x] )
continue;
var i = x;
while( i<map.length )
{
map[i] = map[i+1];
map[i+1]= 0;
i += width;
}
cols[x] = cols[x+1];
cols[x+1] = 0;
}
// FAIL ?
var fail = true,
i = map.length;
while( fail && i-- )
{
if( color = map[i] )
{
var x = i%width,
y = i-x;
if( fail && x>0 && color==map[i-1] ) fail = false;
if( fail && x<width-1 && color==map[i+1] ) fail = false;
if( fail && y>0 && color==map[i-width] ) fail = false;
if( fail && y<height-1 && color==map[i+width] ) fail = false;
}
}
if( fail )
{
hGameArea.className = 'fail';
isGameOver = true;
}
}
// update tiles' display
var node = hGameArea.firstChild,
i = 0;
while( node )
{
var color = map[i++];
if( color )
{
color = 'background-color:'+ color;
if( node.getAttribute( 'style' )!=color )
{
node.setAttribute( 'style', color );
}
}
else if( node.hasAttribute( 'style' ) )
{
node.removeAttribute( 'style' );
}
node.className = '';
node = node.nextSibling;
}
// WIN!?
if( !tiles )
{
score += 100; // ULTRA WIN!
hGameArea.className = 'win';
isGameOver = true;
}
// update score display and hiScore
displayScore();
if( isGameOver && score>hiScore )
{
hiScore = score;
widget.preferences.setItem('hiScore', hiScore);
}
isGameOver = false;
return true;
}
// initialize
var hScore = $(scoreId),
hGameArea = $(gameAreaId),
hHiScore = $(hiScoreId);
var hiScore;
try{
hiScore = widget.preferences.getItem('hiScore');
if(typeof hiScore === "undefined" || hiScore === "undefined" || hiScore === null){
hiScore = 0;
}
}
catch(err){
hiScore = 0;
}
if(window.addEventListener){
$(newGameId).addEventListener
(
'click',
this.newGame.bind(this),
false
);
}
else{
$(newGameId).attachEvent
(
'onclick',
this.newGame.bind(this)
);
}
this.newGame();
}