More progress on audio and rendering

This commit is contained in:
2025-06-02 18:04:15 +02:00
parent 84805b92cb
commit 0c3e2aa730
14 changed files with 569 additions and 264 deletions

BIN
assets/audio/testaid.mid Normal file

Binary file not shown.

View File

@@ -16,103 +16,103 @@ uint16_t itemRegistryIndex = 0;
double speed = 1.0f / TILE_SIZE; // fraction of tile per tick double speed = 1.0f / TILE_SIZE; // fraction of tile per tick
const int dirDx[8] = {-1, -1, -1, 0, 1, 1, 1, 0};
const int dirDy[8] = {1, 0, -1, -1, -1, 0, 1, 1};
const float epsilon = 0.999f; // if we can't move, back it off just below 1
void updateItems() { void updateItems() {
const int dirDx[8] = {-1, -1, -1, 0, 1, 1, 1, 0};
const int dirDy[8] = {1, 0, -1, -1, -1, 0, 1, 1};
const float epsilon = 0.999f; // if we can't move, back it off just below 1 for (int i = 0; i < neededUpdates.activeCount; i++) {
int x = neededUpdates.tiles[i].x;
int y = neededUpdates.tiles[i].y;
Tile *t = &tileMap[y][x];
if (t->type == TYPE_AIR) continue;
TileTypeReg tt = TileRegistry[t->type];
int dir = t->direction;
for (int y = 0; y < MAP_HEIGHT; y++) { bool horz = (dir == ORIENT_LEFT || dir == ORIENT_RIGHT);
for (int x = 0; x < MAP_WIDTH; x++) { bool vert = (dir == ORIENT_UP || dir == ORIENT_DOWN);
Tile *t = &tileMap[y][x];
TileTypeReg tt = TileRegistry[t->type];
if (t->type == TYPE_AIR) continue;
int dir = t->direction;
bool horz = (dir == ORIENT_LEFT || dir == ORIENT_RIGHT); for (uint8_t lane = 0; lane < ItemSlotCount; lane++) {
bool vert = (dir == ORIENT_UP || dir == ORIENT_DOWN); if (!tt.outputLane[lane]) continue;
ItemOnBelt *itm = &t->items[lane];
if (itm->type == 0) continue;
if (tt.itemMoves) {
itm->offset += speed;
// 1) Advance
}
for (uint8_t lane = 0; lane < ItemSlotCount; lane++) { // 2) Time to hop?
if (!tt.outputLane[lane]) continue; if (itm->offset >= 0.5f || !tt.itemMoves) {
ItemOnBelt *itm = &t->items[lane];
if (itm->type == 0) continue;
if (tt.itemMoves) { if (tt.itemMoves) {
itm->offset += speed; itm->offset -= 1.0f;
// 1) Advance
} }
// 2) Time to hop? // target coords
if (itm->offset >= 0.5f || !tt.itemMoves) { int nx = x + dirDx[dir];
int ny = y + dirDy[dir];
// bounds & belt?
if (nx < 0 || nx >= MAP_WIDTH || ny < 0 || ny >= MAP_HEIGHT) {
if (tt.itemMoves) { if (tt.itemMoves) {
itm->offset -= 1.0f; itm->offset += 1.0f - speed;
} }
continue;
// target coords
int nx = x + dirDx[dir];
int ny = y + dirDy[dir];
// bounds & belt?
if (nx < 0 || nx >= MAP_WIDTH || ny < 0 || ny >= MAP_HEIGHT) {
if (tt.itemMoves) {
itm->offset += 1.0f - speed;
}
continue;
}
Tile *next = &tileMap[ny][nx];
TileTypeReg ntt = TileRegistry[next->type];
int newLane = lane;
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;
}
// (diagonals fall back to same-lane)
// Find a free slot in
break;
default:
itm->offset += 1.0f - speed;
}
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
itm->type = 0;
} else {
// both slots full → wait at end
itm->offset = epsilon;
}
} else {
} }
Tile *next = &tileMap[ny][nx];
TileTypeReg ntt = TileRegistry[next->type];
int newLane = lane;
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;
}
// (diagonals fall back to same-lane)
// Find a free slot in
break;
default:
itm->offset += 1.0f - speed;
}
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
itm->type = 0;
} else {
// both slots full → wait at end
itm->offset = epsilon;
}
} else {
} }
const UpdateTileCallback cb = ItemTileCallbacks[t->type]; }
if (cb) { const UpdateTileCallback cb = ItemTileCallbacks[t->type];
cb(t); if (cb) {
} cb(t);
} }
} }
} }
@@ -129,8 +129,10 @@ 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].atlasRects[ORIENT_LEFT] = allocate_32x32(
ItemRegistry[itemRegistryIndex].atlasRectsOnBelt[ORIENT_LEFT] = allocate_16x16(ItemRegistry[itemRegistryIndex].textureOnBelt[ORIENT_LEFT], renderer); 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].isTile = false;
ItemRegistry[itemRegistryIndex].miscVal = 60; ItemRegistry[itemRegistryIndex].miscVal = 60;
@@ -294,7 +296,9 @@ void renderItem(ItemOnBelt item, SDL_Renderer *renderer, int lane, SDL_Rect play
adjustRect(&rectA, playerRect); adjustRect(&rectA, playerRect);
SDL_RenderFillRect(renderer, &rectA); SDL_RenderFillRect(renderer, &rectA);
} }
SDL_RenderCopy(renderer, ItemRegistry[item.type].textureOnBelt[ORIENT_LEFT], NULL, &rect); //SDL_RenderCopyx(renderer, ItemRegistry[item.type].textureOnBelt[ORIENT_LEFT], NULL, &rect);
SDL_RenderCopy(renderer, atlasTexture, &ItemRegistry[item.type].atlasRectsOnBelt[ORIENT_LEFT], &rect);
if (debugMode) { if (debugMode) {
renderText(renderer, fonts[3], tempStr, rectA.x, rectA.y); renderText(renderer, fonts[3], tempStr, rectA.x, rectA.y);
} }

84
main.c
View File

@@ -14,7 +14,8 @@ 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]; BackgroundTile backgroundTileMap[MAP_HEIGHT][MAP_WIDTH];
SynthVoice voices[NUM_SYNTH_VOICES]; AudioData audioData;
TileArray neededUpdates;
} GameState; } GameState;
GameState gameState; GameState gameState;
@@ -34,7 +35,10 @@ int loadGameState(char *filename, Player *plr) {
memcpy(plr, &gameState.player, sizeof(gameState.player)); memcpy(plr, &gameState.player, sizeof(gameState.player));
memcpy(tileMap, gameState.tileMap, sizeof(tileMap)); memcpy(tileMap, gameState.tileMap, sizeof(tileMap));
memcpy(backgroundMap, gameState.backgroundTileMap, sizeof(backgroundMap)); memcpy(backgroundMap, gameState.backgroundTileMap, sizeof(backgroundMap));
memcpy(audioData.synthVoices, gameState.voices, sizeof(gameState.voices)); SDL_Rect *tmp = audioData.playerRect;
memcpy(&audioData, &gameState.audioData, sizeof(gameState.audioData));
audioData.playerRect = tmp;
memcpy(&neededUpdates, &gameState.neededUpdates, sizeof(gameState.neededUpdates));
plr->cursor.targetTile = NULL; plr->cursor.targetTile = NULL;
plr->cursor.prevTargetTile = NULL; plr->cursor.prevTargetTile = NULL;
return 0; return 0;
@@ -46,7 +50,8 @@ 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.backgroundTileMap, backgroundMap, sizeof(gameState.backgroundTileMap));
memcpy(gameState.voices, audioData.synthVoices, sizeof(gameState.voices)); memcpy(&gameState.audioData, &audioData, sizeof(gameState.audioData));
memcpy(&gameState.neededUpdates, &neededUpdates, sizeof(neededUpdates));
FILE *gameSave = fopen(filename, "wb"); FILE *gameSave = fopen(filename, "wb");
if (!gameSave) { if (!gameSave) {
@@ -98,9 +103,11 @@ int init() {
srand(time(NULL)); srand(time(NULL));
memset(tileMap, 0, sizeof(tileMap)); memset(tileMap, 0, sizeof(tileMap));
memset(&neededUpdates, 0, sizeof(neededUpdates));
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");
SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1");
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, "1"); SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, "1");
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
@@ -161,13 +168,16 @@ int init() {
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, 1); load_midi_file("assets/audio/testaid.mid");
SDL_PauseAudioDevice(dev, 0);
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255); SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
SDL_RenderClear(mainRenderer); SDL_RenderClear(mainRenderer);
@@ -203,19 +213,23 @@ int render() {
SDL_Rect rect2; SDL_Rect rect2;
rect2.x = 0; rect2.x = 0;
rect2.y = 0; rect2.y = 0;
rect2.w = 0; rect2.w = ATLAS_SIZE;
rect2.h = 0; rect2.h = ATLAS_SIZE;
renderAllTiles(mainRenderer, player.rect); renderAllTiles(mainRenderer, player.rect);
renderPlayer(&player); renderPlayer(&player);
// SDL_RenderCopy(mainRenderer, backgroundTexture, &screenRect, &screenRect);
// SDL_RenderCopy(mainRenderer, tilesTexture, &screenRect, &screenRect); if (!renderAtlas) {
// SDL_RenderCopy(mainRenderer, itemsTexture, &screenRect, &screenRect); SDL_RenderCopy(mainRenderer, backgroundTexture, &screenRect, &screenRect);
// SDL_RenderCopy(mainRenderer, entityTexture, &screenRect, &screenRect); SDL_RenderCopy(mainRenderer, tilesTexture, &screenRect, &screenRect);
// SDL_RenderCopy(mainRenderer, hudTexture, &screenRect, &screenRect); SDL_RenderCopy(mainRenderer, itemsTexture, &screenRect, &screenRect);
SDL_QueryTexture(atlasTexture, NULL, NULL, &rect2.w, &rect2.h); SDL_RenderCopy(mainRenderer, entityTexture, &screenRect, &screenRect);
SDL_RenderCopy(mainRenderer, atlasTexture, &rect2, &rect2); SDL_RenderCopy(mainRenderer, hudTexture, &screenRect, &screenRect);
} else {
SDL_RenderCopy(mainRenderer, atlasTexture, &rect2, &rect2);
}
SDL_RenderPresent(mainRenderer); SDL_RenderPresent(mainRenderer);
@@ -246,11 +260,12 @@ 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_AIR) { if (player.inventory.activeSlotIndex == 0 && 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) { if (player.inventory.activeSlotIndex == 0 && player.cursor.canReach) {
player.cursor.targetTile->direction = player.cursor.direction; player.cursor.targetTile->direction = player.cursor.direction;
} }
break; break;
@@ -260,6 +275,9 @@ int processEvent(SDL_Event e) {
case SDLK_F3: case SDLK_F3:
debugMode = !debugMode; debugMode = !debugMode;
break; break;
case SDLK_F10:
renderAtlas = !renderAtlas;
break;
case SDLK_F4: case SDLK_F4:
Tile *tile = &tileMap[playerTileY][playerTileX]; Tile *tile = &tileMap[playerTileY][playerTileX];
break; break;
@@ -306,6 +324,12 @@ void processMousePosition() {
if (player.inventory.slotCounts[player.inventory.activeSlotIndex] > 0) { if (player.inventory.slotCounts[player.inventory.activeSlotIndex] > 0) {
player.inventory.slotCounts[player.inventory.activeSlotIndex]--; player.inventory.slotCounts[player.inventory.activeSlotIndex]--;
player.cursor.targetTile->type = player.inventory.activeSlotIndex; player.cursor.targetTile->type = player.inventory.activeSlotIndex;
player.cursor.targetTile->rect.x = player.cursor.tileX;
player.cursor.targetTile->rect.y = player.cursor.tileY;
if (TileRegistry[player.inventory.activeSlotIndex].needsTicks) {
player.cursor.targetTile->neededUpdateIndex = add_tile(&neededUpdates,
player.cursor.targetTile->rect);
}
player.cursor.targetTile->direction = player.cursor.direction; player.cursor.targetTile->direction = player.cursor.direction;
} }
} else if (player.cursor.targetTile->type == player.inventory.activeSlotIndex) { } else if (player.cursor.targetTile->type == player.inventory.activeSlotIndex) {
@@ -329,6 +353,12 @@ void processMousePosition() {
player.cursor.targetTile->items[lane].type = 0; player.cursor.targetTile->items[lane].type = 0;
} }
} }
int neededIndex = player.cursor.targetTile->neededUpdateIndex;
if (TileRegistry[player.cursor.targetTile->type].needsTicks &&
neededUpdates.tiles[neededIndex].x == player.cursor.targetTile->rect.x &&
neededUpdates.tiles[neededIndex].y == player.cursor.targetTile->rect.y) {
remove_tile(&neededUpdates, neededIndex);
}
player.cursor.targetTile->type = TYPE_AIR; player.cursor.targetTile->type = TYPE_AIR;
player.cursor.breakingProgress = 0; player.cursor.breakingProgress = 0;
} else { } else {
@@ -461,8 +491,10 @@ void processKeyboardHeld() {
} }
if (keyboardState[SDL_SCANCODE_Q]) { if (keyboardState[SDL_SCANCODE_Q]) {
if (player.cursor.targetTile->type > 0) { if (player.cursor.targetTile->type > 0 && player.inventory.activeSlotIndex == 0) {
setActivePlayerSlot(&player, player.cursor.targetTile->type); setActivePlayerSlot(&player, player.cursor.targetTile->type);
} else {
setActivePlayerSlot(&player, 0);
} }
} }
if (keyboardState[SDL_SCANCODE_Y]) { if (keyboardState[SDL_SCANCODE_Y]) {
@@ -519,6 +551,14 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[])
genInitMap(); genInitMap();
} }
// audioData.synthVoices[0].frequency = 1000;
// audioData.synthVoices[0].phase = 0;
// audioData.synthVoices[0].sourceRect.w = TILE_SIZE;
// audioData.synthVoices[0].sourceRect.h = TILE_SIZE;
// audioData.synthVoices[0].sourceRect.x = 100 * TILE_SIZE;
// audioData.synthVoices[0].sourceRect.y = 100 * TILE_SIZE;
// audioData.synthVoices[0].volume = 255;
// audioData.synthVoices[0].waveform = WAVE_SINE;
//Hack to get window to stay up //Hack to get window to stay up
SDL_Event e; SDL_Event e;

View File

@@ -6,6 +6,7 @@
#include "player.h" #include "player.h"
#include "../tiles/tile.h" #include "../tiles/tile.h"
#include "../util/font.h" #include "../util/font.h"
#include "../util/atlas.h"
#define HEALTH_MARGIN 4 #define HEALTH_MARGIN 4
@@ -18,6 +19,7 @@ SDL_Texture *hudTexture;
SDL_Texture *PlayerTexture; SDL_Texture *PlayerTexture;
SDL_Rect PlayerRect; SDL_Rect PlayerRect;
SDL_Rect playerTextureRect;
SDL_Rect targetItemBGRect; SDL_Rect targetItemBGRect;
@@ -28,9 +30,6 @@ SDL_Color breakingBarColor = {128, 128, 0, 255};
void setActivePlayerSlot(Player *plr, ItemType activeSlotIndex) { void setActivePlayerSlot(Player *plr, ItemType activeSlotIndex) {
activeSlotIndex = activeSlotIndex % itemRegistryIndex; activeSlotIndex = activeSlotIndex % itemRegistryIndex;
if (activeSlotIndex <= 0) {
activeSlotIndex = 1;
}
plr->inventory.activeSlotIndex = activeSlotIndex; plr->inventory.activeSlotIndex = activeSlotIndex;
} }
@@ -46,11 +45,11 @@ void moveActivePlayerSlot(Player *plr, bool up, bool seek) {
// Stop if we found a slot with count > 0 // Stop if we found a slot with count > 0
if (!strlen(ItemRegistry[newSlot].name)) continue; if (!strlen(ItemRegistry[newSlot].name)) continue;
if (newSlot == 0) continue; if (newSlot == 0) break;
if (seek) { if (seek) {
if (plr->inventory.slotCounts[newSlot] > 0) break; if (plr->inventory.slotCounts[newSlot] > 0) break;
} else { } else {
break; break;
} }
} while (true); } while (true);
@@ -104,6 +103,7 @@ void initPlayer(Player *plr) {
SDL_QueryTexture(PlayerTexture, NULL, NULL, &PlayerRect.w, &PlayerRect.h); SDL_QueryTexture(PlayerTexture, NULL, NULL, &PlayerRect.w, &PlayerRect.h);
PlayerRect.x = (DISPLAY_WIDTH / 2) - (PlayerRect.w / 2); PlayerRect.x = (DISPLAY_WIDTH / 2) - (PlayerRect.w / 2);
PlayerRect.y = (DISPLAY_HEIGHT / 2) - (PlayerRect.h / 2); PlayerRect.y = (DISPLAY_HEIGHT / 2) - (PlayerRect.h / 2);
playerTextureRect = allocate_32x32(PlayerTexture, mainRenderer);
plr->health = 64; plr->health = 64;
plr->healthIdle = 0; plr->healthIdle = 0;
@@ -112,12 +112,12 @@ 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 (ItemType ui = 0; ui < tileTypeIndex; ui++) { for (ItemType ui = 1; ui < tileTypeIndex; ui++) {
plr->inventory.slotCounts[ui] = 64; plr->inventory.slotCounts[ui] = 65535;
} }
for (ItemType ui = ITEMREGISTRY_SIZE / 2; ui < itemRegistryIndex; ui++) { for (ItemType ui = ITEMREGISTRY_SIZE / 2; ui < itemRegistryIndex; ui++) {
plr->inventory.slotCounts[ui] = 64; plr->inventory.slotCounts[ui] = 65535;
} }
hudTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, screenRect.w, hudTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, screenRect.w,
@@ -135,12 +135,12 @@ void initPlayer(Player *plr) {
plr->cursor.targetTileRect.h = TILE_SIZE; plr->cursor.targetTileRect.h = TILE_SIZE;
targetItemBGRect.w = DISPLAY_WIDTH; targetItemBGRect.w = DISPLAY_WIDTH;
targetItemBGRect.h = TILE_SIZE; targetItemBGRect.h = TILE_SIZE + fonts[2].size * 2;
targetItemBGRect.x = 0; targetItemBGRect.x = 0;
targetItemBGRect.y = DISPLAY_HEIGHT - 32; targetItemBGRect.y = DISPLAY_HEIGHT - TILE_SIZE - fonts[2].size * 2;
targetItemRect.w = TILE_SIZE / 2; targetItemRect.w = TILE_SIZE;
targetItemRect.h = TILE_SIZE / 2; targetItemRect.h = TILE_SIZE;
plr->cursor.heldItemRect.w = TILE_SIZE; plr->cursor.heldItemRect.w = TILE_SIZE;
plr->cursor.heldItemRect.h = TILE_SIZE; plr->cursor.heldItemRect.h = TILE_SIZE;
@@ -164,7 +164,8 @@ void renderPlayer(Player *plr) {
SDL_SetRenderTarget(mainRenderer, entityTexture); SDL_SetRenderTarget(mainRenderer, entityTexture);
SDL_RenderClear(mainRenderer); SDL_RenderClear(mainRenderer);
SDL_RenderCopy(mainRenderer, PlayerTexture, NULL, &PlayerRect); SDL_RenderCopy(mainRenderer, atlasTexture, &playerTextureRect, &PlayerRect);
//SDL_RenderCopy(mainRenderer, PlayerTexture, NULL, &PlayerRect);
SDL_SetRenderTarget(mainRenderer, hudTexture); SDL_SetRenderTarget(mainRenderer, hudTexture);
SDL_RenderClear(mainRenderer); SDL_RenderClear(mainRenderer);
@@ -172,29 +173,31 @@ void renderPlayer(Player *plr) {
DrawThickRect(mainRenderer, plr->cursor.targetTileRect, 4); DrawThickRect(mainRenderer, plr->cursor.targetTileRect, 4);
ItemType 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 && itemIndex > 0) {
plr->cursor.heldItemRect.x = plr->cursor.windowX; plr->cursor.heldItemRect.x = plr->cursor.windowX;
plr->cursor.heldItemRect.y = plr->cursor.windowY; plr->cursor.heldItemRect.y = plr->cursor.windowY;
itemTex = ItemRegistry[itemIndex].textureOnBelt[plr->cursor.direction]; //itemTex = ItemRegistry[itemIndex].textureOnBelt[plr->cursor.direction];
if (itemTex == NULL) { SDL_Rect itemAtlasRect = ItemRegistry[itemIndex].atlasRects[plr->cursor.direction];
itemTex = ItemRegistry[itemIndex].textureOnBelt[ORIENT_LEFT]; if (itemAtlasRect.w == 0 || itemAtlasRect.h == 0) {
itemAtlasRect = ItemRegistry[itemIndex].atlasRects[ORIENT_LEFT];
} }
if (itemTex != NULL) { if (itemAtlasRect.w != 0 && itemAtlasRect.h != 0) {
if (plr->inventory.slotCounts[itemIndex] <= 0) { // if (plr->inventory.slotCounts[itemIndex] <= 0) {
// Set a red tint (255, 0, 0) // // Set a red tint (255, 0, 0)
SDL_SetTextureColorMod(itemTex, 128, 128, 255); // SDL_SetTextureColorMod(itemTex, 128, 128, 255);
//
SDL_SetTextureAlphaMod(itemTex, 192); // SDL_SetTextureAlphaMod(itemTex, 192);
} else { // } else {
SDL_SetTextureColorMod(itemTex, 255, 255, 255); // SDL_SetTextureColorMod(itemTex, 255, 255, 255);
//
SDL_SetTextureAlphaMod(itemTex, 255); // SDL_SetTextureAlphaMod(itemTex, 255);
} // }
SDL_RenderCopy(mainRenderer, itemTex, NULL, // SDL_RenderCopy(mainRenderer, itemTex, NULL,
&plr->cursor.heldItemRect); // &plr->cursor.heldItemRect);
SDL_RenderCopy(mainRenderer, atlasTexture, &itemAtlasRect, &plr->cursor.heldItemRect);
renderText(mainRenderer, fonts[2], ItemRegistry[itemIndex].name, plr->cursor.heldItemRect.x, renderText(mainRenderer, fonts[2], ItemRegistry[itemIndex].name, plr->cursor.heldItemRect.x,
plr->cursor.heldItemRect.y - (fonts[2].size * 3 / 2)); plr->cursor.heldItemRect.y - (fonts[2].size * 3 / 2));
} }
@@ -215,21 +218,22 @@ void renderPlayer(Player *plr) {
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255); SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
SDL_RenderFillRect(mainRenderer, &targetItemBGRect); SDL_RenderFillRect(mainRenderer, &targetItemBGRect);
targetItemRect.y = DISPLAY_HEIGHT - 30 + TILE_SIZE / 4 + 3; targetItemRect.y = DISPLAY_HEIGHT - TILE_SIZE;
targetItemRect.x = TILE_SIZE / 4; targetItemRect.x = TILE_SIZE / 4;
for (ItemType i = 1; i < ITEMREGISTRY_SIZE; i++) { SDL_SetTextureBlendMode(atlasTexture, SDL_BLENDMODE_ADD);
itemTex = ItemRegistry[i].textureOnBelt[plr->cursor.direction]; for (ItemType i = 0; i < ITEMREGISTRY_SIZE; i++) {
if (itemTex == NULL) { SDL_Rect itemAtlasRectd = ItemRegistry[i].atlasRects[plr->cursor.direction];
itemTex = ItemRegistry[i].textureOnBelt[ORIENT_LEFT]; if (itemAtlasRectd.w == 0 || itemAtlasRectd.h == 0) {
itemAtlasRectd = ItemRegistry[i].atlasRects[ORIENT_LEFT];
} }
if (itemTex != NULL) { if (itemAtlasRectd.w != 0 && itemAtlasRectd.h != 0) {
if (plr->inventory.slotCounts[i] <= 0) { // if (plr->inventory.slotCounts[i] <= 0) {
// Set a red tint (255, 0, 0) // // Set a red tint (255, 0, 0)
SDL_SetTextureColorMod(itemTex, 128, 128, 255); // SDL_SetTextureColorMod(itemTex, 128, 128, 255);
} // }
SDL_RenderCopy(mainRenderer, itemTex, NULL, &targetItemRect); SDL_RenderCopy(mainRenderer, atlasTexture, &itemAtlasRectd, &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,
@@ -240,9 +244,10 @@ void renderPlayer(Player *plr) {
renderText(mainRenderer, fonts[2], itemStringCount, targetItemRect.x - 3, renderText(mainRenderer, fonts[2], itemStringCount, targetItemRect.x - 3,
targetItemRect.y - (fonts[2].size * 3 / 2)); targetItemRect.y - (fonts[2].size * 3 / 2));
//targetItemRect.x += (TILE_SIZE / 2) + (TILE_SIZE / 4); //targetItemRect.x += (TILE_SIZE / 2) + (TILE_SIZE / 4);
targetItemRect.x += TILE_SIZE; targetItemRect.x += TILE_SIZE / 2 * 3;
} }
} }
SDL_SetTextureBlendMode(atlasTexture, SDL_BLENDMODE_BLEND);
SDL_SetRenderTarget(mainRenderer, originalTarget); SDL_SetRenderTarget(mainRenderer, originalTarget);
} }

View File

@@ -8,6 +8,7 @@
#include "tile.h" #include "tile.h"
#include "../player/player.h" #include "../player/player.h"
#include "../items/item.h" #include "../items/item.h"
#include "../util/atlas.h"
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) {
@@ -36,8 +37,10 @@ void renderBelt(int x, int y, int w, int h, OrientDirection dir, SDL_Rect player
adjustRect(&dst1, playerRect); adjustRect(&dst1, playerRect);
adjustRect(&dst2, playerRect); adjustRect(&dst2, playerRect);
SDL_RenderCopy(renderer, TileRegistry[tileType].textures[dir], &src1, &dst1); SDL_RenderCopy(renderer, TileRegistry[tileType].textures[dir], &src1, &dst1); //TODO CONVERT TO ATLAS
SDL_RenderCopy(renderer, TileRegistry[tileType].textures[dir], &src2, &dst2); SDL_RenderCopy(renderer, TileRegistry[tileType].textures[dir], &src2, &dst2); //TODO CONVERT TO ATLAS
// SDL_RenderCopyx(renderer, atlasTexture, &TileRegistry[tileType].atlasRects[dir], NULL);
// SDL_RenderCopyx(renderer, atlasTexture, &TileRegistry[tileType].atlasRects[dir], NULL);
} else { } else {
int offset = scrollFrame % TILE_SIZE; int offset = scrollFrame % TILE_SIZE;
@@ -56,6 +59,9 @@ void renderBelt(int x, int y, int w, int h, OrientDirection dir, SDL_Rect player
// Rotate to make the belt vertical // Rotate to make the belt vertical
// SDL_RenderCopyx(renderer, atlasTexture, &ItemRegistry[item.type].atlasRectsOnBelt[ORIENT_LEFT], NULL);
// SDL_RenderCopyx(renderer, atlasTexture, &ItemRegistry[item.type].atlasRectsOnBelt[ORIENT_LEFT], NULL);
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);
} }

View File

@@ -27,16 +27,19 @@ void updateFurnace(Tile *tile) {
if (tile->miscVal == 0) { if (tile->miscVal == 0) {
tile->audioCh = getAvailableChannel(); tile->audioCh = getAvailableChannel();
if (tile->audioCh < NUM_SYNTH_VOICES) { if (tile->audioCh < NUM_SYNTH_VOICES) {
audioData.synthVoices[tile->audioCh].volume = 1; audioData.synthVoices[tile->audioCh].volume = 255;
audioData.synthVoices[tile->audioCh].phase = 0; audioData.synthVoices[tile->audioCh].phase = 0;
audioData.synthVoices[tile->audioCh].sourceRect.x = TILE_SIZE * tile->x; audioData.synthVoices[tile->audioCh].sourceRect.x = TILE_SIZE * tile->rect.x;
audioData.synthVoices[tile->audioCh].sourceRect.y = TILE_SIZE * tile->y; audioData.synthVoices[tile->audioCh].sourceRect.y = TILE_SIZE * tile->rect.y;
audioData.synthVoices[tile->audioCh].waveform = WAVE_TRIANGLE; audioData.synthVoices[tile->audioCh].waveform = WAVE_SINE;
audioData.synthVoices[tile->audioCh].frequency = 99; audioData.synthVoices[tile->audioCh].frequency = 200;
} }
} }
if (tile->audioCh < NUM_SYNTH_VOICES) { if (tile->audioCh < NUM_SYNTH_VOICES) {
audioData.synthVoices[tile->audioCh].frequency++; printf("frq: %d\n", ++audioData.synthVoices[tile->audioCh].frequency);
}
if (audioData.synthVoices[tile->audioCh].volume < 255) {
audioData.synthVoices[tile->audioCh].volume++;
} }
if (outItem->type == 0 && ++tile->miscVal >= targetOutItem.miscVal) { if (outItem->type == 0 && ++tile->miscVal >= targetOutItem.miscVal) {
if (tile->audioCh < NUM_SYNTH_VOICES) { if (tile->audioCh < NUM_SYNTH_VOICES) {

View File

@@ -2,15 +2,31 @@
// Created by bruno on 4/24/25. // Created by bruno on 4/24/25.
// //
#include <dirent.h>
#include "tile.h" #include "tile.h"
#include "../player/player.h" #include "../player/player.h"
#include "furnace.h" #include "furnace.h"
#include "../util/atlas.h" #include "../util/atlas.h"
#include "../util/font.h"
int scrollFrame = 0; int scrollFrame = 0;
unsigned long beltFrames = 0; unsigned long beltFrames = 0;
TileArray neededUpdates;
int add_tile(TileArray *arr, MiniRect t) {
if (arr->activeCount >= MAX_TILES) return 0;
arr->tiles[arr->activeCount] = t;
arr->activeCount++;
return arr->activeCount - 1;
}
void remove_tile(TileArray *arr, int index) {
if (index < 0 || index >= arr->activeCount) return;
arr->activeCount--;
arr->tiles[index] = arr->tiles[arr->activeCount]; // swap with last active
}
SDL_Texture *backgroundTexture; SDL_Texture *backgroundTexture;
SDL_Texture *tilesTexture; SDL_Texture *tilesTexture;
SDL_Texture *itemsTexture; SDL_Texture *itemsTexture;
@@ -30,8 +46,8 @@ void generateTestMap() {
for (int y = 0; y < DISPLAY_MAP_HEIGHT; y++) { for (int y = 0; y < DISPLAY_MAP_HEIGHT; y++) {
for (int x = 0; x < DISPLAY_MAP_WIDTH; x++) { for (int x = 0; x < DISPLAY_MAP_WIDTH; x++) {
Tile tile = {0}; Tile tile = {0};
tile.x = x; tile.rect.x = x;
tile.y = y; tile.rect.y = y;
tileMap[y][x] = tile; tileMap[y][x] = tile;
} }
} }
@@ -66,21 +82,19 @@ 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_LEFT], SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(TileRegistry[tileTypeIndex].textures[ORIENT_RIGHT], 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_UP], SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(TileRegistry[tileTypeIndex].textures[ORIENT_DOWN], 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_LEFT] = allocate_32x32(
TileRegistry[tileTypeIndex].atlasRects[ORIENT_RIGHT] = allocate_32x32(TileRegistry[tileTypeIndex].textures[ORIENT_RIGHT], renderer); TileRegistry[tileTypeIndex].textures[ORIENT_LEFT], renderer);
TileRegistry[tileTypeIndex].atlasRects[ORIENT_UP] = allocate_32x32(TileRegistry[tileTypeIndex].textures[ORIENT_UP], renderer); TileRegistry[tileTypeIndex].atlasRects[ORIENT_RIGHT] = allocate_32x32(
TileRegistry[tileTypeIndex].atlasRects[ORIENT_DOWN] = allocate_32x32(TileRegistry[tileTypeIndex].textures[ORIENT_DOWN], renderer); 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;
@@ -96,7 +110,8 @@ void registerBackgroundTile(char name[20], SDL_Renderer *renderer) {
SDL_Texture *texture = IMG_LoadTexture(renderer, texturePath); SDL_Texture *texture = IMG_LoadTexture(renderer, texturePath);
BackgroundTileRegistry[backgroundTileTypeIndex].texture = texture; BackgroundTileRegistry[backgroundTileTypeIndex].texture = texture;
SDL_SetTextureBlendMode(BackgroundTileRegistry[backgroundTileTypeIndex].texture, SDL_BLENDMODE_NONE); SDL_SetTextureBlendMode(BackgroundTileRegistry[backgroundTileTypeIndex].texture, SDL_BLENDMODE_NONE);
BackgroundTileRegistry[backgroundTileTypeIndex].atlasRect = allocate_32x32(BackgroundTileRegistry[backgroundTileTypeIndex].texture, renderer); BackgroundTileRegistry[backgroundTileTypeIndex].atlasRect = allocate_32x32(
BackgroundTileRegistry[backgroundTileTypeIndex].texture, renderer);
BackgroundTileRegistry[backgroundTileTypeIndex].type = backgroundTileTypeIndex; BackgroundTileRegistry[backgroundTileTypeIndex].type = backgroundTileTypeIndex;
@@ -128,6 +143,8 @@ void setupTiles() {
} }
} }
TileRegistry[TYPE_FURNACE].outputLane[FURNACE_OUTPUT_SLOT] = 1; TileRegistry[TYPE_FURNACE].outputLane[FURNACE_OUTPUT_SLOT] = 1;
TileRegistry[TYPE_FURNACE].needsTicks = true;
TileRegistry[TYPE_BELT].needsTicks = true;
} }
uint16_t getBreakTime(int type) { uint16_t getBreakTime(int type) {
@@ -186,8 +203,10 @@ void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect) {
BackgroundTile bt = backgroundMap[y][x]; BackgroundTile bt = backgroundMap[y][x];
SDL_Texture *tex = BackgroundTileRegistry[bt.type].texture; SDL_Texture *tex = BackgroundTileRegistry[bt.type].texture;
if (tex != NULL) { SDL_Rect atlRect = BackgroundTileRegistry[bt.type].atlasRect;
SDL_RenderCopy(renderer, tex, NULL, &dstRect); if (atlRect.w != 0 && atlRect.h != 0) {
SDL_RenderCopy(renderer, atlasTexture, &atlRect, &dstRect);
//SDL_RenderCopy(renderer, tex, NULL, &dstRect);
} }
} }
} }
@@ -209,14 +228,21 @@ void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect) {
Tile t = tileMap[y][x]; Tile t = tileMap[y][x];
switch (t.type) { switch (t.type) {
case TYPE_AIR:
break;
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 atlRect = TileRegistry[t.type].atlasRects[t.direction];
SDL_Texture *tex = TileRegistry[t.type].textures[t.direction]; SDL_Texture *tex = TileRegistry[t.type].textures[t.direction];
if (tex == NULL) tex = TileRegistry[t.type].textures[ORIENT_LEFT]; if (atlRect.w == 0 || atlRect.h == 0) {
if (tex != NULL) { tex = TileRegistry[t.type].textures[ORIENT_LEFT];
SDL_RenderCopy(renderer, tex, NULL, &dstRect); atlRect = TileRegistry[t.type].atlasRects[ORIENT_LEFT];
}
if (atlRect.w != 0 && atlRect.h != 0) {
//SDL_RenderCopy(renderer, tex, NULL, &dstRect);
SDL_RenderCopy(renderer, atlasTexture, &atlRect, &dstRect);
} }
} }
} }
@@ -231,6 +257,18 @@ void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect) {
if (x < 0 || x >= MAP_WIDTH) continue; if (x < 0 || x >= MAP_WIDTH) continue;
Tile t = tileMap[y][x]; Tile t = tileMap[y][x];
if (debugMode) {
char locChar[20];
sprintf(locChar, "X:%d\nY:%d", x, y);
SDL_Rect dstRect = {
.x = x * TILE_SIZE,
.y = y * TILE_SIZE,
.w = TILE_SIZE,
.h = TILE_SIZE
};
adjustRect(&dstRect, playerRect);
renderText(renderer, fonts[3], locChar, dstRect.x, dstRect.y);
}
if (t.type == TYPE_BELT || itemViewing) { if (t.type == TYPE_BELT || itemViewing) {
for (uint8_t lane = 0; lane < ItemSlotCount; lane++) { for (uint8_t lane = 0; lane < ItemSlotCount; lane++) {
if (t.items[lane].type != 0) { if (t.items[lane].type != 0) {

View File

@@ -7,10 +7,9 @@
#include "../util/util.h" #include "../util/util.h"
#include "../items/item.h" #include "../items/item.h"
//#include "../items/item.h"
#define MAP_WIDTH 100 #define MAP_WIDTH 1000
#define MAP_HEIGHT 100 #define MAP_HEIGHT 1000
#define DISPLAY_MAP_WIDTH 60 #define DISPLAY_MAP_WIDTH 60
#define DISPLAY_MAP_HEIGHT 31 #define DISPLAY_MAP_HEIGHT 31
@@ -20,6 +19,25 @@
#define DISPLAY_WIDTH DISPLAY_MAP_WIDTH * TILE_SIZE #define DISPLAY_WIDTH DISPLAY_MAP_WIDTH * TILE_SIZE
#define DISPLAY_HEIGHT DISPLAY_MAP_HEIGHT * TILE_SIZE #define DISPLAY_HEIGHT DISPLAY_MAP_HEIGHT * TILE_SIZE
typedef struct MiniRect {
int x;
int y;
} MiniRect;
#define MAX_TILES MAP_WIDTH * MAP_HEIGHT
typedef struct {
MiniRect tiles[MAX_TILES];
int activeCount;
} TileArray;
extern TileArray neededUpdates;
void remove_tile(TileArray *arr, int index);
int add_tile(TileArray *arr, MiniRect t);
extern SDL_Texture *tilesTexture; extern SDL_Texture *tilesTexture;
extern SDL_Texture *itemsTexture; extern SDL_Texture *itemsTexture;
extern SDL_Texture *backgroundTexture; extern SDL_Texture *backgroundTexture;
@@ -77,6 +95,7 @@ typedef struct TileTypeReg {
bool itemMoves; bool itemMoves;
bool allowedInItems[ItemSlotCount][ITEMREGISTRY_SIZE]; bool allowedInItems[ItemSlotCount][ITEMREGISTRY_SIZE];
bool outputLane[ItemSlotCount]; bool outputLane[ItemSlotCount];
bool needsTicks;
} TileTypeReg; } TileTypeReg;
typedef struct BackgroundTileType { typedef struct BackgroundTileType {
@@ -106,8 +125,8 @@ typedef struct Tile {
int miscVal; int miscVal;
ItemOnBelt items[ItemSlotCount]; ItemOnBelt items[ItemSlotCount];
uint16_t audioCh; uint16_t audioCh;
int x; MiniRect rect;
int y; int neededUpdateIndex;
} Tile; } Tile;

View File

@@ -7,50 +7,97 @@
SDL_Texture *atlasTexture; SDL_Texture *atlasTexture;
int atlasX = 0, atlasY = 0; int tileIndex16 = 0, quadrantIndex16 = 0;
int tileIndex32 = 0;
int tileIndex = 0; // Which 32x32 tile we're on #define MAX_RECTS 256
int quadrantIndex = 0; // Which 16x16 slot inside that tile int allocatedRectCount = 0;
SDL_Rect allocatedRects[MAX_RECTS];
bool isIntersecting(SDL_Rect a) {
for (int i = 0; i < allocatedRectCount; i++) {
SDL_Rect b = allocatedRects[i];
if (SDL_HasIntersection(&a, &b)) {
printf("Rect intersection %d - X:%d, Y: %d, W: %d, H: %d with X:%d, Y: %d, W: %d, H: %d\n",
allocatedRectCount, a.x, a.y, a.w, a.h, b.x, b.y, b.w, b.h);
return 1;
}
}
return 0;
}
void storeRect(SDL_Rect rect) {
if (isIntersecting(rect)) {
printf("PROBLEM\n");
}
if (allocatedRectCount < MAX_RECTS) {
allocatedRects[allocatedRectCount++] = rect;
} else {
fprintf(stderr, "Error: atlas rect limit reached!\n");
}
}
SDL_Rect allocate_16x16(SDL_Texture *srcTexture, SDL_Renderer *renderer) { SDL_Rect allocate_16x16(SDL_Texture *srcTexture, SDL_Renderer *renderer) {
SDL_Texture * oldTarget = SDL_GetRenderTarget(renderer); SDL_Texture *oldTarget = SDL_GetRenderTarget(renderer);
SDL_SetRenderTarget(renderer, atlasTexture); 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 = { SDL_Rect sourceRect = {
tileX * TILE_SIZE + dx, 0,
tileY * TILE_SIZE + dy, 0,
QUADRANT_SIZE, QUADRANT_SIZE,
QUADRANT_SIZE QUADRANT_SIZE
}; };
SDL_RenderCopy(renderer, srcTexture, NULL, &destRect);
quadrantIndex++; SDL_Rect destRect;
if (quadrantIndex >= 4) { while (1) {
tileIndex++; int tileX = tileIndex16 % ATLAS_TILES_PER_ROW;
quadrantIndex = 0; int tileY = tileIndex16 / ATLAS_TILES_PER_ROW;
int dx = (quadrantIndex16 % 2) * QUADRANT_SIZE;
int dy = (quadrantIndex16 / 2) * QUADRANT_SIZE;
destRect.x = tileX * TILE_SIZE + dx;
destRect.y = tileY * TILE_SIZE + dy;
destRect.w = QUADRANT_SIZE;
destRect.h = QUADRANT_SIZE;
if (isIntersecting(destRect)) {
tileIndex16 = tileIndex32;
tileIndex32++;
quadrantIndex16 = 0;
continue;
} else {
break;
}
} }
SDL_RenderCopy(renderer, srcTexture, &sourceRect, &destRect);
quadrantIndex16++;
if (quadrantIndex16 >= 4) {
tileIndex16 = tileIndex32;
tileIndex32 += 1;
quadrantIndex16 = 0;
// Ensure 32x32 allocator skips this tile
if (tileIndex32 <= tileIndex16) {
tileIndex32 = tileIndex16 + 1;
}
}
SDL_SetRenderTarget(renderer, oldTarget); SDL_SetRenderTarget(renderer, oldTarget);
storeRect(destRect);
printf("Rect X:%d, Y: %d, W: %d, H: %d\n", destRect.x, destRect.y, destRect.w, destRect.h);
return destRect; return destRect;
} }
SDL_Rect allocate_32x32(SDL_Texture *srcTexture, SDL_Renderer *renderer) { SDL_Rect allocate_32x32(SDL_Texture *srcTexture, SDL_Renderer *renderer) {
SDL_Texture * oldTarget = SDL_GetRenderTarget(renderer); SDL_Texture *oldTarget = SDL_GetRenderTarget(renderer);
SDL_SetRenderTarget(renderer, atlasTexture); 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 tileX = tileIndex32 % ATLAS_TILES_PER_ROW;
int tileY = tileIndex / ATLAS_TILES_PER_ROW; int tileY = tileIndex32 / ATLAS_TILES_PER_ROW;
SDL_Rect destRect = { SDL_Rect destRect = {
tileX * TILE_SIZE, tileX * TILE_SIZE,
@@ -58,23 +105,30 @@ SDL_Rect allocate_32x32(SDL_Texture *srcTexture, SDL_Renderer *renderer) {
TILE_SIZE, TILE_SIZE,
TILE_SIZE TILE_SIZE
}; };
SDL_RenderCopy(renderer, srcTexture, NULL, &destRect); SDL_RenderCopy(renderer, srcTexture, NULL, &destRect);
tileIndex++; // Move to next tile tileIndex32++;
// quadrantIndex stays 0 — new tile is fresh
SDL_SetRenderTarget(renderer, oldTarget); SDL_SetRenderTarget(renderer, oldTarget);
storeRect(destRect);
printf("Rect X:%d, Y: %d, W: %d, H: %d\n", destRect.x, destRect.y, destRect.w, destRect.h);
return destRect; return destRect;
} }
void initAtlas(SDL_Renderer *renderer) { void initAtlas(SDL_Renderer *renderer) {
SDL_Texture *oldTarget = SDL_GetRenderTarget(renderer);
atlasTexture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
ATLAS_SIZE, ATLAS_SIZE);
// Clear atlas with transparent // Clear atlas with transparent
SDL_SetRenderTarget(renderer, atlasTexture); SDL_SetRenderTarget(renderer, atlasTexture);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer); SDL_RenderClear(renderer);
SDL_SetRenderTarget(renderer, oldTarget);
atlasTexture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
ATLAS_SIZE, ATLAS_SIZE);
} }

View File

@@ -6,6 +6,11 @@
AudioData audioData; AudioData audioData;
#define MAX_MIDI_EVENTS 1024
MidiEvent midiEvents[MAX_MIDI_EVENTS];
int midiEventCount = 0;
int nextMidiEvent = 0;
uint16_t getAvailableChannel() { uint16_t getAvailableChannel() {
for (uint16_t i = 0; i < NUM_SYNTH_VOICES; i++) { for (uint16_t i = 0; i < NUM_SYNTH_VOICES; i++) {
if (audioData.synthVoices[i].volume == 0) { if (audioData.synthVoices[i].volume == 0) {
@@ -39,93 +44,209 @@ static void compute_stereo_gains(float pan, float *outL, float *outR) {
// e.g. *outL *= 0.7071f; *outR *= 0.7071f; // e.g. *outL *= 0.7071f; *outR *= 0.7071f;
} }
// This callback now writes stereo frames: interleaved L/R floats. // Improved audio callback with anti-clipping and smooth fade-out
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;
// 'len' is total bytes; each sampleframe is 2 floats (L+R), i.e. 2 * sizeof(float). int frames = len / (2 * sizeof(float)); // Stereo frame count
int frames = len / (2 * sizeof(float));
float elapsedSec = audio->totalSamples / SAMPLE_RATE;
audio->totalSamples += frames;
while (nextMidiEvent < midiEventCount &&
midiEvents[nextMidiEvent].timeSec <= elapsedSec) {
MidiEvent *ev = &midiEvents[nextMidiEvent];
if (ev->type == 0 && ev->velocity > 0) {
// Note On
for (int i = NUM_SYNTH_VOICES - 4; i < NUM_SYNTH_VOICES; ++i) {
SynthVoice *v = &audio->synthVoices[i];
if (v->volume == 0) {
float freq = 440.0f * powf(2.0f, (ev->note - 69) / 12.0f);
v->frequency = (uint16_t) freq;
v->volume = ev->velocity * 2;
v->waveform = WAVE_SQUARE;
v->smoothedAmp = 0;
break;
}
}
} else {
// Note Off
for (int i = NUM_SYNTH_VOICES - 4; i < NUM_SYNTH_VOICES; ++i) {
SynthVoice *v = &audio->synthVoices[i];
float freq = 440.0f * powf(2.0f, (ev->note - 69) / 12.0f);
if ((uint16_t)freq == v->frequency) {
v->volume = 0;
}
}
}
nextMidiEvent++;
}
// 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; float *outBuf = (float *) stream;
for (int i = 0; i < 2 * frames; ++i) { for (int i = 0; i < 2 * frames; ++i) {
outBuf[i] = 0.0f; outBuf[i] = 0.0f;
} }
// Precompute the listener center
float listenerCx = audio->playerRect->x + audio->playerRect->w * 0.5f; float listenerCx = audio->playerRect->x + audio->playerRect->w * 0.5f;
// For each synth voice, mix into the stereo buffer int *voiceCounts = calloc(frames, sizeof(int));
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; // skip silent or inactive voices
}
// Compute source center X if ((voice->volume == 0 && voice->smoothedAmp < 0.001f) || voice->frequency == 0)
float sourceCx = voice->sourceRect.x + voice->sourceRect.w * 0.5f; continue;
float sourceCx = voice->sourceRect.x + TILE_SIZE * 0.5f;
float dx = sourceCx - listenerCx; float dx = sourceCx - listenerCx;
float pan = fmaxf(-1.0f, fminf(+1.0f, dx / audio->maxPanDistance));
// 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; float gainL, gainR;
compute_stereo_gains(pan, &gainL, &gainR); compute_stereo_gains(pan, &gainL, &gainR);
gainL *= 0.7071f;
gainR *= 0.7071f;
// Optional: You could also attenuate overall volume with distance float dist = fabsf(dx);
// float dist = fabsf(dx); float distanceAtten = 1.0f - fminf(dist / audio->maxPanDistance, 1.0f);
// float distanceAtten = 1.0f - fminf(dist / audio->maxPanDistance, 1.0f); float targetAmp = (voice->volume / 255.0f) * distanceAtten;
// float finalVolume = (voice->volume / 255.0f) * distanceAtten;
// But for now, well just use voice->volume for amplitude.
float amp = (voice->volume / 255.0f); double phaseInc = ((double) voice->frequency * 256.0) / (double) SAMPLE_RATE;
// 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++) { for (int i = 0; i < frames; i++) {
float t = (float) voice->phase / 255.0f * 2.0f - 1.0f; voice->smoothedAmp += (targetAmp - voice->smoothedAmp) * SMOOTHING_FACTOR;
float amp = voice->smoothedAmp;
double norm = voice->phase / 256.0;
double t = norm * 2.0 - 1.0;
float sample; float sample;
switch (voice->waveform) { switch (voice->waveform) {
default:
case WAVE_SINE:
sample = sinf(voice->phase * 2.0f * M_PI / 256.0f);
break;
case WAVE_SQUARE: case WAVE_SQUARE:
sample = (t >= 0.0f) ? 1.0f : -1.0f; sample = (t >= 0.0) ? 1.0f : -1.0f;
break; break;
case WAVE_SAWTOOTH: case WAVE_SAWTOOTH:
sample = t; sample = (float) t;
break; break;
case WAVE_TRIANGLE: case WAVE_TRIANGLE:
sample = (t < 0.0f) ? -t : t; sample = (float) ((t < 0.0) ? -t : t);
break; break;
case WAVE_NOISE: case WAVE_NOISE:
sample = ((float) rand() / RAND_MAX) * 2.0f - 1.0f; sample = ((float) rand() / (float) RAND_MAX) * 2.0f - 1.0f;
break;
default:
sample = (float) sin(norm * 2.0 * M_PI);
break; break;
} }
voice->phase += phaseInc; voice->phase += phaseInc;
if (voice->phase >= 256.0) voice->phase -= 256.0;
else if (voice->phase < 0.0) voice->phase += 256.0;
// Interleaved index: left = 2*i, right = 2*i + 1
int idxL = 2 * i; int idxL = 2 * i;
int idxR = 2 * i + 1; int idxR = 2 * i + 1;
// Accumulate into buffer
outBuf[idxL] += sample * amp * gainL; outBuf[idxL] += sample * amp * gainL;
outBuf[idxR] += sample * amp * gainR; outBuf[idxR] += sample * amp * gainR;
voiceCounts[i]++;
}
}
for (int i = 0; i < frames; ++i) {
int count = voiceCounts[i];
if (count > 0) {
outBuf[2 * i + 0] /= count;
outBuf[2 * i + 1] /= count;
}
}
free(voiceCounts);
}
static uint32_t read_be_uint32(const uint8_t *data) {
return (data[0]<<24) | (data[1]<<16) | (data[2]<<8) | data[3];
}
static uint16_t read_be_uint16(const uint8_t *data) {
return (data[0]<<8) | data[1];
}
static uint32_t read_vlq(const uint8_t **ptr) {
uint32_t value = 0;
const uint8_t *p = *ptr;
while (*p & 0x80) {
value = (value << 7) | (*p++ & 0x7F);
}
value = (value << 7) | (*p++ & 0x7F);
*ptr = p;
return value;
}
void load_midi_file(const char *path) {
FILE *f = fopen(path, "rb");
if (!f) return;
fseek(f, 0, SEEK_END);
long size = ftell(f);
rewind(f);
uint8_t *data = malloc(size);
fread(data, 1, size, f);
fclose(f);
const uint8_t *ptr = data;
if (memcmp(ptr, "MThd", 4) != 0) return;
ptr += 8; // skip header length
uint16_t format = read_be_uint16(ptr); ptr += 2;
uint16_t nTracks = read_be_uint16(ptr); ptr += 2;
uint16_t ppqn = read_be_uint16(ptr); ptr += 2;
if (format != 0 || nTracks != 1) {
printf("Only Type 0 MIDI supported\n");
free(data);
return;
}
if (memcmp(ptr, "MTrk", 4) != 0) return;
uint32_t trackLen = read_be_uint32(ptr+4);
ptr += 8;
const uint8_t *trackEnd = ptr + trackLen;
float curTime = 0.0f;
uint32_t tempo = 500000; // default: 120 BPM
uint8_t lastStatus = 0;
while (ptr < trackEnd && midiEventCount < MAX_MIDI_EVENTS) {
uint32_t delta = read_vlq(&ptr);
curTime += (delta * (tempo / 1000000.0f)) / ppqn;
uint8_t status = *ptr;
if (status < 0x80) status = lastStatus;
else ptr++;
lastStatus = status;
if (status == 0xFF) {
uint8_t metaType = *ptr++;
uint32_t len = read_vlq(&ptr);
if (metaType == 0x51 && len == 3) {
tempo = (ptr[0]<<16 | ptr[1]<<8 | ptr[2]);
}
ptr += len;
} else if ((status & 0xF0) == 0x90 || (status & 0xF0) == 0x80) {
uint8_t note = *ptr++;
uint8_t vel = *ptr++;
midiEvents[midiEventCount++] = (MidiEvent){
.timeSec = curTime,
.type = (status & 0xF0) == 0x90 ? 0 : 1,
.note = note,
.velocity = vel
};
} else {
ptr += 2; // skip unknown
} }
} }
// Note: We did not normalize by active voices here, because each voice already free(data);
// 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

@@ -9,9 +9,18 @@
#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "../tiles/tile.h"
#define SAMPLE_RATE 44100 #define SAMPLE_RATE 44100
#define NUM_SYNTH_VOICES 256 #define NUM_SYNTH_VOICES 256
#define SMOOTHING_FACTOR 0.001f
typedef struct {
float timeSec; // When to trigger this event
uint8_t type; // 0 = Note On, 1 = Note Off
uint8_t note;
uint8_t velocity;
} MidiEvent;
typedef enum Waveform { typedef enum Waveform {
WAVE_SINE, WAVE_SINE,
@@ -23,16 +32,18 @@ typedef enum Waveform {
typedef struct SynthVoice { typedef struct SynthVoice {
Waveform waveform; Waveform waveform;
uint8_t phase; double phase;
uint16_t frequency; uint16_t frequency;
uint8_t volume; uint8_t volume;
SDL_Rect sourceRect; MiniRect sourceRect;
float smoothedAmp; // a float that holds the exponentially smoothed amplitude
} SynthVoice; } SynthVoice;
typedef struct AudioData { typedef struct AudioData {
SynthVoice synthVoices[NUM_SYNTH_VOICES]; SynthVoice synthVoices[NUM_SYNTH_VOICES];
SDL_Rect *playerRect; SDL_Rect *playerRect;
float maxPanDistance; float maxPanDistance;
uint64_t totalSamples;
} AudioData; } AudioData;
extern AudioData audioData; extern AudioData audioData;
@@ -41,4 +52,6 @@ void audio_callback(void *userdata, Uint8 *stream, int len);
uint16_t getAvailableChannel(); uint16_t getAvailableChannel();
void load_midi_file(const char *path);
#endif //RISCB_AUDIO_H #endif //RISCB_AUDIO_H

View File

@@ -46,7 +46,7 @@ void renderText(SDL_Renderer *renderer, BitmapFont font, char *string, uint16_t
string++; string++;
continue; continue;
} }
SDL_RenderCopy(renderer, font.texture[*string], &charRect, &outRect); SDL_RenderCopy(renderer, font.texture[*string], &charRect, &outRect); //TODO CONSIDER FONTS IN ONE ATLAS
outRect.x += charRect.w + 1; outRect.x += charRect.w + 1;
string++; string++;
} }

View File

@@ -13,6 +13,7 @@ volatile bool running = true;
bool debugMode = false; bool debugMode = false;
bool itemViewing = false; bool itemViewing = false;
bool renderAtlas = false;
//The surface contained by the window //The surface contained by the window
SDL_Renderer *mainRenderer = NULL; SDL_Renderer *mainRenderer = NULL;

View File

@@ -30,6 +30,7 @@ typedef enum OrientDirection{
extern bool debugMode; extern bool debugMode;
extern bool itemViewing; extern bool itemViewing;
extern bool renderAtlas;
SDL_Texture *createRotatedTexture(SDL_Renderer *renderer, SDL_Texture *src, double angle); SDL_Texture *createRotatedTexture(SDL_Renderer *renderer, SDL_Texture *src, double angle);