QUOTE: Be the change, make a difference.

feat: Added boss (unstable) & redesigned entity/background textures - freezo - A retro platform game

freezo

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

commit 0619481f3da52146179ce4c102f7011515b6c52c
parent cc01827ef453d3e9c4cd93c85f774f0aa6486746
Author: Sophie <info@soophie.de>
Date:   Mon, 16 Dec 2024 23:48:05 +0100

feat: Added boss (unstable) & redesigned entity/background textures

Diffstat:
Massets/background.png | 0
Massets/entities.png | 0
Aassets/locked.wav | 0
Ainclude/boss.h | 31+++++++++++++++++++++++++++++++
Minclude/entity.h | 3+++
Minclude/game.h | 2++
Minclude/tile.h | 3++-
Asrc/boss.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/enemy.c | 4++--
Msrc/entity.c | 12++++++++++++
Msrc/game.c | 10++++++----
Msrc/level.c | 25+++++++++++++++++++++----
Msrc/player.c | 14++++++++++----
Msrc/tile.c | 6+++++-
Mtodo.txt | 2+-
15 files changed, 153 insertions(+), 17 deletions(-)

diff --git a/assets/background.png b/assets/background.png Binary files differ. diff --git a/assets/entities.png b/assets/entities.png Binary files differ. diff --git a/assets/locked.wav b/assets/locked.wav Binary files differ. diff --git a/include/boss.h b/include/boss.h @@ -0,0 +1,31 @@ +#pragma once + +typedef struct Boss boss_t; + +#include "game.h" +#include "util.h" + +typedef enum { + BOSS_TEST, +} boss_e; + +struct Boss { + pos_t pos; + boss_e type; + bool on_ground; + float velocity; + float gravity; + int dir; + bool stunned; + bool frozen; + int frozen_timer; + float fall_height; + fz_timer_t *timer_walking; + fz_timer_t *timer_sneaking; +}; + +boss_t *boss_create(pos_t pos, boss_e type); +void boss_update(boss_t *boss, game_t *game); +void boss_draw(boss_t *boss, game_t *game); +void boss_kill(boss_t *boss, game_t *game); +void boss_free(boss_t *boss); diff --git a/include/entity.h b/include/entity.h @@ -6,11 +6,13 @@ typedef struct Entity entity_t; #include "gate.h" #include "game.h" #include "door.h" +#include "boss.h" typedef enum { ENTITY_ENEMY, ENTITY_GATE, ENTITY_DOOR, + ENTITY_BOSS, } entity_e; struct Entity { @@ -19,6 +21,7 @@ struct Entity { enemy_t *enemy; gate_t *gate; door_t *door; + boss_t *boss; }; bool is_bad; }; diff --git a/include/game.h b/include/game.h @@ -25,6 +25,7 @@ struct Assets { Texture2D images; Texture2D background; Sound track; + Sound locked; }; struct Game { @@ -46,6 +47,7 @@ struct Game { int xp; int sceen_timer; door_t *door; + RenderTexture2D overlay; }; game_t *game_create(void); diff --git a/include/tile.h b/include/tile.h @@ -15,7 +15,8 @@ typedef enum { TILE_SNOW, TILE_SAND, TILE_BG_CHANDELIER, - TILE_BG_WINDOW, + TILE_BG_WINDOW_1, + TILE_BG_WINDOW_2, TILE_BG_BANNER, TILE_BG_BRICK_1, TILE_BG_BRICK_2, diff --git a/src/boss.c b/src/boss.c @@ -0,0 +1,58 @@ +#include <stdlib.h> +#include <math.h> + +#include "../include/boss.h" +#include "../include/const.h" +#include "../include/entity.h" +#include "../include/effect.h" + +boss_t *boss_create(pos_t pos, boss_e type) { + boss_t *boss = malloc(sizeof(boss_t)); + *boss = (boss_t) { + .pos = pos, + .type = type, + .on_ground = false, + .velocity = 0.0, + .gravity = 0.0, + .dir = 1, + .stunned = false, + .frozen = false, + .frozen_timer = 0, + .fall_height = 0.0, + .timer_walking = fz_timer_create(9, 8), + .timer_sneaking = fz_timer_create(9, 16), + }; + return boss; +} + +void boss_update(boss_t *boss, game_t *game) { + (void) boss; + (void) game; +} + +void boss_draw(boss_t *boss, game_t *game) { + pos_t pos = pos_snap(boss->pos); + DrawTextureRec(game->assets.entities, texture_rect_v(10, 6, 2, 1, ENEMY_WIDTH, ENEMY_HEIGHT), pos, WHITE); +} + +void boss_kill(boss_t *boss, game_t *game) { + for (int i = 0; i < game->entities_len; i++) { + if (game->entities[i].type == ENTITY_BOSS && game->entities[i].boss == boss) { + if (i + 1 < game->entities_len) { + for (int j = i; j < game->entities_len - 1; j++) { + game->entities[j] = game->entities[j + 1]; + } + } + game->entities_len--; + game->entities = realloc(game->entities, sizeof(entity_t) * game->entities_len); + game->xp += 10; + break; + } + } +} + +void boss_free(boss_t *boss) { + fz_timer_free(boss->timer_walking); + fz_timer_free(boss->timer_sneaking); + free(boss); +} diff --git a/src/enemy.c b/src/enemy.c @@ -171,10 +171,10 @@ void enemy_draw(enemy_t *enemy, game_t *game) { int frame_walking = fz_timer_get(enemy->timer_walking); int frame_sneaking = fz_timer_get(enemy->timer_sneaking); if (enemy->dir > 0) { - DrawTextureRec(game->assets.entities, texture_rect(enemy->stunned ? frame_sneaking : frame_walking, 8, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE); + DrawTextureRec(game->assets.entities, texture_rect(enemy->stunned ? frame_sneaking : frame_walking, 10, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE); } else { - DrawTextureRec(game->assets.entities, texture_rect(enemy->stunned ? frame_sneaking : frame_walking, 9, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE); + DrawTextureRec(game->assets.entities, texture_rect(enemy->stunned ? frame_sneaking : frame_walking, 11, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE); } if (enemy->frozen) { if (enemy->frozen_timer <= 1200) { diff --git a/src/entity.c b/src/entity.c @@ -18,6 +18,9 @@ entity_t *entity_create(entity_e type) { case ENTITY_DOOR: entity->is_bad = false; break; + case ENTITY_BOSS: + entity->is_bad = true; + break; } return entity; } @@ -33,6 +36,9 @@ void entity_update(entity_t *entity, game_t *game) { case ENTITY_DOOR: door_update(entity->door, game); break; + case ENTITY_BOSS: + boss_update(entity->boss, game); + break; } } @@ -47,6 +53,9 @@ void entity_draw(entity_t *entity, game_t *game) { case ENTITY_DOOR: door_draw(entity->door, game); break; + case ENTITY_BOSS: + boss_draw(entity->boss, game); + break; } } @@ -61,6 +70,9 @@ void entity_detach(entity_t *entity) { case ENTITY_DOOR: door_free(entity->door); break; + case ENTITY_BOSS: + boss_free(entity->boss); + break; } } diff --git a/src/game.c b/src/game.c @@ -33,6 +33,7 @@ game_t *game_create(void) { .assets.images = texture_load("assets/images.png", SCALE), .assets.background = texture_load("assets/background.png", SCALE), .assets.track = LoadSound("assets/track.wav"), + .assets.locked = LoadSound("assets/locked.wav"), .camera = (Camera2D) { .offset = (pos_t) { 0.0, 0.0 }, .zoom = 1, @@ -40,6 +41,7 @@ game_t *game_create(void) { .xp = 0, .sceen_timer = 0, .door = NULL, + .overlay = LoadRenderTexture(WINDOW_WIDTH, WINDOW_HEIGHT), }; level_load(game, LEVEL_1); return game; @@ -137,8 +139,7 @@ void game_update(game_t *game) { void game_draw(game_t *game) { // render overlay - RenderTexture2D overlay_texture = LoadRenderTexture(WINDOW_WIDTH, WINDOW_HEIGHT); - BeginTextureMode(overlay_texture); + BeginTextureMode(game->overlay); ClearBackground(BLACK); DrawRectangle(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, BLACK); BeginBlendMode(BLEND_SUBTRACT_COLORS); @@ -168,7 +169,7 @@ void game_draw(game_t *game) { effect_draw(game->effects[i], game); } // draw overlay - DrawTexture(overlay_texture.texture, 0, 0, WHITE); + DrawTexture(game->overlay.texture, 0, 0, WHITE); EndMode2D(); // draw interface int i = 0; @@ -201,7 +202,6 @@ void game_draw(game_t *game) { menu_draw(game->menu, game); } EndDrawing(); - UnloadRenderTexture(overlay_texture); } void game_free(game_t *game) { @@ -215,5 +215,7 @@ void game_free(game_t *game) { UnloadTexture(game->assets.images); UnloadTexture(game->assets.background); UnloadSound(game->assets.track); + UnloadSound(game->assets.locked); + UnloadRenderTexture(game->overlay); free(game); } diff --git a/src/level.c b/src/level.c @@ -9,7 +9,7 @@ const char *LEVEL_MAP_1 = { "........................" "........................" - "........k.......o......." + "........k.......O......." "..1....................." "........................" "........................" @@ -32,9 +32,9 @@ const char *LEVEL_MAP_2 = { "........................" "................u......." "...............xxxx....." - "..o......s.e............" + ".........s.e............" ".......xxxxxxtxxx--xx..." - ".............w.........." + "...o.........w.........." ".........2...w...--.1..." ".............w.........." ".........----x...--....." @@ -140,8 +140,11 @@ void level_generate(game_t *game, const char *map, int width, int height) { case 'k': tile->type = TILE_BG_CHANDELIER; break; + case 'O': + tile->type = TILE_BG_WINDOW_1; + break; case 'o': - tile->type = TILE_BG_WINDOW; + tile->type = TILE_BG_WINDOW_2; break; case 'b': tile->type = TILE_BG_BANNER; @@ -209,6 +212,20 @@ void level_generate(game_t *game, const char *map, int width, int height) { game->entities_len++; break; } + case 'z': { + game->entities = realloc(game->entities, sizeof(enemy_t) * (game->entities_len + 1)); + boss_t *boss = boss_create((pos_t) { + .x = x * TILE_WIDTH + (TILE_WIDTH - ENEMY_WIDTH) / 2.0, + .y = y * TILE_HEIGHT, + }, BOSS_TEST); + game->entities[game->entities_len] = (entity_t) { + .type = ENTITY_BOSS, + .boss = boss, + .is_bad = true, + }; + game->entities_len++; + break; + } default: break; } diff --git a/src/player.c b/src/player.c @@ -263,6 +263,9 @@ void player_update(player_t *player, game_t *game) { 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; + case ENTITY_BOSS: + entity_rect = rect_from_pos((pos_t) { entity->gate->pos.x, entity->gate->pos.y - TILE_HEIGHT }, 2 * ENEMY_WIDTH, ENEMY_HEIGHT); + break; } if (rect_collide(shooting_rect, entity_rect)) { float entity_x = entity_rect.x + entity_rect.width / 2.0; @@ -359,6 +362,9 @@ void player_update(player_t *player, game_t *game) { game->door = door; game->victory = true; } + else { + PlaySound(game->assets.locked); + } } } } @@ -420,18 +426,18 @@ void player_draw(player_t *player, game_t *game) { if (player->damage_timer / 4 % 2 == 0) { if (player->sneaking) { if (player->dir > 0) { - DrawTextureRec(game->assets.entities, texture_rect(frame, 6, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE); + DrawTextureRec(game->assets.entities, texture_rect(frame, 14, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE); } else { - DrawTextureRec(game->assets.entities, texture_rect(frame, 7, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE); + DrawTextureRec(game->assets.entities, texture_rect(frame, 15, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE); } } else { if (player->dir > 0) { - DrawTextureRec(game->assets.entities, texture_rect(frame, 4, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE); + DrawTextureRec(game->assets.entities, texture_rect(frame, 12, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE); } else { - DrawTextureRec(game->assets.entities, texture_rect(frame, 5, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE); + DrawTextureRec(game->assets.entities, texture_rect(frame, 13, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE); } } if (player->shooting) { diff --git a/src/tile.c b/src/tile.c @@ -58,10 +58,14 @@ void tile_draw(tile_t *tile, game_t *game) { DrawTextureRec(game->assets.background, texture_rect_v(3, 5, 4, 3, TILE_WIDTH, TILE_HEIGHT), tile->pos, WHITE); break; } - case TILE_BG_WINDOW: { + case TILE_BG_WINDOW_1: { DrawTextureRec(game->assets.background, texture_rect_v(8, 0, 4, 6, TILE_WIDTH, TILE_HEIGHT), tile->pos, WHITE); break; } + case TILE_BG_WINDOW_2: { + DrawTextureRec(game->assets.background, texture_rect_v(8, 7, 3, 4, TILE_WIDTH, TILE_HEIGHT), tile->pos, WHITE); + break; + } case TILE_BG_BANNER: { DrawTextureRec(game->assets.background, texture_rect_v(3, 0, 3, 4, TILE_WIDTH, TILE_HEIGHT), tile->pos, WHITE); break; diff --git a/todo.txt b/todo.txt @@ -10,7 +10,7 @@ Todos - [x] Add wall collision - [ ] Add move velocity (for damage and maybe icy surfaces) - [x] Stop enemy movement on walls -- [ ] Stop freeze laser on walls +- [x] Stop freeze laser on walls - [ ] Add hitboxes (make them visible in debug mode) - [ ] Add countdown - [ ] Add new enemies