PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/client/gui/pawsgameboard.cpp

https://gitlab.com/GreatShift/PSExtended
C++ | 660 lines | 481 code | 113 blank | 66 comment | 121 complexity | 49d9ce765e76797533a00d39832bf188 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0
  1. /*
  2. * pawsgameboard.cpp - Author: Enar Vaikene
  3. *
  4. * Copyright (C) 2006 Atomic Blue (info@planeshift.it, http://www.atomicblue.org)
  5. *
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation (version 2 of the License)
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17. *
  18. */
  19. // STANDARD INCLUDE
  20. #include <psconfig.h>
  21. #include "globals.h"
  22. // COMMON/NET INCLUDES
  23. #include "net/clientmsghandler.h"
  24. #include "net/cmdhandler.h"
  25. #include "util/log.h"
  26. #include "pawsgameboard.h"
  27. using namespace psMiniGame;
  28. //-----------------------------------------------------------------------------
  29. pawsGameBoard::pawsGameBoard()
  30. {
  31. dragging = false;
  32. draggingPiece = NULL;
  33. gameOptions = 0;
  34. cols = 0;
  35. rows = 0;
  36. // Setup the piece ID to art file conversion table
  37. pieceArt.Put(WHITE_1, "Minigame Piece White 1");
  38. pieceArt.Put(WHITE_2, "Minigame Piece White 2");
  39. pieceArt.Put(WHITE_3, "Minigame Piece White 3");
  40. pieceArt.Put(WHITE_4, "Minigame Piece White 4");
  41. pieceArt.Put(WHITE_5, "Minigame Piece White 5");
  42. pieceArt.Put(WHITE_6, "Minigame Piece White 6");
  43. pieceArt.Put(WHITE_7, "Minigame Piece White 7");
  44. pieceArt.Put(BLACK_1, "Minigame Piece Black 1");
  45. pieceArt.Put(BLACK_2, "Minigame Piece Black 2");
  46. pieceArt.Put(BLACK_3, "Minigame Piece Black 3");
  47. pieceArt.Put(BLACK_4, "Minigame Piece Black 4");
  48. pieceArt.Put(BLACK_5, "Minigame Piece Black 5");
  49. pieceArt.Put(BLACK_6, "Minigame Piece Black 6");
  50. pieceArt.Put(BLACK_7, "Minigame Piece Black 7");
  51. }
  52. pawsGameBoard::~pawsGameBoard()
  53. {
  54. psengine->GetMsgHandler()->Unsubscribe(this, MSGTYPE_MINIGAME_BOARD);
  55. psengine->GetMsgHandler()->Unsubscribe(this, MSGTYPE_MINIGAME_STARTSTOP);
  56. }
  57. bool pawsGameBoard::PostSetup()
  58. {
  59. // Subscribe to Minigame messages
  60. psengine->GetMsgHandler()->Subscribe(this, MSGTYPE_MINIGAME_BOARD);
  61. psengine->GetMsgHandler()->Subscribe(this, MSGTYPE_MINIGAME_STARTSTOP);
  62. return true;
  63. }
  64. void pawsGameBoard::HandleMessage(MsgEntry *message)
  65. {
  66. if (message->GetType() == MSGTYPE_MINIGAME_BOARD)
  67. {
  68. // Got the game board layout from the server
  69. psMGBoardMessage msg(message);
  70. if (!msg.valid)
  71. {
  72. Error1("Failed to parse psMGBoardMessage from server.");
  73. return;
  74. }
  75. Debug2(LOG_MINIGAMES, 0, "psMGBoardMessage: %s\n", msg.ToString(0).GetData());
  76. // Verify the message counter
  77. if (counterSet && !msg.IsNewerThan(currentCounter))
  78. {
  79. Error3("Ignoring game board message with version %d because our version is %d.",
  80. msg.msgCounter, currentCounter);
  81. return;
  82. }
  83. currentCounter = msg.msgCounter;
  84. if (!counterSet)
  85. counterSet = true;
  86. // if this update is resetting a disallowed move, need to resync
  87. // with server & other clients, so undo the counter by 1.
  88. if (msg.msgOptions & DISALLOWED_MOVE)
  89. currentCounter--;
  90. // Update or setup the game board
  91. UpdateBoard(msg);
  92. }
  93. else if (message->GetType() == MSGTYPE_MINIGAME_STARTSTOP)
  94. {
  95. // Game stopped by the server
  96. psMGStartStopMessage msg(message);
  97. if (!msg.valid)
  98. {
  99. if (!msg.valid)
  100. {
  101. Error1("Failed to parse psMGStartStopMessage from server.");
  102. return;
  103. }
  104. Debug2(LOG_MINIGAMES, 0, "psMGStartStopMessage: %s\n", msg.ToString(0).GetData());
  105. }
  106. if (!msg.msgStart && visible)
  107. {
  108. pawsWidget::Hide();
  109. }
  110. }
  111. }
  112. void pawsGameBoard::Hide()
  113. {
  114. pawsWidget::Hide();
  115. // Stop the game
  116. psMGStartStopMessage msg(0, false);
  117. msg.SendMessage();
  118. }
  119. void pawsGameBoard::StartGame()
  120. {
  121. // Clean the board
  122. CleanBoard();
  123. // Reset the current counter
  124. currentCounter = 0;
  125. counterSet = false;
  126. // Request to start the game
  127. psMGStartStopMessage msg(0, true);
  128. msg.SendMessage();
  129. }
  130. void pawsGameBoard::CleanBoard()
  131. {
  132. // Remove tiles from the board window
  133. csArray<pawsGameTile *>::Iterator iter = tiles.GetIterator();
  134. while (iter.HasNext())
  135. DeleteChild(iter.Next());
  136. // Clean the array
  137. tiles.DeleteAll();
  138. cols = 0;
  139. rows = 0;
  140. }
  141. void pawsGameBoard::SetupBoard(psMGBoardMessage &msg)
  142. {
  143. if (msg.msgCols <= GAMEBOARD_MIN_COLS || msg.msgRows <= GAMEBOARD_MIN_ROWS )
  144. {
  145. Error1("Invalid number of colums or rows for the game board.");
  146. return;
  147. }
  148. gameID = msg.msgGameID;
  149. // Calculate width and height for one game tile
  150. csRect frame = GetScreenFrame();
  151. int x = GetActualWidth(32);
  152. int y = GetActualHeight(32);
  153. int w = (frame.xmax-frame.xmin - 2*x) / msg.msgCols;
  154. int h = (frame.ymax-frame.ymin - 2*y) / msg.msgRows;
  155. // Limit the tile size to 64 or game pieces will look ugly
  156. if (w > GetActualWidth(64))
  157. w = GetActualWidth(64);
  158. if (h > GetActualHeight(64))
  159. h = GetActualHeight(64);
  160. // Use the smallest value for width and height, and recalculate starting points
  161. if (w < h)
  162. h = w;
  163. else
  164. w = h;
  165. x = (frame.xmax - frame.xmin - w*msg.msgCols)/2;
  166. y = (frame.ymax - frame.ymin - h*msg.msgRows)/2;
  167. Debug5(LOG_MINIGAMES, 0, "Using x = %d, y = %d, w = %d and h = %d", x, y, w, h);
  168. if (w <= 10 || h <= 10)
  169. {
  170. Error1("Too many rows or columns for the game board");
  171. return;
  172. }
  173. // Clean the previous board
  174. CleanBoard();
  175. cols = msg.msgCols;
  176. rows = msg.msgRows;
  177. gameOptions = msg.msgOptions;
  178. // Start from the bottom-right corner if we play with black pieces
  179. if (gameOptions & BLACK_PIECES)
  180. {
  181. x += w*cols;
  182. y += h*rows;
  183. }
  184. // Start with a white (or black) tile
  185. bool white = !(gameOptions & BLACK_SQUARE);
  186. for (int8_t i = 0; i < rows; i++)
  187. {
  188. for (int8_t j = 0; j < cols; j++)
  189. {
  190. uint8_t state = EMPTY_TILE;
  191. // Check the layout
  192. if (msg.msgLayout)
  193. {
  194. int idx = i*cols + j;
  195. uint8_t v = msg.msgLayout[idx / 2];
  196. if (idx % 2)
  197. state = v & 0x0F;
  198. else
  199. state = (v & 0xF0) >> 4;
  200. }
  201. if (state != DISABLED_TILE)
  202. {
  203. pawsGameTile *tile = new pawsGameTile;
  204. if (!tile)
  205. {
  206. Error1("Could not create widget pawsGameTile");
  207. return;
  208. }
  209. tile->SetGameBoard(this);
  210. tiles.Push(tile);
  211. AddChild(tile);
  212. if (white)
  213. tile->SetBackground("Minigame Tile White");
  214. else
  215. tile->SetBackground("Minigame Tile Black");
  216. // Reposition the game tile
  217. if (gameOptions & BLACK_PIECES)
  218. tile->SetRelativeFrame(x-(j+1)*w, y-(i+1)*h, w, h);
  219. else
  220. tile->SetRelativeFrame(x+j*w, y+i*h, w, h);
  221. tile->SetFade(fade);
  222. tile->SetState(state);
  223. tile->SetColumn(j);
  224. tile->SetRow(i);
  225. }
  226. // Flip colors
  227. if (!(gameOptions & PLAIN_SQUARES))
  228. white = !white;
  229. }
  230. // Flip the color once more if we have an even number of columns
  231. if (cols % 2 == 0 && !(gameOptions & PLAIN_SQUARES))
  232. white = !white;
  233. }
  234. // List of available pieces
  235. whitePieces.Empty();
  236. blackPieces.Empty();
  237. if (msg.msgNumOfPieces > 0 && msg.msgPieces)
  238. {
  239. for (size_t i = 0; i < msg.msgNumOfPieces; i++)
  240. {
  241. size_t idx = i/2;
  242. uint8_t piece;
  243. if (i%2)
  244. piece = msg.msgPieces[idx] & 0x0F;
  245. else
  246. piece = (msg.msgPieces[idx] & 0xF0) >> 4;
  247. if (piece >= WHITE_1 && piece <= WHITE_7)
  248. whitePieces.Push(piece);
  249. else if (piece >= BLACK_1 && piece <= BLACK_7)
  250. blackPieces.Push(piece);
  251. Debug2(LOG_MINIGAMES, 0, "Available Piece: %u", piece);
  252. }
  253. }
  254. else
  255. {
  256. // By default we will have only one white and one black variant of pieces
  257. whitePieces.Push(WHITE_1);
  258. blackPieces.Push(BLACK_1);
  259. Debug1(LOG_MINIGAMES, 0, "Available Pieces: Only one black and one white");
  260. }
  261. // Show the window
  262. Show();
  263. }
  264. void pawsGameBoard::UpdateBoard(psMGBoardMessage &msg)
  265. {
  266. // The number of columns and rows shouldn't change, but if it id, setup a new board
  267. if (gameID != msg.msgGameID || cols != msg.msgCols || rows != msg.msgRows)
  268. {
  269. SetupBoard(msg);
  270. return;
  271. }
  272. // This time we need the layout
  273. if (!msg.msgLayout)
  274. {
  275. Error1("No layout given to update the game board.");
  276. return;
  277. }
  278. gameOptions = msg.msgOptions;
  279. size_t k = 0;
  280. for (int8_t i = 0; i < rows; i++)
  281. {
  282. for (int8_t j = 0; j < cols; j++)
  283. {
  284. uint8_t state;
  285. // Check the layout
  286. int idx = i*cols + j;
  287. unsigned char v = msg.msgLayout[idx / 2];
  288. if (idx % 2)
  289. state = v & 0x0F;
  290. else
  291. state = (v & 0xF0) >> 4;
  292. if (state != DISABLED_TILE && k < tiles.GetSize())
  293. {
  294. tiles[k++]->SetState(state);
  295. }
  296. }
  297. }
  298. // List of available pieces may change if it depends on the current board status
  299. if (msg.msgNumOfPieces > 0 && msg.msgPieces)
  300. {
  301. Debug1(LOG_MINIGAMES, 0, "Received update of available Pieces");
  302. whitePieces.Empty();
  303. blackPieces.Empty();
  304. for (size_t i = 0; i < msg.msgNumOfPieces; i++)
  305. {
  306. size_t idx = i/2;
  307. uint8_t piece;
  308. if (i%2)
  309. piece = msg.msgPieces[idx] & 0x0F;
  310. else
  311. piece = (msg.msgPieces[idx] & 0xF0) >> 4;
  312. if (piece >= WHITE_1 && piece <= WHITE_7)
  313. whitePieces.Push(piece);
  314. else if (piece >= BLACK_1 && piece <= BLACK_7)
  315. blackPieces.Push(piece);
  316. Debug2(LOG_MINIGAMES, 0, "Available Piece: %u", piece);
  317. }
  318. }
  319. }
  320. void pawsGameBoard::DropPiece(pawsGameTile *tile)
  321. {
  322. PawsManager::GetSingleton().SetDragDropWidget(NULL);
  323. dragging = false;
  324. pawsGameTile *oldTile = draggingPiece;
  325. draggingPiece = NULL;
  326. // Don't send updates if the old tile and new tile are the same
  327. if (oldTile && tile &&
  328. oldTile->GetColumn() == tile->GetColumn() && oldTile->GetRow() == tile->GetRow())
  329. return;
  330. // Fill in the buffer with updates.
  331. uint8_t updates[4];
  332. int idx = 0;
  333. uint8_t cnt = 0;
  334. // Old location shall be set to empty.
  335. if (oldTile)
  336. {
  337. updates[idx++] = (oldTile->GetColumn() << 4) + oldTile->GetRow();
  338. updates[idx++] = EMPTY_TILE;
  339. cnt++;
  340. }
  341. // New location.
  342. if (tile)
  343. {
  344. updates[idx++] = (tile->GetColumn() << 4) + tile->GetRow();
  345. updates[idx++] = tile->GetState();
  346. cnt++;
  347. }
  348. // Send to the server.
  349. if (cnt > 0)
  350. {
  351. psMGUpdateMessage msg(0, ++currentCounter, gameID, cnt, updates);
  352. msg.SendMessage();
  353. }
  354. }
  355. void pawsGameBoard::UpdatePiece(pawsGameTile *tile)
  356. {
  357. if (!tile)
  358. return;
  359. // Fill in the buffer with updates.
  360. uint8_t updates[2];
  361. updates[0] = (tile->GetColumn() << 4) + tile->GetRow();
  362. updates[1] = tile->GetState();
  363. // Send to the server.
  364. psMGUpdateMessage msg(0, ++currentCounter, gameID, 1, updates);
  365. msg.SendMessage();
  366. }
  367. void pawsGameBoard::StartDragging(pawsGameTile *tile)
  368. {
  369. dragging = true;
  370. draggingPiece = tile;
  371. pawsGameTile *widget = new pawsGameTile;
  372. widget->SetGameBoard(this);
  373. widget->SetParent(NULL);
  374. widget->SetRelativeFrame(0, 0, tile->GetDefaultFrame().Width(), tile->GetDefaultFrame().Height());
  375. widget->SetState(tile->GetState());
  376. PawsManager::GetSingleton().SetDragDropWidget(widget);
  377. }
  378. uint8_t pawsGameBoard::NextPiece(uint8_t current) const
  379. {
  380. if (current >= WHITE_1 && current <= WHITE_7)
  381. {
  382. size_t i = 0;
  383. // Search for the current white piece
  384. while (i < whitePieces.GetSize() && whitePieces[i] != current)
  385. i++;
  386. if (i >= whitePieces.GetSize())
  387. return current; // The current piece was not found in the array
  388. else
  389. {
  390. i++; // Next piece
  391. if (i >= whitePieces.GetSize())
  392. i = 0;
  393. return (uint8_t)whitePieces[i];
  394. }
  395. }
  396. else if (current >= BLACK_1 && current <= BLACK_7)
  397. {
  398. size_t i = 0;
  399. // Search for the current black piece
  400. while (i < blackPieces.GetSize() && blackPieces[i] != current)
  401. i++;
  402. if (i >= blackPieces.GetSize())
  403. return current; // The current piece was not found in the array
  404. else
  405. {
  406. i++; // Next piece
  407. if (i >= blackPieces.GetSize())
  408. i = 0;
  409. return (uint8_t)blackPieces[i];
  410. }
  411. }
  412. else
  413. return EMPTY_TILE;
  414. }
  415. const csString pawsGameBoard::PieceArtName(uint8_t piece) const
  416. {
  417. return pieceArt.Get(piece, "");
  418. }
  419. //-----------------------------------------------------------------------------
  420. pawsGameTile::pawsGameTile()
  421. : pawsWidget()
  422. {
  423. board = 0;
  424. visible = true;
  425. movable = false;
  426. isResizable = false;
  427. configurable = false;
  428. fade = true;
  429. state = EMPTY_TILE;
  430. oldState = EMPTY_TILE;
  431. column = -1;
  432. row = -1;
  433. }
  434. pawsGameTile::~pawsGameTile()
  435. {
  436. }
  437. void pawsGameTile::SetGameBoard(pawsGameBoard *gameBoard)
  438. {
  439. board = gameBoard;
  440. }
  441. void pawsGameTile::SetColumn(int8_t newColum)
  442. {
  443. column = newColum;
  444. }
  445. void pawsGameTile::SetRow(int8_t newRow)
  446. {
  447. row = newRow;
  448. }
  449. void pawsGameTile::Draw()
  450. {
  451. pawsWidget::Draw();
  452. if (state != EMPTY_TILE)
  453. {
  454. csRect frame = screenFrame;
  455. ClipToParent(false);
  456. image->Draw(frame);
  457. }
  458. }
  459. bool pawsGameTile::OnMouseDown(int button, int modifiers, int x, int y)
  460. {
  461. if (!board || board->GetGameOptions() & READ_ONLY)
  462. return false;
  463. // Use the EntitySelect mouse bind to select pieces/create player's pieces
  464. if (psengine->GetMouseBinds()->CheckBind("EntitySelect", button, modifiers))
  465. {
  466. pawsGameTile *tile = dynamic_cast<pawsGameTile *>
  467. (PawsManager::GetSingleton().GetDragDropWidget());
  468. if (tile)
  469. {
  470. SetState(tile->GetState());
  471. board->DropPiece(this);
  472. }
  473. else if (state == EMPTY_TILE)
  474. {
  475. SetState(board->GetGameOptions() & BLACK_PIECES ?
  476. board->BlackPiecesList(0) : board->WhitePiecesList(0));
  477. board->UpdatePiece(this);
  478. }
  479. else if (!(board->GetGameOptions() & OPTION_PLACE_ONLY))
  480. {
  481. board->StartDragging(this);
  482. SetState(EMPTY_TILE);
  483. }
  484. return true;
  485. }
  486. // Use the ContextMenu mouse bind to create oponent's pieces
  487. if (psengine->GetMouseBinds()->CheckBind("ContextMenu", button, modifiers))
  488. {
  489. if (state == EMPTY_TILE)
  490. {
  491. SetState(board->GetGameOptions() & BLACK_PIECES ?
  492. board->WhitePiecesList(0) : board->BlackPiecesList(0));
  493. board->UpdatePiece(this);
  494. }
  495. return true;
  496. }
  497. // Forward to the parent
  498. if (parent)
  499. return parent->OnMouseDown(button, modifiers, x, y);
  500. else
  501. return pawsWidget::OnMouseDown(button, modifiers, x, y);
  502. }
  503. bool pawsGameTile::OnDoubleClick(int button, int modifiers, int x, int y)
  504. {
  505. if (!board || board->GetGameOptions() & READ_ONLY)
  506. return false;
  507. if (psengine->GetMouseBinds()->CheckBind("EntitySelect", button, modifiers) && state != EMPTY_TILE)
  508. {
  509. SetState(board->NextPiece(state));
  510. board->UpdatePiece(this);
  511. return true;
  512. }
  513. // Forward to the parent
  514. if (parent)
  515. return parent->OnDoubleClick(button, modifiers, x, y);
  516. else
  517. return pawsWidget::OnDoubleClick(button, modifiers, x, y);
  518. }
  519. void pawsGameTile::SetState(uint8_t state)
  520. {
  521. if (!board)
  522. return;
  523. if (state != this->state)
  524. {
  525. if (state != EMPTY_TILE && state != DISABLED_TILE)
  526. {
  527. csString art = board->PieceArtName(state);
  528. if (art.IsEmpty())
  529. {
  530. Error2("Invalid minigame state %u", state);
  531. return;
  532. }
  533. image = PawsManager::GetSingleton().GetTextureManager()->GetPawsImage(art);
  534. if (!image)
  535. {
  536. Error2("Could not create image \"%s\"", art.GetData());
  537. return;
  538. }
  539. }
  540. oldState = this->state;
  541. this->state = state;
  542. }
  543. }
  544. void pawsGameTile::RestoreState()
  545. {
  546. SetState(oldState);
  547. }