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

212
util/button.c Normal file
View File

@@ -0,0 +1,212 @@
//
// Created by bruno on 11.6.2025.
//
#include "button.h"
#include "gamestate.h"
void onStart() {
genInitMap();
screenType = SCREEN_GAME;
}
void onContinue() {
if (loadGameState(autosaveName, &mainPlayer)) {
genInitMap();
}
screenType = SCREEN_GAME;
}
void onCredits() {
screenType = SCREEN_CREDITS;
}
void onNext() {
if (!waveInfo.waveRunning && waveInfo.waveTimer > 60 * 5) {
waveInfo.waveTimer = 60 * 5;
}
}
void onQuit() {
running = false;
}
void onBack() {
screenType = SCREEN_MENU;
}
void onAtlas() {
screenType = SCREEN_ATLAS;
}
void onFont() {
screenType = SCREEN_FONTS;
}
Button buttons[5][BUTTON_COUNT];
void initButtons(void) {
buttons[0][0] = (Button){
.label = "New",
.rect = {110, DISPLAY_HEIGHT / 2, 125, 40},
.color = {100, 100, 0, 255},
.callback = onStart,
.font = &fonts[1]
};
buttons[0][1] = (Button){
.label = "Continue",
.rect = {100, DISPLAY_HEIGHT / 2 + 45, 150, 40},
.color = {0, 100, 0, 255},
.callback = onContinue,
.font = &fonts[1]
};
buttons[0][2] = (Button){
.label = "Credits",
.rect = {DISPLAY_WIDTH - 200, DISPLAY_HEIGHT - 120, 150, 40},
.color = {0, 50, 50, 255},
.callback = onCredits,
.font = &fonts[1]
};
buttons[0][3] = (Button){
.label = "Exit",
.rect = {DISPLAY_WIDTH - 200, DISPLAY_HEIGHT - 40, 100, 40},
.color = {100, 0, 0, 255},
.callback = onQuit,
.font = &fonts[1]
};
buttons[1][0] = (Button){
.label = "Back",
.rect = {DISPLAY_WIDTH / 2 - (100 / 2), DISPLAY_HEIGHT - 40, 100, 40},
.color = {100, 0, 0, 255},
.callback = onBack,
.font = &fonts[1]
};
buttons[1][1] = (Button){
.label = "Font atlas",
.rect = {DISPLAY_WIDTH - 900, DISPLAY_HEIGHT - 40, 200, 40},
.color = {100, 0, 0, 255},
.callback = onFont,
.font = &fonts[1]
};
buttons[1][2] = (Button){
.label = "Texture atlas",
.rect = {DISPLAY_WIDTH - 300, DISPLAY_HEIGHT - 40, 250, 40},
.color = {100, 0, 0, 255},
.callback = onAtlas,
.font = &fonts[1]
};
buttons[2][0] = (Button){
.label = "Next",
.rect = {395, 0, 55, 80},
.color = {64, 64, 64, 64},
.callback = onNext,
.font = &fonts[2]
};
buttons[3][0] = (Button){
.label = "Back",
.rect = {DISPLAY_WIDTH / 2 - (400 / 2), DISPLAY_HEIGHT - 40, 100, 40},
.color = {100, 0, 0, 255},
.callback = onCredits,
.font = &fonts[1]
};
buttons[4][0] = (Button){
.label = "Back",
.rect = {DISPLAY_WIDTH / 2 - (400 / 2), DISPLAY_HEIGHT - 40, 100, 40},
.color = {100, 0, 0, 255},
.callback = onCredits,
.font = &fonts[1]
};
}
void renderButtons(SDL_Renderer *renderer, Player player) {
for (int i = 0; i < BUTTON_COUNT; i++) {
renderButton(renderer, buttons[screenType][i], player);
}
}
void renderButton(SDL_Renderer *renderer, Button btn, Player player) {
if (btn.font == NULL || btn.label == NULL || strlen(btn.label) == 0) {
return;
}
// Check for hover/click
bool hovered = (player.cursor.windowX >= btn.rect.x && player.cursor.windowX <= btn.rect.x + btn.rect.w &&
player.cursor.windowY >= btn.rect.y && player.cursor.windowY <= btn.rect.y + btn.rect.h);
// Set color and draw background
SDL_Color bgColor = btn.color;
if (hovered) {
bgColor.a = btn.color.a / 2;
}
SDL_SetRenderDrawColor(renderer, bgColor.r, bgColor.g, bgColor.b, bgColor.a);
SDL_RenderFillRect(renderer, &btn.rect);
// Split label into lines
const char *label = btn.label;
const int maxLines = 10; // Hard cap for sanity
const char *lines[maxLines];
int lineCount = 0;
lines[lineCount++] = label;
for (const char *p = label; *p && lineCount < maxLines; p++) {
if (*p == '\n') {
lines[lineCount++] = p + 1;
}
}
// Get length of each line and find max width (for centering)
int maxLineWidth = 0;
int lineWidths[maxLines];
int fontSize = btn.font->size;
for (int i = 0; i < lineCount; i++) {
const char *lineStart = lines[i];
const char *lineEnd = strchr(lineStart, '\n');
int lineLen = lineEnd ? (lineEnd - lineStart) : strlen(lineStart);
int lineWidth = lineLen * fontSize;
lineWidths[i] = lineWidth;
if (lineWidth > maxLineWidth) {
maxLineWidth = lineWidth;
}
}
// Calculate starting Y to vertically center all lines
int totalHeight = lineCount * fontSize;
int startY = btn.rect.y + (btn.rect.h - totalHeight) / 2;
// Render each line centered horizontally
for (int i = 0; i < lineCount; i++) {
const char *lineStart = lines[i];
const char *lineEnd = strchr(lineStart, '\n');
int lineLen = lineEnd ? (lineEnd - lineStart) : strlen(lineStart);
char buffer[256];
strncpy(buffer, lineStart, lineLen);
buffer[lineLen] = '\0';
int x = btn.rect.x + (btn.rect.w - lineWidths[i]) / 2;
int y = startY + i * fontSize;
renderText(renderer, *btn.font, buffer, x, y);
}
// Trigger callback if clicked
if (hovered && (player.mouseButtons & SDL_BUTTON_LMASK)) {
if (btn.callback) {
btn.callback();
}
}
}

28
util/button.h Normal file
View File

@@ -0,0 +1,28 @@
//
// Created by bruno on 11.6.2025.
//
#ifndef FACTORYGAME_BUTTON_H
#define FACTORYGAME_BUTTON_H
#include "SDL2/SDL.h"
#include "font.h"
#include "../player/player.h"
typedef struct {
char* label;
SDL_Rect rect;
SDL_Color color;
void (*callback)(void);
BitmapFont *font;
} Button;
#define BUTTON_COUNT 10
extern Button buttons[5][BUTTON_COUNT];
void initButtons(void);
void renderButton(SDL_Renderer* renderer, Button btn, Player player);
void renderButtons(SDL_Renderer *renderer, Player player);
#endif //FACTORYGAME_BUTTON_H

View File

@@ -17,7 +17,7 @@ enum {
MACHINE_SLOTS = 3
};
typedef struct {
typedef struct MachineRecipe {
ItemType input1;
ItemType input2; // use TYPE_AIR if single-input
ItemType output;
@@ -34,4 +34,4 @@ bool findMachineRecipe(const MachineRecipe *recipes, size_t count, ItemType in1,
void initMachineTile(ItemType type, const MachineRecipe *recipes, size_t count,
uint8_t startFrame, uint8_t divisor);
#endif //FACTORYGAME_CRAFTER_H
#endif //FACTORYGAME_AMMOCRAFTER_H

61
util/gamestate.c Normal file
View File

@@ -0,0 +1,61 @@
//
// Created by bruno on 11.6.2025.
//
#include "gamestate.h"
GameState gameState;
char *autosaveName = "autosave.dat";
int loadGameState(char *filename, Player *plr) {
fflush(stdout);
FILE *gameSave = fopen(filename, "rb");
if (gameSave) {
fseek(gameSave, 0L, SEEK_END);
long sz = ftell(gameSave);
if (sz != sizeof(gameState)) {
return 1;
}
rewind(gameSave);
fread(&gameState, sizeof(gameState), 1, gameSave);
fclose(gameSave);
memcpy(plr, &gameState.player, sizeof(gameState.player));
memcpy(tileMap, gameState.tileMap, sizeof(tileMap));
memcpy(backgroundMap, gameState.backgroundTileMap, sizeof(backgroundMap));
SDL_Rect *tmp = audioData.playerRect;
memcpy(&audioData, &gameState.audioData, sizeof(gameState.audioData));
audioData.playerRect = tmp;
audioData.totalSamples = 0;
memcpy(&neededUpdates, &gameState.neededUpdates, sizeof(gameState.neededUpdates));
memcpy(&entities, &gameState.entities, sizeof(gameState.entities));
openCount = gameState.openCount;
memcpy(&openList, &gameState.openList, sizeof(gameState.openList));
memcpy(&enemySpawn, &gameState.enemySpawn, sizeof(gameState.enemySpawn));
plr->cursor.targetTile = NULL;
plr->cursor.prevTargetTile = NULL;
return 0;
}
return 1;
}
void saveGameState(char *filename, Player *plr) {
memcpy(&gameState.player, plr, sizeof(gameState.player));
memcpy(gameState.tileMap, tileMap, sizeof(gameState.tileMap));
memcpy(gameState.backgroundTileMap, backgroundMap, sizeof(gameState.backgroundTileMap));
memcpy(&gameState.audioData, &audioData, sizeof(gameState.audioData));
memcpy(&gameState.neededUpdates, &neededUpdates, sizeof(neededUpdates));
memcpy(&gameState.entities, &entities, sizeof(entities));
memcpy(&gameState.openList, &openList, sizeof(openList));
memcpy(&gameState.enemySpawn, &enemySpawn, sizeof(enemySpawn));
gameState.openCount = openCount;
FILE *gameSave = fopen(filename, "wb");
if (!gameSave) {
perror("Failed to open file for saving");
return;
}
fwrite(&gameState, sizeof(gameState), 1, gameSave);
fclose(gameSave);
}

33
util/gamestate.h Normal file
View File

@@ -0,0 +1,33 @@
//
// Created by bruno on 11.6.2025.
//
#ifndef FACTORYGAME_GAMESTATE_H
#define FACTORYGAME_GAMESTATE_H
#include "../tiles/tile.h"
#include "../player/player.h"
#include "audio.h"
#include "../entity/entity.h"
typedef struct GameState {
Player player;
Tile tileMap[MAP_HEIGHT][MAP_WIDTH];
BackgroundTile backgroundTileMap[MAP_HEIGHT][MAP_WIDTH];
AudioData audioData;
TileArray neededUpdates;
EntityArray entities;
Node openList[MAX_OPEN_NODES];
int openCount;
MiniRect enemySpawn;
} GameState;
int loadGameState(char *filename, Player *plr);
void saveGameState(char *filename, Player *plr);
extern GameState gameState;
extern char *autosaveName;
#endif //FACTORYGAME_GAMESTATE_H

View File

@@ -9,6 +9,8 @@
//#include "font.h"
ScreenType screenType = SCREEN_MENU;
//The window we'll be rendering to
SDL_Window *window = NULL;
volatile bool running = true;
@@ -136,7 +138,8 @@ void renderBar(SDL_Renderer *renderer,
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);
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) {
@@ -226,14 +229,14 @@ bool checkCollision(SDL_Rect a, SDL_Rect b) {
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 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 };
MiniRect tile = {tx, ty};
if (!isWalkable(tile)) {
return false;
}
@@ -265,7 +268,7 @@ bool canMoveWithRadius(SDL_Rect centerRect) {
int tileTop = y * TILE_SIZE;
int tileBottom = tileTop + TILE_SIZE;
// Bounding box of the player
// Bounding box of the mainPlayer
int playerLeft = centerRect.x - radius;
int playerRight = centerRect.x + radius;
int playerTop = centerRect.y - radius;
@@ -287,4 +290,40 @@ 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;
}
}

View File

@@ -49,13 +49,50 @@ typedef struct MiniRect {
int y;
} MiniRect;
#define MAX_ENEMIES_PER_WAVE 16
#define MAX_WAVES 128
// Define enemy types (expand as needed)
typedef enum EnemyType {
ENEMY_TYPE_NONE = 0,
ENEMY_TYPE_FLYER,
// Add more enemy types here
} EnemyType;
typedef struct EnemyEntry {
EnemyType type;
int count;
} EnemyEntry;
typedef struct Wave {
EnemyEntry enemies[MAX_ENEMIES_PER_WAVE];
int enemyCount; // Number of different enemy types in this wave
int timeUntilNext; // Time until the next wave (in ticks, ms, etc.)
} Wave;
typedef struct WaveInfo {
int waveCounter;
int waveTimer;
int waveCounter; // Current wave number (index)
int waveTimer; // Countdown until next wave starts
bool waveRunning; // Whether a wave is currently running
Wave waves[MAX_WAVES]; // List of all waves
int totalWaves; // Total number of defined waves
} WaveInfo;
typedef enum ScreenType {
SCREEN_MENU,
SCREEN_CREDITS,
SCREEN_GAME,
SCREEN_FONTS,
SCREEN_ATLAS,
} ScreenType;
extern ScreenType screenType;
extern WaveInfo waveInfo;
void initWaveInfo(WaveInfo *info);
typedef struct OrientedAnimation {
SDL_Rect atlasRects[ORIENT_DIRECTION_COUNT][TILE_SIZE * 2];
@@ -92,4 +129,6 @@ bool canMoveTo(SDL_Rect newRect);
bool canMoveWithRadius(SDL_Rect centerRect);
double angle_between_points_deg(double x1, double y1, double x2, double y2);
#endif //FACTORYGAME_UTIL_H