Line data Source code
1 : #include <client.hpp>
2 : #include <iostream>
3 : #include <json/json.h>
4 : #include <fstream>
5 : #include <string>
6 : #include <codecvt>
7 : #include <stdexcept>
8 :
9 : #define REFRESH_TIME 30
10 :
11 : #define BUTTON_TEXT_SIZE 25
12 :
13 : #define BUTTON_CREAT 1
14 : #define BUTTON_USERNAME 2
15 : #define BUTTON_SERVER 3
16 : #define BUTTON_PORT 4
17 : #define BUTTON_LOAD 4
18 : #define BUTTON_ID 5
19 : #define BUTTON_PLAYER 5
20 : #define BUTTON_SEED 6
21 : #define BUTTON_CONNECT 6
22 : #define BUTTON_START 7
23 :
24 : #define QUIT_OFFSET 10
25 : #define QUIT_SCALE 0.09375
26 :
27 : #define CREATE_GAME "new"
28 :
29 : #define ASCI_BEGIN 41
30 : #define ASCI_END 123
31 :
32 : #ifndef RESOURCES_PATH
33 : #define RESOURCES_PATH "../resources"
34 : #endif
35 :
36 : const sf::Color buttonColor = sf::Color(247, 200, 195);
37 :
38 : using namespace client;
39 :
40 : /*!
41 : * @brief Constructor
42 : *
43 : * Constructor of MenuWindow class
44 : */
45 0 : MenuWindow::MenuWindow()
46 : {
47 0 : currentMenu = &menuButtons;
48 0 : currentText = &menuTexts;
49 0 : }
50 :
51 : /*!
52 : * @brief Display all the menu on the screen
53 : */
54 0 : void MenuWindow::displayWindow()
55 : {
56 :
57 0 : gameEnginePtr->clientWindow->clear(sf::Color::Blue);
58 :
59 0 : gameEnginePtr->clientWindow->draw(backgroundTexture->getSprite());
60 0 : gameEnginePtr->clientWindow->draw(quitTexture->getSprite());
61 :
62 0 : for (unsigned i = 0; i < currentMenu->size(); i++)
63 : {
64 0 : gameEnginePtr->clientWindow->draw(*currentMenu->at(i).buttonRect);
65 0 : gameEnginePtr->clientWindow->draw(*currentMenu->at(i).buttonText);
66 : }
67 :
68 0 : for (unsigned i = 0; i < currentText->size(); i++)
69 : {
70 0 : gameEnginePtr->clientWindow->draw(currentText->at(i));
71 : }
72 :
73 0 : gameEnginePtr->clientWindow->display();
74 0 : }
75 :
76 : /*!
77 : * @brief Loop that look for events to happend and call displayWindow()
78 : * @param clientWindow is window that comes from the engine
79 : * @param quitGame is the function used to quit the menu, it is load as an attribut
80 : */
81 0 : void MenuWindow::startMenu()
82 : {
83 0 : loadMenuTexture();
84 0 : if (gameEnginePtr == nullptr)
85 : {
86 0 : return;
87 : }
88 :
89 0 : long lastUpdateTimer = getCurrentTime(false);
90 :
91 0 : while (gameEnginePtr->clientWindow->isOpen())
92 : {
93 :
94 0 : if (getCurrentTime(false) - lastUpdateTimer > (REFRESH_TIME))
95 : {
96 0 : displayWindow();
97 0 : lastUpdateTimer = getCurrentTime(false);
98 : }
99 :
100 : // handle events
101 : sf::Event event;
102 0 : while (gameEnginePtr->clientWindow->pollEvent(event))
103 : {
104 0 : if (menuEventHappened(event))
105 : {
106 0 : return;
107 : }
108 : }
109 : }
110 : }
111 :
112 : /*!
113 : * @brief Test events and do actions corresponding to the event
114 : * @param event pointer to the event
115 : */
116 0 : bool MenuWindow::menuEventHappened(sf::Event &event)
117 : {
118 :
119 0 : std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter;
120 0 : std::string utf8;
121 0 : sf::Vector2i clickPoint;
122 0 : bool isOnButton = false;
123 :
124 0 : switch (event.type)
125 : {
126 0 : case sf::Event::MouseButtonPressed:
127 :
128 0 : clickPoint = sf::Mouse::getPosition(*gameEnginePtr->clientWindow);
129 :
130 0 : for (unsigned i = 0; i < currentMenu->size(); i++)
131 : {
132 0 : if (clickAction(clickPoint, i, isOnButton))
133 : {
134 0 : return true;
135 : }
136 : }
137 :
138 0 : break;
139 :
140 0 : case sf::Event::TextEntered:
141 0 : if (event.text.unicode > ASCI_BEGIN && event.text.unicode < ASCI_END)
142 : {
143 0 : writeChar(converter.to_bytes(event.text.unicode));
144 : }
145 0 : break;
146 :
147 0 : case sf::Event::KeyPressed:
148 :
149 0 : switch (event.key.code)
150 : {
151 0 : case sf::Keyboard::BackSpace:
152 0 : deleteChar();
153 0 : break;
154 :
155 0 : default:
156 0 : break;
157 : }
158 :
159 0 : break;
160 :
161 0 : case sf::Event::Closed:
162 0 : gameEnginePtr->handleQuitMenu(true);
163 0 : return true;
164 :
165 0 : default:
166 0 : break;
167 : }
168 :
169 0 : return false;
170 0 : }
171 :
172 : /*!
173 : * @brief Do the action corresponding to a click on a particular button
174 : * @param clickPoint cursor coordonate
175 : * @param index index of the testing button
176 : */
177 0 : bool MenuWindow::clickAction(sf::Vector2i clickPoint, int index, bool isOnButton)
178 : {
179 0 : currentMenu->at(index).setInactive();
180 :
181 0 : if (gameEnginePtr->intersectPointRect(
182 : clickPoint,
183 0 : currentMenu->at(index).buttonRect->getGlobalBounds()))
184 : {
185 0 : isOnButton = currentMenu->at(index).clickButton();
186 : }
187 0 : if (gameEnginePtr->intersectPointRect(
188 : clickPoint,
189 0 : quitTexture->getSprite().getGlobalBounds()))
190 : {
191 0 : gameEnginePtr->handleQuitMenu(true);
192 0 : return true;
193 : }
194 0 : if (isOnButton && index == BUTTON_CREAT && currentMenu == &menuButtons)
195 : {
196 0 : currentMenu = &newGameButtons;
197 0 : currentText = &newGameTexts;
198 : }
199 0 : else if (isOnButton && index == BUTTON_CONNECT && currentMenu == &menuButtons)
200 : {
201 0 : if(menuButtons[BUTTON_ID]().size() == 6 && menuButtons[BUTTON_USERNAME]().size() != 0)
202 : {
203 0 : return connectToGame(menuButtons[BUTTON_ID]());
204 : }
205 0 : else if(menuButtons[BUTTON_ID]().size() != 6)
206 : {
207 0 : menuButtons[BUTTON_ID].buttonText->setString("Wrong");
208 0 : menuButtons[BUTTON_ID].centerText(false);
209 : }
210 0 : if (menuButtons[BUTTON_USERNAME]().size() == 0)
211 : {
212 0 : menuButtons[BUTTON_USERNAME].buttonText->setString("Write Username");
213 0 : menuButtons[BUTTON_USERNAME].centerText(false);
214 : }
215 :
216 : }
217 0 : else if (isOnButton && index == BUTTON_LOAD && currentMenu == &newGameButtons)
218 : {
219 0 : std::cout << "Load a Game: Not implemented today\n";
220 : }
221 0 : else if (isOnButton && index == BUTTON_START && currentMenu == &newGameButtons)
222 : {
223 0 : if (menuButtons[BUTTON_USERNAME]().size() == 0)
224 : {
225 0 : menuButtons[BUTTON_USERNAME].buttonText->setString("Write Username");
226 0 : menuButtons[BUTTON_USERNAME].centerText(false);
227 0 : return false;
228 : }
229 0 : return connectToGame(CREATE_GAME);
230 : }
231 0 : return false;
232 : }
233 :
234 : /*!
235 : * @brief connect to the server
236 : * @param gameID id of the new Game
237 : */
238 0 : bool MenuWindow::connectToGame(std::string gameID)
239 : {
240 0 : bool isConnected = gameEnginePtr->tryConnection(
241 : gameID,
242 0 : menuButtons[BUTTON_USERNAME](),
243 0 : menuButtons[BUTTON_SERVER](),
244 0 : menuButtons[BUTTON_PORT]());
245 :
246 0 : if (isConnected)
247 : {
248 0 : gameEnginePtr->handleQuitMenu(false);
249 0 : currentMenu = &menuButtons;
250 0 : currentText = &menuTexts;
251 0 : return true;
252 : }
253 0 : return false;
254 : }
255 :
256 : /*!
257 : * @brief Add a letter to the selected button
258 : * @param ch letter to add
259 : */
260 0 : void MenuWindow::writeChar(std::string ch)
261 : {
262 0 : for (unsigned i = 0; i < currentMenu->size(); i++)
263 : {
264 0 : std::string newString = currentMenu->at(i)();
265 0 : if (currentMenu->at(i).redBorder
266 0 : && i == BUTTON_PLAYER
267 0 : && currentMenu == &newGameButtons)
268 : {
269 0 : int numberOfPlayer = 0;
270 : try
271 : {
272 0 : numberOfPlayer = std::stoi(ch);
273 : }
274 0 : catch ( ... )
275 : {
276 0 : numberOfPlayer = -1;
277 0 : }
278 0 : if (numberOfPlayer > 1 && numberOfPlayer < 5)
279 : {
280 0 : currentMenu->at(i).buttonText->setString(ch);
281 0 : currentMenu->at(i).centerText(true);
282 : }
283 0 : return;
284 : }
285 0 : if (currentMenu->at(i).redBorder &&
286 0 : (int)((std::string)currentMenu->at(i)()).size() < currentMenu->at(i).maxTextSize)
287 : {
288 0 : currentMenu->at(i).addChar(ch);
289 0 : return;
290 : }
291 0 : }
292 : }
293 :
294 : /*!
295 : * @brief Delete a letter to the selected button
296 : */
297 0 : void MenuWindow::deleteChar()
298 : {
299 :
300 0 : for (auto &button : *currentMenu)
301 : {
302 0 : std::string newString = button();
303 0 : if (button.redBorder && newString.size() != 0 && button.maxTextSize != 0)
304 : {
305 0 : button.delChar();
306 : }
307 0 : }
308 0 : }
309 :
310 : /*!
311 : * @brief Load all the textures that will be display on the menu
312 : */
313 0 : void MenuWindow::loadMenuTexture()
314 : {
315 :
316 0 : backgroundTexture = std::make_unique<TextureDisplayer>(RESOURCES_PATH "/menu/background.png");
317 0 : backgroundTexture->addSprite();
318 0 : float backgroundScale = 1 / (float(backgroundTexture->getWidth()) / float(gameEnginePtr->clientWindow->getSize().x));
319 0 : backgroundTexture->setHudSpritePosition(backgroundScale, gameEnginePtr->clientWindow->getSize().x, gameEnginePtr->clientWindow->getSize().y, 0, 0);
320 :
321 0 : quitTexture = std::make_unique<TextureDisplayer>(RESOURCES_PATH "/hud/leave.png");
322 0 : quitTexture->addSprite();
323 0 : quitTexture->moveSpritePosition(QUIT_OFFSET, QUIT_OFFSET);
324 0 : quitTexture->getSprite().setScale(QUIT_SCALE, QUIT_SCALE);
325 :
326 :
327 0 : if (!titleMenuFont.loadFromFile(RESOURCES_PATH "/font/EnchantedLand.otf"))
328 : {
329 0 : std::cerr << "Font not loaded" << std::endl;
330 : }
331 :
332 0 : if (!menuFont.loadFromFile(RESOURCES_PATH "/font/MorrisRomanBlack.otf"))
333 : {
334 0 : std::cerr << "Font not loaded" << std::endl;
335 : }
336 :
337 0 : std::ifstream file(RESOURCES_PATH "/menu/menu.json");
338 0 : if (!file.is_open())
339 : {
340 0 : std::cerr << "Error while opening json ressources file /menu/menu.json" << std::endl;
341 0 : exit(1);
342 : }
343 0 : std::string str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
344 :
345 0 : std::unique_ptr<Json::CharReader> reader = std::unique_ptr<Json::CharReader>(Json::CharReaderBuilder().newCharReader());
346 0 : Json::Value obj;
347 0 : std::string errors;
348 0 : reader->parse(str.c_str(), str.c_str() + str.size(), &obj, &errors);
349 :
350 0 : loadText(obj["welcomeTexts"]);
351 0 : loadButton(obj["welcomeButtons"]);
352 :
353 0 : currentMenu = &newGameButtons;
354 0 : currentText = &newGameTexts;
355 :
356 0 : newGameButtons.push_back(menuButtons[BUTTON_USERNAME]);
357 0 : newGameButtons.push_back(menuButtons[BUTTON_SERVER]);
358 0 : newGameButtons.push_back(menuButtons[BUTTON_PORT]);
359 :
360 0 : loadText(obj["newGameTexts"]);
361 0 : loadButton(obj["newGameButtons"]);
362 :
363 0 : currentMenu = &menuButtons;
364 0 : currentText = &menuTexts;
365 0 : }
366 :
367 : /*!
368 : * @brief Load all the text of a Button from a JSon
369 : * @param data where the data is stored
370 : */
371 0 : void MenuWindow::loadText(Json::Value &data)
372 : {
373 0 : for (auto &dataMenu : data)
374 : {
375 0 : if(dataMenu["size"].asInt() == 200)
376 : {
377 0 : currentText->emplace_back(dataMenu["text"].asString(), titleMenuFont, dataMenu["size"].asInt());
378 : }
379 : else
380 : {
381 0 : currentText->emplace_back(dataMenu["text"].asString(), menuFont, dataMenu["size"].asInt());
382 : }
383 0 : if (dataMenu["bold"].asBool())
384 : {
385 0 : currentText->back().setStyle(sf::Text::Bold);
386 : }
387 0 : currentText->back().setFillColor(sf::Color::Black);
388 0 : currentText->back().setPosition(setXAxisButtonTextPosition(dataMenu["xOffset"].asFloat()),
389 0 : setYAxisButtonTextPosition(dataMenu["yOffset"].asFloat()));
390 : }
391 0 : }
392 :
393 : /*!
394 : * @brief Load all the button from a JSon
395 : * @param data where the data is stored
396 : */
397 0 : void MenuWindow::loadButton(Json::Value &data)
398 : {
399 0 : for (auto &dataMenu : data)
400 : {
401 0 : currentMenu->emplace_back(setButtonSize(dataMenu["width"].asFloat(), dataMenu["height"].asFloat()),
402 0 : setButtonPosition(dataMenu["x"].asFloat(), dataMenu["y"].asFloat()),
403 : buttonColor);
404 :
405 0 : currentMenu->back().setText(BUTTON_TEXT_SIZE, sf::Vector2f(0, 0), dataMenu["text"].asString(), menuFont, dataMenu["sizeMax"].asInt());
406 : }
407 0 : }
408 :
409 : /*!
410 : * @brief Calcul the x offset of the text of a button
411 : * @param offset x offset of the text
412 : */
413 0 : int MenuWindow::setXAxisButtonTextPosition(float offset)
414 : {
415 0 : return (int)(gameEnginePtr->clientWindow->getSize().x
416 0 : - offset
417 0 : * currentText->back().getLocalBounds().height
418 0 : - currentText->back().getLocalBounds().width);
419 : }
420 :
421 : /*!
422 : * @brief Calcul the y offset of the text of a button
423 : * @param offset y offset of the text
424 : */
425 0 : int MenuWindow::setYAxisButtonTextPosition(float offset)
426 : {
427 0 : return (int)(gameEnginePtr->clientWindow->getSize().y
428 0 : - offset
429 0 : * currentText->back().getLocalBounds().height);
430 : }
431 :
432 : /*!
433 : * @brief Calcul the size of a button
434 : * @param width value comming from the JSon file
435 : * @param height value comming from the JSon file
436 : */
437 0 : sf::Vector2f MenuWindow::setButtonSize(float width, float height)
438 : {
439 0 : return sf::Vector2f(width * menuTexts[0].getLocalBounds().width,
440 0 : height * menuTexts[0].getLocalBounds().height);
441 : }
442 :
443 : /*!
444 : * @brief Calcul the position of a button
445 : * @param x value comming from the JSon file
446 : * @param y value comming from the JSon file
447 : */
448 0 : sf::Vector2f MenuWindow::setButtonPosition(float x, float y)
449 : {
450 0 : return sf::Vector2f(menuTexts[0].getPosition().x + x * menuTexts[0].getLocalBounds().width,
451 0 : menuTexts[0].getPosition().y - y * menuTexts[0].getLocalBounds().height);
452 : }
453 :
454 : /*!
455 : * @brief Function that deteck where the user click and what to send to the engine
456 : * @param timeSecond is a boolean used to
457 : */
458 0 : long MenuWindow::getCurrentTime(bool timeSecond)
459 : {
460 0 : if (timeSecond)
461 : {
462 0 : return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
463 : }
464 : else
465 : {
466 0 : return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
467 : }
468 : }
|