diff options
| -rw-r--r-- | Makefile | 3 | ||||
| -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 | 
5 files changed, 187 insertions, 36 deletions
| @@ -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 @@ -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 { | 
