Start atlas

This commit is contained in:
2025-06-01 22:13:02 +02:00
parent 96a9a45c20
commit 84805b92cb
64 changed files with 954 additions and 243 deletions

View File

@@ -9,6 +9,8 @@ pkg_check_modules(SDL2 REQUIRED sdl2)
add_executable(factorygame add_executable(factorygame
tiles/tile.c
tiles/tile.h
util/font.c util/font.c
util/font.h util/font.h
util/audio.c util/audio.c
@@ -17,13 +19,20 @@ add_executable(factorygame
util/util.h util/util.h
items/item.c items/item.c
items/item.h items/item.h
tiles/tile.c
tiles/tile.h
tiles/belt.c tiles/belt.c
tiles/belt.h tiles/belt.h
tiles/furnace.c
tiles/furnace.h
player/player.c player/player.c
player/player.h # Ensure the target is defined before linking player/player.h # Ensure the target is defined before linking
main.c) tiles/tilecallbacks.c
tiles/tilecallbacks.h
main.c
util/perlin.c
util/perlin.h
util/atlas.c
util/atlas.h
)
# Define the path to the assets folder # Define the path to the assets folder
set(ASSETS_SOURCE_DIR "${CMAKE_SOURCE_DIR}/assets") set(ASSETS_SOURCE_DIR "${CMAKE_SOURCE_DIR}/assets")

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 849 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 869 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 814 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 734 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 837 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 546 B

After

Width:  |  Height:  |  Size: 546 B

View File

Before

Width:  |  Height:  |  Size: 566 B

After

Width:  |  Height:  |  Size: 566 B

View File

Before

Width:  |  Height:  |  Size: 526 B

After

Width:  |  Height:  |  Size: 526 B

View File

Before

Width:  |  Height:  |  Size: 516 B

After

Width:  |  Height:  |  Size: 516 B

View File

Before

Width:  |  Height:  |  Size: 492 B

After

Width:  |  Height:  |  Size: 492 B

View File

Before

Width:  |  Height:  |  Size: 530 B

After

Width:  |  Height:  |  Size: 530 B

BIN
assets/tiles/3furnace.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

View File

@@ -5,6 +5,9 @@
#include "item.h" #include "item.h"
#include "../player/player.h" #include "../player/player.h"
#include "../util/font.h" #include "../util/font.h"
#include "../tiles/furnace.h"
#include "../tiles/tilecallbacks.h"
#include "../util/atlas.h"
#include <dirent.h> #include <dirent.h>
Item ItemRegistry[ITEMREGISTRY_SIZE]; Item ItemRegistry[ITEMREGISTRY_SIZE];
@@ -22,22 +25,27 @@ void updateItems() {
for (int y = 0; y < MAP_HEIGHT; y++) { for (int y = 0; y < MAP_HEIGHT; y++) {
for (int x = 0; x < MAP_WIDTH; x++) { for (int x = 0; x < MAP_WIDTH; x++) {
Tile *t = &tileMap[y][x]; Tile *t = &tileMap[y][x];
if (t->type != TYPE_BELT) continue; TileTypeReg tt = TileRegistry[t->type];
if (t->type == TYPE_AIR) continue;
int dir = t->direction; int dir = t->direction;
bool horz = (dir == ORIENT_LEFT || dir == ORIENT_RIGHT); bool horz = (dir == ORIENT_LEFT || dir == ORIENT_RIGHT);
bool vert = (dir == ORIENT_UP || dir == ORIENT_DOWN); bool vert = (dir == ORIENT_UP || dir == ORIENT_DOWN);
for (uint8_t lane = 0; lane < 2; lane++) { for (uint8_t lane = 0; lane < ItemSlotCount; lane++) {
if (!tt.outputLane[lane]) continue;
ItemOnBelt *itm = &t->items[lane]; ItemOnBelt *itm = &t->items[lane];
if (itm->type == 0) continue; if (itm->type == 0) continue;
if (tt.itemMoves) {
// 1) Advance
itm->offset += speed; itm->offset += speed;
// 1) Advance
}
// 2) Time to hop? // 2) Time to hop?
if (itm->offset >= 0.5f) { if (itm->offset >= 0.5f || !tt.itemMoves) {
if (tt.itemMoves) {
itm->offset -= 1.0f; itm->offset -= 1.0f;
}
// target coords // target coords
int nx = x + dirDx[dir]; int nx = x + dirDx[dir];
@@ -45,19 +53,16 @@ void updateItems() {
// bounds & belt? // bounds & belt?
if (nx < 0 || nx >= MAP_WIDTH || ny < 0 || ny >= MAP_HEIGHT) { if (nx < 0 || nx >= MAP_WIDTH || ny < 0 || ny >= MAP_HEIGHT) {
//itm->type = 0; if (tt.itemMoves) {
itm->offset += 1.0f - speed; itm->offset += 1.0f - speed;
}
continue; continue;
} }
Tile *next = &tileMap[ny][nx]; Tile *next = &tileMap[ny][nx];
if (next->type != TYPE_BELT) { TileTypeReg ntt = TileRegistry[next->type];
//itm->type = 0;
itm->offset += 1.0f - speed;
continue;
}
// Decide new lane
int newLane = lane; int newLane = lane;
switch (next->type) {
case TYPE_BELT:
int newDir = next->direction; int newDir = next->direction;
bool nH = (newDir == ORIENT_LEFT || newDir == ORIENT_RIGHT); bool nH = (newDir == ORIENT_LEFT || newDir == ORIENT_RIGHT);
bool nV = (newDir == ORIENT_UP || newDir == ORIENT_DOWN); bool nV = (newDir == ORIENT_UP || newDir == ORIENT_DOWN);
@@ -76,12 +81,21 @@ void updateItems() {
// (diagonals fall back to same-lane) // (diagonals fall back to same-lane)
// Find a free slot in // Find a free slot in
break;
if (next->items[newLane].type == 0) { default:
itm->offset += 1.0f - speed;
}
if (next->items[newLane].type == 0 && ntt.allowedInItems[newLane][itm->type]) {
// MOVE it // MOVE it
ItemOnBelt moved = *itm; ItemOnBelt moved = *itm;
moved.tileX = nx; moved.tileX = nx;
moved.tileY = ny; moved.tileY = ny;
if (!ntt.itemMoves) {
moved.offset = 0.5f;
}
next->items[newLane] = moved; next->items[newLane] = moved;
// clear this one // clear this one
@@ -90,8 +104,15 @@ void updateItems() {
// both slots full → wait at end // both slots full → wait at end
itm->offset = epsilon; itm->offset = epsilon;
} }
} else {
} }
} }
const UpdateTileCallback cb = ItemTileCallbacks[t->type];
if (cb) {
cb(t);
}
} }
} }
} }
@@ -108,7 +129,11 @@ void registerItem(char name[20], SDL_Renderer *renderer) {
ItemRegistry[itemRegistryIndex].texture[ORIENT_LEFT], ItemRegistry[itemRegistryIndex].texture[ORIENT_LEFT],
TILE_SIZE / 2, TILE_SIZE / 2); TILE_SIZE / 2, TILE_SIZE / 2);
SDL_SetTextureBlendMode(ItemRegistry[itemRegistryIndex].textureOnBelt[ORIENT_LEFT], SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(ItemRegistry[itemRegistryIndex].textureOnBelt[ORIENT_LEFT], SDL_BLENDMODE_BLEND);
ItemRegistry[itemRegistryIndex].atlasRects[ORIENT_LEFT] = allocate_32x32(ItemRegistry[itemRegistryIndex].texture[ORIENT_LEFT], renderer);
ItemRegistry[itemRegistryIndex].atlasRectsOnBelt[ORIENT_LEFT] = allocate_16x16(ItemRegistry[itemRegistryIndex].textureOnBelt[ORIENT_LEFT], renderer);
ItemRegistry[itemRegistryIndex].type = itemRegistryIndex; ItemRegistry[itemRegistryIndex].type = itemRegistryIndex;
ItemRegistry[itemRegistryIndex].isTile = false;
ItemRegistry[itemRegistryIndex].miscVal = 60;
itemRegistryIndex++; itemRegistryIndex++;
} }
@@ -196,7 +221,7 @@ void renderItem(ItemOnBelt item, SDL_Renderer *renderer, int lane, SDL_Rect play
yOffset += 0.0f * TILE_SIZE; yOffset += 0.0f * TILE_SIZE;
break; break;
case ORIENT_LEFT: case ORIENT_LEFT:
xOffset += 0.0f * TILE_SIZE + (TILE_SIZE / 2); xOffset += 0.0f * TILE_SIZE + (TILE_SIZE);
yOffset += 0.26f * TILE_SIZE; yOffset += 0.26f * TILE_SIZE;
break; break;
case ORIENT_LEFT_UP: case ORIENT_LEFT_UP:
@@ -205,14 +230,14 @@ void renderItem(ItemOnBelt item, SDL_Renderer *renderer, int lane, SDL_Rect play
break; break;
case ORIENT_UP: case ORIENT_UP:
xOffset += 0.22f * TILE_SIZE; //GOTO HEHREHRHE xOffset += 0.22f * TILE_SIZE; //GOTO HEHREHRHE
yOffset += 0.0f * TILE_SIZE + (TILE_SIZE / 2); yOffset += 0.0f * TILE_SIZE + (TILE_SIZE);
break; break;
case ORIENT_RIGHT_UP: case ORIENT_RIGHT_UP:
xOffset += 0.0f * TILE_SIZE; xOffset += 0.0f * TILE_SIZE;
yOffset += 0.0f * TILE_SIZE; yOffset += 0.0f * TILE_SIZE;
break; break;
case ORIENT_RIGHT: case ORIENT_RIGHT:
xOffset += 0.0f * TILE_SIZE; xOffset += 0.0f * TILE_SIZE - (TILE_SIZE / 2);
yOffset += 0.18f * TILE_SIZE; //FIX THIS yOffset += 0.18f * TILE_SIZE; //FIX THIS
break; break;
case ORIENT_RIGHT_DOWN: case ORIENT_RIGHT_DOWN:
@@ -220,8 +245,8 @@ void renderItem(ItemOnBelt item, SDL_Renderer *renderer, int lane, SDL_Rect play
yOffset += 0.0f * TILE_SIZE; yOffset += 0.0f * TILE_SIZE;
break; break;
case ORIENT_DOWN: case ORIENT_DOWN:
xOffset += 0.18f * TILE_SIZE + (TILE_SIZE / 4); xOffset += 0.18f * TILE_SIZE + (TILE_SIZE / 8);
yOffset += 0.0f * TILE_SIZE; yOffset += 0.0f * TILE_SIZE - (TILE_SIZE / 2);
break; break;
default: default:
break; break;
@@ -275,7 +300,7 @@ void renderItem(ItemOnBelt item, SDL_Renderer *renderer, int lane, SDL_Rect play
} }
} }
void putItem(int x, int y, uint16_t itemType, uint8_t lane) { void putItem(int x, int y, ItemType itemType, uint8_t lane) {
tileMap[y][x].items[lane].type = itemType; tileMap[y][x].items[lane].type = itemType;
tileMap[y][x].items[lane].offset = 0; tileMap[y][x].items[lane].offset = 0;
tileMap[y][x].items[lane].tileX = x; tileMap[y][x].items[lane].tileX = x;
@@ -283,33 +308,29 @@ void putItem(int x, int y, uint16_t itemType, uint8_t lane) {
} }
void loadItems(SDL_Renderer *renderer) { void loadItems(SDL_Renderer *renderer) {
for (int i = 0; i < tileTypeIndex; i++) { for (int i = 0; i < tileTypeIndex; i++) {
TileType tile = TileRegistry[i]; TileTypeReg tile = TileRegistry[i];
strcpy(ItemRegistry[itemRegistryIndex].name, tile.name); strcpy(ItemRegistry[itemRegistryIndex].name, tile.name);
memcpy(ItemRegistry[itemRegistryIndex].texture, tile.textures, sizeof(tile.textures)); memcpy(ItemRegistry[itemRegistryIndex].texture, tile.textures, sizeof(tile.textures));
memcpy(ItemRegistry[itemRegistryIndex].atlasRects, tile.atlasRects, sizeof(tile.atlasRects));
for (int a = 0; a < ORIENT_DIRECTION_COUNT; a++) { for (int a = 0; a < ORIENT_DIRECTION_COUNT; a++) {
SDL_SetTextureBlendMode(ItemRegistry[itemRegistryIndex].texture[a], SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(ItemRegistry[itemRegistryIndex].texture[a], SDL_BLENDMODE_BLEND);
ItemRegistry[itemRegistryIndex].textureOnBelt[a] = ScaleTexture(renderer, tile.textures[a], ItemRegistry[itemRegistryIndex].textureOnBelt[a] = ScaleTexture(renderer, tile.textures[a],
TILE_SIZE / 2, TILE_SIZE / 2); TILE_SIZE / 2, TILE_SIZE / 2);
ItemRegistry[itemRegistryIndex].atlasRectsOnBelt[a] = allocate_16x16(
ItemRegistry[itemRegistryIndex].textureOnBelt[a], renderer);
SDL_SetTextureBlendMode(ItemRegistry[itemRegistryIndex].textureOnBelt[a], SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(ItemRegistry[itemRegistryIndex].textureOnBelt[a], SDL_BLENDMODE_BLEND);
} }
ItemRegistry[itemRegistryIndex].type = itemRegistryIndex; ItemRegistry[itemRegistryIndex].type = itemRegistryIndex;
ItemRegistry[itemRegistryIndex].isTile = true;
itemRegistryIndex++; itemRegistryIndex++;
} }
itemRegistryIndex = ITEMREGISTRY_SIZE / 2;
iterateSortedDir("./assets/items", (DirEntryCallback) registerItem, renderer); iterateSortedDir("./assets/items", (DirEntryCallback) registerItem, renderer);
// DIR *dir = opendir("./assets/items");
// if (dir) {
// struct dirent *entry;
// while ((entry = readdir(dir)) != NULL) {
// if (entry->d_name[0] == '.') {
// continue;
// }
// registerItem(entry->d_name, mainRenderer);
// }
// }
} }

View File

@@ -5,23 +5,46 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include "../util/util.h" #include "../util/util.h"
#include "../tiles/belt.h" #include "../tiles/belt.h"
#include "../tiles/tile.h"
#ifndef FACTORYGAME_ITEM_H #ifndef FACTORYGAME_ITEM_H
#define FACTORYGAME_ITEM_H #define FACTORYGAME_ITEM_H
#define ITEMREGISTRY_SIZE 20 #define ITEMREGISTRY_SIZE 128
typedef struct {
typedef enum ItemType {
TYPE_AIR = 0,
TYPE_BLOCK,
TYPE_BELT,
TYPE_FURNACE,
IRON_ORE = ITEMREGISTRY_SIZE / 2,
SILVER_ORE,
GOLD_ORE,
PLATINUM_ORE,
IRON_INGOT,
SILVER_INGOT,
GOLD_INGOT,
PLATINUM_INGOT,
LOG
} ItemType;
typedef struct ItemOnBelt {
float offset; float offset;
int tileX, tileY; int tileX, tileY;
uint16_t type; ItemType type;
} ItemOnBelt; } ItemOnBelt;
typedef struct { typedef struct Item {
uint16_t type; bool isTile;
ItemType type;
char name[20]; char name[20];
uint16_t miscVal;
SDL_Texture * texture[ORIENT_DIRECTION_COUNT]; SDL_Texture * texture[ORIENT_DIRECTION_COUNT];
SDL_Texture * textureOnBelt[ORIENT_DIRECTION_COUNT]; SDL_Texture * textureOnBelt[ORIENT_DIRECTION_COUNT];
SDL_Rect atlasRects[ORIENT_DIRECTION_COUNT];
SDL_Rect atlasRectsOnBelt[ORIENT_DIRECTION_COUNT];
} Item; } Item;
@@ -39,5 +62,5 @@ extern uint8_t laneTarget;
extern double speed; extern double speed;
void putItem(int x, int y, uint16_t itemType, uint8_t lane); void putItem(int x, int y, ItemType itemType, uint8_t lane);
#endif //FACTORYGAME_ITEM_H #endif //FACTORYGAME_ITEM_H

244
main.c
View File

@@ -7,28 +7,46 @@
#include "items/item.h" #include "items/item.h"
#include "stdlib.h" #include "stdlib.h"
#include "player/player.h" #include "player/player.h"
#include "util/perlin.h"
#include "util/atlas.h"
typedef struct { typedef struct GameState {
Player player; Player player;
Tile tileMap[MAP_HEIGHT][MAP_WIDTH]; Tile tileMap[MAP_HEIGHT][MAP_WIDTH];
BackgroundTile backgroundTileMap[MAP_HEIGHT][MAP_WIDTH];
SynthVoice voices[NUM_SYNTH_VOICES];
} GameState; } GameState;
GameState gameState; GameState gameState;
void loadGameState(char *filename, Player *plr) { int loadGameState(char *filename, Player *plr) {
printf("hello from load\n"); printf("hello from load\n");
fflush(stdout); fflush(stdout);
FILE *gameSave = fopen(filename, "rb"); FILE *gameSave = fopen(filename, "rb");
if (gameSave) { 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); fread(&gameState, sizeof(gameState), 1, gameSave);
fclose(gameSave); fclose(gameSave);
memcpy(tileMap, gameState.tileMap, sizeof(tileMap));
memcpy(plr, &gameState.player, sizeof(gameState.player)); memcpy(plr, &gameState.player, sizeof(gameState.player));
memcpy(tileMap, gameState.tileMap, sizeof(tileMap));
memcpy(backgroundMap, gameState.backgroundTileMap, sizeof(backgroundMap));
memcpy(audioData.synthVoices, gameState.voices, sizeof(gameState.voices));
plr->cursor.targetTile = NULL;
plr->cursor.prevTargetTile = NULL;
return 0;
} }
return 1;
} }
void saveGameState(char *filename, Player *plr) { void saveGameState(char *filename, Player *plr) {
memcpy(&gameState.player, plr, sizeof(gameState.player)); memcpy(&gameState.player, plr, sizeof(gameState.player));
memcpy(gameState.tileMap, tileMap, sizeof(gameState.tileMap)); memcpy(gameState.tileMap, tileMap, sizeof(gameState.tileMap));
memcpy(gameState.backgroundTileMap, backgroundMap, sizeof(gameState.backgroundTileMap));
memcpy(gameState.voices, audioData.synthVoices, sizeof(gameState.voices));
FILE *gameSave = fopen(filename, "wb"); FILE *gameSave = fopen(filename, "wb");
if (!gameSave) { if (!gameSave) {
@@ -51,7 +69,7 @@ const int delayNeeded = 1000 / targetFPS;
#define smallerFont fonts[2] #define smallerFont fonts[2]
#define smallestFont fonts[3] #define smallestFont fonts[3]
const char *autosaveName = "autosave.dat"; char *autosaveName = "autosave.dat";
unsigned long frames = 0; unsigned long frames = 0;
@@ -66,6 +84,8 @@ void msleep(unsigned int milliseconds) {
SDL_GLContext glContext; SDL_GLContext glContext;
void genInitMap();
int init() { int init() {
//Initialize SDL //Initialize SDL
@@ -75,7 +95,9 @@ int init() {
screenRect.w = DISPLAY_WIDTH; screenRect.w = DISPLAY_WIDTH;
screenRect.h = DISPLAY_HEIGHT; screenRect.h = DISPLAY_HEIGHT;
srand(0); srand(time(NULL));
memset(tileMap, 0, sizeof(tileMap));
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, NULL); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, NULL);
SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "1"); SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "1");
@@ -107,8 +129,17 @@ int init() {
return 1; return 1;
} }
initAtlas(mainRenderer);
loadTiles(mainRenderer); loadTiles(mainRenderer);
loadItems(mainRenderer); loadItems(mainRenderer);
setupTiles();
for (ItemType i = 0; i < ITEMREGISTRY_SIZE; i++) {
if (strlen(ItemRegistry[i].name)) {
printf("%d -> %s\n", i, ItemRegistry[i].name);
}
}
// Create OpenGL context // Create OpenGL context
glContext = SDL_GL_CreateContext(window); glContext = SDL_GL_CreateContext(window);
if (!glContext) { if (!glContext) {
@@ -119,21 +150,24 @@ int init() {
// Use OpenGL context // Use OpenGL context
SDL_GL_MakeCurrent(window, glContext); // Make sure OpenGL context is current before any OpenGL rendering SDL_GL_MakeCurrent(window, glContext); // Make sure OpenGL context is current before any OpenGL rendering
audioData.playerRect = &player.rect;
audioData.maxPanDistance = DISPLAY_WIDTH / 2;
SDL_AudioSpec spec = {0}; SDL_AudioSpec spec = {0};
spec.freq = SAMPLE_RATE; spec.freq = SAMPLE_RATE;
spec.format = AUDIO_F32SYS; spec.format = AUDIO_F32;
spec.channels = 1; spec.channels = 2;
spec.samples = 4096; spec.samples = 4096;
spec.callback = audio_callback; spec.callback = audio_callback;
spec.userdata = &audioData; spec.userdata = &audioData;
SDL_AudioDeviceID dev = SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0); // SDL_AudioDeviceID dev = SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0);
if (dev == 0) { // if (dev == 0) {
printf("Failed to open audio: %s\n", SDL_GetError()); // printf("Failed to open audio: %s\n", SDL_GetError());
SDL_Quit(); // SDL_Quit();
} // }
//
SDL_PauseAudioDevice(dev, 0); // SDL_PauseAudioDevice(dev, 1);
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255); SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
SDL_RenderClear(mainRenderer); SDL_RenderClear(mainRenderer);
@@ -150,6 +184,10 @@ int init() {
initPlayer(&player); initPlayer(&player);
for (ItemType i = 0; i < 13; i++) {
player.inventory.hotKeySlots[i] = i;
}
initTiles(); initTiles();
//generateTestMap(); //generateTestMap();
@@ -163,20 +201,22 @@ int render() {
SDL_RenderClear(mainRenderer); SDL_RenderClear(mainRenderer);
SDL_Rect rect2; SDL_Rect rect2;
rect2.x = 20; rect2.x = 0;
rect2.y = 20; rect2.y = 0;
rect2.w = 0; rect2.w = 0;
rect2.h = 0; rect2.h = 0;
renderAllTiles(mainRenderer, player.rect); renderAllTiles(mainRenderer, player.rect);
SDL_RenderCopy(mainRenderer, tilesTexture, &screenRect, &screenRect);
SDL_RenderCopy(mainRenderer, itemsTexture, &screenRect, &screenRect);
SDL_RenderCopy(mainRenderer, entityTexture, &screenRect, &screenRect);
SDL_RenderCopy(mainRenderer, hudTexture, &screenRect, &screenRect);
renderPlayer(&player); renderPlayer(&player);
// SDL_RenderCopy(mainRenderer, backgroundTexture, &screenRect, &screenRect);
// SDL_RenderCopy(mainRenderer, tilesTexture, &screenRect, &screenRect);
// SDL_RenderCopy(mainRenderer, itemsTexture, &screenRect, &screenRect);
// SDL_RenderCopy(mainRenderer, entityTexture, &screenRect, &screenRect);
// SDL_RenderCopy(mainRenderer, hudTexture, &screenRect, &screenRect);
SDL_QueryTexture(atlasTexture, NULL, NULL, &rect2.w, &rect2.h);
SDL_RenderCopy(mainRenderer, atlasTexture, &rect2, &rect2);
SDL_RenderPresent(mainRenderer); SDL_RenderPresent(mainRenderer);
frames++; frames++;
@@ -206,11 +246,11 @@ int processEvent(SDL_Event e) {
speed = speed == 0 ? 0.004f : 0; speed = speed == 0 ? 0.004f : 0;
break; break;
case SDLK_r: case SDLK_r:
if (player.cursor.canReach && player.cursor.targetTile->type == TYPE_BELT) { if (player.cursor.canReach && player.cursor.targetTile->type != TYPE_AIR) {
player.cursor.direction = player.cursor.targetTile->direction; player.cursor.direction = player.cursor.targetTile->direction;
} }
player.cursor.direction = (player.cursor.direction + 2) % ORIENT_DIRECTION_COUNT; player.cursor.direction = (player.cursor.direction + 2) % ORIENT_DIRECTION_COUNT;
if (player.cursor.canReach && player.cursor.targetTile->type == TYPE_BELT) { if (player.cursor.canReach) {
player.cursor.targetTile->direction = player.cursor.direction; player.cursor.targetTile->direction = player.cursor.direction;
} }
break; break;
@@ -220,6 +260,12 @@ int processEvent(SDL_Event e) {
case SDLK_F3: case SDLK_F3:
debugMode = !debugMode; debugMode = !debugMode;
break; break;
case SDLK_F4:
Tile *tile = &tileMap[playerTileY][playerTileX];
break;
case SDLK_LALT:
itemViewing = !itemViewing;
break;
default: default:
break; break;
} }
@@ -274,7 +320,6 @@ void processMousePosition() {
if (player.cursor.targetTile->type < tileTypeIndex) { if (player.cursor.targetTile->type < tileTypeIndex) {
player.inventory.slotCounts[player.cursor.targetTile->type]++; player.inventory.slotCounts[player.cursor.targetTile->type]++;
} }
if (player.cursor.targetTile->type == TYPE_BELT) {
for (int lane = 0; lane < 2; lane++) { for (int lane = 0; lane < 2; lane++) {
if (player.cursor.targetTile->items[lane].type != 0) { if (player.cursor.targetTile->items[lane].type != 0) {
int itemType = player.cursor.targetTile->items[lane].type; int itemType = player.cursor.targetTile->items[lane].type;
@@ -284,7 +329,6 @@ void processMousePosition() {
player.cursor.targetTile->items[lane].type = 0; player.cursor.targetTile->items[lane].type = 0;
} }
} }
}
player.cursor.targetTile->type = TYPE_AIR; player.cursor.targetTile->type = TYPE_AIR;
player.cursor.breakingProgress = 0; player.cursor.breakingProgress = 0;
} else { } else {
@@ -311,6 +355,18 @@ void processMousePosition() {
player.cursor.windowY = (player.cursor.windowY - viewport.y) * DISPLAY_HEIGHT / viewport.h; player.cursor.windowY = (player.cursor.windowY - viewport.y) * DISPLAY_HEIGHT / viewport.h;
player.cursor.tileX = (player.cursor.windowX + player.rect.x) / TILE_SIZE - (DISPLAY_WIDTH / TILE_SIZE / 2); player.cursor.tileX = (player.cursor.windowX + player.rect.x) / TILE_SIZE - (DISPLAY_WIDTH / TILE_SIZE / 2);
player.cursor.tileY = (player.cursor.windowY + player.rect.y) / TILE_SIZE - (DISPLAY_HEIGHT / TILE_SIZE / 2); player.cursor.tileY = (player.cursor.windowY + player.rect.y) / TILE_SIZE - (DISPLAY_HEIGHT / TILE_SIZE / 2);
if (player.cursor.tileX < 0) {
player.cursor.tileX = 0;
}
if (player.cursor.tileY < 0) {
player.cursor.tileY = 0;
}
if (player.cursor.tileX >= MAP_WIDTH) {
player.cursor.tileX = MAP_WIDTH - 1;
}
if (player.cursor.tileY >= MAP_HEIGHT) {
player.cursor.tileY = MAP_HEIGHT - 1;
}
player.cursor.prevTargetTile = player.cursor.targetTile; player.cursor.prevTargetTile = player.cursor.targetTile;
player.cursor.targetTile = &tileMap[player.cursor.tileY][player.cursor.tileX]; player.cursor.targetTile = &tileMap[player.cursor.tileY][player.cursor.tileX];
@@ -329,7 +385,7 @@ void processKeyboardHeld() {
int cameraSpeed = playerSpeed; int cameraSpeed = playerSpeed;
if (keyboardState[SDL_SCANCODE_LSHIFT] || keyboardState[SDL_SCANCODE_RSHIFT]) { if (keyboardState[SDL_SCANCODE_LSHIFT] || keyboardState[SDL_SCANCODE_RSHIFT]) {
cameraSpeed *= 2; cameraSpeed *= 8;
} }
if (keyboardState[SDL_SCANCODE_LCTRL] || keyboardState[SDL_SCANCODE_RCTRL]) { if (keyboardState[SDL_SCANCODE_LCTRL] || keyboardState[SDL_SCANCODE_RCTRL]) {
cameraSpeed /= 2; cameraSpeed /= 2;
@@ -339,39 +395,57 @@ void processKeyboardHeld() {
if (keyboardState[SDL_SCANCODE_W]) { if (keyboardState[SDL_SCANCODE_W]) {
// Example: move up // Example: move up
player.rect.y -= cameraSpeed; player.rect.y -= cameraSpeed;
if (player.rect.y < (DISPLAY_HEIGHT / 2)) { // if (player.rect.y < (DISPLAY_HEIGHT / 2)) {
player.rect.y = (DISPLAY_HEIGHT / 2); // 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; player.rect.y += cameraSpeed;
if (player.rect.y > (MAP_HEIGHT * TILE_SIZE) - (DISPLAY_HEIGHT / 2)) { // if (player.rect.y > (MAP_HEIGHT * TILE_SIZE) - (DISPLAY_HEIGHT / 2)) {
player.rect.y = (MAP_HEIGHT * TILE_SIZE) - (DISPLAY_HEIGHT / 2); // player.rect.y = (MAP_HEIGHT * TILE_SIZE) - (DISPLAY_HEIGHT / 2);
// }
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; player.rect.x -= cameraSpeed;
if (player.rect.x < (DISPLAY_WIDTH / 2)) { // if (player.rect.x < (DISPLAY_WIDTH / 2)) {
player.rect.x = (DISPLAY_WIDTH / 2); // player.rect.x = (DISPLAY_WIDTH / 2);
// }
if (player.rect.x < 0) {
player.rect.x = 0;
} }
} }
if (keyboardState[SDL_SCANCODE_D]) { if (keyboardState[SDL_SCANCODE_D]) {
player.rect.x += cameraSpeed; player.rect.x += cameraSpeed;
if (player.rect.x > (MAP_WIDTH * TILE_SIZE) - (DISPLAY_WIDTH / 2)) { // if (player.rect.x > (MAP_WIDTH * TILE_SIZE) - (DISPLAY_WIDTH / 2)) {
player.rect.x = (MAP_WIDTH * TILE_SIZE) - (DISPLAY_WIDTH / 2); // player.rect.x = (MAP_WIDTH * TILE_SIZE) - (DISPLAY_WIDTH / 2);
// }
if (player.rect.x > (MAP_WIDTH * TILE_SIZE)) {
player.rect.x = (MAP_WIDTH * TILE_SIZE);
} }
} }
} }
if (keyboardState[SDL_SCANCODE_F]) { if (keyboardState[SDL_SCANCODE_F]) {
for (int x = player.rect.x - 1; x < player.rect.x + 1; player.rect.x++) { for (int x = playerTileX - 2; x < playerTileX + 2; x++) {
if (x < 0) { if (x < 0) {
continue; continue;
} }
for (int y = player.rect.y - 1; y < player.rect.y + 1; player.rect.y++) { if (x >= MAP_WIDTH) {
continue;
}
for (int y = playerTileY - 2; y < playerTileY + 2; y++) {
if (y < 0) { if (y < 0) {
continue; continue;
} }
if (y >= MAP_HEIGHT) {
continue;
}
Tile *t = &tileMap[y][x]; Tile *t = &tileMap[y][x];
if (t->type == TYPE_BELT) { if (t->type == TYPE_BELT) {
for (uint8_t lane = 0; lane < 2; lane++) { for (uint8_t lane = 0; lane < 2; lane++) {
@@ -431,8 +505,8 @@ void processKeyboardHeld() {
} else if (keyboardState[SDL_SCANCODE_EQUALS]) { } else if (keyboardState[SDL_SCANCODE_EQUALS]) {
slot = 13; slot = 13;
} }
if (slot > 0 && slot < itemRegistryIndex) { if (slot > 0 && slot < sizeof(player.inventory.hotKeySlots) / sizeof(player.inventory.hotKeySlots[0])) {
setActivePlayerSlot(&player, slot); setActivePlayerSlot(&player, player.inventory.hotKeySlots[slot]);
} }
} }
@@ -441,7 +515,9 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[])
if (status) { if (status) {
return status; return status;
} }
loadGameState(autosaveName, &player); if (loadGameState(autosaveName, &player)) {
genInitMap();
}
//Hack to get window to stay up //Hack to get window to stay up
@@ -460,6 +536,7 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[])
updateItems(); updateItems();
updatePlayer(&player); updatePlayer(&player);
updateTiles();
status = render(); status = render();
if (status) { if (status) {
return status; return status;
@@ -486,3 +563,90 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[])
return 0; return 0;
} }
// Main generator:
void genInitMap() {
const double terrainScale = 2;
const double humidityScale = 1;
const double oreScale = 2;
const int terrainOct = 4;
const int humidityOct = 4;
const int oreOct = 4;
int seedX = rand();
int seedY = rand();
int seedN = rand();
// Min/max trackers
double terrainMin = 1e9, terrainMax = -1e9;
double humidityMin = 1e9, humidityMax = -1e9;
double oreNrmMin = 1e9, oreNrmMax = -1e9;
for (uint16_t y = 0; y < MAP_HEIGHT; y++) {
for (uint16_t x = 0; x < MAP_WIDTH; x++) {
double terrain = pnoise2d(x + 1000 + seedX,
y + 1000 + seedY,
terrainScale, terrainOct, seedN);
double humidity = pnoise2d(x + seedX,
y + seedY,
humidityScale, humidityOct, seedN);
double oreNrm = pnoise2d(x + 9999 + seedX,
y + 1111 + seedY,
oreScale, oreOct, seedN);
// Track min/max
if (terrain < terrainMin) terrainMin = terrain;
if (terrain > terrainMax) terrainMax = terrain;
if (humidity < humidityMin) humidityMin = humidity;
if (humidity > humidityMax) humidityMax = humidity;
if (oreNrm < oreNrmMin) oreNrmMin = oreNrm;
if (oreNrm > oreNrmMax) oreNrmMax = oreNrm;
// [Same as your original terrain generation logic...]
BackgroundType baseType;
if (terrain < 0.30) {
baseType = (humidity < 0.5) ? BGType_WATER0 : BGType_WATER1;
} else if (terrain < 0.35) {
if (humidity < 0.3) baseType = BGType_SAND4;
else if (humidity < 0.6) baseType = BGType_SAND2;
else baseType = BGType_SAND7;
} else if (terrain < 0.7) {
double grassVal = (terrain - 0.35) / (0.70 - 0.35);
int idx = (int) (grassVal * 8.0);
if (idx >= 8) idx = 7;
if (humidity > 0.6 && ((rand() & 0xFF) < 10)) {
int flowerIdx = rand() % 4;
baseType = (BackgroundType) (BGType_GRASS_FLOWER0 + flowerIdx);
} else {
baseType = (BackgroundType) (BGType_GRASS0 + idx);
}
} else if (terrain < 0.85) {
int idx = rand() % 4;
baseType = (BackgroundType) (BGType_COBBLE0 + idx);
} else {
int idx = rand() % 4;
baseType = (BackgroundType) (BGType_TILES0 + idx);
}
BackgroundType finalType = baseType;
if (baseType != BGType_WATER0 && baseType != BGType_WATER1) {
if (oreNrm > 0.86) {
double sub = (oreNrm - 0.86) / (1.0 - 0.86);
if (sub < 0.25) finalType = BGType_PLATINUM_ORE;
else if (sub < 0.50) finalType = BGType_GOLD_ORE;
else if (sub < 0.80) finalType = BGType_SILVER_ORE;
else finalType = BGType_IRON_ORE;
}
}
backgroundMap[y][x].type = finalType;
}
}
// Debug printout
printf("Terrain Noise: min = %f, max = %f\n", terrainMin, terrainMax);
printf("Humidity Noise: min = %f, max = %f\n", humidityMin, humidityMax);
printf("Ore Normalized: min = %f, max = %f\n", oreNrmMin, oreNrmMax);
}

View File

@@ -26,7 +26,7 @@ SDL_Rect targetItemRect;
SDL_Color healthBarColor = {0, 240, 0, 255}; SDL_Color healthBarColor = {0, 240, 0, 255};
SDL_Color breakingBarColor = {128, 128, 0, 255}; SDL_Color breakingBarColor = {128, 128, 0, 255};
void setActivePlayerSlot(Player *plr, uint16_t activeSlotIndex) { void setActivePlayerSlot(Player *plr, ItemType activeSlotIndex) {
activeSlotIndex = activeSlotIndex % itemRegistryIndex; activeSlotIndex = activeSlotIndex % itemRegistryIndex;
if (activeSlotIndex <= 0) { if (activeSlotIndex <= 0) {
activeSlotIndex = 1; activeSlotIndex = 1;
@@ -35,9 +35,8 @@ void setActivePlayerSlot(Player *plr, uint16_t activeSlotIndex) {
} }
void moveActivePlayerSlot(Player *plr, bool up, bool seek) { void moveActivePlayerSlot(Player *plr, bool up, bool seek) {
if (seek) { ItemType prevSlot = plr->inventory.activeSlotIndex;
uint16_t prevSlot = plr->inventory.activeSlotIndex; ItemType newSlot = prevSlot;
uint16_t newSlot = prevSlot;
do { do {
newSlot = (newSlot + (up ? 1 : ITEMREGISTRY_SIZE - 1)) % ITEMREGISTRY_SIZE; newSlot = (newSlot + (up ? 1 : ITEMREGISTRY_SIZE - 1)) % ITEMREGISTRY_SIZE;
@@ -46,24 +45,20 @@ void moveActivePlayerSlot(Player *plr, bool up, bool seek) {
if (newSlot == prevSlot) break; if (newSlot == prevSlot) break;
// Stop if we found a slot with count > 0 // Stop if we found a slot with count > 0
if (plr->inventory.slotCounts[newSlot] > 0 && newSlot != 0) break; if (!strlen(ItemRegistry[newSlot].name)) continue;
if (newSlot == 0) continue;
if (seek) {
if (plr->inventory.slotCounts[newSlot] > 0) break;
} else {
break;
}
} while (true); } while (true);
plr->inventory.activeSlotIndex = newSlot; plr->inventory.activeSlotIndex = newSlot;
} else {
if (plr->inventory.activeSlotIndex == 1 && !up) {
plr->inventory.activeSlotIndex = itemRegistryIndex - 1;
} else if (plr->inventory.activeSlotIndex == itemRegistryIndex - 1 && up) {
plr->inventory.activeSlotIndex = 1;
} else {
plr->inventory.activeSlotIndex =
(plr->inventory.activeSlotIndex + (up ? 1 : ITEMREGISTRY_SIZE - 1)) % ITEMREGISTRY_SIZE;
}
}
} }
void adjustRect(SDL_Rect *rect, SDL_Rect playerRect) { inline void adjustRect(SDL_Rect *rect, SDL_Rect playerRect) {
rect->x -= playerRect.x; rect->x -= playerRect.x;
rect->y -= playerRect.y; rect->y -= playerRect.y;
rect->x += DISPLAY_WIDTH / 2; rect->x += DISPLAY_WIDTH / 2;
@@ -117,7 +112,11 @@ void initPlayer(Player *plr) {
plr->rect.w = TILE_SIZE; plr->rect.w = TILE_SIZE;
plr->rect.h = TILE_SIZE; plr->rect.h = TILE_SIZE;
for (uint16_t ui = 0; ui < itemRegistryIndex; ui++) { for (ItemType ui = 0; ui < tileTypeIndex; ui++) {
plr->inventory.slotCounts[ui] = 64;
}
for (ItemType ui = ITEMREGISTRY_SIZE / 2; ui < itemRegistryIndex; ui++) {
plr->inventory.slotCounts[ui] = 64; plr->inventory.slotCounts[ui] = 64;
} }
@@ -172,7 +171,7 @@ void renderPlayer(Player *plr) {
SDL_SetRenderDrawColor(mainRenderer, plr->cursor.canReach ? 0 : 255, plr->cursor.canReach ? 255 : 0, 0, 128); SDL_SetRenderDrawColor(mainRenderer, plr->cursor.canReach ? 0 : 255, plr->cursor.canReach ? 255 : 0, 0, 128);
DrawThickRect(mainRenderer, plr->cursor.targetTileRect, 4); DrawThickRect(mainRenderer, plr->cursor.targetTileRect, 4);
uint16_t itemIndex = plr->inventory.activeSlotIndex; ItemType itemIndex = plr->inventory.activeSlotIndex;
SDL_Texture *itemTex; SDL_Texture *itemTex;
char itemStringCount[6]; char itemStringCount[6];
if (itemIndex < itemRegistryIndex) { if (itemIndex < itemRegistryIndex) {
@@ -205,8 +204,12 @@ void renderPlayer(Player *plr) {
renderBar(mainRenderer, (DISPLAY_WIDTH / 2) - 128, DISPLAY_HEIGHT - 50, 200, 8, playerMaxHealth, plr->health, renderBar(mainRenderer, (DISPLAY_WIDTH / 2) - 128, DISPLAY_HEIGHT - 50, 200, 8, playerMaxHealth, plr->health,
healthBarColor, 4); healthBarColor, 4);
if (plr->cursor.targetTile) {
uint16_t tempko = getBreakTime(plr->cursor.targetTile->type);
uint16_t tempko2 = plr->cursor.breakingProgress;
renderBar(mainRenderer, (DISPLAY_WIDTH / 2) - 128, DISPLAY_HEIGHT - 70, 200, 8, renderBar(mainRenderer, (DISPLAY_WIDTH / 2) - 128, DISPLAY_HEIGHT - 70, 200, 8,
getBreakTime(plr->cursor.targetTile->type), plr->cursor.breakingProgress, breakingBarColor, 4); tempko, tempko2, breakingBarColor, 4);
}
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255); SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
@@ -215,7 +218,7 @@ void renderPlayer(Player *plr) {
targetItemRect.y = DISPLAY_HEIGHT - 30 + TILE_SIZE / 4 + 3; targetItemRect.y = DISPLAY_HEIGHT - 30 + TILE_SIZE / 4 + 3;
targetItemRect.x = TILE_SIZE / 4; targetItemRect.x = TILE_SIZE / 4;
for (uint16_t i = 1; i < itemRegistryIndex; i++) { for (ItemType i = 1; i < ITEMREGISTRY_SIZE; i++) {
itemTex = ItemRegistry[i].textureOnBelt[plr->cursor.direction]; itemTex = ItemRegistry[i].textureOnBelt[plr->cursor.direction];
if (itemTex == NULL) { if (itemTex == NULL) {
itemTex = ItemRegistry[i].textureOnBelt[ORIENT_LEFT]; itemTex = ItemRegistry[i].textureOnBelt[ORIENT_LEFT];
@@ -228,7 +231,6 @@ void renderPlayer(Player *plr) {
SDL_RenderCopy(mainRenderer, itemTex, NULL, &targetItemRect); SDL_RenderCopy(mainRenderer, itemTex, NULL, &targetItemRect);
SDL_SetTextureColorMod(itemTex, 255, 255, 255); SDL_SetTextureColorMod(itemTex, 255, 255, 255);
}
if (plr->inventory.activeSlotIndex == i) { if (plr->inventory.activeSlotIndex == i) {
SDL_SetRenderDrawColor(mainRenderer, 16, plr->inventory.slotCounts[i] > 0 ? 128 : 16, SDL_SetRenderDrawColor(mainRenderer, 16, plr->inventory.slotCounts[i] > 0 ? 128 : 16,
plr->inventory.slotCounts[i] > 0 ? 32 : 128, 255); plr->inventory.slotCounts[i] > 0 ? 32 : 128, 255);
@@ -240,6 +242,7 @@ void renderPlayer(Player *plr) {
//targetItemRect.x += (TILE_SIZE / 2) + (TILE_SIZE / 4); //targetItemRect.x += (TILE_SIZE / 2) + (TILE_SIZE / 4);
targetItemRect.x += TILE_SIZE; targetItemRect.x += TILE_SIZE;
} }
}
SDL_SetRenderTarget(mainRenderer, originalTarget); SDL_SetRenderTarget(mainRenderer, originalTarget);
} }

View File

@@ -28,12 +28,13 @@ void adjustRect(SDL_Rect *rect, SDL_Rect playerRect);
#define playerMaxHealth 255 #define playerMaxHealth 255
typedef struct { typedef struct PlayerInventory {
uint16_t slotCounts[ITEMREGISTRY_SIZE]; uint16_t slotCounts[ITEMREGISTRY_SIZE];
uint16_t activeSlotIndex; ItemType activeSlotIndex;
ItemType hotKeySlots[13];
} PlayerInventory; } PlayerInventory;
typedef struct { typedef struct PlayerCursor {
int windowX; int windowX;
int windowY; int windowY;
int tileX; int tileX;
@@ -50,7 +51,7 @@ typedef struct {
int breakingProgress; int breakingProgress;
} PlayerCursor; } PlayerCursor;
typedef struct { typedef struct Player{
PlayerCursor cursor; PlayerCursor cursor;
PlayerInventory inventory; PlayerInventory inventory;
uint8_t health; uint8_t health;
@@ -59,7 +60,7 @@ typedef struct {
SDL_Rect rect; SDL_Rect rect;
} Player; } Player;
void setActivePlayerSlot(Player *plr, uint16_t activeSlotIndex); void setActivePlayerSlot(Player *plr, ItemType activeSlotIndex);
void moveActivePlayerSlot(Player *plr, bool up, bool seek); void moveActivePlayerSlot(Player *plr, bool up, bool seek);

View File

@@ -16,7 +16,7 @@ void renderBelt(int x, int y, int w, int h, OrientDirection dir, SDL_Rect player
Tile *t = &tileMap[y][x]; Tile *t = &tileMap[y][x];
uint16_t tileType = t->type; ItemType tileType = t->type;
SDL_Rect src1, src2, dst1, dst2; SDL_Rect src1, src2, dst1, dst2;
@@ -59,11 +59,8 @@ void renderBelt(int x, int y, int w, int h, OrientDirection dir, SDL_Rect player
SDL_RenderCopy(renderer, TileRegistry[tileType].textures[dir], &src1, &dst1); SDL_RenderCopy(renderer, TileRegistry[tileType].textures[dir], &src1, &dst1);
SDL_RenderCopy(renderer, TileRegistry[tileType].textures[dir], &src2, &dst2); SDL_RenderCopy(renderer, TileRegistry[tileType].textures[dir], &src2, &dst2);
} }
}
void updateBelt(Tile * tile) {
SDL_SetRenderTarget(renderer, itemsTexture);
for (uint8_t lane = 0; lane < 2; lane++) {
if (t->items[lane].type != 0) {
renderItem(t->items[lane], renderer, lane, playerRect);
}
}
} }

View File

@@ -7,9 +7,12 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <SDL2/SDL_image.h> #include <SDL2/SDL_image.h>
#include "tile.h"
#include "../util/util.h" #include "../util/util.h"
struct Tile;
void renderBelt(int x, int y, int w, int h, OrientDirection dir, SDL_Rect playerRect, SDL_Renderer *renderer); void renderBelt(int x, int y, int w, int h, OrientDirection dir, SDL_Rect playerRect, SDL_Renderer *renderer);
void updateBelt(struct Tile * tile);
#endif //FACTORYGAME_BELT_H #endif //FACTORYGAME_BELT_H

View File

@@ -4,11 +4,48 @@
#include "furnace.h" #include "furnace.h"
#include "tile.h" #include "tile.h"
#include "../util/audio.h"
uint16_t getFurnaceNewItem(uint16_t sourceItem) { const ItemType FurnaceRecipes[ITEMREGISTRY_SIZE] = {
uint16_t realItemIndex = sourceItem - tileTypeIndex - 1; [IRON_ORE] = IRON_INGOT,
if (realItemIndex < 8) { [SILVER_ORE] = SILVER_INGOT,
return sourceItem + 1; [GOLD_ORE] = GOLD_INGOT,
[PLATINUM_ORE] = PLATINUM_INGOT
};
void updateFurnace(Tile *tile) {
ItemOnBelt *inItem = &tile->items[FURNACE_INPUT_SLOT];
ItemOnBelt *outItem = &tile->items[FURNACE_OUTPUT_SLOT];
Item inItemType = ItemRegistry[inItem->type];
ItemType targetOutItemType = FurnaceRecipes[inItem->type];
Item targetOutItem = ItemRegistry[targetOutItemType];
if (targetOutItemType != TYPE_AIR) {
if (tile->miscVal == 0) {
tile->audioCh = getAvailableChannel();
if (tile->audioCh < NUM_SYNTH_VOICES) {
audioData.synthVoices[tile->audioCh].volume = 1;
audioData.synthVoices[tile->audioCh].phase = 0;
audioData.synthVoices[tile->audioCh].sourceRect.x = TILE_SIZE * tile->x;
audioData.synthVoices[tile->audioCh].sourceRect.y = TILE_SIZE * tile->y;
audioData.synthVoices[tile->audioCh].waveform = WAVE_TRIANGLE;
audioData.synthVoices[tile->audioCh].frequency = 99;
}
}
if (tile->audioCh < NUM_SYNTH_VOICES) {
audioData.synthVoices[tile->audioCh].frequency++;
}
if (outItem->type == 0 && ++tile->miscVal >= targetOutItem.miscVal) {
if (tile->audioCh < NUM_SYNTH_VOICES) {
audioData.synthVoices[tile->audioCh].volume = 0;
}
tile->miscVal = 0;
inItem->type = 0;
outItem->type = targetOutItemType;
outItem->offset = -0.5f;
}
} }
return 0;
} }

View File

@@ -5,6 +5,14 @@
#ifndef FACTORYGAME_FURNACE_H #ifndef FACTORYGAME_FURNACE_H
#define FACTORYGAME_FURNACE_H #define FACTORYGAME_FURNACE_H
#include "../items/item.h"
#include "stdint.h" #include "stdint.h"
extern const ItemType FurnaceRecipes[];
#define FURNACE_INPUT_SLOT 0
#define FURNACE_OUTPUT_SLOT 1
void updateFurnace(Tile * tile);
#endif //FACTORYGAME_FURNACE_H #endif //FACTORYGAME_FURNACE_H

View File

@@ -5,18 +5,26 @@
#include <dirent.h> #include <dirent.h>
#include "tile.h" #include "tile.h"
#include "../player/player.h" #include "../player/player.h"
#include "furnace.h"
#include "../util/atlas.h"
int scrollFrame = 0; int scrollFrame = 0;
unsigned long beltFrames = 0; unsigned long beltFrames = 0;
SDL_Texture *backgroundTexture;
SDL_Texture *tilesTexture; SDL_Texture *tilesTexture;
SDL_Texture *itemsTexture; SDL_Texture *itemsTexture;
Tile tileMap[MAP_HEIGHT][MAP_WIDTH]; Tile tileMap[MAP_HEIGHT][MAP_WIDTH];
uint16_t tileTypeIndex = 0; BackgroundTile backgroundMap[MAP_HEIGHT][MAP_WIDTH];
uint16_t tileTypeIndex = 0;
uint16_t backgroundTileTypeIndex = 0;
TileTypeReg TileRegistry[TILEREGISTRY_SIZE];
BackgroundTileType BackgroundTileRegistry[TILEREGISTRY_SIZE];
TileType TileRegistry[TILEREGISTRY_SIZE];
void generateTestMap() { void generateTestMap() {
for (int y = 0; y < DISPLAY_MAP_HEIGHT; y++) { for (int y = 0; y < DISPLAY_MAP_HEIGHT; y++) {
@@ -32,7 +40,7 @@ void generateTestMap() {
for (int y = 0; y < MAP_HEIGHT; y += 1) { for (int y = 0; y < MAP_HEIGHT; y += 1) {
tileMap[y][x].type = TYPE_BELT; tileMap[y][x].type = TYPE_BELT;
tileMap[y][x].frameOffset = 0; tileMap[y][x].miscVal = 0;
//tileMap[y][x].direction = ((x + y) % 4 * 2) + 1; //tileMap[y][x].direction = ((x + y) % 4 * 2) + 1;
//tileMap[y][x].direction = 5; //tileMap[y][x].direction = 5;
tileMap[y][x].direction = (rand() % 4 * 2) + 1; tileMap[y][x].direction = (rand() % 4 * 2) + 1;
@@ -58,26 +66,68 @@ void registerTile(char name[20], SDL_Renderer *renderer) {
TileRegistry[tileTypeIndex].textures[ORIENT_RIGHT] = createFlippedTexture(renderer, texture, SDL_FLIP_HORIZONTAL); TileRegistry[tileTypeIndex].textures[ORIENT_RIGHT] = createFlippedTexture(renderer, texture, SDL_FLIP_HORIZONTAL);
TileRegistry[tileTypeIndex].textures[ORIENT_UP] = createRotatedTexture(renderer, texture, 90); TileRegistry[tileTypeIndex].textures[ORIENT_UP] = createRotatedTexture(renderer, texture, 90);
TileRegistry[tileTypeIndex].textures[ORIENT_DOWN] = createRotatedTexture(renderer, texture, 270); TileRegistry[tileTypeIndex].textures[ORIENT_DOWN] = createRotatedTexture(renderer, texture, 270);
if (tileTypeIndex == 0) {
SDL_SetTextureAlphaMod(TileRegistry[0].textures[ORIENT_LEFT], 64);
SDL_SetTextureAlphaMod(TileRegistry[0].textures[ORIENT_RIGHT], 64);
SDL_SetTextureAlphaMod(TileRegistry[0].textures[ORIENT_UP], 64);
SDL_SetTextureAlphaMod(TileRegistry[0].textures[ORIENT_DOWN], 64);
}
SDL_SetTextureBlendMode(TileRegistry[tileTypeIndex].textures[ORIENT_LEFT], SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(TileRegistry[tileTypeIndex].textures[ORIENT_RIGHT], SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(TileRegistry[tileTypeIndex].textures[ORIENT_UP], SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(TileRegistry[tileTypeIndex].textures[ORIENT_DOWN], SDL_BLENDMODE_BLEND);
TileRegistry[tileTypeIndex].atlasRects[ORIENT_LEFT] = allocate_32x32(TileRegistry[tileTypeIndex].textures[ORIENT_LEFT], renderer);
TileRegistry[tileTypeIndex].atlasRects[ORIENT_RIGHT] = allocate_32x32(TileRegistry[tileTypeIndex].textures[ORIENT_RIGHT], renderer);
TileRegistry[tileTypeIndex].atlasRects[ORIENT_UP] = allocate_32x32(TileRegistry[tileTypeIndex].textures[ORIENT_UP], renderer);
TileRegistry[tileTypeIndex].atlasRects[ORIENT_DOWN] = allocate_32x32(TileRegistry[tileTypeIndex].textures[ORIENT_DOWN], renderer);
TileRegistry[tileTypeIndex].type = tileTypeIndex; TileRegistry[tileTypeIndex].type = tileTypeIndex;
TileRegistry[tileTypeIndex].breakTime = 15; TileRegistry[tileTypeIndex].breakTime = 15;
tileTypeIndex++; tileTypeIndex++;
} }
void registerBackgroundTile(char name[20], SDL_Renderer *renderer) {
const char *dot = strchr(name, '.');
memcpy(BackgroundTileRegistry[backgroundTileTypeIndex].name, name, dot - name);
char texturePath[80];
snprintf(texturePath, 80, "./assets/backgrounds/%s", name);
SDL_Texture *texture = IMG_LoadTexture(renderer, texturePath);
BackgroundTileRegistry[backgroundTileTypeIndex].texture = texture;
SDL_SetTextureBlendMode(BackgroundTileRegistry[backgroundTileTypeIndex].texture, SDL_BLENDMODE_NONE);
BackgroundTileRegistry[backgroundTileTypeIndex].atlasRect = allocate_32x32(BackgroundTileRegistry[backgroundTileTypeIndex].texture, renderer);
BackgroundTileRegistry[backgroundTileTypeIndex].type = backgroundTileTypeIndex;
backgroundTileTypeIndex++;
}
void loadTiles(SDL_Renderer *renderer) { void loadTiles(SDL_Renderer *renderer) {
iterateSortedDir("./assets/tiles", (DirEntryCallback) registerTile, renderer); iterateSortedDir("./assets/tiles", (DirEntryCallback) registerTile, renderer);
// DIR *dir = opendir("./assets/tiles"); iterateSortedDir("./assets/backgrounds", (DirEntryCallback) registerBackgroundTile, renderer);
// if (dir) {
// struct dirent *entry; }
// while ((entry = readdir(dir)) != NULL) {
// if (entry->d_name[0] == '.') { void setupTiles() {
// continue; TileRegistry[TYPE_AIR].breakTime = 0;
// } TileRegistry[TYPE_BELT].itemMoves = true;
// registerTile(entry->d_name, mainRenderer); for (uint16_t i = 0; i < ItemSlotCount; i++) {
// } TileRegistry[TYPE_BELT].outputLane[i] = true;
// } }
TileRegistry[0].breakTime = 0; 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;
} }
uint16_t getBreakTime(int type) { uint16_t getBreakTime(int type) {
@@ -92,6 +142,9 @@ void initTiles() {
screenRect.h); screenRect.h);
tilesTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, screenRect.w, tilesTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, screenRect.w,
screenRect.h); screenRect.h);
backgroundTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
screenRect.w,
screenRect.h);
SDL_SetTextureBlendMode(itemsTexture, SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(itemsTexture, SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(tilesTexture, SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(tilesTexture, SDL_BLENDMODE_BLEND);
} }
@@ -99,10 +152,14 @@ void initTiles() {
void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect) { void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect) {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_Texture *oldTarget = SDL_GetRenderTarget(renderer); SDL_Texture *oldTarget = SDL_GetRenderTarget(renderer);
SDL_SetRenderTarget(renderer, itemsTexture);
SDL_RenderClear(renderer); int tileSize = TILE_SIZE;
SDL_SetRenderTarget(renderer, tilesTexture);
SDL_RenderClear(renderer); 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 scrollSpeed = 1; // pixels per step
int scrollDelay = 1; // frames between steps int scrollDelay = 1; // frames between steps
@@ -110,42 +167,83 @@ void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect) {
scrollFrame += scrollSpeed; scrollFrame += scrollSpeed;
} }
int tileSize = TILE_SIZE; // --- Render background tiles ---
for (int y = (playerRect.y / TILE_SIZE) - (DISPLAY_MAP_HEIGHT / 2) - 1; SDL_SetRenderTarget(renderer, backgroundTexture);
y < (playerRect.y / TILE_SIZE) + (DISPLAY_MAP_HEIGHT / 2) + 1; y++) { SDL_RenderClear(renderer);
if (y < 0 || y >= MAP_HEIGHT) {
continue; 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];
SDL_Texture *tex = BackgroundTileRegistry[bt.type].texture;
if (tex != NULL) {
SDL_RenderCopy(renderer, tex, NULL, &dstRect);
} }
for (int x = (playerRect.x / TILE_SIZE) - (DISPLAY_MAP_WIDTH / 2) - 1;
x < (playerRect.x / TILE_SIZE) + (DISPLAY_MAP_WIDTH / 2) + 1; x++) {
if (x < 0 || x >= MAP_WIDTH) {
continue;
} }
Tile t = tileMap[y][x]; }
SDL_SetRenderTarget(renderer, tilesTexture); 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) { switch (t.type) {
case TYPE_BELT: case TYPE_BELT:
renderBelt(x, y, tileSize, tileSize, t.direction, playerRect, renderer); renderBelt(x, y, tileSize, tileSize, t.direction, playerRect, renderer);
break; break;
default: default: {
SDL_Rect dstRect;
dstRect.x = x * TILE_SIZE;
dstRect.y = y * TILE_SIZE;
dstRect.w = TILE_SIZE;
dstRect.h = TILE_SIZE;
adjustRect(&dstRect, playerRect);
SDL_Texture *tex = TileRegistry[t.type].textures[t.direction]; SDL_Texture *tex = TileRegistry[t.type].textures[t.direction];
if (tex == NULL) { if (tex == NULL) tex = TileRegistry[t.type].textures[ORIENT_LEFT];
tex = TileRegistry[t.type].textures[ORIENT_LEFT];
}
if (tex != NULL) { if (tex != NULL) {
SDL_RenderCopy(renderer, tex, NULL, &dstRect); SDL_RenderCopy(renderer, tex, NULL, &dstRect);
} }
}
}
}
}
} SDL_SetRenderTarget(renderer, itemsTexture);
if (t.type == TYPE_BELT) { 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 (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); SDL_SetRenderTarget(renderer, oldTarget);
} }
void updateTiles() {
}

View File

@@ -5,15 +5,15 @@
#ifndef FACTORYGAME_TILE_H #ifndef FACTORYGAME_TILE_H
#define FACTORYGAME_TILE_H #define FACTORYGAME_TILE_H
#include "belt.h"
#include "../util/util.h" #include "../util/util.h"
#include "../items/item.h" #include "../items/item.h"
//#include "../items/item.h"
#define MAP_WIDTH 600 #define MAP_WIDTH 100
#define MAP_HEIGHT 340 #define MAP_HEIGHT 100
#define DISPLAY_MAP_WIDTH 44 #define DISPLAY_MAP_WIDTH 60
#define DISPLAY_MAP_HEIGHT 22 #define DISPLAY_MAP_HEIGHT 31
#define TILE_SIZE 32 #define TILE_SIZE 32
@@ -22,42 +22,108 @@
extern SDL_Texture *tilesTexture; extern SDL_Texture *tilesTexture;
extern SDL_Texture *itemsTexture; extern SDL_Texture *itemsTexture;
extern SDL_Texture *backgroundTexture;
extern int scrollFrame; extern int scrollFrame;
extern unsigned long beltFrames; extern unsigned long beltFrames;
typedef struct {
uint16_t type; #define ItemSlotCount 4
typedef enum BackgroundType {
BGType_WATER0,
BGType_WATER1,
BGType_GRASS0,
BGType_GRASS1,
BGType_GRASS2,
BGType_GRASS3,
BGType_GRASS4,
BGType_GRASS5,
BGType_GRASS6,
BGType_GRASS7,
BGType_GRASS_FLOWER0,
BGType_GRASS_FLOWER1,
BGType_GRASS_FLOWER2,
BGType_GRASS_FLOWER3,
BGType_SAND0,
BGType_SAND1,
BGType_SAND2,
BGType_SAND3,
BGType_SAND4,
BGType_SAND5,
BGType_SAND6,
BGType_SAND7,
BGType_TILES0,
BGType_TILES1,
BGType_TILES2,
BGType_TILES3,
BGType_COBBLE0,
BGType_COBBLE1,
BGType_COBBLE2,
BGType_COBBLE3,
BGType_PLATINUM_ORE,
BGType_GOLD_ORE,
BGType_SILVER_ORE,
BGType_IRON_ORE,
BGType_END
} BackgroundType;
typedef struct TileTypeReg {
ItemType type;
char name[20]; char name[20];
SDL_Texture *textures[ORIENT_DIRECTION_COUNT]; SDL_Texture *textures[ORIENT_DIRECTION_COUNT];
SDL_Rect atlasRects[ORIENT_DIRECTION_COUNT];
uint16_t breakTime; uint16_t breakTime;
} TileType; bool itemMoves;
bool allowedInItems[ItemSlotCount][ITEMREGISTRY_SIZE];
bool outputLane[ItemSlotCount];
} TileTypeReg;
#define TILEREGISTRY_SIZE 512 typedef struct BackgroundTileType {
ItemType type;
char name[20];
SDL_Texture *texture;
SDL_Rect atlasRect;
} BackgroundTileType;
extern TileType TileRegistry[TILEREGISTRY_SIZE]; typedef struct BackgroundTile {
BackgroundType type;
} BackgroundTile;
#define TYPE_AIR 0 #define TILEREGISTRY_SIZE 64
#define TYPE_BELT 2
extern BackgroundTileType BackgroundTileRegistry[TILEREGISTRY_SIZE];
extern TileTypeReg TileRegistry[TILEREGISTRY_SIZE];
void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect); void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect);
typedef struct { void updateTiles();
typedef struct Tile {
OrientDirection direction; OrientDirection direction;
uint16_t type; ItemType type;
int frameOffset; int miscVal;
ItemOnBelt items[2]; ItemOnBelt items[ItemSlotCount];
uint16_t audioCh;
int x; int x;
int y; int y;
} Tile; } Tile;
extern Tile tileMap[MAP_HEIGHT][MAP_WIDTH]; extern Tile tileMap[MAP_HEIGHT][MAP_WIDTH];
extern BackgroundTile backgroundMap[MAP_HEIGHT][MAP_WIDTH];
void setupTiles();
void generateTestMap(); void generateTestMap();
void loadTiles(SDL_Renderer *renderer); void loadTiles(SDL_Renderer *renderer);
extern uint16_t tileTypeIndex; extern uint16_t tileTypeIndex;
extern uint16_t backgroundTileTypeIndex;
uint16_t getBreakTime(int type); uint16_t getBreakTime(int type);

13
tiles/tilecallbacks.c Normal file
View File

@@ -0,0 +1,13 @@
//
// Created by bruno on 1.6.2025.
//
#include "tilecallbacks.h"
#include "furnace.h"
const UpdateTileCallback ItemTileCallbacks[TILEREGISTRY_SIZE] = {
[TYPE_AIR] = NULL,
[TYPE_BLOCK] = NULL,
[TYPE_BELT] = updateBelt,
[TYPE_FURNACE] = updateFurnace
};

14
tiles/tilecallbacks.h Normal file
View File

@@ -0,0 +1,14 @@
//
// Created by bruno on 1.6.2025.
//
#ifndef FACTORYGAME_TILECALLBACKS_H
#define FACTORYGAME_TILECALLBACKS_H
#include "tile.h"
typedef void (*UpdateTileCallback)(struct Tile *tile);
extern const UpdateTileCallback ItemTileCallbacks[];
#endif //FACTORYGAME_TILECALLBACKS_H

80
util/atlas.c Normal file
View File

@@ -0,0 +1,80 @@
//
// Created by bruno on 1.6.2025.
//
#include "atlas.h"
#include "util.h"
SDL_Texture *atlasTexture;
int atlasX = 0, atlasY = 0;
int tileIndex = 0; // Which 32x32 tile we're on
int quadrantIndex = 0; // Which 16x16 slot inside that tile
SDL_Rect allocate_16x16(SDL_Texture *srcTexture, SDL_Renderer *renderer) {
SDL_Texture * oldTarget = SDL_GetRenderTarget(renderer);
SDL_SetRenderTarget(renderer, atlasTexture);
int tileX = tileIndex % ATLAS_TILES_PER_ROW;
int tileY = tileIndex / ATLAS_TILES_PER_ROW;
int dx = (quadrantIndex % 2) * QUADRANT_SIZE;
int dy = (quadrantIndex / 2) * QUADRANT_SIZE;
SDL_Rect destRect = {
tileX * TILE_SIZE + dx,
tileY * TILE_SIZE + dy,
QUADRANT_SIZE,
QUADRANT_SIZE
};
SDL_RenderCopy(renderer, srcTexture, NULL, &destRect);
quadrantIndex++;
if (quadrantIndex >= 4) {
tileIndex++;
quadrantIndex = 0;
}
SDL_SetRenderTarget(renderer, oldTarget);
return destRect;
}
SDL_Rect allocate_32x32(SDL_Texture *srcTexture, SDL_Renderer *renderer) {
SDL_Texture * oldTarget = SDL_GetRenderTarget(renderer);
SDL_SetRenderTarget(renderer, atlasTexture);
// If were not at the start of a tile, skip to the next clean one
if (quadrantIndex != 0) {
tileIndex++;
quadrantIndex = 0;
}
int tileX = tileIndex % ATLAS_TILES_PER_ROW;
int tileY = tileIndex / ATLAS_TILES_PER_ROW;
SDL_Rect destRect = {
tileX * TILE_SIZE,
tileY * TILE_SIZE,
TILE_SIZE,
TILE_SIZE
};
SDL_RenderCopy(renderer, srcTexture, NULL, &destRect);
tileIndex++; // Move to next tile
// quadrantIndex stays 0 — new tile is fresh
SDL_SetRenderTarget(renderer, oldTarget);
return destRect;
}
void initAtlas(SDL_Renderer *renderer) {
// Clear atlas with transparent
SDL_SetRenderTarget(renderer, atlasTexture);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
atlasTexture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
ATLAS_SIZE, ATLAS_SIZE);
}

23
util/atlas.h Normal file
View File

@@ -0,0 +1,23 @@
//
// Created by bruno on 1.6.2025.
//
#ifndef FACTORYGAME_ATLAS_H
#define FACTORYGAME_ATLAS_H
#define ATLAS_SIZE 512
#define TILE_SIZE 32
#define QUADRANT_SIZE 16
#define ATLAS_TILES_PER_ROW (ATLAS_SIZE / TILE_SIZE)
#include "SDL2/SDL.h"
extern SDL_Texture *atlasTexture;
SDL_Rect allocate_32x32(SDL_Texture *srcTexture, SDL_Renderer *renderer);
SDL_Rect allocate_16x16(SDL_Texture *srcTexture, SDL_Renderer *renderer);
void initAtlas(SDL_Renderer *renderer);
#endif //FACTORYGAME_ATLAS_H

View File

@@ -6,21 +6,93 @@
AudioData audioData; AudioData audioData;
uint16_t getAvailableChannel() {
for (uint16_t i = 0; i < NUM_SYNTH_VOICES; i++) {
if (audioData.synthVoices[i].volume == 0) {
return i;
}
}
return -1;
}
// Helper: compute left/right gains from a pan value in [1..+1]
// pan = 1.0 → full left (L=1, R=0)
// pan = +1.0 → full right (L=0, R=1)
// pan = 0.0 → center (L=R=1/sqrt(2) or just 0.707 to avoid clipping)
static void compute_stereo_gains(float pan, float *outL, float *outR) {
// Simple linear panning (no constantpower law).
// If you prefer constantpower, you could do:
// float angle = (pan + 1.0f) * (M_PI / 4.0f);
// *outL = cosf(angle);
// *outR = sinf(angle);
//
// Here well just do linear:
pan = fmaxf(-1.0f, fminf(+1.0f, pan));
if (pan <= 0.0f) {
*outL = 1.0f;
*outR = 1.0f + pan; // pan is negative, so R < 1
} else {
*outL = 1.0f - pan; // pan is positive, so L < 1
*outR = 1.0f;
}
// Optionally, scale down both so we never exceed 1.0f / sqrt(2)
// e.g. *outL *= 0.7071f; *outR *= 0.7071f;
}
// This callback now writes stereo frames: interleaved L/R floats.
void audio_callback(void *userdata, Uint8 *stream, int len) { void audio_callback(void *userdata, Uint8 *stream, int len) {
AudioData *audio = (AudioData *) userdata; AudioData *audio = (AudioData *) userdata;
int samples = len / sizeof(float);
for (int i = 0; i < samples; i++) { // 'len' is total bytes; each sampleframe is 2 floats (L+R), i.e. 2 * sizeof(float).
float mix = 0.0f; int frames = len / (2 * sizeof(float));
int activeVoices = 0;
// Zero out the entire output buffer (silence)
// Well accumulate into it.
// Each float is 4 bytes, so total floats = 2 * frames.
float *outBuf = (float *) stream;
for (int i = 0; i < 2 * frames; ++i) {
outBuf[i] = 0.0f;
}
// Precompute the listener center
float listenerCx = audio->playerRect->x + audio->playerRect->w * 0.5f;
// For each synth voice, mix into the stereo buffer
for (int v = 0; v < NUM_SYNTH_VOICES; v++) { for (int v = 0; v < NUM_SYNTH_VOICES; v++) {
SynthVoice *voice = &audio->synthVoices[v]; SynthVoice *voice = &audio->synthVoices[v];
if (voice->volume == 0 || voice->frequency == 0) continue; if (voice->volume == 0 || voice->frequency == 0) {
continue; // skip silent or inactive voices
}
float sample; // Compute source center X
float sourceCx = voice->sourceRect.x + voice->sourceRect.w * 0.5f;
float dx = sourceCx - listenerCx;
// Normalize for pan. If |dx| >= maxPanDistance → full left or full right.
float pan = dx / audio->maxPanDistance;
if (pan < -1.0f) pan = -1.0f;
if (pan > +1.0f) pan = +1.0f;
float gainL, gainR;
compute_stereo_gains(pan, &gainL, &gainR);
// Optional: You could also attenuate overall volume with distance
// float dist = fabsf(dx);
// float distanceAtten = 1.0f - fminf(dist / audio->maxPanDistance, 1.0f);
// float finalVolume = (voice->volume / 255.0f) * distanceAtten;
// But for now, well just use voice->volume for amplitude.
float amp = (voice->volume / 255.0f);
// Phase increment per sampleframe:
// (freq * 256) / SAMPLE_RATE tells how many phase steps per mono-sample.
// Because were writing stereo, we still advance phase once per frame.
uint8_t phaseInc = (uint8_t)((voice->frequency * 256) / SAMPLE_RATE);
// Mix into each frame
for (int i = 0; i < frames; i++) {
float t = (float) voice->phase / 255.0f * 2.0f - 1.0f; float t = (float) voice->phase / 255.0f * 2.0f - 1.0f;
float sample;
switch (voice->waveform) { switch (voice->waveform) {
default: default:
case WAVE_SINE: case WAVE_SINE:
@@ -33,18 +105,27 @@ void audio_callback(void *userdata, Uint8 *stream, int len) {
sample = t; sample = t;
break; break;
case WAVE_TRIANGLE: case WAVE_TRIANGLE:
sample = (t < 0) ? -t : t; sample = (t < 0.0f) ? -t : t;
break; break;
case WAVE_NOISE: case WAVE_NOISE:
sample = ((float) rand() / RAND_MAX) * 2.0f - 1.0f; sample = ((float) rand() / RAND_MAX) * 2.0f - 1.0f;
break; break;
} }
voice->phase += (uint8_t) ((voice->frequency * 256) / SAMPLE_RATE); voice->phase += phaseInc;
mix += sample * (voice->volume / 255.0f);
activeVoices++; // Interleaved index: left = 2*i, right = 2*i + 1
int idxL = 2 * i;
int idxR = 2 * i + 1;
// Accumulate into buffer
outBuf[idxL] += sample * amp * gainL;
outBuf[idxR] += sample * amp * gainR;
}
} }
((float *) stream)[i] = (activeVoices > 0) ? mix / activeVoices : 0.0f; // Note: We did not normalize by active voices here, because each voice already
} // uses its own volume. If you still want an automatic “divide by N active voices”,
// you would need to track active voices perframe, which is relatively expensive.
// In practice, you manage the volume per voice so clipping doesnt occur.
} }

View File

@@ -11,9 +11,9 @@
#include <stdlib.h> #include <stdlib.h>
#define SAMPLE_RATE 44100 #define SAMPLE_RATE 44100
#define NUM_SYNTH_VOICES 3 #define NUM_SYNTH_VOICES 256
typedef enum { typedef enum Waveform {
WAVE_SINE, WAVE_SINE,
WAVE_SQUARE, WAVE_SQUARE,
WAVE_SAWTOOTH, WAVE_SAWTOOTH,
@@ -21,19 +21,24 @@ typedef enum {
WAVE_NOISE WAVE_NOISE
} Waveform; } Waveform;
typedef struct { typedef struct SynthVoice {
uint8_t volume;
uint16_t frequency;
uint8_t phase;
Waveform waveform; Waveform waveform;
uint8_t phase;
uint16_t frequency;
uint8_t volume;
SDL_Rect sourceRect;
} SynthVoice; } SynthVoice;
typedef struct { typedef struct AudioData {
SynthVoice synthVoices[NUM_SYNTH_VOICES]; SynthVoice synthVoices[NUM_SYNTH_VOICES];
SDL_Rect *playerRect;
float maxPanDistance;
} AudioData; } AudioData;
extern AudioData audioData; extern AudioData audioData;
void audio_callback(void *userdata, Uint8 *stream, int len); void audio_callback(void *userdata, Uint8 *stream, int len);
uint16_t getAvailableChannel();
#endif //RISCB_AUDIO_H #endif //RISCB_AUDIO_H

View File

@@ -11,7 +11,7 @@
#define fontCount 4 #define fontCount 4
typedef struct { typedef struct BitmapFont {
SDL_Texture *texture[256]; SDL_Texture *texture[256];
SDL_Surface *surface[256]; SDL_Surface *surface[256];
uint8_t size; uint8_t size;

52
util/perlin.c Normal file
View File

@@ -0,0 +1,52 @@
#include <math.h>
double rawnoise(int n) {
n = (n << 13) ^ n;
return (1.0 - ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);
}
double noise2d(int x, int y, int octave, int seed) {
return rawnoise(x * 1619 + y * 31337 + octave * 3463 + seed * 13397);
}
double interpolate(double a, double b, double x) {
double f = (1 - cos(x * 3.141593)) * 0.5;
return a * (1 - f) + b * f;
}
double smooth2d(double x, double y, int octave, int seed) {
int intx = (int) x;
double fracx = x - intx;
int inty = (int) y;
double fracy = y - inty;
double v1 = noise2d(intx, inty, octave, seed);
double v2 = noise2d(intx + 1, inty, octave, seed);
double v3 = noise2d(intx, inty + 1, octave, seed);
double v4 = noise2d(intx + 1, inty + 1, octave, seed);
double i1 = interpolate(v1, v2, fracx);
double i2 = interpolate(v3, v4, fracx);
return interpolate(i1, i2, fracy);
}
double pnoise2d(double x, double y, double persistence, int octaves, int seed) {
double total = 0.0;
double frequency = 1.0;
double amplitude = 1.0;
double max = 0.0;
for (int i = 0; i < octaves; i++) {
total += smooth2d(x * frequency, y * frequency, i, seed) * amplitude;
max += amplitude;
frequency /= 2.0;
amplitude *= persistence;
}
// Normalize to [0, 1]
return (total + max) / (2.0 * max);
}

10
util/perlin.h Normal file
View File

@@ -0,0 +1,10 @@
//
// Created by bruno on 1.6.2025.
//
#ifndef FACTORYGAME_PERLIN_H
#define FACTORYGAME_PERLIN_H
double pnoise2d(double x, double y, double persistence, int octaves, int seed);
#endif //FACTORYGAME_PERLIN_H

View File

@@ -6,11 +6,13 @@
#include "util.h" #include "util.h"
//#include "font.h" //#include "font.h"
//The window we'll be rendering to //The window we'll be rendering to
SDL_Window *window = NULL; SDL_Window *window = NULL;
volatile bool running = true; volatile bool running = true;
bool debugMode = false; bool debugMode = false;
bool itemViewing = false;
//The surface contained by the window //The surface contained by the window
SDL_Renderer *mainRenderer = NULL; SDL_Renderer *mainRenderer = NULL;

View File

@@ -16,7 +16,7 @@ extern SDL_Renderer *mainRenderer;
extern SDL_Rect screenRect; extern SDL_Rect screenRect;
typedef enum { typedef enum OrientDirection{
ORIENT_LEFT_DOWN, ORIENT_LEFT_DOWN,
ORIENT_LEFT, ORIENT_LEFT,
ORIENT_LEFT_UP, ORIENT_LEFT_UP,
@@ -29,6 +29,7 @@ typedef enum {
} OrientDirection; } OrientDirection;
extern bool debugMode; extern bool debugMode;
extern bool itemViewing;
SDL_Texture *createRotatedTexture(SDL_Renderer *renderer, SDL_Texture *src, double angle); SDL_Texture *createRotatedTexture(SDL_Renderer *renderer, SDL_Texture *src, double angle);