Back to shtanton's homepage
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2025-04-06 17:17:59 +0100
committerCharlie Stanton <charlie@shtanton.xyz>2025-04-06 17:17:59 +0100
commit75d02c350fa76ffb9f960338b893179f3661692d (patch)
tree1c701e571a8f89a465a581d3496b398672b11651
parent775d80fe2ea7c541eb3f1180e02a48ae5498743e (diff)
parent672ae1cdc364d15787e0433b3578d0fb30e2231e (diff)
downloadldjam57-75d02c350fa76ffb9f960338b893179f3661692d.tar
Merge branch 'main' into hover
-rw-r--r--Makefile3
-rw-r--r--src/all.c138
-rw-r--r--src/index.html.in19
-rw-r--r--src/levels.c38
-rw-r--r--src/types.c25
5 files changed, 187 insertions, 36 deletions
diff --git a/Makefile b/Makefile
index fc8b9f6..8b6c2e4 100644
--- a/Makefile
+++ b/Makefile
@@ -19,7 +19,8 @@ build/main.wasm.b64: build/main.wasm
mkdir -p build
(printf '"'; base64 < build/main.wasm | tr -d '\n'; printf '"') > build/main.wasm.b64
-build/index.html: src/index.html.in build/main.wasm.b64 build/music.mp3.b64 build/continue.png.b64
+build/index.html: src/index.html.in build/main.wasm.b64 build/music.mp3.b64 \
+ build/continue.png.b64 build/exit.png.b64 build/pause.png.b64 build/play.png.b64 build/restart.png.b64
mkdir -p build
clang -E -P -undef -nostdinc -x c -o build/index.html src/index.html.in
diff --git a/src/all.c b/src/all.c
index 3a249c9..6e1525a 100644
--- a/src/all.c
+++ b/src/all.c
@@ -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 {