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.

412 lines
13 KiB

  1. /**********************************************************************************************
  2. *
  3. * raylib - Advance Game template
  4. *
  5. * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload)
  6. *
  7. * Copyright (c) 2014-2019 Ramon Santamaria (@raysan5)
  8. *
  9. * This software is provided "as-is", without any express or implied warranty. In no event
  10. * will the authors be held liable for any damages arising from the use of this software.
  11. *
  12. * Permission is granted to anyone to use this software for any purpose, including commercial
  13. * applications, and to alter it and redistribute it freely, subject to the following restrictions:
  14. *
  15. * 1. The origin of this software must not be misrepresented; you must not claim that you
  16. * wrote the original software. If you use this software in a product, an acknowledgment
  17. * in the product documentation would be appreciated but is not required.
  18. *
  19. * 2. Altered source versions must be plainly marked as such, and must not be misrepresented
  20. * as being the original software.
  21. *
  22. * 3. This notice may not be removed or altered from any source distribution.
  23. *
  24. **********************************************************************************************/
  25. #include "raylib.h"
  26. #include "screens.h"
  27. #include <string.h>
  28. #include <stdlib.h>
  29. #include <stdio.h>
  30. //#define MAX_CODING_WORDS 12
  31. //#define MAX_MISSION_WORDS 8
  32. #define MAX_LINE_CHAR 30
  33. /*
  34. // NOTE: Coding words are generic and the same words
  35. // are used for all missions,
  36. typedef enum CodingWords {
  37. POLLO = 0,
  38. CONEJO,
  39. HUEVO,
  40. NIDO,
  41. AIRE,
  42. ARMARIO,
  43. AGUJERO,
  44. COSA,
  45. WORD,
  46. } CodingWords;
  47. */
  48. static char *codingWords[MAX_CODING_WORDS] = {
  49. "pollo\0",
  50. "conejo\0",
  51. "huevo\0",
  52. "nido\0",
  53. "aire\0",
  54. "armario\0",
  55. "agujero\0",
  56. "platano\0",
  57. "pastel\0",
  58. "mercado\0",
  59. "raton\0",
  60. "melon\0"
  61. };
  62. // Words to be coded or coding words
  63. /*typedef struct Word {
  64. int id;
  65. Rectangle rec;
  66. Rectangle iniRec;
  67. bool hover;
  68. bool picked;
  69. char text[32]; // text
  70. } Word;*/
  71. /*
  72. // Mission information
  73. typedef struct Mission {
  74. int id;
  75. char brief[512]; // Mission briefing
  76. char key[32]; // Mission keyword
  77. char msg[256]; // Message to be coded
  78. int wordsCount; // Number of words to coded
  79. int sols[8]; // Solution code, depends on wordsCount
  80. } Mission;
  81. */
  82. //----------------------------------------------------------------------------------
  83. // Global Variables Definition (local to this module)
  84. //----------------------------------------------------------------------------------
  85. // Gameplay screen global variables
  86. static int framesCounter;
  87. static int finishScreen;
  88. static Texture2D texBackground;
  89. static Font fontMessage;
  90. static Texture2D texWordsAtlas;
  91. static Texture2D texVignette;
  92. static Sound fxGrab;
  93. static Sound fxPlace;
  94. static Sound fxLeave;
  95. static Music musSpy;
  96. static Word words[MAX_CODING_WORDS] = { 0 };
  97. // Hay que hacerlo global, para poder consultar el resultado desde la endingscreen
  98. //static Word messageWords[MAX_MISSION_WORDS] = { 0 };
  99. static Mission *missions = NULL;
  100. static bool canSend = false;
  101. Vector2 msgOffset = { 430, 300 };
  102. //----------------------------------------------------------------------------------
  103. // Gameplay Screen Functions Definition
  104. //----------------------------------------------------------------------------------
  105. // Gameplay Screen Initialization logic
  106. void InitGameplayScreen(void)
  107. {
  108. framesCounter = 0;
  109. finishScreen = 0;
  110. fontMessage = LoadFontEx("resources/fonts/traveling_typewriter.ttf", 30, 0, 250);
  111. texBackground = LoadTexture("resources/textures/message_background.png");
  112. texVignette = LoadTexture("resources/textures/message_vignette.png");
  113. fxGrab = LoadSound("resources/audio/fx_grab.ogg");
  114. fxPlace = LoadSound("resources/audio/fx_place.ogg");
  115. fxLeave = LoadSound("resources/audio/fx_leave.ogg");
  116. musSpy = LoadMusicStream("resources/audio/s_p_y.xm");
  117. PlayMusicStream(musSpy);
  118. #if defined(PLATFORM_WEB)
  119. #define WORD_ATLAS_FROM_FILE
  120. #endif
  121. #if defined(WORD_ATLAS_FROM_FILE)
  122. texWordsAtlas = LoadTexture("resources/textures/mission_words.png");
  123. #else
  124. // Generate coding words atlas directly from text
  125. Image imWordsBase = LoadImage("resources/textures/words_base.png");
  126. Image imWords = GenImageColor(imWordsBase.width, imWordsBase.height*MAX_CODING_WORDS, WHITE);
  127. for (int i = 0; i < MAX_CODING_WORDS; i++)
  128. {
  129. ImageDraw(&imWords, imWordsBase,
  130. (Rectangle){ 0, 0, imWordsBase.width, imWordsBase.height },
  131. (Rectangle){ 0, imWordsBase.height*i, imWordsBase.width, imWordsBase.height }, WHITE);
  132. ImageDrawTextEx(&imWords,(Vector2){ imWordsBase.width/2 - MeasureTextEx(fontMessage, codingWords[i],
  133. fontMessage.baseSize, 0).x/2, imWordsBase.height*i }, fontMessage, codingWords[i],
  134. fontMessage.baseSize, 0, BLACK);
  135. }
  136. texWordsAtlas = LoadTextureFromImage(imWords);
  137. UnloadImage(imWordsBase);
  138. UnloadImage(imWords);
  139. #endif
  140. // Initialize missions
  141. // WARNING: Some problem with imWords image generation (memory leak?) could cause
  142. // that loading missions before/after generation breaks game, on web is the other way round... :(
  143. missions = LoadMissions("resources/missions.txt");
  144. TraceLog(LOG_WARNING, "Words count %i", missions[currentMission].wordsCount);
  145. // Initialize coding words
  146. for (int i = 0; i < MAX_CODING_WORDS; i++)
  147. {
  148. words[i].id = -1; // Not placed anywhere
  149. words[i].rec.x = 110 + 940*(i/(MAX_CODING_WORDS/2));
  150. words[i].rec.y = 200 + 60*(i%(MAX_CODING_WORDS/2));
  151. words[i].rec.width = 140; // texWordsAtlas.width/MAX_MISSIONS
  152. words[i].rec.height = 35; // texWordsAtlas.height/MAX_MISSION_WORDS
  153. words[i].iniRec = words[i].rec;
  154. words[i].hover = false; // Mouse hover detected
  155. words[i].picked = false; // Mouse picked
  156. //words[i].text = ''; //codingWords[i]; // Fill text if required...
  157. }
  158. // Analize missions[currentMission].msg string for words!
  159. int msgLen = strlen(missions[currentMission].msg);
  160. // Add '/' each MAX_LINE_CHAR chars
  161. int currentLine = 1;
  162. int i = currentLine * MAX_LINE_CHAR;
  163. while (i < msgLen - 1)
  164. {
  165. if (missions[currentMission].msg[i] == ' ')
  166. {
  167. missions[currentMission].msg[i] = '/';
  168. currentLine++;
  169. i = currentLine*MAX_LINE_CHAR;
  170. }
  171. else i++;
  172. }
  173. int currentWord = 0;
  174. int offsetX = 0;
  175. int offsetY = 0;
  176. bool foundWord = false;
  177. int wordInitPosX = 0;
  178. int wordInitPosY = 0;
  179. for (int i = 0; i < msgLen; i++)
  180. {
  181. char c = missions[currentMission].msg[i];
  182. if (foundWord && (c == ' ' || c == '.'))
  183. {
  184. foundWord = false;
  185. messageWords[currentWord - 1].rec.width = (int)MeasureTextEx(fontMessage, TextSubtext(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), 30, 0).x;
  186. messageWords[currentWord - 1].rec.height = fontMessage.baseSize;
  187. strncpy(messageWords[currentWord - 1].text, TextSubtext(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), i - wordInitPosX);
  188. }
  189. if (c == '@') // One word to change
  190. {
  191. foundWord = true;
  192. missions[currentMission].msg[i] = ' ';
  193. offsetX = (int)MeasureTextEx(fontMessage, TextSubtext(missions[currentMission].msg, wordInitPosY, (i + 1) - wordInitPosY), 30, 0).x;
  194. messageWords[currentWord].rec.x = offsetX;
  195. messageWords[currentWord].rec.y = offsetY;
  196. wordInitPosX = i + 1;
  197. currentWord++;
  198. }
  199. else if (c == '/')
  200. {
  201. missions[currentMission].msg[i] = '\n';
  202. wordInitPosY = i;
  203. offsetY += (fontMessage.baseSize + fontMessage.baseSize/2); // raylib internal increment on line break...
  204. }
  205. }
  206. for (int i = 0; i < missions[currentMission].wordsCount; i++)
  207. {
  208. messageWords[i].id = -1; // Not required for message words, id is the array position
  209. // Recalculate words rectangles considering text offset on screen
  210. messageWords[i].rec.x += msgOffset.x;
  211. messageWords[i].rec.y += msgOffset.y;
  212. // Recalculate words rectangle considering new width height
  213. messageWords[i].rec.x -= (texWordsAtlas.width - messageWords[i].rec.width)/2;
  214. messageWords[i].rec.y -= ((texWordsAtlas.height / MAX_CODING_WORDS) - messageWords[i].rec.height)/2;
  215. //Recalculate width height
  216. messageWords[i].rec.width = texWordsAtlas.width;
  217. messageWords[i].rec.height = texWordsAtlas.height / MAX_CODING_WORDS;
  218. messageWords[i].hover = false; // Mouse hover detected
  219. messageWords[i].picked = false; // Mouse picked
  220. }
  221. }
  222. // Gameplay Screen Update logic
  223. void UpdateGameplayScreen(void)
  224. {
  225. UpdateMusicStream(musSpy);
  226. for (int i = 0; i < MAX_CODING_WORDS; i++)
  227. {
  228. if (CheckCollisionPointRec(GetMousePosition(), words[i].rec))
  229. {
  230. words[i].hover = true;
  231. if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
  232. {
  233. words[i].picked = true;
  234. PlaySound(fxGrab);
  235. }
  236. }
  237. else words[i].hover = false;
  238. if (words[i].picked)
  239. {
  240. for (int j = 0; j < missions[currentMission].wordsCount; j++)
  241. {
  242. if (CheckCollisionPointRec(GetMousePosition(), messageWords[j].rec)) messageWords[j].hover = true;
  243. else messageWords[j].hover = false;
  244. }
  245. if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
  246. {
  247. words[i].picked = false;
  248. for (int j = 0; j < missions[currentMission].wordsCount; j++)
  249. {
  250. messageWords[j].hover = false;
  251. if (CheckCollisionPointRec(GetMousePosition(), messageWords[j].rec))
  252. {
  253. PlaySound(fxPlace);
  254. words[i].rec.x = messageWords[j].rec.x;
  255. words[i].rec.y = messageWords[j].rec.y;
  256. if (messageWords[j].id != -1)
  257. {
  258. int id = messageWords[j].id;
  259. words[id].rec = words[id].iniRec;
  260. }
  261. messageWords[j].id = i;
  262. for (int k = 0; k < missions[currentMission].wordsCount; k++)
  263. {
  264. if (j != k && messageWords[j].id == messageWords[k].id)
  265. {
  266. messageWords[k].id = -1;
  267. break;
  268. }
  269. }
  270. break;
  271. }
  272. else
  273. {
  274. PlaySound(fxLeave);
  275. words[i].rec = words[i].iniRec;
  276. if (i == messageWords[j].id) messageWords[j].id = -1;
  277. }
  278. }
  279. }
  280. }
  281. // Move word picked with mouse
  282. if (words[i].picked)
  283. {
  284. words[i].rec.x = GetMouseX() - words[i].rec.width/2;
  285. words[i].rec.y = GetMouseY() - words[i].rec.height/2;
  286. }
  287. }
  288. canSend = true;
  289. for (int j = 0; j < missions[currentMission].wordsCount; j++)
  290. {
  291. if (messageWords[j].id == -1)
  292. {
  293. canSend = false;
  294. break;
  295. }
  296. }
  297. if (canSend && (IsKeyPressed(KEY_ENTER) || IsButtonPressed()))
  298. {
  299. finishScreen = true;
  300. }
  301. }
  302. // Gameplay Screen Draw logic
  303. void DrawGameplayScreen(void)
  304. {
  305. DrawTexture(texBackground, 0, 0, WHITE);
  306. DrawTextEx(fontMessage, missions[currentMission].msg, msgOffset, fontMessage.baseSize, 0, BLACK);
  307. for (int i = 0; i < missions[currentMission].wordsCount; i++)
  308. {
  309. Rectangle recLines = messageWords[i].rec;
  310. DrawRectangleLines(recLines.x, recLines.y, recLines.width, recLines.height, Fade(RED, 0.35f));
  311. if (messageWords[i].hover) DrawRectangleRec(messageWords[i].rec, Fade(RED, 0.30f));
  312. DrawText(FormatText("%i", messageWords[i].id), i*25, 0, 30, RED);
  313. }
  314. for (int i = 0; i < MAX_CODING_WORDS; i++)
  315. {
  316. if (words[i].picked) DrawTextureRec(texWordsAtlas, (Rectangle){ 0, i*35, 140, 35 }, (Vector2){ words[i].rec.x, words[i].rec.y }, MAROON);
  317. else if (words[i].hover) DrawTextureRec(texWordsAtlas, (Rectangle){ 0, i*35, 140, 35 }, (Vector2){ words[i].rec.x, words[i].rec.y }, RED);
  318. else DrawTextureRec(texWordsAtlas, (Rectangle){ 0, i*35, 140, 35 }, (Vector2){ words[i].rec.x, words[i].rec.y }, WHITE);
  319. }
  320. DrawTexturePro(texVignette, (Rectangle){0,0,texVignette.width, texVignette.height}, (Rectangle){0,0,GetScreenWidth(), GetScreenHeight()}, (Vector2){0,0}, 0, WHITE);
  321. if (canSend) DrawButton("enviar");
  322. }
  323. // Gameplay Screen Unload logic
  324. void UnloadGameplayScreen(void)
  325. {
  326. UnloadTexture(texBackground);
  327. UnloadTexture(texVignette);
  328. UnloadTexture(texWordsAtlas);
  329. UnloadSound(fxGrab);
  330. UnloadSound(fxLeave);
  331. UnloadSound(fxPlace);
  332. UnloadMusicStream(musSpy);
  333. free(missions);
  334. }
  335. // Gameplay Screen should finish?
  336. int FinishGameplayScreen(void)
  337. {
  338. return finishScreen;
  339. }