GameModel.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. import CellModel from "./CellModel";
  2. // import { mergePointArray, exclusivePoint } from "../Utils/ModelUtils"
  3. import { mergePointArray, exclusivePoint } from "../Utils/ModelUtil"
  4. import { CELL_TYPE, CELL_BASENUM, CELL_STATUS, GRID_WIDTH, GRID_HEIGHT, ANITIME } from "./ConstValue";
  5. export default class GameModel {
  6. constructor() {
  7. this.cells = null;
  8. this.cellBgs = null;
  9. this.lastPos = cc.v2(-1, -1);
  10. this.cellTypeNum = 5;
  11. this.cellCreateType = []; // 升成种类只在这个数组里面查找
  12. }
  13. init(cellTypeNum) {
  14. this.cells = [];
  15. this.setCellTypeNum(cellTypeNum || this.cellTypeNum);
  16. for (var i = 1; i <= GRID_WIDTH; i++) {
  17. this.cells[i] = [];
  18. for (var j = 1; j <= GRID_HEIGHT; j++) {
  19. this.cells[i][j] = new CellModel();
  20. }
  21. }
  22. // this.mock();
  23. for (var i = 1; i <= GRID_WIDTH; i++) {
  24. for (var j = 1; j <= GRID_HEIGHT; j++) {
  25. //已经被mock数据生成了
  26. if (this.cells[i][j].type != null) {
  27. continue;
  28. }
  29. let flag = true;
  30. while (flag) {
  31. flag = false;
  32. this.cells[i][j].init(this.getRandomCellType());
  33. let result = this.checkPoint(j, i)[0];
  34. if (result.length > 2) {
  35. flag = true;
  36. }
  37. this.cells[i][j].setXY(j, i);
  38. this.cells[i][j].setStartXY(j, i);
  39. }
  40. }
  41. }
  42. }
  43. mock() {
  44. this.mockInit(5, 1, CELL_TYPE.A);
  45. this.mockInit(5, 3, CELL_TYPE.A);
  46. this.mockInit(4, 2, CELL_TYPE.A);
  47. this.mockInit(3, 2, CELL_TYPE.A);
  48. this.mockInit(5, 2, CELL_TYPE.B);
  49. this.mockInit(6, 2, CELL_TYPE.B);
  50. this.mockInit(7, 3, CELL_TYPE.B);
  51. this.mockInit(8, 2, CELL_TYPE.A);
  52. }
  53. mockInit(x, y, type) {
  54. this.cells[x][y].init(type)
  55. this.cells[x][y].setXY(y, x);
  56. this.cells[x][y].setStartXY(y, x);
  57. }
  58. initWithData(data) {
  59. // to do
  60. }
  61. /**
  62. *
  63. * @param x
  64. * @param y
  65. * @param recursive 是否递归查找
  66. * @returns {([]|string|*)[]}
  67. */
  68. checkPoint(x, y, recursive) {
  69. let rowResult = this.checkWithDirection(x, y, [cc.v2(1, 0), cc.v2(-1, 0)]);
  70. let colResult = this.checkWithDirection(x, y, [cc.v2(0, -1), cc.v2(0, 1)]);
  71. let samePoints = [];
  72. let newCellStatus = "";
  73. if (rowResult.length >= 5 || colResult.length >= 5) {
  74. newCellStatus = CELL_STATUS.BIRD;
  75. }
  76. else if (rowResult.length >= 3 && colResult.length >= 3) {
  77. newCellStatus = CELL_STATUS.WRAP;
  78. }
  79. else if (rowResult.length >= 4) {
  80. newCellStatus = CELL_STATUS.LINE;
  81. }
  82. else if (colResult.length >= 4) {
  83. newCellStatus = CELL_STATUS.COLUMN;
  84. }
  85. if (rowResult.length >= 3) {
  86. samePoints = rowResult;
  87. }
  88. if (colResult.length >= 3) {
  89. samePoints = mergePointArray(samePoints, colResult);
  90. }
  91. let result = [samePoints, newCellStatus, this.cells[y][x].type, cc.v2(x, y)];
  92. // 检查一下消除的其他节点, 能不能生成更大范围的消除
  93. if (recursive && result.length >= 3) {
  94. let subCheckPoints = exclusivePoint(samePoints, cc.v2(x, y));
  95. for (let point of subCheckPoints) {
  96. let subResult = this.checkPoint(point.x, point.y, false);
  97. if (subResult[1] > result[1] || (subResult[1] === result[1] && subResult[0].length > result[0].length)) {
  98. result = subResult;
  99. }
  100. }
  101. }
  102. return result;
  103. }
  104. checkWithDirection(x, y, direction) {
  105. let queue = [];
  106. let vis = [];
  107. vis[x + y * 9] = true;
  108. queue.push(cc.v2(x, y));
  109. let front = 0;
  110. while (front < queue.length) {
  111. //let direction = [cc.v2(0, -1), cc.v2(0, 1), cc.v2(1, 0), cc.v2(-1, 0)];
  112. let point = queue[front];
  113. let cellModel = this.cells[point.y][point.x];
  114. front++;
  115. if (!cellModel) {
  116. continue;
  117. }
  118. for (let i = 0; i < direction.length; i++) {
  119. let tmpX = point.x + direction[i].x;
  120. let tmpY = point.y + direction[i].y;
  121. if (tmpX < 1 || tmpX > 9
  122. || tmpY < 1 || tmpY > 9
  123. || vis[tmpX + tmpY * 9]
  124. || !this.cells[tmpY][tmpX]) {
  125. continue;
  126. }
  127. if (cellModel.type === this.cells[tmpY][tmpX].type) {
  128. vis[tmpX + tmpY * 9] = true;
  129. queue.push(cc.v2(tmpX, tmpY));
  130. }
  131. }
  132. }
  133. return queue;
  134. }
  135. printInfo() {
  136. for (var i = 1; i <= 9; i++) {
  137. var printStr = "";
  138. for (var j = 1; j <= 9; j++) {
  139. printStr += this.cells[i][j].type + " ";
  140. }
  141. console.log(printStr);
  142. }
  143. }
  144. getCells() {
  145. return this.cells;
  146. }
  147. // controller调用的主要入口
  148. // 点击某个格子
  149. selectCell(pos) {
  150. this.changeModels = [];// 发生改变的model,将作为返回值,给view播动作
  151. this.effectsQueue = []; // 动物消失,爆炸等特效
  152. var lastPos = this.lastPos;
  153. var delta = Math.abs(pos.x - lastPos.x) + Math.abs(pos.y - lastPos.y);
  154. if (delta != 1) { //非相邻格子, 直接返回
  155. this.lastPos = pos;
  156. return [[], []];
  157. }
  158. let curClickCell = this.cells[pos.y][pos.x]; //当前点击的格子
  159. let lastClickCell = this.cells[lastPos.y][lastPos.x]; // 上一次点击的格式
  160. this.exchangeCell(lastPos, pos);
  161. var result1 = this.checkPoint(pos.x, pos.y)[0];
  162. var result2 = this.checkPoint(lastPos.x, lastPos.y)[0];
  163. this.curTime = 0; // 动画播放的当前时间
  164. this.pushToChangeModels(curClickCell);
  165. this.pushToChangeModels(lastClickCell);
  166. let isCanBomb = (curClickCell.status != CELL_STATUS.COMMON && // 判断两个是否是特殊的动物
  167. lastClickCell.status != CELL_STATUS.COMMON) ||
  168. curClickCell.status == CELL_STATUS.BIRD ||
  169. lastClickCell.status == CELL_STATUS.BIRD;
  170. if (result1.length < 3 && result2.length < 3 && !isCanBomb) {//不会发生消除的情况
  171. this.exchangeCell(lastPos, pos);
  172. curClickCell.moveToAndBack(lastPos);
  173. lastClickCell.moveToAndBack(pos);
  174. this.lastPos = cc.v2(-1, -1);
  175. return [this.changeModels];
  176. }
  177. else {
  178. this.lastPos = cc.v2(-1, -1);
  179. curClickCell.moveTo(lastPos, this.curTime);
  180. lastClickCell.moveTo(pos, this.curTime);
  181. var checkPoint = [pos, lastPos];
  182. this.curTime += ANITIME.TOUCH_MOVE;
  183. this.processCrush(checkPoint);
  184. return [this.changeModels, this.effectsQueue];
  185. }
  186. }
  187. // 消除
  188. processCrush(checkPoint) {
  189. let cycleCount = 0;
  190. while (checkPoint.length > 0) {
  191. let bombModels = [];
  192. if (cycleCount == 0 && checkPoint.length == 2) { //特殊消除
  193. let pos1 = checkPoint[0];
  194. let pos2 = checkPoint[1];
  195. let model1 = this.cells[pos1.y][pos1.x];
  196. let model2 = this.cells[pos2.y][pos2.x];
  197. if (model1.status == CELL_STATUS.BIRD || model2.status == CELL_STATUS.BIRD) {
  198. let bombModel = null;
  199. if (model1.status == CELL_STATUS.BIRD) {
  200. model1.type = model2.type;
  201. bombModels.push(model1);
  202. }
  203. else {
  204. model2.type = model1.type;
  205. bombModels.push(model2);
  206. }
  207. }
  208. }
  209. for (var i in checkPoint) {
  210. var pos = checkPoint[i];
  211. if (!this.cells[pos.y][pos.x]) {
  212. continue;
  213. }
  214. var [result, newCellStatus, newCellType, crushPoint] = this.checkPoint(pos.x, pos.y, true);
  215. if (result.length < 3) {
  216. continue;
  217. }
  218. for (var j in result) {
  219. var model = this.cells[result[j].y][result[j].x];
  220. this.crushCell(result[j].x, result[j].y, false, cycleCount);
  221. if (model.status != CELL_STATUS.COMMON) {
  222. bombModels.push(model);
  223. }
  224. }
  225. this.createNewCell(crushPoint, newCellStatus, newCellType);
  226. }
  227. this.processBomb(bombModels, cycleCount);
  228. this.curTime += ANITIME.DIE;
  229. checkPoint = this.down();
  230. cycleCount++;
  231. }
  232. }
  233. //生成新cell
  234. createNewCell(pos, status, type) {
  235. if (status == "") {
  236. return;
  237. }
  238. if (status == CELL_STATUS.BIRD) {
  239. type = CELL_TYPE.BIRD
  240. }
  241. let model = new CellModel();
  242. this.cells[pos.y][pos.x] = model
  243. model.init(type);
  244. model.setStartXY(pos.x, pos.y);
  245. model.setXY(pos.x, pos.y);
  246. model.setStatus(status);
  247. model.setVisible(0, false);
  248. model.setVisible(this.curTime, true);
  249. this.changeModels.push(model);
  250. }
  251. // 下落
  252. down() {
  253. let newCheckPoint = [];
  254. for (var i = 1; i <= GRID_WIDTH; i++) {
  255. for (var j = 1; j <= GRID_HEIGHT; j++) {
  256. if (this.cells[i][j] == null) {
  257. var curRow = i;
  258. for (var k = curRow; k <= GRID_HEIGHT; k++) {
  259. if (this.cells[k][j]) {
  260. this.pushToChangeModels(this.cells[k][j]);
  261. newCheckPoint.push(this.cells[k][j]);
  262. this.cells[curRow][j] = this.cells[k][j];
  263. this.cells[k][j] = null;
  264. this.cells[curRow][j].setXY(j, curRow);
  265. this.cells[curRow][j].moveTo(cc.v2(j, curRow), this.curTime);
  266. curRow++;
  267. }
  268. }
  269. var count = 1;
  270. for (var k = curRow; k <= GRID_HEIGHT; k++) {
  271. this.cells[k][j] = new CellModel();
  272. this.cells[k][j].init(this.getRandomCellType());
  273. this.cells[k][j].setStartXY(j, count + GRID_HEIGHT);
  274. this.cells[k][j].setXY(j, count + GRID_HEIGHT);
  275. this.cells[k][j].moveTo(cc.v2(j, k), this.curTime);
  276. count++;
  277. this.changeModels.push(this.cells[k][j]);
  278. newCheckPoint.push(this.cells[k][j]);
  279. }
  280. }
  281. }
  282. }
  283. this.curTime += ANITIME.TOUCH_MOVE + 0.3
  284. return newCheckPoint;
  285. }
  286. pushToChangeModels(model) {
  287. if (this.changeModels.indexOf(model) != -1) {
  288. return;
  289. }
  290. this.changeModels.push(model);
  291. }
  292. cleanCmd() {
  293. for (var i = 1; i <= GRID_WIDTH; i++) {
  294. for (var j = 1; j <= GRID_HEIGHT; j++) {
  295. if (this.cells[i][j]) {
  296. this.cells[i][j].cmd = [];
  297. }
  298. }
  299. }
  300. }
  301. exchangeCell(pos1, pos2) {
  302. var tmpModel = this.cells[pos1.y][pos1.x];
  303. this.cells[pos1.y][pos1.x] = this.cells[pos2.y][pos2.x];
  304. this.cells[pos1.y][pos1.x].x = pos1.x;
  305. this.cells[pos1.y][pos1.x].y = pos1.y;
  306. this.cells[pos2.y][pos2.x] = tmpModel;
  307. this.cells[pos2.y][pos2.x].x = pos2.x;
  308. this.cells[pos2.y][pos2.x].y = pos2.y;
  309. }
  310. // 设置种类
  311. // Todo 改成乱序算法
  312. setCellTypeNum(num) {
  313. console.log("num = ", num);
  314. this.cellTypeNum = num;
  315. this.cellCreateType = [];
  316. let createTypeList = this.cellCreateType;
  317. for (let i = 1; i <= CELL_BASENUM; i++) {
  318. createTypeList.push(i);
  319. }
  320. for (let i = 0; i < createTypeList.length; i++) {
  321. let index = Math.floor(Math.random() * (CELL_BASENUM - i)) + i;
  322. createTypeList[i], createTypeList[index] = createTypeList[index], createTypeList[i]
  323. }
  324. }
  325. // 随要生成一个类型
  326. getRandomCellType() {
  327. var index = Math.floor(Math.random() * this.cellTypeNum);
  328. return this.cellCreateType[index];
  329. }
  330. // TODO bombModels去重
  331. processBomb(bombModels, cycleCount) {
  332. while (bombModels.length > 0) {
  333. let newBombModel = [];
  334. let bombTime = ANITIME.BOMB_DELAY;
  335. bombModels.forEach(function (model) {
  336. if (model.status == CELL_STATUS.LINE) {
  337. for (let i = 1; i <= GRID_WIDTH; i++) {
  338. if (this.cells[model.y][i]) {
  339. if (this.cells[model.y][i].status != CELL_STATUS.COMMON) {
  340. newBombModel.push(this.cells[model.y][i]);
  341. }
  342. this.crushCell(i, model.y, false, cycleCount);
  343. }
  344. }
  345. this.addRowBomb(this.curTime, cc.v2(model.x, model.y));
  346. }
  347. else if (model.status == CELL_STATUS.COLUMN) {
  348. for (let i = 1; i <= GRID_HEIGHT; i++) {
  349. if (this.cells[i][model.x]) {
  350. if (this.cells[i][model.x].status != CELL_STATUS.COMMON) {
  351. newBombModel.push(this.cells[i][model.x]);
  352. }
  353. this.crushCell(model.x, i, false, cycleCount);
  354. }
  355. }
  356. this.addColBomb(this.curTime, cc.v2(model.x, model.y));
  357. }
  358. else if (model.status == CELL_STATUS.WRAP) {
  359. let x = model.x;
  360. let y = model.y;
  361. for (let i = 1; i <= GRID_HEIGHT; i++) {
  362. for (let j = 1; j <= GRID_WIDTH; j++) {
  363. let delta = Math.abs(x - j) + Math.abs(y - i);
  364. if (this.cells[i][j] && delta <= 2) {
  365. if (this.cells[i][j].status != CELL_STATUS.COMMON) {
  366. newBombModel.push(this.cells[i][j]);
  367. }
  368. this.crushCell(j, i, false, cycleCount);
  369. }
  370. }
  371. }
  372. }
  373. else if (model.status == CELL_STATUS.BIRD) {
  374. let crushType = model.type
  375. if (bombTime < ANITIME.BOMB_BIRD_DELAY) {
  376. bombTime = ANITIME.BOMB_BIRD_DELAY;
  377. }
  378. if (crushType == CELL_TYPE.BIRD) {
  379. crushType = this.getRandomCellType();
  380. }
  381. for (let i = 1; i <= GRID_HEIGHT; i++) {
  382. for (let j = 1; j <= GRID_WIDTH; j++) {
  383. if (this.cells[i][j] && this.cells[i][j].type == crushType) {
  384. if (this.cells[i][j].status != CELL_STATUS.COMMON) {
  385. newBombModel.push(this.cells[i][j]);
  386. }
  387. this.crushCell(j, i, true, cycleCount);
  388. }
  389. }
  390. }
  391. //this.crushCell(model.x, model.y);
  392. }
  393. }, this);
  394. if (bombModels.length > 0) {
  395. this.curTime += bombTime;
  396. }
  397. bombModels = newBombModel;
  398. }
  399. }
  400. /**
  401. *
  402. * @param {开始播放的时间} playTime
  403. * @param {*cell位置} pos
  404. * @param {*第几次消除,用于播放音效} step
  405. */
  406. addCrushEffect(playTime, pos, step) {
  407. this.effectsQueue.push({
  408. playTime,
  409. pos,
  410. action: "crush",
  411. step
  412. });
  413. }
  414. addRowBomb(playTime, pos) {
  415. this.effectsQueue.push({
  416. playTime,
  417. pos,
  418. action: "rowBomb"
  419. });
  420. }
  421. addColBomb(playTime, pos) {
  422. this.effectsQueue.push({
  423. playTime,
  424. pos,
  425. action: "colBomb"
  426. });
  427. }
  428. addWrapBomb(playTime, pos) {
  429. // TODO
  430. }
  431. // cell消除逻辑
  432. crushCell(x, y, needShake, step) {
  433. let model = this.cells[y][x];
  434. this.pushToChangeModels(model);
  435. if (needShake) {
  436. model.toShake(this.curTime)
  437. }
  438. let shakeTime = needShake ? ANITIME.DIE_SHAKE : 0;
  439. model.toDie(this.curTime + shakeTime);
  440. this.addCrushEffect(this.curTime + shakeTime, cc.v2(model.x, model.y), step);
  441. this.cells[y][x] = null;
  442. }
  443. }