Testing
This commit is contained in:
@@ -45,6 +45,10 @@ set(SOURCE_FILES
|
|||||||
util/atlas.h
|
util/atlas.h
|
||||||
tiles/miner.c
|
tiles/miner.c
|
||||||
tiles/miner.h
|
tiles/miner.h
|
||||||
|
util/pathfinding.c
|
||||||
|
util/pathfinding.h
|
||||||
|
entity/entity.c
|
||||||
|
entity/entity.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(factorygame ${SOURCE_FILES})
|
add_executable(factorygame ${SOURCE_FILES})
|
||||||
|
BIN
assets/entities/00_enemy_00.png
Normal file
BIN
assets/entities/00_enemy_00.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 405 B |
187
entity/entity.c
Normal file
187
entity/entity.c
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
|
}
|
55
entity/entity.h
Normal file
55
entity/entity.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
//
|
||||||
|
// Created by bruno on 7.6.2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef FACTORYGAME_ENTITY_H
|
||||||
|
#define FACTORYGAME_ENTITY_H
|
||||||
|
|
||||||
|
#include "../tiles/tile.h"
|
||||||
|
#include "../util/pathfinding.h"
|
||||||
|
|
||||||
|
#define ENTITY_MAX_COUNT 1024
|
||||||
|
|
||||||
|
typedef enum EntityType {
|
||||||
|
GHOST,
|
||||||
|
} EntityType;
|
||||||
|
|
||||||
|
typedef struct EntityTypeReg {
|
||||||
|
EntityType type;
|
||||||
|
Animation animation;
|
||||||
|
char name[20];
|
||||||
|
int speed;
|
||||||
|
int entityTickRate;
|
||||||
|
} EntityTypeReg;
|
||||||
|
|
||||||
|
typedef struct Entity {
|
||||||
|
MiniRect tileRect;
|
||||||
|
SDL_Rect renderRect;
|
||||||
|
EntityType type;
|
||||||
|
uint16_t health;
|
||||||
|
MiniRect target;
|
||||||
|
Path path;
|
||||||
|
int entityNextTick;
|
||||||
|
unsigned char interpolateTick;
|
||||||
|
MiniRect fromTile;
|
||||||
|
MiniRect toTile;
|
||||||
|
MiniRect targetSnapshot;
|
||||||
|
} Entity;
|
||||||
|
|
||||||
|
typedef struct EntityArray {
|
||||||
|
Entity entities[ENTITY_MAX_COUNT];
|
||||||
|
int activeCount;
|
||||||
|
} EntityArray;
|
||||||
|
|
||||||
|
extern EntityArray entities;
|
||||||
|
|
||||||
|
void remove_entity(EntityArray *arr, int index);
|
||||||
|
|
||||||
|
int add_entity(EntityArray *arr, Entity t);
|
||||||
|
|
||||||
|
void renderEntities(SDL_Renderer *renderer, SDL_Rect playerRect);
|
||||||
|
void updateEntities();
|
||||||
|
void registerEntity(char fname[20], SDL_Renderer *renderer);
|
||||||
|
void loadEntities(SDL_Renderer *renderer);
|
||||||
|
|
||||||
|
#endif //FACTORYGAME_ENTITY_H
|
@@ -246,7 +246,7 @@ void renderItem(ItemOnBelt item, SDL_Renderer *renderer, int lane, SDL_Rect play
|
|||||||
xOffset += perpX * laneSign * laneOffset;
|
xOffset += perpX * laneSign * laneOffset;
|
||||||
yOffset += perpY * laneSign * laneOffset;
|
yOffset += perpY * laneSign * laneOffset;
|
||||||
|
|
||||||
//--- 5) apply to rect
|
//--- 5) apply to tileRect
|
||||||
rect.x += (int) roundf(xOffset);
|
rect.x += (int) roundf(xOffset);
|
||||||
rect.y += (int) roundf(yOffset);
|
rect.y += (int) roundf(yOffset);
|
||||||
|
|
||||||
@@ -273,7 +273,7 @@ void renderItem(ItemOnBelt item, SDL_Renderer *renderer, int lane, SDL_Rect play
|
|||||||
adjustRect(&rectA, playerRect);
|
adjustRect(&rectA, playerRect);
|
||||||
SDL_RenderFillRect(renderer, &rectA);
|
SDL_RenderFillRect(renderer, &rectA);
|
||||||
}
|
}
|
||||||
//SDL_RenderCopyx(renderer, ItemRegistry[item.type].textureOnBelt[ORIENT_LEFT], NULL, &rect);
|
//SDL_RenderCopyx(renderer, ItemRegistry[item.type].textureOnBelt[ORIENT_LEFT], NULL, &tileRect);
|
||||||
SDL_RenderCopy(renderer, atlasTexture,
|
SDL_RenderCopy(renderer, atlasTexture,
|
||||||
&ItemRegistry[item.type].beltAnimation.atlasRects[ORIENT_LEFT][
|
&ItemRegistry[item.type].beltAnimation.atlasRects[ORIENT_LEFT][
|
||||||
(animationStep / ItemRegistry[item.type].beltAnimation.divisor) %
|
(animationStep / ItemRegistry[item.type].beltAnimation.divisor) %
|
||||||
|
91
main.c
91
main.c
@@ -9,6 +9,7 @@
|
|||||||
#include "player/player.h"
|
#include "player/player.h"
|
||||||
#include "util/perlin.h"
|
#include "util/perlin.h"
|
||||||
#include "util/atlas.h"
|
#include "util/atlas.h"
|
||||||
|
#include "entity/entity.h"
|
||||||
|
|
||||||
typedef struct GameState {
|
typedef struct GameState {
|
||||||
Player player;
|
Player player;
|
||||||
@@ -16,6 +17,9 @@ typedef struct GameState {
|
|||||||
BackgroundTile backgroundTileMap[MAP_HEIGHT][MAP_WIDTH];
|
BackgroundTile backgroundTileMap[MAP_HEIGHT][MAP_WIDTH];
|
||||||
AudioData audioData;
|
AudioData audioData;
|
||||||
TileArray neededUpdates;
|
TileArray neededUpdates;
|
||||||
|
EntityArray entities;
|
||||||
|
Node openList[MAX_OPEN_NODES];
|
||||||
|
int openCount;
|
||||||
} GameState;
|
} GameState;
|
||||||
GameState gameState;
|
GameState gameState;
|
||||||
|
|
||||||
@@ -40,6 +44,9 @@ int loadGameState(char *filename, Player *plr) {
|
|||||||
audioData.playerRect = tmp;
|
audioData.playerRect = tmp;
|
||||||
audioData.totalSamples = 0;
|
audioData.totalSamples = 0;
|
||||||
memcpy(&neededUpdates, &gameState.neededUpdates, sizeof(gameState.neededUpdates));
|
memcpy(&neededUpdates, &gameState.neededUpdates, sizeof(gameState.neededUpdates));
|
||||||
|
memcpy(&entities, &gameState.entities, sizeof(gameState.entities));
|
||||||
|
openCount = gameState.openCount;
|
||||||
|
memcpy(&openList, &gameState.openList, sizeof(gameState.openList));
|
||||||
plr->cursor.targetTile = NULL;
|
plr->cursor.targetTile = NULL;
|
||||||
plr->cursor.prevTargetTile = NULL;
|
plr->cursor.prevTargetTile = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -53,6 +60,9 @@ void saveGameState(char *filename, Player *plr) {
|
|||||||
memcpy(gameState.backgroundTileMap, backgroundMap, sizeof(gameState.backgroundTileMap));
|
memcpy(gameState.backgroundTileMap, backgroundMap, sizeof(gameState.backgroundTileMap));
|
||||||
memcpy(&gameState.audioData, &audioData, sizeof(gameState.audioData));
|
memcpy(&gameState.audioData, &audioData, sizeof(gameState.audioData));
|
||||||
memcpy(&gameState.neededUpdates, &neededUpdates, sizeof(neededUpdates));
|
memcpy(&gameState.neededUpdates, &neededUpdates, sizeof(neededUpdates));
|
||||||
|
memcpy(&gameState.entities, &entities, sizeof(entities));
|
||||||
|
memcpy(&gameState.openList, &openList, sizeof(openList));
|
||||||
|
gameState.openCount = openCount;
|
||||||
|
|
||||||
FILE *gameSave = fopen(filename, "wb");
|
FILE *gameSave = fopen(filename, "wb");
|
||||||
if (!gameSave) {
|
if (!gameSave) {
|
||||||
@@ -142,6 +152,20 @@ int init() {
|
|||||||
loadBackgroundTiles(mainRenderer);
|
loadBackgroundTiles(mainRenderer);
|
||||||
loadTiles(mainRenderer);
|
loadTiles(mainRenderer);
|
||||||
loadItems(mainRenderer);
|
loadItems(mainRenderer);
|
||||||
|
loadEntities(mainRenderer);
|
||||||
|
|
||||||
|
Entity entTest;
|
||||||
|
memset(&entTest, 0, sizeof(Entity));
|
||||||
|
entTest.tileRect.x = 4;
|
||||||
|
entTest.tileRect.y = 5;
|
||||||
|
entTest.renderRect.w = TILE_SIZE;
|
||||||
|
entTest.renderRect.h = TILE_SIZE;
|
||||||
|
entTest.target.x = 0;
|
||||||
|
entTest.target.y = 0;
|
||||||
|
entTest.health = 100;
|
||||||
|
entTest.type = GHOST;
|
||||||
|
add_entity(&entities, entTest);
|
||||||
|
|
||||||
setupTiles();
|
setupTiles();
|
||||||
|
|
||||||
// for (ItemType i = 0; i < ITEMREGISTRY_SIZE; i++) {
|
// for (ItemType i = 0; i < ITEMREGISTRY_SIZE; i++) {
|
||||||
@@ -226,6 +250,7 @@ int render() {
|
|||||||
rect2.h = ATLAS_SIZE;
|
rect2.h = ATLAS_SIZE;
|
||||||
|
|
||||||
renderAllTiles(mainRenderer, player.rect);
|
renderAllTiles(mainRenderer, player.rect);
|
||||||
|
renderEntities(mainRenderer, player.rect);
|
||||||
renderPlayer(&player);
|
renderPlayer(&player);
|
||||||
|
|
||||||
|
|
||||||
@@ -333,6 +358,7 @@ void processMousePosition() {
|
|||||||
if (player.inventory.slotCounts[player.inventory.activeSlotIndex] > 0) {
|
if (player.inventory.slotCounts[player.inventory.activeSlotIndex] > 0) {
|
||||||
player.inventory.slotCounts[player.inventory.activeSlotIndex]--;
|
player.inventory.slotCounts[player.inventory.activeSlotIndex]--;
|
||||||
player.cursor.targetTile->type = player.inventory.activeSlotIndex;
|
player.cursor.targetTile->type = player.inventory.activeSlotIndex;
|
||||||
|
player.cursor.targetTile->health = TileRegistry[player.inventory.activeSlotIndex].maxHealth;
|
||||||
player.cursor.targetTile->rect.x = player.cursor.tileX;
|
player.cursor.targetTile->rect.x = player.cursor.tileX;
|
||||||
player.cursor.targetTile->rect.y = player.cursor.tileY;
|
player.cursor.targetTile->rect.y = player.cursor.tileY;
|
||||||
if (TileRegistry[player.inventory.activeSlotIndex].needsTicks) {
|
if (TileRegistry[player.inventory.activeSlotIndex].needsTicks) {
|
||||||
@@ -431,46 +457,53 @@ void processKeyboardHeld() {
|
|||||||
cameraSpeed /= 2;
|
cameraSpeed /= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (keyboardState[SDL_SCANCODE_F8]) {
|
||||||
|
if (player.cursor.targetTile->health) {
|
||||||
|
player.cursor.targetTile->health--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (player.cursor.breakingProgress == 0) {
|
if (player.cursor.breakingProgress == 0) {
|
||||||
|
SDL_Rect newRect = player.rect;
|
||||||
|
|
||||||
if (keyboardState[SDL_SCANCODE_W]) {
|
if (keyboardState[SDL_SCANCODE_W]) {
|
||||||
// Example: move up
|
newRect.y -= cameraSpeed;
|
||||||
player.rect.y -= cameraSpeed;
|
if (newRect.y >= 0 && canMoveWithRadius(newRect)) {
|
||||||
// if (player.rect.y < (DISPLAY_HEIGHT / 2)) {
|
player.rect = newRect;
|
||||||
// player.rect.y = (DISPLAY_HEIGHT / 2);
|
|
||||||
// }
|
|
||||||
if (player.rect.y < 0) {
|
|
||||||
player.rect.y = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyboardState[SDL_SCANCODE_S]) {
|
if (keyboardState[SDL_SCANCODE_S]) {
|
||||||
player.rect.y += cameraSpeed;
|
newRect = player.rect;
|
||||||
// if (player.rect.y > (MAP_HEIGHT * TILE_SIZE) - (DISPLAY_HEIGHT / 2)) {
|
newRect.y += cameraSpeed;
|
||||||
// player.rect.y = (MAP_HEIGHT * TILE_SIZE) - (DISPLAY_HEIGHT / 2);
|
if (newRect.y + newRect.h <= MAP_HEIGHT * TILE_SIZE && canMoveWithRadius(newRect)) {
|
||||||
// }
|
player.rect = newRect;
|
||||||
if (player.rect.y > (MAP_HEIGHT * TILE_SIZE)) {
|
|
||||||
player.rect.y = (MAP_HEIGHT * TILE_SIZE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyboardState[SDL_SCANCODE_A]) {
|
if (keyboardState[SDL_SCANCODE_A]) {
|
||||||
player.rect.x -= cameraSpeed;
|
newRect = player.rect;
|
||||||
// if (player.rect.x < (DISPLAY_WIDTH / 2)) {
|
newRect.x -= cameraSpeed;
|
||||||
// player.rect.x = (DISPLAY_WIDTH / 2);
|
if (newRect.x >= 0 && canMoveWithRadius(newRect)) {
|
||||||
// }
|
player.rect = newRect;
|
||||||
if (player.rect.x < 0) {
|
|
||||||
player.rect.x = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyboardState[SDL_SCANCODE_D]) {
|
if (keyboardState[SDL_SCANCODE_D]) {
|
||||||
player.rect.x += cameraSpeed;
|
newRect = player.rect;
|
||||||
// if (player.rect.x > (MAP_WIDTH * TILE_SIZE) - (DISPLAY_WIDTH / 2)) {
|
newRect.x += cameraSpeed;
|
||||||
// player.rect.x = (MAP_WIDTH * TILE_SIZE) - (DISPLAY_WIDTH / 2);
|
if (newRect.x + newRect.w <= MAP_WIDTH * TILE_SIZE && canMoveWithRadius(newRect)) {
|
||||||
// }
|
player.rect = newRect;
|
||||||
if (player.rect.x > (MAP_WIDTH * TILE_SIZE)) {
|
|
||||||
player.rect.x = (MAP_WIDTH * TILE_SIZE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Update tileRect only after actual movement
|
||||||
|
player.tileRect.x = player.rect.x / TILE_SIZE;
|
||||||
|
player.tileRect.y = player.rect.y / TILE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (keyboardState[SDL_SCANCODE_F]) {
|
if (keyboardState[SDL_SCANCODE_F]) {
|
||||||
for (int x = playerTileX - 2; x < playerTileX + 2; x++) {
|
for (int x = playerTileX - 2; x < playerTileX + 2; x++) {
|
||||||
if (x < 0) {
|
if (x < 0) {
|
||||||
@@ -587,7 +620,10 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[])
|
|||||||
running = processEvent(e);
|
running = processEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entities.entities[0].target = player.tileRect;
|
||||||
|
|
||||||
updateItems();
|
updateItems();
|
||||||
|
updateEntities();
|
||||||
updatePlayer(&player);
|
updatePlayer(&player);
|
||||||
updateTiles();
|
updateTiles();
|
||||||
animationStep++;
|
animationStep++;
|
||||||
@@ -658,7 +694,7 @@ void genInitMap() {
|
|||||||
if (oreNrm > oreNrmMax) oreNrmMax = oreNrm;
|
if (oreNrm > oreNrmMax) oreNrmMax = oreNrm;
|
||||||
|
|
||||||
// [Same as your original terrain generation logic...]
|
// [Same as your original terrain generation logic...]
|
||||||
BackgroundType baseType;
|
BackgroundType baseType = BGType_COBBLE0;
|
||||||
if (terrain < 0.30) {
|
if (terrain < 0.30) {
|
||||||
baseType = (humidity < 0.5) ? BGType_WATER_SHALLOW : BGType_WATER_DEEP;
|
baseType = (humidity < 0.5) ? BGType_WATER_SHALLOW : BGType_WATER_DEEP;
|
||||||
} else if (terrain < 0.35) {
|
} else if (terrain < 0.35) {
|
||||||
@@ -694,6 +730,9 @@ void genInitMap() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (finalType > BGType_END) {
|
||||||
|
finalType = BGType_COBBLE0;
|
||||||
|
}
|
||||||
backgroundMap[y][x].type = finalType;
|
backgroundMap[y][x].type = finalType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -74,26 +74,26 @@ inline void adjustRect(SDL_Rect *rect, SDL_Rect playerRect) {
|
|||||||
// return x > 0 && y > 0 && x < DISPLAY_WIDTH && y < DISPLAY_HEIGHT;
|
// return x > 0 && y > 0 && x < DISPLAY_WIDTH && y < DISPLAY_HEIGHT;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//bool isInboundsRect(SDL_Rect rect) {
|
//bool isInboundsRect(SDL_Rect tileRect) {
|
||||||
// if (isInbounds(rect.x, rect.y)) {
|
// if (isInbounds(tileRect.x, tileRect.y)) {
|
||||||
// return true;
|
// return true;
|
||||||
// }
|
// }
|
||||||
// if (rect.x < 0) {
|
// if (tileRect.x < 0) {
|
||||||
// rect.x += rect.w;
|
// tileRect.x += tileRect.w;
|
||||||
// }
|
// }
|
||||||
// if (rect.y < 0) {
|
// if (tileRect.y < 0) {
|
||||||
// rect.y += rect.h;
|
// tileRect.y += tileRect.h;
|
||||||
// }
|
// }
|
||||||
// if (isInbounds(rect.x, rect.y)) {
|
// if (isInbounds(tileRect.x, tileRect.y)) {
|
||||||
// return true;
|
// return true;
|
||||||
// }
|
// }
|
||||||
// if (rect.x > DISPLAY_WIDTH) {
|
// if (tileRect.x > DISPLAY_WIDTH) {
|
||||||
// rect.x -= rect.w;
|
// tileRect.x -= tileRect.w;
|
||||||
// }
|
// }
|
||||||
// if (rect.y > DISPLAY_HEIGHT) {
|
// if (tileRect.y > DISPLAY_HEIGHT) {
|
||||||
// rect.y -= rect.h;
|
// tileRect.y -= tileRect.h;
|
||||||
// }
|
// }
|
||||||
// return isInbounds(rect.x, rect.y);
|
// return isInbounds(tileRect.x, tileRect.y);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
void initPlayer(Player *plr) {
|
void initPlayer(Player *plr) {
|
||||||
@@ -163,7 +163,6 @@ void renderPlayer(Player *plr) {
|
|||||||
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 0);
|
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 0);
|
||||||
|
|
||||||
SDL_SetRenderTarget(mainRenderer, entityTexture);
|
SDL_SetRenderTarget(mainRenderer, entityTexture);
|
||||||
SDL_RenderClear(mainRenderer);
|
|
||||||
SDL_RenderCopy(mainRenderer, atlasTexture, &playerTextureRect, &PlayerRect);
|
SDL_RenderCopy(mainRenderer, atlasTexture, &playerTextureRect, &PlayerRect);
|
||||||
//SDL_RenderCopy(mainRenderer, PlayerTexture, NULL, &PlayerRect);
|
//SDL_RenderCopy(mainRenderer, PlayerTexture, NULL, &PlayerRect);
|
||||||
SDL_SetRenderTarget(mainRenderer, hudTexture);
|
SDL_SetRenderTarget(mainRenderer, hudTexture);
|
||||||
|
@@ -18,7 +18,7 @@ extern SDL_Texture *hudTexture;
|
|||||||
|
|
||||||
//bool isInbounds(int x, int y);
|
//bool isInbounds(int x, int y);
|
||||||
//
|
//
|
||||||
//bool isInboundsRect(SDL_Rect rect);
|
//bool isInboundsRect(SDL_Rect tileRect);
|
||||||
//
|
//
|
||||||
//bool isInboundsTile(int x, int y);
|
//bool isInboundsTile(int x, int y);
|
||||||
|
|
||||||
@@ -58,6 +58,7 @@ typedef struct Player{
|
|||||||
uint8_t prevHealth;
|
uint8_t prevHealth;
|
||||||
uint8_t healthIdle;
|
uint8_t healthIdle;
|
||||||
SDL_Rect rect;
|
SDL_Rect rect;
|
||||||
|
MiniRect tileRect;
|
||||||
} Player;
|
} Player;
|
||||||
|
|
||||||
void setActivePlayerSlot(Player *plr, ItemType activeSlotIndex);
|
void setActivePlayerSlot(Player *plr, ItemType activeSlotIndex);
|
||||||
|
35
tiles/tile.c
35
tiles/tile.c
@@ -104,10 +104,10 @@ void registerTile(char fname[20], SDL_Renderer *renderer) {
|
|||||||
NULL,
|
NULL,
|
||||||
texture,
|
texture,
|
||||||
NULL,
|
NULL,
|
||||||
createFlippedTexture(renderer, texture, SDL_FLIP_HORIZONTAL),
|
|
||||||
NULL,
|
|
||||||
createRotatedTexture(renderer, texture, 90),
|
createRotatedTexture(renderer, texture, 90),
|
||||||
NULL,
|
NULL,
|
||||||
|
createFlippedTexture(renderer, texture, SDL_FLIP_HORIZONTAL),
|
||||||
|
NULL,
|
||||||
createRotatedTexture(renderer, texture, 270)
|
createRotatedTexture(renderer, texture, 270)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -118,6 +118,7 @@ void registerTile(char fname[20], SDL_Renderer *renderer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TileRegistry[indexTile].type = indexTile;
|
TileRegistry[indexTile].type = indexTile;
|
||||||
|
TileRegistry[indexTile].maxHealth = 200;
|
||||||
TileRegistry[indexTile].animation.frameCount = frame + 1;
|
TileRegistry[indexTile].animation.frameCount = frame + 1;
|
||||||
TileRegistry[indexTile].animation.divisor = 1;
|
TileRegistry[indexTile].animation.divisor = 1;
|
||||||
|
|
||||||
@@ -162,18 +163,13 @@ void registerBackgroundTile(char fname[20], SDL_Renderer *renderer) {
|
|||||||
BackgroundTileRegistry[indexBgTile].type = indexBgTile;
|
BackgroundTileRegistry[indexBgTile].type = indexBgTile;
|
||||||
BackgroundTileRegistry[indexBgTile].animation.frameCount = frame + 1;
|
BackgroundTileRegistry[indexBgTile].animation.frameCount = frame + 1;
|
||||||
BackgroundTileRegistry[indexBgTile].animation.divisor = 1;
|
BackgroundTileRegistry[indexBgTile].animation.divisor = 1;
|
||||||
|
BackgroundTileRegistry[indexBgTile].walkable = true;
|
||||||
|
|
||||||
if (indexBgTile + 1 > backgroundTileTypeIndex) {
|
if (indexBgTile + 1 > backgroundTileTypeIndex) {
|
||||||
backgroundTileTypeIndex = indexBgTile + 1;
|
backgroundTileTypeIndex = indexBgTile + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int compareStrings(const void *a, const void *b) {
|
|
||||||
const char *strA = *(const char **) a;
|
|
||||||
const char *strB = *(const char **) b;
|
|
||||||
return strcmp(strA, strB);
|
|
||||||
}
|
|
||||||
|
|
||||||
void loadTiles(SDL_Renderer *renderer) {
|
void loadTiles(SDL_Renderer *renderer) {
|
||||||
DIR *dir = opendir("./assets/tiles");
|
DIR *dir = opendir("./assets/tiles");
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
@@ -281,15 +277,19 @@ void setupTiles() {
|
|||||||
TileRegistry[TYPE_FURNACE].needsTicks = true;
|
TileRegistry[TYPE_FURNACE].needsTicks = true;
|
||||||
TileRegistry[TYPE_FURNACE].animation.divisor = 8;
|
TileRegistry[TYPE_FURNACE].animation.divisor = 8;
|
||||||
TileRegistry[TYPE_BELT].needsTicks = true;
|
TileRegistry[TYPE_BELT].needsTicks = true;
|
||||||
|
TileRegistry[TYPE_BELT].walkable = true;
|
||||||
TileRegistry[TYPE_MINER].needsTicks = true;
|
TileRegistry[TYPE_MINER].needsTicks = true;
|
||||||
TileRegistry[TYPE_MINER].outputLane[MINER_OUTPUT_SLOT] = 1;
|
TileRegistry[TYPE_MINER].outputLane[MINER_OUTPUT_SLOT] = 1;
|
||||||
TileRegistry[TYPE_MINER].startFrame = 1;
|
TileRegistry[TYPE_MINER].startFrame = 1;
|
||||||
|
TileRegistry[TYPE_AIR].walkable = true;
|
||||||
|
|
||||||
BackgroundTileRegistry[BGType_WATER_DEEP].animation.divisor = 16;
|
BackgroundTileRegistry[BGType_WATER_DEEP].animation.divisor = 16;
|
||||||
BackgroundTileRegistry[BGType_WATER_SHALLOW].animation.divisor = 12;
|
BackgroundTileRegistry[BGType_WATER_SHALLOW].animation.divisor = 12;
|
||||||
BackgroundTileRegistry[BGType_GRASS_FLOWER0].animation.divisor = 16;
|
BackgroundTileRegistry[BGType_GRASS_FLOWER0].animation.divisor = 16;
|
||||||
BackgroundTileRegistry[BGType_GRASS_FLOWER1].animation.divisor = 16;
|
BackgroundTileRegistry[BGType_GRASS_FLOWER1].animation.divisor = 16;
|
||||||
BackgroundTileRegistry[BGType_GRASS_FLOWER2].animation.divisor = 16;
|
BackgroundTileRegistry[BGType_GRASS_FLOWER2].animation.divisor = 16;
|
||||||
|
BackgroundTileRegistry[BGType_WATER_SHALLOW].walkable = false;
|
||||||
|
BackgroundTileRegistry[BGType_WATER_DEEP].walkable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t getBreakTime(int type) {
|
uint16_t getBreakTime(int type) {
|
||||||
@@ -347,6 +347,12 @@ void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect) {
|
|||||||
adjustRect(&dstRect, playerRect);
|
adjustRect(&dstRect, playerRect);
|
||||||
|
|
||||||
BackgroundTile bt = backgroundMap[y][x];
|
BackgroundTile bt = backgroundMap[y][x];
|
||||||
|
if (bt.type > BGType_END) {
|
||||||
|
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
|
||||||
|
SDL_RenderFillRect(renderer, &dstRect);
|
||||||
|
printf("Error on tile %d, %d\n", x, y);
|
||||||
|
backgroundMap[y][x].type = BGType_PLATINUM_ORE;
|
||||||
|
} else {
|
||||||
SDL_Texture *tex = BackgroundTileRegistry[bt.type].animation.textures[animationStep %
|
SDL_Texture *tex = BackgroundTileRegistry[bt.type].animation.textures[animationStep %
|
||||||
BackgroundTileRegistry[bt.type].animation.frameCount];
|
BackgroundTileRegistry[bt.type].animation.frameCount];
|
||||||
SDL_Rect atlRect = BackgroundTileRegistry[bt.type].animation.atlasRects[
|
SDL_Rect atlRect = BackgroundTileRegistry[bt.type].animation.atlasRects[
|
||||||
@@ -358,6 +364,7 @@ void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SDL_SetRenderTarget(renderer, tilesTexture);
|
SDL_SetRenderTarget(renderer, tilesTexture);
|
||||||
SDL_RenderClear(renderer);
|
SDL_RenderClear(renderer);
|
||||||
@@ -398,6 +405,11 @@ void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect) {
|
|||||||
if (atlRect.w != 0 && atlRect.h != 0) {
|
if (atlRect.w != 0 && atlRect.h != 0) {
|
||||||
//SDL_RenderCopy(renderer, tex, NULL, &dstRect);
|
//SDL_RenderCopy(renderer, tex, NULL, &dstRect);
|
||||||
SDL_RenderCopy(renderer, atlasTexture, &atlRect, &dstRect);
|
SDL_RenderCopy(renderer, atlasTexture, &atlRect, &dstRect);
|
||||||
|
// if (t.health < TileRegistry[t.type].maxHealth) {
|
||||||
|
// SDL_Color tileHealthColor = {(t.health / TileRegistry[t.type].maxHealth ) * 255, (TileRegistry[t.type].maxHealth / t.health) * 255, 0, 255};
|
||||||
|
// renderBar(mainRenderer, x * TILE_SIZE, (y * TILE_SIZE) + (TILE_SIZE / 2), TILE_SIZE, 8,
|
||||||
|
// TileRegistry[t.type].maxHealth, t.health, tileHealthColor, 4);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -437,6 +449,13 @@ void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect) {
|
|||||||
SDL_SetRenderTarget(renderer, oldTarget);
|
SDL_SetRenderTarget(renderer, oldTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isWalkable(MiniRect tileCoords) {
|
||||||
|
BackgroundTileType bgt = BackgroundTileRegistry[backgroundMap[tileCoords.y][tileCoords.x].type];
|
||||||
|
TileTypeReg fgt = TileRegistry[tileMap[tileCoords.y][tileCoords.x].type];
|
||||||
|
|
||||||
|
return bgt.walkable && fgt.walkable;
|
||||||
|
}
|
||||||
|
|
||||||
void updateTiles() {
|
void updateTiles() {
|
||||||
|
|
||||||
}
|
}
|
28
tiles/tile.h
28
tiles/tile.h
@@ -8,8 +8,8 @@
|
|||||||
#include "../util/util.h"
|
#include "../util/util.h"
|
||||||
#include "../items/item.h"
|
#include "../items/item.h"
|
||||||
|
|
||||||
#define MAP_WIDTH 1000
|
#define MAP_WIDTH 500
|
||||||
#define MAP_HEIGHT 1000
|
#define MAP_HEIGHT 500
|
||||||
|
|
||||||
#define DISPLAY_MAP_WIDTH 60
|
#define DISPLAY_MAP_WIDTH 60
|
||||||
#define DISPLAY_MAP_HEIGHT 31
|
#define DISPLAY_MAP_HEIGHT 31
|
||||||
@@ -93,17 +93,19 @@ typedef struct TileTypeReg {
|
|||||||
bool outputLane[ItemSlotCount];
|
bool outputLane[ItemSlotCount];
|
||||||
bool needsTicks;
|
bool needsTicks;
|
||||||
char startFrame;
|
char startFrame;
|
||||||
|
bool walkable;
|
||||||
|
uint16_t maxHealth;
|
||||||
} TileTypeReg;
|
} TileTypeReg;
|
||||||
|
|
||||||
|
bool isWalkable(MiniRect tileCoords);
|
||||||
|
|
||||||
typedef struct BackgroundTileType {
|
typedef struct BackgroundTileType {
|
||||||
ItemType type;
|
ItemType type;
|
||||||
char name[20];
|
char name[20];
|
||||||
Animation animation;
|
Animation animation;
|
||||||
|
bool walkable;
|
||||||
} BackgroundTileType;
|
} BackgroundTileType;
|
||||||
|
|
||||||
typedef struct BackgroundTile {
|
|
||||||
BackgroundType type;
|
|
||||||
} BackgroundTile;
|
|
||||||
|
|
||||||
#define TILEREGISTRY_SIZE 64
|
#define TILEREGISTRY_SIZE 64
|
||||||
|
|
||||||
@@ -115,6 +117,20 @@ void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect);
|
|||||||
|
|
||||||
void updateTiles();
|
void updateTiles();
|
||||||
|
|
||||||
|
typedef struct PathFindDat {
|
||||||
|
int gCost;
|
||||||
|
int hCost;
|
||||||
|
struct PathFindDat* parent;
|
||||||
|
bool closed;
|
||||||
|
bool open;
|
||||||
|
uint32_t version;
|
||||||
|
} PathFindDat;
|
||||||
|
|
||||||
|
typedef struct BackgroundTile {
|
||||||
|
BackgroundType type;
|
||||||
|
PathFindDat pathFind;
|
||||||
|
} BackgroundTile;
|
||||||
|
|
||||||
typedef struct Tile {
|
typedef struct Tile {
|
||||||
OrientDirection direction;
|
OrientDirection direction;
|
||||||
ItemType type;
|
ItemType type;
|
||||||
@@ -124,6 +140,8 @@ typedef struct Tile {
|
|||||||
MiniRect rect;
|
MiniRect rect;
|
||||||
int neededUpdateIndex;
|
int neededUpdateIndex;
|
||||||
char fixedFrame;
|
char fixedFrame;
|
||||||
|
PathFindDat pathFind;
|
||||||
|
uint16_t health;
|
||||||
} Tile;
|
} Tile;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -33,7 +33,7 @@ void storeRect(SDL_Rect rect) {
|
|||||||
if (allocatedRectCount < MAX_RECTS) {
|
if (allocatedRectCount < MAX_RECTS) {
|
||||||
allocatedRects[allocatedRectCount++] = rect;
|
allocatedRects[allocatedRectCount++] = rect;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Error: atlas rect limit reached!\n");
|
fprintf(stderr, "Error: atlas tileRect limit reached!\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -106,7 +106,7 @@ void audio_callback(void *userdata, Uint8 *stream, int len) {
|
|||||||
|
|
||||||
float gainL = 1;
|
float gainL = 1;
|
||||||
float gainR = 1;
|
float gainR = 1;
|
||||||
float targetAmp = (voice->volume / 255.0f);
|
float targetAmp = (voice->volume / 255.0f) / 2;
|
||||||
if (v < NUM_SYNTH_VOICES - MIDI_VOICES) {
|
if (v < NUM_SYNTH_VOICES - MIDI_VOICES) {
|
||||||
float distanceAtten = 1.0f - fminf(fabsf(dx) / audio->maxPanDistance, 1.0f);
|
float distanceAtten = 1.0f - fminf(fabsf(dx) / audio->maxPanDistance, 1.0f);
|
||||||
targetAmp *= distanceAtten;
|
targetAmp *= distanceAtten;
|
||||||
|
128
util/pathfinding.c
Normal file
128
util/pathfinding.c
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
//
|
||||||
|
// Created by bruno on 7.6.2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "pathfinding.h"
|
||||||
|
|
||||||
|
uint32_t globalPathfindingVersion = 1;
|
||||||
|
|
||||||
|
Node openList[MAX_OPEN_NODES];
|
||||||
|
int openCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
void clear_pathfind_data() {
|
||||||
|
for (int y = 0; y < MAP_HEIGHT; ++y)
|
||||||
|
for (int x = 0; x < MAP_WIDTH; ++x)
|
||||||
|
tileMap[y][x].pathFind = (PathFindDat) {0};
|
||||||
|
}
|
||||||
|
|
||||||
|
int heuristic(MiniRect a, MiniRect b) {
|
||||||
|
return abs(a.x - b.x) + abs(a.y - b.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void add_to_open(MiniRect pos, int fCost) {
|
||||||
|
if (openCount < MAX_OPEN_NODES)
|
||||||
|
openList[openCount++] = (Node) {pos.x, pos.y, fCost};
|
||||||
|
}
|
||||||
|
|
||||||
|
Node pop_best_node() {
|
||||||
|
int bestIdx = 0;
|
||||||
|
for (int i = 1; i < openCount; ++i) {
|
||||||
|
if (openList[i].fCost < openList[bestIdx].fCost)
|
||||||
|
bestIdx = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node best = openList[bestIdx];
|
||||||
|
openList[bestIdx] = openList[--openCount];
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool find_path(MiniRect start, MiniRect end) {
|
||||||
|
clear_pathfind_data();
|
||||||
|
openCount = 0;
|
||||||
|
|
||||||
|
Tile *startT = &tileMap[start.y][start.x];
|
||||||
|
startT->pathFind.gCost = 0;
|
||||||
|
startT->pathFind.hCost = heuristic(start, end);
|
||||||
|
startT->pathFind.open = true;
|
||||||
|
|
||||||
|
add_to_open(start, startT->pathFind.gCost + startT->pathFind.hCost);
|
||||||
|
|
||||||
|
const int dirs[4][2] = {
|
||||||
|
{0, -1},
|
||||||
|
{1, 0},
|
||||||
|
{0, 1},
|
||||||
|
{-1, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
while (openCount > 0) {
|
||||||
|
Node current = pop_best_node();
|
||||||
|
PathFindDat *currentPath = &tileMap[current.pos.y][current.pos.x].pathFind;
|
||||||
|
currentPath->open = false;
|
||||||
|
currentPath->closed = true;
|
||||||
|
|
||||||
|
if (memcmp(¤t.pos, &end, sizeof(MiniRect)) == 0) {
|
||||||
|
return true; // Path found!
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
MiniRect n = {current.pos.x + dirs[i][0], current.pos.y + dirs[i][1]};
|
||||||
|
|
||||||
|
if (!isWalkable(n)) continue;
|
||||||
|
|
||||||
|
PathFindDat *neighbor = &tileMap[n.y][n.x].pathFind;
|
||||||
|
|
||||||
|
if (neighbor->closed) continue;
|
||||||
|
|
||||||
|
int tentativeG = currentPath->gCost + 1;
|
||||||
|
|
||||||
|
if (!neighbor->open || tentativeG < neighbor->gCost) {
|
||||||
|
neighbor->gCost = tentativeG;
|
||||||
|
neighbor->hCost = heuristic(n, end);
|
||||||
|
neighbor->parent = currentPath;
|
||||||
|
|
||||||
|
if (!neighbor->open) {
|
||||||
|
neighbor->open = true;
|
||||||
|
add_to_open(n, neighbor->gCost + neighbor->hCost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // No path found
|
||||||
|
}
|
||||||
|
|
||||||
|
Path reconstruct_path(MiniRect end) {
|
||||||
|
Path path = { .length = 0 };
|
||||||
|
|
||||||
|
PathFindDat* current = &tileMap[end.y][end.x].pathFind;
|
||||||
|
|
||||||
|
// Walk back through the parent pointers
|
||||||
|
while (current != NULL) {
|
||||||
|
// Find the tile coordinates for the current node
|
||||||
|
for (int y = 0; y < MAP_HEIGHT; ++y) {
|
||||||
|
for (int x = 0; x < MAP_WIDTH; ++x) {
|
||||||
|
if (&tileMap[y][x].pathFind == current) {
|
||||||
|
if (path.length < MAX_OPEN_NODES) {
|
||||||
|
path.steps[path.length++] = (MiniRect){ x, y };
|
||||||
|
}
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found:
|
||||||
|
current = current->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse the path to go from start to end
|
||||||
|
for (int i = 0; i < path.length / 2; ++i) {
|
||||||
|
MiniRect tmp = path.steps[i];
|
||||||
|
path.steps[i] = path.steps[path.length - i - 1];
|
||||||
|
path.steps[path.length - i - 1] = tmp;
|
||||||
|
}
|
||||||
|
path.stepIndex = 0;
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
42
util/pathfinding.h
Normal file
42
util/pathfinding.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
//
|
||||||
|
// Created by bruno on 7.6.2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef FACTORYGAME_PATHFINDING_H
|
||||||
|
#define FACTORYGAME_PATHFINDING_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "../tiles/tile.h"
|
||||||
|
|
||||||
|
//extern uint32_t globalPathfindingVersion;
|
||||||
|
//#define MAX_OPEN_NODES (MAP_WIDTH * MAP_HEIGHT)
|
||||||
|
#define MAX_OPEN_NODES 100
|
||||||
|
|
||||||
|
typedef struct Node {
|
||||||
|
MiniRect pos;
|
||||||
|
int fCost;
|
||||||
|
} Node;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
MiniRect steps[MAX_OPEN_NODES];
|
||||||
|
int stepIndex;
|
||||||
|
int length;
|
||||||
|
} Path;
|
||||||
|
|
||||||
|
|
||||||
|
extern Node openList[MAX_OPEN_NODES];
|
||||||
|
extern int openCount;
|
||||||
|
|
||||||
|
int heuristic(MiniRect a, MiniRect b);
|
||||||
|
|
||||||
|
Path reconstruct_path(MiniRect end);
|
||||||
|
|
||||||
|
bool find_path(MiniRect start, MiniRect end);
|
||||||
|
|
||||||
|
Node pop_best_node();
|
||||||
|
|
||||||
|
void add_to_open(MiniRect pos, int fCost);
|
||||||
|
|
||||||
|
void clear_pathfind_data();
|
||||||
|
|
||||||
|
#endif //FACTORYGAME_PATHFINDING_H
|
109
util/util.c
109
util/util.c
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "../tiles/tile.h"
|
||||||
|
#include "font.h"
|
||||||
//#include "font.h"
|
//#include "font.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -132,7 +134,7 @@ void renderBar(SDL_Renderer *renderer,
|
|||||||
char barString[20];
|
char barString[20];
|
||||||
sprintf(barString, "%d/%d", currentValue, maxValue);
|
sprintf(barString, "%d/%d", currentValue, maxValue);
|
||||||
|
|
||||||
//renderText(mainRenderer, fonts[3], barString, width / 2, margin);
|
renderText(mainRenderer, fonts[3], barString, width / 2, margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cmpstringp(const void *p1, const void *p2) {
|
int cmpstringp(const void *p1, const void *p2) {
|
||||||
@@ -179,3 +181,108 @@ void iterateSortedDir(const char *path, DirEntryCallback callback, SDL_Renderer
|
|||||||
free(names);
|
free(names);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool checkCollision(SDL_Rect a, SDL_Rect b) {
|
||||||
|
//The sides of the rectangles
|
||||||
|
int leftA, leftB;
|
||||||
|
int rightA, rightB;
|
||||||
|
int topA, topB;
|
||||||
|
int bottomA, bottomB;
|
||||||
|
|
||||||
|
//Calculate the sides of tileRect A
|
||||||
|
leftA = a.x;
|
||||||
|
rightA = a.x + a.w;
|
||||||
|
topA = a.y;
|
||||||
|
bottomA = a.y + a.h;
|
||||||
|
|
||||||
|
//Calculate the sides of tileRect B
|
||||||
|
leftB = b.x;
|
||||||
|
rightB = b.x + b.w;
|
||||||
|
topB = b.y;
|
||||||
|
bottomB = b.y + b.h;
|
||||||
|
|
||||||
|
//If any of the sides from A are outside of B
|
||||||
|
if (bottomA <= topB) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topA >= bottomB) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightA <= leftB) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftA >= rightB) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//If none of the sides from A are outside B
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canMoveTo(SDL_Rect newRect) {
|
||||||
|
// Round down to get all tiles the rect overlaps
|
||||||
|
int left = newRect.x / TILE_SIZE;
|
||||||
|
int right = (newRect.x + newRect.w - 1) / TILE_SIZE;
|
||||||
|
int top = newRect.y / TILE_SIZE;
|
||||||
|
int bottom = (newRect.y + newRect.h - 1) / TILE_SIZE;
|
||||||
|
|
||||||
|
for (int tx = left; tx <= right; ++tx) {
|
||||||
|
for (int ty = top; ty <= bottom; ++ty) {
|
||||||
|
MiniRect tile = { tx, ty };
|
||||||
|
if (!isWalkable(tile)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canMoveWithRadius(SDL_Rect centerRect) {
|
||||||
|
// 16px radius — create a square bounding box around center
|
||||||
|
int radius = 16;
|
||||||
|
|
||||||
|
int left = (centerRect.x - radius) / TILE_SIZE;
|
||||||
|
int right = (centerRect.x + radius - 1) / TILE_SIZE;
|
||||||
|
int top = (centerRect.y - radius) / TILE_SIZE;
|
||||||
|
int bottom = (centerRect.y + radius - 1) / TILE_SIZE;
|
||||||
|
|
||||||
|
int x, y;
|
||||||
|
for (x = left; x <= right; x++) {
|
||||||
|
for (y = top; y <= bottom; y++) {
|
||||||
|
MiniRect tile;
|
||||||
|
tile.x = x;
|
||||||
|
tile.y = y;
|
||||||
|
|
||||||
|
if (!isWalkable(tile)) {
|
||||||
|
// Get pixel bounds of tile
|
||||||
|
int tileLeft = x * TILE_SIZE;
|
||||||
|
int tileRight = tileLeft + TILE_SIZE;
|
||||||
|
int tileTop = y * TILE_SIZE;
|
||||||
|
int tileBottom = tileTop + TILE_SIZE;
|
||||||
|
|
||||||
|
// Bounding box of the player
|
||||||
|
int playerLeft = centerRect.x - radius;
|
||||||
|
int playerRight = centerRect.x + radius;
|
||||||
|
int playerTop = centerRect.y - radius;
|
||||||
|
int playerBottom = centerRect.y + radius;
|
||||||
|
|
||||||
|
// AABB collision check
|
||||||
|
if (playerRight > tileLeft && playerLeft < tileRight &&
|
||||||
|
playerBottom > tileTop && playerTop < tileBottom) {
|
||||||
|
return 0; // Collision
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1; // No collisions
|
||||||
|
}
|
||||||
|
|
||||||
|
int compareStrings(const void *a, const void *b) {
|
||||||
|
const char *strA = *(const char **) a;
|
||||||
|
const char *strB = *(const char **) b;
|
||||||
|
return strcmp(strA, strB);
|
||||||
|
}
|
@@ -72,4 +72,12 @@ void renderBar(SDL_Renderer *renderer,
|
|||||||
int maxValue, int currentValue,
|
int maxValue, int currentValue,
|
||||||
SDL_Color barColor, int margin);
|
SDL_Color barColor, int margin);
|
||||||
|
|
||||||
|
bool checkCollision(SDL_Rect a, SDL_Rect b);
|
||||||
|
|
||||||
|
int compareStrings(const void *a, const void *b);
|
||||||
|
|
||||||
|
bool canMoveTo(SDL_Rect newRect);
|
||||||
|
|
||||||
|
bool canMoveWithRadius(SDL_Rect centerRect);
|
||||||
|
|
||||||
#endif //FACTORYGAME_UTIL_H
|
#endif //FACTORYGAME_UTIL_H
|
Reference in New Issue
Block a user