diff --git a/CMakeLists.txt b/CMakeLists.txt index 2629265..da350bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,8 @@ pkg_check_modules(SDL2 REQUIRED sdl2) add_executable(factorygame + tiles/tile.c + tiles/tile.h util/font.c util/font.h util/audio.c @@ -17,13 +19,20 @@ add_executable(factorygame util/util.h items/item.c items/item.h - tiles/tile.c - tiles/tile.h tiles/belt.c tiles/belt.h + tiles/furnace.c + tiles/furnace.h player/player.c 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 set(ASSETS_SOURCE_DIR "${CMAKE_SOURCE_DIR}/assets") diff --git a/assets/backgrounds/00water.png b/assets/backgrounds/00water.png new file mode 100644 index 0000000..db6fec3 Binary files /dev/null and b/assets/backgrounds/00water.png differ diff --git a/assets/backgrounds/01water.png b/assets/backgrounds/01water.png new file mode 100644 index 0000000..df8c4d1 Binary files /dev/null and b/assets/backgrounds/01water.png differ diff --git a/assets/backgrounds/02grass.png b/assets/backgrounds/02grass.png new file mode 100644 index 0000000..b2fc2e6 Binary files /dev/null and b/assets/backgrounds/02grass.png differ diff --git a/assets/backgrounds/03grass.png b/assets/backgrounds/03grass.png new file mode 100644 index 0000000..cd09621 Binary files /dev/null and b/assets/backgrounds/03grass.png differ diff --git a/assets/backgrounds/04grass.png b/assets/backgrounds/04grass.png new file mode 100644 index 0000000..cca5bc9 Binary files /dev/null and b/assets/backgrounds/04grass.png differ diff --git a/assets/backgrounds/05grass.png b/assets/backgrounds/05grass.png new file mode 100644 index 0000000..8d5a36d Binary files /dev/null and b/assets/backgrounds/05grass.png differ diff --git a/assets/backgrounds/06grass.png b/assets/backgrounds/06grass.png new file mode 100644 index 0000000..c2dd033 Binary files /dev/null and b/assets/backgrounds/06grass.png differ diff --git a/assets/backgrounds/07grass.png b/assets/backgrounds/07grass.png new file mode 100644 index 0000000..7ba2bb7 Binary files /dev/null and b/assets/backgrounds/07grass.png differ diff --git a/assets/backgrounds/08grass.png b/assets/backgrounds/08grass.png new file mode 100644 index 0000000..ed0130c Binary files /dev/null and b/assets/backgrounds/08grass.png differ diff --git a/assets/backgrounds/09grass.png b/assets/backgrounds/09grass.png new file mode 100644 index 0000000..3da753e Binary files /dev/null and b/assets/backgrounds/09grass.png differ diff --git a/assets/backgrounds/10grass.png b/assets/backgrounds/10grass.png new file mode 100644 index 0000000..21bda5b Binary files /dev/null and b/assets/backgrounds/10grass.png differ diff --git a/assets/backgrounds/11grass.png b/assets/backgrounds/11grass.png new file mode 100644 index 0000000..e7c9fae Binary files /dev/null and b/assets/backgrounds/11grass.png differ diff --git a/assets/backgrounds/12grass.png b/assets/backgrounds/12grass.png new file mode 100644 index 0000000..1912282 Binary files /dev/null and b/assets/backgrounds/12grass.png differ diff --git a/assets/backgrounds/13grass.png b/assets/backgrounds/13grass.png new file mode 100644 index 0000000..3b73282 Binary files /dev/null and b/assets/backgrounds/13grass.png differ diff --git a/assets/backgrounds/14sand.png b/assets/backgrounds/14sand.png new file mode 100644 index 0000000..60afa1a Binary files /dev/null and b/assets/backgrounds/14sand.png differ diff --git a/assets/backgrounds/15sand.png b/assets/backgrounds/15sand.png new file mode 100644 index 0000000..29feadc Binary files /dev/null and b/assets/backgrounds/15sand.png differ diff --git a/assets/backgrounds/16sand.png b/assets/backgrounds/16sand.png new file mode 100644 index 0000000..65aa1da Binary files /dev/null and b/assets/backgrounds/16sand.png differ diff --git a/assets/backgrounds/17sand.png b/assets/backgrounds/17sand.png new file mode 100644 index 0000000..e8bfef5 Binary files /dev/null and b/assets/backgrounds/17sand.png differ diff --git a/assets/backgrounds/18sand.png b/assets/backgrounds/18sand.png new file mode 100644 index 0000000..254e779 Binary files /dev/null and b/assets/backgrounds/18sand.png differ diff --git a/assets/backgrounds/19sand.png b/assets/backgrounds/19sand.png new file mode 100644 index 0000000..e14dec2 Binary files /dev/null and b/assets/backgrounds/19sand.png differ diff --git a/assets/backgrounds/20sand.png b/assets/backgrounds/20sand.png new file mode 100644 index 0000000..7057fe2 Binary files /dev/null and b/assets/backgrounds/20sand.png differ diff --git a/assets/backgrounds/21sand.png b/assets/backgrounds/21sand.png new file mode 100644 index 0000000..97a03b7 Binary files /dev/null and b/assets/backgrounds/21sand.png differ diff --git a/assets/backgrounds/22tiles.png b/assets/backgrounds/22tiles.png new file mode 100644 index 0000000..fd83747 Binary files /dev/null and b/assets/backgrounds/22tiles.png differ diff --git a/assets/backgrounds/23tiles.png b/assets/backgrounds/23tiles.png new file mode 100644 index 0000000..3ae9c4d Binary files /dev/null and b/assets/backgrounds/23tiles.png differ diff --git a/assets/backgrounds/24tiles.png b/assets/backgrounds/24tiles.png new file mode 100644 index 0000000..67bd1e5 Binary files /dev/null and b/assets/backgrounds/24tiles.png differ diff --git a/assets/backgrounds/25tiles.png b/assets/backgrounds/25tiles.png new file mode 100644 index 0000000..0f1e90e Binary files /dev/null and b/assets/backgrounds/25tiles.png differ diff --git a/assets/backgrounds/26cobble.png b/assets/backgrounds/26cobble.png new file mode 100644 index 0000000..47e6e30 Binary files /dev/null and b/assets/backgrounds/26cobble.png differ diff --git a/assets/backgrounds/27cobble.png b/assets/backgrounds/27cobble.png new file mode 100644 index 0000000..7ffdc81 Binary files /dev/null and b/assets/backgrounds/27cobble.png differ diff --git a/assets/backgrounds/28cobble.png b/assets/backgrounds/28cobble.png new file mode 100644 index 0000000..2388110 Binary files /dev/null and b/assets/backgrounds/28cobble.png differ diff --git a/assets/backgrounds/29cobble.png b/assets/backgrounds/29cobble.png new file mode 100644 index 0000000..9087efa Binary files /dev/null and b/assets/backgrounds/29cobble.png differ diff --git a/assets/backgrounds/30resource.png b/assets/backgrounds/30resource.png new file mode 100644 index 0000000..76854a6 Binary files /dev/null and b/assets/backgrounds/30resource.png differ diff --git a/assets/backgrounds/31resource.png b/assets/backgrounds/31resource.png new file mode 100644 index 0000000..7be3f4b Binary files /dev/null and b/assets/backgrounds/31resource.png differ diff --git a/assets/backgrounds/32resource.png b/assets/backgrounds/32resource.png new file mode 100644 index 0000000..59a1063 Binary files /dev/null and b/assets/backgrounds/32resource.png differ diff --git a/assets/backgrounds/33resource.png b/assets/backgrounds/33resource.png new file mode 100644 index 0000000..8a4cfa8 Binary files /dev/null and b/assets/backgrounds/33resource.png differ diff --git a/assets/items/02silver_ore.png b/assets/items/01silver_ore.png similarity index 100% rename from assets/items/02silver_ore.png rename to assets/items/01silver_ore.png diff --git a/assets/items/04gold_ore.png b/assets/items/02gold_ore.png similarity index 100% rename from assets/items/04gold_ore.png rename to assets/items/02gold_ore.png diff --git a/assets/items/06platinum_ore.png b/assets/items/03platinum_ore.png similarity index 100% rename from assets/items/06platinum_ore.png rename to assets/items/03platinum_ore.png diff --git a/assets/items/01iron_ingot.png b/assets/items/04iron_ingot.png similarity index 100% rename from assets/items/01iron_ingot.png rename to assets/items/04iron_ingot.png diff --git a/assets/items/07platinum_ingot.png b/assets/items/06platinum_ingot.png similarity index 100% rename from assets/items/07platinum_ingot.png rename to assets/items/06platinum_ingot.png diff --git a/assets/items/03silver_ingot.png b/assets/items/07silver_ingot.png similarity index 100% rename from assets/items/03silver_ingot.png rename to assets/items/07silver_ingot.png diff --git a/assets/tiles/3furnace.png b/assets/tiles/3furnace.png new file mode 100644 index 0000000..72b9d1c Binary files /dev/null and b/assets/tiles/3furnace.png differ diff --git a/items/item.c b/items/item.c index 4fe8555..b562aa1 100644 --- a/items/item.c +++ b/items/item.c @@ -5,6 +5,9 @@ #include "item.h" #include "../player/player.h" #include "../util/font.h" +#include "../tiles/furnace.h" +#include "../tiles/tilecallbacks.h" +#include "../util/atlas.h" #include Item ItemRegistry[ITEMREGISTRY_SIZE]; @@ -22,22 +25,27 @@ 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->type != TYPE_BELT) continue; + TileTypeReg tt = TileRegistry[t->type]; + if (t->type == TYPE_AIR) continue; int dir = t->direction; bool horz = (dir == ORIENT_LEFT || dir == ORIENT_RIGHT); 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]; if (itm->type == 0) continue; - - // 1) Advance - itm->offset += speed; + if (tt.itemMoves) { + itm->offset += speed; + // 1) Advance + } // 2) Time to hop? - if (itm->offset >= 0.5f) { - itm->offset -= 1.0f; + if (itm->offset >= 0.5f || !tt.itemMoves) { + if (tt.itemMoves) { + itm->offset -= 1.0f; + } // target coords int nx = x + dirDx[dir]; @@ -45,43 +53,49 @@ void updateItems() { // bounds & belt? if (nx < 0 || nx >= MAP_WIDTH || ny < 0 || ny >= MAP_HEIGHT) { - //itm->type = 0; - itm->offset += 1.0f - speed; + if (tt.itemMoves) { + itm->offset += 1.0f - speed; + } continue; } Tile *next = &tileMap[ny][nx]; - if (next->type != TYPE_BELT) { - //itm->type = 0; - itm->offset += 1.0f - speed; - continue; - } - - // Decide new lane + TileTypeReg ntt = TileRegistry[next->type]; int newLane = lane; - int newDir = next->direction; - bool nH = (newDir == ORIENT_LEFT || newDir == ORIENT_RIGHT); - bool nV = (newDir == ORIENT_UP || newDir == ORIENT_DOWN); + switch (next->type) { + case TYPE_BELT: + int newDir = next->direction; + bool nH = (newDir == ORIENT_LEFT || newDir == ORIENT_RIGHT); + bool nV = (newDir == ORIENT_UP || newDir == ORIENT_DOWN); - if ((horz && nH) || (vert && nV)) { - // same axis → keep lane - } else if (horz && nV) { - // came off a horizontal: lane0=top→vertical.left, lane1=bottom→vertical.right - newLane = (dir == ORIENT_RIGHT ^ newDir == ORIENT_UP ? 0 : 1); - itm->offset = 0.0f; - } else if (vert && nH) { - // came off vertical: lane0=left→horizontal.top, lane1=right→horizontal.bottom - newLane = (dir == ORIENT_UP ^ newDir == ORIENT_RIGHT ? 1 : 0); - itm->offset = 0.0f; + if ((horz && nH) || (vert && nV)) { + // same axis → keep lane + } else if (horz && nV) { + // came off a horizontal: lane0=top→vertical.left, lane1=bottom→vertical.right + newLane = (dir == ORIENT_RIGHT ^ newDir == ORIENT_UP ? 0 : 1); + itm->offset = 0.0f; + } else if (vert && nH) { + // came off vertical: lane0=left→horizontal.top, lane1=right→horizontal.bottom + newLane = (dir == ORIENT_UP ^ newDir == ORIENT_RIGHT ? 1 : 0); + itm->offset = 0.0f; + } + // (diagonals fall back to same-lane) + + // Find a free slot in + break; + + default: + itm->offset += 1.0f - speed; } - // (diagonals fall back to same-lane) - // Find a free slot in - if (next->items[newLane].type == 0) { + if (next->items[newLane].type == 0 && ntt.allowedInItems[newLane][itm->type]) { // MOVE it ItemOnBelt moved = *itm; moved.tileX = nx; moved.tileY = ny; + if (!ntt.itemMoves) { + moved.offset = 0.5f; + } next->items[newLane] = moved; // clear this one @@ -90,8 +104,15 @@ void updateItems() { // both slots full → wait at end 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], TILE_SIZE / 2, TILE_SIZE / 2); 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].isTile = false; + ItemRegistry[itemRegistryIndex].miscVal = 60; itemRegistryIndex++; } @@ -196,7 +221,7 @@ void renderItem(ItemOnBelt item, SDL_Renderer *renderer, int lane, SDL_Rect play yOffset += 0.0f * TILE_SIZE; break; case ORIENT_LEFT: - xOffset += 0.0f * TILE_SIZE + (TILE_SIZE / 2); + xOffset += 0.0f * TILE_SIZE + (TILE_SIZE); yOffset += 0.26f * TILE_SIZE; break; case ORIENT_LEFT_UP: @@ -205,14 +230,14 @@ void renderItem(ItemOnBelt item, SDL_Renderer *renderer, int lane, SDL_Rect play break; case ORIENT_UP: xOffset += 0.22f * TILE_SIZE; //GOTO HEHREHRHE - yOffset += 0.0f * TILE_SIZE + (TILE_SIZE / 2); + yOffset += 0.0f * TILE_SIZE + (TILE_SIZE); break; case ORIENT_RIGHT_UP: xOffset += 0.0f * TILE_SIZE; yOffset += 0.0f * TILE_SIZE; break; case ORIENT_RIGHT: - xOffset += 0.0f * TILE_SIZE; + xOffset += 0.0f * TILE_SIZE - (TILE_SIZE / 2); yOffset += 0.18f * TILE_SIZE; //FIX THIS break; 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; break; case ORIENT_DOWN: - xOffset += 0.18f * TILE_SIZE + (TILE_SIZE / 4); - yOffset += 0.0f * TILE_SIZE; + xOffset += 0.18f * TILE_SIZE + (TILE_SIZE / 8); + yOffset += 0.0f * TILE_SIZE - (TILE_SIZE / 2); break; default: 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].offset = 0; 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) { + for (int i = 0; i < tileTypeIndex; i++) { - TileType tile = TileRegistry[i]; + TileTypeReg tile = TileRegistry[i]; strcpy(ItemRegistry[itemRegistryIndex].name, tile.name); 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++) { SDL_SetTextureBlendMode(ItemRegistry[itemRegistryIndex].texture[a], SDL_BLENDMODE_BLEND); ItemRegistry[itemRegistryIndex].textureOnBelt[a] = ScaleTexture(renderer, tile.textures[a], 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); } ItemRegistry[itemRegistryIndex].type = itemRegistryIndex; + ItemRegistry[itemRegistryIndex].isTile = true; itemRegistryIndex++; } + itemRegistryIndex = ITEMREGISTRY_SIZE / 2; 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); -// } -// } } \ No newline at end of file diff --git a/items/item.h b/items/item.h index 43df48b..b89b7b1 100644 --- a/items/item.h +++ b/items/item.h @@ -5,23 +5,46 @@ #include #include "../util/util.h" #include "../tiles/belt.h" +#include "../tiles/tile.h" #ifndef 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; int tileX, tileY; - uint16_t type; + ItemType type; } ItemOnBelt; -typedef struct { - uint16_t type; +typedef struct Item { + bool isTile; + ItemType type; char name[20]; + uint16_t miscVal; SDL_Texture * texture[ORIENT_DIRECTION_COUNT]; SDL_Texture * textureOnBelt[ORIENT_DIRECTION_COUNT]; + SDL_Rect atlasRects[ORIENT_DIRECTION_COUNT]; + SDL_Rect atlasRectsOnBelt[ORIENT_DIRECTION_COUNT]; } Item; @@ -39,5 +62,5 @@ extern uint8_t laneTarget; 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 diff --git a/main.c b/main.c index 778425e..a2f0b24 100644 --- a/main.c +++ b/main.c @@ -7,28 +7,46 @@ #include "items/item.h" #include "stdlib.h" #include "player/player.h" +#include "util/perlin.h" +#include "util/atlas.h" -typedef struct { +typedef struct GameState { Player player; Tile tileMap[MAP_HEIGHT][MAP_WIDTH]; + BackgroundTile backgroundTileMap[MAP_HEIGHT][MAP_WIDTH]; + SynthVoice voices[NUM_SYNTH_VOICES]; } GameState; GameState gameState; -void loadGameState(char *filename, Player *plr) { +int loadGameState(char *filename, Player *plr) { printf("hello from load\n"); 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(tileMap, gameState.tileMap, sizeof(tileMap)); 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) { memcpy(&gameState.player, plr, sizeof(gameState.player)); 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"); if (!gameSave) { @@ -51,7 +69,7 @@ const int delayNeeded = 1000 / targetFPS; #define smallerFont fonts[2] #define smallestFont fonts[3] -const char *autosaveName = "autosave.dat"; +char *autosaveName = "autosave.dat"; unsigned long frames = 0; @@ -66,6 +84,8 @@ void msleep(unsigned int milliseconds) { SDL_GLContext glContext; +void genInitMap(); + int init() { //Initialize SDL @@ -75,7 +95,9 @@ int init() { screenRect.w = DISPLAY_WIDTH; 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_VIDEO_HIGHDPI_DISABLED, "1"); @@ -107,8 +129,17 @@ int init() { return 1; } + initAtlas(mainRenderer); + loadTiles(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 glContext = SDL_GL_CreateContext(window); if (!glContext) { @@ -119,21 +150,24 @@ int init() { // Use OpenGL context 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}; spec.freq = SAMPLE_RATE; - spec.format = AUDIO_F32SYS; - spec.channels = 1; + spec.format = AUDIO_F32; + spec.channels = 2; 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_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, 1); SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255); SDL_RenderClear(mainRenderer); @@ -150,6 +184,10 @@ int init() { initPlayer(&player); + for (ItemType i = 0; i < 13; i++) { + player.inventory.hotKeySlots[i] = i; + } + initTiles(); //generateTestMap(); @@ -163,20 +201,22 @@ int render() { SDL_RenderClear(mainRenderer); SDL_Rect rect2; - rect2.x = 20; - rect2.y = 20; + rect2.x = 0; + rect2.y = 0; rect2.w = 0; rect2.h = 0; 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); +// 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); frames++; @@ -206,11 +246,11 @@ int processEvent(SDL_Event e) { speed = speed == 0 ? 0.004f : 0; break; 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.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; } break; @@ -220,6 +260,12 @@ int processEvent(SDL_Event e) { case SDLK_F3: debugMode = !debugMode; break; + case SDLK_F4: + Tile *tile = &tileMap[playerTileY][playerTileX]; + break; + case SDLK_LALT: + itemViewing = !itemViewing; + break; default: break; } @@ -274,15 +320,13 @@ void processMousePosition() { if (player.cursor.targetTile->type < tileTypeIndex) { player.inventory.slotCounts[player.cursor.targetTile->type]++; } - if (player.cursor.targetTile->type == TYPE_BELT) { - for (int lane = 0; lane < 2; lane++) { - if (player.cursor.targetTile->items[lane].type != 0) { - int itemType = player.cursor.targetTile->items[lane].type; - if (itemType < itemRegistryIndex) { - player.inventory.slotCounts[itemType]++; - } - player.cursor.targetTile->items[lane].type = 0; + for (int lane = 0; lane < 2; lane++) { + if (player.cursor.targetTile->items[lane].type != 0) { + int itemType = player.cursor.targetTile->items[lane].type; + if (itemType < itemRegistryIndex) { + player.inventory.slotCounts[itemType]++; } + player.cursor.targetTile->items[lane].type = 0; } } player.cursor.targetTile->type = TYPE_AIR; @@ -311,6 +355,18 @@ void processMousePosition() { 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.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.targetTile = &tileMap[player.cursor.tileY][player.cursor.tileX]; @@ -329,7 +385,7 @@ void processKeyboardHeld() { int cameraSpeed = playerSpeed; if (keyboardState[SDL_SCANCODE_LSHIFT] || keyboardState[SDL_SCANCODE_RSHIFT]) { - cameraSpeed *= 2; + cameraSpeed *= 8; } if (keyboardState[SDL_SCANCODE_LCTRL] || keyboardState[SDL_SCANCODE_RCTRL]) { cameraSpeed /= 2; @@ -339,39 +395,57 @@ void processKeyboardHeld() { if (keyboardState[SDL_SCANCODE_W]) { // Example: move up player.rect.y -= cameraSpeed; - if (player.rect.y < (DISPLAY_HEIGHT / 2)) { - player.rect.y = (DISPLAY_HEIGHT / 2); +// if (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]) { player.rect.y += cameraSpeed; - if (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) - (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]) { player.rect.x -= cameraSpeed; - if (player.rect.x < (DISPLAY_WIDTH / 2)) { - player.rect.x = (DISPLAY_WIDTH / 2); +// if (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]) { player.rect.x += cameraSpeed; - if (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) - (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]) { - 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) { 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) { continue; } + if (y >= MAP_HEIGHT) { + continue; + } Tile *t = &tileMap[y][x]; if (t->type == TYPE_BELT) { for (uint8_t lane = 0; lane < 2; lane++) { @@ -431,8 +505,8 @@ void processKeyboardHeld() { } else if (keyboardState[SDL_SCANCODE_EQUALS]) { slot = 13; } - if (slot > 0 && slot < itemRegistryIndex) { - setActivePlayerSlot(&player, slot); + if (slot > 0 && slot < sizeof(player.inventory.hotKeySlots) / sizeof(player.inventory.hotKeySlots[0])) { + setActivePlayerSlot(&player, player.inventory.hotKeySlots[slot]); } } @@ -441,7 +515,9 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[]) if (status) { return status; } - loadGameState(autosaveName, &player); + if (loadGameState(autosaveName, &player)) { + genInitMap(); + } //Hack to get window to stay up @@ -460,6 +536,7 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[]) updateItems(); updatePlayer(&player); + updateTiles(); status = render(); if (status) { return status; @@ -485,4 +562,91 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[]) SDL_Quit(); return 0; -} \ No newline at end of file +} + +// 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); +} + diff --git a/player/player.c b/player/player.c index f642f0a..79a61f4 100644 --- a/player/player.c +++ b/player/player.c @@ -26,7 +26,7 @@ SDL_Rect targetItemRect; SDL_Color healthBarColor = {0, 240, 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; if (activeSlotIndex <= 0) { activeSlotIndex = 1; @@ -35,35 +35,30 @@ void setActivePlayerSlot(Player *plr, uint16_t activeSlotIndex) { } void moveActivePlayerSlot(Player *plr, bool up, bool seek) { - if (seek) { - uint16_t prevSlot = plr->inventory.activeSlotIndex; - uint16_t newSlot = prevSlot; + ItemType prevSlot = plr->inventory.activeSlotIndex; + ItemType newSlot = prevSlot; - do { - newSlot = (newSlot + (up ? 1 : ITEMREGISTRY_SIZE - 1)) % ITEMREGISTRY_SIZE; + do { + newSlot = (newSlot + (up ? 1 : ITEMREGISTRY_SIZE - 1)) % ITEMREGISTRY_SIZE; - // Stop if we've looped all the way around - if (newSlot == prevSlot) break; + // Stop if we've looped all the way around + if (newSlot == prevSlot) break; - // Stop if we found a slot with count > 0 - if (plr->inventory.slotCounts[newSlot] > 0 && newSlot != 0) break; - } while (true); - 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; + // Stop if we found a slot with count > 0 + if (!strlen(ItemRegistry[newSlot].name)) continue; + if (newSlot == 0) continue; + if (seek) { + if (plr->inventory.slotCounts[newSlot] > 0) break; } else { - plr->inventory.activeSlotIndex = - (plr->inventory.activeSlotIndex + (up ? 1 : ITEMREGISTRY_SIZE - 1)) % ITEMREGISTRY_SIZE; - + break; } - } + + } while (true); + plr->inventory.activeSlotIndex = newSlot; } -void adjustRect(SDL_Rect *rect, SDL_Rect playerRect) { +inline void adjustRect(SDL_Rect *rect, SDL_Rect playerRect) { rect->x -= playerRect.x; rect->y -= playerRect.y; rect->x += DISPLAY_WIDTH / 2; @@ -117,7 +112,11 @@ void initPlayer(Player *plr) { plr->rect.w = 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; } @@ -172,7 +171,7 @@ void renderPlayer(Player *plr) { SDL_SetRenderDrawColor(mainRenderer, plr->cursor.canReach ? 0 : 255, plr->cursor.canReach ? 255 : 0, 0, 128); DrawThickRect(mainRenderer, plr->cursor.targetTileRect, 4); - uint16_t itemIndex = plr->inventory.activeSlotIndex; + ItemType itemIndex = plr->inventory.activeSlotIndex; SDL_Texture *itemTex; char itemStringCount[6]; 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, healthBarColor, 4); - renderBar(mainRenderer, (DISPLAY_WIDTH / 2) - 128, DISPLAY_HEIGHT - 70, 200, 8, - getBreakTime(plr->cursor.targetTile->type), plr->cursor.breakingProgress, breakingBarColor, 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, + tempko, tempko2, breakingBarColor, 4); + } 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.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]; if (itemTex == NULL) { itemTex = ItemRegistry[i].textureOnBelt[ORIENT_LEFT]; @@ -228,17 +231,17 @@ void renderPlayer(Player *plr) { SDL_RenderCopy(mainRenderer, itemTex, NULL, &targetItemRect); SDL_SetTextureColorMod(itemTex, 255, 255, 255); + if (plr->inventory.activeSlotIndex == i) { + SDL_SetRenderDrawColor(mainRenderer, 16, plr->inventory.slotCounts[i] > 0 ? 128 : 16, + plr->inventory.slotCounts[i] > 0 ? 32 : 128, 255); + DrawThickRect(mainRenderer, targetItemRect, 4); + } + sprintf(itemStringCount, "%d", plr->inventory.slotCounts[i]); + renderText(mainRenderer, fonts[2], itemStringCount, targetItemRect.x - 3, + targetItemRect.y - (fonts[2].size * 3 / 2)); + //targetItemRect.x += (TILE_SIZE / 2) + (TILE_SIZE / 4); + targetItemRect.x += TILE_SIZE; } - if (plr->inventory.activeSlotIndex == i) { - SDL_SetRenderDrawColor(mainRenderer, 16, plr->inventory.slotCounts[i] > 0 ? 128 : 16, - plr->inventory.slotCounts[i] > 0 ? 32 : 128, 255); - DrawThickRect(mainRenderer, targetItemRect, 4); - } - sprintf(itemStringCount, "%d", plr->inventory.slotCounts[i]); - renderText(mainRenderer, fonts[2], itemStringCount, targetItemRect.x - 3, - targetItemRect.y - (fonts[2].size * 3 / 2)); - //targetItemRect.x += (TILE_SIZE / 2) + (TILE_SIZE / 4); - targetItemRect.x += TILE_SIZE; } SDL_SetRenderTarget(mainRenderer, originalTarget); diff --git a/player/player.h b/player/player.h index 0405f69..bcfc228 100644 --- a/player/player.h +++ b/player/player.h @@ -28,12 +28,13 @@ void adjustRect(SDL_Rect *rect, SDL_Rect playerRect); #define playerMaxHealth 255 -typedef struct { +typedef struct PlayerInventory { uint16_t slotCounts[ITEMREGISTRY_SIZE]; - uint16_t activeSlotIndex; + ItemType activeSlotIndex; + ItemType hotKeySlots[13]; } PlayerInventory; -typedef struct { +typedef struct PlayerCursor { int windowX; int windowY; int tileX; @@ -50,7 +51,7 @@ typedef struct { int breakingProgress; } PlayerCursor; -typedef struct { +typedef struct Player{ PlayerCursor cursor; PlayerInventory inventory; uint8_t health; @@ -59,7 +60,7 @@ typedef struct { SDL_Rect rect; } Player; -void setActivePlayerSlot(Player *plr, uint16_t activeSlotIndex); +void setActivePlayerSlot(Player *plr, ItemType activeSlotIndex); void moveActivePlayerSlot(Player *plr, bool up, bool seek); diff --git a/tiles/belt.c b/tiles/belt.c index f7dd1bc..16b6729 100644 --- a/tiles/belt.c +++ b/tiles/belt.c @@ -16,7 +16,7 @@ void renderBelt(int x, int y, int w, int h, OrientDirection dir, SDL_Rect player Tile *t = &tileMap[y][x]; - uint16_t tileType = t->type; + ItemType tileType = t->type; 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], &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); - } - } } \ No newline at end of file diff --git a/tiles/belt.h b/tiles/belt.h index 009e993..6573d73 100644 --- a/tiles/belt.h +++ b/tiles/belt.h @@ -7,9 +7,12 @@ #include #include -#include "tile.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 updateBelt(struct Tile * tile); + #endif //FACTORYGAME_BELT_H diff --git a/tiles/furnace.c b/tiles/furnace.c index 360d4d7..55474ec 100644 --- a/tiles/furnace.c +++ b/tiles/furnace.c @@ -4,11 +4,48 @@ #include "furnace.h" #include "tile.h" +#include "../util/audio.h" -uint16_t getFurnaceNewItem(uint16_t sourceItem) { - uint16_t realItemIndex = sourceItem - tileTypeIndex - 1; - if (realItemIndex < 8) { - return sourceItem + 1; +const ItemType FurnaceRecipes[ITEMREGISTRY_SIZE] = { + [IRON_ORE] = IRON_INGOT, + [SILVER_ORE] = SILVER_INGOT, + [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; } \ No newline at end of file diff --git a/tiles/furnace.h b/tiles/furnace.h index 96702c1..7f9f046 100644 --- a/tiles/furnace.h +++ b/tiles/furnace.h @@ -5,6 +5,14 @@ #ifndef FACTORYGAME_FURNACE_H #define FACTORYGAME_FURNACE_H +#include "../items/item.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 diff --git a/tiles/tile.c b/tiles/tile.c index d737534..76fd88a 100644 --- a/tiles/tile.c +++ b/tiles/tile.c @@ -5,18 +5,26 @@ #include #include "tile.h" #include "../player/player.h" +#include "furnace.h" +#include "../util/atlas.h" int scrollFrame = 0; unsigned long beltFrames = 0; +SDL_Texture *backgroundTexture; SDL_Texture *tilesTexture; SDL_Texture *itemsTexture; 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() { for (int y = 0; y < DISPLAY_MAP_HEIGHT; y++) { @@ -32,7 +40,7 @@ void generateTestMap() { for (int y = 0; y < MAP_HEIGHT; y += 1) { 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 = 5; 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_UP] = createRotatedTexture(renderer, texture, 90); 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].breakTime = 15; 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) { iterateSortedDir("./assets/tiles", (DirEntryCallback) registerTile, renderer); -// DIR *dir = opendir("./assets/tiles"); -// if (dir) { -// struct dirent *entry; -// while ((entry = readdir(dir)) != NULL) { -// if (entry->d_name[0] == '.') { -// continue; -// } -// registerTile(entry->d_name, mainRenderer); -// } -// } - TileRegistry[0].breakTime = 0; + iterateSortedDir("./assets/backgrounds", (DirEntryCallback) registerBackgroundTile, renderer); + +} + +void setupTiles() { + TileRegistry[TYPE_AIR].breakTime = 0; + TileRegistry[TYPE_BELT].itemMoves = true; + for (uint16_t i = 0; i < ItemSlotCount; i++) { + TileRegistry[TYPE_BELT].outputLane[i] = true; + } + for (uint16_t l = 0; l < ItemSlotCount; l++) { + for (ItemType i = 0; i < itemRegistryIndex; i++) { + TileRegistry[TYPE_BELT].allowedInItems[l][i] = true; + } + } + + for (ItemType i = 0; i < itemRegistryIndex; i++) { + if (FurnaceRecipes[i] != 0) { + TileRegistry[TYPE_FURNACE].allowedInItems[FURNACE_INPUT_SLOT][i] = true; + } + } + TileRegistry[TYPE_FURNACE].outputLane[FURNACE_OUTPUT_SLOT] = 1; } uint16_t getBreakTime(int type) { @@ -92,6 +142,9 @@ void initTiles() { screenRect.h); tilesTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, screenRect.w, screenRect.h); + backgroundTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, + screenRect.w, + screenRect.h); SDL_SetTextureBlendMode(itemsTexture, SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(tilesTexture, SDL_BLENDMODE_BLEND); } @@ -99,10 +152,14 @@ void initTiles() { void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect) { SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_Texture *oldTarget = SDL_GetRenderTarget(renderer); - SDL_SetRenderTarget(renderer, itemsTexture); - SDL_RenderClear(renderer); - SDL_SetRenderTarget(renderer, tilesTexture); - SDL_RenderClear(renderer); + + int tileSize = TILE_SIZE; + + int minTileX = (playerRect.x / TILE_SIZE) - (DISPLAY_MAP_WIDTH / 2) - 1; + int maxTileX = (playerRect.x / TILE_SIZE) + (DISPLAY_MAP_WIDTH / 2) + 1; + int minTileY = (playerRect.y / TILE_SIZE) - (DISPLAY_MAP_HEIGHT / 2) - 1; + int maxTileY = (playerRect.y / TILE_SIZE) + (DISPLAY_MAP_HEIGHT / 2) + 1; + int scrollSpeed = 1; // pixels per step int scrollDelay = 1; // frames between steps @@ -110,42 +167,83 @@ void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect) { scrollFrame += scrollSpeed; } - int tileSize = TILE_SIZE; - for (int y = (playerRect.y / TILE_SIZE) - (DISPLAY_MAP_HEIGHT / 2) - 1; - y < (playerRect.y / TILE_SIZE) + (DISPLAY_MAP_HEIGHT / 2) + 1; y++) { - if (y < 0 || y >= MAP_HEIGHT) { - continue; - } - 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; + // --- Render background tiles --- + SDL_SetRenderTarget(renderer, backgroundTexture); + SDL_RenderClear(renderer); + + for (int y = minTileY; y < maxTileY; y++) { + if (y < 0 || y >= MAP_HEIGHT) continue; + for (int x = minTileX; x < maxTileX; x++) { + if (x < 0 || x >= MAP_WIDTH) continue; + + SDL_Rect dstRect = { + .x = x * TILE_SIZE, + .y = y * TILE_SIZE, + .w = TILE_SIZE, + .h = TILE_SIZE + }; + adjustRect(&dstRect, playerRect); + + BackgroundTile bt = backgroundMap[y][x]; + SDL_Texture *tex = BackgroundTileRegistry[bt.type].texture; + if (tex != NULL) { + SDL_RenderCopy(renderer, tex, NULL, &dstRect); } + } + } + + SDL_SetRenderTarget(renderer, tilesTexture); + SDL_RenderClear(renderer); + for (int y = minTileY; y < maxTileY; y++) { + if (y < 0 || y >= MAP_HEIGHT) continue; + for (int x = minTileX; x < maxTileX; x++) { + if (x < 0 || x >= MAP_WIDTH) continue; + + SDL_Rect dstRect = { + .x = x * TILE_SIZE, + .y = y * TILE_SIZE, + .w = TILE_SIZE, + .h = TILE_SIZE + }; + adjustRect(&dstRect, playerRect); + Tile t = tileMap[y][x]; - SDL_SetRenderTarget(renderer, tilesTexture); switch (t.type) { case TYPE_BELT: renderBelt(x, y, tileSize, tileSize, t.direction, playerRect, renderer); break; - 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); + default: { SDL_Texture *tex = TileRegistry[t.type].textures[t.direction]; - if (tex == NULL) { - tex = TileRegistry[t.type].textures[ORIENT_LEFT]; - } + if (tex == NULL) tex = TileRegistry[t.type].textures[ORIENT_LEFT]; if (tex != NULL) { SDL_RenderCopy(renderer, tex, NULL, &dstRect); } - - } - if (t.type == TYPE_BELT) { + } } } } + + SDL_SetRenderTarget(renderer, itemsTexture); + SDL_RenderClear(renderer); + for (int y = minTileY; y < maxTileY; y++) { + if (y < 0 || y >= MAP_HEIGHT) continue; + for (int x = minTileX; x < maxTileX; x++) { + if (x < 0 || x >= MAP_WIDTH) continue; + + Tile t = tileMap[y][x]; + if (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); +} + +void updateTiles() { + } \ No newline at end of file diff --git a/tiles/tile.h b/tiles/tile.h index a5478ce..fb5d115 100644 --- a/tiles/tile.h +++ b/tiles/tile.h @@ -5,15 +5,15 @@ #ifndef FACTORYGAME_TILE_H #define FACTORYGAME_TILE_H -#include "belt.h" #include "../util/util.h" #include "../items/item.h" +//#include "../items/item.h" -#define MAP_WIDTH 600 -#define MAP_HEIGHT 340 +#define MAP_WIDTH 100 +#define MAP_HEIGHT 100 -#define DISPLAY_MAP_WIDTH 44 -#define DISPLAY_MAP_HEIGHT 22 +#define DISPLAY_MAP_WIDTH 60 +#define DISPLAY_MAP_HEIGHT 31 #define TILE_SIZE 32 @@ -22,42 +22,108 @@ extern SDL_Texture *tilesTexture; extern SDL_Texture *itemsTexture; +extern SDL_Texture *backgroundTexture; extern int scrollFrame; 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]; SDL_Texture *textures[ORIENT_DIRECTION_COUNT]; + SDL_Rect atlasRects[ORIENT_DIRECTION_COUNT]; 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 TYPE_BELT 2 +#define TILEREGISTRY_SIZE 64 + +extern BackgroundTileType BackgroundTileRegistry[TILEREGISTRY_SIZE]; + +extern TileTypeReg TileRegistry[TILEREGISTRY_SIZE]; void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect); -typedef struct { +void updateTiles(); + +typedef struct Tile { OrientDirection direction; - uint16_t type; - int frameOffset; - ItemOnBelt items[2]; + ItemType type; + int miscVal; + ItemOnBelt items[ItemSlotCount]; + uint16_t audioCh; int x; int y; } Tile; + extern Tile tileMap[MAP_HEIGHT][MAP_WIDTH]; +extern BackgroundTile backgroundMap[MAP_HEIGHT][MAP_WIDTH]; + +void setupTiles(); + void generateTestMap(); void loadTiles(SDL_Renderer *renderer); extern uint16_t tileTypeIndex; +extern uint16_t backgroundTileTypeIndex; + uint16_t getBreakTime(int type); diff --git a/tiles/tilecallbacks.c b/tiles/tilecallbacks.c new file mode 100644 index 0000000..4924b72 --- /dev/null +++ b/tiles/tilecallbacks.c @@ -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 +}; \ No newline at end of file diff --git a/tiles/tilecallbacks.h b/tiles/tilecallbacks.h new file mode 100644 index 0000000..62ef2f7 --- /dev/null +++ b/tiles/tilecallbacks.h @@ -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 diff --git a/util/atlas.c b/util/atlas.c new file mode 100644 index 0000000..754248a --- /dev/null +++ b/util/atlas.c @@ -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 we’re 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); +} \ No newline at end of file diff --git a/util/atlas.h b/util/atlas.h new file mode 100644 index 0000000..85b4a3f --- /dev/null +++ b/util/atlas.h @@ -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 diff --git a/util/audio.c b/util/audio.c index fa87587..2260e0d 100644 --- a/util/audio.c +++ b/util/audio.c @@ -6,21 +6,93 @@ 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 constant‐power law). + // If you prefer constant‐power, you could do: + // float angle = (pan + 1.0f) * (M_PI / 4.0f); + // *outL = cosf(angle); + // *outR = sinf(angle); + // + // Here we’ll 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) { AudioData *audio = (AudioData *) userdata; - int samples = len / sizeof(float); - for (int i = 0; i < samples; i++) { - float mix = 0.0f; - int activeVoices = 0; + // 'len' is total bytes; each sample‐frame is 2 floats (L+R), i.e. 2 * sizeof(float). + int frames = len / (2 * sizeof(float)); - for (int v = 0; v < NUM_SYNTH_VOICES; v++) { - SynthVoice *voice = &audio->synthVoices[v]; - if (voice->volume == 0 || voice->frequency == 0) continue; + // Zero out the entire output buffer (silence) + // We’ll 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; + } - float sample; + // 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++) { + SynthVoice *voice = &audio->synthVoices[v]; + if (voice->volume == 0 || voice->frequency == 0) { + continue; // skip silent or inactive voices + } + + // 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, we’ll just use voice->volume for amplitude. + + float amp = (voice->volume / 255.0f); + + // Phase increment per sample‐frame: + // (freq * 256) / SAMPLE_RATE tells how many phase steps per mono-sample. + // Because we’re 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 sample; switch (voice->waveform) { default: case WAVE_SINE: @@ -33,18 +105,27 @@ void audio_callback(void *userdata, Uint8 *stream, int len) { sample = t; break; case WAVE_TRIANGLE: - sample = (t < 0) ? -t : t; + sample = (t < 0.0f) ? -t : t; break; case WAVE_NOISE: sample = ((float) rand() / RAND_MAX) * 2.0f - 1.0f; break; } - voice->phase += (uint8_t) ((voice->frequency * 256) / SAMPLE_RATE); - mix += sample * (voice->volume / 255.0f); - activeVoices++; - } + voice->phase += phaseInc; - ((float *) stream)[i] = (activeVoices > 0) ? mix / activeVoices : 0.0f; + // 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; + } } -} + + // 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 per‐frame, which is relatively expensive. + // In practice, you manage the volume per voice so clipping doesn’t occur. +} \ No newline at end of file diff --git a/util/audio.h b/util/audio.h index 1c770a6..e4f9bf7 100644 --- a/util/audio.h +++ b/util/audio.h @@ -11,9 +11,9 @@ #include #define SAMPLE_RATE 44100 -#define NUM_SYNTH_VOICES 3 +#define NUM_SYNTH_VOICES 256 -typedef enum { +typedef enum Waveform { WAVE_SINE, WAVE_SQUARE, WAVE_SAWTOOTH, @@ -21,19 +21,24 @@ typedef enum { WAVE_NOISE } Waveform; -typedef struct { - uint8_t volume; - uint16_t frequency; - uint8_t phase; +typedef struct SynthVoice { Waveform waveform; + uint8_t phase; + uint16_t frequency; + uint8_t volume; + SDL_Rect sourceRect; } SynthVoice; -typedef struct { +typedef struct AudioData { SynthVoice synthVoices[NUM_SYNTH_VOICES]; + SDL_Rect *playerRect; + float maxPanDistance; } AudioData; extern AudioData audioData; void audio_callback(void *userdata, Uint8 *stream, int len); +uint16_t getAvailableChannel(); + #endif //RISCB_AUDIO_H diff --git a/util/font.h b/util/font.h index e1f119b..3430e9b 100644 --- a/util/font.h +++ b/util/font.h @@ -11,7 +11,7 @@ #define fontCount 4 -typedef struct { +typedef struct BitmapFont { SDL_Texture *texture[256]; SDL_Surface *surface[256]; uint8_t size; diff --git a/util/perlin.c b/util/perlin.c new file mode 100644 index 0000000..1fb5c99 --- /dev/null +++ b/util/perlin.c @@ -0,0 +1,52 @@ + +#include + +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); +} diff --git a/util/perlin.h b/util/perlin.h new file mode 100644 index 0000000..17b72ee --- /dev/null +++ b/util/perlin.h @@ -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 diff --git a/util/util.c b/util/util.c index 34dd837..b916a60 100644 --- a/util/util.c +++ b/util/util.c @@ -6,11 +6,13 @@ #include "util.h" //#include "font.h" + //The window we'll be rendering to SDL_Window *window = NULL; volatile bool running = true; bool debugMode = false; +bool itemViewing = false; //The surface contained by the window SDL_Renderer *mainRenderer = NULL; diff --git a/util/util.h b/util/util.h index aef74a5..5695a8e 100644 --- a/util/util.h +++ b/util/util.h @@ -16,7 +16,7 @@ extern SDL_Renderer *mainRenderer; extern SDL_Rect screenRect; -typedef enum { +typedef enum OrientDirection{ ORIENT_LEFT_DOWN, ORIENT_LEFT, ORIENT_LEFT_UP, @@ -29,6 +29,7 @@ typedef enum { } OrientDirection; extern bool debugMode; +extern bool itemViewing; SDL_Texture *createRotatedTexture(SDL_Renderer *renderer, SDL_Texture *src, double angle);