| 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) |