Working HTML5 version
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -13,5 +13,9 @@ typedef enum Player {
|
||||
PLAYER_COUNT
|
||||
} Player;
|
||||
|
||||
// defined in main.c
|
||||
|
||||
extern const char *SETTINGS_PATH;
|
||||
|
||||
|
||||
#endif //PIXELDEFENSE_CONSTANTS_H
|
||||
|
||||
112
game/main.c
112
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 <emscripten.h>
|
||||
#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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
BIN
settings.ini
Normal file
BIN
settings.ini
Normal file
Binary file not shown.
Reference in New Issue
Block a user