samegame.js 8.7 KB

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