Files
factorygame/main.c
2025-04-23 22:55:22 +02:00

369 lines
9.9 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.
#include <SDL2/SDL.h>
#include <stdio.h>
#include <time.h>
#include "util/font.h"
#include "util/audio.h"
#include <SDL2/SDL_image.h>
#define MAP_WIDTH 64
#define MAP_HEIGHT 36
#define TILE_SIZE 16
#define DISPLAY_WIDTH MAP_WIDTH * TILE_SIZE
#define DISPLAY_HEIGHT MAP_HEIGHT * TILE_SIZE
typedef enum {
BELT_LEFT_DOWN,
BELT_LEFT,
BELT_LEFT_UP,
BELT_UP,
BELT_RIGHT_UP,
BELT_RIGHT,
BELT_RIGHT_DOWN,
BELT_DOWN
} BeltDirection;
typedef struct {
float x, y; // local position in tile (0.01.0)
bool active;
} Item;
typedef struct {
bool hasBelt;
BeltDirection direction;
int frameOffset;
Item item;
} Tile;
Tile tileMap[MAP_HEIGHT][MAP_WIDTH];
//Screen dimension constants
const int SCREEN_WIDTH = DISPLAY_WIDTH;
const int SCREEN_HEIGHT = DISPLAY_HEIGHT;
const int targetFPS = 60;
const int delayNeeded = 1000 / targetFPS;
//The window we'll be rendering to
SDL_Window *window = NULL;
volatile bool running = true;
//The surface contained by the window
SDL_Renderer *renderer = NULL;
#define biggerFont fonts[0]
#define smallFont fonts[1]
#define smallerFont fonts[2]
#define fontCount 3
BitmapFont fonts[fontCount];
unsigned long frames = 0;
bool cursor = true;
SDL_Texture *beltTex;
void msleep(unsigned int milliseconds) {
struct timespec ts;
ts.tv_sec = milliseconds / 1000;
ts.tv_nsec = (milliseconds % 1000) * 1000000;
nanosleep(&ts, NULL);
}
void generateTestMap() {
for (int y = 0; y < MAP_HEIGHT; y++) {
for (int x = 0; x < MAP_WIDTH; x++) {
tileMap[y][x] = (Tile){0};
}
}
for (int x = 5; x < 5 + (4*2); x+=2) {
tileMap[10][x].hasBelt = true;
tileMap[10][x].frameOffset = 0;
}
tileMap[10][5].direction = BELT_LEFT;
tileMap[10][7].direction = BELT_RIGHT;
tileMap[10][9].direction = BELT_UP;
tileMap[10][11].direction = BELT_DOWN;
tileMap[10][5].item = (Item){.x = 0.5f, .y = 0.5f, .active = true};
}
int init() {
//Initialize SDL
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, NULL);
SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "1");
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
return 1;
}
//Initialize SDL_ttf
if (TTF_Init() == -1) {
printf("SDL_ttf could not initialize! SDL_ttf Error: %s\n", TTF_GetError());
return 1;
}
//Create window
window = SDL_CreateWindow("Factory game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
SCREEN_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if (window == NULL) {
printf("Window could not be created! SDL_Error: %s\n", SDL_GetError());
return 1;
}
//Get window surface
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (renderer == NULL) {
printf("Renderer could not be created SDL_Error: %s\n", SDL_GetError());
return 1;
}
// Create OpenGL context
SDL_GLContext glContext = SDL_GL_CreateContext(window);
if (!glContext) {
fprintf(stderr, "SDL_GL_CreateContext failed: %s\n", SDL_GetError());
exit(1);
}
// Use OpenGL context
SDL_GL_MakeCurrent(window, glContext); // Make sure OpenGL context is current before any OpenGL rendering
SDL_AudioSpec spec = {0};
spec.freq = SAMPLE_RATE;
spec.format = AUDIO_F32SYS;
spec.channels = 1;
spec.samples = 4096;
spec.callback = audio_callback;
spec.userdata = &audioData;
SDL_AudioDeviceID dev = SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0);
if (dev == 0) {
printf("Failed to open audio: %s\n", SDL_GetError());
SDL_Quit();
}
SDL_PauseAudioDevice(dev, 0);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_SetRenderTarget(renderer, NULL);
SDL_Rect viewport = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
SDL_RenderSetViewport(renderer, &viewport);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
biggerFont = prepText(renderer, 16, "PublicPixel.ttf", 255, 255, 255, 255);
smallFont = prepText(renderer, 12, "PublicPixel.ttf", 255, 255, 255, 255);
smallerFont = prepText(renderer, 8, "PublicPixel.ttf", 255, 255, 255, 255);
SDL_RenderSetLogicalSize(renderer, SCREEN_WIDTH, SCREEN_HEIGHT);
beltTex = IMG_LoadTexture(renderer, "../assets/dopravnik.png");
generateTestMap();
return 0;
}
void updateItems() {
for (int y = 0; y < MAP_HEIGHT; y++) {
for (int x = 0; x < MAP_WIDTH; x++) {
Tile *t = &tileMap[y][x];
if (!t->hasBelt || !t->item.active) continue;
float speed = 0.05f;
switch (t->direction) {
case BELT_LEFT: t->item.x -= speed; break;
case BELT_RIGHT: t->item.x += speed; break;
case BELT_UP: t->item.y -= speed; break;
case BELT_DOWN: t->item.y += speed; break;
default: break;
}
if (t->item.x < 0 || t->item.x > 1 || t->item.y < 0 || t->item.y > 1) {
int nx = x, ny = y;
if (t->direction == BELT_LEFT) nx--;
if (t->direction == BELT_RIGHT) nx++;
if (t->direction == BELT_UP) ny--;
if (t->direction == BELT_DOWN) ny++;
if (nx >= 0 && nx < MAP_WIDTH && ny >= 0 && ny < MAP_HEIGHT) {
Tile *next = &tileMap[ny][nx];
if (next->hasBelt && next->direction == t->direction && !next->item.active) {
next->item = (Item){ .x = 0.5f, .y = 0.5f, .active = true };
t->item.active = false;
}
} else {
t->item.active = false;
}
}
}
}
}
void renderBelt(SDL_Texture *tex, int x, int y, int w, int h, BeltDirection dir) {
int texW, texH;
SDL_QueryTexture(tex, NULL, NULL, &texW, &texH);
static int scrollFrame = 0;
int scrollSpeed = 1; // pixels per step
int scrollDelay = 15; // frames between steps
if (frames % scrollDelay == 0) {
scrollFrame += scrollSpeed;
}
SDL_Rect src1, src2, dst1, dst2;
if (dir == BELT_LEFT || dir == BELT_RIGHT) {
int offset = scrollFrame % texW;
if (dir == BELT_LEFT) {
offset = texW - offset; // reverse scroll
}
src1 = (SDL_Rect){offset, 0, texW - offset, texH};
dst1 = (SDL_Rect){x, y, w - offset, h};
src2 = (SDL_Rect){0, 0, offset, texH};
dst2 = (SDL_Rect){x + (w - offset), y, offset, h};
SDL_RenderCopy(renderer, tex, &src1, &dst1);
SDL_RenderCopy(renderer, tex, &src2, &dst2);
} else {
int offset = scrollFrame % texH;
if (dir == BELT_UP) {
offset = texH - offset; // reverse scroll
}
src1 = (SDL_Rect){0, offset, texW, texH - offset};
dst1 = (SDL_Rect){x, y, w, h - offset};
src2 = (SDL_Rect){0, 0, texW, offset};
dst2 = (SDL_Rect){x, y + (h - offset), w, offset};
SDL_RenderCopy(renderer, tex, &src1, &dst1);
SDL_RenderCopy(renderer, tex, &src2, &dst2);
}
}
void renderAllBelts() {
int tileSize = TILE_SIZE;
for (int y = 0; y < MAP_HEIGHT; y++) {
for (int x = 0; x < MAP_WIDTH; x++) {
Tile t = tileMap[y][x];
if (!t.hasBelt) continue;
int px = x * tileSize;
int py = y * tileSize;
renderBelt(beltTex, px, py, tileSize, tileSize, t.direction);
}
}
}
int render() {
SDL_SetRenderDrawColor(renderer, 32, 32, 32, 255);
SDL_RenderClear(renderer);
SDL_Rect rect2;
rect2.x = 20;
rect2.y = 20;
rect2.w = 0;
rect2.h = 0;
renderAllBelts();
SDL_RenderPresent(renderer);
frames++;
if (!(frames % 60)) {
cursor = !cursor;
}
return 0;
}
int processEvent(SDL_Event e) {
if (e.type == SDL_QUIT) { return 0; }
else if (e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_RESIZED) {
int newWidth = e.window.data1;
int newHeight = e.window.data2;
// Adjust the viewport to match the new window size;
SDL_Rect viewport = {0, 0, newWidth, newHeight};
SDL_RenderSetViewport(renderer, &viewport);
} else if (e.type == SDL_KEYDOWN) {
int keySym = e.key.keysym.sym;
int keyMod = e.key.keysym.mod;
cursor = true;
switch (keySym) {
default:
break;
}
} else if (e.type == SDL_MOUSEBUTTONDOWN) {
SDL_Rect viewport;
SDL_RenderGetViewport(renderer, &viewport);
SDL_Rect mouset;
mouset.w = 1;
mouset.h = 1;
SDL_GetMouseState(&mouset.x, &mouset.y);
// Translate mouse coordinates to viewport space
SDL_Rect mouse;
mouse.w = 1;
mouse.h = 1;
mouse.x = ((mouset.x - viewport.x) * SCREEN_WIDTH) / viewport.w;
mouse.y = (mouset.y - viewport.y) * SCREEN_HEIGHT / viewport.h;
}
return 1;
}
int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[]) {
int status = init();
if (status) {
return status;
}
//Hack to get window to stay up
SDL_Event e;
Uint64 start;
Uint64 end;
while (running) {
start = SDL_GetTicks64();
while (SDL_PollEvent(&e)) {
running = processEvent(e);
}
updateItems();
status = render();
if (status) {
return status;
}
end = SDL_GetTicks64();
const unsigned long timeNeeded = end - start;
if (timeNeeded < delayNeeded) {
SDL_Delay(delayNeeded - timeNeeded);
}
}
for (uint8_t i = 0; i < fontCount; i++) {
destroyFont(&fonts[i]);
}
puts(SDL_GetError());
if (renderer) SDL_DestroyRenderer(renderer);
if (window) SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}