Hopefully last commit

This commit is contained in:
2025-06-11 23:01:05 +02:00
parent 78bccd6c6f
commit 8bbe17491b
74 changed files with 1306 additions and 516 deletions

View File

@@ -14,6 +14,8 @@ EntityArray entities;
EntityTypeReg EntityRegistry[ENTITY_MAX_COUNT];
EnemySpawnState currentSpawnState;
void renderEntities(SDL_Renderer *renderer, SDL_Rect playerRect) {
SDL_Texture *oldTarget = SDL_GetRenderTarget(renderer);
SDL_SetRenderTarget(renderer, entityTexture);
@@ -44,6 +46,7 @@ void updateEntities(Player *plr) {
remove_entity(&entities, i);
continue;
}
bool atTargetSnapshot = ent->tileRect.x == ent->targetSnapshot.x &&
ent->tileRect.y == ent->targetSnapshot.y;
@@ -51,22 +54,19 @@ void updateEntities(Player *plr) {
ent->tileRect.y == ent->target.y;
if (animationStep >= ent->entityNextTick) {
if (sqrt(pow(abs(plr->tileRect.x - ent->tileRect.x), 2) + pow(abs(plr->tileRect.y - ent->tileRect.y), 2)) < ENEMY_RANGE) {
if (sqrt(pow(abs(plr->tileRect.x - ent->tileRect.x), 2) +
pow(abs(plr->tileRect.y - ent->tileRect.y), 2)) < ENEMY_RANGE) {
plr->health -= ENEMY_DAMAGE;
}
if (plr->tileRect.x)
for (int y = ent->tileRect.y - ENEMY_RANGE; y < ent->tileRect.y + ENEMY_RANGE; y++) {
if (y < 0 || y >= MAP_HEIGHT) {
continue;
}
for (int x = ent->tileRect.x - ENEMY_RANGE; x < ent->tileRect.x + ENEMY_RANGE; x++) {
if (x < 0 || x >= MAP_WIDTH) {
continue;
}
for (int y = ent->tileRect.y - ENEMY_RANGE; y <= ent->tileRect.y + ENEMY_RANGE; y++) {
if (y < 0 || y >= MAP_HEIGHT) continue;
for (int x = ent->tileRect.x - ENEMY_RANGE; x <= ent->tileRect.x + ENEMY_RANGE; x++) {
if (x < 0 || x >= MAP_WIDTH) continue;
Tile *targTile = &tileMap[y][x];
if (targTile->type == TYPE_AIR) {
continue;
}
if (targTile->type == TYPE_AIR) continue;
targTile->health -= ENEMY_DAMAGE;
if (targTile->health <= 0) {
if (targTile->audioCh < NUM_SYNTH_VOICES) {
@@ -79,31 +79,63 @@ void updateEntities(Player *plr) {
}
}
// 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);
MiniRect fallbackTarget = ent->target;
// If the target is not walkable, search nearby
if (!isWalkable(ent->target)) {
int bestDist = 999999;
bool found = false;
for (int dy = -5; dy <= 5; dy++) {
for (int dx = -5; dx <= 5; dx++) {
int nx = ent->target.x + dx;
int ny = ent->target.y + dy;
if (nx < 0 || ny < 0 || nx >= MAP_WIDTH || ny >= MAP_HEIGHT) continue;
MiniRect check = {nx, ny};
if (!isWalkable(check)) continue;
int dist = abs(dx) + abs(dy); // Manhattan distance
if (dist < bestDist) {
bestDist = dist;
fallbackTarget = check;
found = true;
}
}
}
if (!found) {
// No walkable fallback tile found
ent->path.length = 0;
continue;
}
}
// Attempt pathfinding to fallbackTarget
if (find_path(ent->tileRect, fallbackTarget)) {
ent->path = reconstruct_path(fallbackTarget);
ent->path.stepIndex = 0;
ent->targetSnapshot = ent->target; // snapshot the current target
ent->targetSnapshot = fallbackTarget;
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
// Movement
if (ent->path.length > 0 && ent->path.stepIndex < ent->path.length &&
animationStep >= ent->entityNextTick) {
ent->fromTile = ent->tileRect;
@@ -114,7 +146,7 @@ void updateEntities(Player *plr) {
ent->path.stepIndex++;
}
// Step 3: Interpolation
// Interpolation
MiniRect from = {
.x = ent->fromTile.x * TILE_SIZE,
.y = ent->fromTile.y * TILE_SIZE
@@ -166,7 +198,7 @@ void registerEntity(char fname[20], SDL_Renderer *renderer) {
EntityRegistry[indexEntity].type = indexEntity;
EntityRegistry[indexEntity].animation.frameCount = frame + 1;
EntityRegistry[indexEntity].animation.divisor = 1;
EntityRegistry[indexEntity].entityTickRate = 8;
EntityRegistry[indexEntity].entityTickRate = 16;
EntityRegistry[indexEntity].maxHealth = 100;
if (indexEntity + 1 > backgroundTileTypeIndex) {
@@ -213,6 +245,7 @@ void loadEntities(SDL_Renderer *renderer) {
registerEntity(fileName, renderer);
free(entityNames[i]);
}
EntityRegistry[GHOST].animation.divisor = 16;
}
int add_entity(EntityArray *arr, Entity t) {
@@ -226,4 +259,88 @@ 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
}
bool isTileAlreadyTargeted(int x, int y) {
for (int i = 0; i < entities.activeCount; i++) {
Entity *ent = &entities.entities[i];
if (ent->target.x == x && ent->target.y == y) {
return 1;
}
}
return 0;
}
void spawn_enemy_at_random_tile() {
Entity ent;
memset(&ent, 0, sizeof(Entity));
// Start by placing it at a default location (same as before)
int offsetX = (rand() % (SPAWN_RADIUS * 2 + 1)) - SPAWN_RADIUS;
int offsetY = (rand() % (SPAWN_RADIUS * 2 + 1)) - SPAWN_RADIUS;
ent.tileRect.x = enemySpawn.x + offsetX;
ent.tileRect.y = enemySpawn.y + offsetY;
ent.renderRect.x = ent.tileRect.x * TILE_SIZE;
ent.renderRect.y = ent.tileRect.y * TILE_SIZE;
ent.renderRect.w = TILE_SIZE;
ent.renderRect.h = TILE_SIZE;
ent.health = 100;
ent.type = GHOST;
// Try to find a unique, walkable target tile near the player
for (int attempt = 0; attempt < MAX_SPAWN_ATTEMPTS; attempt++) {
int targetOffsetX = (rand() % (SPAWN_RADIUS * 2 + 1)) - SPAWN_RADIUS;
int targetOffsetY = (rand() % (SPAWN_RADIUS * 2 + 1)) - SPAWN_RADIUS;
int targetX = mainPlayer.tileRect.x + targetOffsetX;
int targetY = mainPlayer.tileRect.y + targetOffsetY;
MiniRect target = {targetX, targetY};
if (!isWalkable(target)) continue;
if (isTileAlreadyTargeted(targetX, targetY)) continue;
ent.target.x = targetX;
ent.target.y = targetY;
add_entity(&entities, ent);
return;
}
printf("Failed to spawn enemy: no unique target found after %d attempts.\n", MAX_SPAWN_ATTEMPTS);
}
void updateWaveLogic(WaveInfo *info) {
if (info->waveRunning) {
Wave *currentWave = &info->waves[info->waveCounter];
// Cooldown between enemy spawns
if (--currentSpawnState.spawnCooldown <= 0 &&
currentSpawnState.enemiesSpawned < currentWave->enemies[0].count) {
spawn_enemy_at_random_tile();
currentSpawnState.enemiesSpawned++;
currentSpawnState.spawnCooldown = SPAWN_COOLDOWN;
}
// All enemies spawned
if (currentSpawnState.enemiesSpawned >= currentWave->enemies[0].count) {
// Wait for all enemies to be dead before ending wave
if (entities.activeCount == 0) {
info->waveRunning = false;
info->waveTimer = info->waves[info->waveCounter].timeUntilNext;
}
}
} else {
// Timer starts only after wave is completely over (including enemies)
if (--info->waveTimer <= 0 && info->waveCounter + 1 < info->totalWaves) {
// Start next wave
info->waveCounter++;
info->waveRunning = true;
currentSpawnState.enemiesSpawned = 0;
currentSpawnState.spawnCooldown = 0;
}
}
}

View File

@@ -11,9 +11,22 @@
#define ENTITY_MAX_COUNT 1024
#define ENEMY_DAMAGE 2
#define ENEMY_DAMAGE 1
#define ENEMY_RANGE 3
#define SPAWN_RADIUS 5
typedef struct EnemySpawnState {
int enemiesSpawned;
int spawnCooldown;
} EnemySpawnState;
#define SPAWN_COOLDOWN 30 // frames between each enemy spawn
#define MAX_SPAWN_ATTEMPTS 50
extern EnemySpawnState currentSpawnState;
typedef enum EntityType {
GHOST,
} EntityType;
@@ -56,5 +69,6 @@ void renderEntities(SDL_Renderer *renderer, SDL_Rect playerRect);
void updateEntities(Player * plr);
void registerEntity(char fname[20], SDL_Renderer *renderer);
void loadEntities(SDL_Renderer *renderer);
void updateWaveLogic(WaveInfo* info);
#endif //FACTORYGAME_ENTITY_H