diff options
author | Alan Alpert <alan.alpert@nokia.com> | 2012-07-05 11:45:28 +1000 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-07-12 06:33:12 +0200 |
commit | 942b5fa9cc3924d5f022091c015ab0717bc79512 (patch) | |
tree | e66bb88d43be433a7a93649f9b43f7427a10d41c /examples/demos/samegame/content/samegame.js | |
parent | 3aaaf52af2f980fa0026adc0a891df4ac86d2010 (diff) |
Add new samegame and calculator demos
Change-Id: I06ac38a1d0f844eba367fc5e163151d1f70a0012
Reviewed-by: Alan Alpert <alan.alpert@nokia.com>
Diffstat (limited to 'examples/demos/samegame/content/samegame.js')
-rwxr-xr-x | examples/demos/samegame/content/samegame.js | 474 |
1 files changed, 362 insertions, 112 deletions
diff --git a/examples/demos/samegame/content/samegame.js b/examples/demos/samegame/content/samegame.js index d859bf11bc..456007c2d0 100755 --- a/examples/demos/samegame/content/samegame.js +++ b/examples/demos/samegame/content/samegame.js @@ -3,57 +3,67 @@ ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/ ** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** +** This file is part of the examples of the Qt Toolkit. ** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: ** +** "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 Nokia Corporation and its Subsidiary(-ies) 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "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 THE COPYRIGHT +** OWNER OR CONTRIBUTORS 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." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ + /* This script file handles the game logic */ .pragma library .import QtQuick.LocalStorage 2.0 as Sql var maxColumn = 10; -var maxRow = 15; +var maxRow = 13; +var types = 3; var maxIndex = maxColumn*maxRow; var board = new Array(maxIndex); -var blockSrc = "BoomBlock.qml"; -var scoresURL = ""; +var blockSrc = "Block.qml"; var gameDuration; var component = Qt.createComponent(blockSrc); -var highScoreBar = -1; var gameCanvas; -var nameInputDialog = null; -var dialog = null; +var betweenTurns = false; + +var puzzleLevel = null; +var puzzlePath = ""; + +var gameMode = "arcade"; //Set in new game, then tweaks behaviour of other functions +var gameOver = false; + +function changeBlock(src) +{ + blockSrc = src; + component = Qt.createComponent(blockSrc); +} // Index function used instead of a 2D array function index(column, row) @@ -69,35 +79,58 @@ function timeStr(msecs) return ret; } -function startNewGame(gc) +function cleanUp() { - gameCanvas = gc; + if (gameCanvas == undefined) + return; // Delete blocks from previous game for (var i = 0; i < maxIndex; i++) { if (board[i] != null) board[i].destroy(); + board[i] = null; } + if (puzzleLevel != null){ + puzzleLevel.destroy(); + puzzleLevel = null; + } + gameCanvas.mode = "" +} +function startNewGame(gc, mode, map) +{ + gameCanvas = gc; + if (mode == undefined) + gameMode = "arcade"; + else + gameMode = mode; + gameOver = false; + + cleanUp(); + + gc.gameOver = false; + gc.mode = gameMode; // Calculate board size maxColumn = Math.floor(gameCanvas.width/gameCanvas.blockSize); maxRow = Math.floor(gameCanvas.height/gameCanvas.blockSize); maxIndex = maxRow * maxColumn; + if (gameMode == "arcade") //Needs to be after board sizing + getHighScore(); - // Close dialogs - if(nameInputDialog != null) - nameInputDialog.forceClose(); - if(dialog != null) - dialog.forceClose(); // Initialize Board board = new Array(maxIndex); gameCanvas.score = 0; - for (var column = 0; column < maxColumn; column++) { - for (var row = 0; row < maxRow; row++) { - board[index(column, row)] = null; - createBlock(column, row); - } - } + gameCanvas.score2 = 0; + gameCanvas.moves = 0; + gameCanvas.curTurn = 1; + if (gameMode == "puzzle") + loadMap(map); + else//Note that we load them in reverse order for correct visual stacking + for (var column = maxColumn - 1; column >= 0; column--) + for (var row = maxRow - 1; row >= 0; row--) + createBlock(column, row); + if (gameMode == "puzzle") + getLevelHistory();//Needs to be after map load gameDuration = new Date(); } @@ -107,10 +140,8 @@ var floodBoard; // Set to 1 if the floodFill reaches off that node // NOTE: Be careful with vars named x,y, as the calling object's x,y are still in scope function handleClick(x,y) { - if(gameCanvas == undefined){ - console.log("But the game hasn't started yet!"); + if (betweenTurns || gameOver || gameCanvas == undefined) return; - } var column = Math.floor(x/gameCanvas.blockSize); var row = Math.floor(y/gameCanvas.blockSize); if (column >= maxColumn || column < 0 || row >= maxRow || row < 0) @@ -121,9 +152,23 @@ function handleClick(x,y) floodFill(column,row, -1); if (fillFound <= 0) return; - gameCanvas.score += (fillFound - 1) * (fillFound - 1); - shuffleDown(); - victoryCheck(); + if (gameMode == "multiplayer" && gameCanvas.curTurn == 2) + gameCanvas.score2 += (fillFound - 1) * (fillFound - 1); + else + gameCanvas.score += (fillFound - 1) * (fillFound - 1); + if (gameMode == "multiplayer" && gameCanvas.curTurn == 2) + shuffleUp(); + else + shuffleDown(); + gameCanvas.moves += 1; + if (gameMode == "endless") + refill(); + else if (gameMode != "multiplayer") + victoryCheck(); + if (gameMode == "multiplayer" && !gc.gameOver){ + betweenTurns = true; + gameCanvas.swapPlayers();//signal, animate and call turnChange() when ready + } } function floodFill(column,row,type) @@ -193,39 +238,108 @@ function shuffleDown() } } + +function shuffleUp() +{ + // Fall up + for (var column = 0; column < maxColumn; column++) { + var fallDist = 0; + for (var row = 0; row < maxRow; row++) { + if (board[index(column,row)] == null) { + fallDist += 1; + } else { + if (fallDist > 0) { + var obj = board[index(column, row)]; + obj.y = (row - fallDist) * gameCanvas.blockSize; + board[index(column, row - fallDist)] = obj; + board[index(column, row)] = null; + } + } + } + } + // Fall to the left (or should it be right, so as to be left for P2?) + fallDist = 0; + for (column = 0; column < maxColumn; column++) { + if (board[index(column, 0)] == null) { + fallDist += 1; + } else { + if (fallDist > 0) { + for (row = 0; row < maxRow; row++) { + obj = board[index(column, row)]; + if (obj == null) + continue; + obj.x = (column - fallDist) * gameCanvas.blockSize; + board[index(column - fallDist,row)] = obj; + board[index(column, row)] = null; + } + } + } + } +} + +function turnChange()//called by ui outside +{ + betweenTurns = false; + if (gameCanvas.curTurn == 1){ + shuffleUp(); + gameCanvas.curTurn = 2; + victoryCheck(); + }else{ + shuffleDown(); + gameCanvas.curTurn = 1; + victoryCheck(); + } +} + +function refill() +{ + for (var column = 0; column < maxColumn; column++) { + for (var row = 0; row < maxRow; row++) { + if (board[index(column, row)] == null) + createBlock(column, row); + } + } +} + function victoryCheck() { // Awards bonuses for no blocks left var deservesBonus = true; - for (var column = maxColumn - 1; column >= 0; column--) - if (board[index(column, maxRow - 1)] != null) - deservesBonus = false; - if (deservesBonus) - gameCanvas.score += 500; + if (board[index(0,maxRow - 1)] != null || board[index(0,0)] != null) + deservesBonus = false; // Checks for game over - if (deservesBonus || !(floodMoveCheck(0, maxRow - 1, -1))) { - gameDuration = new Date() - gameDuration; - if(nameInputDialog == null){ - nameInputDialog = Qt.createQmlObject('import "."; import "samegame.js" as Logic; NameInputDialog{onAccepted: Logic.saveHighScore(name)}', gameCanvas, "highscoredialog.qml"); - } - if(dialog == null){ - dialog = Qt.createComponent("Dialog.qml").createObject(gameCanvas); - } - initHighScoreBar(); - if(gameCanvas.score > highScoreBar){ - nameInputDialog.show("You won! Your name: "); - nameInputDialog.initialWidth = nameInputDialog.text.width + 20; - if (nameInputDialog.name == "") - nameInputDialog.width = nameInputDialog.initialWidth; - nameInputDialog.text.opacity = 0; // Just a spacer - }else{ - dialog.show("You won!"); + if (deservesBonus){ + if (gameCanvas.curTurn = 1) + gameCanvas.score += 1000; + else + gameCanvas.score2 += 1000; + } + gameOver = deservesBonus; + if (gameCanvas.curTurn == 1){ + if (!(floodMoveCheck(0, maxRow - 1, -1))) + gameOver = true; + }else{ + if (!(floodMoveCheck(0, 0, -1, true))) + gameOver = true; + } + if (gameMode == "puzzle"){ + puzzleVictoryCheck(deservesBonus);//Takes it from here + return; + } + if (gameOver) { + var winnerScore = Math.max(gameCanvas.score, gameCanvas.score2); + if (gameMode == "multiplayer"){ + gameCanvas.score = winnerScore; + saveHighScore(gameCanvas.score2); } + saveHighScore(gameCanvas.score); + gameDuration = new Date() - gameDuration; + gameCanvas.gameOver = true; } } // Only floods up and right, to see if it can find adjacent same-typed blocks -function floodMoveCheck(column, row, type) +function floodMoveCheck(column, row, type, goDownInstead) { if (column >= maxColumn || column < 0 || row >= maxRow || row < 0) return false; @@ -234,24 +348,35 @@ function floodMoveCheck(column, row, type) var myType = board[index(column, row)].type; if (type == myType) return true; - return floodMoveCheck(column + 1, row, myType) || - floodMoveCheck(column, row - 1, board[index(column, row)].type); + if (goDownInstead) + return floodMoveCheck(column + 1, row, myType, goDownInstead) || + floodMoveCheck(column, row + 1, myType, goDownInstead); + else + return floodMoveCheck(column + 1, row, myType) || + floodMoveCheck(column, row - 1, myType); } -function createBlock(column,row) +function createBlock(column,row,type) { // Note that we don't wait for the component to become ready. This will // only work if the block QML is a local file. Otherwise the component will // not be ready immediately. There is a statusChanged signal on the // component you could use if you want to wait to load remote files. - if(component.status == 1){ + if (component.status == 1){ + if (type == undefined) + type = Math.floor(Math.random() * types); + if (type < 0 || type > 4) { + console.log("Invalid type requested");//TODO: Is this triggered by custom levels much? + return; + } var dynamicObject = component.createObject(gameCanvas, - {"type": Math.floor(Math.random() * 3), + {"type": type, "x": column*gameCanvas.blockSize, + "y": -1*gameCanvas.blockSize, "width": gameCanvas.blockSize, "height": gameCanvas.blockSize, "particleSystem": gameCanvas.ps}); - if(dynamicObject == null){ + if (dynamicObject == null){ console.log("error creating block"); console.log(component.errorString()); return false; @@ -268,63 +393,188 @@ function createBlock(column,row) return true; } -function initHighScoreBar() +function showPuzzleError(str) +{ + //TODO: Nice user visible UI? + console.log(str); +} + +function loadMap(map) +{ + puzzlePath = map; + var levelComp = Qt.createComponent(puzzlePath); + if (levelComp.status != 1){ + console.log("Error loading level"); + showPuzzleError(levelComp.errorString()); + return; + } + puzzleLevel = levelComp.createObject(); + if (puzzleLevel == null || !puzzleLevel.startingGrid instanceof Array) { + showPuzzleError("Bugger!"); + return; + } + gameCanvas.showPuzzleGoal(puzzleLevel.goalText); + //showPuzzleGoal should call finishLoadingMap as the next thing it does, before handling more events +} + +function finishLoadingMap() +{ + for (var i in puzzleLevel.startingGrid) + if (! (puzzleLevel.startingGrid[i] >= 0 && puzzleLevel.startingGrid[i] <= 9) ) + puzzleLevel.startingGrid[i] = 0; + //TODO: Don't allow loading larger levels, leads to cheating + while (puzzleLevel.startingGrid.length > maxIndex) puzzleLevel.startingGrid.shift(); + while (puzzleLevel.startingGrid.length < maxIndex) puzzleLevel.startingGrid.unshift(0); + for (var i in puzzleLevel.startingGrid) + if (puzzleLevel.startingGrid[i] > 0) + createBlock(i % maxColumn, Math.floor(i / maxColumn), puzzleLevel.startingGrid[i] - 1); + + //### Experimental feature - allow levels to contain arbitrary QML scenes as well! + //while (puzzleLevel.children.length) + // puzzleLevel.children[0].parent = gameCanvas; + gameDuration = new Date(); //Don't start until we finish loading +} + +function puzzleVictoryCheck(clearedAll)//gameOver has also been set if no more moves +{ + var won = true; + var soFar = new Date() - gameDuration; + if (puzzleLevel.scoreTarget != -1 && gameCanvas.score < puzzleLevel.scoreTarget){ + won = false; + } if (puzzleLevel.scoreTarget != -1 && gameCanvas.score >= puzzleLevel.scoreTarget && !puzzleLevel.mustClear){ + gameOver = true; + } if (puzzleLevel.timeTarget != -1 && soFar/1000.0 > puzzleLevel.timeTarget){ + gameOver = true; + } if (puzzleLevel.moveTarget != -1 && gameCanvas.moves >= puzzleLevel.moveTarget){ + gameOver = true; + } if (puzzleLevel.mustClear && gameOver && !clearedAll) { + won = false; + } + + if (gameOver) { + gameCanvas.gameOver = true; + gameCanvas.showPuzzleEnd(won); + + if (won) { + // Store progress + saveLevelHistory(); + } + } +} + +function getHighScore() { var db = Sql.openDatabaseSync( - "SameGameScores", - "1.0", - "Local SameGame High Scores", + "SameGame", + "2.0", + "SameGame Local Data", 100 ); db.transaction( function(tx) { - tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(name TEXT, score NUMBER, gridSize TEXT, time NUMBER)'); + tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(game TEXT, score NUMBER, gridSize TEXT, time NUMBER)'); // Only show results for the current grid size var rs = tx.executeSql('SELECT * FROM Scores WHERE gridSize = "' - + maxColumn + "x" + maxRow + '" ORDER BY score desc LIMIT 10'); - if(rs.rows.length < 10) - highScoreBar = 0; - else - highScoreBar = rs.rows.item(rs.rows.length - 1).score; + + maxColumn + "x" + maxRow + '" AND game = "' + gameMode + '" ORDER BY score desc'); + if (rs.rows.length > 0) + gameCanvas.highScore = rs.rows.item(0).score; + else + gameCanvas.highScore = 0; } ); } -function saveHighScore(name) +function saveHighScore(score) { - if (scoresURL != "") - sendHighScore(name); // Offline storage var db = Sql.openDatabaseSync( - "SameGameScores", - "1.0", - "Local SameGame High Scores", + "SameGame", + "2.0", + "SameGame Local Data", 100 ); var dataStr = "INSERT INTO Scores VALUES(?, ?, ?, ?)"; var data = [ - name, - gameCanvas.score, + gameMode, + score, maxColumn + "x" + maxRow, Math.floor(gameDuration / 1000) ]; + if (score >= gameCanvas.highScore)//Update UI field + gameCanvas.highScore = score; + db.transaction( function(tx) { - tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(name TEXT, score NUMBER, gridSize TEXT, time NUMBER)'); + tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(game TEXT, score NUMBER, gridSize TEXT, time NUMBER)'); tx.executeSql(dataStr, data); + } + ); +} - // Only show results for the current grid size - var rs = tx.executeSql('SELECT * FROM Scores WHERE gridSize = "' - + maxColumn + "x" + maxRow + '" ORDER BY score desc LIMIT 10'); - var r = "\nHIGH SCORES for this grid size\n\n" - for (var i = 0; i < rs.rows.length; i++) { - r += (i+1) + ". " + rs.rows.item(i).name + ' got ' - + rs.rows.item(i).score + ' points in ' - + rs.rows.item(i).time + ' seconds.\n'; +function getLevelHistory() +{ + var db = Sql.openDatabaseSync( + "SameGame", + "2.0", + "SameGame Local Data", + 100 + ); + db.transaction( + function(tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS Puzzle(level TEXT, score NUMBER, moves NUMBER, time NUMBER)'); + var rs = tx.executeSql('SELECT * FROM Puzzle WHERE level = "' + puzzlePath + '" ORDER BY score desc'); + if (rs.rows.length > 0) { + gameCanvas.puzzleWon = true; + gameCanvas.highScore = rs.rows.item(0).score; + } else { + gameCanvas.puzzleWon = false; + gameCanvas.highScore = 0; } - if(rs.rows.length == 10) - highScoreBar = rs.rows.item(9).score; - dialog.show(r); } ); } + +function saveLevelHistory() +{ + var db = Sql.openDatabaseSync( + "SameGame", + "2.0", + "SameGame Local Data", + 100 + ); + var dataStr = "INSERT INTO Puzzle VALUES(?, ?, ?, ?)"; + var data = [ + puzzlePath, + gameCanvas.score, + gameCanvas.moves, + Math.floor(gameDuration / 1000) + ]; + gameCanvas.puzzleWon = true; + + db.transaction( + function(tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS Puzzle(level TEXT, score NUMBER, moves NUMBER, time NUMBER)'); + tx.executeSql(dataStr, data); + } + ); +} + +function nuke() //For "Debug mode" +{ + for (var row = 1; row <= 5; row++) { + for (var col = 0; col < 5; col++) { + if (board[index(col, maxRow - row)] != null) { + board[index(col, maxRow - row)].dying = true; + board[index(col, maxRow - row)] = null; + } + } + } + if (gameMode == "multiplayer" && gameCanvas.curTurn == 2) + shuffleUp(); + else + shuffleDown(); + if (gameMode == "endless") + refill(); + else + victoryCheck(); +} |