diff --git a/src/client/game_event.qc b/src/client/game_event.qc index e388b4f..4766640 100644 --- a/src/client/game_event.qc +++ b/src/client/game_event.qc @@ -182,6 +182,9 @@ ClientGame_EventParse(float fHeader) case EVENT_TS::PLAYER_DEATH:{ EV_PlayerDeath(); }break; + case EVENT_TS::PLAYER_NOCLIP:{ + EV_PlayerNoclip(); + }break; case EVENT_TS::EQUIP_CALLBACK:{ EV_EquipCallback(); }break; diff --git a/src/client/hud.qc b/src/client/hud.qc index 7aedb67..05b975a 100644 --- a/src/client/hud.qc +++ b/src/client/hud.qc @@ -71,6 +71,10 @@ HUD_Draw(void) player pl = (player)pSeat->m_ePlayer; + //TAGGG - outside the rest for now + Obituary_Draw(); + Textmenu_Draw(); + if(pl.iState == PLAYER_STATE::SPAWNED){ // A player @@ -109,8 +113,6 @@ HUD_Draw(void) // Nuclide methods in its weapons struct Weapons_DrawCrosshair(); TS_HUD_DrawWeaponSelect(); - Obituary_Draw(); - Textmenu_Draw(); //TAGGG - new diff --git a/src/client/obituary.qc b/src/client/obituary.qc index ea61f79..fadb7c8 100644 --- a/src/client/obituary.qc +++ b/src/client/obituary.qc @@ -144,6 +144,7 @@ Obituary_Add(string attacker, string victim, float weapon, float flags) void Obituary_Draw(void) { + int i; vector pos; vector item; diff --git a/src/server/defs.h b/src/server/defs.h index b8f5a10..f52da6f 100644 --- a/src/server/defs.h +++ b/src/server/defs.h @@ -34,6 +34,12 @@ var float autocvar_mp_timelimit = 60; var string autocvar_motdfile = "motd.txt"; var int autocvar_mp_friendlyfire = FALSE; +var float autocvar_weaponstay = 60; //default originally 15 + +// orignal TS name. Minimum allowed time before respawns for self-respawnable gamemodes, +// deathmatch and team-deathmatch, I forget if there's anything else. Not round-based ones. +var float autocvar_respawntime = 5.0; + var int autocvar_debug_spawnpointforced = 0; diff --git a/src/server/gamerules.h b/src/server/gamerules.h index 055edb8..f06ab0a 100644 --- a/src/server/gamerules.h +++ b/src/server/gamerules.h @@ -113,7 +113,9 @@ class TSMultiplayerRules:TSGameRules virtual void(player pl) MakePlayerInvisible; virtual void(base_player) PlayerMakePlayable; + virtual void(base_player pp) PlayerMakePlayableWithDefaultMoney; virtual void(base_player) PlayerMakeSpectator; + virtual void(base_player) PlayerMakeSpectatorAndNotify; virtual void(base_player pp) PlayerMakeSpectatorDelayed; virtual void(base_player, int) PlayerRespawn; virtual entity(float) PlayerFindSpawn; diff --git a/src/server/gamerules_multiplayer.qc b/src/server/gamerules_multiplayer.qc index aeb8992..23b65df 100644 --- a/src/server/gamerules_multiplayer.qc +++ b/src/server/gamerules_multiplayer.qc @@ -360,11 +360,17 @@ TSMultiplayerRules::PlayerDeath(base_player pp) // Redundant for gibbing but doesn't hurt MakePlayerInvisible(pl); - PlayerMakeSpectatorDelayed(pl); + // Now doing this instead + //PlayerMakeSpectatorDelayed(pl); + pl.iState = PLAYER_STATE::DEAD_RECENT; + pl.deathCameraChangeTime = 2.5; + pl.minimumRespawnTime = autocvar_respawntime; + + // Not done just for this player, still a check on all alive players + // that happens to involve this deceased player. Strange. DeathCheck(pl); - } @@ -580,45 +586,19 @@ TSMultiplayerRules::RestartRound(int iWipe) // TAGGG - CRITICAL. // If gamemodes where the starting cash isn't reset to a static amount at every spawn - // are ever intended, this is bad! - // Skipping a clean reset for all currently in spectator (classname="spectator") is bad! + // are ever intended, this has to be adjusted. for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) { //self = eFind; player pl = (player)eFind; - // only affect players ingame! - if(pl.iState != PLAYER_STATE::SPAWNED){ - continue; + if(pl.iState == PLAYER_STATE::SPAWNED){ + PlayerMakePlayableWithDefaultMoney(pl); } - - //printfline("PLAYER MONEY 1: %i", pl.money); - - // only respawn players that were ingame. Those that weren't may want to stay in spectator. - if(pl.health > 0){ - //pl.reset(TRUE); - - // Something about this resets the player money..? Is it changing to Spectator - // right before Player that did it here? - //PlayerMakeSpectator(pl); - // just let PlayerMakePlayable work. - pl.iState = PLAYER_STATE::NOCLIP; - PlayerMakePlayable(pl); - } - - // do the money set here and it works out fine, doesn't leave the player on - // initial-round-respawn with 0 money. - setPlayerMoneyDefault(pl); - //printfline("PLAYER MONEY 3: %i", pl.money); - } - // anything to do with spectators. - //TAGGG - CRITICAL! As spectators, changing "pl.money" would not make sense. Not a player now. - /* - for (entity eFind = world; (eFind = find(eFind, ::classname, "spectator"));) { - - } - */ + // anything to do with spectators? + // Remember, any classname="spectator" is an authentic spectator, not players that can open the + // buymenu and click to spawn after. }//END OF !startingGame check @@ -679,6 +659,8 @@ TSMultiplayerRules::RestartRound(int iWipe) +// From FreeCS, to check if all players on a team are dead for doing something +// gamerules-wise like ending in the other side's favor. void TSMultiplayerRules::DeathCheck(base_player pl) { @@ -806,7 +788,6 @@ TSMultiplayerRules::PlayerRespawn(base_player pp, int fTeam) //pl.classname = "player"; // fresh inventory for you - //TAGGG - QUESTION: Why reset the player on death or going to spectator instead? TS_resetPlayer(pl, TRUE); // Also, FreeHL did these, but it looks like a simple way of 0'ing everything out. @@ -1006,12 +987,6 @@ void TSMultiplayerRules::PlayerMakePlayable(base_player pp) { player pl = (player)pp; - if(pl.iState == PLAYER_STATE::SPAWNED){ - // no need to do this again. - // Set pl.iState to something else first if this was an intentional re-spawn - // for an ingame player - return; - } pl.iState = PLAYER_STATE::SPAWNED; // Nope! @@ -1021,32 +996,40 @@ TSMultiplayerRules::PlayerMakePlayable(base_player pp) PlayerRespawn(pl, pl.team); } -/* -================= -PlayerMakeSpectator -Force the player to become an observer. -================= -*/ +void +TSMultiplayerRules::PlayerMakePlayableWithDefaultMoney(base_player pp){ + player pl = (player)pp; + PlayerMakePlayable(pp); + setPlayerMoneyDefault(pl); +} + + + +void +TSMultiplayerRules::PlayerMakeSpectatorAndNotify(base_player pp){ + + PlayerMakeSpectator(pp); + + // let the client know of the chane to revert any 'cl_thirdperson' change + WriteByte( MSG_MULTICAST, SVC_CGAMEPACKET ); + WriteByte( MSG_MULTICAST, EVENT_TS::PLAYER_NOCLIP ); + msg_entity = pp; + multicast( [0,0,0], MULTICAST_ONE_R ); +} + +// Turns the player into a fake spectator (open buymenu and fire to spawn +// when its closed) void TSMultiplayerRules::PlayerMakeSpectator(base_player pp) { player pl = (player)pp; - if(pl.iState == PLAYER_STATE::NOCLIP){ - // Already in fake spectator! Stop. - return; - } - if(pl.modelindex != 0){ // assume this is necessary MakePlayerInvisible(pl); } - // is that necessary still? - //TS_resetPlayer(pl, TRUE); - // And don't do this! Just change iState - //MakeSpectator(pl); pl.iState = PLAYER_STATE::NOCLIP; // And do the rest of the lines to finish that @@ -1056,12 +1039,8 @@ TSMultiplayerRules::PlayerMakeSpectator(base_player pp) pl.think = NULL; pl.nextthink = 0.0f; pl.maxspeed = 250; - //pl.spec_ent = 0; - //pl.spec_mode = 0; - //#ifdef SERVER - forceinfokey(pl, "*spec", "1"); - //#endif + forceinfokey(pl, "*spec", "1"); pl.armor = pl.activeweapon = pl.g_items = 0; pl.health = 0; @@ -1080,27 +1059,6 @@ TSMultiplayerRules::PlayerMakeSpectatorDelayed(base_player pp) player pl = (player)pp; PlayerMakeSpectator(pp); - /* - player pl = (player)pp; - if(pl.classname != "spectator" && pl.modelindex != 0){ - // assume this is necessary - MakePlayerInvisible(pl); - } - - //TS_resetPlayer(pl, TRUE); - static void GoSpec(void) { - spawnfunc_spectator(); - } - pl.think = GoSpec; - pl.nextthink = time; - // ? Is this for the specator-spawn to sense that the classname - // wasn't already "spectator" in case this happens twice in the - // same frame? - pl.classname = "player"; - // "dead" should already be set. - //forceinfokey(pl, "*dead", "1"); - //forceinfokey(pl, "*team", ftos(pl.team)); - */ } /* @@ -1136,16 +1094,12 @@ TSMultiplayerRules::PlayerSpawn(base_player pp) //pl.team = TEAM_SPECTATOR; - // immediately put us into spectating mode - // (iState forced to a wrong value to stop MakeSpectator from being skipped) - pl.iState = PLAYER_STATE::SPAWNED; + // immediately put us into fake spectating mode (MoTD followed by buy-menu) PlayerMakeSpectator(pl); // Use our game's custom ObserverCam instead. Game_Spawn_ObserverCam(pl); // give the initial server-joining money - //TAGGG - WARNING! This sets 'pl.money', should that be some infokey stat instead - // for player/spectator entity-change reasons? setPlayerMoneyDefault(pl); // I guess this state counts as "dead"? @@ -1156,10 +1110,13 @@ TSMultiplayerRules::PlayerSpawn(base_player pp) forceinfokey(pl, "*deaths", "0"); //ftos(pl.deaths)); forceinfokey(pl, "done_connecting", "1"); + + // ALSO - start the minimum spawn delay. + pl.minimumRespawnTime = autocvar_respawntime; + } - void TSMultiplayerRules::setPlayerMoneyDefault(player pl) { @@ -1169,30 +1126,51 @@ TSMultiplayerRules::setPlayerMoneyDefault(player pl) +void +CSEv_GameEarlyNoclip(void) +{ + TSMultiplayerRules rules; + player pl = (player)self; + + if(pl.iState != PLAYER_STATE::DEAD_RECENT){ + // This is for looking at a recently dead player only. + return; + } + + if(pl.deathCameraChangeTime > 1.5){ + if(pl.deathCameraChangeTime < 1.5 + 0.5){ + pl.waitingForEarlyNoclip = TRUE; + } + return; + } + + rules = (TSMultiplayerRules)g_grMode; + rules.PlayerMakeSpectatorAndNotify(pl); + + +} + // An order from the client (while in spectator) that they want to spawn. void CSEv_GamePlayerSpawn(void) { - TSMultiplayerRules rules = (TSMultiplayerRules)g_grMode; + TSMultiplayerRules rules; player pl = (player)self; if(pl.iState == PLAYER_STATE::SPAWNED){ - // what. + // already spawned? return; } - //self.dmg_take = 0; - //self.dmg_inflictor = NULL; //good idea? - - // For now stop deathspammin' - if (pl.health > 0) { + if(pl.minimumRespawnTime > 0){ + // the spawn-delay hasn't expired yet, then no. + // Although, if close enough, mark this as 'waitingToSpawn' to happen as it finishes + if(pl.minimumRespawnTime < 0.5){ + pl.waitingForSpawn = TRUE; + } return; } - rules.PlayerMakeSpectator(pl); - rules.PlayerMakePlayable(pl); - - // reset the money - rules.setPlayerMoneyDefault(pl); + rules = (TSMultiplayerRules)g_grMode; + rules.PlayerMakePlayableWithDefaultMoney(pl); } - diff --git a/src/server/init.qc b/src/server/init.qc index bebaf47..60eed35 100644 --- a/src/server/init.qc +++ b/src/server/init.qc @@ -289,8 +289,9 @@ Game_Worldspawn(void) // see what calls that if uncertain from nuclide. initSpawnMem(); - clientstat(STAT_MONEY, EV_INTEGER, player::money); + clientstat(STAT_DEATHCAMERACHANGETIME, EV_FLOAT, player::deathCameraChangeTime); + clientstat(STAT_MINIMUMRESPAWNTIME, EV_FLOAT, player::minimumRespawnTime); pointerstat(STAT_GAMETIME, EV_FLOAT, &g_ts_gametime); pointerstat(STAT_GAMESTATE, EV_INTEGER, &g_ts_gamestate); diff --git a/src/shared/defs.h b/src/shared/defs.h index 3273d2b..f018b43 100644 --- a/src/shared/defs.h +++ b/src/shared/defs.h @@ -20,12 +20,6 @@ enum TS_Team{ //#define INPUT_BUTTON9 0x00000100 -#ifdef SERVER - //server CVar - var float autocvar_weaponstay = 60; //default originally 15 -#endif - - // for convenience and clarity, see fteextensions.qc for the rest of the SOUNDFLAG choices #define SOUNDFLAG_NONE 0 @@ -66,10 +60,14 @@ enum TS_Team{ //TAGGG - Still need to remove some irrelevant ones like BUYZONE,, ESCAPEZONE, WON_T, etc. // Also starting at stat 34? Feel some constant would be more comfortable for that, might not be an option though. +// ALSO - don't exceed 127, going by fteextensions.qc enum { STAT_MONEY = STAT_BUILTIN_SEPARATOR, - //STAT_TEAM, // will we ever need that? + STAT_DEATHCAMERACHANGETIME, + STAT_MINIMUMRESPAWNTIME, + + //STAT_TEAM, // later STAT_GAMETIME, STAT_GAMESTATE, STAT_RULE_MONEYALLOWED, diff --git a/src/shared/event_custom.h b/src/shared/event_custom.h index 0793904..35a618a 100644 --- a/src/shared/event_custom.h +++ b/src/shared/event_custom.h @@ -37,6 +37,7 @@ void EV_TS_resetPlayer(player pl, BOOL resetInventory) #ifdef CLIENT void EV_PlayerDeath(void) +void EV_PlayerNoclip(void); #endif diff --git a/src/shared/event_custom.qc b/src/shared/event_custom.qc index 71ccefa..7df5a5d 100644 --- a/src/shared/event_custom.qc +++ b/src/shared/event_custom.qc @@ -390,8 +390,6 @@ EV_TS_resetPlayer(player pl, BOOL resetInventory){ // keeping the event in case it has some use later. void EV_PlayerDeath(void){ - printfline("EV_PlayerDeath"); - // Require a tiny amount of time and a mouse release before a respawn, so that // dying with the mouse held down isn't enough to trigger a respawn request. //pSeatLocal->m_bNeedPrimaryRelease = TRUE; @@ -406,11 +404,27 @@ EV_PlayerDeath(void){ player pl = (player)self; pl.resetZoom(); + + // also this, anticipating that it will be shown after the DEAD_RECENT state is over + pl.displayMinimumRespawnTimeCountdown = TRUE; + + // force third-person while the player is in RECENT_DEAD at least + pl.old_cl_thirdperson = cvar("cl_thirdperson"); + //autocvar_cl_thirdperson = 1i; + cvar_set("cl_thirdperson", itos(1i)); +} + +void +EV_PlayerNoclip(void){ + player pl = (player)self; + //autocvar_cl_thirdperson = pl.old_cl_thirdperson; + cvar_set("cl_thirdperson", itos(pl.old_cl_thirdperson)); + //cvar_set("cl_thirdperson", ftos(0)); + } #endif - #ifdef CLIENT //NOTICE - clientside components removed, except for clientside being able // to signal that the server should remove a weapon. diff --git a/src/shared/event_enum.h b/src/shared/event_enum.h index d692d2e..398a66e 100644 --- a/src/shared/event_enum.h +++ b/src/shared/event_enum.h @@ -7,6 +7,7 @@ enum EVENT_TS{ RESET_VIEW_MODEL, RESET_PLAYER, PLAYER_DEATH, + PLAYER_NOCLIP, EV_IMPACT_MELEE, //DROP_WEAPON, FX_TS_EXPLOSION_GRENADE, diff --git a/src/shared/input.qc b/src/shared/input.qc index ad09b29..e5f976d 100644 --- a/src/shared/input.qc +++ b/src/shared/input.qc @@ -64,6 +64,12 @@ Game_Input(void) // not ingame (fake spectator)? Do another check instead: spawning. #ifdef CLIENT PreSpawn_Input(); + // since PreSpawn_Input may end early, guarantee this runs + if(input_buttons & INPUT_BUTTON0){ + pl.gflags |= GF_SEMI_TOGGLED; + }else{ + pl.gflags &= ~GF_SEMI_TOGGLED; + } #endif // client or server, don't pay attention to the rest of this file if not spawned return; @@ -411,9 +417,49 @@ void PreSpawn_Input(void){ !(pl.gflags & GF_SEMI_TOGGLED) && pSeatLocal->m_flBlockSpawnInputTime <= time ){ - sendevent( "GamePlayerSpawn", ""); + + if(pl.iState == PLAYER_STATE::DEAD_RECENT){ + + if(getstatf(STAT_DEATHCAMERACHANGETIME) <= 1.5 + 0.35){ + // change, and fall-through to do the minimum respawn timer too. + sendevent("GameEarlyNoclip", ""); + }else{ + // don't do anything + return; + } + } + + if(getstatf(STAT_MINIMUMRESPAWNTIME) < 0.35){ + // allow a little time before the countdown finishes to send the message anyway in case of lag, + // should reach the server soon, no need for the countdown notice + sendevent("GamePlayerSpawn", ""); + }else{ + + } + + } + // show a printout of how long until the player can click to spawn. + // TODO - use our own system instead to use a larger font, and the same one + // used for the buymenu buttons, most things seem to do that. + // Also, any 'Using New Config' centerprint will be overwritten by this instantly, + // so there should probably be a check to let that one appear independently of this + // countdown and below if both need to be shown. + if(pl.iState == PLAYER_STATE::NOCLIP && pl.displayMinimumRespawnTimeCountdown){ + int roundedMinimumRespawnTime = (int)ceil(getstatf(STAT_MINIMUMRESPAWNTIME)); + if(roundedMinimumRespawnTime != 0){ + CSQC_Parse_CenterPrint(sprintf("%i", roundedMinimumRespawnTime)); + }else{ + // last one. + pl.displayMinimumRespawnTimeCountdown = FALSE; + CSQC_Parse_CenterPrint("Press fire to play!"); + } + } + + + + /* if( ((input_buttons & INPUT_BUTTON0) && pSeatLocal->m_flBlockSpawnInputTime > time) || @@ -426,12 +472,6 @@ void PreSpawn_Input(void){ } */ - // since the rest of this method is about to be skipped. - if(input_buttons & INPUT_BUTTON0){ - pl.gflags |= GF_SEMI_TOGGLED; - }else{ - pl.gflags &= ~GF_SEMI_TOGGLED; - } }//PreSpawn_Input diff --git a/src/shared/player.h b/src/shared/player.h index 6b68807..d60f5b7 100644 --- a/src/shared/player.h +++ b/src/shared/player.h @@ -15,9 +15,11 @@ enum PLAYER_STATE{ // Nothing special. Collision, gravity, equips things. SPAWNED = 0, // Like a third-person view of the player's most recent position, unsure - // if it follows the corpse, try dying while moving in TS to see. - // Slowly zooms out to a point. - // Changes to DEAD_NOCLIP (aka "Fake Spectator") on clicking between 1 + // if it follows the corpse, try dying while moving in TS to see, my guess + // is yes because it appears to behave like third-person. + // Slowly zooms out to a point. I doubt wonkiness in dying in third-person + // (already) was intentional. + // Changes to NOCLIP (aka "Fake Spectator") on clicking between 1 // and 2.5 seconds, or waiting out the 2.5 seconds (happens anyway) DEAD_RECENT, // Starts at the exact point where the dead player was. @@ -51,9 +53,21 @@ class player:base_player // On death, the player cannot change the camera to fake-specator until at least 1 second // has passed. - // On death, set this to 2.5. If it is less than 1.5, + // On death, set this to 2.5. If it is less than 1.5, allow clicking to reach +#ifdef CLIENT + BOOL displayMinimumRespawnTimeCountdown; + int old_cl_thirdperson; +#else + // SERVER float deathCameraChangeTime; + float minimumRespawnTime; + BOOL waitingForSpawn; + BOOL waitingForEarlyNoclip; +#endif + + // Send everything in the inventory to the client this frame? + // If not, only the one of the currently equipped weapon gets updated. BOOL completeInventorySend; @@ -142,8 +156,6 @@ class player:base_player int equippedWeaponWaitingForCallback_ID; float equippedWeaponWaitingForCallback_maxWaitTime; - - // For telling how long it's been since I've been on the ground. // Don't oscillate the view model bob on going down short steps. float flRecentGroundTime; @@ -211,58 +223,32 @@ class player:base_player // ...also, this variable will be shared. Serverside since it must be presistent // (not just something to step away from after it happens the first frame), and // clientside to keep track of how to behave in case the connection is broken since. - //vector vViewAngleOffsetTarget; - #else #endif - // should be better networked when it's better understood what the block feature // (reload with karate out, I assume?) is supposed to do. float flKarateBlockCooldown; PREDICTED_INT(iMeleeCycler); - - // SHARED - // PREDICTED_whatever(...); - //vector vViewAngleOffsetTarget; - - /* - vector vViewAngleOffsetEnd; - vector vViewAngleOffsetStart; - float flViewAngleOffsetLerp; - vector vViewAngleOffsetCurrent; - vector vViewAngleOffsetTarget; - vector vViewAngleMemory; - */ - //vector vViewAngleOffsetEnd; - //vector vViewAngleOffsetStart; - //float flViewAngleOffsetLerp; - //vector vViewAngleOffsetCurrent; PREDICTED_FLOAT(flViewAngleOffsetTarget); PREDICTED_VECTOR(vViewAngleOffsetTargetDir); - //vector vViewAngleMemory; - //vector vViewAngleOffsetTotalChange; PREDICTED_VECTOR(vViewAngleOffsetTotalChange); #ifdef SERVER vector vViewAngleOffsetTotalChangeAlt; #endif - - PREDICTED_FLOAT(fAccuracyKickback); PREDICTED_FLOAT(fAccuracyKickbackStartCooldown); // The client can keep its own in mind as prediction between frames if needed. Server // time and client time aren't really compatible to send like this. - - PREDICTED_FLOAT(fMoveBlockDelay); PREDICTED_FLOAT(fUncrouchBlockDelay); // not networked, set by the player individually client and serverside on picking @@ -271,8 +257,8 @@ class player:base_player PREDICTED_FLOAT(fKarateStamina); -//These will be shared and help with slowdown logic. - //They are all 0 to 1.0 (normal) unless there are further restrictions. + // These will be shared and help with slowdown logic. + // They are all 0 to 1.0 (normal) unless there are further restrictions. float flBaseSpeedMulti; //How should miscelaneous things be affected (client animation)? float flMoveSpeedMulti; //how is move speed affected? float flSoundSpeedMulti; //can range from 0.5 to 1. @@ -283,8 +269,6 @@ class player:base_player float flProjectileSpeedMulti; //how are non-projectiles (grenades, knives, etc.) affected? - - //TODO - see how to handle saving / loadig these on the client's end. //Server probably dosen't need to store config stuff at all. #ifdef CLIENT @@ -323,8 +307,6 @@ class player:base_player float flViewShake; #else - //serverside - float nextUseCooldown; #endif diff --git a/src/shared/player.qc b/src/shared/player.qc index ca0687d..e561209 100644 --- a/src/shared/player.qc +++ b/src/shared/player.qc @@ -296,7 +296,7 @@ player::ReceiveEntity(float new, float fl) this.completeInventorySend = readbyte(); #ifdef FORCE_NETWORK_ALL_INVENTORY - completeInventorySend = TRUE; + this.completeInventorySend = TRUE; #endif if(this.completeInventorySend){ @@ -1004,6 +1004,11 @@ player::reset(BOOL resetInventory){ // should this even make any assumptions about this? //iState = ?; + // seem like good ideas? +#ifdef SERVER + dmg_take = 0; + dmg_inflictor = NULL; +#endif resetZoom(); @@ -1037,12 +1042,19 @@ player::reset(BOOL resetInventory){ forceViewModelUpdate = FALSE; prev_iForceBodygroup1Submodel = 0; - + + // assuming the player is in spectator and wanting to spawn soon. + displayMinimumRespawnTimeCountdown = TRUE; + old_cl_thirdperson = 0; #endif #ifdef SERVER switchToRecentlyAddedWeapon = FALSE; - nextUseCooldown = 0; + + deathCameraChangeTime = 0; + minimumRespawnTime = 0; + waitingForSpawn = FALSE; + waitingForEarlyNoclip = FALSE; #endif @@ -1683,11 +1695,47 @@ player::callWeaponThink(void){ void player::frameThink_fromServer(void){ + TSMultiplayerRules rules; // no, leave that to shared/input.qc //updateTimers(); //preThinkShared(); + + // only in DEAD_RECENT. This is the temporary third-person for looking at the recently + // dead player (self). After the countdown is over, goto the NOCLIP one. + if(iState == PLAYER_STATE::DEAD_RECENT){ + + if(deathCameraChangeTime > 0){ + deathCameraChangeTime -= frametime; + } + if(deathCameraChangeTime <= 0 || (waitingForEarlyNoclip && deathCameraChangeTime <= 1.5) ){ + // time's up? Go ahead and change + deathCameraChangeTime = 0; + rules = (TSMultiplayerRules)g_grMode; + rules.PlayerMakeSpectatorAndNotify(this); + + } + } + + if(iState != PLAYER_STATE::SPAWNED){ + // Once this delay has passed, spawning is allowed. + // If the click was very close to the end (waitingForSpawn), count it and spawn + // as soon as possible anyway, helps with some lag perhaps. + if(minimumRespawnTime > 0){ + minimumRespawnTime -= frametime; + if(minimumRespawnTime <= 0){ + minimumRespawnTime = 0; + if(waitingForSpawn){ + // do it! + rules = (TSMultiplayerRules)g_grMode; + rules.PlayerMakePlayableWithDefaultMoney(this); + }else{ + // Nothing special happens, user must click to spawn. + } + } + } + }// not-SPAWNED check }// frameThink_fromServer