aboutsummaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/all.h219
-rw-r--r--source/chad.h1
-rw-r--r--source/game.c213
-rw-r--r--source/gamemode.c115
-rw-r--r--source/main.c5
-rw-r--r--source/raylib.c40
-rw-r--r--source/render.c76
-rw-r--r--source/update.c164
8 files changed, 729 insertions, 104 deletions
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 <stdint.h>
#include <math.h>
+#include <netinet/in.h>
+
#include <raylib.h>
#include <raygui.h>
#include <rlgl.h>
#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 <stdint.h>
#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 <raylib.h>
-
-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 <raylib.h>
+
+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;
+ }
+ }
+ }
+}