Back

menu.c coverage

overall coverage: 65.9%

line# code(run#)
1 #include "menu.h"
2 #include "tiles.h"
3 #include "coordinates.h"
4 #include "input.h"
5
6 #include <SDL.h>
7 #include <SDL_image.h>
8 #include <SDL_ttf.h>
9 #include <stdbool.h>
10
11 #include "debugmalloc.h"
12
13 // Current state of menu
14 // x, y: coordinates of player, filename: current filename, can be for opening and saving
15 typedef struct MenuState
16 {
17 Coordinates crate;
18 Coordinates player;
19 char *filename;
20 int result;
21 SDL_Renderer *renderer;
22 SDL_Texture *tiles;
23 TTF_Font *font;
24 } MenuState;
25
26 // Render menu to renderer based on menu state
27 // Menu state must include proper renderer, tiles and font
28 static void renderMenu(MenuState *state)(14)
29 {
30 // easier to work with variables this way
31 SDL_Renderer *renderer = state->renderer;(14)
32 SDL_Texture *tiles = state->tiles;(14)
33 TTF_Font *font = state->font;(14)
34
35 SDL_RenderClear(renderer);(14)
36
37 // white color for texts
38 SDL_Color color = {255, 255, 255};(14)
39
40 // background floor
41 renderTiles(renderer, tiles, greyFloor, 0, 0, 19, 11);(14)
42
43 // option selector walls and floor
44 renderTiles(renderer, tiles, brownFloor, 1, 1, 6, 10);(14)
45 renderTiles(renderer, tiles, wall, 1, 1, 1, 10);(14)
46 renderTiles(renderer, tiles, wall, 2, 1, 5, 1);(14)
47 renderTiles(renderer, tiles, wall, 6, 1, 6, 10);(14)
48 renderTiles(renderer, tiles, wall, 2, 10, 5, 10);(14)
49
50 // option selector crate and targets
51 renderTileC(renderer, tiles, crate, state->crate);(14)
52
53 renderTile(renderer, tiles, target, 4, 4);(14)
54 renderTile(renderer, tiles, target, 4, 7);(14)
55 renderTile(renderer, tiles, target, 2, 2);(14)
56 renderTile(renderer, tiles, target, 5, 2);(14)
57 renderTile(renderer, tiles, target, 2, 9);(14)
58 renderTile(renderer, tiles, target, 5, 9);(14)
59
60 // option selector player
61 renderTileC(renderer, tiles, player, state->player);(14)
62
63 // render buttons to control player
64 renderTiles(renderer, tiles, greenFloor, 14, 5, 18, 9);(14)
65 renderTiles(renderer, tiles, wall, 14, 5, 18, 5);(14)
66 renderTiles(renderer, tiles, wall, 14, 9, 18, 9);(14)
67 renderTiles(renderer, tiles, wall, 14, 6, 14, 8);(14)
68 renderTiles(renderer, tiles, wall, 18, 6, 18, 8);(14)
69 renderTile(renderer, tiles, up, 16, 6);(14)
70 renderTile(renderer, tiles, down, 16, 8);(14)
71 renderTile(renderer, tiles, left, 15, 7);(14)
72 renderTile(renderer, tiles, right, 17, 7);(14)
73
74 // render texts
75 renderFont(renderer, font, color, "Sokoban", 10, 1, false, true);(14)
76
77 renderFont(renderer, font, color, "Játék kezdése", 7, 4, false, true);(14)
78 renderFont(renderer, font, color, "Szintszerkesztő", 7, 7, false, true);(14)
79 renderFont(renderer, font, color, "Kilépésch", 7, 9, false, true);(14)
80 renderFont(renderer, font, color, "Kilépésch", 7, 2, false, true);(14)
81
82 renderFont(renderer, font, color, "Told a ládát a megfelelő opció előtti célra!", 0, 11, false, true);(14)
83
84 SDL_RenderPresent(renderer);(14)
85 }(14)
86
87 // Process current position of crate and set menu result accordingly
88 // Returns true if crate movement is allowed
89 static bool processCratePosition(MenuState *state, int crateX, int crateY)(6)
90 {
91 if ((crateX == 2 || crateX == 5) && (crateY == 2 || crateY == 9)) // exit(6)
92 {
93 state->result = 0;(1)
94 return false;(1)
95 }
96 if (crateX == 4 && crateY == 4) // start game(5)
97 {
98 // call text input for filename
99 int result = textInput(state->renderer, state->tiles, state->font, "File neve", state->filename, 63);(1)
100 switch (result)(1)
101 {
102 case 0: // exit program(0)
103 state->result = 0;(0)
104 return false;(0)
105 break;
106 case 1: // file input successfull, return 1(1)
107 state->result = 1;(1)
108 return false;(1)
109 break;
110 case 2: // file input cancelled(0)
111 return false;(0)
112 break;
113 default:(0)
114 return false;(0)
115 break;
116 }
117 }
118 if (crateX == 4 && crateY == 7) // level edtor(4)
119 {
120 // call text input for filename
121 int result = textInput(state->renderer, state->tiles, state->font, "File neve", state->filename, 63);(0)
122 switch (result)(0)
123 {
124 case 0: // exit program(0)
125 state->result = 0;(0)
126 return false;(0)
127 break;
128 case 1: // file input successfull, return 1(0)
129 state->result = 2;(0)
130 return false;(0)
131 break;
132 case 2: // file input cancelled(0)
133 return false;(0)
134 break;
135 default:(0)
136 return false;(0)
137 break;
138 }
139 }
140 return true;(4)
141 }
142
143 // Process player movement
144 // Direction: 0 - left, 1 - up, 2 - right, 3 - down
145 // Returns true if rerender is needed
146 static bool processMovement(int dir, MenuState *state)(14)
147 {
148 // get coordinates for simpler code
149 Coordinates *player = &state->player;(14)
150 Coordinates *crate = &state->crate;(14)
151
152 switch (dir)(14)
153 {
154 case 0: // left(0)
155 if (player->x == crate->x + 1 && player->y == crate->y) // crate in front of player(0)
156 {
157 if (crate->x - 1 > 1) // crate can move(0)
158 {
159 if (processCratePosition(state, crate->x - 1, crate->y))(0)
160 {
161 crate->x--;(0)
162 player->x--;(0)
163 return true;(0)
164 }
165 return true;(0)
166 }
167 return false;(0)
168 }
169 if (player->x - 1 > 1) // player can move(0)
170 {
171 player->x--;(0)
172 return true;(0)
173 }
174 return false;(0)
175 case 1: // up(3)
176 if (player->y == crate->y + 1 && player->x == crate->x) // crate in front of player(3)
177 {
178 if (crate->y - 1 > 1) // crate can move(2)
179 {
180 if (processCratePosition(state, crate->x, crate->y - 1))(2)
181 {
182 crate->y--;(1)
183 player->y--;(1)
184 return true;(1)
185 }
186 return true;(1)
187 }
188 return false;(0)
189 }
190 if (player->y - 1 > 1) // player can move(1)
191 {
192 player->y--;(1)
193 return true;(1)
194 }
195 return false;(0)
196 case 2: // right(3)
197 if (player->x == crate->x - 1 && player->y == crate->y) // crate in front of player(3)
198 {
199 if (crate->x + 1 < 6) // crate can move(1)
200 {
201 if (processCratePosition(state, crate->x + 1, crate->y))(1)
202 {
203 crate->x++;(1)
204 player->x++;(1)
205 return true;(1)
206 }
207 return true;(0)
208 }
209 return false;(0)
210 }
211 if (player->x + 1 < 6) // player can move(2)
212 {
213 player->x++;(2)
214 return true;(2)
215 }
216 return false;(0)
217 case 3: // down(8)
218 if (player->y == crate->y - 1 && player->x == crate->x) // crate in front of player(8)
219 {
220 if (crate->y + 1 < 10) // crate can move(3)
221 {
222 if (processCratePosition(state, crate->x, crate->y + 1))(3)
223 {
224 crate->y++;(2)
225 player->y++;(2)
226 return true;(2)
227 }
228 return true;(1)
229 }
230 return false;(0)
231 }
232 if (player->y + 1 < 10) // player can move(5)
233 {
234 player->y++;(5)
235 return true;(5)
236 }
237 return false;(0)
238 default:(0)
239 return false;(0)
240 }
241 }
242
243 // Handles SDL event
244 // Returns true if rerender is needed
245 static bool handleEvent(SDL_Event event, MenuState *state)(38)
246 {
247 switch (event.type)(38)
248 {
249 case SDL_KEYDOWN:(14)
250 switch (event.key.keysym.scancode)(14)
251 {
252 case 0x50: // left arrow(0)
253 case 0x04: // letter A
254 return processMovement(0, state);(0)
255 case 0x52: // up arrow(3)
256 case 0x1A: // letter W
257 return processMovement(1, state);(3)
258 case 0x4F: // right arrow(3)
259 case 0x07: // letter D
260 return processMovement(2, state);(3)
261 case 0x51: // down arrow(8)
262 case 0x16: // letter S
263 return processMovement(3, state);(8)
264 default:(0)
265 return false;(0)
266 }
267 break;
268 case SDL_MOUSEBUTTONDOWN:(0)
269 if (clickTile(16, 6, event.button.x, event.button.y)) // up(0)
270 return processMovement(1, state);(0)
271 if (clickTile(16, 8, event.button.x, event.button.y)) // down(0)
272 return processMovement(3, state);(0)
273 if (clickTile(15, 7, event.button.x, event.button.y)) // left(0)
274 return processMovement(0, state);(0)
275 if (clickTile(17, 7, event.button.x, event.button.y)) // right(0)
276 return processMovement(2, state);(0)
277 return false;(0)
278 break;
279 case SDL_QUIT: // exit program(0)
280 state->result = 0;(0)
281 return false;(0)
282 default:(24)
283 return false;(24)
284 break;
285 }
286 }
287
288 // Do main menu, returns MenuResult struct on menu exit
289 // Must include proper tiles and font
290 // Result: 0 - exit program, 1 - play level, 2 - level editor
291 int mainMenu(SDL_Renderer *renderer, SDL_Texture *tiles, TTF_Font *font, char *filename)(2)
292 {
293 MenuState state;
294
295 // set renderer, tiles and font for state
296 // easier to pass to functions this way
297 state.renderer = renderer;(2)
298 state.tiles = tiles;(2)
299 state.font = font;(2)
300 state.filename = filename;(2)
301
302 // initialize state variables
303 state.result = -1;(2)
304 state.player.x = 3;(2)
305 state.player.y = 4;(2)
306 state.crate.x = 4;(2)
307 state.crate.y = 6;(2)
308
309 // render initialized state
310 renderMenu(&state);(2)
311
312 SDL_Event ev;
313 while (SDL_WaitEvent(&ev))(38)
314 {
315 bool rerender = handleEvent(ev, &state);(38)
316 if (state.result != -1) // if result was set(38)
317 {
318 return state.result; // return to main(2)
319 }
320 if (rerender) // if rerender is needed(36)
321 renderMenu(&state);(12)
322 }
323 }(0)