From af92af22667a855a65102d6a12a798c737bfed67 Mon Sep 17 00:00:00 2001 From: drjaska Date: Sat, 7 May 2022 13:08:47 +0300 Subject: [PATCH] implement savestates currently there's an import dependency issue which disables saving and loading a bunch of things --- .../common/gamemodes/gamemode/ctscup/_mod.inc | 1 + .../common/gamemodes/gamemode/ctscup/_mod.qh | 1 + .../gamemodes/gamemode/ctscup/savestate.qc | 69 +++++++++++++++++++ .../gamemodes/gamemode/ctscup/savestate.qh | 21 ++++++ .../gamemodes/gamemode/ctscup/sv_ctscup.qc | 55 ++++++++++----- .../gamemodes/gamemode/ctscup/sv_ctscup.qh | 2 +- 6 files changed, 129 insertions(+), 20 deletions(-) create mode 100644 qcsrc/common/gamemodes/gamemode/ctscup/savestate.qc create mode 100644 qcsrc/common/gamemodes/gamemode/ctscup/savestate.qh diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/_mod.inc b/qcsrc/common/gamemodes/gamemode/ctscup/_mod.inc index 89d6a2c7a..d57a6255e 100644 --- a/qcsrc/common/gamemodes/gamemode/ctscup/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/ctscup/_mod.inc @@ -6,3 +6,4 @@ #ifdef SVQC #include #endif +#include diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/_mod.qh b/qcsrc/common/gamemodes/gamemode/ctscup/_mod.qh index e9459a017..6c08b79f4 100644 --- a/qcsrc/common/gamemodes/gamemode/ctscup/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/ctscup/_mod.qh @@ -6,3 +6,4 @@ #ifdef SVQC #include #endif +#include diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/savestate.qc b/qcsrc/common/gamemodes/gamemode/ctscup/savestate.qc new file mode 100644 index 000000000..e07c55e3a --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctscup/savestate.qc @@ -0,0 +1,69 @@ +//these files can be moved but while CTS Cup is WIP I'll keep them in CTS Cup's gamemode folder +//files to update if moving: sv_ctscup.qh, ctscup folder's _mod files + +#include "savestate.qh" + +void SaveSaveState(entity player) +{ + if (player.savestate) + { + player.savestate.origin = player.origin; + player.savestate.v_angle = player.v_angle; + player.savestate.angles = player.angles; + player.savestate.velocity = player.velocity; + //SetResource(player.savestate, RES_ROCKETS, GetResource(player, RES_ROCKETS)); + //SetResource(player.savestate, RES_BULLETS, GetResource(player, RES_BULLETS)); + //SetResource(player.savestate, RES_CELLS, GetResource(player, RES_CELLS)); + //SetResource(player.savestate, RES_PLASMA, GetResource(player, RES_PLASMA)); + //SetResource(player.savestate, RES_SHELLS, GetResource(player, RES_SHELLS)); + //SetResource(player.savestate, RES_FUEL, GetResource(player, RES_FUEL)); + //SetResource(player.savestate, RES_HEALTH, max(1, GetResource(player, RES_HEALTH))); + //SetResource(player.savestate, RES_ARMOR, GetResource(player, RES_ARMOR)); + //STAT(WEAPONS, player.savestate) = STAT(WEAPONS, player); + //StatusEffects_copy(player.statuseffects, player.savestate, 0); + player.savestate.items = player.items; + player.savestate.pauserotarmor_finished = player.pauserotarmor_finished; + player.savestate.pauserothealth_finished = player.pauserothealth_finished; + player.savestate.pauserotfuel_finished = player.pauserotfuel_finished; + player.savestate.pauseregen_finished = player.pauseregen_finished; + player.savestate.teleport_time = time; + } +} + +bool LoadSaveState(entity player) +{ + if (player.savestate) + { + player.origin = player.savestate.origin; + player.v_angle = player.savestate.v_angle; + player.angles = player.savestate.angles; + player.fixangle = true; + player.velocity = player.savestate.velocity; + //SetResource(player, RES_ROCKETS, GetResource(player.savestate, RES_ROCKETS)); + //SetResource(player, RES_BULLETS, GetResource(player.savestate, RES_BULLETS)); + //SetResource(player, RES_CELLS, GetResource(player.savestate, RES_CELLS)); + //SetResource(player, RES_PLASMA, GetResource(player.savestate, RES_PLASMA)); + //SetResource(player, RES_SHELLS, GetResource(player.savestate, RES_SHELLS)); + //SetResource(player, RES_FUEL, GetResource(player.savestate, RES_FUEL)); + //SetResource(player, RES_HEALTH, GetResource(player.savestate, RES_HEALTH)); + //SetResource(player, RES_ARMOR, GetResource(player.savestate, RES_ARMOR)); + //STAT(WEAPONS, player) = STAT(WEAPONS, player.savestate); + //StatusEffects_copy(player.savestate, player.statuseffects, player.savestate.teleport_time); + //StatusEffects_update(player); + player.items = player.savestate.items; + player.pauserotarmor_finished = time + player.savestate.pauserotarmor_finished - player.savestate.teleport_time; + player.pauserothealth_finished = time + player.savestate.pauserothealth_finished - player.savestate.teleport_time; + player.pauserotfuel_finished = time + player.savestate.pauserotfuel_finished - player.savestate.teleport_time; + player.pauseregen_finished = time + player.savestate.pauseregen_finished - player.savestate.teleport_time; + + return true; + } + return false; +} + +void DeleteSaveState(entity player) +{ + if (player.savestate) + delete(player.savestate); +} + diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/savestate.qh b/qcsrc/common/gamemodes/gamemode/ctscup/savestate.qh new file mode 100644 index 000000000..eac6517a9 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctscup/savestate.qh @@ -0,0 +1,21 @@ +//#include +//#include +//#include + +.entity savestate; + +void SaveSaveState(entity player); +bool LoadSaveState(entity player); +void DeleteSaveState(entity player); + +.vector origin; +.vector v_angle; +.vector angles; +.bool fixangle; +.vector velocity; +.float items; +.float pauserotarmor_finished; +.float pauserothealth_finished; +.float pauserotfuel_finished; +.float pauseregen_finished; +.float teleport_time; diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qc b/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qc index 9c080d2d6..f801ebb4c 100644 --- a/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qc +++ b/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qc @@ -272,19 +272,31 @@ float autocvar_g_ctscup_maxroundlength; //round length unless it ends prematurel MUTATOR_HOOKFUNCTION(ctscup, PlayerSpawn) { entity player = M_ARGV(0, entity); - entity spawn_spot = M_ARGV(1, entity); + //entity spawn_spot = M_ARGV(1, entity); + //player.race_place = 0; - if(spawn_spot.target == "") - // Emergency: this wasn't a real spawnpoint. Can this ever happen? - race_PreparePlayer(player); - - // if we need to respawn, do it right - player.race_respawn_checkpoint = player.race_checkpoint; - player.race_respawn_spotref = spawn_spot; + player.tournamentParticipant = true; - player.race_place = 0; + // only spectators and connecting players do not have a savestate so here: + // newly connected player has spawned for the first time + // or during warmup a player who was spectating has joined (back) + // and as they're now a participant they're given a savestate + if (player.savestate == NULL) + { + player.savestate = new_pure(savestate); + SaveSaveState(player); + } - player.tournamentParticipant = true; + // upon spawning for a new round within 1s of the round starting, save a savestate + if ((time - 1) < (round_handler_GetEndTime() - autocvar_g_ctscup_maxroundlength)) + { + SaveSaveState(player); + } + // if somehow dying during a round and respawning, load last savestate + else + { + LoadSaveState(player); + } } MUTATOR_HOOKFUNCTION(ctscup, MakePlayerObserver) @@ -295,6 +307,8 @@ MUTATOR_HOOKFUNCTION(ctscup, MakePlayerObserver) race_PreparePlayer(player); player.race_checkpoint = -1; + + DeleteSaveState(player); } MUTATOR_HOOKFUNCTION(ctscup, Race_FinalCheckpoint) @@ -305,8 +319,6 @@ MUTATOR_HOOKFUNCTION(ctscup, Race_FinalCheckpoint) //if(autocvar_g_cts_finish_kill_delay) // ClientKill_Silent(player, autocvar_g_cts_finish_kill_delay); - // clear savestate - if (roundFirstFinisherTime == 0 && tournamentStarted) roundFirstFinisherTime = time; } @@ -316,22 +328,27 @@ MUTATOR_HOOKFUNCTION(ctscup, Damage_Calculate) entity frag_target = M_ARGV(2, entity); float frag_deathtype = M_ARGV(3, float); float frag_damage = M_ARGV(4, float); + vector frag_force = M_ARGV(7, vector); if((frag_target == frag_attacker || frag_deathtype == DEATH_FALL.m_id) && !autocvar_g_cts_selfdamage) { frag_damage = 0; + frag_force = '0 0 0'; M_ARGV(4, float) = frag_damage; + M_ARGV(6, vector) = frag_force; } // if the player were to be about to die, try to save them and restore a loadstate if possible -// else if (IS_PLAYER(frag_target)) -// if (((GetResource(frag_target, RES_HEALTH) + GetResource(frag_target, RES_ARMOR)) - frag_damage) <= 0) -// if (LoadSaveState(frag_target)) -// { + else if (IS_PLAYER(frag_target)) + if (((GetResource(frag_target, RES_HEALTH) + GetResource(frag_target, RES_ARMOR)) - frag_damage) <= 0) + if (LoadSaveState(frag_target)) + { // if savestate loading was successful // try to cheat death by offsetting the incoming damage -// frag_damage = 0; -// M_ARGV(4, float) = frag_damage; -// } + frag_damage = 0; + frag_force = '0 0 0'; + M_ARGV(4, float) = frag_damage; + M_ARGV(6, vector) = frag_force; + } } diff --git a/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qh b/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qh index c92362162..943db58d9 100644 --- a/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qh +++ b/qcsrc/common/gamemodes/gamemode/ctscup/sv_ctscup.qh @@ -1,6 +1,6 @@ #pragma once -//#include "savestate.qh" +#include "savestate.qh" #include #include -- 2.39.2