Files
factorygame/entity/entity.c
2025-06-10 16:29:17 +02:00

202 lines
6.9 KiB
C

//
// Created by bruno on 7.6.2025.
//
#include <SDL2/SDL.h>
#include <dirent.h>
#include "entity.h"
#include "../player/player.h"
#include "../util/pathfinding.h"
#include "../util/font.h"
EntityArray entities;
EntityTypeReg EntityRegistry[ENTITY_MAX_COUNT];
void renderEntities(SDL_Renderer *renderer, SDL_Rect playerRect) {
SDL_Texture *oldTarget = SDL_GetRenderTarget(renderer);
SDL_SetRenderTarget(renderer, entityTexture);
SDL_RenderClear(mainRenderer);
for (int i = 0; i < entities.activeCount; i++) {
Entity *ent = &entities.entities[i];
SDL_Rect renderRect = ent->renderRect;
adjustRect(&renderRect, playerRect);
if (!checkCollision(renderRect, screenRect)) {
continue;
}
EntityTypeReg entType = EntityRegistry[ent->type];
char animationFrame = (animationStep / entType.animation.divisor) % entType.animation.frameCount;
SDL_RenderCopy(renderer, atlasTexture, &entType.animation.atlasRects[animationFrame], &renderRect);
char healthStr[12];
snprintf(healthStr, 12, "%d/%d", ent->health, entType.maxHealth);
renderText(renderer, fonts[2], healthStr, renderRect.x, renderRect.y);
}
SDL_SetRenderTarget(renderer, oldTarget);
}
void updateEntities() {
for (int i = 0; i < entities.activeCount; i++) {
Entity *ent = &entities.entities[i];
EntityTypeReg entT = EntityRegistry[ent->type];
if (ent->health > entT.maxHealth || ent->health <= 0) {
remove_entity(&entities, i);
continue;
}
bool atTargetSnapshot = ent->tileRect.x == ent->targetSnapshot.x &&
ent->tileRect.y == ent->targetSnapshot.y;
bool atTarget = ent->tileRect.x == ent->target.x &&
ent->tileRect.y == ent->target.y;
// Retry pathfinding every 10 ticks if we don't have a path and aren't at the target
bool shouldRetryPathfinding = (
ent->path.length == 0 &&
!atTarget &&
(animationStep % 10 == 0)
);
// Also try pathfinding if we reached the snapshot (end of path)
if (atTargetSnapshot || shouldRetryPathfinding) {
if (isWalkable(ent->target) && find_path(ent->tileRect, ent->target)) {
ent->path = reconstruct_path(ent->target);
ent->path.stepIndex = 0;
ent->targetSnapshot = ent->target; // snapshot the current target
ent->fromTile = ent->tileRect;
ent->toTile = ent->path.steps[0];
ent->interpolateTick = 0;
ent->entityNextTick = animationStep + entT.entityTickRate;
} else if (atTargetSnapshot) {
// No path found and we finished current one — freeze
ent->path.length = 0;
continue;
}
}
// Step 2: Movement
if (ent->path.length > 0 && ent->path.stepIndex < ent->path.length &&
animationStep >= ent->entityNextTick) {
ent->fromTile = ent->tileRect;
ent->toTile = ent->path.steps[ent->path.stepIndex];
ent->tileRect = ent->toTile;
ent->entityNextTick = animationStep + entT.entityTickRate;
ent->interpolateTick = 0;
ent->path.stepIndex++;
}
// Step 3: Interpolation
MiniRect from = {
.x = ent->fromTile.x * TILE_SIZE,
.y = ent->fromTile.y * TILE_SIZE
};
MiniRect to = {
.x = ent->toTile.x * TILE_SIZE,
.y = ent->toTile.y * TILE_SIZE
};
float t = (float)ent->interpolateTick / entT.entityTickRate;
ent->renderRect.x = (int)(from.x + (to.x - from.x) * t);
ent->renderRect.y = (int)(from.y + (to.y - from.y) * t);
if (ent->interpolateTick < entT.entityTickRate) {
ent->interpolateTick++;
}
}
}
void registerEntity(char fname[20], SDL_Renderer *renderer) {
char name[21];
// Load animation frames
int frame = 0;
int indexEntity = 0;
char texturePath[80];
if (sscanf(fname, "%d_%20[^_]_%d.png", &indexEntity, name, &frame) == 3) {
// Success: you now have index, fname, and frame
} else {
fprintf(stderr, "Invalid format: %s\n", fname);
}
strcpy(EntityRegistry[indexEntity].name, name);
snprintf(texturePath, sizeof(texturePath), "./assets/entities/%s", fname);
SDL_Texture *texture = IMG_LoadTexture(renderer, texturePath);
if (!texture) {
if (frame == 0) {
fprintf(stderr, "Failed to load entity texture %s: %s\n", texturePath, IMG_GetError());
}
}
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_NONE);
//printf("Ent %s to %d\n", fname, indexEntity);
EntityRegistry[indexEntity].animation.textures[frame] = texture;
EntityRegistry[indexEntity].animation.atlasRects[frame] = allocate_32x32(texture, renderer);
EntityRegistry[indexEntity].type = indexEntity;
EntityRegistry[indexEntity].animation.frameCount = frame + 1;
EntityRegistry[indexEntity].animation.divisor = 1;
EntityRegistry[indexEntity].entityTickRate = 8;
EntityRegistry[indexEntity].maxHealth = 100;
if (indexEntity + 1 > backgroundTileTypeIndex) {
backgroundTileTypeIndex = indexEntity + 1;
}
}
void loadEntities(SDL_Renderer *renderer) {
DIR *dir = opendir("./assets/entities");
if (!dir) {
perror("Failed to open entities directory");
return;
}
char *entityNames[ENTITY_MAX_COUNT];
int entityCount = 0;
struct dirent *entry;
while ((entry = readdir(dir))) {
char *dot = strrchr(entry->d_name, '.');
if (!dot || strcmp(dot, ".png") != 0) continue;
// Check if baseName already stored
int found = 0;
for (int i = 0; i < entityCount; ++i) {
if (strcmp(entityNames[i], entry->d_name) == 0) {
found = 1;
break;
}
}
if (!found && entityCount < ENTITY_MAX_COUNT) {
entityNames[entityCount++] = strdup(entry->d_name); // Only store base, not full file name
}
}
closedir(dir);
qsort(entityNames, entityCount, sizeof(char *), compareStrings);
// Call registerEntity on each base name
for (int i = 0; i < entityCount; ++i) {
char fileName[64];
snprintf(fileName, sizeof(fileName), "%s", entityNames[i]);
registerEntity(fileName, renderer);
free(entityNames[i]);
}
}
int add_entity(EntityArray *arr, Entity t) {
if (arr->activeCount >= ENTITY_MAX_COUNT) return 0;
arr->entities[arr->activeCount] = t;
arr->activeCount++;
return arr->activeCount - 1;
}
void remove_entity(EntityArray *arr, int index) {
if (index < 0 || index >= arr->activeCount) return;
arr->activeCount--;
arr->entities[index] = arr->entities[arr->activeCount]; // swap with last active
}