crude TS dead thirdperson and respawn delay, obituary drawn at all times

This commit is contained in:
Christopher Dawalt 2021-10-03 06:30:41 -04:00
parent 497393eea6
commit b5e7cf4bbf
14 changed files with 239 additions and 162 deletions

View File

@ -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;

View File

@ -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

View File

@ -144,6 +144,7 @@ Obituary_Add(string attacker, string victim, float weapon, float flags)
void
Obituary_Draw(void)
{
int i;
vector pos;
vector item;

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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,

View File

@ -37,6 +37,7 @@ void EV_TS_resetPlayer(player pl, BOOL resetInventory)
#ifdef CLIENT
void EV_PlayerDeath(void)
void EV_PlayerNoclip(void);
#endif

View File

@ -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.

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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