QUOTE: Let your heart guide you always.

freezo

A retro platform game

game.c (11545B)


      1#include <stdio.h>
      2#include <stdlib.h>
      3#include <string.h>
      4
      5#include "../lib/raylib.h"
      6#include "../include/entity.h"
      7#include "../include/game.h"
      8#include "../include/tile.h"
      9#include "../include/util.h"
     10#include "../include/player.h"
     11#include "../include/const.h"
     12#include "../include/level.h"
     13
     14game_t *game_create(void) {
     15  game_t *game = malloc(sizeof(game_t));
     16  *game = (game_t) {
     17    .state = STATE_GAME,
     18    .level = NULL,
     19    .quit = false,
     20    .defeat = false,
     21    .victory = false,
     22    .menu = menu_create(),
     23    .player = NULL,
     24    .tiles = NULL,
     25    .tiles_len = 0,
     26    .entities = NULL,
     27    .entities_len = 0,
     28    .effects = NULL,
     29    .effects_len = 0,
     30    .assets.tiles = texture_load("assets/tiles.png", SCALE),
     31    .assets.entities = texture_load("assets/entities.png", SCALE),
     32    .assets.font = texture_load("assets/font.png", SCALE - 1),
     33    .assets.images = texture_load("assets/images.png", SCALE),
     34    .assets.background = texture_load("assets/background.png", SCALE),
     35    .assets.track = LoadSound("assets/song.wav"),
     36    .assets.win = LoadSound("assets/win.wav"),
     37    .assets.lose = LoadSound("assets/lose.wav"),
     38    .assets.locked = LoadSound("assets/locked.wav"),
     39    .camera = (Camera2D) {
     40      .offset = (pos_t) { 0.0, 0.0 },
     41      .zoom = 1,
     42    },
     43    .xp = 0,
     44    .sceen_timer = 0,
     45    .door = NULL,
     46    .overlay = LoadRenderTexture(WINDOW_WIDTH, WINDOW_HEIGHT),
     47    .is_muted = false,
     48  };
     49  level_load(game, LEVEL_1);
     50  SetSoundVolume(game->assets.track, GAME_MUSIC_VOLUME);
     51  SetSoundVolume(game->assets.win, GAME_SOUND_VOLUME);
     52  SetSoundVolume(game->assets.lose, GAME_SOUND_VOLUME);
     53  SetSoundVolume(game->assets.locked, GAME_SOUND_VOLUME);
     54  return game;
     55}
     56
     57void game_update(game_t *game) {
     58  bool do_toggle_mute = false;
     59  if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) {
     60    pos_t mouse_pos = GetMousePosition();
     61    rect_t mouse_rect = {
     62      .x = mouse_pos.x,
     63      .y = mouse_pos.y,
     64      .width = 1,
     65      .height = 1,
     66    };
     67    rect_t mute_button_rect = {
     68      .x = TILE_WIDTH / 2.0,
     69      .y = WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0,
     70      .width = TILE_WIDTH,
     71      .height = TILE_HEIGHT,
     72    };
     73    rect_t pause_button_rect = {
     74      .x = WINDOW_WIDTH - TILE_WIDTH - TILE_WIDTH / 2.0,
     75      .y = WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0,
     76      .width = TILE_WIDTH,
     77      .height = TILE_HEIGHT,
     78    };
     79    if (rect_collide(mouse_rect, mute_button_rect)) {
     80      do_toggle_mute = true;
     81    }
     82    if (!game->victory && !game->defeat) {
     83      if (rect_collide(mouse_rect, pause_button_rect)) {
     84        switch (game->state) {
     85          case STATE_GAME:
     86            game->state = STATE_MENU;
     87            break;
     88          case STATE_MENU:
     89            game->state = STATE_GAME;
     90            break;
     91        }
     92      }
     93    }
     94  }
     95  if (IsKeyPressed(KEY_M)) {
     96    do_toggle_mute = true;
     97  }
     98  if (do_toggle_mute) {
     99    game->is_muted = !game->is_muted;
    100    if (game->is_muted) {
    101      SetSoundVolume(game->assets.track, 0.0f);
    102      SetSoundVolume(game->assets.win, 0.0f);
    103      SetSoundVolume(game->assets.lose, 0.0f);
    104      SetSoundVolume(game->assets.locked, 0.0f);
    105    }
    106    else {
    107      SetSoundVolume(game->assets.track, GAME_MUSIC_VOLUME);
    108      SetSoundVolume(game->assets.win, GAME_SOUND_VOLUME);
    109      SetSoundVolume(game->assets.lose, GAME_SOUND_VOLUME);
    110      SetSoundVolume(game->assets.locked, GAME_SOUND_VOLUME);
    111    }
    112  }
    113  if (!IsSoundPlaying(game->assets.track) && !game->victory && !game->defeat) {
    114    PlaySound(game->assets.track);
    115  }
    116  switch (game->state) {
    117    case STATE_MENU: {
    118      menu_update(game->menu, game);
    119      break;
    120    }
    121    case STATE_GAME: {
    122      if (!game->victory && !game->defeat) {
    123        if (IsKeyPressed(KEY_ESCAPE)) {
    124          game->state = STATE_MENU;
    125          game->menu->idx = 0;
    126        }
    127      }
    128      if (game->level != NULL) {
    129        // defeat
    130        if (game->defeat) {
    131          if (game->sceen_timer == 0) {
    132            StopSound(game->assets.track);
    133          }
    134          if (game->sceen_timer == 20) {
    135            PlaySound(game->assets.lose);
    136          }
    137          if (game->sceen_timer == GAME_SCEEN_TIMER) {
    138            level_e level = game->level->type;
    139            int max_health = game->player->max_health;
    140            max_health -= 2;
    141            if (max_health <= 0) {
    142              level = LEVEL_1;
    143              max_health = PLAYER_MAX_HEALTH;
    144            }
    145            level_unload(game);
    146            level_load(game, level);
    147            game->player->max_health = max_health;
    148            game->player->health = max_health;
    149            game->defeat = false;
    150            game->xp = 0;
    151            game->sceen_timer = 0;
    152          }
    153          else {
    154            game->sceen_timer++;
    155          }
    156        }
    157        // victory
    158        if (game->victory) {
    159          if (game->sceen_timer == 0) {
    160            StopSound(game->assets.track);
    161          }
    162          if (game->sceen_timer == 20) {
    163            PlaySound(game->assets.win);
    164          }
    165          if (game->sceen_timer == GAME_SCEEN_TIMER) {
    166            if (game->level->type < LEVELS) {
    167              level_e level = game->level->type;
    168              int max_health = game->player->max_health;
    169              level++;
    170              level_unload(game);
    171              level_load(game, level);
    172              game->player->max_health = max_health;
    173              game->player->health = max_health;
    174            }
    175            game->victory = false;
    176            game->sceen_timer = 0;
    177          }
    178          else {
    179            if (game->sceen_timer == 125) {
    180              game->door->is_locked = true;
    181            }
    182            game->sceen_timer++;
    183          }
    184        }
    185        if (!game->victory && !game->defeat) {
    186          player_update(game->player, game);
    187        }
    188        for (int i = 0; i < game->tiles_len; i++) {
    189          tile_update(game->tiles[i], game);
    190        }
    191        for (int i = 0; i < game->entities_len; i++) {
    192          int len = game->entities_len;
    193          entity_update(&game->entities[i], game);
    194          // check if entity was removed
    195          if (game->entities_len < len) {
    196            i--;
    197          }
    198        }
    199        for (int i = 0; i < game->effects_len; i++) {
    200          int len = game->effects_len;
    201          effect_update(game->effects[i], game);
    202          // check if enemy was removed
    203          if (game->effects_len < len) {
    204            i--;
    205          }
    206        }
    207        pos_t camera_pos = (pos_t) {
    208          .x = WINDOW_WIDTH / 2.0 - game->player->pos.x - PLAYER_WIDTH / 2.0,
    209          .y = WINDOW_HEIGHT / 2.0 - game->player->pos.y,
    210        };
    211        if (camera_pos.x > 0.0) {
    212          camera_pos.x = 0.0;
    213        }
    214        if (camera_pos.x + TILE_WIDTH * game->level->width < WINDOW_WIDTH) {
    215          camera_pos.x = WINDOW_WIDTH - TILE_WIDTH * game->level->width;
    216        }
    217        if (camera_pos.y > 0.0) {
    218          camera_pos.y = 0.0;
    219        }
    220        if (camera_pos.y + TILE_HEIGHT * game->level->height < WINDOW_HEIGHT) {
    221          camera_pos.y = WINDOW_HEIGHT - TILE_HEIGHT * game->level->height;
    222        }
    223        game->camera.offset = pos_snap(camera_pos);
    224      }
    225      break;
    226    }
    227  }
    228}
    229
    230void game_draw(game_t *game) {
    231  // render overlay
    232  BeginTextureMode(game->overlay);
    233  ClearBackground(BLACK);
    234  DrawRectangle(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, BLACK);
    235  BeginBlendMode(BLEND_SUBTRACT_COLORS);
    236  int radius = WINDOW_WIDTH;
    237  radius -= radius / 80.0 * game->sceen_timer;
    238  if (radius < PLAYER_HEIGHT) {
    239    radius = PLAYER_HEIGHT;
    240  }
    241  DrawCircle(game->player->pos.x + PLAYER_WIDTH / 2.0, WINDOW_HEIGHT - (game->player->pos.y + PLAYER_HEIGHT / 2.0), radius, WHITE);
    242  EndBlendMode();
    243  EndTextureMode();
    244  // render sceen
    245  BeginDrawing();
    246  ClearBackground((Color) { 5, 5, 5, 255 });
    247  if (game->level != LEVEL_NULL) {
    248    BeginMode2D(game->camera);
    249    for (int i = 0; i < game->tiles_len; i++) {
    250      tile_draw(game->tiles[i], game);
    251    }
    252    for (int i = 0; i < game->entities_len; i++) {
    253      entity_t *entity = &game->entities[i];
    254      if (entity->type == ENTITY_GATE || entity->type == ENTITY_DOOR) {
    255        entity_draw(entity, game);
    256      }
    257    }
    258    for (int i = 0; i < game->entities_len; i++) {
    259      entity_t *entity = &game->entities[i];
    260      if (entity->type != ENTITY_GATE && entity->type != ENTITY_DOOR) {
    261        entity_draw(entity, game);
    262      }
    263    }
    264    if (game->sceen_timer < 100) {
    265      player_draw(game->player, game);
    266    }
    267    for (int i = 0; i < game->effects_len; i++) {
    268      effect_draw(game->effects[i], game);
    269    }
    270    // draw overlay
    271    DrawTexture(game->overlay.texture, 0, 0, WHITE);
    272    EndMode2D();
    273    // draw interface
    274    int i = 0;
    275    for (i = 0; i < game->player->health / 2; i++) {
    276      DrawTextureRec(game->assets.tiles, texture_rect(0, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { TILE_WIDTH / 2.0 + i * (TILE_WIDTH + TILE_WIDTH / 4.0), TILE_WIDTH / 2.0 }, WHITE);
    277    }
    278    if (game->player->health % 2 != 0) {
    279      DrawTextureRec(game->assets.tiles, texture_rect(1, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { TILE_WIDTH / 2.0 + i * (TILE_WIDTH + TILE_WIDTH / 4.0), TILE_WIDTH / 2.0 }, WHITE);
    280      i++;
    281    }
    282    for (; i < game->player->max_health / 2; i++) {
    283      DrawTextureRec(game->assets.tiles, texture_rect(2, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { TILE_WIDTH / 2.0 + i * (TILE_WIDTH + TILE_WIDTH / 4.0), TILE_WIDTH / 2.0 }, WHITE);
    284    }
    285    char xp_text[10];
    286    snprintf(xp_text, 10, "%dXP", game->xp);
    287    pos_t xp_pos = (pos_t) {
    288      .x = WINDOW_WIDTH - TILE_WIDTH / 2.0,
    289      .y = TILE_WIDTH / 1.5,
    290    };
    291    text_draw(xp_pos, xp_text, TEXT_ALIGNMENT_RIGHT, game);
    292    char level_text[10];
    293    snprintf(level_text, 10, "L%d", game->level->type);
    294    pos_t level_pos = (pos_t) {
    295      .x = WINDOW_WIDTH - TILE_WIDTH / 2.0,
    296      .y = TILE_WIDTH / 1.5 + 25,
    297    };
    298    text_draw(level_pos, level_text, TEXT_ALIGNMENT_RIGHT, game);
    299  }
    300  if (game->state == STATE_MENU) {
    301    menu_draw(game->menu, game);
    302  }
    303  if (game->is_muted) {
    304    DrawTextureRec(game->assets.tiles, texture_rect(5, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { TILE_WIDTH / 2.0, WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0 }, WHITE);
    305  }
    306  else {
    307    DrawTextureRec(game->assets.tiles, texture_rect(4, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { TILE_WIDTH / 2.0, WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0 }, WHITE);
    308  }
    309  if (game->state == STATE_GAME) {
    310    if (game->victory || game->defeat) {
    311      DrawTextureRec(game->assets.tiles, texture_rect(8, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { WINDOW_WIDTH - TILE_WIDTH - TILE_WIDTH / 2.0, WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0 }, WHITE);
    312    }
    313    else {
    314      DrawTextureRec(game->assets.tiles, texture_rect(6, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { WINDOW_WIDTH - TILE_WIDTH - TILE_WIDTH / 2.0, WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0 }, WHITE);
    315    }
    316  }
    317  else {
    318    DrawTextureRec(game->assets.tiles, texture_rect(7, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { WINDOW_WIDTH - TILE_WIDTH - TILE_WIDTH / 2.0, WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0 }, WHITE);
    319  }
    320  EndDrawing();
    321}
    322
    323void game_free(game_t *game) {
    324  menu_free(game->menu);
    325  if (game->level != LEVEL_NULL) {
    326    level_unload(game);
    327  }
    328  UnloadTexture(game->assets.tiles);
    329  UnloadTexture(game->assets.entities);
    330  UnloadTexture(game->assets.font);
    331  UnloadTexture(game->assets.images);
    332  UnloadTexture(game->assets.background);
    333  UnloadSound(game->assets.track);
    334  UnloadSound(game->assets.win);
    335  UnloadSound(game->assets.lose);
    336  UnloadSound(game->assets.locked);
    337  UnloadRenderTexture(game->overlay);
    338  free(game);
    339}