Files
factorygame/tiles/tile.c
2025-06-08 17:22:30 +02:00

461 lines
16 KiB
C

//
// Created by bruno on 4/24/25.
//
#include <dirent.h>
#include "tile.h"
#include "../player/player.h"
#include "furnace.h"
#include "../util/atlas.h"
#include "../util/font.h"
#include "miner.h"
int scrollFrame = 0;
unsigned long beltFrames = 0;
TileArray neededUpdates;
int add_tile(TileArray *arr, MiniRect t) {
if (arr->activeCount >= MAX_TILES) return 0;
arr->tiles[arr->activeCount] = t;
arr->activeCount++;
return arr->activeCount - 1;
}
void remove_tile(TileArray *arr, int index) {
if (index < 0 || index >= arr->activeCount) return;
arr->activeCount--;
arr->tiles[index] = arr->tiles[arr->activeCount]; // swap with last active
}
SDL_Texture *backgroundTexture;
SDL_Texture *tilesTexture;
SDL_Texture *itemsTexture;
Tile tileMap[MAP_HEIGHT][MAP_WIDTH];
BackgroundTile backgroundMap[MAP_HEIGHT][MAP_WIDTH];
uint16_t tileTypeIndex = 0;
uint16_t backgroundTileTypeIndex = 0;
TileTypeReg TileRegistry[TILEREGISTRY_SIZE];
BackgroundTileType BackgroundTileRegistry[TILEREGISTRY_SIZE];
void generateTestMap() {
for (int y = 0; y < DISPLAY_MAP_HEIGHT; y++) {
for (int x = 0; x < DISPLAY_MAP_WIDTH; x++) {
Tile tile = {0};
tile.rect.x = x;
tile.rect.y = y;
tileMap[y][x] = tile;
}
}
for (int x = 0; x < MAP_WIDTH; x += 1) {
for (int y = 0; y < MAP_HEIGHT; y += 1) {
tileMap[y][x].type = TYPE_BELT;
tileMap[y][x].miscVal = 0;
//tileMap[y][x].direction = ((x + y) % 4 * 2) + 1;
//tileMap[y][x].direction = 5;
tileMap[y][x].direction = (rand() % 4 * 2) + 1;
}
}
uint8_t type = 0;
for (int x = 142; x < 154; x += 3) {
for (int y = 80; y < 94; y += 3) {
putItem(x, y, type++ % ITEMREGISTRY_SIZE, 0);
}
}
}
void registerTile(char fname[20], SDL_Renderer *renderer) {
char name[21];
// Load animation frames
int frame = 0;
int indexTile = 0;
char texturePath[80];
if (sscanf(fname, "%d_%20[^_]_%d.png", &indexTile, name, &frame) == 3) {
// Success: you now have index, fname, and frame
} else {
fprintf(stderr, "Invalid format: %s\n", fname);
}
strcpy(TileRegistry[indexTile].name, name);
snprintf(texturePath, sizeof(texturePath), "./assets/tiles/%s", fname);
SDL_Texture *texture = IMG_LoadTexture(renderer, texturePath);
if (!texture) {
// Stop loading frames if the first one fails, or after all valid ones are added
if (frame == 0) {
fprintf(stderr, "Failed to load tile texture %s: %s\n", texturePath, IMG_GetError());
}
}
for (int o = 0; o < ORIENT_DIRECTION_COUNT; o++) {
SDL_Texture *textures[ORIENT_DIRECTION_COUNT] = {
NULL,
texture,
NULL,
createRotatedTexture(renderer, texture, 90),
NULL,
createFlippedTexture(renderer, texture, SDL_FLIP_HORIZONTAL),
NULL,
createRotatedTexture(renderer, texture, 270)
};
printf("Bound %s to %d orient %s\n", fname, indexTile, OrientStrings[o]);
TileRegistry[indexTile].animation.textures[o][frame] = textures[o];
SDL_SetTextureBlendMode(textures[o], SDL_BLENDMODE_BLEND);
TileRegistry[indexTile].animation.atlasRects[o][frame] = allocate_32x32(textures[o], renderer);
}
TileRegistry[indexTile].type = indexTile;
TileRegistry[indexTile].maxHealth = 200;
TileRegistry[indexTile].animation.frameCount = frame + 1;
TileRegistry[indexTile].animation.divisor = 1;
TileRegistry[indexTile].type = tileTypeIndex;
TileRegistry[indexTile].breakTime = 15;
if (indexTile + 1 > tileTypeIndex) {
tileTypeIndex = indexTile + 1;
}
}
void registerBackgroundTile(char fname[20], SDL_Renderer *renderer) {
char name[21];
// Load animation frames
int frame = 0;
int indexBgTile = 0;
char texturePath[80];
if (sscanf(fname, "%d_%20[^_]_%d.png", &indexBgTile, name, &frame) == 3) {
// Success: you now have index, fname, and frame
} else {
fprintf(stderr, "Invalid format: %s\n", fname);
}
strcpy(BackgroundTileRegistry[indexBgTile].name, name);
snprintf(texturePath, sizeof(texturePath), "./assets/backgrounds/%s", fname);
SDL_Texture *texture = IMG_LoadTexture(renderer, texturePath);
if (!texture) {
if (frame == 0) {
fprintf(stderr, "Failed to load background texture %s: %s\n", texturePath, IMG_GetError());
}
}
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_NONE);
printf("Bound %s to %d\n", fname, indexBgTile);
BackgroundTileRegistry[indexBgTile].animation.textures[frame] = texture;
BackgroundTileRegistry[indexBgTile].animation.atlasRects[frame] = allocate_32x32(texture, renderer);
BackgroundTileRegistry[indexBgTile].type = indexBgTile;
BackgroundTileRegistry[indexBgTile].animation.frameCount = frame + 1;
BackgroundTileRegistry[indexBgTile].animation.divisor = 1;
BackgroundTileRegistry[indexBgTile].walkable = true;
if (indexBgTile + 1 > backgroundTileTypeIndex) {
backgroundTileTypeIndex = indexBgTile + 1;
}
}
void loadTiles(SDL_Renderer *renderer) {
DIR *dir = opendir("./assets/tiles");
if (!dir) {
perror("Failed to open tiles directory");
return;
}
char *baseNames[MAX_BASE_NAMES];
int baseNameCount = 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 < baseNameCount; ++i) {
if (strcmp(baseNames[i], entry->d_name) == 0) {
found = 1;
break;
}
}
if (!found && baseNameCount < MAX_BASE_NAMES) {
baseNames[baseNameCount++] = strdup(entry->d_name); // Only store base, not full file name
}
}
closedir(dir);
qsort(baseNames, baseNameCount, sizeof(char *), compareStrings);
// Call registerTile on each base name
for (int i = 0; i < baseNameCount; ++i) {
char fileName[64];
snprintf(fileName, sizeof(fileName), "%s", baseNames[i]);
registerTile(fileName, renderer);
free(baseNames[i]);
}
generateBeltFrames(renderer);
}
void loadBackgroundTiles(SDL_Renderer *renderer) {
DIR *dir = opendir("./assets/backgrounds");
if (!dir) {
perror("Failed to open backgrounds directory");
return;
}
char *baseNames[MAX_BASE_NAMES];
int baseNameCount = 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 < baseNameCount; ++i) {
if (strcmp(baseNames[i], entry->d_name) == 0) {
found = 1;
break;
}
}
if (!found && baseNameCount < MAX_BASE_NAMES) {
baseNames[baseNameCount++] = strdup(entry->d_name); // Only store base, not full file name
}
}
closedir(dir);
qsort(baseNames, baseNameCount, sizeof(char *), compareStrings);
// Call registerBackgroundTile on each base name
for (int i = 0; i < baseNameCount; ++i) {
char fileName[64];
snprintf(fileName, sizeof(fileName), "%s", baseNames[i]);
registerBackgroundTile(fileName, renderer);
free(baseNames[i]);
}
}
void setupTiles() {
TileRegistry[TYPE_AIR].breakTime = 0;
TileRegistry[TYPE_BELT].itemMoves = true;
for (uint16_t i = 0; i < ItemSlotCount; i++) {
TileRegistry[TYPE_BELT].outputLane[i] = true;
}
for (uint16_t l = 0; l < ItemSlotCount; l++) {
for (ItemType i = 0; i < itemRegistryIndex; i++) {
TileRegistry[TYPE_BELT].allowedInItems[l][i] = true;
}
}
for (ItemType i = 0; i < itemRegistryIndex; i++) {
if (FurnaceRecipes[i] != 0) {
TileRegistry[TYPE_FURNACE].allowedInItems[FURNACE_INPUT_SLOT][i] = true;
}
}
TileRegistry[TYPE_FURNACE].outputLane[FURNACE_OUTPUT_SLOT] = 1;
TileRegistry[TYPE_FURNACE].startFrame = 1;
TileRegistry[TYPE_FURNACE].needsTicks = true;
TileRegistry[TYPE_FURNACE].animation.divisor = 8;
TileRegistry[TYPE_BELT].needsTicks = true;
TileRegistry[TYPE_BELT].walkable = true;
TileRegistry[TYPE_MINER].needsTicks = true;
TileRegistry[TYPE_MINER].outputLane[MINER_OUTPUT_SLOT] = 1;
TileRegistry[TYPE_MINER].startFrame = 1;
TileRegistry[TYPE_AIR].walkable = true;
BackgroundTileRegistry[BGType_WATER_DEEP].animation.divisor = 16;
BackgroundTileRegistry[BGType_WATER_SHALLOW].animation.divisor = 12;
BackgroundTileRegistry[BGType_GRASS_FLOWER0].animation.divisor = 16;
BackgroundTileRegistry[BGType_GRASS_FLOWER1].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) {
if (type >= tileTypeIndex) {
return 0;
}
return TileRegistry[type].breakTime;
}
void initTiles() {
itemsTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, screenRect.w,
screenRect.h);
tilesTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, screenRect.w,
screenRect.h);
backgroundTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
screenRect.w,
screenRect.h);
SDL_SetTextureBlendMode(itemsTexture, SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(tilesTexture, SDL_BLENDMODE_BLEND);
}
void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect) {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_Texture *oldTarget = SDL_GetRenderTarget(renderer);
int tileSize = TILE_SIZE;
int minTileX = (playerRect.x / TILE_SIZE) - (DISPLAY_MAP_WIDTH / 2) - 1;
int maxTileX = (playerRect.x / TILE_SIZE) + (DISPLAY_MAP_WIDTH / 2) + 1;
int minTileY = (playerRect.y / TILE_SIZE) - (DISPLAY_MAP_HEIGHT / 2) - 1;
int maxTileY = (playerRect.y / TILE_SIZE) + (DISPLAY_MAP_HEIGHT / 2) + 1;
int scrollSpeed = 1; // pixels per step
int scrollDelay = 1; // frames between steps
if (beltFrames++ % scrollDelay == 0) {
scrollFrame += scrollSpeed;
}
// --- Render background tiles ---
SDL_SetRenderTarget(renderer, backgroundTexture);
SDL_RenderClear(renderer);
for (int y = minTileY; y < maxTileY; y++) {
if (y < 0 || y >= MAP_HEIGHT) continue;
for (int x = minTileX; x < maxTileX; x++) {
if (x < 0 || x >= MAP_WIDTH) continue;
SDL_Rect dstRect = {
.x = x * TILE_SIZE,
.y = y * TILE_SIZE,
.w = TILE_SIZE,
.h = TILE_SIZE
};
adjustRect(&dstRect, playerRect);
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 %
BackgroundTileRegistry[bt.type].animation.frameCount];
SDL_Rect atlRect = BackgroundTileRegistry[bt.type].animation.atlasRects[
(animationStep / BackgroundTileRegistry[bt.type].animation.divisor) %
BackgroundTileRegistry[bt.type].animation.frameCount];
if (atlRect.w != 0 && atlRect.h != 0) {
SDL_RenderCopy(renderer, atlasTexture, &atlRect, &dstRect);
//SDL_RenderCopy(renderer, tex, NULL, &dstRect);
}
}
}
}
SDL_SetRenderTarget(renderer, tilesTexture);
SDL_RenderClear(renderer);
for (int y = minTileY; y < maxTileY; y++) {
if (y < 0 || y >= MAP_HEIGHT) continue;
for (int x = minTileX; x < maxTileX; x++) {
if (x < 0 || x >= MAP_WIDTH) continue;
SDL_Rect dstRect = {
.x = x * TILE_SIZE,
.y = y * TILE_SIZE,
.w = TILE_SIZE,
.h = TILE_SIZE
};
adjustRect(&dstRect, playerRect);
Tile t = tileMap[y][x];
switch (t.type) {
case TYPE_AIR:
break;
case TYPE_BELT:
renderBelt(x, y, tileSize, tileSize, t.direction, playerRect, renderer);
break;
default: {
char animationFrame = ((animationStep / TileRegistry[t.type].animation.divisor) %
(TileRegistry[t.type].animation.frameCount -
TileRegistry[t.type].startFrame)) + TileRegistry[t.type].startFrame;
if (t.fixedFrame > 0) {
animationFrame = t.fixedFrame - 1;
}
SDL_Rect atlRect = TileRegistry[t.type].animation.atlasRects[t.direction][animationFrame];
SDL_Texture *tex = TileRegistry[t.type].animation.textures[t.direction][animationFrame];
if (atlRect.w == 0 || atlRect.h == 0) {
tex = TileRegistry[t.type].animation.textures[ORIENT_LEFT][animationFrame];
atlRect = TileRegistry[t.type].animation.atlasRects[ORIENT_LEFT][
animationFrame];
}
if (atlRect.w != 0 && atlRect.h != 0) {
//SDL_RenderCopy(renderer, tex, NULL, &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);
// }
}
}
}
}
}
SDL_SetRenderTarget(renderer, itemsTexture);
SDL_RenderClear(renderer);
for (int y = minTileY; y < maxTileY; y++) {
if (y < 0 || y >= MAP_HEIGHT) continue;
for (int x = minTileX; x < maxTileX; x++) {
if (x < 0 || x >= MAP_WIDTH) continue;
Tile t = tileMap[y][x];
if (debugMode) {
char locChar[20];
sprintf(locChar, "X:%d\nY:%d", x, y);
SDL_Rect dstRect = {
.x = x * TILE_SIZE,
.y = y * TILE_SIZE,
.w = TILE_SIZE,
.h = TILE_SIZE
};
adjustRect(&dstRect, playerRect);
renderText(renderer, fonts[3], locChar, dstRect.x, dstRect.y);
}
if (t.type == TYPE_BELT || itemViewing) {
for (uint8_t lane = 0; lane < ItemSlotCount; lane++) {
if (t.items[lane].type != 0) {
renderItem(t.items[lane], renderer, lane, playerRect);
}
}
}
}
}
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() {
}