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