aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xMakefile17
-rw-r--r--README.md256
-rw-r--r--source/all.h183
-rw-r--r--source/game.c73
-rw-r--r--source/gamemode.c101
-rw-r--r--source/main.c60
-rw-r--r--source/options.gperf28
-rw-r--r--source/render.c55
-rw-r--r--source/update.c138
9 files changed, 564 insertions, 347 deletions
diff --git a/Makefile b/Makefile
index e3419fd..20760bc 100755
--- a/Makefile
+++ b/Makefile
@@ -1,11 +1,16 @@
#!/usr/bin/make -f
+GPERF = gperf
+
+PERF.orig := $(wildcard ${SOURCE.dir}/*.gperf)
+PERF := $(PERF:${SOURCE.dir}/%=%)
+PERF.h := $(addprefix ${OBJECT.dir}/,$(PERF:.gperf=.h))
SOURCE.dir := source
OBJECT.dir := object
INCLUDE.dir := include
LIBRARY.dir := library
LIBRARY := $(addprefix ${LIBRARY.dir}/, libraylib.amd64.a)
-HEADER := $(wildcard ${SOURCE.dir}/*.h) $(wildcard ${INCLUDE.dir}/*.h) include/raygui.h include/raylib.h
+HEADER := $(wildcard ${SOURCE.dir}/*.h) $(wildcard ${INCLUDE.dir}/*.h) include/raygui.h include/raylib.h ${PERF.h}
HEADER.orig := $(HEADER:${SOURCE.dir}/%=%)
HEADER.orig := $(HEADER.orig:${INCLUDE.dir}/%=%)
HEADER.pch := $(filter-out ${OBJECT.dir}/style_dark.h,$(addprefix ${OBJECT.dir}/, ${HEADER.orig:.c=}))
@@ -18,7 +23,7 @@ DEPEND := $(wildcard ${OBJECT.dir}/*.d)
TARGET := $(shell basename $$PWD)
CFLAGS := -std=c23 -pthread -MMD -MP
-CPPFLAGS := -Iinclude -D_GNU_SOURCE -I ${OBJECT.dir}
+CPPFLAGS := -I${INCLUDE.dir} -I${OBJECT.dir} -D_GNU_SOURCE -I ${OBJECT.dir}
LDFLAGS := -lm -lbsd
ifneq ($(shell which mold 2> /dev/null),)
@@ -75,11 +80,15 @@ endif
vpath %.c ${SOURCE.dir}
vpath %.h ${SOURCE.dir} ${INCLUDE.dir}
+vpath %.gperf ${SOURCE.dir}
${OBJECT.dir}/%.o: %.c
@echo "CC $<"
@${COMPILE.c} -o $@ $<
+${OBJECT.dir}/%.h: %.gperf
+ ${GPERF} --null-strings -tEIH $(shell basename $(basename $<))_hash -N $(shell basename $(basename $<))_lookup < $< > $@
+
# The tree builds incorrectly if these two are merged
# at the target level at a bare build.
${OBJECT.dir}/%.h.pch: %.h
@@ -91,12 +100,12 @@ ${OBJECT.dir}/%.h.pch: %.h
@${COMPILE.c} ${PCHFLAGS} -x c-header -o $@ $<
.PHONY: all clean
-all: include/raylib.h include/raygui.h .WAIT ${HEADER.pch} ${TARGET}
+all: include/raylib.h include/raygui.h object/options.h .WAIT ${HEADER.pch} ${TARGET}
${HEADER.pch}: ${LIBRARY.dir}/libraylib.amd64.a include/raygui.h include/style_dark.h
clean:
- rm -f ${OBJECT.dir}/*.o ${OBJECT.dir}/*.pch ${OBJECT.dir}/*.gch ${OBJECT.dir}/*.d ${LIBRARY.dir}/*.a ${INCLUDE.dir}/*.h ${TARGET}
+ rm -f ${OBJECT.dir}/*.o ${OBJECT.dir}/*.pch ${OBJECT.dir}/*.gch ${OBJECT.dir}/*.d ${LIBRARY.dir}/*.a ${INCLUDE.dir}/*.h ${OBJECT.dir}/*.h ${TARGET}
rm -f /tmp/raygui.tgz /tmp/raylib.tgz
${TARGET}: ${LIBRARY.dir}/libraylib.amd64.a ${OBJECT}
diff --git a/README.md b/README.md
index e258042..27ff5f2 100644
--- a/README.md
+++ b/README.md
@@ -10,111 +10,188 @@ We want the traditional experience with simple power-ups and benign enemies.
Design-wise we'll keep things centralized and assume that this is a unified highly particular program - as any game is.
-#### Structures
+We do not just allocate things, we designate them. Runtime Allocation is for losers.
+Hardcoding is our one true design goal. Knowing exactly what we want, rather than trying to cover a hypothetical's hypothetical potdream.
-We'll start with the fundamental corpus:
+#### Structures
- #define EXPECTED_UPS 30 // shouldn't matter at low values.
- #define EXPECTED_FPS 60 // or higher
+Our Corpus:
typedef struct {
- tiles_t tiles; // tiles classifying texturing, accessibility, and lethality.
- // Any communication between entities is done here.
- players_t players; // Controlled-by-humans.
- bombs_t bombs; // timed explosives.
- enemies_t enemies; // Simple walk-back-and-forth enemies.
+ tiles_t tiles;
+ players_t players;
+ bombs_t bombs;
+ enemies_t enemies;
+ config_t config;
+ u16 time_limit;
+ u8 client;
- // Windowing / Game Loop
- Font font; // the global font
- u16 horizontal, vertical; // highly mutable width & height of the current window
- u16 ups, fps; // Individually tracked intervals
- // ...
+ Font font;
+ Rectangle atlas[4*6];
+ Texture spritesheet;
+ Camera2D camera;
} game_t;
-
-The naming here is to imply that these substructures clearly imply that they cover everything, rather than needing to be arrays.
- #define TILE_WIDTH 13
- #define TILE_HEIGHT 13
- #define TILE_LIMIT (TILE_WIDTH * TILE_HEIGHT)
+Everything near the bottom is for rendering / Raylib wrangling, and does not really correspond to the actual important data.
+
+Our Configuration detail:
+
+ #define CONFIG_STRING_LIMIT 128
+ typedef struct {
+ u16 resolution_x;
+ u16 resolution_y;
+ u8 fps;
+ u8 ups;
+ char font[CONFIG_STRING_LIMIT];
+ char spritesheet[CONFIG_STRING_LIMIT];
+ char window_name[CONFIG_STRING_LIMIT];
+ u16 spritesheet_scale;
+ /* --- */
+ u8 player_count;
+ u8 map_x;
+ u8 map_y;
+ } config_t;
+
+These configuration details are parameterized in GameInitialize in game.c:
+
+ #define DEFAULT(a, b) ((b) ? (b) : (a))
+ game->config.resolution_x = MAX(200, DEFAULT(600, game->config.resolution_x));
+ game->config.resolution_y = game->config.resolution_x+ FONT_SIZE;
+ game->config.fps = MAX(1, DEFAULT(60, game->config.fps));
+ game->config.ups = MAX(1, DEFAULT(30, game->config.ups));
+ if (!*game->config.font)
+ { strlcpy(game->config.font, "fonts/Atkinson/mono/AtkinsonHyperlegibleMono-Bold.otf", CONFIG_STRING_LIMIT); }
+ if (!*game->config.spritesheet)
+ { strlcpy(game->config.spritesheet, "assets/simple.png", CONFIG_STRING_LIMIT); }
+ game->config.spritesheet_scale = DEFAULT(128, game->config.spritesheet_scale);
+ game->config.map_x = MAX(5, DEFAULT(13, game->config.map_x));
+ game->config.map_y = MAX(5, DEFAULT(13, game->config.map_y));
+ game->config.player_count = CLAMP(DEFAULT(4, game->config.player_count), 1, 4);
+ if (!*game->config.window_name)
+ { strlcpy(game->config.window_name, "Unset Window Name, lol lmao", CONFIG_STRING_LIMIT); }
+ #undef DEFAULT
+
+This ensures a rational set of possible values allowing for finite flexibility in our finite memory space.
+
+ tiles_t tiles;
+ players_t players;
+ bombs_t bombs;
+ enemies_t enemies;
+
+The above should be thought of as _subgroups_, instead of as structures. These structures do not really exist in terms of the program.
+
+We'll start from the top, being the most verbose and critical:
+
+ /* highly dependent on atlas definition in game.c */
+ enum atlas {
+ RENDER_UNBREAKABLE = 0,
+ RENDER_BREAKABLE,
- enum {
- POWERUP_NONE = 0,
- POWERUP_BOMB,
- POWERUP_POWER,
- POWERUP_SPEED,
- // These will probably never be negative:
- POWERUP_PIERCE,
- POWERUP_KICK,
- POWERUP_THROW,
- POWERUP_BOUNCE,
- // Curse is something I've slightly read up on, but it seems to be timer based.
- // POWERUP_CURSE, // no plan to implement.
+ RENDER_EXPLOSION_START,
+ RENDER_EXPLOSION_END,
+
+ RENDER_POWERUP_BOMB,
+ RENDER_POWERUP_POWER,
+ RENDER_POWERUP_SPEED,
+ RENDER_POWERUP_PIERCE,
+ RENDER_POWERUP_KICK,
+ RENDER_POWERUP_THROW,
+ RENDER_POWERUP_BOUNCE,
+ RENDER_POWERUP_CURSE,
+
+ RENDER_PLAYER_RIGHT,
+ RENDER_PLAYER_LEFT,
+ RENDER_PLAYER_UP,
+ RENDER_PLAYER_DOWN,
+
+ RENDER_BOMB_0,
+ RENDER_BOMB_1,
+ RENDER_BOMB_2,
+ RENDER_BOMB_3,
+
+ RENDER_ENEMY_RIGHT,
+ RENDER_ENEMY_LEFT,
+ RENDER_ENEMY_UP,
+ RENDER_ENEMY_DOWN,
};
+
+The above serves as our direct mapping onto our Atlas. This is not very useful past being a set of useful offsets and laying out the data. While we could forgo the following and simply have a 5 bit texture, instead this explicit bitfielding is easier:
+
+ typedef struct {
+ u8 texture : 1; // 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
+ // 3 bits left for extensions.
+ } tile_data_t;
+
+This covers edge cases like enemies which emit damage and directly encode powerups.
+
+ static const tile_data_t impassable_tile = (tile_data_t) {.passable = 0};
+ static const tile_data_t impassable_wall = (tile_data_t) {.passable = 0, .texture = 1};
+ static const tile_data_t breakable_wall = (tile_data_t) {.passable = 0, .texture = 1, .breakable = 1};
+ static const tile_data_t passable_tile = (tile_data_t) {.passable = 1};
+ static const tile_data_t explosive_tile = (tile_data_t) {.passable = 1, .explosive = 1, .lethal = 1, .texture = 0};
+This is a not very useful list that provides short presents. This, in my mind, is the only valid use of const.
+
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;
- 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_LIMIT];
- u8 color[TILE_LIMIT];
+ tile_data_t state[TILE_LENGTH_LIMIT][TILE_LENGTH_LIMIT];
+ u8 color;
} tiles_t;
-
-We will track fundamental interactions here. Powerups are grouped in per their simplicity.
+
+Our tile system is very simple. The color is seeded at Init-time.
#define PLAYER_LIMIT (1<<2)
+ enum {
+ RIGHT, LEFT, UP, DOWN
+ };
+
typedef struct {
- // for smooth movement, rounding is used for tile checks.
- f32 x[PLAYER_LIMIT], y[PLAYER_LIMIT];
- 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;
- // 12 bits left for extensions.
- };
- } state[PLAYER_LIMIT];
- u8 color[PLAYER_LIMIT];
+ 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; // no intent to implement
+ u8 throw : 1; // no intent to implement
+ u8 bounce : 1; // no intent to implement
+ u8 alive : 1;
+ u8 direction : 2; // right left up down
+ u8 moving : 1;
+ // 10 bits left for extensions.
+ } player_data_t;
+
+ typedef struct {
+ i16 x[PLAYER_LIMIT] aligned;
+ i16 y[PLAYER_LIMIT] aligned;
+ player_data_t state[PLAYER_LIMIT] aligned;
+ u8 color[PLAYER_LIMIT] aligned;
} players_t;
+
+Need I say more?
-The player will donate state to each bomb they place. The movement is meant to match the grid as best as possible and should set to the nearest whole number.
-One update means one tile of movement, so we can reasonably set the updates per second in relation to that.
-The framerate being a higher value should result in a visually consistent style.
-
- #define BOMB_LIMIT (PLAYER_LIMIT*(1<<4))
+ #define BOMB_LIMIT (1<<4)
typedef struct {
- f32 x[BOMB_LIMIT], y[BOMB_LIMIT];
- union {
- u16 _;
- struct {
u8 power : 4; // < MAX(TILE_WIDTH, TILE_HEIGHT)
u8 pierce : 1;
- u8 bounce : 1;
+ u8 bounce : 1; // no intent to implement
// 10 bits left for extensions.
- };
- } state[BOMB_LIMIT];
- u16 timer[BOMB_LIMIT]; // updates until explosion.
- u8 color[BOMB_LIMIT], color_flash[BOMB_LIMIT];
+ } bomb_data_t;
+
+ typedef struct {
+ i16 x[PLAYER_LIMIT][BOMB_LIMIT] aligned;
+ i16 y[PLAYER_LIMIT][BOMB_LIMIT] aligned;
+ bomb_data_t state[PLAYER_LIMIT][BOMB_LIMIT] aligned;
+ u16 timer[PLAYER_LIMIT][BOMB_LIMIT] aligned; // updates until explosion.
+ u8 color[2] aligned;
} bombs_t;
-Bombs are pretty simple. After they explode they lag behind.
-The actual explosion is timed statically, and each animation frame is a certain time.
-We can assume frames are interpolated and UPS is low.
+Bombs behave very primitively, inheriting everything and being excluded in every regard from everything else. This is so that they're isolated for further improvements, even though that'll most likely never occur.
#define ENEMY_LIMIT (1<<4)
@@ -122,26 +199,21 @@ We can assume frames are interpolated and UPS is low.
MOVEMENT_VERTICAL,
MOVEMENT_HORIZONTAL,
MOVEMENT_RANDOM,
+ MOVEMENT_LAST,
};
typedef struct {
- f32 x[ENEMY_LIMIT], y[ENEMY_LIMIT];
- u8 movement[ENEMY_LIMIT];
+ i16 x[ENEMY_LIMIT] aligned;
+ i16 y[ENEMY_LIMIT] aligned;
+ u8 movement[ENEMY_LIMIT] aligned;
} enemies_t;
-Enemies are primitive. These set the tile they are on as lethal.
-We can assume they have the same base movement speed.
+Enemies are unimplemented and trivial, walking in a certain pattern in a cleared area.
#### Loading
-We want to load a bunch of information at the start and it's trivial with or without Raylib.
-
-The important directories/files are:
-assets/(spritesheet).png
-fonts/(fontset)/
-configuration.txt <- omitted for simplicity.
-
-We'll look for the assets for tiles, bombs, explosion frames, & players.
+Loading is trivial, we want a single font and a single png specified at runtime.
+this is specified in game->conf.
#### Networking
diff --git a/source/all.h b/source/all.h
index 5aa6fd1..f98943d 100644
--- a/source/all.h
+++ b/source/all.h
@@ -41,7 +41,7 @@
#define TEXTURE_LIMIT (3*8)
-/* Spritesheets will be (128*4x128*6)
+/* Spritesheets will be (N*4xN*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 ] |
@@ -50,65 +50,74 @@
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
+enum powerup {
+ POWERUP_BOMB = 1,
+ POWERUP_POWER,
+ POWERUP_SPEED,
+ POWERUP_PIERCE,
+ POWERUP_KICK,
+ POWERUP_THROW,
+ POWERUP_BOUNCE,
+ POWERUP_CURSE,
};
-#define TILE_LENGTH_LIMIT 21
+/* highly dependent on atlas definition in game.c */
+enum atlas {
+ RENDER_UNBREAKABLE = 0,
+ RENDER_BREAKABLE,
+
+ RENDER_EXPLOSION_START,
+ RENDER_EXPLOSION_END,
+
+ RENDER_POWERUP_BOMB,
+ RENDER_POWERUP_POWER,
+ RENDER_POWERUP_SPEED,
+ RENDER_POWERUP_PIERCE,
+ RENDER_POWERUP_KICK,
+ RENDER_POWERUP_THROW,
+ RENDER_POWERUP_BOUNCE,
+ RENDER_POWERUP_CURSE,
+
+ RENDER_PLAYER_RIGHT,
+ RENDER_PLAYER_LEFT,
+ RENDER_PLAYER_UP,
+ RENDER_PLAYER_DOWN,
+
+ RENDER_BOMB_0,
+ RENDER_BOMB_1,
+ RENDER_BOMB_2,
+ RENDER_BOMB_3,
+
+ RENDER_ENEMY_RIGHT,
+ RENDER_ENEMY_LEFT,
+ RENDER_ENEMY_UP,
+ RENDER_ENEMY_DOWN,
+};
+
+typedef struct {
+ u8 texture : 1; // 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
+ // 3 bits left for extensions.
+} tile_data_t;
+
+static const tile_data_t impassable_tile = (tile_data_t) {.passable = 0};
+static const tile_data_t impassable_wall = (tile_data_t) {.passable = 0, .texture = 1};
+static const tile_data_t breakable_wall = (tile_data_t) {.passable = 0, .texture = 1, .breakable = 1};
+static const tile_data_t passable_tile = (tile_data_t) {.passable = 1};
+static const tile_data_t explosive_tile = (tile_data_t) {.passable = 1, .explosive = 1, .lethal = 1, .texture = 0};
+
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] aligned;
+ tile_data_t state[TILE_LENGTH_LIMIT][TILE_LENGTH_LIMIT] aligned;
u8 color aligned;
- Rectangle wall[2] aligned;
- Rectangle explosion[2] aligned;
- Rectangle powerup[8] aligned;
} tiles_t;
#define PLAYER_LIMIT (1<<2)
@@ -116,66 +125,59 @@ typedef struct {
enum {
RIGHT, LEFT, UP, DOWN
};
-
+
+typedef 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; // no intent to implement
+ u8 throw : 1; // no intent to implement
+ u8 bounce : 1; // no intent to implement
+ u8 alive : 1;
+ u8 direction : 2; // right left up down
+ u8 moving : 1;
+ // 10 bits left for extensions.
+} player_data_t;
+
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;
+ player_data_t state[PLAYER_LIMIT] aligned;
u8 color[PLAYER_LIMIT] aligned;
- Rectangle player[4] aligned;
} players_t;
#define BOMB_LIMIT (1<<4)
-
+
typedef struct {
- 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;
+ u8 bounce : 1; // no intent to implement
// 10 bits left for extensions.
- };
- } state[PLAYER_LIMIT][BOMB_LIMIT] aligned;
+} bomb_data_t;
+
+typedef struct {
+ i16 x[PLAYER_LIMIT][BOMB_LIMIT] aligned;
+ i16 y[PLAYER_LIMIT][BOMB_LIMIT] aligned;
+ bomb_data_t 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] aligned;
} enemies_t;
#define CONFIG_STRING_LIMIT 128
@@ -200,20 +202,21 @@ typedef struct {
bombs_t bombs aligned;
enemies_t enemies aligned;
config_t config aligned;
+ u16 time_limit aligned;
+ u8 client aligned;
Font font aligned;
-
+ Rectangle atlas[4*6] aligned;
Texture spritesheet aligned;
Camera2D camera aligned;
- u16 time_limit aligned;
- u8 client aligned;
} game_t;
/* game.c */
+void GameInitialize(game_t * game);
+void GameDeinitialize(game_t * game);
void GameStart(config_t config);
void GameResize(game_t * game);
-void GameReinitialize(game_t * game);
/* gamemode.c */
diff --git a/source/game.c b/source/game.c
index d98b235..943cf96 100644
--- a/source/game.c
+++ b/source/game.c
@@ -1,15 +1,8 @@
#include "all.h"
-static void GameInitialize(game_t * game);
-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 GameReinitialize(game_t * game) {
- GameDeinitialize(game);
- GameInitialize(game);
-}
-
void GameStart(config_t config) {
_Alignas(64) game_t game[1] = {0};
game->config = config;
@@ -40,7 +33,7 @@ void GameResize(game_t * game) {
}
}
-static void GameInitialize(game_t * game) {
+void GameInitialize(game_t * game) {
/* Strict parameters */
#define DEFAULT(a, b) ((b) ? (b) : (a))
game->config.resolution_x = MAX(200, DEFAULT(600, game->config.resolution_x));
@@ -59,58 +52,47 @@ static void GameInitialize(game_t * game) {
{ strlcpy(game->config.window_name, "Unset Window Name, lol lmao", CONFIG_STRING_LIMIT); }
#undef DEFAULT
+
{
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 atlas[4*6] =
+ /* walls : group 0 */
+ {(Rectangle){ 0, 0, t, t}, /* unbreakable */
+ (Rectangle){t - 1, 0, t, t}, /* breakable */
+ /* explosions : group 1 */
+ (Rectangle){t * 2 - 1, 0, t, t},
+ (Rectangle){t * 3 - 1, 0, t, t},
+ /* powerup : 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 * 3 - 1, t * 2, t, t},
+ /* player : 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 * 3 - 1, t * 3, t, t},
+ /* bomb : 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 * 3 - 1, t * 4, t, t},
+ /* enemy : 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));
+ memcpy(game->atlas, atlas, sizeof(atlas));
}
MultiPlayer(game);
- 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->tiles.color = (rand() % 2 + 2) | ((rand() % 2 + 2) << 2) | ((rand() % 2 + 2) << 4) | GAME_OPAQUE;
game->bombs.color[0] = GAME_WHITE | GAME_OPAQUE;
game->bombs.color[1] = GAME_RED | GAME_OPAQUE;
@@ -125,7 +107,7 @@ static void GameInitialize(game_t * game) {
ClearWindowState(FLAG_WINDOW_HIDDEN);
}
-static void GameDeinitialize(game_t * game) {
+void GameDeinitialize(game_t * game) {
UnloadTexture(game->spritesheet);
if (GetFontDefault().texture.id != game->font.texture.id) { UnloadFont(game->font); }
RaylibDeinitialize();
@@ -186,10 +168,10 @@ static void GameLoop(game_t * game) {
StepStart(print);
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;
StepStop(print);
@@ -204,3 +186,4 @@ static void GameReport(game_t * game, f32 fps, f32 ups, u32 total_fps, u32 total
fps, ups, total_fps, total_ups);
#endif
}
+
diff --git a/source/gamemode.c b/source/gamemode.c
index d548773..6a09b08 100644
--- a/source/gamemode.c
+++ b/source/gamemode.c
@@ -1,33 +1,84 @@
#include "all.h"
-void MultiPlayer(game_t * game) {
+static void MapPrint(game_t * game) {
u8 width = game->config.map_x;
u8 height = game->config.map_y;
- int i, j;
+ printf("texture, explosive, breakable, lethal, pickup\n");
+ for (int i = 0; i < width; ++i) {
+ for (int j = 0; j < height; ++j) {
+ tile_data_t * tile = &game->tiles.state[i][j];
+ printf("%2d:%1d:%1d:%1d:%1d ", tile->texture, tile->explosive, tile->breakable, tile->lethal, tile->pickup);
+ }
+ printf("\n");
+ }
+}
- for (i = 0; i < PLAYER_LIMIT; ++i) {
- for (j = 0; j < BOMB_LIMIT; ++j) {
+static void GamemodeReset(game_t * game) {
+ for (int i = 0; i < PLAYER_LIMIT; ++i) {
+ for (int j = 0; j < BOMB_LIMIT; ++j) {
game->bombs.timer[i][j] = 0;
}
}
+ bzero(game->players.state, sizeof(*game->players.state) * PLAYER_LIMIT);
+}
+
+static void PlayerPlaceCorners(game_t * game, u8 * color, size_t color_count) {
+ u8 width = game->config.map_x;
+ u8 height = game->config.map_y;
+ float player_x[4] =
+ {0, (width-1), (width-1), 0 };
+ float player_y[4] =
+ {0, (height-1), 0, (height-1)};
+
+ for (int i = 0; i < MIN(game->config.player_count, PLAYER_LIMIT); ++i) {
+ game->players.x[i] = player_x[i % color_count];
+ game->players.y[i] = player_y[i % color_count];
+ game->players.color[i] = color[i % color_count];
+ }
+}
+
+static void MapClearCorners(game_t * game) {
+ /* areas that must be passible nothings */
+ u8 width = game->config.map_x;
+ u8 height = game->config.map_y;
+
+ 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 (int i = 0; i < MIN((game->config.player_count * 3), 12); ++i) {
+ game->tiles.state[offset_x[i]][offset_y[i]] = passable_tile;
+ }
+}
+
+void MultiPlayer(game_t * game) {
+ u8 width = game->config.map_x;
+ u8 height = game->config.map_y;
+ int i, j;
+
+ GamemodeReset(game);
+
for (i = 0; i < width; ++i) {
for (j = 0; j < height; ++j) {
- game->tiles.state[i][j]._ = rand() % 10 ? IMPASSIBLE_BREAKABLE_WALL : PASSIBLE_NOTHING;
- /* game->tiles.state[i][j]._ |= rand() % 3 ? 0 : POWERUP; */
+ game->tiles.state[i][j] = rand() % 10 ? breakable_wall : passable_tile;
+ if (rand() % 3 == 0)
+ {
+ game->tiles.state[i][j].pickup = rand() % 5 ? POWERUP_POWER : POWERUP_BOMB;
+ } else if (rand() % 10 == 0) {
+ game->tiles.state[i][j].pickup = POWERUP_PIERCE;
+ }
}
}
for (i = 1; i < width; i += 2) {
for (j = 1; j < height; j += 2) {
- game->tiles.state[i][j]._ = IMPASSIBLE_WALL;
+ game->tiles.state[i][j] = impassable_wall;
}
}
- bzero(game->players.state, sizeof(*game->players.state) * PLAYER_LIMIT);
-
for (i = 0; i < MIN(PLAYER_LIMIT, game->config.player_count); ++i) {
- bzero(&game->players.state[i]._, sizeof(game->players.state[i]._));
game->players.state[i].bomb_limit = 1;
game->players.state[i].power = 2;
game->players.state[i].speed = 2;
@@ -37,11 +88,6 @@ void MultiPlayer(game_t * game) {
game->time_limit = game->config.ups * 60 * 3;
- 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,
@@ -49,26 +95,7 @@ void MultiPlayer(game_t * game) {
GAME_BLUE | GAME_OPAQUE,
};
- for (i = 0; i < MIN(game->config.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((game->config.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");
- }
+ PlayerPlaceCorners(game, color, 4);
+ MapClearCorners(game);
+ MapPrint(game);
}
diff --git a/source/main.c b/source/main.c
index 70ab419..bc55b2d 100644
--- a/source/main.c
+++ b/source/main.c
@@ -1,10 +1,66 @@
#include "all.h"
+#include <options.h>
+
+#define arg if (++arguments, !--count) { goto help; } else
+config_t Arguments(int count, char ** arguments) {
+ config_t config = {0};
+ while (++arguments, --count > 0) {
+ while (**arguments == '-') { ++*arguments; }
+ struct options * option = options_lookup(*arguments,strlen(*arguments));
+ if (!option) {
+ printf("Unknown option '%s', try 'help'\n", *arguments);
+ goto abort;
+ }
+ switch (option->number) {
+ case OPTION_HELP: {
+ help:
+ printf("help/h/? -- -- This.\n"
+ "resolution -- <x> -- The on-display resolution squared\n"
+ "fps -- <x> -- Framerate\n"
+ "ups -- <x> -- Updates\n"
+ "font -- </path/to> -- Glorious Font\n"
+ "spritesheet -- </path/to> -- The spritesheet for displaying all of reality\n"
+ "spritesheet_scale -- <x> -- The square scale of the above\n"
+ "players -- <x> -- N players\n"
+ "map/map_size -- <x y> -- the in-game map size\n"
+ );
+ exit(0); }
+ case OPTION_RESOLUTION: {
+ arg { config.resolution_x = atoi(*arguments); }
+ break; }
+ case OPTION_FPS: {
+ arg { config.fps = atoi(*arguments); }
+ break; }
+ case OPTION_UPS: {
+ arg { config.ups = atoi(*arguments); }
+ break; }
+ case OPTION_PLAYER_COUNT: {
+ arg { config.player_count = atoi(*arguments); }
+ break; }
+ case OPTION_MAP_SIZE: {
+ arg { config.map_x = atoi(*arguments); }
+ arg { config.map_y = atoi(*arguments); }
+ break; }
+ case OPTION_SPRITESHEET: {
+ arg { strlcpy(config.spritesheet, *arguments, CONFIG_STRING_LIMIT); }
+ break; }
+ case OPTION_SPRITESHEET_SCALE: {
+ arg { config.spritesheet_scale = atoi(*arguments); }
+ break; }
+ case OPTION_FONT: {
+ arg { strlcpy(config.font, *arguments, CONFIG_STRING_LIMIT); }
+ break; }
+ }
+ }
+ return config;
+abort:
+ exit(1);
+}
int Main(int count, char ** arguments)
{
- (void)count;
char * window_name = arguments[0];
- config_t config = (config_t){0};
+ config_t config = Arguments(count, arguments);
char * p = strchr(window_name, '/');
strlcpy(config.window_name, p ? p+1 : window_name, CONFIG_STRING_LIMIT);
srand(time(NULL));
diff --git a/source/options.gperf b/source/options.gperf
new file mode 100644
index 0000000..5d07a6f
--- /dev/null
+++ b/source/options.gperf
@@ -0,0 +1,28 @@
+%{
+enum {
+OPTION_NONE=0,
+OPTION_HELP,
+OPTION_RESOLUTION,
+OPTION_FPS,
+OPTION_UPS,
+OPTION_FONT,
+OPTION_SPRITESHEET,
+OPTION_SPRITESHEET_SCALE,
+OPTION_PLAYER_COUNT,
+OPTION_MAP_SIZE,
+};
+%}
+struct options { char * name; int number; };
+%%
+help, OPTION_HELP
+h, OPTION_HELP
+?, OPTION_HELP
+resolution, OPTION_RESOLUTION
+fps, OPTION_FPS
+ups, OPTION_UPS
+font, OPTION_FONT
+spritesheet, OPTION_SPRITESHEET
+spritesheet_scale, OPTION_SPRITESHEET_SCALE
+players, OPTION_PLAYER_COUNT
+map, OPTION_MAP_SIZE
+map_size, OPTION_MAP_SIZE
diff --git a/source/render.c b/source/render.c
index 5f5fb25..3ee28e2 100644
--- a/source/render.c
+++ b/source/render.c
@@ -11,11 +11,7 @@ void Render(game_t * game, f64 interpolation) {
BeginDrawing();
-#ifndef NDEBUG
- ClearBackground(BLACK);
-#else
ClearBackground(COLOR_TO_RAYLIB(game->tiles.color));
-#endif
BeginMode2D(game->camera);
RenderTiles(game);
RenderBombs(game);
@@ -27,30 +23,26 @@ void Render(game_t * game, f64 interpolation) {
SwapScreenBuffer();
}
+always_inline void AtlasDraw(game_t * game, u16 atlas_number, u8 color, int i, int j) {
+ DrawTextureRec(
+ game->spritesheet,
+ game->atlas[atlas_number],
+ (Vector2) {i*game->config.spritesheet_scale, j*game->config.spritesheet_scale},
+ COLOR_TO_RAYLIB(color));
+}
+
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) {
- #ifndef NDEBUG
- 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));
+ tile_data_t * tile = &game->tiles.state[i][j];
+ if (!tile->passable) {
+ if (tile->texture)
+ { AtlasDraw(game, tile->breakable ? 1 : 0, game->tiles.color, i, j); }
+ } else if (tile->pickup) {
+ AtlasDraw(game, RENDER_POWERUP_BOMB + tile->pickup - 1, GAME_WHITE | GAME_OPAQUE, i, j);
}
- #endif
-
- 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);
+ if (tile->explosive) {
+ AtlasDraw(game, RENDER_EXPLOSION_START + tile->texture, game->bombs.color[0], i, j);
}
}
}
@@ -59,11 +51,8 @@ void RenderTiles(game_t * game) {
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]));
+ AtlasDraw(game, RENDER_PLAYER_RIGHT + game->players.state[i].direction,
+ game->players.color[i], game->players.x[i], game->players.y[i]);
}
}
}
@@ -72,11 +61,9 @@ 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]));
+ AtlasDraw(game, RENDER_BOMB_0 + game->bombs.timer[i][j] % 4,
+ game->bombs.color[game->bombs.timer[i][j]%2],
+ game->bombs.x[i][j], game->bombs.y[i][j]);
}
}
}
diff --git a/source/update.c b/source/update.c
index 610252b..766073e 100644
--- a/source/update.c
+++ b/source/update.c
@@ -6,8 +6,11 @@ static void PlaceBomb(game_t * game);
static int CheckInputDebug(game_t * game);
static void CheckInputPlaceBomb(game_t * game);
static void CheckInputMovement(game_t * game);
-static void UpdatePlayer(game_t * game);
+static void UpdateClientPlayer(game_t * game);
+static void UpdatePlayers(game_t * game);
static void UpdateBomb(game_t * game);
+static void UpdateTimeLimit(game_t * game);
+static void CheckWin(game_t * game);
i16 Update(game_t * game, timespec_t now) {
(void) now;
@@ -16,11 +19,14 @@ i16 Update(game_t * game, timespec_t now) {
if (CheckInputDebug(game)) { return 1; }
CheckInputMovement(game);
CheckInputPlaceBomb(game);
- UpdatePlayer(game);
+ UpdateClientPlayer(game);
+ UpdatePlayers(game);
UpdateBomb(game);
CheckKilled(game);
UpdateExplosions(game);
- return 0;
+ CheckWin(game);
+ UpdateTimeLimit(game);
+return 0;
}
static void CheckKilled(game_t * game) {
@@ -31,28 +37,40 @@ static void CheckKilled(game_t * game) {
}
}
-
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
- && game->tiles.state[i][j]._ <= PASSIBLE_EXPLOSIVE_LETHAL_END) {
- if (game->tiles.state[i][j]._ == PASSIBLE_EXPLOSIVE_LETHAL_END)
- { game->tiles.state[i][j]._ = PASSIBLE_NOTHING; }
+ #define EXPLOSIVE_START 0
+ #define EXPLOSIVE_END 1
+ tile_data_t * tile = &game->tiles.state[i][j];
+ if (tile->explosive
+ && tile->texture >= EXPLOSIVE_START
+ && tile->texture <= EXPLOSIVE_END) {
+ if (tile->texture == EXPLOSIVE_END)
+ {
+ tile->texture = 0;
+ tile->explosive = 0;
+ tile->passable = 1;
+ tile->lethal = 0;
+ if (!tile->breakable) {
+ tile->pickup = 0;
+ }
+ tile->breakable = 0;
+ }
else
- { ++game->tiles.state[i][j]._; }
+ { ++tile->texture; }
}
}
}
}
static void PlaceBomb(game_t * game) {
- auto state = &game->players.state[game->client];
+ player_data_t * 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->players.y[game->client]] = impassable_tile;
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;
@@ -67,7 +85,7 @@ static int CheckInputDebug(game_t * game) {
switch (GetKeyPressed()) {
case KEY_ESCAPE: return 1;
#ifndef NDEBUG
- case KEY_F1: GameReinitialize(game); break;
+ case KEY_F1: GameDeinitialize(game); GameInitialize(game); break;
case KEY_R: MultiPlayer(game); break;
case KEY_T: if (game->client < 3) game->client++; break;
case KEY_G: if (game->client != 0) game->client--; break;
@@ -77,7 +95,7 @@ static int CheckInputDebug(game_t * game) {
}
static void CheckInputPlaceBomb(game_t * game) {
- auto state = &game->players.state[game->client];
+ player_data_t * 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))
@@ -87,7 +105,7 @@ static void CheckInputPlaceBomb(game_t * game) {
static void CheckInputMovement(game_t * game) {
- auto state = &game->players.state[game->client];
+ player_data_t * state = &game->players.state[game->client];
state->moving = 0;
if (IsKeyPressed(KEY_UP) || IsKeyPressed(KEY_EIGHT) || IsKeyPressed(KEY_I) || IsKeyPressed(KEY_W))
@@ -112,63 +130,97 @@ static void CheckInputMovement(game_t * game) {
/* { state->moving = 0; } */
}
-static void UpdatePlayer(game_t * game) {
- auto state = &game->players.state[game->client];
+
+static void UpdatePlayers(game_t * game) {
+ for (int i = 0; i < game->config.player_count; ++i) {
+ player_data_t * state = &game->players.state[i];
+ i16 * player_x = &game->players.x[i];
+ i16 * player_y = &game->players.y[i];
+ if (game->tiles.state[*player_x][*player_y].pickup) {
+ switch (game->tiles.state[*player_x][*player_y].pickup) {
+ case POWERUP_BOMB: if (state->bomb_limit < 3) { ++state->bomb_limit; } break;
+ case POWERUP_POWER: if (state->power < 15) { ++state->power; } break;
+ case POWERUP_PIERCE: state->pierce = 1; break;
+ }
+ game->tiles.state[*player_x][*player_y].pickup = 0;
+ }
+ }
+}
+
+static void UpdateClientPlayer(game_t * game) {
+ player_data_t * 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)
+ if (delta_x
+ && *player_x + delta_x >= 0
+ && *player_x + delta_x < game->config.map_x
+ && game->tiles.state[*player_x + delta_x][*player_y].passable)
{ *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 < game->config.map_y
+ && game->tiles.state[*player_x][*player_y + delta_y].passable)
{ *player_y += delta_y; }
}
static void UpdateBomb(game_t * game) {
- size_t i, j, k;
ssize_t
offset_x[4] = {-1, 1, 0, 0},
offset_y[4] = { 0, 0, -1, 1};
- for (i = 0; i < PLAYER_LIMIT; ++i) {
- for (j = 0; j < BOMB_LIMIT; ++j) {
+ for (int i = 0; i < PLAYER_LIMIT; ++i) {
+ for (int j = 0; j < BOMB_LIMIT; ++j) {
if (game->bombs.timer[i][j]) {
--game->bombs.timer[i][j];
if (!game->bombs.timer[i][j]) {
- ssize_t block[4] = {0};
- for (k = 0; k < 4 * game->players.state[i].power; ++k) {
- if (block[k%4]) { continue; }
+ ssize_t block[4] = {0};
+ i16 x = game->bombs.x[i][j], y = game->bombs.y[i][j];
+ for (int k = 0; k < 4 * game->players.state[i].power; ++k) {
+ if (block[k%4]) { continue; }
i16
rx = game->bombs.x[i][j] + offset_x[k%4] * ((k / 4) + 1),
ry = game->bombs.y[i][j] + offset_y[k%4] * ((k / 4) + 1);
if (rx < game->config.map_x && rx >= 0
- && ry < game->config.map_y && ry >= 0)
- {
- if (game->tiles.state[rx][ry]._ & PASSIBLE) {
- game->tiles.state[rx][ry]._ = PASSIBLE_EXPLOSIVE_LETHAL;
- } else if (game->tiles.state[rx][ry]._ == IMPASSIBLE_BREAKABLE_WALL) {
- game->tiles.state[rx][ry]._ = PASSIBLE_EXPLOSIVE_LETHAL;
- if (!game->players.state[i].pierce) {
- block[k%4] = 1;
- }
- } else {
- block[k%4] = 1;
- }
- }
+ && ry < game->config.map_y && ry >= 0) {
+ printf("rx %d ry %d\n", rx, ry);
+ tile_data_t * tile = &game->tiles.state[rx][ry];
+ if (tile->passable) {
+ tile->explosive = 1;
+ tile->lethal = 1;
+ } else if (tile->breakable) {
+ tile->explosive = 1;
+ tile->lethal = 1;
+ if (!game->players.state[i].pierce) {
+ block[k%4] = 1;
+ }
+ } else {
+ block[k%4] = 1;
+ }
+ }
}
- game->tiles.state[game->bombs.x[i][j]][game->bombs.y[i][j]]._ = PASSIBLE_EXPLOSIVE_LETHAL;
+ game->tiles.state[x][y].explosive = 1;
+ game->tiles.state[x][y].lethal = 1;
--game->players.state[i].bomb_count;
}
}
}
}
}
+
+static void CheckWin(game_t * game) {
+ int sum = 0;
+ for (int i = 0; i < game->config.player_count; ++i) {
+ sum += game->players.state[i].alive;
+ }
+ if (sum <= 1) { MultiPlayer(game);}
+}
+
+static void UpdateTimeLimit(game_t * game) {
+ --game->time_limit;
+ if (!game->time_limit) { MultiPlayer(game); }
+}