Space Walk board game
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

339 lines
9.1 KiB

  1. #include <algorithm>
  2. #include "board.h"
  3. #include "boardwindow.h"
  4. #include "inputoutput.h"
  5. #include "maingame.h"
  6. #include "mainmenu.h"
  7. #include "messagebox.h"
  8. #include "player.h"
  9. #include "selectionbox.h"
  10. #include "spaceship.h"
  11. #include "window.h"
  12. #include "infowindow.h"
  13. MainGame::MainGame(Player **player) :
  14. help("Phase 1!\n\nThe players take turns, placing a spaceship on a planet\nof their choice."),
  15. phase(false),
  16. playerTurn(0),
  17. queuePlanet(-1),
  18. player(player),
  19. board(nullptr),
  20. ship(nullptr),
  21. boardWindow(nullptr),
  22. infoWindow(nullptr) {
  23. // Reserve 0 spaceships of memory for the queue
  24. this->queue = new std::vector<SpaceShip *>(0);
  25. }
  26. void MainGame::initialize() {
  27. this->board = new Board();
  28. this->ship = new SpaceShip*[SHIP_SIZE] { nullptr };
  29. unsigned char cCounter = 0;
  30. unsigned char colors[PLAYER_SIZE] = { PAIR_RED_BLACK, PAIR_BLUE_BLACK };
  31. unsigned char sCounter = 0;
  32. unsigned char sizes[3] = { SpaceShip::SMALL, SpaceShip::MEDIUM, SpaceShip::BIG };
  33. // Create 9 ships for each player, 3 of each size
  34. for (unsigned char i = 0; i < SHIP_SIZE; i++) {
  35. if (i != 0 && i % (SHIP_SIZE / PLAYER_SIZE) == 0) {
  36. cCounter++;
  37. }
  38. if (i != 0 && i % 3 == 0) {
  39. sCounter++;
  40. }
  41. if (sCounter >= 3) {
  42. sCounter = 0;
  43. }
  44. this->ship[i] = new SpaceShip(colors[cCounter], sizes[sCounter]);
  45. }
  46. this->boardWindow = new BoardWindow(0, 16, this->board);
  47. this->infoWindow = new InfoWindow(0, 16, this->player, this->ship);
  48. // Display help message
  49. MainGame::render();
  50. MessageBox(this->help);
  51. }
  52. void MainGame::update() {
  53. MainGame::sleep(250);
  54. // Display new help message on phase change
  55. if (this->changePhase) {
  56. this->phase = true;
  57. this->changePhase = false;
  58. this->help = "Phase 2!\n\nThe players take turns, evacuating a planet of their choice.";
  59. MessageBox(this->help);
  60. return;
  61. }
  62. if (!this->phase) {
  63. this->phase1();
  64. }
  65. else {
  66. this->phase2();
  67. }
  68. }
  69. void MainGame::render() {
  70. this->boardWindow->clear();
  71. this->infoWindow->clear();
  72. this->boardWindow->update();
  73. this->infoWindow->update();
  74. this->boardWindow->render();
  75. this->infoWindow->render();
  76. }
  77. void MainGame::destroy() {
  78. delete this->board;
  79. delete this->boardWindow;
  80. delete this->infoWindow;
  81. for (int i = 0; i < SHIP_SIZE; i++) {
  82. delete this->ship[i];
  83. }
  84. delete []this->ship;
  85. for (int i = 0; i < PLAYER_SIZE; i++) {
  86. delete this->player[i];
  87. }
  88. delete []this->player;
  89. }
  90. void MainGame::phase1() {
  91. std::string playerTurnStr = std::to_string(this->playerTurn + 1);
  92. SelectionBox s = SelectionBox(0, 16);
  93. // Ask the current player to pick a Spaceship size
  94. std::string sizeStr = "Player " + playerTurnStr + ", pick a spaceship size.";
  95. std::string sizeOptions[] = {"S", "M", "B", "\0"};
  96. unsigned char sizeSelection = s.select(sizeStr, &sizeOptions[0]);
  97. // Ask the current player to pick a planet
  98. std::string planetStr = "Player " + playerTurnStr + ", pick a planet to put this spaceship on.";
  99. std::string planetOptions[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "\0"};
  100. unsigned char planetSelection = s.select(planetStr, &planetOptions[0]);
  101. // Calculate array index
  102. int startLoop = SHIP_SIZE / PLAYER_SIZE * this->playerTurn + (sizeSelection * 3);
  103. // Get ships of player selected planet
  104. auto ships = this->board->getShips(std::stoi(planetOptions[planetSelection]));
  105. bool placedShip = false;
  106. bool shouldExit;
  107. // Check all 3 ships of the size the player selected
  108. for (int i = startLoop; i < startLoop + 3; i++) {
  109. shouldExit = false;
  110. // Check if spaceship is already on a planet
  111. if (this->ship[i]->getPlanet() != PLANET_UNSET) {
  112. continue;
  113. }
  114. // If planet already holds spaceship of equal size
  115. for(auto ship : *ships) {
  116. if (this->ship[i]->getSize() == ship->getSize()) {
  117. shouldExit = true;
  118. break;
  119. }
  120. }
  121. if (shouldExit) {
  122. MessageBox("You cant place spaceships of the same size on a planet during this phase!", "Error");
  123. break;
  124. }
  125. // Add spaceship to planet
  126. this->board->setShip(std::stoi(planetOptions[planetSelection]), this->ship[i]);
  127. this->nextPlayerTurn();
  128. placedShip = true;
  129. break;
  130. }
  131. if (!placedShip && !shouldExit) {
  132. MessageBox("Please select a size of which you still have unplaced spaceships!", "Error");
  133. }
  134. // Check if all ships have been placed
  135. bool allPlaced = true;
  136. for (int i = 0; i < SHIP_SIZE; i++) {
  137. if (this->ship[i]->getPlanet() == PLANET_UNSET) {
  138. allPlaced = false;
  139. break;
  140. }
  141. }
  142. if (allPlaced) {
  143. this->changePhase = true;
  144. }
  145. }
  146. void MainGame::phase2() {
  147. std::string playerTurnStr = std::to_string(this->playerTurn + 1);
  148. SelectionBox s = SelectionBox(0, 16);
  149. // Move next spaceship in the queue
  150. if (this->queue->size() > 0) {
  151. // Determine the middle of the board
  152. unsigned char halfBoard = (BOARD_SIZE - 2) / 2; // 6
  153. // Planet rotation order, where |x| is a black hole
  154. // 1 ... 6 -> |13| -> 12 ... 7 -> |0| -> 1
  155. // 0 .. 5
  156. if (this->queuePlanet < halfBoard) {
  157. this->queuePlanet++;
  158. }
  159. // 8 .. 13
  160. else if (this->queuePlanet > halfBoard + 1) {
  161. this->queuePlanet--;
  162. }
  163. // 6
  164. else if (this->queuePlanet == halfBoard) {
  165. this->queuePlanet = BOARD_SIZE - 1;
  166. }
  167. // 7
  168. else if (this->queuePlanet == halfBoard + 1) {
  169. this->queuePlanet = 0;
  170. }
  171. unsigned char shipSelection = 0;
  172. // If the the current and next ship are of the same size, ask which goes first
  173. if (this->queue->size() > 1) {
  174. SpaceShip *tmpShip = this->queue->at(0);
  175. SpaceShip *tmpShip2 = this->queue->at(1);
  176. if (tmpShip->getSize() == tmpShip2->getSize() &&
  177. tmpShip->getColor() != tmpShip2->getColor()) {
  178. std::string shipStr = "Player " + playerTurnStr + ", pick the next ship that will evacuate.";
  179. std::string shipOptions[] = {"Yours", "Theirs", "\0"};
  180. shipSelection = s.select(shipStr, &shipOptions[0]);
  181. }
  182. }
  183. board->moveShip(this->queuePlanet, this->queue->at(shipSelection));
  184. return;
  185. }
  186. else if (this->queuePlanet != -1) {
  187. this->queuePlanet = -1;
  188. this->nextPlayerTurn();
  189. this->calculateWinner();
  190. return;
  191. }
  192. // Ask the current player if they want to skip their turn
  193. unsigned char playerChip = this->player[this->playerTurn]->getChip();
  194. if (playerChip > 0) {
  195. std::string skipStr = "Player " + playerTurnStr + ", do you want to skip your turn for 1 chip?";
  196. std::string skipOptions[] = {"No", "Yes", "\0"};
  197. unsigned char skipSelection = s.select(skipStr, &skipOptions[0]);
  198. if (skipOptions[skipSelection] == "Yes") {
  199. this->player[this->playerTurn]->setChip(playerChip - 1);
  200. this->nextPlayerTurn();
  201. return;
  202. }
  203. }
  204. // Ask the current player to pick a planet
  205. std::string planetStr = "Player " + playerTurnStr + ", pick a planet to evacuate.";
  206. std::string planetOptions[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "\0"};
  207. unsigned char planetSelection = s.select(planetStr, &planetOptions[0]);
  208. // Get ships of player selected planet
  209. auto ships = this->board->getShips(std::stoi(planetOptions[planetSelection]));
  210. // Check if the selected planet holds a spaceship of the current player,
  211. // using function/functor/lambda
  212. unsigned char playerColor = this->player[(int)this->playerTurn]->getColor();
  213. auto findShip = std::find_if(ships->begin(), ships->end(),
  214. [playerColor](SpaceShip *ship) {return ship->getColor() == playerColor; });
  215. // If so, add this planet to the queue
  216. if (findShip != ships->end()) {
  217. this->queue = ships;
  218. this->queuePlanet = this->queue->at(0)->getPlanet();
  219. }
  220. else {
  221. MessageBox("Please select a planet that holds a ship of your own!", "Error");
  222. }
  223. }
  224. void MainGame::nextPlayerTurn() {
  225. this->playerTurn++;
  226. if (this->playerTurn > PLAYER_SIZE - 1) {
  227. this->playerTurn = 0;
  228. }
  229. }
  230. void MainGame::calculateWinner() {
  231. // Get all ships from the black holes
  232. auto blackHole1 = this->board->getShips(0);
  233. auto blackHole2 = this->board->getShips(BOARD_SIZE - 1);
  234. int totalScore = CHIP_AMOUNT * CHIP_SCORE +
  235. 3 * SpaceShip::BIG +
  236. 3 * SpaceShip::MEDIUM +
  237. 3 * SpaceShip::SMALL;
  238. // Fill players possible total score
  239. int score[PLAYER_SIZE];
  240. std::fill_n(score, PLAYER_SIZE, totalScore);
  241. // Calculate players score
  242. int crashedShips;
  243. bool gameOver = false;
  244. for (int i = 0; i < PLAYER_SIZE; i++) {
  245. // Subtract all used chips from the total score
  246. score[i] -= (CHIP_AMOUNT - this->player[i]->getChip()) * CHIP_SCORE;
  247. crashedShips = 0;
  248. for (auto ship : *blackHole1) {
  249. if (this->player[i]->getColor() == ship->getColor()) {
  250. crashedShips++;
  251. // Subtract crashed ship from the total score
  252. score[i] -= ship->getSize();
  253. }
  254. }
  255. for (auto ship : *blackHole2) {
  256. if (this->player[i]->getColor() == ship->getColor()) {
  257. crashedShips++;
  258. // Subtract crashed ship from the total score
  259. score[i] -= ship->getSize();
  260. }
  261. }
  262. // If a player has run out of ships..
  263. if (crashedShips == SHIP_SIZE / PLAYER_SIZE) {
  264. gameOver = true;
  265. }
  266. }
  267. // Print victory screen
  268. if (gameOver) {
  269. std::string printStr;
  270. int winnerIdx = 0;
  271. for (int i = 0; i < PLAYER_SIZE; i++) {
  272. printStr += "Player " + std::to_string(i + 1) + ": scored " +
  273. std::to_string(score[i]) + " points.\n";
  274. if (i < PLAYER_SIZE - 1 && score[i] < score[i + 1]) {
  275. winnerIdx = i + 1;
  276. }
  277. }
  278. printStr.insert(0, this->player[winnerIdx]->getName() + " has won!\n\n");
  279. MessageBox(printStr, "Victory");
  280. gameStateManager.setState(new MainMenu());
  281. }
  282. }