From dc655ac2079e0eea55c56b6712bf6a2167b57845 Mon Sep 17 00:00:00 2001 From: Emil Williams Date: Wed, 11 Feb 2026 22:40:44 +0000 Subject: primitive implementation added movement, explosions, the conception of death, and bugs --- source/all.h | 219 +++++++++++++++++++++++++++++++++++++++++++++++++++--- source/chad.h | 1 + source/game.c | 213 +++++++++++++++++++++++++++++++++------------------- source/gamemode.c | 115 ++++++++++++++++++++++++++++ source/main.c | 5 +- source/raylib.c | 40 +++++++--- source/render.c | 76 +++++++++++++++++++ source/update.c | 164 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 729 insertions(+), 104 deletions(-) create mode 100644 source/gamemode.c create mode 100644 source/render.c create mode 100644 source/update.c (limited to 'source') diff --git a/source/all.h b/source/all.h index 0c9bcec..93ea881 100644 --- a/source/all.h +++ b/source/all.h @@ -5,33 +5,228 @@ #include #include +#include + #include #include #include #include "chad.h" +#define StepStart(prefix) \ + prefix##_delta = timespec_sub(now, prefix##_last); \ + if (timespec_cmp(prefix##_delta, prefix##_interval) >= 0) { \ + (void) 0 + /* ... */ +#define StepStop(prefix) \ + prefix##s_per_second++; \ + prefix##_total++; \ + prefix##_last = timespec_add(prefix##_last, prefix##_interval); \ + if (timespec_cmp(prefix##_last, now) < 0) { \ + prefix##_last = now; \ + } \ + } + +/* this is precisely why namespacing is a good idea */ +#define GAME_RED (0x03<<0) +#define GAME_GREEN (0x03<<2) +#define GAME_BLUE (0x03<<4) +#define GAME_OPAQUE (0x03<<6) +#define GAME_WHITE (GAME_RED | GAME_GREEN | GAME_BLUE) +#define COLOR_TO_RAYLIB(c) (Color) { \ + 85*((c & GAME_RED)>>0), \ + 85*((c & GAME_GREEN)>>2), \ + 85*((c & GAME_BLUE)>>4), \ + 85*((c & GAME_OPAQUE)>>6), } + +#define TEXTURE_LIMIT (3*8) + +/* Spritesheets will be (128*4x128*6) + group 0 is [0-1][0 ] / un/breakable walls + group 1 (just explosions, the nulls are skipped for sake of compactness) + is [2-3][0 ] | + group 2 is [0-3][1 ] | Negatives use color mask + [0-2][2 ] / + group 3 is [0-3][3 ] / player + group 4 is [0-3][4 ] / bomb + group 5 is [0-3][4 ] / enemy + + */ + +enum { + /* each group is for its own group of textures, + with at most 8 textures being possible within a functional group. + we assume LSB per x86-64 ABI, + this would need to be redefined / bit field part removed */ + EXPLOSIVE = (1 << 3), + PASSIBLE = (1 << 4), + BREAKABLE = (1 << 5), + LETHAL = (1 << 6), + POWERUP = (1 << 7), + /* lethality / breakable / explosive are not specially grouped. */ + + /* group 0 "impassible" set */ + IMPASSIBLE_WALL = 0, + IMPASSIBLE_BREAKABLE_WALL = 1, + IMPASSIBLE_NOTHING = 2, + + /* group 1 "passible" set */ + PASSIBLE_NOTHING = PASSIBLE | 0, + PASSIBLE_NOTHING_LETHAL = PASSIBLE | LETHAL | 1, + PASSIBLE_EXPLOSIVE_LETHAL = PASSIBLE | LETHAL | EXPLOSIVE | 0, + PASSIBLE_EXPLOSIVE_LETHAL_END = PASSIBLE | LETHAL | EXPLOSIVE | 1, + + /* group 2 "pickup" set */ + POWERUP_BOMB = PASSIBLE | POWERUP | 0, + POWERUP_POWER = PASSIBLE | POWERUP | 1, + POWERUP_SPEED = PASSIBLE | POWERUP | 2, + // These will probably never be negative: + POWERUP_PIERCE = PASSIBLE | POWERUP | 3, + POWERUP_KICK = PASSIBLE | POWERUP | 4, + POWERUP_THROW = PASSIBLE | POWERUP | 5, + POWERUP_BOUNCE = PASSIBLE | POWERUP | 6, + + /* group 3 and higher is not directly classified by the tile system */ + +}; + +#define TILE_LENGTH_LIMIT 21 + +typedef struct { + union { + u16 _; + struct { + u8 texture : 3; // frames for everything that doesn't move, static assets. + u8 explosive : 1; // explosion animations, coopts texture for explosion frames. + u8 passable : 1; // important subgroup. + u8 breakable : 1; + u8 lethal : 1; // player will die if they occupy this space during the check + i8 pickup : 4; // positive / negative pickups + // 5 bits left for extensions. + }; + } state[TILE_LENGTH_LIMIT][TILE_LENGTH_LIMIT]; + u8 color; + Rectangle wall[2] aligned; + Rectangle explosion[2] aligned; + Rectangle powerup[8] aligned; +} tiles_t; + +#define PLAYER_LIMIT (1<<2) + +enum { + RIGHT, LEFT, UP, DOWN +}; + +typedef struct { + i16 x[PLAYER_LIMIT] aligned; + i16 y[PLAYER_LIMIT] aligned; + f32 animation_x[PLAYER_LIMIT] aligned; + f32 animation_y[PLAYER_LIMIT] aligned; + union { + u32 _; + struct { + u8 bomb_limit : 4; + u8 bomb_count : 4; + u8 power : 4; // < MAX(TILE_WIDTH, TILE_HEIGHT) + u8 speed : 4; // travels n units per second + u8 pierce : 1; + u8 kick : 1; + u8 throw : 1; // no intent to implement + u8 bounce : 1; + u8 alive : 1; + u8 direction : 2; // right left up down + u8 moving : 1; + // 10 bits left for extensions. + }; + } state[PLAYER_LIMIT] aligned; + u8 color[PLAYER_LIMIT] aligned; + Rectangle player[4] aligned; +} players_t; + +#define BOMB_LIMIT (1<<4) + typedef struct { - Font font __attribute__((aligned)); - u16 horizontal, vertical __attribute__((aligned)); - u16 ups, fps __attribute__((aligned)); + i16 x[PLAYER_LIMIT][BOMB_LIMIT] aligned; + i16 y[PLAYER_LIMIT][BOMB_LIMIT] aligned; + union { + u16 _; + struct { + u8 power : 4; // < MAX(TILE_WIDTH, TILE_HEIGHT) + u8 pierce : 1; + u8 bounce : 1; + // 10 bits left for extensions. + }; + } state[PLAYER_LIMIT][BOMB_LIMIT] aligned; + u16 timer[PLAYER_LIMIT][BOMB_LIMIT] aligned; // updates until explosion. + u8 color[2] aligned; + Rectangle bomb[4] aligned; +} bombs_t; + +#define ENEMY_LIMIT (1<<4) + +enum { + MOVEMENT_VERTICAL, + MOVEMENT_HORIZONTAL, + MOVEMENT_RANDOM, + MOVEMENT_LAST, +}; + +typedef struct { + i16 x[ENEMY_LIMIT] aligned; + i16 y[ENEMY_LIMIT] aligned; + u8 movement[ENEMY_LIMIT] aligned; + Rectangle enemy[4]; +} enemies_t; + +typedef struct { + u16 resolution_x, resolution_y; + u8 fps, ups; + char font[128]; + char spritesheet[128]; + u16 spritesheet_scale; + /* --- */ + u8 map_x, map_y; +} config_t; + +typedef struct { + tiles_t tiles aligned; + players_t players aligned; + bombs_t bombs aligned; + enemies_t enemies aligned; + config_t config aligned; + + Font font aligned; + + Texture spritesheet aligned; + Camera2D camera aligned; + + u8 client; } game_t; /* game.c */ -void GameInitialize(game_t * game, char * window_name); -void GameDeinitialize(game_t * game); -void GameFrame(game_t * game, size_t frame, f32 x, f32 y); -Vector2 GameFrameVector(game_t * game, size_t frame); -void GameLoop(game_t * game); -i16 GameUpdate(game_t * game, timespec_t now); -void GameRender(game_t * game, f64 interpolation); -void GameReport(game_t * game, f32 fps, f32 ups, u32 total_fps, u32 total_ups) ; +void GameStart(char * program_name); +void GameResize(game_t * game); + +/* gamemode.c */ + +void MultiPlayer(game_t * game, u16 width, u16 height, u8 player_count); +void SinglePlayer(game_t * game, u16 width, u16 height); + +/* update.c */ + +i16 Update(game_t * game, timespec_t now); + +/* render.c */ + +void Render(game_t * game, f64 interpolation); /* raylib.c */ -Font DefaultFont(char * choice); void GuiLoadStyleDarkSimple(void); +Font DefaultFont(char * choice); +void RaylibInitialize(int horizontal, int vertical, char * window_name, Font default_font); +void RaylibDeinitialize(void); /* ... */ diff --git a/source/chad.h b/source/chad.h index ceaec36..01eed68 100644 --- a/source/chad.h +++ b/source/chad.h @@ -13,6 +13,7 @@ #include #define always_inline static inline __attribute__((always_inline)) +#define aligned __attribute__((aligned)) #define MIN(a,b) ((a)<(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b)) diff --git a/source/game.c b/source/game.c index 2ade12a..ea02cbb 100644 --- a/source/game.c +++ b/source/game.c @@ -1,49 +1,131 @@ #include "all.h" -void GameInitialize(game_t * game, char * window_name) { - SetConfigFlags(FLAG_WINDOW_RESIZABLE); - SetTraceLogLevel(LOG_NONE); - /* :config */ - game->horizontal = 1920; - game->vertical = 1080; - InitWindow(game->horizontal, game->vertical, window_name); - game->ups = 60; - game->fps = 30; - game->font = DefaultFont("fonts/Atkinson/mono/AtkinsonHyperlegibleMono-Bold.otf"); - /* :setup */ - SetWindowState(FLAG_WINDOW_HIDDEN); - InitAudioDevice(); - SetWindowPosition(0, 0); - GuiLoadStyleDarkSimple(); - GuiSetFont(game->font); - /* --- */ +static void GameInitialize(game_t * game, char * window_name); +static void GameDeinitialize(game_t * game); +static void GameLoop(game_t * game); +static void GameReport(game_t * game, f32 fps, f32 ups, u32 total_fps, u32 total_ups); + +void GameStart(char * program_name) { + _Alignas(64) game_t game[1] = {0}; + GameInitialize(game, program_name); + GameLoop(game); + GameDeinitialize(game); } -void GameDeinitialize(game_t * game) { - SetWindowState(FLAG_WINDOW_HIDDEN); - UnloadFont(game->font); - CloseAudioDevice(); - CloseWindow(); +static void GameRecalculateViewport(game_t * game) { + game->config.resolution_x = GetScreenWidth(); + game->config.resolution_y = GetScreenHeight(); + game->camera = (Camera2D) { + .offset = (Vector2) { 0 }, + .target = (Vector2) { 0 }, + .rotation = 0., + .zoom = fminf( + (float) game->config.resolution_x / + (game->config.map_x * game->config.spritesheet_scale), + (float) game->config.resolution_y / + (game->config.map_y * game->config.spritesheet_scale)), + }; } -void GameLoop(game_t * game) { - - #define StepStart(prefix) \ - prefix##_delta = timespec_sub(now, prefix##_last); \ - if (timespec_cmp(prefix##_delta, prefix##_interval) >= 0) { (void) 0 +void GameResize(game_t * game) { + if (IsWindowResized()) { + GameRecalculateViewport(game); + } +} - #define StepStop(prefix) \ - prefix##s_per_second++; \ - prefix##_total++; \ - prefix##_last = timespec_add(prefix##_last, prefix##_interval); \ - if (timespec_cmp(prefix##_last, now) < 0) { \ - prefix##_last = now; \ - } \ +static void GameInitialize(game_t * game, char * window_name) { + + game->config = (config_t) { + .resolution_x = 600, + .resolution_y = 600, + .fps = 60, + .ups = 30, + .font = "fonts/Atkinson/mono/AtkinsonHyperlegibleMono-Bold.otf", + .spritesheet = "assets/simple.png", + .spritesheet_scale = 128, + .map_x = 13, + .map_y = 13, + }; + { + int t = game->config.spritesheet_scale; + /* better, but not really good, it's FINE */ + Rectangle wall[2] = // group 0 + {(Rectangle){ 0, 0, t, t}, + (Rectangle){t - 1, 0, t, t}}; + + Rectangle explosion[2] = // group 1 + {(Rectangle){t * 2 - 1, 0, t, t}, + (Rectangle){t * 3 - 1, 0, t, t}}; + + Rectangle powerup[8] = // group 2 + {(Rectangle){ 0, t, t, t}, + (Rectangle){t - 1, t, t, t}, + (Rectangle){t * 2 - 1, t, t, t}, + (Rectangle){t * 3 - 1, t, t, t}, + (Rectangle){ 0, t * 2, t, t}, + (Rectangle){t - 1, t * 2, t, t}, + (Rectangle){t * 2 - 1, t * 2, t, t}, + (Rectangle){t * 3 - 1, t * 2, t, t}}; + + Rectangle player[4] = // group 3 + {(Rectangle){ 0, t * 3, t, t}, + (Rectangle){t - 1, t * 3, t, t}, + (Rectangle){t * 2 - 1, t * 3, t, t}, + (Rectangle){t * 3 - 1, t * 3, t, t}}; + + Rectangle bomb[4] = // group 4 + {(Rectangle){ 0, t * 4, t, t}, + (Rectangle){t - 1, t * 4, t, t}, + (Rectangle){t * 2 - 1, t * 4, t, t}, + (Rectangle){t * 3 - 1, t * 4, t, t}}; + + Rectangle enemy[4] = // group 5 + {(Rectangle){ 0, t * 5, t, t}, + (Rectangle){t - 1, t * 5, t, t}, + (Rectangle){t * 2 - 1, t * 5, t, t}, + (Rectangle){t * 3 - 1, t * 5, t, t}}; + + memcpy(game->tiles.wall, wall, sizeof(wall)); + memcpy(game->tiles.explosion, explosion, sizeof(explosion)); + memcpy(game->tiles.powerup, powerup, sizeof(powerup)); + memcpy(game->players.player, player, sizeof(player)); + memcpy(game->bombs.bomb, bomb, sizeof(bomb)); + memcpy(game->enemies.enemy, enemy, sizeof(enemy)); } + MultiPlayer(game, game->config.map_x, game->config.map_y, 4); + + game->tiles.color = (rand() % 4) | ((rand() % 4) << 2) | ((rand() % 4) << 4) | GAME_OPAQUE; + if (game->tiles.color == GAME_OPAQUE) { game->tiles.color |= GAME_WHITE; } + + game->bombs.color[0] = GAME_WHITE | GAME_OPAQUE; + game->bombs.color[1] = GAME_RED | GAME_OPAQUE; + + /* :config */ + game->font = DefaultFont(game->config.font); + + /* this is retarded (intentionally) */ + RaylibInitialize(game->config.resolution_x-1, game->config.resolution_y-1, window_name, game->font); + SetWindowSize(game->config.resolution_x, game->config.resolution_y); + GameRecalculateViewport(game); + + game->spritesheet = LoadTexture(game->config.spritesheet); + if (game->spritesheet.id <= 0) { abort(); } + + ClearWindowState(FLAG_WINDOW_HIDDEN); +} + +static void GameDeinitialize(game_t * game) { + UnloadTexture(game->spritesheet); + if (GetFontDefault().texture.id != game->font.texture.id) { UnloadFont(game->font); } + RaylibDeinitialize(); +} + +static void GameLoop(game_t * game) { + #define StepSimpleStart(prefix,linear) \ prefix##_delta = timespec_sub(now, prefix##_last); \ - if (timespec_cmp(prefix##_delta, (timespec_t){1.,0.}) >= 0) { \ + if (timespec_cmp(prefix##_delta, linear) >= 0) { \ (void)0 #define StepSimpleStop(prefix) \ @@ -52,8 +134,8 @@ void GameLoop(game_t * game) { timespec_t now, - update_interval = {0, (f64) TIMESPEC_HZ / game->ups}, - frame_interval = {0, (f64) TIMESPEC_HZ / game->fps}, + update_interval = {0, (f64) TIMESPEC_HZ / game->config.ups}, + frame_interval = {0, (f64) TIMESPEC_HZ / game->config.fps}, update_last, frame_last, print_last, update_delta, frame_delta, print_delta, wait; @@ -66,32 +148,30 @@ void GameLoop(game_t * game) { f64 interpolation = 0.; - ClearWindowState(FLAG_WINDOW_HIDDEN); - clock_gettime(CLOCK_MONOTONIC, &now); update_last = frame_last = print_last = now; while (1) { StepStart(update); - if (GameUpdate(game, now)) { return; } + if (Update(game, now)) { return; } StepStop(update); StepStart(frame); clock_gettime(CLOCK_MONOTONIC, &now); interpolation = CLAMP( - TIMESPEC_TO_F64(update_delta) - / TIMESPEC_TO_F64(update_interval), - 0.0, 1.0f); - GameRender(game, interpolation); + TIMESPEC_TO_F64(update_delta) + / TIMESPEC_TO_F64(update_interval), + 0.0, 1.0f); + Render(game, interpolation); StepStop(frame); clock_gettime(CLOCK_MONOTONIC, &now); wait = timespec_sub( timespec_min( - timespec_add(update_last, update_interval), - timespec_add(frame_last, frame_interval)), + timespec_add(update_last, update_interval), + timespec_add(frame_last, frame_interval)), now); if (timespec_cmp(wait, zero_seconds) > 0) { @@ -100,10 +180,10 @@ void GameLoop(game_t * game) { StepSimpleStart(print, one_second); GameReport(game, - round(frames_per_second / TIMESPEC_TO_F64(print_delta)), - round(updates_per_second / TIMESPEC_TO_F64(print_delta)), - frame_total, - update_total); + round(frames_per_second / TIMESPEC_TO_F64(print_delta)), + round(updates_per_second / TIMESPEC_TO_F64(print_delta)), + frame_total, + update_total); frames_per_second = updates_per_second = 0; StepSimpleStop(print); @@ -111,33 +191,10 @@ void GameLoop(game_t * game) { } } -i16 GameUpdate(game_t * game, timespec_t now) { - (void) now; - - PollInputEvents(); - if (IsWindowResized()) { - game->horizontal = GetScreenWidth(); - game->vertical = GetScreenHeight(); - } - switch (GetKeyPressed()) { - case KEY_ESCAPE: return 1; - } - return 0; -} - -void GameRender(game_t * game, f64 interpolation) { - (void)interpolation; - - BeginDrawing(); - ClearBackground(BLACK); - /* >>> */ - - /* --- */ - rlDrawRenderBatchActive(); - SwapScreenBuffer(); -} - -void GameReport(game_t * game, f32 fps, f32 ups, u32 total_fps, u32 total_ups) { +static void GameReport(game_t * game, f32 fps, f32 ups, u32 total_fps, u32 total_ups) { + (void)game; +#ifndef NDEBUG printf("[FPS|UPS|Total] (%3.0f : %3.0f) | [%7u/%7u]\n", - fps, ups, total_fps, total_ups); + fps, ups, total_fps, total_ups); +#endif } diff --git a/source/gamemode.c b/source/gamemode.c new file mode 100644 index 0000000..cf5d06a --- /dev/null +++ b/source/gamemode.c @@ -0,0 +1,115 @@ +#include "all.h" + +void MultiPlayer(game_t * game, u16 width, u16 height, u8 player_count) { + int i, j; + + for (i = 0; i < width; ++i) { + for (j = 0; j < height; ++j) { + game->tiles.state[i][j]._ = rand() % 10 ? IMPASSIBLE_BREAKABLE_WALL : PASSIBLE_NOTHING; + } + } + + for (i = 1; i < width; i += 2) { + for (j = 1; j < height; j += 2) { + game->tiles.state[i][j]._ = IMPASSIBLE_WALL; + } + } + + bzero(game->players.state, sizeof(*game->players.state) * PLAYER_LIMIT); + + for (i = 0; i < MIN(PLAYER_LIMIT, player_count); ++i) { + game->players.state[i].bomb_limit = 1; + game->players.state[i].power = 2; + game->players.state[i].speed = 2; + game->players.state[i].alive = 1; + game->players.state[i].direction = DOWN; + } + + float player_x[4] = + {0, (width-1), (width-1), 0 }; + float player_y[4] = + {0, (height-1), 0, (height-1)}; + + u8 color[4] = { + GAME_RED | GAME_GREEN | GAME_OPAQUE, + GAME_RED | GAME_GREEN | GAME_BLUE | GAME_OPAQUE, + GAME_GREEN | GAME_OPAQUE, + GAME_BLUE | GAME_OPAQUE, + }; + + for (i = 0; i < MIN(player_count, PLAYER_LIMIT); ++i) { + game->players.x[i] = player_x[i % 4]; + game->players.y[i] = player_y[i % 4]; + game->players.color[i] = color[i % 4]; + } + + /* areas that must be passible nothings */ + u8 offset_x[12] = + {0, 1, 0, width-1, width-2, width-1, width-1, width-2, width-1, 0, 1, 0}; + u8 offset_y[12] = + {0, 0, 1, height-1, height-1, height-2, 0, 0, 1, height-1, height-1, height-2}; + + for (i = 0; i < MIN((player_count * 3), 12); ++i) { + game->tiles.state[offset_x[i]][offset_y[i]]._ = PASSIBLE_NOTHING; + } + + for (i = 0; i < width; ++i) { + for (j = 0; j < height; ++j) { + printf("%3d ", game->tiles.state[i][j]._); + } + printf("\n"); + } +} + +/* missing proper player / bomb / enemy initialization */ +void SinglePlayer(game_t * game, u16 width, u16 height) { + u16 i, j; + + for (i = 0; i < width; ++i) { + for (j = 0; j < height; ++j) { + game->tiles.state[i][j]._ = rand() % 10 ? IMPASSIBLE_BREAKABLE_WALL : PASSIBLE_NOTHING; + } + } + + u16 x, y; + int distance, direction; + for (i = 0; i < ENEMY_LIMIT; ++i) { + distance = rand() % MIN(width, height); + direction = rand() % MOVEMENT_LAST; + x = rand() % width; + y = rand() % height; + game->enemies.x[i] = x; + game->enemies.y[i] = y; + game->enemies.movement[i] = direction; + game->tiles.state[x][y]._ = PASSIBLE_NOTHING; + for (j = -(distance/2); j+(distance/2) < distance; ++j) { + game->tiles.state + [x + (j * (direction == MOVEMENT_HORIZONTAL) * (x + j < width ))] + [y + (j * (direction == MOVEMENT_VERTICAL ) * (y + j < height))]._ = PASSIBLE_NOTHING; + } + } + + for (i = 1; i < width; i += 2) { + for (j = 1; j < height; j += 2) { + game->tiles.state[i][j]._ = IMPASSIBLE_WALL; + } + } + + game->tiles.state[0][0]._ = PASSIBLE_NOTHING; + game->tiles.state[1][0]._ = PASSIBLE_NOTHING; + game->tiles.state[0][1]._ = PASSIBLE_NOTHING; + + game->players.x[0] = 0; + game->players.y[0] = 0; + game->players.state[0].bomb_limit = 1; + game->players.state[0].power = 2; + game->players.state[0].speed = 3; + game->players.state[0].alive = 1; + + for (i = 0; i < width; ++i) { + for (j = 0; j < height; ++j) { + printf("%3d ", game->tiles.state[i][j]._); + } + printf("\n"); + } +} diff --git a/source/main.c b/source/main.c index d00f7e7..370e463 100644 --- a/source/main.c +++ b/source/main.c @@ -3,13 +3,10 @@ int Main(int count, char ** arguments) { (void)count; - _Alignas(64) game_t game[1] = {0}; char * program_name = arguments[0]; srand(time(NULL)); Root(program_name); - GameInitialize(game, program_name); - GameLoop(game); - GameDeinitialize(game); + GameStart(program_name); return 0; } diff --git a/source/raylib.c b/source/raylib.c index 935feee..d96aa4a 100644 --- a/source/raylib.c +++ b/source/raylib.c @@ -1,13 +1,3 @@ -/* raylib.c & raygui.c */ - -#include - -Font DefaultFont(char * choice) { - Font font = LoadFont(choice); - if (!IsFontValid(font)) { font = GetFontDefault(); } - return font; -} - /* raygui.c */ #pragma GCC diagnostic push @@ -31,3 +21,33 @@ void GuiLoadStyleDarkSimple(void) { GuiSetStyle(darkStyleProps[i].controlId, darkStyleProps[i].propertyId, darkStyleProps[i].propertyValue); } } + +/* raylib.c */ + +#include + +Font DefaultFont(char * choice) { + Font font = LoadFont(choice); + if (!IsFontValid(font)) { font = GetFontDefault(); } + return font; +} + +void RaylibInitialize(int horizontal, int vertical, char * window_name, Font default_font) { +#ifdef NDEBUG + SetTraceLogLevel(LOG_NONE); +#endif + /* SetConfigFlags(FLAG_WINDOW_RESIZABLE); */ + InitWindow(horizontal, vertical, window_name); + SetWindowState(FLAG_WINDOW_HIDDEN); + /* we should spawn this in the center of the screen and have our window scale to the limit of the screen */ + InitAudioDevice(); + SetWindowPosition(0, 0); + GuiLoadStyleDarkSimple(); + GuiSetFont(default_font); +} + +void RaylibDeinitialize(void) { + SetWindowState(FLAG_WINDOW_HIDDEN); + CloseAudioDevice(); + CloseWindow(); +} diff --git a/source/render.c b/source/render.c new file mode 100644 index 0000000..5af04f8 --- /dev/null +++ b/source/render.c @@ -0,0 +1,76 @@ +#include "all.h" + +static void RenderTiles(game_t * game); +static void RenderPlayers(game_t * game); +static void RenderBombs(game_t * game); + +void Render(game_t * game, f64 interpolation) { + (void)game; + (void)interpolation; + + BeginDrawing(); + ClearBackground(BLACK); + BeginMode2D(game->camera); + RenderTiles(game); + RenderBombs(game); + RenderPlayers(game); + EndMode2D(); + /* --- */ + rlDrawRenderBatchActive(); + SwapScreenBuffer(); +} + +void RenderTiles(game_t * game) { + for (int i = 0; i < game->config.map_x; ++i) { + for (int j = 0; j < game->config.map_y; ++j) { + if ((game->tiles.state[i][j]._ | 1) & PASSIBLE || game->tiles.state[i][j]._ == IMPASSIBLE_NOTHING) { + DrawRectangleRec((Rectangle) {i*game->config.spritesheet_scale, j*game->config.spritesheet_scale, game->config.spritesheet_scale, game->config.spritesheet_scale}, COLOR_TO_RAYLIB(game->tiles.color)); + } + + if (game->tiles.state[i][j]._ < 2) { + DrawTextureRec( + game->spritesheet, + game->tiles.wall[game->tiles.state[i][j].texture], + (Vector2) {i*game->config.spritesheet_scale, j*game->config.spritesheet_scale}, + COLOR_TO_RAYLIB(game->tiles.color)); + } + + /* This almost requires horrible no good melding. Almost. */ + if (game->tiles.state[i][j]._ & EXPLOSIVE) { + DrawTextureRec( + game->spritesheet, + game->tiles.explosion[game->tiles.state[i][j].texture], + (Vector2) {i*game->config.spritesheet_scale, j*game->config.spritesheet_scale}, + WHITE); + } + + + } + } +} + +void RenderPlayers(game_t * game) { + for (int i = 0; i < PLAYER_LIMIT; ++i) { + if (game->players.state[i].alive) { + DrawTextureRec( + game->spritesheet, + game->players.player[game->players.state[i].direction], + (Vector2) {game->players.x[i] * game->config.spritesheet_scale, game->players.y[i] * game->config.spritesheet_scale}, + COLOR_TO_RAYLIB(game->players.color[i])); + } + } +} + +void RenderBombs(game_t * game) { + for (int i = 0; i < PLAYER_LIMIT; ++i) { + for (int j = 0; j < PLAYER_LIMIT; ++j) { + if (game->bombs.timer[i][j]) { + DrawTextureRec( + game->spritesheet, + game->bombs.bomb[game->bombs.timer[i][j] % 4], + (Vector2) {game->bombs.x[i][j]*game->config.spritesheet_scale, game->bombs.y[i][j]*game->config.spritesheet_scale}, + COLOR_TO_RAYLIB(game->bombs.color[game->bombs.timer[i][j]%2])); + } + } + } +} diff --git a/source/update.c b/source/update.c new file mode 100644 index 0000000..9cec977 --- /dev/null +++ b/source/update.c @@ -0,0 +1,164 @@ +#include "all.h" + +static void CheckKilled(game_t * game); +static void UpdateExplosions(game_t * game); +static void PlaceExplosive(game_t * game); +static int CheckInputDebug(game_t * game); +static void CheckInputPlaceExplosive(game_t * game); +static void CheckInputMovement(game_t * game); +static void UpdatePlayer(game_t * game); +static void UpdateBomb(game_t * game); + +i16 Update(game_t * game, timespec_t now) { + (void) now; + PollInputEvents(); + GameResize(game); + if (CheckInputDebug(game)) { return 1; } + CheckInputMovement(game); + CheckInputPlaceExplosive(game); + UpdatePlayer(game); + UpdateBomb(game); + CheckKilled(game); + UpdateExplosions(game); + return 0; +} + +static void CheckKilled(game_t * game) { + for (size_t i = 0; i < PLAYER_LIMIT; ++i) { + if (game->tiles.state[game->players.x[i]][game->players.y[i]].lethal) { + game->players.state[i].alive = 0; + } + } +} + + +static void UpdateExplosions(game_t * game) { + size_t i, j; + for (i = 0; i < game->config.map_x; ++i) { + for (j = 0; j < game->config.map_y; ++j) { + if (game->tiles.state[i][j]._ >= PASSIBLE_EXPLOSIVE_LETHAL) { + if (game->tiles.state[i][j]._ == PASSIBLE_EXPLOSIVE_LETHAL_END) + { game->tiles.state[i][j]._ = PASSIBLE_NOTHING; } + else + { ++game->tiles.state[i][j]._; } + } + } + } +} + +static void PlaceExplosive(game_t * game) { + auto state = &game->players.state[game->client]; + if (state->bomb_count < state->bomb_limit) { + game->tiles.state + [game->players.x[game->client]] + [game->players.y[game->client]]._ = IMPASSIBLE_NOTHING; + game->bombs.x[game->client][state->bomb_count] = game->players.x[game->client]; + game->bombs.y[game->client][state->bomb_count] = game->players.y[game->client]; + game->bombs.state[game->client][state->bomb_count].power = state->power; + game->bombs.state[game->client][state->bomb_count].pierce = state->pierce; + game->bombs.state[game->client][state->bomb_count].bounce = state->bounce; + game->bombs.timer[game->client][state->bomb_count] = game->config.ups * 2; + ++state->bomb_count; + } +} + +static int CheckInputDebug(game_t * game) { + switch (GetKeyPressed()) { + case KEY_ESCAPE: return 1; +#ifndef NDEBUG + case KEY_R: MultiPlayer(game, game->config.map_x, game->config.map_y, 4); break; + case KEY_T: if (game->client < 3) game->client++; break; + case KEY_G: if (game->client != 0) game->client--; break; +#endif + } + return 0; +} + +static void CheckInputPlaceExplosive(game_t * game) { + auto state = &game->players.state[game->client]; + if ((IsKeyPressed(KEY_FIVE) || IsKeyPressed(KEY_SPACE) || IsKeyPressed(KEY_U) || IsKeyPressed(KEY_O) + || IsKeyPressed(KEY_M) || IsKeyPressed(KEY_PERIOD) || IsKeyPressed(KEY_Z) || IsKeyPressed(KEY_C) + || IsKeyPressed(KEY_Q) || IsKeyPressed(KEY_E) || IsKeyPressed(KEY_ENTER)) + && state->alive) + { PlaceExplosive(game); } +} + + +static void CheckInputMovement(game_t * game) { + auto state = &game->players.state[game->client]; + + state->moving = 0; + if (IsKeyPressed(KEY_UP) || IsKeyPressed(KEY_EIGHT) || IsKeyPressed(KEY_I) || IsKeyPressed(KEY_W)) + { state->moving = 1; state->direction = UP; } + if (IsKeyPressed(KEY_LEFT) || IsKeyPressed(KEY_FOUR) || IsKeyPressed(KEY_J) || IsKeyPressed(KEY_A)) + { state->moving = 1; state->direction = LEFT; } + if (IsKeyPressed(KEY_DOWN) || IsKeyPressed(KEY_TWO) || IsKeyPressed(KEY_K) || IsKeyPressed(KEY_S)) + { state->moving = 1; state->direction = DOWN; } + if (IsKeyPressed(KEY_RIGHT) || IsKeyPressed(KEY_SIX) || IsKeyPressed(KEY_L) || IsKeyPressed(KEY_D)) + { state->moving = 1; state->direction = RIGHT; } + + /* if (IsKeyDown(KEY_EIGHT) || IsKeyDown(KEY_I) || IsKeyDown(KEY_W) */ + /* || IsKeyDown(KEY_FOUR) || IsKeyDown(KEY_J) || IsKeyDown(KEY_A) */ + /* || IsKeyDown(KEY_TWO) || IsKeyDown(KEY_K) || IsKeyDown(KEY_S) */ + /* || IsKeyDown(KEY_SIX) || IsKeyDown(KEY_L) || IsKeyDown(KEY_D)) */ + /* { state->moving = 1; } */ + + /* if (IsKeyReleased(KEY_EIGHT) || IsKeyReleased(KEY_I) || IsKeyReleased(KEY_W) */ + /* || IsKeyReleased(KEY_FOUR) || IsKeyReleased(KEY_J) || IsKeyReleased(KEY_A) */ + /* || IsKeyReleased(KEY_TWO) || IsKeyReleased(KEY_K) || IsKeyReleased(KEY_S) */ + /* || IsKeyReleased(KEY_SIX) || IsKeyReleased(KEY_L) || IsKeyReleased(KEY_D)) */ + /* { state->moving = 0; } */ +} + +static void UpdatePlayer(game_t * game) { + auto state = &game->players.state[game->client]; + i16 * player_x = &game->players.x[game->client]; + i16 * player_y = &game->players.y[game->client]; + /* f32 * animation_x = &game->players.animation_x[game->client]; */ + /* f32 * animation_y = &game->players.animation_y[game->client]; */ + float direction_x = -(state->direction == LEFT) + (state->direction == RIGHT); + float direction_y = -(state->direction == UP) + (state->direction == DOWN); + + i16 delta_x = direction_x * state->moving; + i16 delta_y = direction_y * state->moving; + + if (*player_x + delta_x >= 0 + && *player_x + delta_x < game->config.map_x + && game->tiles.state[*player_x + delta_x][*player_y]._ & PASSIBLE) + { *player_x += delta_x; } + if (*player_y + delta_y >= 0 + && *player_y + delta_y < game->config.map_y + && game->tiles.state[*player_x][*player_y + delta_y]._ & PASSIBLE) + { *player_y += delta_y; } +} + +static void UpdateBomb(game_t * game) { + u16 * b = *game->bombs.timer; + ssize_t + offset_x[4] = {-1, 1, 0, 0}, + offset_y[4] = { 0, 0, -1, 1}; + size_t i, j; + for (i = 0; i < PLAYER_LIMIT * BOMB_LIMIT; ++i) { + if (b[i]) { + --b[i]; + if (!b[i]) { + /* game->tiles.state[(*game->bombs.x)[i]][(*game->bombs.y)[i]]._ = PASSIBLE_NOTHING; */ + /* explosion */ + for (j = 0; j < 4; ++j) { + i16 + rx = (*game->bombs.x)[i] + offset_x[j], + ry = (*game->bombs.y)[i] + offset_y[j]; + + if (game->tiles.state[rx][ry]._ & PASSIBLE || game->tiles.state[rx][ry]._ == IMPASSIBLE_BREAKABLE_WALL) { + if (rx < game->config.map_x && rx >= 0 + && ry < game->config.map_y && ry >= 0) + { game->tiles.state[rx][ry]._ = PASSIBLE_EXPLOSIVE_LETHAL; } + } + } + game->tiles.state[*game->bombs.x[i]][*game->bombs.y[i]]._ = PASSIBLE_EXPLOSIVE_LETHAL; + /* --- */ + --game->players.state[i>>2].bomb_count; + } + } + } +} -- cgit v1.2.3