From 74edcec2adfe318f8c82fa7a737bde4e3f6157a5 Mon Sep 17 00:00:00 2001 From: greenmarine Date: Sat, 17 May 2008 20:21:36 +0000 Subject: [PATCH] extension of the ready-restart feature: - bugfix: extend the pause for rotting health when map is being reset, otherwise health will start to rot after the 10 second countdown is over, because the rot is set to 10 seconds - new feature: sv_ready_restart_after_countdown: allows player to walk around during countdown (and prevents that a player knows where he spawns as he gets spawned after the countdown is over) - new feature: sv_ready_restart_repeatable: players can restart the map more often (more than once) - new feature: sv_ready_restart_nag: nags other real players who haven't readied up yet to do so, interval and duration can also be setup git-svn-id: svn://svn.icculus.org/nexuiz/trunk@3629 f962a42d-fe04-0410-a3ab-8c8b0445ebaa --- data/default.cfg | 5 ++ data/qcsrc/server/arena.qc | 54 ++++++++++-- data/qcsrc/server/cl_client.qc | 46 ++++++---- data/qcsrc/server/cl_weapons.qc | 2 +- data/qcsrc/server/cl_weaponsystem.qc | 7 ++ data/qcsrc/server/clientcommands.qc | 121 ++++++++++++++++++++++++++- data/qcsrc/server/defs.qh | 8 +- data/qcsrc/server/miscfunctions.qc | 2 + data/qcsrc/server/teamplay.qc | 17 +++- 9 files changed, 228 insertions(+), 34 deletions(-) diff --git a/data/default.cfg b/data/default.cfg index 98babf6f7..6aee3a621 100644 --- a/data/default.cfg +++ b/data/default.cfg @@ -112,6 +112,11 @@ r_cullentities_trace 1 // restart server if all players hit "ready"-button set sv_ready_restart 0 +set sv_ready_restart_after_countdown 0 //if set to 1 the players and map items are reset after the countdown ended, otherwise they're reset already at the beginning of the countdown +set sv_ready_restart_repeatable 0 //allows the players to restart the game as often as needed +set sv_ready_restart_nag 0 //whether to nag players who are not ready yet, the message is shown in intervals +set sv_ready_restart_nag_duration 6 //how long to show the ready-nag, in seconds +set sv_ready_restart_nag_interval 10 //how long the pause between the ready-nags is, in seconds // use default physics exec physicsQBR.cfg diff --git a/data/qcsrc/server/arena.qc b/data/qcsrc/server/arena.qc index aaf242deb..1832d4478 100644 --- a/data/qcsrc/server/arena.qc +++ b/data/qcsrc/server/arena.qc @@ -19,6 +19,10 @@ void dom_controlpoint_setup(); void onslaught_generator_reset(); void onslaught_controlpoint_reset(); +/** + * Resets the state of all clients, items, flags, runes, keys, weapons, waypoints, ... of the map. + * Sets the 'warmup' global variable. + */ void reset_map() { if(g_arena) @@ -105,21 +109,36 @@ void reset_map() FOR_EACH_CLIENT(self) { if(self.flags & FL_CLIENT) // reset all players { - if(time < restart_countdown) - { - self.frags = (g_lms)?LMS_NewPlayerLives():0; - self.deaths = 0; - self.killcount = 0; - self.classname = "player"; - PutClientInServer(); - } - else if(g_arena) + if(g_arena) { if(self.spawned) PutClientInServer(); else PutObserverInServer(); } + else + { + /* + only reset players if a restart countdown is active + this can either be due to cvar sv_ready_restart_after_countdown having set + restart_mapalreadyrestarted to 1 after the countdown ended or when + sv_ready_restart_after_countdown is not used and countdown is still running + */ + if (restart_mapalreadyrestarted || (time < restart_countdown)) + { + //NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players + if (self.classname == "player") { + self.frags = (g_lms)?LMS_NewPlayerLives():0; + self.deaths = 0; + self.killcount = 0; + //stop the player from moving so that he stands still once he gets respawned + self.velocity = '0 0 0'; + self.avelocity = '0 0 0'; + self.movement = '0 0 0'; + PutClientInServer(); + } + } + } } } @@ -180,6 +199,14 @@ void Spawnqueue_Mark(entity e) numspawned = numspawned + 1; } +/** + * If roundbased arena game mode is active, it centerprints the texts for the + * player when player is waiting for the countdown to finish. + * Blocks the players movement while countdown is active. + * Unblocks the player once the countdown is over. + * + * Called in PlayerPostThink() + */ void Arena_Warmup() { float f; @@ -223,11 +250,20 @@ void Arena_Warmup() } float next_round; + +/** + * This function finds out whether an arena round is over 1 player is left. + * It determines the last player who's still alive and saves it's entity reference + * in the global variable 'champion'. Then the new enemy/enemies are put into the server. + * + * Gets called in StartFrame() + */ void Spawnqueue_Check() { if(time < warmup + 1) return; + //extend next_round if it isn't set yet and only 1 player is spawned if(!next_round) if(numspawned < 2) next_round = time + 3; diff --git a/data/qcsrc/server/cl_client.qc b/data/qcsrc/server/cl_client.qc index 9a8e16477..a84ba6720 100644 --- a/data/qcsrc/server/cl_client.qc +++ b/data/qcsrc/server/cl_client.qc @@ -555,6 +555,13 @@ void PutClientInServer (void) self.pauserotarmor_finished = time + cvar("g_balance_pause_armor_rot_spawn"); self.pauserothealth_finished = time + cvar("g_balance_pause_health_rot_spawn"); self.pauseregen_finished = time + cvar("g_balance_pause_health_regen_spawn"); + //extend the pause of rotting if client was reset at the beginning of the countdown + if(!cvar("sv_ready_restart_after_countdown") && time < restart_countdown) { + self.spawnshieldtime += RESTART_COUNTDOWN; + self.pauserotarmor_finished += RESTART_COUNTDOWN; + self.pauserothealth_finished += RESTART_COUNTDOWN; + self.pauseregen_finished += RESTART_COUNTDOWN; + } self.damageforcescale = 2; self.death_time = 0; self.dead_frame = 0; @@ -1780,24 +1787,29 @@ void PlayerPostThink (void) //if (TetrisPostFrame()) return; // restart countdown - if(time < restart_countdown) - { - string s; - float sec; - - sec = ceil(restart_countdown-time); - s = strcat(NEWLINES, "^1Game starts in ", ftos(sec), " seconds"); - centerprint(self, s); - self.movetype = MOVETYPE_NONE; - self.velocity = '0 0 0'; - self.avelocity = '0 0 0'; - self.movement = '0 0 0'; - } - else if(self.movetype == MOVETYPE_NONE) - { - self.movetype = MOVETYPE_WALK; - centerprint(self, "\n"); + if (restart_countdown) { + if(time < restart_countdown) { + if (!cvar("sv_ready_restart_after_countdown")) + { + self.movetype = MOVETYPE_NONE; + self.velocity = '0 0 0'; + self.avelocity = '0 0 0'; + self.movement = '0 0 0'; + } + } + else + { + //allow the player to move again if sv_ready_restart_after_countdown is not used and countdown is over + if (!cvar("sv_ready_restart_after_countdown")) + { + if(self.movetype == MOVETYPE_NONE) + { + self.movetype = MOVETYPE_WALK; + } + } + } } + } else if (self.classname == "observer") { //do nothing } else if (self.classname == "spectator") { diff --git a/data/qcsrc/server/cl_weapons.qc b/data/qcsrc/server/cl_weapons.qc index 3f380bcb0..2a6f36b0a 100644 --- a/data/qcsrc/server/cl_weapons.qc +++ b/data/qcsrc/server/cl_weapons.qc @@ -220,7 +220,7 @@ void() W_PreviousWeapon = // Bringed back weapon frame void() W_WeaponFrame = { - if((arena_roundbased && time < warmup) || (time < restart_countdown)) + if((arena_roundbased && time < warmup) || ((time < restart_countdown) && !cvar("sv_ready_restart_after_countdown"))) return; if (!self.weaponentity || self.health < 1) diff --git a/data/qcsrc/server/cl_weaponsystem.qc b/data/qcsrc/server/cl_weaponsystem.qc index f308610fb..5afa6d5e4 100644 --- a/data/qcsrc/server/cl_weaponsystem.qc +++ b/data/qcsrc/server/cl_weaponsystem.qc @@ -423,6 +423,13 @@ void(float windex, string wname, float hudammo) weapon_setup = // perform weapon to attack (weaponstate and attack_finished check is here) float(float secondary, float attacktime) weapon_prepareattack = { + //if sv_ready_restart_after_countdown is set, don't allow the player to shoot + //if all players readied up and the countdown is running + if (cvar("sv_ready_restart_after_countdown")) + if(time < restart_countdown) { + return FALSE; + } + if (!weapon_action(self.weapon, WR_CHECKAMMO1 + secondary)) { self.switchweapon = w_getbestweapon(self); diff --git a/data/qcsrc/server/clientcommands.qc b/data/qcsrc/server/clientcommands.qc index 7e9365540..afe251095 100644 --- a/data/qcsrc/server/clientcommands.qc +++ b/data/qcsrc/server/clientcommands.qc @@ -483,7 +483,7 @@ void SV_ParseClientCommand(string s) { } else if(argv(0) == "ready") { if(cvar("sv_ready_restart")) { - if(!restart_countdown) + if(!restart_countdown || cvar("sv_ready_restart_repeatable")) { self.ready = TRUE; bprint(self.netname, "^2 is ready\n"); @@ -812,6 +812,15 @@ void VoteCount() { float timelimit_orig; +/** + * Counts how many players are ready. If not enough players are ready, the function + * does nothing. If all players are ready, the timelimit will be extended and the + * restart_countdown variable is set to allow other functions like PlayerPostThink + * to detect that the countdown is now active. If the cvar sv_ready_restart_after_countdown + * is not set the map will be resetted. + * + * Function is called after the server receives a 'ready' sign from a player. + */ void ReadyCount() { local entity e; @@ -824,6 +833,16 @@ void ReadyCount() r += 1; } + if(cvar("sv_ready_restart_nag")) { + if(!readyNagActive) { + readyNagger = spawn(); + readyNagger.think = readyNagger_Think; + readyNagger.cnt = cvar("sv_ready_restart_nag_duration"); + readyNagger.nextthink = time; + readyNagActive = 1; + } + } + if(!p || r < p) return; @@ -833,7 +852,18 @@ void ReadyCount() if(g_arena | g_assault | gameover | intermission_running) localcmd("restart\n"); + if(readyNagActive) { //if every player is ready, remove the ready-nagger again + readyNagActive = 0; + remove(readyNagger); + } + restart_countdown = time + RESTART_COUNTDOWN; + restart_mapalreadyrestarted = 0; //reset this var, needed when cvar sv_ready_restart_repeatable is in use + //reset the .ready status of all players (also spectators) + FOR_EACH_CLIENT(e) + { + e.ready = 0; + } if(0 time) return; - self.welcomemessage_time = time + self.cvar_scr_centertime * 0.6; + if( !(time < restart_countdown) ) { //really print the WelcomeMessage to the player every frame when the game is restarted, to make sure that the shown number is accurate + if(self.welcomemessage_time > time) return; + self.welcomemessage_time = time + self.cvar_scr_centertime * 0.6; + } if(cvar("g_campaign")) { @@ -377,7 +379,14 @@ void PrintWelcomeMessage(entity pl) if ((g_lms && self.frags < 1) || g_arena) return centerprint_atprio(self, CENTERPRIO_SPAM, strcat(NEWLINES, "spectating ", self.enemy.netname, "\n\n\n^7press attack for next player\npress attack2 for free fly mode")); else - return centerprint_atprio(self, CENTERPRIO_SPAM, strcat(NEWLINES, "spectating ", self.enemy.netname, "\n\n\n^7press jump to play\n^7press attack for next player\npress attack2 for free fly mode")); + { + local string spectatorText; + spectatorText = strcat(NEWLINES, "spectating ", self.enemy.netname, "\n\n\n^7press jump to play\n^7press attack for next player\npress attack2 for free fly mode"); + + if(time < restart_countdown) //also show the countdown when being a spectator + spectatorText = strcat(spectatorText, "\n\n^1Game starts in ", ftos(restartAnnouncer.cnt + 1), " seconds^7"); + return centerprint_atprio(self, CENTERPRIO_SPAM, spectatorText); + } } } @@ -420,7 +429,7 @@ void PrintWelcomeMessage(entity pl) s = strcat(s, "^8\nactive modifications: ^3", modifications, "^8\n"); if(time < restart_countdown) - s = strcat(s, "\n^1Game starts in ", ftos(ceil(restart_countdown-time)), " seconds^7"); + s = strcat(s, "\n^1Game starts in ", ftos(restartAnnouncer.cnt + 1), " seconds^7"); s = strzone(s); -- 2.39.2