aboutsummaryrefslogtreecommitdiffstats
path: root/examples/demos/samegame/content/samegame.js
diff options
context:
space:
mode:
authorAlan Alpert <alan.alpert@nokia.com>2012-07-05 11:45:28 +1000
committerQt by Nokia <qt-info@nokia.com>2012-07-12 06:33:12 +0200
commit942b5fa9cc3924d5f022091c015ab0717bc79512 (patch)
treee66bb88d43be433a7a93649f9b43f7427a10d41c /examples/demos/samegame/content/samegame.js
parent3aaaf52af2f980fa0026adc0a891df4ac86d2010 (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-xexamples/demos/samegame/content/samegame.js474
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();
+}