diff --git a/source/duke3d/src/game.h b/source/duke3d/src/game.h index dddab807b..0d936727b 100644 --- a/source/duke3d/src/game.h +++ b/source/duke3d/src/game.h @@ -228,6 +228,8 @@ typedef struct { int8_t menutitle_pal, slidebar_palselected, slidebar_paldisabled; + int32_t last_stateless_level, last_stateless_volume; // strictly internal + struct { int32_t AutoAim; int32_t ShowWeapons; diff --git a/source/duke3d/src/player.cpp b/source/duke3d/src/player.cpp index 7bb4b0f54..62ed4d25f 100644 --- a/source/duke3d/src/player.cpp +++ b/source/duke3d/src/player.cpp @@ -5631,3 +5631,91 @@ HORIZONLY:; P_ProcessWeapon(playerNum); } + + +#define SJSON_IMPLEMENT +#include "sjson.h" + +int portableBackupSave(const char *path) +{ + if (!FURY) + return 0; + + char fn[BMAX_PATH]; + + if (G_ModDirSnprintf(fn, sizeof(fn), "%s.ext", path)) + { + return 1; + } + + sjson_context* ctx = sjson_create_context(0, 0, NULL); + if (!ctx) + { + buildprint("Could not create sjson_context\n"); + return 1; + } + + sjson_node* root = sjson_mkobject(ctx); + + sjson_node * players = sjson_mkarray(ctx); + + // sjson_put_string(ctx, root, "map", currentboardfilename); + sjson_put_int(ctx, root, "volume", ud.last_stateless_volume); + sjson_put_int(ctx, root, "level", ud.last_stateless_level); + sjson_put_int(ctx, root, "skill", ud.player_skill); + + for (int TRAVERSE_CONNECT(p)) + { + playerdata_t const * playerData = &g_player[p]; + DukePlayer_t const * ps = playerData->ps; + auto pSprite = (uspritetype const *)&sprite[ps->i]; + + sjson_node * player = sjson_mkobject(ctx); + sjson_append_element(players, player); + sjson_put_int(ctx, player, "extra", pSprite->extra); + + sjson_node * gotweapon = sjson_put_array(ctx, player, "gotweapon"); + for (int w = 0; w < MAX_WEAPONS; ++w) + sjson_append_element(gotweapon, sjson_mkbool(ctx, !!(ps->gotweapon & (1<ammo_amount[w]; + sjson_put_ints(ctx, player, "ammo_amount", ammo_amount, MAX_WEAPONS); + + int inv_amount[GET_MAX]; + for (int i = 0; i < GET_MAX; ++i) + inv_amount[i] = ps->inv_amount[i]; + sjson_put_ints(ctx, player, "inv_amount", inv_amount, GET_MAX); + + sjson_put_int(ctx, player, "curr_weapon", ps->curr_weapon); + sjson_put_int(ctx, player, "subweapon", ps->subweapon); + sjson_put_int(ctx, player, "inven_icon", ps->inven_icon); + } + + sjson_append_member(ctx, root, "players", players); + + char errmsg[256]; + if (!sjson_check(root, errmsg)) + { + buildprint(errmsg, "\n"); + sjson_destroy_context(ctx); + return 1; + } + + char* encoded = sjson_stringify(ctx, root, " "); + + buildvfs_FILE fil = buildvfs_fopen_write(fn); + if (!fil) + { + sjson_destroy_context(ctx); + return 1; + } + + buildvfs_fwrite(encoded, strlen(encoded), 1, fil); + buildvfs_fclose(fil); + + sjson_destroy_context(ctx); + + return 0; +} diff --git a/source/duke3d/src/premap.cpp b/source/duke3d/src/premap.cpp index 13fd25be1..a479de5f4 100644 --- a/source/duke3d/src/premap.cpp +++ b/source/duke3d/src/premap.cpp @@ -1807,6 +1807,23 @@ void G_SetupFilenameBasedMusic(char *nameBuf, const char *fileName, int levelNum realloc_copy(&g_mapInfo[levelNum].musicfn, usermapMusic); } +static void G_CheckIfStateless() +{ + for (bssize_t i = 0; i < (MAXVOLUMES * MAXLEVELS); i++) + { + map_t *const pMapInfo = &g_mapInfo[i]; + if (pMapInfo->savedstate != nullptr) + { + // buildprint("G_CheckIfStateless: no ", ud.volume_number, " ", ud.level_number, "\n"); + return; + } + } + + // buildprint("G_CheckIfStateless: yes ", ud.volume_number, " ", ud.level_number, "\n"); + ud.last_stateless_volume = ud.volume_number; + ud.last_stateless_level = ud.level_number; +} + int G_EnterLevel(int gameMode) { vote_map = vote_episode = voting = -1; @@ -2017,6 +2034,8 @@ int G_EnterLevel(int gameMode) Bmemcpy(currentboardfilename, boardfilename, BMAX_PATH); + G_CheckIfStateless(); + for (int TRAVERSE_CONNECT(i)) { if (!VM_OnEventWithReturn(EVENT_ENTERLEVEL, g_player[i].ps->i, i, 0)) diff --git a/source/duke3d/src/savegame.cpp b/source/duke3d/src/savegame.cpp index ed86ae3ec..4b5f1eaf7 100644 --- a/source/duke3d/src/savegame.cpp +++ b/source/duke3d/src/savegame.cpp @@ -548,6 +548,9 @@ int32_t G_SavePlayer(savebrief_t & sv, bool isAutoSave) VM_OnEvent(EVENT_SAVEGAME, g_player[screenpeek].ps->i, screenpeek); + extern int portableBackupSave(const char *); + portableBackupSave(sv.path); + // SAVE! sv_saveandmakesnapshot(fil, sv.name, 0, 0, 0, 0, isAutoSave); @@ -2297,4 +2300,3 @@ static void postloadplayer(int32_t savegamep) } ////////// END GENERIC SAVING/LOADING SYSTEM ////////// -