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.

423 lines
14 KiB

  1. /**********************************************************************************************
  2. *
  3. * raylib - Advance Game template
  4. *
  5. * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload)
  6. *
  7. * Copyright (c) 2014-2018 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 SpriteFont 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 = LoadSpriteFontEx("resources/fonts/traveling_typewriter.ttf", 30, 250, 0);
  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 });
  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. // TODO: messageWords should be reseted every mission
  180. //memcpy(messageWords, 0, sizeof(Word)*MAX_MISSION_WORDS);
  181. for (int i = 0; i < msgLen; i++)
  182. {
  183. char c = missions[currentMission].msg[i];
  184. if (foundWord && (c == ' ' || c == '.'))
  185. {
  186. foundWord = false;
  187. messageWords[currentWord - 1].rec.width = (int)MeasureTextEx(fontMessage, SubText(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), 30, 0).x;
  188. messageWords[currentWord - 1].rec.height = fontMessage.baseSize;
  189. //TODO: Guardar en message
  190. strncpy(messageWords[currentWord - 1].text, SubText(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), i - wordInitPosX);
  191. }
  192. if (c == '@') // One word to change
  193. {
  194. foundWord = true;
  195. missions[currentMission].msg[i] = ' ';
  196. offsetX = (int)MeasureTextEx(fontMessage, SubText(missions[currentMission].msg, wordInitPosY, (i + 1) - wordInitPosY), 30, 0).x;
  197. messageWords[currentWord].rec.x = offsetX;
  198. messageWords[currentWord].rec.y = offsetY;
  199. wordInitPosX = i + 1;
  200. currentWord++;
  201. }
  202. else if (c == '/')
  203. {
  204. missions[currentMission].msg[i] = '\n';
  205. wordInitPosY = i;
  206. offsetY += (fontMessage.baseSize + fontMessage.baseSize/2); // raylib internal increment on line break...
  207. }
  208. }
  209. for (int i = 0; i < missions[currentMission].wordsCount; i++)
  210. {
  211. messageWords[i].id = -1; // Not required for message words, id is the array position
  212. // Recalculate words rectangles considering text offset on screen
  213. messageWords[i].rec.x += msgOffset.x;
  214. messageWords[i].rec.y += msgOffset.y;
  215. // Recalculate words rectangle considering new width height
  216. messageWords[i].rec.x -= (texWordsAtlas.width - messageWords[i].rec.width)/2;
  217. messageWords[i].rec.y -= ((texWordsAtlas.height / MAX_CODING_WORDS) - messageWords[i].rec.height)/2;
  218. //Recalculate width height
  219. messageWords[i].rec.width = texWordsAtlas.width;
  220. messageWords[i].rec.height = texWordsAtlas.height / MAX_CODING_WORDS;
  221. messageWords[i].hover = false; // Mouse hover detected
  222. messageWords[i].picked = false; // Mouse picked
  223. }
  224. }
  225. // Gameplay Screen Update logic
  226. void UpdateGameplayScreen(void)
  227. {
  228. UpdateMusicStream(musSpy);
  229. for (int i = 0; i < MAX_CODING_WORDS; i++)
  230. {
  231. if (CheckCollisionPointRec(GetMousePosition(), words[i].rec))
  232. {
  233. words[i].hover = true;
  234. if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
  235. {
  236. words[i].picked = true;
  237. PlaySound(fxGrab);
  238. }
  239. }
  240. else words[i].hover = false;
  241. if (words[i].picked)
  242. {
  243. for (int j = 0; j < missions[currentMission].wordsCount; j++)
  244. {
  245. if (CheckCollisionPointRec(GetMousePosition(), messageWords[j].rec)) messageWords[j].hover = true;
  246. else messageWords[j].hover = false;
  247. }
  248. if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
  249. {
  250. words[i].picked = false;
  251. for (int j = 0; j < missions[currentMission].wordsCount; j++)
  252. {
  253. messageWords[j].hover = false;
  254. if (CheckCollisionPointRec(GetMousePosition(), messageWords[j].rec))
  255. {
  256. PlaySound(fxPlace);
  257. words[i].rec.x = messageWords[j].rec.x;
  258. words[i].rec.y = messageWords[j].rec.y;
  259. if (messageWords[j].id != -1)
  260. {
  261. int id = messageWords[j].id;
  262. words[id].rec = words[id].iniRec;
  263. }
  264. messageWords[j].id = i;
  265. for (int k = 0; k < missions[currentMission].wordsCount; k++)
  266. {
  267. if (j != k && messageWords[j].id == messageWords[k].id)
  268. {
  269. messageWords[k].id = -1;
  270. break;
  271. }
  272. }
  273. break;
  274. }
  275. else
  276. {
  277. PlaySound(fxLeave);
  278. words[i].rec = words[i].iniRec;
  279. if (i == messageWords[j].id) messageWords[j].id = -1;
  280. }
  281. }
  282. }
  283. }
  284. // TODO: Move word picked with mouse
  285. if (words[i].picked)
  286. {
  287. words[i].rec.x = GetMouseX() - words[i].rec.width/2;
  288. words[i].rec.y = GetMouseY() - words[i].rec.height/2;
  289. // TODO: Check if label is placed in some mission word position
  290. //if (CheckCollisionRecs(words[i].rec))
  291. }
  292. else
  293. {
  294. //if (words[i].id != -1)
  295. }
  296. }
  297. canSend = true;
  298. for(int j = 0; j < missions[currentMission].wordsCount; j++)
  299. {
  300. if(messageWords[j].id == -1)
  301. {
  302. canSend = false;
  303. break;
  304. }
  305. }
  306. if (canSend && (IsKeyPressed(KEY_ENTER) || IsButtonPressed()))
  307. {
  308. finishScreen = true;
  309. }
  310. }
  311. // Gameplay Screen Draw logic
  312. void DrawGameplayScreen(void)
  313. {
  314. DrawTexture(texBackground, 0, 0, WHITE);
  315. DrawTextEx(fontMessage, missions[currentMission].msg, msgOffset, fontMessage.baseSize, 0, BLACK);
  316. for (int i = 0; i < missions[currentMission].wordsCount; i++)
  317. {
  318. Rectangle recLines = messageWords[i].rec;
  319. DrawRectangleLines(recLines.x, recLines.y, recLines.width, recLines.height, Fade(RED, 0.35f));
  320. if(messageWords[i].hover) DrawRectangleRec(messageWords[i].rec, Fade(RED, 0.30f));
  321. DrawText(FormatText("%i", messageWords[i].id), i*25, 0, 30, RED);
  322. }
  323. for (int i = 0; i < MAX_CODING_WORDS; i++)
  324. {
  325. if (words[i].picked) DrawTextureRec(texWordsAtlas, (Rectangle){ 0, i*35, 140, 35 }, (Vector2){ words[i].rec.x, words[i].rec.y }, MAROON);
  326. else if (words[i].hover) DrawTextureRec(texWordsAtlas, (Rectangle){ 0, i*35, 140, 35 }, (Vector2){ words[i].rec.x, words[i].rec.y }, RED);
  327. else DrawTextureRec(texWordsAtlas, (Rectangle){ 0, i*35, 140, 35 }, (Vector2){ words[i].rec.x, words[i].rec.y }, WHITE);
  328. }
  329. DrawTexturePro(texVignette, (Rectangle){0,0,texVignette.width, texVignette.height}, (Rectangle){0,0,GetScreenWidth(), GetScreenHeight()}, (Vector2){0,0}, 0, WHITE);
  330. if (canSend) DrawButton("enviar");
  331. }
  332. // Gameplay Screen Unload logic
  333. void UnloadGameplayScreen(void)
  334. {
  335. UnloadTexture(texBackground);
  336. UnloadTexture(texVignette);
  337. UnloadTexture(texWordsAtlas);
  338. UnloadSound(fxGrab);
  339. UnloadSound(fxLeave);
  340. UnloadSound(fxPlace);
  341. UnloadMusicStream(musSpy);
  342. free(missions);
  343. }
  344. // Gameplay Screen should finish?
  345. int FinishGameplayScreen(void)
  346. {
  347. return finishScreen;
  348. }