187 lines
6.4 KiB
C
187 lines
6.4 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"
|
|
|
|
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);
|
|
}
|
|
SDL_SetRenderTarget(renderer, oldTarget);
|
|
}
|
|
|
|
void updateEntities() {
|
|
for (int i = 0; i < entities.activeCount; i++) {
|
|
Entity *ent = &entities.entities[i];
|
|
EntityTypeReg entT = EntityRegistry[ent->type];
|
|
|
|
// Step 1: If no path, or we're at the end of a path, check for re-path
|
|
bool atTargetSnapshot = ent->tileRect.x == ent->targetSnapshot.x &&
|
|
ent->tileRect.y == ent->targetSnapshot.y;
|
|
|
|
bool needsPath = ent->path.length == 0 || ent->path.stepIndex >= ent->path.length;
|
|
|
|
if (needsPath && (atTargetSnapshot || ent->path.length == 0)) {
|
|
if (ent->tileRect.x != ent->target.x || ent->tileRect.y != ent->target.y) {
|
|
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 {
|
|
// No path found — freeze
|
|
ent->path.length = 0;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Step 2: If it's time to move to the next tile
|
|
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: Interpolate renderRect between fromTile and toTile
|
|
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;
|
|
|
|
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
|
|
} |