#ifndef INCLUDE_ALL_C #define INCLUDE_ALL_C #include #include #include "types.c" #include "levels.c" #include "tick.c" static const Color colors[N_COLORS][4] = { { {255, 255, 255, 255}, {255, 255, 255, 255}, {255, 255, 255, 255}, {255, 255, 255, 255}, }, { {0, 0, 0, 255}, {0, 0, 0, 255}, {0, 0, 0, 255}, {0, 0, 0, 255}, }, { {255, 0, 0, 255}, {255, 0, 0, 255}, {255, 0, 0, 255}, {255, 0, 0, 255}, }, { {255, 255, 0, 255}, {255, 255, 0, 255}, {255, 255, 0, 255}, {255, 255, 0, 255}, }, { {255, 0, 0, 255}, {255, 0, 0, 255}, {255, 255, 0, 255}, {255, 255, 0, 255}, }, { {255, 255, 0, 255}, {255, 255, 0, 255}, {255, 0, 0, 255}, {255, 0, 0, 255}, }, { {255, 0, 0, 255}, {255, 255, 0, 255}, {255, 0, 0, 255}, {255, 255, 0, 255}, }, { {255, 255, 0, 255}, {255, 0, 0, 255}, {255, 255, 0, 255}, {255, 0, 0, 255}, }, { {0, 0, 255, 255}, {0, 0, 255, 255}, {0, 0, 255, 255}, {0, 0, 255, 255}, }, { {0, 0, 255, 255}, {0, 0, 255, 255}, {255, 255, 255, 255}, {255, 255, 255, 255}, }, { {255, 255, 255, 255}, {255, 255, 255, 255}, {0, 0, 255, 255}, {0, 0, 255, 255}, }, { {0, 0, 255, 255}, {255, 255, 255, 255}, {0, 0, 255, 255}, {255, 255, 255, 255}, }, { {255, 255, 255, 255}, {0, 0, 255, 255}, {255, 255, 255, 255}, {0, 0, 255, 255}, }, }; #define BUTTON_SIZE 32 #define BUTTON_SPACING 16 #define BUTTON_SPACER(i) (BUTTON_SPACING + (BUTTON_SIZE * (i) + (i) * BUTTON_SPACING)) 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}, }; static DrawList *render(State *state, UI *ui, Arena *a) { (void) ui; DrawList *drawList = new(a, 1, DrawList); int cellWidth = (ui->width - GRID_OFFSET_X) / GRIDWIDTH; int cellHeight = ui->height / GRIDHEIGHT; for (int x = 0; x < GRIDWIDTH; x++) { for (int y = 0; y < GRIDHEIGHT; y++) { drawList->els[drawList->len++] = (DrawElement) { .x = cellWidth * x + GRID_OFFSET_X, .y = cellHeight * y, .w = cellWidth, .h = cellHeight, .fill = {0, 0, 0, 0}, .border = {0, 0, 0, 255}, }; for (int subx = 0; subx < 2; subx++) { for (int suby = 0; suby < 2; suby++) { drawList->els[drawList->len++] = (DrawElement) { .x = cellWidth * x + GRID_OFFSET_X + subx * cellWidth / 2, .y = cellHeight * y + suby * cellHeight / 2, .w = cellWidth / 2, .h = cellHeight / 2, .fill = colors[state->grid[x + GRIDWIDTH * y]][subx + 2 * suby], .border = {0, 0, 0, 0}, }; } } } } drawList->els[drawList->len++] = (DrawElement) { .x = cellWidth * state->goalx + GRID_OFFSET_X, .y = cellHeight * state->goaly, .w = cellWidth, .h = cellHeight, .fill = {255, 0, 0, 63}, .border = {255, 0, 0, 255}, .image = 1, }; drawList->els[drawList->len++] = (DrawElement) { .x = 0, .y = 0, .w = GRID_OFFSET_X, .h = ui->height, .fill = {255, 255, 0, 255}, .border = {0, 0, 0, 255}, }; for (int i = 0; i < N_BUTTONS; i++) { Color colour = {0, 0, 0, 255}; switch (state->buttonStates[i]) { case BUTTON_STATE_IDLE: colour = (Color) {0, 0, 0, 255}; break; case BUTTON_STATE_HOVERED: colour = (Color) {255, 255, 0, 255}; break; case BUTTON_STATE_PRESSED: colour = (Color) {255, 0, 0, 255}; break; } drawList->els[drawList->len++] = (DrawElement) { .x = buttons[i].x, .y = buttons[i].y, .w = buttons[i].w, .h = buttons[i].h, .fill = colour, .border = {0, 0, 0, 255} }; } return drawList; } static void update(Game *game, uint64_t now, Arena a) { const int offset_width = game->ui.width - GRID_OFFSET_X; switch (game->input) { int x, y; case INPUT_CLICK: x = (game->mousex - GRID_OFFSET_X) * GRIDWIDTH / offset_width; y = game->mousey * GRIDHEIGHT / game->ui.height; game->state.grid[x + y * GRIDWIDTH] = (game->state.grid[x + y * GRIDWIDTH] + 1) % (sizeof(colors) / sizeof(colors[0])); for (int i = 0; i < N_BUTTONS; i++) { if ( game->mousex > buttons[i].x && game->mousex < buttons[i].x + buttons[i].w && game->mousey > buttons[i].y && game->mousey < buttons[i].y + buttons[i].h ) { // TODO - CLICK THINGS //game->state.buttonStates[i] = BUTTON_STATE_PRESSED; } } break; case INPUT_RCLICK: x = game->mousex * GRIDWIDTH / game->ui.width; y = game->mousey * GRIDHEIGHT / game->ui.height; game->state.grid[x + y * GRIDWIDTH] = EMPTY; break; case INPUT_PAUSE_PLAY: game->state.playing = !game->state.playing; break; default: break; } if (game->state.playing && game->state.lastTick + TICK_LENGTH <= now) { game->state.lastTick = now; tick(game, a); } } #if SDL #include typedef struct { int width, height; Color colors[16]; const char *pixels; } Image; #include "../build/images.c" SDL_Texture *textures[sizeof(images) / sizeof(images[0])]; #include "../build/music.c" SDL_AudioStream *stream; int main(int argc, char **argv) { (void) argc; (void) argv; char *mem = SDL_malloc(MEM_SIZE); Arena a = { .start = mem, .end = mem + MEM_SIZE, }; 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->ui = (UI) { .width = 640, .height = 480, }; for (int i = 0; i < N_BUTTONS; i++) { game->state.buttonStates[i] = BUTTON_STATE_IDLE; } SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); SDL_Window *w = SDL_CreateWindow( "LDJam 57", game->ui.width, game->ui.height, SDL_WINDOW_RESIZABLE ); SDL_PropertiesID renderProps = SDL_CreateProperties(); SDL_SetPointerProperty(renderProps, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, w); SDL_SetNumberProperty(renderProps, SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, 1); SDL_Renderer *r = SDL_CreateRendererWithProperties(renderProps); SDL_DestroyProperties(renderProps); SDL_SetRenderDrawBlendMode(r, SDL_BLENDMODE_BLEND); for (int j = 1; j < (int) (sizeof(images) / sizeof(images[0])); j++) { textures[j] = SDL_CreateTexture( r, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, images[j].width, images[j].height ); SDL_SetTextureScaleMode(textures[j], SDL_SCALEMODE_NEAREST); Arena scratch = a; Color *pixels = new(&scratch, images[j].width * images[j].height, Color); for (int i = 0; i < images[j].width * images[j].height; i++) { pixels[i] = images[j].colors[(int) images[j].pixels[i]]; } SDL_UpdateTexture(textures[j], NULL, pixels, images[j].width * 4); } SDL_AudioSpec audioSpec = { .format = SDL_AUDIO_S16LE, .channels = 2, .freq = 48000, }; stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audioSpec, NULL, NULL); SDL_PutAudioStreamData(stream, musicBytes, sizeof(musicBytes)); SDL_ResumeAudioStreamDevice(stream); for (;;) { uint64_t now = SDL_GetTicks(); game->input = INPUT_NONE; SDL_Event e = {0}; while (SDL_PollEvent(&e)) { switch (e.type) { case SDL_EVENT_QUIT: return 0; case SDL_EVENT_MOUSE_BUTTON_DOWN: game->mousex = (int) e.button.x; game->mousey = (int) e.button.y; if (e.button.button == 1) { game->input = INPUT_CLICK; } else if (e.button.button == 3) { game->input = INPUT_RCLICK; } break; case SDL_EVENT_KEY_DOWN: switch (e.key.key) { case SDLK_SPACE: game->input = INPUT_PAUSE_PLAY; break; } break; case SDL_EVENT_WINDOW_RESIZED: SDL_GetWindowSize(w, &game->ui.width, &game->ui.height); break; } } Arena scratch = a; DrawList *drawList = render(&game->state, &game->ui, &scratch); SDL_SetRenderDrawColor(r, 0, 0, 0, 255); SDL_RenderFillRect(r, NULL); for (int i = 0; i < drawList->len; i++) { SDL_FRect rect = { .x = (float) drawList->els[i].x, .y = (float) drawList->els[i].y, .w = (float) drawList->els[i].w, .h = (float) drawList->els[i].h }; SDL_SetRenderDrawColor( r, drawList->els[i].fill.r, drawList->els[i].fill.g, drawList->els[i].fill.b, drawList->els[i].fill.a ); SDL_RenderFillRect(r, &rect); SDL_SetRenderDrawColor( r, drawList->els[i].border.r, drawList->els[i].border.g, drawList->els[i].border.b, drawList->els[i].border.a ); SDL_RenderRect(r, &rect); if (drawList->els[i].image != 0) { SDL_RenderTexture(r, textures[drawList->els[i].image], NULL, &rect); } } update(game, now, a); if (SDL_GetAudioStreamQueued(stream) < (int) sizeof(musicBytes) / 8) { SDL_PutAudioStreamData(stream, musicBytes, sizeof(musicBytes)); } SDL_RenderPresent(r); } return 0; } #elif WASM static Game *game; static Arena perm; __attribute((export_name("game_init"))) void game_init(void) { static char heap[MEM_SIZE]; perm.start = heap; perm.end = heap + MEM_SIZE; game = new(&perm, 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; } __attribute((export_name("game_render"))) DrawList *game_render(int width, int height) { game->ui.width = width; game->ui.height = height; Arena scratch = perm; return render(&game->state, &game->ui, &scratch); } __attribute((export_name("game_update"))) void game_update(int input, int mousex, int mousey, int now) { game->input = input; game->mousex = mousex; game->mousey = mousey; update(game, now, perm); } #endif #endif