QUOTE: Kindness is always fashionable.

game.c - freezo - A retro platform game

freezo

A retro platform game
git clone git@soophie.de:/srv/git/freezo
log | files | refs | readme

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 
     14 game_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 
     57 void 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 
    230 void 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 
    323 void 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 }