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