File indexing completed on 2024-04-28 04:02:03

0001 /*
0002     SPDX-FileCopyrightText: 2012 Viranch Mehta <viranch.mehta@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 var ballComponent = Qt.createComponent("Ball.qml");
0008 var balls = new Array;
0009 var brickComponent = Qt.createComponent("Brick.qml");
0010 var bricks = new Array;
0011 var giftComponent = Qt.createComponent("Gift.qml");
0012 var gifts = new Array;
0013 // score to add if you hit a brick
0014 // decreases over time since last hit
0015 var dScore;
0016 // count of remaining bricks
0017 // (not counting the unbreakable ones)
0018 var remainingBricks=0;
0019 var tick = 0;
0020 var repaintInterval;
0021 var randomCounter = 0;
0022 // is set to true when deleteMovingObjects() is called
0023 var itemsGotDeleted;
0024 var singleShotComponent = Qt.createComponent("Singleshot.qml");
0025 
0026 // Substeps are used to avoid that the ball is moved too far in a single update,
0027 // possibly leading to buggy behavior. The movement is divided into multiple
0028 // substeps and collisions are detected after each substep.
0029 var substeps = 1;
0030 
0031 function remove(array, object) {
0032     array.splice(array.indexOf(object), 1);
0033 }
0034 
0035 function singleShot(interval, slot, target) {
0036     var timer = singleShotComponent.createObject(canvas);
0037     timer.interval = interval;
0038     timer.target = target;
0039     timer.timeout.connect(slot);
0040     timer.start();
0041 }
0042 
0043 function scoreString(score) {
0044     var absScore = Math.abs(score);
0045     var str = absScore.toString().split("");
0046 
0047     // insert spaces every 3 characters
0048     var start = str.length%3;
0049     if (start==0) start=3;
0050     for (; start<str.length; start+=4) {
0051         str.splice(start, 0, " ");
0052     }
0053     
0054     // join the array
0055     var displayString = score<0 ? "-" : "";
0056     for (var i in str) {
0057         displayString += str[i];
0058     }
0059     return displayString;
0060 }
0061 
0062 function reset() {
0063     level = 0;
0064     score = 0;
0065     resetBricks();
0066     bar.reset();
0067     gameOver = false;
0068     gameWon = false;
0069     deleteMovingObjects();
0070     paused = false;
0071     lives = Globals.INITIAL_LIVES;
0072     mouseArea.enabled = true;
0073 }
0074 
0075 function resetBricks() {
0076     for (var i in bricks) {
0077         bricks[i].destroy();
0078     }
0079     bricks = new Array;
0080     remainingBricks = 0;
0081 }
0082 
0083 function getTypeFromChar(type) 
0084 {
0085     switch (type) {
0086     case '1': return "PlainBrick1";
0087     case '2': return "PlainBrick2";
0088     case '3': return "PlainBrick3";
0089     case '4': return "PlainBrick4";
0090     case '5': return "PlainBrick5";
0091     case '6': return "PlainBrick6";
0092     case 'm': return "MultipleBrick3";
0093     case 'x': return "ExplodingBrick";
0094     case 'u': return "UnbreakableBrick";
0095     case 'h': return "HiddenBrick";
0096     default:
0097         print("Invalid File: unknown character '", type, "'");
0098         return "PlainBrick1";
0099     }
0100 }
0101 
0102 function indexForPos(position) {
0103     var pos = position.split(",");
0104     var column = parseInt(pos[0]) - 1;
0105     var row = parseInt(pos[1]) - 1;
0106     return index(row, column);
0107 }
0108 
0109 function index(row, column) {
0110     for (var i in bricks) {
0111         var brick = bricks[i];
0112         if (brick.row==row && brick.column==column) {
0113             return i;
0114         }
0115     }
0116     return -1;
0117 }
0118 
0119 function putGiftOnRandomBrick(gift, except) {
0120     var index;
0121     var brick;
0122     do {
0123         index = Math.round(Math.random()*bricks.length);
0124         brick = bricks[index];
0125     } while (index==except || brick==null || brick.hasGift);
0126     brick.giftType = gift;
0127 }
0128 
0129 function giftlessBricks() {
0130     var count = 0;
0131     for (var i=0; i<bricks.length; i++) {
0132         var brick = bricks[i];
0133         if (!brick.hasGift) count++;
0134     }
0135 }
0136 
0137 function createBrick(row, column, type) {
0138     var brick = brickComponent.createObject(bgOverlay);
0139     brick.row = row;
0140     brick.column = column;
0141     brick.type = type;
0142     bricks.push(brick);
0143 }
0144 
0145 function showLine(line, number) {
0146     for (var i=0; i<line.length; i++) {
0147         var c = line[i];
0148         if (c == '-') continue;
0149         createBrick(number-1, i, getTypeFromChar(c));
0150         if (c!='h' && c!='u') ++remainingBricks;
0151     }
0152 }
0153 
0154 function putGift(gift, times, pos) {
0155     if (pos!="") {
0156         // Put gift at given position
0157         var index = indexForPos(pos);
0158         if (index == -1) {
0159             print("error:", "Can't place gift at position (", pos, "). There is no brick.");
0160         }
0161         else {
0162             var giftBrick = bricks[index];
0163             if (giftBrick.hasGift) {
0164                 // Brick already has a gift -> move this gift to a random position
0165                 putGiftOnRandomBrick(giftBrick.giftType, index);
0166             }
0167             giftBrick.giftType = gift;
0168         }
0169     }
0170     else {
0171         // Distribute gifts randomly
0172         var bricksLeft = giftlessBricks();
0173         if (bricksLeft < times) {
0174             print("error:", "Too many gifts of type", gift);
0175             return;
0176         }
0177         for (var i=0; i<times; i++) {
0178             putGiftOnRandomBrick(gift, -1);
0179         }
0180     }
0181 }
0182 
0183 function createBall() {
0184     var ball = ballComponent.createObject(bgOverlay);
0185     ball.posX = (bar.x + ball.barPosition*bar.width)/m_scale;
0186     ball.posY = (bar.y - ball.height)/m_scale;
0187     balls.push(ball);
0188 }
0189 
0190 function startLevel() {
0191     if (bricks.length==0) {
0192         if (level == 0) {
0193             // No level in the levelset
0194             print("Invalid levelset");
0195         } else {
0196             // No more levels: game won
0197             gameWon = true;
0198             score += Globals.GAME_WON_SCORE + lives*Globals.LIFE_SCORE;
0199             showMessage(i18n("Well done! You won the game"));
0200             singleShot(10, endGame, null);
0201             deleteMovingObjects();
0202         }
0203         return;
0204     }
0205     ++level;
0206     showMessage(i18n("Level %1", level));
0207     hideLater(messageBox, 2000);
0208     resumeGame();
0209 }
0210 
0211 function loadNextLevel() {
0212     // assign points for each remaining brick
0213     for (var i=0; i<bricks.length; i++) {
0214         var type = bricks[i].type;
0215         // don't assign points for unbreakable bricks
0216         if (type == "UnbreakableBrick") {
0217             continue;
0218         }
0219         if (type == "") {
0220             continue;
0221         }
0222         score += Globals.AUTOBRICK_SCORE;
0223 
0224         // add extra points for multiple bricks
0225         if (type == "MultipleBrick3") {
0226             score += Globals.AUTOBRICK_SCORE*2;
0227         } else if (type == "MultipleBrick2") {
0228             score += Globals.AUTOBRICK_SCORE;
0229         }
0230     }
0231     deleteMovingObjects();
0232     score += Globals.LEVEL_SCORE;
0233     resetBricks();
0234     canvas.levelComplete();
0235 }
0236 
0237 function resumeGame() {
0238     createBall();
0239     bar.reset();
0240     speed = 1.8;
0241     repaintInterval = 1;
0242     substeps = 1;
0243     elapsedTimeTimer.restart();
0244     gameTimer.interval = Globals.REPAINT_INTERVAL;
0245     gameTimer.restart();
0246     showFireBallMessage();
0247 }
0248 
0249 function endGame() {
0250     canvas.gameEnded(score, (gameWon ? -1 : level), elapsedTimeTimer.elapsedTime);
0251 }
0252 
0253 function moveBalls() {
0254     for (var i in balls) {
0255         var ball = balls[i]
0256         if (ball.toBeFired) {
0257             ball.posX = (bar.x + ball.barPosition*bar.width)/m_scale;
0258         } else {
0259             ball.posX += ball.directionX * speed;
0260             ball.posY += ball.directionY * speed;
0261         }
0262 
0263     }
0264 }
0265 
0266 function detectCollisions() {
0267     // needed to exit from the loop if the arrays that they cycle
0268     // change (items get deleted)
0269     itemsGotDeleted = false;
0270 
0271     dScore *= Globals.SCORE_AUTO_DECREASE;
0272     for (var i in balls) {
0273         var ball = balls[i];
0274         if (ball.toBeFired) {
0275             continue;
0276         }
0277 
0278         // collision detection
0279         firstTime = true;
0280         detectBallCollisions(ball);
0281         if (itemsGotDeleted) {
0282             return;
0283         }
0284     }
0285 
0286     for (var i=0; i<gifts.length; i++) {
0287         var gift = gifts[i];
0288         var giftRect = createRect(gift);
0289         var barRect = createRect(bar);
0290         if (gift.y+gift.height > bgOverlay.height) {
0291             remove(gifts, gift);
0292             gift.destroy();
0293             i--;
0294         } else if (intersects(giftRect, barRect)) {
0295             executeGift(gift.type);
0296             if (itemsGotDeleted) {
0297                 return;
0298             }
0299             remove(gifts, gift);
0300             gift.destroy();
0301             i--;
0302         }
0303     }
0304 
0305     tick = (tick+1) % repaintInterval;
0306 
0307     if (tick==0) {
0308         updateBallDirection();
0309     }
0310 }
0311 
0312 function fireBall() {
0313     if (paused) {
0314         print("trying to fire while game is paused!!!");
0315         return;
0316     }
0317 
0318     for (var i in balls) {
0319         var ball = balls[i];
0320         if (!ball.toBeFired) continue;
0321 
0322         var ballCenter = ball.x + ball.width/2;
0323         var barCenter = bar.x + bar.width/2;
0324         var angle = (Math.PI/3) * (barCenter-ballCenter)/(bar.width/2) + Math.PI/2;
0325 
0326         ball.directionX =  Math.cos(angle) * Globals.BALL_SPEED;
0327         ball.directionY = -Math.sin(angle) * Globals.BALL_SPEED;
0328         ball.toBeFired = false;
0329     }
0330 
0331     dScore = Globals.BRICK_SCORE;
0332     hideFireBallMessage();
0333 
0334     randomCounter = 0;
0335 }
0336 
0337 function addLife() {
0338     if (lives < Globals.MAXIMUM_LIVES) {
0339         lives++;
0340     }
0341 }
0342 
0343 function setGamePaused(paused) {
0344     if (gameOver || gameWon || canvas.paused==paused) return;
0345     canvas.paused = paused;
0346     if (paused) {
0347         showMessage(i18n("Game Paused!"));
0348         elapsedTimeTimer.stop();
0349         gameTimer.stop();
0350     } else {
0351         elapsedTimeTimer.start();
0352         gameTimer.start();
0353         hideMessage();
0354     }
0355 }
0356 
0357 function showMessage(text) {
0358     if (messageBox.text == text) {
0359         messageBox.opacity = 1;
0360     } else {
0361         messageBox.text = text;
0362     }
0363 }
0364 
0365 function showFireBallMessage(text) {
0366     fireBallMessage.opacity = 1;
0367 }
0368 
0369 function hideMessage() {
0370     if (gameOver || gameWon || paused) return;
0371     messageBox.opacity = 0;
0372 }
0373 
0374 function hideFireBallMessage() {
0375     fireBallMessage.opacity = 0;
0376 }
0377 
0378 function hideLater(target, interval) {
0379     hideTimer.target = target;
0380     hideTimer.interval = interval;
0381     hideTimer.start();
0382 }
0383 
0384 function updateBallDirection() {
0385     // avoid infinite loops of the ball
0386     ++randomCounter;
0387     if (randomCounter == 1024) {
0388         randomCounter = 0;
0389         for (var i in balls) {
0390             var ball = balls[i];
0391             if (Math.floor(Math.random()*10) % 2) {
0392                 ball.directionX += 0.002;
0393             } else {
0394                 ball.directionY += 0.002;
0395             }
0396         }
0397 
0398         // increase the speed a little
0399         // if there is at least one ball moving
0400         // and the game isn't paused
0401         var ballMoving = false;
0402         for (var i in balls) {
0403             if (!balls[i].toBeFired) {
0404                 ballMoving = true;
0405                 break;
0406             }
0407         }
0408         if (ballMoving && !paused) {
0409             changeSpeed(Globals.AUTO_SPEED_INCREASE);
0410         }
0411     }
0412 }
0413 
0414 function changeSpeed(ratio) {
0415     speed *= ratio;
0416     if (speed > 2.0) {
0417         // half the speed
0418         speed /= 2.0;
0419         // and double the number of ticks of the timer per time unit
0420         substeps *= 2;
0421     }
0422     if (speed < 1.0) {
0423         if (substeps == 1) {
0424             speed = Globals.MINIMUM_SPEED
0425             return;
0426         }
0427         // else
0428 
0429         // double the speed
0430         speed *= 2.0;
0431         // and half the number of ticks of the timer per time unit
0432         substeps /= 2;
0433     }
0434 }
0435 
0436 function deleteMovingObjects() {
0437     itemsGotDeleted = true;
0438     var times = balls.length
0439     for (var i=0; i<times; i++) {
0440         balls.shift().destroy();
0441     }
0442     
0443     times = gifts.length
0444     for (var i=0; i<times; i++) {
0445         gifts.shift().destroy();
0446     }
0447 }
0448 
0449 function createRect(object) {
0450     return {
0451         left: object.x,
0452         top: object.y,
0453         right: object.x + object.width,
0454         bottom: object.y + object.height
0455     };
0456 }
0457 
0458 function intersects(r1, r2) {
0459     return !(r2.left > r1.right ||
0460              r2.right < r1.left ||
0461              r2.top > r1.bottom ||
0462              r2.bottom < r1.top);
0463 }
0464 
0465 function intersectArea(r1, r2) {
0466     return (Math.min(r1.right, r2.right) - Math.max(r1.left, r2.left)) *
0467            (Math.min(r1.bottom, r2.bottom) - Math.max(r1.top, r2.top));
0468 }
0469 
0470 // never run this function more than two times recursively
0471 var firstTime = true;
0472 function detectBallCollisions(ball) {
0473     // bounce a little early in some cases so the average position is centered
0474     var rect = createRect(ball);
0475     rect.left += ball.directionX/2;
0476     rect.top += ball.directionY/2;
0477     var x = rect.left;
0478     var y = rect.top;
0479 
0480     var barRect = createRect(bar);
0481 
0482     // bounce against the wall
0483     if (x < 0 && ball.directionX < 0) {
0484         ball.directionX *= -1;
0485     } else if (x+ball.width > bgOverlay.width
0486                 && ball.directionX > 0) {
0487         ball.directionX *= -1;
0488     } else if (y < 0 && ball.directionY < 0) {
0489         ball.directionY *= -1;
0490     } else if (y+ball.height > bgOverlay.height
0491                 && ball.directionY > 0) {
0492         // delete the ball
0493         remove(balls, ball);
0494         ball.destroy();
0495         itemsGotDeleted = true;
0496         if (balls.length == 0) {
0497             showMessage(i18n("Oops! You have lost the ball!"));
0498             handleDeathTimer.start();
0499             gameTimer.stop();
0500 
0501             deleteMovingObjects();
0502         }
0503         return;
0504     }
0505     // bounce against the bar
0506     else if (intersects(rect, barRect) && ball.directionY > 0) {
0507         
0508         var ballCenter = ball.x + ball.width/2;
0509         var barCenter = bar.x + bar.width/2;
0510 
0511         if (ballCenter > bar.x &&
0512                 ballCenter < bar.x+bar.width) {
0513             // the bar has been hit
0514 
0515             if (bar.type == "StickyBar") {
0516                 ball.toBeFired = true;
0517 
0518                 var diff = ball.x - bar.x;
0519 
0520                 ball.barPosition = diff / bar.width;
0521                 ball.posY = (bar.y-ball.height)/m_scale;
0522             }
0523 
0524             var angle = (Math.PI/3) * (barCenter-ballCenter)/(bar.width/2) + Math.PI/2;
0525 
0526             ball.directionX =  Math.cos(angle) * Globals.BALL_SPEED;
0527             ball.directionY = -Math.sin(angle) * Globals.BALL_SPEED;
0528         }
0529     } else { // bounce against the bricks (and optionally break them)
0530         handleBrickCollisions(ball);
0531     }
0532 
0533     // never run this function more than two times recursively
0534     if (firstTime) {
0535         firstTime = false;
0536         // check if there is another collision
0537         if (!itemsGotDeleted) {
0538             detectBallCollisions(ball);
0539         }
0540     } else {
0541         firstTime = true;
0542         return;
0543     }
0544 }
0545 
0546 function handleDeath() {
0547     hideMessage();
0548     deleteMovingObjects();
0549     bar.reset();
0550     if (lives == 0) {
0551         gameOver = true;
0552         showMessage(i18n("Game Over!"));
0553         elapsedTimeTimer.stop();
0554         bar.stopMoving();
0555         singleShot(10, endGame, null);
0556     } else {
0557         lives--;
0558         resumeGame();
0559     }
0560 }
0561 
0562 function handleBrickCollisions(ball) {
0563     if (itemsGotDeleted) {
0564         return;
0565     }
0566     var rect = createRect(ball);
0567     var intersectingBricks = new Array;
0568 
0569     for (var i=0; i<bricks.length; i++) {
0570         var brick = bricks[i];
0571         if (brick == null) continue;
0572         var brickRect = createRect(brick);
0573 
0574         if (intersects(brickRect, rect)) {
0575             intersectingBricks.push(brick);
0576         }
0577     }
0578 
0579     if (intersectingBricks.length != 0) {
0580         collideWithBricks(ball, intersectingBricks);
0581     }
0582 }
0583 
0584 function collideWithBricks(ball, bricks) {
0585     if (bricks.length == 2) {
0586         collideWithTwoBricks(ball, bricks);
0587     } else {
0588         collideWithBrick(ball, bricks[0]);
0589     }
0590 }
0591 
0592 function collideWithTwoBricks(ball, bricks) {
0593     var ballRect = createRect(ball);
0594     var r1 = createRect(bricks[0]);
0595     var r2 = createRect(bricks[1]);
0596 
0597     var area1 = intersectArea(ballRect, r1);
0598     var area2 = intersectArea(ballRect, r2);
0599 
0600     if (area1 > area2) {
0601         // the area of intersection with the first brick is bigger
0602         collideWithBrick(ball, bricks[0]);
0603     } else {
0604         collideWithBrick(ball, bricks[1]);
0605     }
0606 }
0607 
0608 function collideWithBrick(ball, brick) {
0609     if (ball.type == "UnstoppableBall") {
0610         forcedHit(brick);
0611         return; // don't bounce
0612     }
0613     if (ball.type == "UnstoppableBurningBall") {
0614         explode(brick);
0615         return; // don't bounce
0616     }
0617 
0618     // calculate bounce
0619     var brickRect = createRect(brick);
0620     var ballRect = createRect(ball);
0621 
0622     var top = Math.round(ballRect.bottom - brickRect.top);
0623     var bottom = Math.round(brickRect.bottom - ballRect.top);
0624     var left = Math.round(ballRect.right - brickRect.left);
0625     var right = Math.round(brickRect.right - ballRect.left);
0626     var min = Math.min(Math.min(top, bottom), Math.min(left, right));
0627 
0628     // bounce
0629     if (min == top && ball.directionY > 0) {
0630         ball.directionY *= -1;
0631         ball.posY -= top/m_scale;
0632     } else if (min == bottom && ball.directionY < 0) {
0633         ball.directionY *= -1;
0634         ball.posY += bottom/m_scale;
0635     } else if (min == left && ball.directionX > 0) {
0636         ball.directionX *= -1;
0637         ball.posX -= left/m_scale;
0638     } else if (min == right && ball.directionX < 0) {
0639         ball.directionX *= -1;
0640         ball.posX += right/m_scale;
0641     } else {
0642         return; // already bounced
0643     }
0644 
0645     if (ball.type == "BurningBall") {
0646         explode(brick);
0647     } else {
0648         hit(brick);
0649     }
0650 }
0651 
0652 function addBrickScore() {
0653     score += Math.round(dScore);
0654     dScore = Globals.BRICK_SCORE;
0655 }
0656 
0657 function forcedHit(brick) {
0658     if (brick == null) return;
0659 
0660     if (brick.type == "ExplodingBrick") {
0661         explode(brick);
0662     } else {
0663         handleDeletion(brick);
0664     }
0665 }
0666 
0667 function hit(brick) {
0668     if (brick.type == "HiddenBrick" && !brick.visible) {
0669         brick.visible = true;
0670         ++remainingBricks;
0671     } else if (brick.type == "MultipleBrick3") {
0672         brick.type = "MultipleBrick2";
0673         addBrickScore();
0674     } else if (brick.type == "MultipleBrick2") {
0675         brick.type = "MultipleBrick1";
0676         addBrickScore();
0677     } else if (brick.type == "ExplodingBrick") {
0678         explode(brick);
0679     } else if (brick.type != "UnbreakableBrick") {
0680         handleDeletion(brick);
0681     }
0682 }
0683 
0684 function explode(brick) {
0685     if (brick == null) return;
0686 
0687     burn(brick);
0688     singleShot(Globals.BURNING_SPEED, burnNearbyBricks, createRect(brick));
0689 }
0690 
0691 function burnNearbyBricks(brickRect) {
0692     var nearby = nearbyBricks(brickRect);
0693     for (var i in nearby) {
0694         burn(nearby[i]);
0695     }
0696 }
0697 
0698 function burn(brick) {
0699     if (brick == null) return;
0700 
0701     if (brick.type == "ExplodingBrick") {
0702         // make sure it doesn't explode twice
0703         brick.type = "BurningBrick";
0704         explode(brick);
0705     } else {
0706         singleShot(Globals.BURNING_SPEED, deleteBrick, new Array(brick.x, brick.y));
0707     }
0708 
0709     if (brick.type == "HiddenBrick" && !brick.visible
0710             || brick.type == "UnbreakableBrick") {
0711         // Add to brick count when burning invisible and ubreakable bricks
0712         ++remainingBricks;
0713     }
0714     brick.type = "BurningBrick";
0715 }
0716 
0717 // wrapper function to get the brick from position
0718 // and then passing it to handleDeletion
0719 function deleteBrick(pos) {
0720     var brick = bgOverlay.childAt(pos[0], pos[1]);
0721     if (brick!=null && bricks.indexOf(brick)>=0) {
0722         handleDeletion(brick);
0723     }
0724 }
0725 
0726 function createGiftAt(brick) {
0727     var gift = giftComponent.createObject(bgOverlay);
0728     gift.type = brick.giftType;
0729     gift.setPosition(brick.x/m_scale, brick.y/m_scale);
0730     gift.falling = true;
0731     gifts.push(gift);
0732 }
0733 
0734 function handleDeletion(brick) {
0735     remove(bricks, brick);
0736     if (brick.hasGift) {
0737         createGiftAt(brick);
0738     }
0739 
0740     // take the properties we want later before destroying the brick
0741     var brickType = brick.type;
0742     var brickVisible = brick.visible;
0743     brick.destroy();
0744 
0745     --remainingBricks;
0746     addBrickScore();
0747 
0748     // these two kind of bricks aren't counted in remainingBricks
0749     if ( (brickType == "HiddenBrick" && !brickVisible)
0750             || brickType == "UnbreakableBrick") {
0751         ++remainingBricks;
0752         return; // never need to load the next level
0753     }
0754 
0755     if (remainingBricks == 0) {
0756         loadNextLevel();
0757     }
0758 }
0759 
0760 function nearbyBricks(brickRect) {
0761     var result = new Array;
0762 
0763     // coordinates of the center of the brick
0764     var x = (brickRect.left + brickRect.right)/2;
0765     var y = (brickRect.top + brickRect.bottom)/2;
0766     var width = (brickRect.right - brickRect.left);
0767     var height = (brickRect.bottom - brickRect.top);
0768 
0769     // points to the left, right, top and bottom of the brick
0770     var points = new Array;
0771     points.push([x-width, y]);
0772     points.push([x+width, y]);
0773     points.push([x, y-height]);
0774     points.push([x, y+height]);
0775 
0776     for (var j in points) {
0777         var px = points[j][0];
0778         var py = points[j][1];
0779         var b = bgOverlay.childAt(px, py);
0780         if (b != null && bricks.indexOf(b)>=0) result.push(b);
0781     }
0782     return result;
0783 }
0784 
0785 function executeGift(type) {
0786     score += Globals.GIFT_SCORE;
0787     
0788     if (type == "Gift100Points") {
0789         score += 100 - Globals.GIFT_SCORE;
0790     } 
0791     else if (type == "Gift200Points") {
0792         score += 200 - Globals.GIFT_SCORE;
0793     } 
0794     else if (type == "Gift500Points") {
0795         score += 500 - Globals.GIFT_SCORE;
0796     } 
0797     else if (type == "Gift1000Points") {
0798         score += 1000 - Globals.GIFT_SCORE;
0799     } 
0800     else if (type == "GiftAddLife") {
0801         addLife();
0802     } 
0803     else if (type == "GiftLoseLife") {
0804         handleDeath();
0805     } 
0806     else if (type == "GiftNextLevel") {
0807         loadNextLevel();
0808     }
0809     else if (type == "GiftMagicEye") {
0810         giftMagicEye();
0811     }
0812     else if (type == "GiftMagicWand") {
0813         giftMagicWand();
0814     }
0815     else if (type == "GiftSplitBall") {
0816         giftSplitBall();
0817     }
0818     else if (type == "GiftAddBall") {
0819         createBall();
0820     } 
0821     else if (type == "GiftUnstoppableBall") {
0822         giftUnstoppableBall();
0823     }
0824     else if (type == "GiftBurningBall") {
0825         giftBurningBall();
0826     }
0827     else if (type == "GiftDecreaseSpeed") {
0828         changeSpeed(1.0/Globals.CHANGE_SPEED_RATIO);
0829     }
0830     else if (type == "GiftIncreaseSpeed") {
0831         changeSpeed(Globals.CHANGE_SPEED_RATIO);
0832     }
0833     else if (type == "GiftEnlargeBar") {
0834         bar.enlarge();
0835     }
0836     else if (type == "GiftShrinkBar") {
0837         bar.shrink();
0838     }
0839     else if (type == "GiftStickyBar") {
0840         bar.type = "StickyBar";
0841     }
0842     else if (type == "GiftMoreExplosion") {
0843         giftMoreExplosion();
0844     }
0845     else {
0846         print("Unrecognized gift type!!!", type);
0847     }
0848 }
0849 
0850 function giftMagicEye() {
0851     // make all hidden bricks visible
0852     for (var i in bricks) {
0853         var brick = bricks[i];
0854         if (!brick.visible) {
0855             brick.visible = true;
0856             ++remainingBricks;
0857         }
0858     }
0859 }
0860 
0861 function giftMagicWand() {
0862     for (var i in bricks) {
0863         var brick = bricks[i];
0864 
0865         // make unbreakable bricks breakable
0866         if (brick.type == "UnbreakableBrick") {
0867             brick.type = "BreakableBrick";
0868             ++remainingBricks;
0869         }
0870 
0871         // make multiple bricks single
0872         if (brick.type == "MultipleBrick3") {
0873             brick.type = "MultipleBrick1";
0874             score += Globals.AUTOBRICK_SCORE * 2;
0875         } else if (brick.type == "MultipleBrick2") {
0876             brick.type = "MultipleBrick1";
0877             score += Globals.AUTOBRICK_SCORE;
0878         }
0879     }
0880 }
0881 
0882 function giftSplitBall() {
0883     var newBalls = new Array;
0884     for (var i in balls) {
0885         var ball = balls[i];
0886         var newBall = ballComponent.createObject(bgOverlay);
0887 
0888         // give it a nice direction
0889         newBall.directionX = ball.directionX;
0890         newBall.directionY = ball.directionY;
0891         if (ball.directionY > 0)
0892             newBall.directionY *= -1;
0893         else
0894             newBall.directionX *= -1;
0895 
0896         newBall.toBeFired = ball.toBeFired;
0897         newBall.spriteKey = ball.spriteKey;
0898         newBall.type = ball.type;
0899         newBall.posX = ball.posX;
0900         newBall.posY = ball.posY;
0901         newBalls.push(newBall);
0902     }
0903     balls = balls.concat(newBalls);
0904 }
0905 
0906 function giftUnstoppableBall() {
0907     for (var i in balls) {
0908         var ball = balls[i];
0909         if (ball.type == "BurningBall") {
0910             ball.type = "UnstoppableBurningBall";
0911         } else if (ball.type != "UnstoppableBurningBall") {
0912             ball.type = "UnstoppableBall";
0913         }
0914     }
0915 }
0916 
0917 function giftBurningBall() {
0918     for (var i in balls) {
0919         var ball = balls[i];
0920         if (ball.type == "UnstoppableBall") {
0921             ball.type = "UnstoppableBurningBall";
0922         } else if (ball.type != "UnstoppableBurningBall") {
0923             ball.type = "BurningBall";
0924         }
0925     }
0926 }
0927 
0928 function giftMoreExplosion() {
0929     var explodingBricks = new Array;
0930     for (var i in bricks) {
0931         var brick = bricks[i];
0932         if (brick.type == "ExplodingBrick") {
0933             explodingBricks.push(brick);
0934         }
0935     }
0936 
0937     for (var i in explodingBricks) {
0938         var nearby = nearbyBricks(createRect(explodingBricks[i]));
0939         for (var j in nearby) {
0940             var nearbyBrick = nearby[j];
0941             if (nearbyBrick.type == "UnbreakableBrick") {
0942                 ++remainingBricks;
0943             }
0944             if (nearbyBrick.type == "HiddenBrick" && !nearbyBrick.visible) {
0945                 nearbyBrick.visible = true;
0946                 ++remainingBricks;
0947             }
0948 
0949             nearbyBrick.type = "ExplodingBrick";
0950         }
0951     }
0952 }