QUOTE: Be your own kind of beautiful.

feat: Added doors & updated levels - freezo - A retro platform game

freezo

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

commit 34267decfdf74e85e5b74df2d07cfaf43c531275
parent daadafd033e5442fbd78ef33c5c3f96f52b36767
Author: Sophie <info@soophie.de>
Date:   Mon, 16 Dec 2024 13:16:44 +0100

feat: Added doors & updated levels

Diffstat:
Massets/background.png | 0
Massets/tiles.png | 0
Ainclude/door.h | 21+++++++++++++++++++++
Minclude/entity.h | 4++++
Asrc/door.c | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/entity.c | 21+++++++++++++++++++++
Msrc/game.c | 40+++++++++++++---------------------------
Msrc/level.c | 26+++++++++++++++++++++-----
Msrc/player.c | 34++++++++++++++++++++++++++++++----
9 files changed, 175 insertions(+), 36 deletions(-)

diff --git a/assets/background.png b/assets/background.png Binary files differ. diff --git a/assets/tiles.png b/assets/tiles.png Binary files differ. diff --git a/include/door.h b/include/door.h @@ -0,0 +1,21 @@ +#pragma once + +typedef struct Door door_t; + +#include "game.h" +#include "util.h" + +#define DOOR_COUNTDOWN 20 + +struct Door { + pos_t pos; + bool is_locked; + int countdown; +}; + +door_t *door_create(pos_t pos); +void door_update(door_t *door, game_t *game); +void door_draw(door_t *door, game_t *game); +void door_free(door_t *door); + +bool door_unlock(door_t *door, game_t *game); diff --git a/include/entity.h b/include/entity.h @@ -5,10 +5,12 @@ typedef struct Entity entity_t; #include "enemy.h" #include "gate.h" #include "game.h" +#include "door.h" typedef enum { ENTITY_ENEMY, ENTITY_GATE, + ENTITY_DOOR, } entity_e; struct Entity { @@ -16,7 +18,9 @@ struct Entity { union { enemy_t *enemy; gate_t *gate; + door_t *door; }; + bool is_bad; }; entity_t *entity_create(entity_e type); diff --git a/src/door.c b/src/door.c @@ -0,0 +1,65 @@ +#include <stdlib.h> + +#include "../include/door.h" +#include "../include/const.h" + +door_t *door_create(pos_t pos) { + door_t *door = malloc(sizeof(door_t)); + door->pos = pos; + door->is_locked = true; + door->countdown = 0; + return door; +} + +void door_update(door_t *door, game_t *game) { + (void) game; + if (door->countdown > 0) { + door->countdown--; + } +} + +void door_draw(door_t *door, game_t *game) { + if (door->is_locked) { + DrawTextureRec(game->assets.tiles, texture_rect(1, 2, TILE_WIDTH, TILE_HEIGHT), (pos_t) { door->pos.x, door->pos.y - TILE_HEIGHT }, WHITE); + DrawTextureRec(game->assets.tiles, texture_rect(1, 3, TILE_WIDTH, TILE_HEIGHT), door->pos, WHITE); + if (door->countdown > 0) { + pos_t lock_pos = { + .x = door->pos.x, + .y = door->pos.y - 2 * TILE_HEIGHT, + }; + DrawTextureRec(game->assets.tiles, texture_rect(3, 1, TILE_WIDTH, TILE_HEIGHT), lock_pos, WHITE); + } + } + else { + DrawTextureRec(game->assets.tiles, texture_rect(2, 2, TILE_WIDTH, TILE_HEIGHT), (pos_t) { door->pos.x, door->pos.y - TILE_HEIGHT }, WHITE); + DrawTextureRec(game->assets.tiles, texture_rect(2, 3, TILE_WIDTH, TILE_HEIGHT), door->pos, WHITE); + } +} + +void door_free(door_t *door) { + free(door); +} + +bool door_unlock(door_t *door, game_t *game) { + if (!door->is_locked) { + return true; + } + int enemies_len = 0; + bool gates_frozen = true; + for (int i = 0; i < game->entities_len; i++) { + if(game->entities[i].type == ENTITY_ENEMY) { + enemies_len++; + } + if(game->entities[i].type == ENTITY_GATE) { + if (!game->entities[i].gate->frozen) { + gates_frozen = false; + } + } + } + if (enemies_len == 0 && gates_frozen) { + door->is_locked = false; + return true; + } + door->countdown = DOOR_COUNTDOWN; + return false; +} diff --git a/src/entity.c b/src/entity.c @@ -3,10 +3,22 @@ #include "../include/entity.h" #include "../include/enemy.h" #include "../include/gate.h" +#include "../include/door.h" entity_t *entity_create(entity_e type) { entity_t *entity = malloc(sizeof(entity_t)); entity->type = type; + switch (entity->type) { + case ENTITY_ENEMY: + entity->is_bad = true; + break; + case ENTITY_GATE: + entity->is_bad = true; + break; + case ENTITY_DOOR: + entity->is_bad = false; + break; + } return entity; } @@ -18,6 +30,9 @@ void entity_update(entity_t *entity, game_t *game) { case ENTITY_GATE: gate_update(entity->gate, game); break; + case ENTITY_DOOR: + door_update(entity->door, game); + break; } } @@ -29,6 +44,9 @@ void entity_draw(entity_t *entity, game_t *game) { case ENTITY_GATE: gate_draw(entity->gate, game); break; + case ENTITY_DOOR: + door_draw(entity->door, game); + break; } } @@ -40,6 +58,9 @@ void entity_detach(entity_t *entity) { case ENTITY_GATE: gate_free(entity->gate); break; + case ENTITY_DOOR: + door_free(entity->door); + break; } } diff --git a/src/game.c b/src/game.c @@ -58,8 +58,9 @@ void game_update(game_t *game) { // defeat if (game->defeat) { if (game->sceen_timer == 100) { + level_e level = game->level->type; level_unload(game); - level_load(game, LEVEL_1); + level_load(game, level); game->defeat = false; game->xp = 0; game->sceen_timer = 0; @@ -69,34 +70,19 @@ void game_update(game_t *game) { } } // victory - int enemies_len = 0; - bool gates_frozen = true; - for (int i = 0; i < game->entities_len; i++) { - if(game->entities[i].type == ENTITY_ENEMY) { - enemies_len++; - } - if(game->entities[i].type == ENTITY_GATE) { - if (!game->entities[i].gate->frozen) { - gates_frozen = false; + if (game->victory) { + if (game->sceen_timer == 100) { + if (game->level->type < LEVELS) { + level_e level = game->level->type; + level++; + level_unload(game); + level_load(game, level); } + game->victory = false; + game->sceen_timer = 0; } - } - if (enemies_len == 0 && gates_frozen) { - game->victory = true; - if (game->victory) { - if (game->sceen_timer == 100) { - if (game->level->type < LEVELS) { - level_e level = game->level->type; - level++; - level_unload(game); - level_load(game, level); - } - game->victory = false; - game->sceen_timer = 0; - } - else { - game->sceen_timer++; - } + else { + game->sceen_timer++; } } if (!game->victory && !game->defeat) { diff --git a/src/level.c b/src/level.c @@ -14,12 +14,12 @@ const char *LEVEL_MAP_1 = { "........................" "........................" "......e......3.........." - "....xxxx................" + "....xxxxx..............." ".....b.................." "...........--...xxx....." "................e......." "...........2...--xxxxx.." - "....p..................." + "....p....u.............." "..xxxxxxxxx---xxxxxxxx.." "........................" "........................" @@ -30,7 +30,7 @@ const char *LEVEL_MAP_2 = { "........................" "......3....b............" "........................" - "........................" + "................u......." "...............xxxx....." "..o......s.e............" ".......xxxxxxtxxx--xx..." @@ -51,8 +51,8 @@ const char *LEVEL_MAP_3 = { "............-txx-......." ".............w.........." "............-w..-......." - "........s....w.........." - "..x---xxxxxxxtxxx--xx..." + "........s....w......u..." + "..x---xxxxxxxtxxx--xxx.." "....e........w....e....." "...---....2..w--txxx...." "....3........w..w......." @@ -176,6 +176,7 @@ void level_generate(game_t *game, const char *map, int width, int height) { game->entities[game->entities_len] = (entity_t) { .type = ENTITY_ENEMY, .enemy = enemy, + .is_bad = true, }; game->entities_len++; break; @@ -189,6 +190,21 @@ void level_generate(game_t *game, const char *map, int width, int height) { game->entities[game->entities_len] = (entity_t) { .type = ENTITY_GATE, .gate = gate, + .is_bad = true, + }; + game->entities_len++; + break; + } + case 'u': { + game->entities = realloc(game->entities, sizeof(enemy_t) * (game->entities_len + 1)); + door_t *door = door_create((pos_t) { + .x = x * TILE_WIDTH + (TILE_WIDTH - ENEMY_WIDTH) / 2.0, + .y = y * TILE_HEIGHT, + }); + game->entities[game->entities_len] = (entity_t) { + .type = ENTITY_DOOR, + .door = door, + .is_bad = false, }; game->entities_len++; break; diff --git a/src/player.c b/src/player.c @@ -248,6 +248,10 @@ void player_update(player_t *player, game_t *game) { if (entity->type == ENTITY_GATE && entity->gate->frozen) { continue; } + // ignore non-bad entities + if (!entity->is_bad) { + continue; + } rect_t entity_rect; switch (entity->type) { case ENTITY_ENEMY: @@ -256,6 +260,9 @@ void player_update(player_t *player, game_t *game) { case ENTITY_GATE: entity_rect = rect_from_pos((pos_t) { entity->gate->pos.x, entity->gate->pos.y - TILE_HEIGHT }, ENEMY_WIDTH, ENEMY_HEIGHT); break; + case ENTITY_DOOR: + entity_rect = rect_from_pos((pos_t) { entity->gate->pos.x, entity->gate->pos.y - TILE_HEIGHT }, ENEMY_WIDTH, ENEMY_HEIGHT); + break; } if (rect_collide(shooting_rect, entity_rect)) { float entity_x = entity_rect.x + entity_rect.width / 2.0; @@ -314,8 +321,8 @@ void player_update(player_t *player, game_t *game) { float tolerance = 0.0; if ( player->pos.x <= enemy->pos.x + ENEMY_WIDTH - tolerance && - player->pos.x + PLAYER_WIDTH >= enemy->pos.x + tolerance && - player->pos.y <= enemy->pos.y + ENEMY_HEIGHT - tolerance && + player->pos.x + PLAYER_WIDTH >= enemy->pos.x + tolerance && + player->pos.y <= enemy->pos.y + ENEMY_HEIGHT - tolerance && player->pos.y + PLAYER_HEIGHT >= enemy->pos.y + tolerance ) { if (player->held_enemy == NULL) { @@ -336,6 +343,25 @@ void player_update(player_t *player, game_t *game) { .y = player->pos.y - (player->sneaking ? 0.3 : 0.5) * TILE_HEIGHT, }; } + // detect door entering + if (holding) { + for (int i = 0; i < game->entities_len; i++) { + if (game->entities[i].type == ENTITY_DOOR) { + door_t *door = game->entities[i].door; + float tolerance = 0.0; + if ( + player->pos.x <= door->pos.x + ENEMY_WIDTH - tolerance && + player->pos.x + PLAYER_WIDTH >= door->pos.x + tolerance && + player->pos.y <= door->pos.y + ENEMY_HEIGHT - tolerance && + player->pos.y + PLAYER_HEIGHT >= door->pos.y + tolerance + ) { + if (door_unlock(door, game)) { + game->victory = true; + } + } + } + } + } // detect damage for (int i = 0; i < game->entities_len; i++) { if (game->entities[i].type == ENTITY_ENEMY) { @@ -344,8 +370,8 @@ void player_update(player_t *player, game_t *game) { float tolerance = TILE_WIDTH / 5.0; if ( player->pos.x <= enemy->pos.x + ENEMY_WIDTH - tolerance && - player->pos.x + PLAYER_WIDTH >= enemy->pos.x + tolerance && - player->pos.y <= enemy->pos.y + ENEMY_HEIGHT - tolerance && + player->pos.x + PLAYER_WIDTH >= enemy->pos.x + tolerance && + player->pos.y <= enemy->pos.y + ENEMY_HEIGHT - tolerance && player->pos.y + PLAYER_HEIGHT >= enemy->pos.y + tolerance ) { if (player->damage_timer == 0) {