Files
factorygame/util/util.c

330 lines
9.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Created by bruno on 4/24/25.
//
#include <dirent.h>
#include "util.h"
#include "../tiles/tile.h"
#include "font.h"
//#include "font.h"
ScreenType screenType = SCREEN_MENU;
//The window we'll be rendering to
SDL_Window *window = NULL;
volatile bool running = true;
WaveInfo waveInfo;
const char OrientStrings[ORIENT_DIRECTION_COUNT][10] = {
"LEFT_DOWN",
"LEFT",
"LEFT_UP",
"UP",
"RIGHT_UP",
"RIGHT",
"RIGHT_DOWN",
"DOWN",
};
int animationStep = 0;
bool debugMode = false;
bool itemViewing = false;
bool renderAtlas = false;
//The surface contained by the window
SDL_Renderer *mainRenderer = NULL;
SDL_Rect screenRect;
SDL_Texture *createFlippedTexture(SDL_Renderer *renderer, SDL_Texture *src, SDL_RendererFlip flip) {
int w, h;
SDL_QueryTexture(src, NULL, NULL, &w, &h);
SDL_Texture *renderTarget = SDL_GetRenderTarget(renderer);
SDL_Texture *target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h);
SDL_SetRenderTarget(renderer, target);
SDL_RenderCopyEx(renderer, src, NULL, NULL, 0, NULL, flip);
SDL_SetRenderTarget(renderer, renderTarget);
return target;
}
SDL_Texture *createRotatedTexture(SDL_Renderer *renderer, SDL_Texture *src, double angle) {
int w, h;
SDL_QueryTexture(src, NULL, NULL, &w, &h);
SDL_Texture *renderTarget = SDL_GetRenderTarget(renderer);
SDL_Texture *target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h);
SDL_SetRenderTarget(renderer, target);
SDL_RenderCopyEx(renderer, src, NULL, NULL, angle, NULL, SDL_FLIP_NONE);
SDL_SetRenderTarget(renderer, renderTarget);
return target;
}
SDL_Texture *ScaleTexture(SDL_Renderer *renderer, SDL_Texture *src, int newWidth, int newHeight) {
SDL_Texture *scaledTex = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_RGBA8888,
SDL_TEXTUREACCESS_TARGET,
newWidth,
newHeight);
if (!scaledTex) {
SDL_Log("Failed to create target texture: %s", SDL_GetError());
return nullptr;
}
// Save current render target
SDL_Texture *oldTarget = SDL_GetRenderTarget(renderer);
SDL_SetRenderTarget(renderer, scaledTex);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
SDL_Rect dst = {0, 0, newWidth, newHeight};
SDL_RenderCopy(renderer, src, NULL, &dst);
SDL_SetRenderTarget(renderer, oldTarget); // Restore
return scaledTex;
}
void DrawThickRect(SDL_Renderer *renderer, SDL_Rect rect, int thickness) {
for (int i = 0; i < thickness; i++) {
SDL_Rect r = {rect.x - i, rect.y - i, rect.w + i * 2, rect.h + i * 2};
SDL_RenderDrawRect(renderer, &r);
}
}
void renderBar(SDL_Renderer *renderer,
int x, int y, int width, int height,
int maxValue, int currentValue,
SDL_Color barColor, int margin) {
if (maxValue <= 0) return; // Avoid division by zero
// Clamp value
if (currentValue < 0) currentValue = 0;
if (currentValue == 0) {
return;
}
if (currentValue > maxValue) currentValue = maxValue;
// Calculate filled width based on currentValue
int filledWidth = (width * currentValue) / maxValue;
// Bar rectangle
SDL_Rect barRect = {x, y, filledWidth, height};
// Background rectangle with margin
SDL_Rect bgRect = {
x - margin,
y - margin,
width + margin * 2,
height + margin * 2
};
// Draw background (black)
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderFillRect(renderer, &bgRect);
// Draw bar with provided color
SDL_SetRenderDrawColor(renderer, barColor.r, barColor.g, barColor.b, barColor.a);
SDL_RenderFillRect(renderer, &barRect);
char barString[20];
sprintf(barString, "%d/%d", currentValue, maxValue);
renderText(mainRenderer, fonts[1], barString, x + (width / 2 - (fonts[2].size * strlen(barString))),
y - fonts[2].size - barRect.h);
}
int cmpstringp(const void *p1, const void *p2) {
return strcmp(*(const char **) p1, *(const char **) p2);
}
// Helper function to iterate over sorted entries in a directory
void iterateSortedDir(const char *path, DirEntryCallback callback, SDL_Renderer *renderer) {
DIR *dir = opendir(path);
if (!dir) {
perror("opendir");
return;
}
struct dirent *entry;
char **names = NULL;
size_t count = 0;
// Collect file names
while ((entry = readdir(dir)) != NULL) {
if (entry->d_name[0] == '.') continue;
names = realloc(names, sizeof(char *) * (count + 1));
if (!names) {
perror("realloc");
closedir(dir);
return;
}
names[count++] = strdup(entry->d_name);
}
closedir(dir);
// Sort entries
qsort(names, count, sizeof(char *), cmpstringp);
// Call the user-provided function for each file
if (names != NULL) {
for (size_t i = 0; i < count; i++) {
if (names[i] != NULL) {
callback(names[i], renderer);
free(names[i]);
}
}
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 mainPlayer
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);
}
double angle_between_points_deg(double x1, double y1, double x2, double y2) {
double dx = x2 - x1;
double dy = y2 - y1;
double angle_rad = atan2(dy, dx);
double angle_deg = angle_rad * (180.0 / M_PI);
// Normalize to 0360 degrees (optional, depending on use case)
if (angle_deg < 0) {
angle_deg += 360.0;
}
return angle_deg;
}
#define TICKS_PER_SECOND 60
void initWaveInfo(WaveInfo *info) {
info->waveCounter = 0;
info->waveTimer = 200 * TICKS_PER_SECOND; // 200 seconds before first wave
info->waveRunning = false;
info->totalWaves = MAX_WAVES;
for (int i = 0; i < MAX_WAVES; i++) {
int flyerCount = 1 + i * 2; // Each wave increases flyer count
info->waves[i].enemyCount = 1;
info->waves[i].enemies[0] = (EnemyEntry) {ENEMY_TYPE_FLYER, flyerCount};
// Reduce time between waves gradually (but not below 5 seconds)
int secondsBetween = 15 - (i / 5);
if (secondsBetween < 5) secondsBetween = 5;
info->waves[i].timeUntilNext = secondsBetween * TICKS_PER_SECOND;
}
}