Examples cleanup
[profile/ivi/qtdeclarative.git] / examples / demos / samegame / content / samegame.js
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 /* This script file handles the game logic */
42 .pragma library
43 .import QtQuick.LocalStorage 2.0 as Sql
44
45 var maxColumn = 10;
46 var maxRow = 15;
47 var maxIndex = maxColumn*maxRow;
48 var board = new Array(maxIndex);
49 var blockSrc = "BoomBlock.qml";
50 var scoresURL = "";
51 var gameDuration;
52 var component = Qt.createComponent(blockSrc);
53 var highScoreBar = -1;
54 var gameCanvas;
55 var nameInputDialog = null;
56 var dialog = null;
57
58 // Index function used instead of a 2D array
59 function index(column, row)
60 {
61     return column + row * maxColumn;
62 }
63
64 function timeStr(msecs)
65 {
66     var secs = Math.floor(msecs/1000);
67     var m = Math.floor(secs/60);
68     var ret = "" + m + "m " + (secs%60) + "s";
69     return ret;
70 }
71
72 function startNewGame(gc)
73 {
74     gameCanvas = gc;
75     // Delete blocks from previous game
76     for (var i = 0; i < maxIndex; i++) {
77         if (board[i] != null)
78             board[i].destroy();
79     }
80
81     // Calculate board size
82     maxColumn = Math.floor(gameCanvas.width/gameCanvas.blockSize);
83     maxRow = Math.floor(gameCanvas.height/gameCanvas.blockSize);
84     maxIndex = maxRow * maxColumn;
85
86     // Close dialogs
87     if(nameInputDialog != null)
88         nameInputDialog.forceClose();
89     if(dialog != null)
90         dialog.forceClose();
91
92     // Initialize Board
93     board = new Array(maxIndex);
94     gameCanvas.score = 0;
95     for (var column = 0; column < maxColumn; column++) {
96         for (var row = 0; row < maxRow; row++) {
97             board[index(column, row)] = null;
98             createBlock(column, row);
99         }
100     }
101     gameDuration = new Date();
102 }
103
104 var fillFound;  // Set after a floodFill call to the number of blocks found
105 var floodBoard; // Set to 1 if the floodFill reaches off that node
106
107 // NOTE: Be careful with vars named x,y, as the calling object's x,y are still in scope
108 function handleClick(x,y)
109 {
110     if(gameCanvas == undefined){
111         console.log("But the game hasn't started yet!");
112         return;
113     }
114     var column = Math.floor(x/gameCanvas.blockSize);
115     var row = Math.floor(y/gameCanvas.blockSize);
116     if (column >= maxColumn || column < 0 || row >= maxRow || row < 0)
117         return;
118     if (board[index(column, row)] == null)
119         return;
120     // If it's a valid block, remove it and all connected (does nothing if it's not connected)
121     floodFill(column,row, -1);
122     if (fillFound <= 0)
123         return;
124     gameCanvas.score += (fillFound - 1) * (fillFound - 1);
125     shuffleDown();
126     victoryCheck();
127 }
128
129 function floodFill(column,row,type)
130 {
131     if (board[index(column, row)] == null)
132         return;
133     var first = false;
134     if (type == -1) {
135         first = true;
136         type = board[index(column,row)].type;
137
138         // Flood fill initialization
139         fillFound = 0;
140         floodBoard = new Array(maxIndex);
141     }
142     if (column >= maxColumn || column < 0 || row >= maxRow || row < 0)
143         return;
144     if (floodBoard[index(column, row)] == 1 || (!first && type != board[index(column, row)].type))
145         return;
146     floodBoard[index(column, row)] = 1;
147     floodFill(column + 1, row, type);
148     floodFill(column - 1, row, type);
149     floodFill(column, row + 1, type);
150     floodFill(column, row - 1, type);
151     if (first == true && fillFound == 0)
152         return; // Can't remove single blocks
153     board[index(column, row)].dying = true;
154     board[index(column, row)] = null;
155     fillFound += 1;
156 }
157
158 function shuffleDown()
159 {
160     // Fall down
161     for (var column = 0; column < maxColumn; column++) {
162         var fallDist = 0;
163         for (var row = maxRow - 1; row >= 0; row--) {
164             if (board[index(column,row)] == null) {
165                 fallDist += 1;
166             } else {
167                 if (fallDist > 0) {
168                     var obj = board[index(column, row)];
169                     obj.y = (row + fallDist) * gameCanvas.blockSize;
170                     board[index(column, row + fallDist)] = obj;
171                     board[index(column, row)] = null;
172                 }
173             }
174         }
175     }
176     // Fall to the left
177     fallDist = 0;
178     for (column = 0; column < maxColumn; column++) {
179         if (board[index(column, maxRow - 1)] == null) {
180             fallDist += 1;
181         } else {
182             if (fallDist > 0) {
183                 for (row = 0; row < maxRow; row++) {
184                     obj = board[index(column, row)];
185                     if (obj == null)
186                         continue;
187                     obj.x = (column - fallDist) * gameCanvas.blockSize;
188                     board[index(column - fallDist,row)] = obj;
189                     board[index(column, row)] = null;
190                 }
191             }
192         }
193     }
194 }
195
196 function victoryCheck()
197 {
198     // Awards bonuses for no blocks left
199     var deservesBonus = true;
200     for (var column = maxColumn - 1; column >= 0; column--)
201         if (board[index(column, maxRow - 1)] != null)
202             deservesBonus = false;
203     if (deservesBonus)
204         gameCanvas.score += 500;
205     // Checks for game over
206     if (deservesBonus || !(floodMoveCheck(0, maxRow - 1, -1))) {
207         gameDuration = new Date() - gameDuration;
208         if(nameInputDialog == null){
209             nameInputDialog = Qt.createQmlObject('import "."; import "samegame.js" as Logic; NameInputDialog{onAccepted: Logic.saveHighScore(name)}', gameCanvas, "highscoredialog.qml");
210         }
211         if(dialog == null){
212             dialog = Qt.createComponent("Dialog.qml").createObject(gameCanvas);
213         }
214         initHighScoreBar();
215         if(gameCanvas.score > highScoreBar){
216             nameInputDialog.show("You won! Your name:                 ");
217             nameInputDialog.initialWidth = nameInputDialog.text.width + 20;
218             if (nameInputDialog.name == "")
219                nameInputDialog.width = nameInputDialog.initialWidth;
220             nameInputDialog.text.opacity = 0; // Just a spacer
221         }else{
222             dialog.show("You won!");
223         }
224     }
225 }
226
227 // Only floods up and right, to see if it can find adjacent same-typed blocks
228 function floodMoveCheck(column, row, type)
229 {
230     if (column >= maxColumn || column < 0 || row >= maxRow || row < 0)
231         return false;
232     if (board[index(column, row)] == null)
233         return false;
234     var myType = board[index(column, row)].type;
235     if (type == myType)
236         return true;
237     return floodMoveCheck(column + 1, row, myType) ||
238            floodMoveCheck(column, row - 1, board[index(column, row)].type);
239 }
240
241 function createBlock(column,row)
242 {
243     // Note that we don't wait for the component to become ready. This will
244     // only work if the block QML is a local file. Otherwise the component will
245     // not be ready immediately. There is a statusChanged signal on the
246     // component you could use if you want to wait to load remote files.
247     if(component.status == 1){
248         var dynamicObject = component.createObject(gameCanvas,
249                 {"type": Math.floor(Math.random() * 3),
250                 "x": column*gameCanvas.blockSize,
251                 "width": gameCanvas.blockSize,
252                 "height": gameCanvas.blockSize,
253                 "particleSystem": gameCanvas.ps});
254         if(dynamicObject == null){
255             console.log("error creating block");
256             console.log(component.errorString());
257             return false;
258         }
259         dynamicObject.y = row*gameCanvas.blockSize;
260         dynamicObject.spawned = true;
261
262         board[index(column,row)] = dynamicObject;
263     }else{
264         console.log("error loading block component");
265         console.log(component.errorString());
266         return false;
267     }
268     return true;
269 }
270
271 function initHighScoreBar()
272 {
273     var db = Sql.openDatabaseSync(
274         "SameGameScores",
275         "1.0",
276         "Local SameGame High Scores",
277         100
278     );
279     db.transaction(
280         function(tx) {
281             tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(name TEXT, score NUMBER, gridSize TEXT, time NUMBER)');
282             // Only show results for the current grid size
283             var rs = tx.executeSql('SELECT * FROM Scores WHERE gridSize = "'
284                 + maxColumn + "x" + maxRow + '" ORDER BY score desc LIMIT 10');
285             if(rs.rows.length < 10)
286                 highScoreBar = 0;
287             else 
288                 highScoreBar = rs.rows.item(rs.rows.length - 1).score;
289         }
290     );
291 }
292
293 function saveHighScore(name)
294 {
295     if (scoresURL != "")
296         sendHighScore(name);
297     // Offline storage
298     var db = Sql.openDatabaseSync(
299         "SameGameScores",
300         "1.0",
301         "Local SameGame High Scores",
302         100
303     );
304     var dataStr = "INSERT INTO Scores VALUES(?, ?, ?, ?)";
305     var data = [
306         name,
307         gameCanvas.score,
308         maxColumn + "x" + maxRow,
309         Math.floor(gameDuration / 1000)
310     ];
311     db.transaction(
312         function(tx) {
313             tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(name TEXT, score NUMBER, gridSize TEXT, time NUMBER)');
314             tx.executeSql(dataStr, data);
315
316             // Only show results for the current grid size
317             var rs = tx.executeSql('SELECT * FROM Scores WHERE gridSize = "'
318                 + maxColumn + "x" + maxRow + '" ORDER BY score desc LIMIT 10');
319             var r = "\nHIGH SCORES for this grid size\n\n"
320             for (var i = 0; i < rs.rows.length; i++) {
321                 r += (i+1) + ". " + rs.rows.item(i).name + ' got '
322                     + rs.rows.item(i).score + ' points in '
323                     + rs.rows.item(i).time + ' seconds.\n';
324             }
325             if(rs.rows.length == 10)
326                 highScoreBar = rs.rows.item(9).score;
327             dialog.show(r);
328         }
329     );
330 }