From d3a1bb552e9efc4e516841158aaaea3d8ab9202d Mon Sep 17 00:00:00 2001 From: Leander Scherer Date: Wed, 11 Mar 2026 23:16:49 +0100 Subject: refactor(entity): replace EntityManager with typed Entities struct --- include/entities.h | 126 +++++++++++++++++++++++++++++++++ include/entity.h | 61 ---------------- include/game.h | 11 --- include/globals.h | 13 ---- src/entity.c | 202 ----------------------------------------------------- 5 files changed, 126 insertions(+), 287 deletions(-) create mode 100644 include/entities.h delete mode 100644 include/entity.h delete mode 100644 include/game.h delete mode 100644 include/globals.h delete mode 100644 src/entity.c diff --git a/include/entities.h b/include/entities.h new file mode 100644 index 0000000..7bd38f3 --- /dev/null +++ b/include/entities.h @@ -0,0 +1,126 @@ +#pragma once + +#include "raylib.h" +#include "raytmx.h" + +#include "map.h" +#include "player.h" + +#define MAX_ENTITIES 120 + +typedef enum { + PICKUP_COIN, + PICKUP_POTION, + PICKUP_BOMB, + PICKUP_FULL_HEART, + PICKUP_HALF_HEART, + PICKUP_GOLD_BAR, + PICKUP_GOLD_NUGGET +} PickupType; + +static const int PICKUP_TILE_ID[] = {0, 4, 8, 12, 16, 20, 24}; + +typedef enum { STATE_IDLE, STATE_PATROL, STATE_CHASE, STATE_ATTACK } EnemyState; + +typedef struct { + PickupType type; + Vector2 position; + Rectangle bounds; + int value; + bool active; + uint32_t gid; +} Pickup; + +typedef struct { + Vector2 position; + Rectangle bounds; + int uniqueId; + bool active; + uint32_t gid; +} Key; + +typedef struct { + Vector2 position; + Rectangle bounds; + int health; + EnemyState state; + + TmxTileset tileset; + int currentFrame; + float frameTime; + bool facingRight; + + bool active; +} Bat; + +typedef struct { + Vector2 position; + Rectangle bounds; + int health; + EnemyState state; + + TmxTileset tileset; + int currentFrame; + float frameTime; + bool facingRight; + + bool active; +} Slime; + +typedef struct { + Vector2 position; + Rectangle bounds; + int health; + EnemyState state; + + TmxTileset tileset; + int currentFrame; + float frameTime; + bool facingRight; + + bool active; +} FlyingSkull; + +typedef struct { + Vector2 position; + Rectangle bounds; + bool active; +} Vase; + +typedef struct { + Vector2 position; + Rectangle bounds; + bool active; +} Crate; + +struct Entities { + Pickup pickups[MAX_ENTITIES]; + int pickupsCount; + + Key keys[MAX_ENTITIES]; + int keysCount; + + Bat bats[MAX_ENTITIES]; + int batsCount; + + Slime slimes[MAX_ENTITIES]; + int slimesCount; + + FlyingSkull flyingSkulls[MAX_ENTITIES]; + int flyingSkullsCount; + + Vase vases[MAX_ENTITIES]; + int vasesCount; + + Crate crates[MAX_ENTITIES]; + int cratesCount; +}; + +void UpdateBat(Bat *bat, Player *player, Map *map); +void DrawBat(Bat *bat); + +void UpdateSlime(Slime *slime, Player *player, Map *map); +void DrawSlime(Slime *slime); + +void UpdateFlyingSkull(FlyingSkull *skull, Player *player, Map *map); +void DrawFlyingSkull(FlyingSkull *skull); diff --git a/include/entity.h b/include/entity.h deleted file mode 100644 index 60467ab..0000000 --- a/include/entity.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef ENTITY_H -#define ENTITY_H - -#include "raylib.h" -#include "raytmx.h" - -#include "player.h" -#include "map_manager.h" - -typedef struct Enemy { - Vector2 position; - Rectangle bounds; - uint32_t gid; - int health; - bool active; -} Enemy; - -typedef struct Static { - Vector2 position; - Rectangle bounds; - uint32_t gid; - bool active; -} Static; - -typedef struct Collectible { - Vector2 position; - Rectangle bounds; - uint32_t gid; - bool active; - int tileX; - int tileY; -} Collectible; - -typedef struct Movable { - Vector2 position; - Rectangle bounds; - uint32_t gid; - bool active; -} Movable; - -typedef struct EntityManager { - Enemy *enemies; - int enemiesCount; - - Collectible *collectibles; - int collectiblesCount; - - Static *statics; - int staticsCount; - - Movable *movables; - int movablesCount; -} EntityManager; - -EntityManager InitEntityManager(uint32_t capacity); -void SpawnEntitiesFromMap(EntityManager *mgr, TmxMap *map); -void UpdateEntities(EntityManager *mgr, Player *player, MapManager *mapMgr); -void DrawEntities(EntityManager *mgr, TmxMap *map); -void UnloadEntityManager(EntityManager *mgr); - -#endif // ENTITY_H diff --git a/include/game.h b/include/game.h deleted file mode 100644 index 6b37293..0000000 --- a/include/game.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef GAME_H -#define GAME_H - -typedef enum GameScreen { - SCREEN_TITLE, - SCREEN_PLAYING, - SCREEN_PAUSED, - SCREEN_GAME_OVER -} GameScreen; - -#endif // GAME_H diff --git a/include/globals.h b/include/globals.h deleted file mode 100644 index e7830de..0000000 --- a/include/globals.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef GLOBALS_H -#define GLOBALS_H - -#include "raylib.h" - -#define TILE_SIZE 16 -#define SCREEN_WIDTH 1280 -#define SCREEN_HEIGHT 720 -#define BACKGROUND_COLOR (Color){ 88, 68, 34, 255 } - -extern bool debugMode; - -#endif diff --git a/src/entity.c b/src/entity.c deleted file mode 100644 index 9fc582c..0000000 --- a/src/entity.c +++ /dev/null @@ -1,202 +0,0 @@ -#include "entity.h" -#include "map_manager.h" -#include "player.h" -#include "raymath.h" -#include "globals.h" -#include -#include - -EntityManager InitEntityManager(uint32_t maxPerType) { - EntityManager mgr; - mgr.enemies = (Enemy *)calloc(maxPerType, sizeof(Enemy)); - mgr.collectibles = (Collectible *)calloc(maxPerType, sizeof(Collectible)); - mgr.movables = (Movable *)calloc(maxPerType, sizeof(Movable)); - mgr.statics = (Static *)calloc(maxPerType, sizeof(Static)); - - mgr.enemiesCount = 0; - mgr.collectiblesCount = 0; - mgr.movablesCount = 0; - mgr.staticsCount = 0; - - return mgr; -} - -void SpawnEntitiesFromMap(EntityManager *mgr, TmxMap *map) { - for (uint32_t i = 0; i < map->layersLength; i++) { - TmxLayer *layer = &map->layers[i]; - - // Handle Tile Layer Pickups - if (layer->type == LAYER_TYPE_TILE_LAYER && - (strcmp(layer->name, "Pickups") == 0 || strcmp(layer->name, "Collectible") == 0)) { - TmxTileLayer *tileLayer = &layer->exact.tileLayer; - for (uint32_t y = 0; y < tileLayer->height; y++) { - for (uint32_t x = 0; x < tileLayer->width; x++) { - uint32_t gid = tileLayer->tiles[y * tileLayer->width + x]; - if (gid != 0) { - float px = (float)x * map->tileWidth; - float py = (float)y * map->tileHeight; - - // Default bounds (whole tile) - Rectangle customBounds = { px, py, (float)map->tileWidth, (float)map->tileHeight }; - - // Check if the tile has a custom collision shape from Tileset Editor - if (gid < map->gidsToTilesLength) { - TmxTile tileData = map->gidsToTiles[gid]; - - // Use the first animation frame's collision if it exists - uint32_t targetGid = gid; - if (tileData.hasAnimation && tileData.animation.framesLength > 0) { - targetGid = tileData.animation.frames[0].gid; - } - - // Look up tile data for the frame (accounting for local vs global IDs) - // Note: raytmx internal gidsToTiles uses global IDs - if (targetGid < map->gidsToTilesLength) { - TmxObjectGroup colGroup = map->gidsToTiles[targetGid].objectGroup; - if (colGroup.objectsLength > 0) { - TmxObject colObj = colGroup.objects[0]; - customBounds = (Rectangle){ - px + (float)colObj.x, - py + (float)colObj.y, - (float)colObj.width, - (float)colObj.height - }; - } - } - } - - mgr->collectibles[mgr->collectiblesCount++] = (Collectible){ - .position = {px, py}, - .bounds = customBounds, - .active = true, - .gid = gid, - .tileX = (int)x, - .tileY = (int)y - }; - } - } - } - } - - if (layer->type != LAYER_TYPE_OBJECT_GROUP) - continue; - - TmxObjectGroup group = layer->exact.objectGroup; - - if (strcmp(layer->name, "Static") == 0) { - for (uint32_t j = 0; j < group.objectsLength; j++) { - TmxObject object = group.objects[j]; - mgr->statics[mgr->staticsCount++] = - (Static){.position = {(float)object.x, (float)object.y}, - .bounds = {(float)object.x, (float)object.y, - (float)object.width, (float)object.height}, - .active = true, - .gid = object.gid}; - } - } - if (strcmp(layer->name, "Movable") == 0) { - for (uint32_t j = 0; j < group.objectsLength; j++) { - TmxObject object = group.objects[j]; - mgr->movables[mgr->movablesCount++] = - (Movable){.position = {(float)object.x, (float)object.y}, - .bounds = {(float)object.x, (float)object.y, - (float)object.width, (float)object.height}, - .active = true, - .gid = object.gid}; - } - } - if (strcmp(layer->name, "Collectible") == 0) { - for (uint32_t j = 0; j < group.objectsLength; j++) { - TmxObject object = group.objects[j]; - mgr->collectibles[mgr->collectiblesCount++] = - (Collectible){.position = {(float)object.x, (float)object.y}, - .bounds = {(float)object.x, (float)object.y, - (float)object.width, (float)object.height}, - .active = true, - .gid = object.gid}; - } - } - if (strcmp(layer->name, "Enemy") == 0) { - for (uint32_t j = 0; j < group.objectsLength; j++) { - TmxObject object = group.objects[j]; - mgr->enemies[mgr->enemiesCount++] = - (Enemy){.position = {(float)object.x, (float)object.y}, - .bounds = {(float)object.x, (float)object.y, - (float)object.width, (float)object.height}, - .active = true, - .gid = object.gid}; - } - } - } -} - -void UpdateEntities(EntityManager *mgr, Player *player, MapManager *mapMgr) { - // 1. Update Pickups (Collision only) - for (int i = 0; i < mgr->collectiblesCount; i++) { - if (mgr->collectibles[i].active && - CheckCollisionRecs(player->bounds, mgr->collectibles[i].bounds)) { - mgr->collectibles[i].active = false; - - // Visually remove from map if it's a tile pickup - if (mapMgr->pickupLayer && mapMgr->pickupLayer->type == LAYER_TYPE_TILE_LAYER) { - TmxTileLayer *tileLayer = &mapMgr->pickupLayer->exact.tileLayer; - int idx = mgr->collectibles[i].tileY * tileLayer->width + mgr->collectibles[i].tileX; - if (idx >= 0 && idx < (int)tileLayer->tilesLength) { - tileLayer->tiles[idx] = 0; - } - } - } - } - - // 2. Update Movables (Physics/Pushing) - for (int i = 0; i < mgr->movablesCount; i++) { - Movable *m = &mgr->movables[i]; - if (CheckCollisionRecs(player->bounds, m->bounds)) { - // Push logic: move the block in player's direction if no wall is there - Vector2 pushDir = - Vector2Normalize(Vector2Subtract(m->position, player->position)); - Vector2 nextPos = Vector2Add( - m->position, Vector2Scale(pushDir, player->speed * GetFrameTime())); - Rectangle nextRect = {nextPos.x, nextPos.y, m->bounds.width, - m->bounds.height}; - - if (!IsWallCollision(mapMgr, nextRect)) { - m->position = nextPos; - m->bounds.x = nextPos.x; - m->bounds.y = nextPos.y; - } - } - } - - // 3. Update Enemies (AI) - for (int i = 0; i < mgr->enemiesCount; i++) { - if (!mgr->enemies[i].active) - continue; - // Basic AI: Move toward player if within range - if (Vector2Distance(mgr->enemies[i].position, player->position) < 100.0f) { - Vector2 dir = Vector2Normalize( - Vector2Subtract(player->position, mgr->enemies[i].position)); - mgr->enemies[i].position = Vector2Add( - mgr->enemies[i].position, Vector2Scale(dir, 50.0f * GetFrameTime())); - mgr->enemies[i].bounds.x = mgr->enemies[i].position.x; - mgr->enemies[i].bounds.y = mgr->enemies[i].position.y; - } - } -} - -void UnloadEntityManager(EntityManager *mgr) { - if (mgr->enemies) free(mgr->enemies); - if (mgr->collectibles) free(mgr->collectibles); - if (mgr->movables) free(mgr->movables); - if (mgr->statics) free(mgr->statics); - - mgr->enemies = NULL; - mgr->collectibles = NULL; - mgr->movables = NULL; - mgr->statics = NULL; - - mgr->enemiesCount = 0; - mgr->collectiblesCount = 0; - mgr->movablesCount = 0; - mgr->staticsCount = 0; -} -- cgit v1.3.1