Line data Source code
1 : #include <client.hpp>
2 : #include <dirent.h>
3 : #include <iostream>
4 : #include <fstream>
5 : #include <json/json.h>
6 : #include <cmath>
7 : #include <string>
8 : #include <codecvt>
9 : #include <variant>
10 :
11 : #define MAP_X_OFFSET 175
12 : #define MAP_Y_OFFSET 50
13 :
14 : #define NUMBER_OF_FIELD 12
15 :
16 : #define TCHAT_MAX_SIZE 200
17 :
18 : #define WINDOW_LENGTH 1600
19 : #define WINDOW_WIDTH 900
20 :
21 : #define ACTION_CARD_PROPORTION 0.125
22 : #define NBR_CHAR_MAX_PER_LIGNE 22
23 : #define TURN_NUMBER 2
24 :
25 : #define ASCI_BEGIN 19
26 : #define ASCI_END 127
27 :
28 : #define CARD_BORDER 18
29 :
30 : #define INDEX_CHAT_BUTTON 8
31 : #define INDEX_MAP_BUTTON 9
32 : #define INDEX_QUIT_BUTTON 10
33 :
34 : #define ARROW_INDEX 5
35 :
36 : #define ELEMENT_PATH "/map/element/"
37 : #define CHAT_MIN_SIZE 7
38 :
39 : #ifndef RESOURCES_PATH
40 : #define RESOURCES_PATH "../resources"
41 : #endif
42 :
43 : typedef std::variant<shared::Caravan, shared::Barbarian, shared::BarbarianVillage, shared::ControlPawn, shared::City> variantElement;
44 :
45 : const std::vector<sf::Color> PLAYER_COLOR = {sf::Color(119, 238, 217, 160), sf::Color(251, 76, 255, 160), sf::Color(93, 109, 126, 160), sf::Color(230, 176, 170, 160)};
46 : const sf::Color TEXT_COLOR = sf::Color(240, 230, 230);
47 : const sf::Color TEXT_FOR_USER_BUTTON_COLOR = sf::Color(255, 255, 255, 100);
48 : const sf::Color TEXT_FOR_USER_COLOR = sf::Color(204, 0, 102);
49 : const sf::Color END_OF_TURN_BUTTON_COLOR = sf::Color(247, 200, 195);
50 :
51 : using namespace client;
52 :
53 : /*!
54 : * @brief Constructor
55 : *
56 : * Constructor of GameWindow class
57 : */
58 0 : GameWindow::GameWindow()
59 : {
60 0 : firstHexagonPosition = {MAP_X_OFFSET, MAP_Y_OFFSET};
61 0 : chatBox = std::make_unique<Chat>();
62 :
63 0 : const Json::Value &data = openJsonFile("/pop-up/dataButton.json");
64 0 : validateBoxesWindow = std::make_unique<PopUpWindow>(WINDOW_LENGTH, WINDOW_WIDTH, data, true);
65 0 : winnerWindow = std::make_unique<PopUpWindow>(WINDOW_LENGTH, WINDOW_WIDTH, data, false);
66 0 : validateBoxesWindow->gameWindow = this;
67 :
68 0 : chatBox = std::make_unique<Chat>();
69 0 : }
70 :
71 : /*!
72 : * @brief Display all the different variable in the screen
73 : */
74 0 : void GameWindow::displayWindow()
75 : {
76 :
77 0 : gameEnginePtr->clientWindow->clear(sf::Color::Blue);
78 :
79 0 : backgroundTexture->drawTextureDisplayerSprite(gameEnginePtr->clientWindow);
80 :
81 0 : for (auto &mapTexture : mapTextureToDisplay)
82 : {
83 0 : mapTexture.drawTextureDisplayerSprite(gameEnginePtr->clientWindow);
84 : }
85 :
86 0 : std::unique_lock<std::mutex> lock(updatePlayerMutex);
87 0 : for (auto &elementTexture : elementTextureToDisplay)
88 : {
89 0 : elementTexture.second->drawTextureDisplayerSprite(gameEnginePtr->clientWindow);
90 : }
91 0 : lock.unlock();
92 :
93 0 : for (auto &priorityCardTexture : priorityCards)
94 : {
95 0 : priorityCardTexture.texture->drawTextureDisplayerSprite(gameEnginePtr->clientWindow);
96 0 : gameEnginePtr->clientWindow->draw(*priorityCardTexture.title);
97 0 : gameEnginePtr->clientWindow->draw(*priorityCardTexture.nbOfBoxesText);
98 0 : if (priorityCardTexture.isUp)
99 : {
100 0 : gameEnginePtr->clientWindow->draw(*priorityCardTexture.body);
101 0 : priorityCardTexture.validateButton->drawButton(gameEnginePtr->clientWindow);
102 : }
103 : }
104 0 : boxTexture->drawTextureDisplayerSprite(gameEnginePtr->clientWindow);
105 :
106 0 : for (auto &actionCardTexture : actionCardsToDisplay)
107 : {
108 0 : gameEnginePtr->clientWindow->draw(actionCardTexture.texture->getSprite(0));
109 0 : gameEnginePtr->clientWindow->draw(*actionCardTexture.title);
110 0 : gameEnginePtr->clientWindow->draw(*actionCardTexture.body);
111 : }
112 :
113 0 : for (auto &whoIsPlayingButton : whoIsPlayingButtons)
114 : {
115 0 : whoIsPlayingButton.drawButton(gameEnginePtr->clientWindow);
116 : }
117 :
118 0 : gameEnginePtr->clientWindow->draw(hudTextureToDisplay.at(TURN_NUMBER % 5).getSprite());
119 :
120 0 : for (auto &hudTexture : hudTextureToDisplay)
121 : {
122 0 : hudTexture.drawTextureDisplayerSprite(gameEnginePtr->clientWindow);
123 : }
124 :
125 0 : textForTheUser->drawButton(gameEnginePtr->clientWindow);
126 0 : endOfRoundButton->drawButton(gameEnginePtr->clientWindow);
127 :
128 0 : if (validateBoxesWindow->isWindowActive)
129 : {
130 0 : validateBoxesWindow->drawValidateBoxesButtons(gameEnginePtr->clientWindow);
131 : }
132 :
133 0 : if (isChatOpen)
134 : {
135 0 : chatBox->drawChat(gameEnginePtr->clientWindow);
136 : }
137 :
138 0 : if (winnerWindow->isWindowActive)
139 : {
140 0 : winnerWindow->drawWinnerWindow(gameEnginePtr->clientWindow);
141 : }
142 :
143 0 : gameEnginePtr->clientWindow->display();
144 0 : }
145 :
146 : /*!
147 : * @brief Loop that look for events to happend and call displayWindow()
148 : * @param clientWindow is window that comes from the engine
149 : * @param quitGame is the function used to quit the menu, it is load as an attribut
150 : * @param callback is the function used to return where the user click on the screen
151 : */
152 0 : void GameWindow::startGame()
153 : {
154 0 : if (gameEnginePtr == nullptr)
155 : {
156 0 : return;
157 : }
158 :
159 0 : loadMapTexture();
160 0 : loadElementTexture();
161 0 : updateElementTexture();
162 0 : loadHudTexture();
163 0 : gameEnginePtr->areTextureLoaded.store(true);
164 0 : addPlayer(gameEnginePtr->myself->getName());
165 :
166 0 : std::shared_ptr<bool> moveMode = std::make_shared<bool>(false);
167 0 : std::shared_ptr<bool> clickMode = std::make_shared<bool>(false);
168 :
169 0 : sf::Vector2i clickStartingPoint;
170 :
171 0 : long lastUpdateTimer = getCurrentTime(false);
172 :
173 0 : while (gameEnginePtr->clientWindow->isOpen())
174 : {
175 :
176 0 : if (getCurrentTime(false) - lastUpdateTimer > (100 / 3))
177 : {
178 0 : displayWindow();
179 0 : lastUpdateTimer = getCurrentTime(false);
180 : }
181 :
182 : // handle events
183 : sf::Event event;
184 0 : while (gameEnginePtr->clientWindow->pollEvent(event))
185 : {
186 0 : if (handleGameEvent(event, clickStartingPoint, moveMode, clickMode))
187 : {
188 0 : return;
189 : }
190 : }
191 : }
192 0 : }
193 :
194 : /*!
195 : * @brief Test events and do actions corresponding to the event
196 : * @param event pointer to the event
197 : * @param clickStartingPoint reference used to know where the user start pressing mouse
198 : * @param moveMode pointer to know if the map is moving on the screen
199 : * @param clickMode pointer to know if the user is clicking on the screen
200 : */
201 0 : bool GameWindow::handleGameEvent(sf::Event &event, sf::Vector2i &clickStartingPoint, std::shared_ptr<bool> moveMode, std::shared_ptr<bool> clickMode)
202 : {
203 0 : std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter;
204 0 : switch (event.type)
205 : {
206 0 : case sf::Event::MouseButtonPressed:
207 :
208 0 : *clickMode = true;
209 0 : clickStartingPoint = sf::Mouse::getPosition(*gameEnginePtr->clientWindow);
210 0 : if (clickAction(event, clickStartingPoint, moveMode))
211 : {
212 0 : std::cout << "You have quit the game\n";
213 0 : gameEnginePtr->handleQuitMenu(true);
214 0 : return true;
215 : }
216 0 : break;
217 :
218 0 : case sf::Event::MouseButtonReleased:
219 0 : *clickMode = false;
220 0 : break;
221 :
222 0 : case sf::Event::MouseMoved:
223 0 : if (*moveMode && *clickMode)
224 : {
225 0 : moveMap(clickStartingPoint, sf::Mouse::getPosition(*gameEnginePtr->clientWindow));
226 : }
227 0 : break;
228 :
229 0 : case sf::Event::TextEntered:
230 0 : if (event.text.unicode > ASCI_BEGIN && event.text.unicode < ASCI_END && isChatOpen)
231 : {
232 0 : chatBox->addChatChar(converter.to_bytes(event.text.unicode));
233 : }
234 0 : break;
235 :
236 0 : case sf::Event::KeyPressed:
237 0 : if (handleKeyboardEvent(event.key))
238 0 : return true;
239 0 : break;
240 :
241 0 : case sf::Event::Closed:
242 0 : gameEnginePtr->handleQuitMenu(true);
243 0 : return true;
244 :
245 0 : default:
246 0 : break;
247 : }
248 0 : return false;
249 0 : }
250 :
251 : /*!
252 : * @brief Test keyboard events and do actions corresponding to the event
253 : * @param keyEvent The event
254 : * @param moveMode pointer to know if the map is moving on the screen
255 : * @param clickStartingPoint reference used to know where the user start pressing mouse
256 : */
257 0 : bool GameWindow::handleKeyboardEvent(sf::Event::KeyEvent keyEvent)
258 : {
259 0 : switch (keyEvent.code)
260 : {
261 0 : case sf::Keyboard::Enter:
262 0 : if (isChatOpen)
263 : {
264 0 : sendMessage();
265 : }
266 0 : break;
267 :
268 0 : case sf::Keyboard::BackSpace:
269 0 : chatBox->deleteChatChar();
270 0 : break;
271 :
272 0 : default:
273 0 : break;
274 : }
275 0 : return false;
276 : }
277 :
278 : /*!
279 : * @brief This function send a message to the server
280 : */
281 0 : void GameWindow::sendMessage()
282 : {
283 0 : std::unique_lock<std::mutex> lock(chatBox->mutexChat);
284 0 : std::string message = "chat " + chatBox->message + "\n";
285 0 : lock.unlock();
286 :
287 0 : if (message.size() < CHAT_MIN_SIZE)
288 0 : return;
289 :
290 0 : std::unique_lock<std::mutex> lock2(gameEnginePtr->myself->qAndA.sharedDataMutex);
291 0 : gameEnginePtr->myself->qAndA.question = message;
292 0 : lock2.unlock();
293 0 : gameEnginePtr->askServer();
294 0 : }
295 :
296 : /*!
297 : * @brief Change the cursor type to a hand or an arrow
298 : * @param moveMode pointer to know if the map is moving on the screen
299 : */
300 0 : void GameWindow::changeMouseCursor(sf::Event &event, std::shared_ptr<bool> moveMode)
301 : {
302 0 : if (event.mouseButton.button == sf::Mouse::Right)
303 : {
304 0 : sf::Vector2i nullPosition(0, 0);
305 0 : moveMap(nullPosition, {MAP_X_OFFSET, MAP_Y_OFFSET}, true);
306 0 : gameEnginePtr->clientWindow->setMouseCursor(clientCursor);
307 0 : return;
308 : }
309 :
310 0 : if (*moveMode)
311 : {
312 0 : *moveMode = false;
313 0 : clientCursor.loadFromSystem(sf::Cursor::Arrow);
314 : }
315 : else
316 : {
317 0 : *moveMode = true;
318 0 : clientCursor.loadFromSystem(sf::Cursor::Hand);
319 : }
320 0 : gameEnginePtr->clientWindow->setMouseCursor(clientCursor);
321 : }
322 :
323 : /*!
324 : * @brief Move the map on the screen
325 : * @param clickStartingPoint reference used to know where the user start pressing mouse
326 : * @param position position of the mouse
327 : * @param reset if true, reset the map to the original position, false by default
328 : */
329 0 : void GameWindow::moveMap(sf::Vector2i &clickStartingPoint, sf::Vector2i position, bool reset)
330 : {
331 0 : if (reset)
332 : {
333 0 : clickStartingPoint.x = firstHexagonPosition[0];
334 0 : clickStartingPoint.y = firstHexagonPosition[1];
335 : }
336 0 : std::array<int, 2> newMapOffset = {position.x - clickStartingPoint.x,
337 0 : position.y - clickStartingPoint.y};
338 :
339 0 : if (reset)
340 : {
341 0 : firstHexagonPosition = {MAP_X_OFFSET, MAP_Y_OFFSET};
342 : }
343 : else
344 : {
345 0 : firstHexagonPosition = {firstHexagonPosition[0] + newMapOffset[0],
346 0 : firstHexagonPosition[1] + newMapOffset[1]};
347 : }
348 :
349 0 : for (unsigned i = 0; i < mapTextureToDisplay.size(); i++)
350 : {
351 0 : mapTextureToDisplay[i].moveSpritePosition(newMapOffset[0], newMapOffset[1]);
352 : }
353 :
354 0 : for (auto &kv : elementTextureToDisplay)
355 : {
356 0 : kv.second->moveSpritePosition(newMapOffset[0], newMapOffset[1]);
357 : }
358 :
359 0 : clickStartingPoint = sf::Mouse::getPosition(*gameEnginePtr->clientWindow);
360 0 : }
361 :
362 : /*!
363 : * @brief Open JSON File
364 : * @param path path of the JSON File
365 : */
366 0 : const Json::Value GameWindow::openJsonFile(std::string path)
367 : {
368 0 : std::ifstream file(RESOURCES_PATH + path);
369 :
370 0 : if (!file.is_open())
371 : {
372 0 : std::cerr << "Error while opening json ressources file" << std::endl;
373 0 : std::cerr << path << std::endl;
374 0 : exit(1);
375 : }
376 0 : std::string str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
377 :
378 0 : std::unique_ptr<Json::CharReader> reader = std::unique_ptr<Json::CharReader>(Json::CharReaderBuilder().newCharReader());
379 0 : Json::Value obj;
380 0 : std::string errors;
381 0 : reader->parse(str.c_str(), str.c_str() + str.size(), &obj, &errors);
382 :
383 0 : const Json::Value &data = obj["data"];
384 :
385 0 : return data;
386 0 : }
387 :
388 : /*!
389 : * @brief Move to right priority cards when a player play one
390 : * @param difficulty level of difficulty when the card is played (0 to 4 for the 5 different field)
391 : */
392 0 : void GameWindow::moveToRightPriorityCards(int difficulty)
393 : {
394 0 : const Json::Value &dataNumber = openJsonFile("/hud/data-number.json");
395 :
396 : int xPos;
397 : int yPos;
398 :
399 0 : for (unsigned i = difficulty; i > 0; i--)
400 : {
401 0 : priorityCards[i - 1].difficulty = i;
402 0 : std::iter_swap(priorityCards.begin() + i, priorityCards.begin() + (i - 1));
403 : }
404 0 : priorityCards[0].difficulty = 0;
405 :
406 0 : for (int i = 0; i <= difficulty; i++)
407 : {
408 0 : xPos = dataNumber["priority-card-offset"].asFloat() * WINDOW_LENGTH * i + dataNumber["priority-card-first-offset"].asFloat() * WINDOW_LENGTH;
409 0 : yPos = priorityCards[i].texture->getSprite().getPosition().y;
410 0 : priorityCards[i].texture->getSprite().setPosition(xPos, yPos);
411 0 : priorityCards[i].movePriorityCardElements(dataNumber);
412 : }
413 0 : }
414 :
415 : /*!
416 : * @brief Detect when we click on a priority card or on the play button on priorityCard and make the action associated
417 : * @param cursorRect emplacement of the mouse
418 : * Example to use the winner window in this function:
419 : * setWinnerWindow("Lasso", "1. Tech-Wheel level >=24 \n2. More than 15 control pawns \n3. You are the best");
420 : */
421 0 : bool GameWindow::priorityCardClickAction(sf::Vector2i clickPosition)
422 : {
423 0 : std::string questionString;
424 0 : std::string nbOfBoxesOnPriorityCard;
425 : int newNumberOfBoxes;
426 :
427 0 : sf::FloatRect spriteArrowMoreBoxes = validateBoxesWindow->arrowMoreTexture->getSprite().getGlobalBounds();
428 0 : sf::FloatRect spriteArrowLessBoxes = validateBoxesWindow->arrowLessTexture->getSprite().getGlobalBounds();
429 0 : sf::FloatRect spriteValidateBoxesButton = validateBoxesWindow->doneTexture->getSprite().getGlobalBounds();
430 :
431 : // If we click on the done button to accept the number of boxes to play
432 0 : if (gameEnginePtr->intersectPointRect(clickPosition, spriteValidateBoxesButton) && validateBoxesWindow->isWindowActive)
433 : {
434 0 : validateBoxesWindow->isWindowActive = false;
435 0 : moveToRightPriorityCards(validateBoxesWindow->priorityCardPlayed);
436 0 : newNumberOfBoxes = validateBoxesWindow->nbOfBoxesMax - validateBoxesWindow->nbOfBoxesChosen;
437 0 : validateBoxesWindow->nbOfBoxesMax = newNumberOfBoxes;
438 0 : priorityCards[0].nbOfBoxesText->setString(std::to_string(newNumberOfBoxes) + " x");
439 :
440 0 : gameEnginePtr->handlePriorityCardPlay(
441 0 : validateBoxesWindow->priorityCardPlayedType,
442 0 : validateBoxesWindow->priorityCardPlayed,
443 0 : validateBoxesWindow->nbOfBoxesChosen);
444 :
445 0 : return true;
446 : }
447 :
448 : // if we click on the little arrow to add boxes
449 0 : if (gameEnginePtr->intersectPointRect(clickPosition, spriteArrowMoreBoxes) &&
450 0 : validateBoxesWindow->isWindowActive &&
451 0 : validateBoxesWindow->nbOfBoxesChosen < validateBoxesWindow->nbOfBoxesMax)
452 : {
453 0 : validateBoxesWindow->nbOfBoxesChosen++;
454 0 : validateBoxesWindow->chooseNumberOfBoxesButton->buttonText->setString(std::to_string(validateBoxesWindow->nbOfBoxesChosen)); // sent by the server
455 0 : return true;
456 : }
457 :
458 : // if we click on the little arrow to delete boxes
459 0 : if (gameEnginePtr->intersectPointRect(clickPosition, spriteArrowLessBoxes) &&
460 0 : validateBoxesWindow->isWindowActive &&
461 0 : validateBoxesWindow->nbOfBoxesChosen > 0)
462 : {
463 0 : validateBoxesWindow->nbOfBoxesChosen--;
464 0 : validateBoxesWindow->chooseNumberOfBoxesButton->buttonText->setString(std::to_string(validateBoxesWindow->nbOfBoxesChosen)); // sent by the server
465 0 : return true;
466 : }
467 :
468 0 : for (auto &priorityCard : priorityCards)
469 : {
470 0 : sf::FloatRect spriteCards = priorityCard.texture->getSprite().getGlobalBounds();
471 0 : sf::FloatRect spriteValidateButton = priorityCard.validateButton->buttonRect->getGlobalBounds();
472 :
473 : // if we click on the play button on priorityCards
474 0 : if (gameEnginePtr->intersectPointRect(clickPosition, spriteValidateButton) && priorityCard.isUp)
475 : {
476 0 : validateBoxesWindow->isWindowActive = true;
477 0 : validateBoxesWindow->priorityCardPlayed = priorityCard.difficulty;
478 0 : validateBoxesWindow->priorityCardPlayedType = priorityCard.type;
479 :
480 0 : nbOfBoxesOnPriorityCard = priorityCard.nbOfBoxesText->getString().substring(0, 1);
481 0 : validateBoxesWindow->nbOfBoxesChosen = std::stoi(nbOfBoxesOnPriorityCard); // sent by the server
482 0 : validateBoxesWindow->nbOfBoxesMax = std::stoi(nbOfBoxesOnPriorityCard); // sent by the server
483 :
484 0 : validateBoxesWindow->chooseNumberOfBoxesButton->buttonText->setString(nbOfBoxesOnPriorityCard); // sent by the server
485 0 : questionString = "You have " + nbOfBoxesOnPriorityCard + " boxes \nHow many boxes do you want to play?";
486 0 : validateBoxesWindow->title->setString(questionString);
487 0 : return true;
488 : }
489 :
490 : // if we click on a priorityCard card
491 0 : if (gameEnginePtr->intersectPointRect(clickPosition, spriteCards))
492 : {
493 0 : priorityCard.moveUpPriorityCard();
494 0 : return true;
495 : }
496 : }
497 :
498 0 : return false;
499 0 : }
500 :
501 : /*!
502 : * @brief Function that deteck where the user click and what to send to the engine
503 : * @param clickPosition is the position on the cursor when the user click
504 : * @brief Dectect click and actions to do after
505 : */
506 0 : bool GameWindow::clickAction(sf::Event &event, sf::Vector2i clickPosition, std::shared_ptr<bool> moveMode)
507 : {
508 0 : if (!*moveMode)
509 : {
510 0 : if (priorityCardClickAction(clickPosition))
511 : {
512 0 : return false;
513 : }
514 :
515 0 : if (onHexagonClick(clickPosition))
516 : {
517 0 : return false;
518 : }
519 : }
520 :
521 0 : if (gameEnginePtr->intersectPointRect(clickPosition, endOfRoundButton->buttonRect->getGlobalBounds()))
522 : {
523 0 : gameEnginePtr->handleEndTurnButton();
524 0 : return false;
525 : }
526 :
527 : // Check if the click position is inside the move map button
528 0 : if (gameEnginePtr->intersectPointRect(clickPosition, hudTextureToDisplay[INDEX_MAP_BUTTON].getSprite().getGlobalBounds()))
529 : {
530 0 : changeMouseCursor(event, moveMode);
531 0 : return false;
532 : }
533 :
534 : // Check if the click position is inside the chat button
535 0 : if (gameEnginePtr->intersectPointRect(clickPosition, hudTextureToDisplay[INDEX_CHAT_BUTTON].getSprite().getGlobalBounds()))
536 : {
537 0 : isChatOpen = !isChatOpen;
538 0 : return false;
539 : }
540 :
541 : // Check if the click position is inside the chat button
542 0 : if (gameEnginePtr->intersectPointRect(clickPosition, hudTextureToDisplay[INDEX_QUIT_BUTTON].getSprite().getGlobalBounds()))
543 : {
544 0 : return true;
545 : }
546 0 : return false;
547 : }
548 :
549 0 : void GameWindow::rotateTechWheel(int newLevel)
550 : {
551 0 : int newRotation = techWheelRotation[newLevel];
552 0 : hudTextureToDisplay[ARROW_INDEX].getSprite(0).setRotation(newRotation);
553 0 : }
554 :
555 : /*!
556 : * @brief Display text on the cards
557 : * @param cards pointer to the card you want to setUp the text
558 : * @param title text to be display on the top of the card
559 : * @param body text to be display on body of the card, float
560 : * @param titleFont Font that will be used for the titile of the card
561 : * @param bodyFont Font that will be used for the body of the card
562 : * @param titleTextSizeProportion Proportion of the title
563 : * @param bodyTextSizeProportion Proportion of the body
564 : */
565 0 : void GameWindow::setUpText(
566 : GraphicCard &card,
567 : std::string title,
568 : std::string body,
569 : sf::Font &titleFont,
570 : sf::Font &bodyFont,
571 : const Json::Value &dataNumber,
572 : float titleTextProportion,
573 : float bodyTextProportion)
574 : {
575 0 : int titleTextSize = titleTextProportion * WINDOW_LENGTH;
576 0 : int bodyTextSize = bodyTextProportion * WINDOW_LENGTH;
577 :
578 : // display the title on the card
579 0 : card.title = std::make_unique<sf::Text>(title, titleFont, titleTextSize);
580 0 : card.title->setStyle(sf::Text::Bold);
581 0 : card.title->setFillColor(TEXT_COLOR);
582 0 : auto titleSize = card.title->getLocalBounds();
583 0 : int xTitleOffset = (card.texture->getWidth() - titleSize.width) / 2;
584 0 : int xTitlePosition = card.texture->getSprite().getPosition().x + xTitleOffset;
585 0 : int yTitlePosition = card.texture->getSprite().getPosition().y;
586 0 : card.title->setPosition(xTitlePosition, yTitlePosition);
587 :
588 : // display the body on the card
589 0 : card.body = std::make_unique<sf::Text>(body, bodyFont, bodyTextSize);
590 :
591 : // to have the text on several lines without exceeding the card
592 0 : int countEndLine = 1;
593 0 : while (card.body->getLocalBounds().width > card.texture->getWidth() - CARD_BORDER) // 18 to not touch the black border
594 : {
595 :
596 0 : for (int i = countEndLine * NBR_CHAR_MAX_PER_LIGNE; i > 0; i--)
597 : {
598 0 : if ((char)body[i] == ' ')
599 : {
600 0 : body.replace(i, 1, "\n");
601 0 : countEndLine++;
602 0 : break;
603 : }
604 : }
605 0 : card.body->setString(body);
606 : }
607 :
608 0 : card.body->setFillColor(TEXT_COLOR);
609 0 : card.body->setLineSpacing(dataNumber["body-line-space"].asFloat());
610 0 : int xBodyOffset = dataNumber["body-x-proportion"].asFloat() * WINDOW_LENGTH;
611 0 : int yBodyOffset = dataNumber["body-y-proportion"].asFloat() * WINDOW_WIDTH;
612 0 : int xBodyPosition = card.texture->getSprite().getPosition().x + xBodyOffset;
613 0 : int yBodyPosition = card.texture->getSprite().getPosition().y + yBodyOffset;
614 0 : card.body->setPosition(xBodyPosition, yBodyPosition);
615 0 : }
616 :
617 0 : bool GameWindow::onHexagonClick(sf::Vector2i clickPosition)
618 : {
619 0 : bool isClickable = false;
620 0 : std::array<int, 2> hexagonOnClick = {0, 0};
621 0 : int minimumDistance = WINDOW_LENGTH;
622 :
623 0 : for (auto &mapTexture : mapTextureToDisplay)
624 : {
625 0 : for (unsigned j = 0; j < mapTexture.getSize(); j++)
626 : {
627 0 : if (validateBoxesWindow->isWindowActive || !gameEnginePtr->intersectPointRect(clickPosition, mapTexture.getSprite(j).getGlobalBounds()))
628 : {
629 0 : continue;
630 : }
631 0 : isClickable = true;
632 :
633 0 : int x = mapTexture.getSprite(j).getGlobalBounds().left;
634 0 : int y = mapTexture.getSprite(j).getGlobalBounds().top;
635 0 : int width = mapTexture.getSprite(j).getGlobalBounds().width;
636 0 : int height = mapTexture.getSprite(j).getGlobalBounds().height;
637 :
638 0 : int distance = sqrt(pow(x + width / 2 - clickPosition.x, 2) +
639 0 : pow(y + height / 2 - clickPosition.y, 2));
640 :
641 0 : if (distance < minimumDistance)
642 : {
643 :
644 0 : minimumDistance = distance;
645 0 : hexagonOnClick[1] = (int)((y - firstHexagonPosition[1])) / (int)((height * 3 / 4));
646 0 : hexagonOnClick[0] = (int)((x - firstHexagonPosition[0])) / (int)((width - 1));
647 : }
648 : }
649 : }
650 :
651 0 : if (isClickable)
652 : {
653 0 : gameEnginePtr->handleInformation(hexagonOnClick[0], hexagonOnClick[1]);
654 0 : return true;
655 : }
656 0 : return false;
657 : }
658 :
659 : /*!
660 : * @brief Load all the textures of the map
661 : */
662 0 : void GameWindow::loadMapTexture()
663 : {
664 0 : std::string hexagonImgPath = RESOURCES_PATH "/map/field/field-";
665 : std::array<std::string, 12> mapField = {"water", "grassland", "hill", "forest", "desert", "mountain",
666 : "wonder-everest", "wonder-galapagos", "wonder-kilimanjaro",
667 0 : "wonder-messa", "wonder-pantanal", "wonder-volcanic"};
668 :
669 0 : for (unsigned i{0}; i < mapField.size(); i++)
670 : {
671 0 : std::string mapElementPath = hexagonImgPath + mapField.at(i) + ".png";
672 0 : mapTextureToDisplay.emplace_back(mapElementPath);
673 0 : }
674 :
675 0 : for (unsigned i = 0; i < mapShared->getMapHeight(); i++)
676 : {
677 0 : for (unsigned j = 0; j < mapShared->getMapWidth(); j++)
678 : {
679 0 : int indexSprite = mapTextureToDisplay.at((int)(*mapShared)(j, i)->getFieldLevel()).getSize();
680 0 : mapTextureToDisplay.at((int)(*mapShared)(j, i)->getFieldLevel()).addSprite();
681 0 : mapTextureToDisplay.at((int)(*mapShared)(j, i)->getFieldLevel())
682 0 : .setSpritePosition(indexSprite, j, i, MAP_X_OFFSET, MAP_Y_OFFSET, {0, 0});
683 : }
684 : }
685 0 : }
686 :
687 : /*!
688 : * @brief Load all the textures of the elements
689 : */
690 0 : void GameWindow::loadElementTexture()
691 : {
692 0 : std::string folder_path = RESOURCES_PATH "/map/element/";
693 0 : std::vector<std::string> png_files;
694 0 : DIR *dir = opendir(folder_path.c_str());
695 :
696 : struct dirent *entry;
697 0 : while ((entry = readdir(dir)) != nullptr)
698 : {
699 :
700 0 : if (entry->d_name[0] == '.')
701 : {
702 0 : continue;
703 : }
704 :
705 0 : std::string filename = entry->d_name;
706 0 : if (filename.size() >= 4 && filename.substr(filename.size() - 4) == ".png")
707 : {
708 0 : png_files.push_back(filename);
709 : }
710 0 : }
711 :
712 0 : closedir(dir);
713 :
714 : // Affiche les noms de fichiers trouvés
715 0 : for (const std::string &filename : png_files)
716 : {
717 0 : std::string path = RESOURCES_PATH ELEMENT_PATH + filename;
718 0 : elementTextureToDisplay[path] = std::make_unique<TextureDisplayer>(path);
719 0 : }
720 0 : }
721 :
722 : /*!
723 : * @brief Update all the textures of the map
724 : */
725 0 : void GameWindow::updateElementTexture()
726 : {
727 0 : std::lock_guard<std::mutex> lock(updatePlayerMutex);
728 0 : for (auto &kv : elementTextureToDisplay)
729 : {
730 0 : kv.second->clearSprites();
731 : }
732 :
733 0 : for (unsigned i = 0; i < mapShared->getMapHeight(); i++)
734 : {
735 0 : for (unsigned j = 0; j < mapShared->getMapWidth(); j++)
736 : {
737 0 : selectElementToDisplay(j, i);
738 : }
739 : }
740 0 : }
741 :
742 0 : void GameWindow::selectElementToDisplay(int x, int y)
743 : {
744 0 : const Json::Value &elementData = openJsonFile("/map/elementPath.json");
745 0 : const Json::Value &resourceData = openJsonFile("/map/resourcePath.json");
746 0 : const Json::Value &stateCityData = openJsonFile("/map/stateCityPath.json");
747 :
748 0 : std::array<int, 2> hexSize = {mapTextureToDisplay.at(0).getWidth(), mapTextureToDisplay.at(0).getHeight()};
749 :
750 0 : for (unsigned k = 0; k < (*mapShared)(x, y)->getElements().size(); k++)
751 : {
752 0 : std::shared_ptr<variantElement> variant = (*mapShared)(x, y)->getElements()[k];
753 :
754 : int index;
755 0 : std::string path;
756 :
757 0 : std::visit([&index](auto &&arg)
758 0 : { index = (int)arg.getType(); },
759 0 : *variant);
760 :
761 0 : if (std::holds_alternative<shared::City>(*variant))
762 : {
763 0 : shared::City city = std::get<shared::City>(*variant);
764 :
765 0 : if (city.isStateCity)
766 : {
767 0 : path = stateCityData[(int)city.stateCityType]["path"].asString();
768 : }
769 : else
770 : {
771 0 : path = elementData[index]["path"].asString();
772 :
773 0 : path = path.substr(0, 20) + std::to_string(getPlayerNumber(city.player)) + (city.isMature ? "-mature" : (city.isCapital ? "-capital" : "-city")) + ".png";
774 : }
775 0 : }
776 0 : else if (std::holds_alternative<shared::ControlPawn>(*variant))
777 : {
778 0 : shared::ControlPawn pawn = std::get<shared::ControlPawn>(*variant);
779 :
780 0 : path = elementData[index]["path"].asString();
781 :
782 0 : path = path.substr(0, 20) + std::to_string(getPlayerNumber(pawn.player)) + (pawn.isReinforced() ? "-reinforced" : "") + ".png";
783 :
784 0 : }
785 0 : else if (std::holds_alternative<shared::Caravan>(*variant))
786 : {
787 0 : shared::Caravan caravan = std::get<shared::Caravan>(*variant);
788 :
789 0 : path = elementData[index]["path"].asString();
790 :
791 0 : path = path.substr(0, 20) + std::to_string(getPlayerNumber(caravan.player)) + path.substr(21);
792 0 : }
793 : else
794 : {
795 0 : path = elementData[index]["path"].asString();
796 : }
797 :
798 0 : path = RESOURCES_PATH + path;
799 :
800 0 : elementTextureToDisplay[path]->addSprite();
801 :
802 0 : elementTextureToDisplay[path]->setSpritePosition(elementTextureToDisplay[path]->getSize() - 1, x, y, firstHexagonPosition[0], firstHexagonPosition[1], hexSize);
803 0 : }
804 :
805 0 : if ((*mapShared)(x, y)->hexResource != nullptr)
806 : {
807 0 : shared::Resource resource = *(*mapShared)(x, y)->hexResource;
808 :
809 0 : int index = (int)resource.getType();
810 :
811 0 : std::string path = RESOURCES_PATH + resourceData[index]["path"].asString();
812 :
813 0 : elementTextureToDisplay[path]->addSprite();
814 :
815 0 : elementTextureToDisplay[path]->setSpritePosition(elementTextureToDisplay[path]->getSize() - 1, x, y, firstHexagonPosition[0], firstHexagonPosition[1], hexSize);
816 0 : }
817 0 : }
818 :
819 0 : int GameWindow::getPlayerNumber(std::string username)
820 : {
821 0 : for (unsigned i = 0; i < whoIsPlayingButtons.size(); i++)
822 : {
823 0 : std::string buttonText = whoIsPlayingButtons[i].buttonText->getString();
824 0 : if (username.compare(buttonText) == 0)
825 : {
826 0 : return i + 1;
827 : }
828 0 : }
829 0 : return 1;
830 : }
831 :
832 : /*!
833 : * @brief Get position of number of boxes and boxes on priority cards
834 : * @param boxXProportion proportion of the box on x axis
835 : * @param boxYProportion proportion of the box on y axis
836 : * @param priorityCard pointer to the card you want to setUp the text
837 : */
838 0 : sf::Vector2i GameWindow::getBoxesElementsPosition(float boxXProportion, float boxYProportion, GraphicCard &priorityCard)
839 : {
840 : int xBoxPos;
841 : int yBoxPos;
842 : int xBoxOffset;
843 : int yBoxOffset;
844 :
845 0 : xBoxOffset = boxXProportion * WINDOW_LENGTH;
846 0 : yBoxOffset = boxYProportion * WINDOW_WIDTH;
847 :
848 0 : xBoxPos = priorityCard.texture->getSprite().getPosition().x + xBoxOffset;
849 0 : yBoxPos = priorityCard.texture->getSprite().getPosition().y + yBoxOffset;
850 :
851 0 : return (sf::Vector2i(xBoxPos, yBoxPos));
852 : }
853 :
854 : /*!
855 : * @brief Load all the HUD textures
856 : */
857 0 : void GameWindow::loadHudTexture()
858 : {
859 :
860 0 : int rotation = 0;
861 0 : int priorityCardIndex = 0;
862 :
863 0 : const Json::Value &dataNumber = openJsonFile("/hud/data-number.json");
864 :
865 0 : backgroundTexture = std::make_unique<TextureDisplayer>(RESOURCES_PATH "/hud/background.png");
866 0 : backgroundTexture->addSprite();
867 0 : float backgroundScale = 1 / (float(backgroundTexture->getWidth()) / float(WINDOW_LENGTH));
868 0 : backgroundTexture->setHudSpritePosition(backgroundScale, WINDOW_LENGTH, WINDOW_WIDTH, rotation, priorityCardIndex);
869 :
870 0 : const Json::Value &data = openJsonFile("/hud/files.json");
871 :
872 0 : for (unsigned index = 0; index < data.size(); ++index)
873 : {
874 0 : hudTextureToDisplay.emplace_back(RESOURCES_PATH + data[index]["path"].asString());
875 0 : hudTextureToDisplay.back().addSprite();
876 0 : float scale = data[index]["scale"].asFloat() / (float(hudTextureToDisplay.back().getWidth()) / float(WINDOW_LENGTH));
877 0 : hudTextureToDisplay.back().setImageType((HudTextureType)index);
878 0 : hudTextureToDisplay.back().setHudSpritePosition(scale, WINDOW_LENGTH, WINDOW_WIDTH, data[index]["rotation"].asInt(), priorityCardIndex);
879 : }
880 :
881 : // load the priorityCard
882 0 : boxTexture = std::make_unique<TextureDisplayer>(RESOURCES_PATH "/hud/box.png");
883 0 : std::vector<int> numberOfBoxesPerCard = {2, 4, 2, 1, 0}; // sent by the server
884 0 : std::string boxString = "0";
885 :
886 0 : if (!titleFont.loadFromFile(RESOURCES_PATH "/font/EnchantedLand.otf"))
887 : {
888 0 : std::cerr << "Font not loaded" << std::endl;
889 : }
890 :
891 0 : if (!bodyFont.loadFromFile(RESOURCES_PATH "/font/MorrisRomanBlack.otf"))
892 : {
893 0 : std::cerr << "Font not loaded" << std::endl;
894 : }
895 :
896 0 : const Json::Value &priorityData = openJsonFile("/hud/priority-card.json");
897 :
898 0 : float priorityTitleTextProportion = dataNumber["priority-card-title-proportion"].asFloat();
899 0 : float priorityBodyTextProportion = dataNumber["priority-card-body-proportion"].asFloat();
900 :
901 0 : for (unsigned index = 0; index < priorityData.size(); ++index)
902 : {
903 0 : priorityCards.emplace_back(
904 0 : RESOURCES_PATH + priorityData[index]["path"].asString(),
905 : dataNumber,
906 0 : WINDOW_LENGTH,
907 0 : WINDOW_WIDTH,
908 : index,
909 0 : bodyFont);
910 :
911 : // title and body
912 :
913 0 : setUpText(
914 0 : priorityCards.back(),
915 0 : priorityData[index]["title"].asString(),
916 0 : priorityData[index]["body"][priorityCards.back().level].asString(),
917 0 : titleFont,
918 0 : bodyFont,
919 : dataNumber,
920 : priorityTitleTextProportion,
921 : priorityBodyTextProportion);
922 :
923 : // boxes
924 :
925 0 : boxString = std::to_string(numberOfBoxesPerCard[index]) + " x";
926 :
927 0 : priorityCards.back().nbOfBoxesText = std::make_unique<sf::Text>(
928 : boxString,
929 0 : titleFont,
930 0 : dataNumber["box-number-text-size"].asInt());
931 :
932 0 : sf::Vector2i boxNumberPosition = getBoxesElementsPosition(
933 0 : dataNumber["box-x-number-offset-proportion"].asFloat(),
934 0 : dataNumber["box-y-number-offset-proportion"].asFloat(),
935 0 : priorityCards.back());
936 :
937 0 : priorityCards.back().nbOfBoxesText->setPosition(boxNumberPosition.x, boxNumberPosition.y);
938 :
939 0 : boxTexture->addSprite();
940 0 : sf::Vector2i boxPosition = getBoxesElementsPosition(
941 0 : dataNumber["box-x-offset-proportion"].asFloat(),
942 0 : dataNumber["box-y-offset-proportion"].asFloat(),
943 0 : priorityCards.back());
944 0 : boxTexture->getSprite(index).setPosition(boxPosition.x, boxPosition.y);
945 : }
946 :
947 : // actionCard
948 0 : const Json::Value &actionCardData = openJsonFile("/hud/action-card.json");
949 0 : float actionTitleTextProportion = dataNumber["action-card-title-proportion"].asFloat();
950 0 : float actionBodyTextProportion = dataNumber["action-card-body-proportion"].asFloat();
951 :
952 0 : std::vector<int> actionCardOwned = {1, 3, 7}; // array that will be sent by shared
953 :
954 0 : for (unsigned index = 0; index < actionCardOwned.size(); ++index)
955 : {
956 0 : actionCardsToDisplay.emplace_back(
957 0 : RESOURCES_PATH + actionCardData[actionCardOwned[index]]["path"].asString(),
958 0 : ACTION_CARD_PROPORTION,
959 0 : WINDOW_LENGTH,
960 0 : WINDOW_WIDTH,
961 0 : actionCardOwned[index],
962 : index);
963 :
964 0 : std::string titleCardAction = actionCardData[actionCardOwned[index]]["type"].asString();
965 0 : std::string bodyCardAction = actionCardData[actionCardOwned[index]]["body"].asString();
966 :
967 0 : setUpText(
968 0 : actionCardsToDisplay.back(),
969 : titleCardAction,
970 : bodyCardAction,
971 0 : titleFont,
972 0 : bodyFont,
973 : dataNumber,
974 : actionTitleTextProportion,
975 : actionBodyTextProportion);
976 0 : }
977 :
978 : // rotation of the techWheel
979 0 : const Json::Value &dataRotation = openJsonFile("/hud/tech-wheel-rotation.json");
980 0 : for (unsigned index = 0; index < dataRotation.size(); ++index)
981 : {
982 0 : techWheelRotation[index] = dataRotation[index]["rotation"].asInt();
983 : }
984 :
985 : // text for the user
986 0 : std::string textForTheUserString = "";
987 0 : textForTheUser = std::make_shared<Button>(sf::Vector2f(dataNumber["text-for-user-size-x"].asFloat(), dataNumber["text-for-user-size-y"].asFloat()),
988 0 : sf::Vector2f(dataNumber["text-for-user-pos-x"].asFloat(), dataNumber["text-for-user-pos-y"].asFloat()),
989 : TEXT_FOR_USER_BUTTON_COLOR,
990 0 : false);
991 0 : textForTheUser->setText(dataNumber["text-for-user-string-size"].asInt(),
992 : sf::Vector2f(0, 0),
993 : textForTheUserString,
994 0 : bodyFont,
995 0 : dataNumber["text-for-user-max-char"].asInt());
996 0 : textForTheUser->buttonText->setFillColor(TEXT_FOR_USER_COLOR);
997 :
998 : // end of round button
999 0 : endOfRoundButton = std::make_unique<Button>(sf::Vector2f(dataNumber["end-of-round-size-x"].asFloat(), dataNumber["end-of-round-size-y"].asFloat()),
1000 0 : sf::Vector2f(dataNumber["text-for-user-pos-x"].asFloat(), dataNumber["end-of-round-pos-y"].asFloat()),
1001 : END_OF_TURN_BUTTON_COLOR,
1002 0 : false);
1003 0 : endOfRoundButton->setText(dataNumber["end-of-round-text-size"].asInt(),
1004 : sf::Vector2f(0, 0),
1005 : "End of turn",
1006 0 : bodyFont);
1007 0 : endOfRoundButton->buttonRect->setOutlineThickness(2.0f);
1008 0 : }
1009 :
1010 : /*!
1011 : * @brief Function to modify the text that is specified at a player
1012 : * @param text string: text we want to set
1013 : */
1014 0 : void GameWindow::modifyTextForUser(std::string text)
1015 : {
1016 0 : textForTheUser->buttonText->setString(text);
1017 0 : textForTheUser->centerText();
1018 0 : }
1019 :
1020 0 : void GameWindow::addPlayer(std::string username)
1021 : {
1022 0 : for (auto &button : whoIsPlayingButtons)
1023 : {
1024 0 : std::string buttonText = button.buttonText->getString();
1025 0 : if (!username.compare(buttonText))
1026 : {
1027 0 : return;
1028 : }
1029 0 : }
1030 :
1031 0 : whoIsPlayingButtons.emplace_back(
1032 0 : sf::Vector2f(75, 90 / 2),
1033 0 : sf::Vector2f(0, 0),
1034 0 : PLAYER_COLOR[whoIsPlayingButtons.size()],
1035 0 : false);
1036 :
1037 0 : whoIsPlayingButtons.back().setText(18, sf::Vector2f(0, 0), username, titleFont);
1038 :
1039 0 : for (unsigned i = 0; i < whoIsPlayingButtons.size(); i++)
1040 : {
1041 0 : whoIsPlayingButtons[i].buttonRect->setPosition(
1042 0 : (WINDOW_LENGTH - 75 * whoIsPlayingButtons.size() - 30 * (whoIsPlayingButtons.size() - 1)) / 2 + 105 * i,
1043 : 0);
1044 0 : whoIsPlayingButtons[i].centerText(true);
1045 : }
1046 : }
1047 :
1048 : /*!
1049 : * @brief Function that deteck where the user click and what to send to the engine
1050 : * @param timeSecond is a boolean used to
1051 : */
1052 0 : long GameWindow::getCurrentTime(bool timeSecond)
1053 : {
1054 0 : if (timeSecond)
1055 : {
1056 0 : return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
1057 : }
1058 : else
1059 : {
1060 0 : return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
1061 : }
1062 : }
1063 :
1064 : /*!
1065 : * @brief Function to set the winnee window at the end of the party
1066 : * @param winner string: who is the winner
1067 : * @param causes string: why the winner have won
1068 : */
1069 0 : void GameWindow::setWinnerWindow(std::string winner, std::string causes)
1070 : {
1071 :
1072 0 : winnerWindow->title->setString(winner + " is the winner !!!");
1073 0 : winnerWindow->body->setString(causes);
1074 0 : winnerWindow->centerText();
1075 0 : winnerWindow->isWindowActive = true;
1076 0 : }
|