From 428b68f791edcb89c811c9aca5dedbf7ec5d1335 Mon Sep 17 00:00:00 2001 From: Emil Williams Date: Tue, 17 Feb 2026 06:47:32 +0000 Subject: revised tiles, powerups, timer + basic gameplay --- source/all.h | 183 ++++++++++++++++++++++++++------------------------- source/game.c | 73 ++++++++------------ source/gamemode.c | 101 +++++++++++++++++----------- source/main.c | 60 ++++++++++++++++- source/options.gperf | 28 ++++++++ source/render.c | 55 ++++++---------- source/update.c | 138 ++++++++++++++++++++++++++------------ 7 files changed, 387 insertions(+), 251 deletions(-) create mode 100644 source/options.gperf (limited to 'source') 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 + +#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 -- -- The on-display resolution squared\n" + "fps -- -- Framerate\n" + "ups -- -- Updates\n" + "font -- -- Glorious Font\n" + "spritesheet -- -- The spritesheet for displaying all of reality\n" + "spritesheet_scale -- -- The square scale of the above\n" + "players -- -- N players\n" + "map/map_size -- -- 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); } +} -- cgit v1.2.3