// // Created by bruno on 7.6.2025. // #include #include #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 }