diff options
author | Charlie Stanton <charlie@shtanton.xyz> | 2025-04-06 17:17:59 +0100 |
---|---|---|
committer | Charlie Stanton <charlie@shtanton.xyz> | 2025-04-06 17:17:59 +0100 |
commit | 75d02c350fa76ffb9f960338b893179f3661692d (patch) | |
tree | 1c701e571a8f89a465a581d3496b398672b11651 /src | |
parent | 775d80fe2ea7c541eb3f1180e02a48ae5498743e (diff) | |
parent | 672ae1cdc364d15787e0433b3578d0fb30e2231e (diff) | |
download | ldjam57-75d02c350fa76ffb9f960338b893179f3661692d.tar |
Merge branch 'main' into hover
Diffstat (limited to 'src')
-rw-r--r-- | src/all.c | 138 | ||||
-rw-r--r-- | src/index.html.in | 19 | ||||
-rw-r--r-- | src/levels.c | 38 | ||||
-rw-r--r-- | src/types.c | 25 |
4 files changed, 185 insertions, 35 deletions
@@ -91,12 +91,14 @@ static const Color colors[N_COLORS][4] = { #define BUTTON_SIZE 32 #define BUTTON_SPACING 16 -#define BUTTON_SPACER(i) (BUTTON_SPACING + (BUTTON_SIZE * (i) + (i) * BUTTON_SPACING)) +#define BUTTON_SPACER(i) (BUTTON_SPACING + BUTTON_SIZE + (BUTTON_SIZE * (i) + (i) * BUTTON_SPACING)) +// reverse order as they're anchored to bottom static const Button buttons[N_BUTTONS] = { - [BUTTON_CONTINUE] = {.x = BUTTON_SPACING, .y = BUTTON_SPACER(BUTTON_CONTINUE), .w = BUTTON_SIZE, .h = BUTTON_SIZE}, - [BUTTON_RETRY] = {.x = BUTTON_SPACING, .y = BUTTON_SPACER(BUTTON_RETRY), .w = BUTTON_SIZE, .h = BUTTON_SIZE}, [BUTTON_BACK] = {.x = BUTTON_SPACING, .y = BUTTON_SPACER(BUTTON_BACK), .w = BUTTON_SIZE, .h = BUTTON_SIZE}, + [BUTTON_RETRY] = {.x = BUTTON_SPACING, .y = BUTTON_SPACER(BUTTON_RETRY), .w = BUTTON_SIZE, .h = BUTTON_SIZE}, + [BUTTON_PLAY] = {.x = BUTTON_SPACING, .y = BUTTON_SPACER(BUTTON_PLAY), .w = BUTTON_SIZE, .h = BUTTON_SIZE}, + [BUTTON_CONTINUE] = {.x = BUTTON_SPACING, .y = BUTTON_SPACER(BUTTON_CONTINUE), .w = BUTTON_SIZE, .h = BUTTON_SIZE}, }; static int getPlaceAction(int currentColor, int cellx, int celly, float cellWidth, float cellHeight) { @@ -219,10 +221,11 @@ static DrawList *render(State *state, UI *ui, Arena *a) { .y = 0, .w = GRID_OFFSET_X, .h = ui->height, - .fill = {255, 255, 0, 255}, + .fill = {100, 100, 100, 255}, .border = {0, 0, 0, 255}, }; - + + // render buttons for (int i = 0; i < N_BUTTONS; i++) { Color colour = {0, 0, 0, 255}; switch (state->buttonStates[i]) { @@ -236,20 +239,75 @@ static DrawList *render(State *state, UI *ui, Arena *a) { colour = (Color) {255, 0, 0, 255}; break; } + int image = IMAGE_NULL; + switch (i) { + case BUTTON_CONTINUE: + image = IMAGE_CONTINUE; + break; + case BUTTON_BACK: + image = IMAGE_EXIT; + break; + case BUTTON_RETRY: + image = IMAGE_RETRY; + break; + case BUTTON_PLAY: + image = state->playing ? IMAGE_PAUSE : IMAGE_PLAY; + break; + } drawList->els[drawList->len++] = (DrawElement) { .x = buttons[i].x, - .y = buttons[i].y, + .y = ui->height - buttons[i].y, .w = buttons[i].w, .h = buttons[i].h, - .fill = colour, - .border = {0, 0, 0, 255} + .image = image, }; } + // render placeable cells + int cellCount = 0; // doesn't increment when ignoring empty cells + for (int i = 0; i < MAX_PLACEABLE_CELLS; i++) { + if (levels[state->currentLevel].placeableCells[i] == EMPTY) + continue; + + // TODO - FIXME (how to tell how many cells we have left of a colour? Maybe placedCells is a bad idea!) + int canContinue = 1; + for (int j = 0; j < MAX_PLACEABLE_CELLS; j++) { + if (levels[state->currentLevel].placeableCells[i] == state->placedCells[j]) { + canContinue = 0; + break; + } + } + if (canContinue == 0) + continue; + + for (int x = 0; x < 2; x++) { + for (int y = 0; y < 2; y++) { + drawList->els[drawList->len++] = (DrawElement) { + .x = BUTTON_SIZE / 2 + x * (BUTTON_SIZE / 2), + .y = (BUTTON_SIZE / 2 + y * (BUTTON_SIZE / 2) + BUTTON_SPACING * (cellCount % 2 + 1)) * (cellCount + 1), // TODO - padding + .w = BUTTON_SIZE / 2, + .h = BUTTON_SIZE / 2, + .fill = colors[cellCount + 1][x + 2 * y], + .border = {0, 0, 0, 0}, + }; + } + } + cellCount++; + } + return drawList; } +static void restart_level(Game* game) { + const int level = game->state.currentLevel; + xmemcpy(&game->state.grid, &levels[level].grid, sizeof(game->state.grid)); + game->state.goalx = levels[level].goalx; + game->state.goaly = levels[level].goaly; + game->state.playing = 0; +} + static void update(Game *game, uint64_t now, Arena a) { + // TODO - offset some more because of right black bar, dependent on ui size and window size? const int offset_width = game->ui.width - GRID_OFFSET_X; switch (game->input) { @@ -257,26 +315,50 @@ static void update(Game *game, uint64_t now, Arena a) { case INPUT_CLICK: x = (game->ui.mousex - GRID_OFFSET_X) * GRIDWIDTH / offset_width; y = game->ui.mousey * GRIDHEIGHT / game->ui.height; - float cellWidth = (float) (game->ui.width - GRID_OFFSET_X) / GRIDWIDTH; - float cellHeight = (float) game->ui.height / GRIDHEIGHT; - int cellx = game->ui.mousex - GRID_OFFSET_X - x * cellWidth; - int celly = game->ui.mousey - y * cellHeight; - // Keeping this around for testing purposes - // game->state.grid[x + y * GRIDWIDTH] = (game->state.grid[x + y * GRIDWIDTH] + 1) % (sizeof(colors) / sizeof(colors[0])); - game->state.grid[x + y * GRIDWIDTH] = getPlaceAction( - game->state.grid[x + y * GRIDWIDTH], - cellx, - celly, - cellWidth, - cellHeight - ); + if (game->ui.mousex >= GRID_OFFSET_X) { + float cellWidth = (float) (game->ui.width - GRID_OFFSET_X) / GRIDWIDTH; + float cellHeight = (float) game->ui.height / GRIDHEIGHT; + int cellx = game->ui.mousex - GRID_OFFSET_X - x * cellWidth; + int celly = game->ui.mousey - y * cellHeight; + // Keeping this around for testing purposes + // game->state.grid[x + y * GRIDWIDTH] = (game->state.grid[x + y * GRIDWIDTH] + 1) % (sizeof(colors) / sizeof(colors[0])); + game->state.grid[x + y * GRIDWIDTH] = getPlaceAction( + game->state.grid[x + y * GRIDWIDTH], + cellx, + celly, + cellWidth, + cellHeight + ); + } + + // TODO - some ignore list for which cells we have left to place + game->state.placedCells[0] = BLACK; + for (int i = 0; i < N_BUTTONS; i++) { if ( game->ui.mousex > buttons[i].x && game->ui.mousex < buttons[i].x + buttons[i].w && - game->ui.mousey > buttons[i].y && game->ui.mousey < buttons[i].y + buttons[i].h + game->ui.mousey > game->ui.height - buttons[i].y && + game->ui.mousey < game->ui.height - buttons[i].y + buttons[i].h ) { // TODO - CLICK THINGS //game->state.buttonStates[i] = BUTTON_STATE_PRESSED; + switch (i) { + case BUTTON_RETRY: + restart_level(game); + break; + case BUTTON_CONTINUE: + // TODO - don't allow if level has not been completed + if (game->state.currentLevel + 1 >= 2) // TODO - get size of levels array + break; + game->state.currentLevel++; + restart_level(game); + break; + case BUTTON_BACK: + break; + case BUTTON_PLAY: + game->state.playing = !game->state.playing; + break; + } } } break; @@ -288,6 +370,9 @@ static void update(Game *game, uint64_t now, Arena a) { case INPUT_PAUSE_PLAY: game->state.playing = !game->state.playing; break; + case INPUT_RESTART: + restart_level(game); + break; default: break; } @@ -328,10 +413,8 @@ int main(int argc, char **argv) { }; Game *game = new(&a, 1, Game); - xmemcpy(&game->state.grid, &levels[0].grid, sizeof(game->state.grid)); - game->state.goalx = levels[0].goalx; - game->state.goaly = levels[0].goaly; - game->state.playing = 0; + game->state.currentLevel = 0; + restart_level(game); game->ui = (UI) { .width = 640, .height = 480, @@ -409,6 +492,9 @@ int main(int argc, char **argv) { case SDLK_SPACE: game->input = INPUT_PAUSE_PLAY; break; + case SDLK_R: + game->input = INPUT_RESTART; + break; } break; case SDL_EVENT_WINDOW_RESIZED: diff --git a/src/index.html.in b/src/index.html.in index 6147475..14731f6 100644 --- a/src/index.html.in +++ b/src/index.html.in @@ -33,6 +33,7 @@ const INPUT_CLICK = 1; const INPUT_RCLICK = 2; const INPUT_PAUSE_PLAY = 3; const INPUT_MOVE = 4; +const INPUT_RESTART = 5; const WASM = #include "../build/main.wasm.b64" @@ -43,6 +44,14 @@ const MUSIC = const IMAGES = [ #include "../build/continue.png.b64" , +#include "../build/exit.png.b64" +, +#include "../build/pause.png.b64" +, +#include "../build/play.png.b64" +, +#include "../build/restart.png.b64" +, ]; async function main() { @@ -94,6 +103,9 @@ async function main() { let len = dl[0]; let ops = dl.subarray(1); + ctx.fillStyle = "#000000"; + ctx.fillRect(0, 0, width, height); + console.log("frame"); for (let i = 0; i < len; i++) { let op = ops.subarray(7*i, 7*i+7); const color = new Uint8Array(new Uint32Array(op.subarray(4, 6)).buffer); @@ -104,6 +116,7 @@ async function main() { ctx.globalAlpha = color[7] / 255; ctx.strokeRect(op[0], op[1], op[2], op[3]); if (op[6] !== 0) { + ctx.globalAlpha = 1; ctx.drawImage(images[op[6]], op[0], op[1], op[2], op[3]); } } @@ -140,14 +153,16 @@ async function main() { document.addEventListener("keydown", function (e) { if (e.key === " ") { - exports.game_update(INPUT_PAUSE_PLAY, 0, 0, now()); + exports.game_update(INPUT_PAUSE_PLAY, mousex, mousey, now()); + } else if (e.key == "r") { + exports.game_update(INPUT_RESTART, mousex, mousey, now()); } }); function animate() { // TODO: stop requesting frames when state is static requestAnimationFrame(animate); - exports.game_update(INPUT_NONE, 0, 0, now()); + exports.game_update(INPUT_NONE, mousex, mousey, now()); render(); } requestAnimationFrame(animate); diff --git a/src/levels.c b/src/levels.c index 004d0d9..7f49fb1 100644 --- a/src/levels.c +++ b/src/levels.c @@ -3,11 +3,6 @@ #include "all.c" -typedef struct { - int grid[GRIDWIDTH * GRIDHEIGHT]; - int goalx, goaly; -} Level; - #define _ EMPTY, #define B BLACK, #define O BLUE, @@ -33,6 +28,39 @@ static Level levels[] = { }, .goalx = 18, .goaly = 7, + .placeableCells = { + BLACK, + RED, + EMPTY, + YELLOW, + RED_RIGHT + }, + }, + { + .grid = { + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ B B _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ B O _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ _ B _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + }, + .goalx = 18, + .goaly = 10, + .placeableCells = { + BLACK, + RED_DOWN + }, }, }; #undef _ diff --git a/src/types.c b/src/types.c index b50228c..bee6b05 100644 --- a/src/types.c +++ b/src/types.c @@ -8,6 +8,7 @@ #define GRIDHEIGHT 16 #define TICK_LENGTH 200 #define GRID_OFFSET_X 64 +#define MAX_PLACEABLE_CELLS 16 typedef struct { unsigned char r, g, b, a; @@ -48,9 +49,20 @@ enum { }; enum { - BUTTON_CONTINUE, - BUTTON_RETRY, + IMAGE_NULL, + IMAGE_CONTINUE, + IMAGE_EXIT, + IMAGE_PAUSE, + IMAGE_PLAY, + IMAGE_RETRY, + N_IMAGES, +}; + +enum { BUTTON_BACK, + BUTTON_RETRY, + BUTTON_PLAY, + BUTTON_CONTINUE, N_BUTTONS, }; @@ -70,11 +82,19 @@ typedef struct { } Button; typedef struct { + int grid[GRIDWIDTH * GRIDHEIGHT]; + int goalx, goaly; + int placeableCells[MAX_PLACEABLE_CELLS]; // Color +} Level; + +typedef struct { uint64_t lastTick; char playing; int grid[GRIDWIDTH * GRIDHEIGHT]; int goalx, goaly; ButtonState buttonStates[N_BUTTONS]; + int currentLevel; + int placedCells[MAX_PLACEABLE_CELLS]; // indices into Level placeableCells } State; // Mirror these in src/index.html.in @@ -84,6 +104,7 @@ enum { INPUT_RCLICK, INPUT_PAUSE_PLAY, INPUT_MOVE, + INPUT_RESTART, }; typedef struct { |