From 409d9027ef8c65844b2a81653eeb2ea30807f71a Mon Sep 17 00:00:00 2001 From: Klemen Plestenjak Date: Mon, 29 Jan 2024 00:10:31 +0100 Subject: [PATCH] Working HTML5 version --- CMakeLists.txt | 2 +- engine/CMakeLists.txt | 3 +- engine/breeze.h | 1 - game/ai_actions.c | 4 +- game/ai_actions.h | 4 +- game/buildings.c | 2 +- game/constants.h | 4 ++ game/main.c | 112 ++++++++++++++++++++++++++++++++++-------- game/systems/s_ui.c | 2 + game/utils.h | 4 ++ settings.ini | Bin 0 -> 16 bytes 11 files changed, 108 insertions(+), 30 deletions(-) create mode 100644 settings.ini diff --git a/CMakeLists.txt b/CMakeLists.txt index 5461553..0cc5280 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,5 +50,5 @@ target_link_libraries(PixelDefense LINK_PRIVATE Breeze) if (EMSCRIPTEN) set_target_properties(PixelDefense PROPERTIES SUFFIX ".html" - LINK_FLAGS " --bind -s WASM=1 -s ALLOW_MEMORY_GROWTH -s STACK_SIZE=2048kb -s ASSERTIONS=2 -s MIN_WEBGL_VERSION=1 --preload-file ../assets -g2 -gseparate-dwarf -gsource-map -s USE_GLFW=3") + LINK_FLAGS "-lidbfs.js -sEXPORTED_FUNCTIONS=_loopPause,_loopResume,_main -sEXTRA_EXPORTED_RUNTIME_METHODS=ccall -sFORCE_FILESYSTEM=1 -sWASM=1 -sSTACK_OVERFLOW_CHECK=2 -sALLOW_MEMORY_GROWTH -sSTACK_SIZE=10MB -sASSERTIONS=2 -sMIN_WEBGL_VERSION=1 --preload-file ../assets -g2 -gseparate-dwarf -gsource-map -sUSE_GLFW=3") endif() diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index ea4b243..23954fd 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -73,7 +73,6 @@ set(BreezeHeaders breeze/util/array.h breeze/util/heap.h - breeze/util/lambda.h breeze/util/object_pool.h breeze/util/spatial_grid.h breeze/util/string.h @@ -111,7 +110,7 @@ target_include_directories(Breeze file(COPY ${BreezeHeaders} DESTINATION "include") -if (${BUILD_BREEZE_TESTS}) +if (${BUILD_BREEZE_TESTS} AND NOT EMSCRIPTEN) MESSAGE(STATUS "Building breeze tests is enabled") add_subdirectory(tests) endif() diff --git a/engine/breeze.h b/engine/breeze.h index 1dc7ca8..c1eead0 100644 --- a/engine/breeze.h +++ b/engine/breeze.h @@ -15,7 +15,6 @@ #include "breeze/util/array.h" #include "breeze/util/heap.h" -#include "breeze/util/lambda.h" #include "breeze/util/object_pool.h" #include "breeze/util/spatial_grid.h" #include "breeze/util/string.h" diff --git a/game/ai_actions.c b/game/ai_actions.c index 49673ef..713b5c6 100644 --- a/game/ai_actions.c +++ b/game/ai_actions.c @@ -194,14 +194,14 @@ BzBTStatus aiDepositRes(AIBlackboard *data, f32 dt) { return BZ_BT_SUCCESS; } -BzBTStatus aiCarryCapacityFull(AIBlackboard *data) { +BzBTStatus aiCarryCapacityFull(AIBlackboard *data, f32 dt) { BZ_ASSERT(ecs_has(ECS, data->entity, Worker)); Worker *worker = ecs_get_mut(ECS, data->entity, Worker); if (worker->carry >= worker->carryCapacity) return BZ_BT_SUCCESS; return BZ_BT_FAIL; } -BzBTStatus aiCarryCapacityEmpty(AIBlackboard *data) { +BzBTStatus aiCarryCapacityEmpty(AIBlackboard *data, f32 dt) { BZ_ASSERT(ecs_has(ECS, data->entity, Worker)); Worker *worker = ecs_get_mut(ECS, data->entity, Worker); if (worker->carry == 0) diff --git a/game/ai_actions.h b/game/ai_actions.h index a5944b6..ff57e08 100644 --- a/game/ai_actions.h +++ b/game/ai_actions.h @@ -37,8 +37,8 @@ BzBTStatus aiFindNextHarvestable(AIBlackboard *data, f32 dt); BzBTStatus aiFindNearestStorage(AIBlackboard *data, f32 dt); BzBTStatus aiHarvestRes(AIBlackboard *data, f32 dt); BzBTStatus aiDepositRes(AIBlackboard *data, f32 dt); -BzBTStatus aiCarryCapacityFull(AIBlackboard *data); -BzBTStatus aiCarryCapacityEmpty(AIBlackboard *data); +BzBTStatus aiCarryCapacityFull(AIBlackboard *data, f32 dt); +BzBTStatus aiCarryCapacityEmpty(AIBlackboard *data, f32 dt); //BzBTStatus aiIsTargetHarvestable(AIBlackboard *data); //BzBTStatus aiIsTargetStorage(AIBlackboard *data); diff --git a/game/buildings.c b/game/buildings.c index b22e245..6121a2c 100644 --- a/game/buildings.c +++ b/game/buildings.c @@ -99,7 +99,7 @@ ecs_entity_t placeBuilding(Game *game, BuildingType type, case BUILDING_WHEAT_1: hasCollision = false; ecs_add_id(ECS, building, Harvestable); - ecs_set(ECS, building, Resource, {RES_FOOD, INFINITY}); + ecs_set(ECS, building, Resource, {RES_FOOD, INT32_MAX}); break; default: break; diff --git a/game/constants.h b/game/constants.h index 479824f..2d8ea07 100644 --- a/game/constants.h +++ b/game/constants.h @@ -13,5 +13,9 @@ typedef enum Player { PLAYER_COUNT } Player; +// defined in main.c + +extern const char *SETTINGS_PATH; + #endif //PIXELDEFENSE_CONSTANTS_H diff --git a/game/main.c b/game/main.c index e07558a..6b3807e 100644 --- a/game/main.c +++ b/game/main.c @@ -17,6 +17,9 @@ #include "pathfinding.h" #include "sounds.h" +// Constants +const char *SETTINGS_PATH = "settings.ini"; +// Constants end ECS_COMPONENT_DECLARE(Game); ECS_COMPONENT_DECLARE(InputState); @@ -32,7 +35,20 @@ void update(float dt, void *userData); void render(float dt, void *userData); void imguiRender(float dt, void *userData); +static bool loopPaused = true; +extern void loopPause(void) { + loopPaused = true; +} +extern void loopResume(void) { + loopPaused = false; + +} + + +#ifdef EMSCRIPTEN +#include +#endif bool bzMain(BzAppDesc *appDesc, int argc, const char **argv) { appDesc->width = 1280; appDesc->height = 720; @@ -48,6 +64,25 @@ bool bzMain(BzAppDesc *appDesc, int argc, const char **argv) { appDesc->userData = NULL; //SetConfigFlags(FLAG_WINDOW_RESIZABLE); +#ifdef EMSCRIPTEN + EM_ASM( + FS.mkdir('/game'); + FS.mount(IDBFS, {}, '/game'); + FS.syncfs(true, function (err) { + console.log("resumed"); + Module.ccall('loopResume', 'void', ['void']); + }); + console.log('/game mounted'); + /* + * Note: FS.syncfs is async so we need to pause the + * main loop until it finishes :/ + */ + Module.ccall('loopPause', 'void', ['void']); + ); + loopPaused = true; +#else + loopPaused = false; +#endif return true; } @@ -63,14 +98,42 @@ int cmpDrawData(const void *a, const void *b) { return cmpVal; } + bool serializeOptions(const char *path, const Options *opts) { + +#ifdef EMSCRIPTEN + char buf[4096]; + snprintf(buf, sizeof(buf), "%s%s", "/game/", path); + path = buf; +#endif + bzLogInfo("write path: %s", path); FILE *f = fopen(path, "w"); + if (!f) return false; size_t numWritten = fwrite(opts, sizeof(*opts), 1, f); fclose(f); +#ifdef EMSCRIPTEN + // Sync FS + EM_ASM( + FS.syncfs(false, function (err) { + }); + ); + +#endif return numWritten == 1; } + bool deserializeOptions(const char *path, Options *optsOut) { +#ifdef EMSCRIPTEN + char buf[4096]; + snprintf(buf, sizeof(buf), "%s%s", "/game/", path); + path = buf; +#endif + bzLogInfo("read path: %s", path); + FILE *f = fopen(path, "r"); + bzLogInfo("start reading"); + if (!f) return false; + bzLogInfo("reading"); Options opts; size_t numRead = fread(&opts, sizeof(opts), 1, f); fclose(f); @@ -143,7 +206,7 @@ bool init(void *userData) { } setScreen(game, SCREEN_MAIN_MENU); - game->stackAlloc = bzStackAllocCreate(10 * 1000 * 1000); // 10 MB + game->stackAlloc = bzStackAllocCreate(1 * 1000 * 1000); // 1 MB // init pools game->pools.pathData = bzObjectPoolCreate(&(BzObjectPoolDesc) { .objectSize = sizeof(PathData), @@ -252,22 +315,12 @@ bool init(void *userData) { loadMap(game, "assets/maps/main_menu_01.tmj"); - game->debug.drawMapColliders = true; - game->debug.drawSpatialGrid = true; - game->debug.drawPath = true; + game->debug.drawMapColliders = false; + game->debug.drawSpatialGrid = false; + game->debug.drawPath = false; game->debug.inspecting = bzArrayCreate(ecs_entity_t, 10); - // Load settings - const char *workDir = GetApplicationDirectory(); - char buf[FILENAME_MAX]; - snprintf(buf, sizeof(buf), "%s%s", workDir, "settings"); - Options opts; - if (deserializeOptions(buf, &opts)) { - game->options = opts; - } else { - game->options = getDefaultOptions(); - } return true; } @@ -277,10 +330,7 @@ void deinit(void *userData) { InputState *input = ecs_singleton_get_mut(ECS, InputState); SoundState *sounds = ecs_singleton_get_mut(ECS, SoundState); - const char *workDir = GetApplicationDirectory(); - char buf[FILENAME_MAX]; - snprintf(buf, sizeof(buf), "%s%s", workDir, "settings"); - serializeOptions(buf, &game->options); + serializeOptions(SETTINGS_PATH, &game->options); unloadMap(game); @@ -320,6 +370,8 @@ void deinit(void *userData) { void update(float dt, void *userData) { + if (loopPaused) + return; BZ_UNUSED(userData); char titleBuf[32]; @@ -329,6 +381,22 @@ void update(float dt, void *userData) { Game *game = ecs_singleton_get_mut(ECS, Game); InputState *input = ecs_singleton_get_mut(ECS, InputState); + { + static bool optsLoaded = false; + if (!optsLoaded) { + // Load settings + const char *path = SETTINGS_PATH; + Options opts; + if (deserializeOptions(path, &opts)) { + game->options = opts; + } else { + game->options = getDefaultOptions(); + } + optsLoaded = true; + } + + } + game->screenPrevFrame = game->screen; if (game->screen != game->nextScreen) game->screen = game->nextScreen; @@ -487,6 +555,8 @@ static void renderGame(Game *game, float dt) { } void render(float dt, void *userData) { + if (loopPaused) + return; BZ_UNUSED(userData); Game *game = ecs_singleton_get_mut(ECS, Game); @@ -600,9 +670,9 @@ void imguiRender(float dt, void *userData) { igSetNextWindowSize((ImVec2){300, 400}, ImGuiCond_FirstUseEver); igBegin("Debug Menu", NULL, 0); - igText("PathData pool available: %llu", bzObjectPoolGetNumFree(game->pools.pathData)); - igText("BTNode pool available: %llu", bzObjectPoolGetNumFree(game->pools.btNode)); - igText("BTNodeState pool available: %llu", bzObjectPoolGetNumFree(game->pools.btNodeState)); + igText("PathData pool available: %lu", bzObjectPoolGetNumFree(game->pools.pathData)); + igText("BTNode pool available: %lu", bzObjectPoolGetNumFree(game->pools.btNode)); + igText("BTNodeState pool available: %lu", bzObjectPoolGetNumFree(game->pools.btNodeState)); const char *inputState = "NONE"; switch (input->state) { case INPUT_NONE: diff --git a/game/systems/s_ui.c b/game/systems/s_ui.c index 7394402..3a7618b 100644 --- a/game/systems/s_ui.c +++ b/game/systems/s_ui.c @@ -5,6 +5,7 @@ #include "../map_init.h" #include "../ui_widgets.h" #include "../buildings.h" +#include "../utils.h" void drawGameUI(Game *game, f32 dt) { // UI @@ -303,6 +304,7 @@ void drawSettingsUI(Game *game, f32 dt) { opts = game->options; } if (uiSettingsButton("Apply")) { + serializeOptions(SETTINGS_PATH, &opts); game->options = opts; setScreen(game, SCREEN_MAIN_MENU); } diff --git a/game/utils.h b/game/utils.h index 3421d5f..9eba572 100644 --- a/game/utils.h +++ b/game/utils.h @@ -18,4 +18,8 @@ static Rectangle getCameraBounds(Camera2D camera) { return bounds; } +// Implemented in main.c +bool serializeOptions(const char *path, const Options *opts); +bool deserializeOptions(const char *path, Options *optsOut); + #endif //PIXELDEFENSE_UTILS_H diff --git a/settings.ini b/settings.ini new file mode 100644 index 0000000000000000000000000000000000000000..5e0c6206e4f6c2f19b0f5e170240002040d564a5 GIT binary patch literal 16 QcmZQz0D%P#5d6Ra01uD?;Q#;t literal 0 HcmV?d00001