r3661 - trunk/data/qcsrc/server
DONOTREPLY at icculus.org
DONOTREPLY at icculus.org
Sun May 25 07:18:22 EDT 2008
Author: greenmarine
Date: 2008-05-25 07:18:15 -0400 (Sun, 25 May 2008)
New Revision: 3661
Modified:
trunk/data/qcsrc/server/cl_client.qc
trunk/data/qcsrc/server/cl_impulse.qc
trunk/data/qcsrc/server/cl_weaponsystem.qc
trunk/data/qcsrc/server/clientcommands.qc
trunk/data/qcsrc/server/defs.qh
trunk/data/qcsrc/server/g_hook.qc
trunk/data/qcsrc/server/g_world.qc
trunk/data/qcsrc/server/miscfunctions.qc
trunk/data/qcsrc/server/teamplay.qc
Log:
Modified: trunk/data/qcsrc/server/cl_client.qc
===================================================================
--- trunk/data/qcsrc/server/cl_client.qc 2008-05-24 19:55:22 UTC (rev 3660)
+++ trunk/data/qcsrc/server/cl_client.qc 2008-05-25 11:18:15 UTC (rev 3661)
@@ -979,6 +979,7 @@
}
self.jointime = time;
+ self.allowedTimeouts = cvar("sv_timeout_number");
}
/*
@@ -1153,6 +1154,47 @@
PutClientInServer();
}
+/**
+ * When sv_timeout is used this function returs strings like
+ * "Timeout begins in 2 seconds!\n" or "Timeout ends in 23 seconds!\n".
+ * Called by centerprint functions
+ * @param addOneSecond boolean, set to 1 if the welcome-message centerprint asks for the text
+ */
+string getTimeoutText(float addOneSecond) {
+ if (!cvar("sv_timeout") || !timeoutStatus)
+ return "";
+
+ local string retStr;
+ if (timeoutStatus == 1) {
+ if (addOneSecond == 1) {
+ retStr = strcat("Timeout begins in ", ftos(remainingLeadTime + 1), " seconds!\n");
+ }
+ else {
+ retStr = strcat("Timeout begins in ", ftos(remainingLeadTime), " seconds!\n");
+ }
+ return retStr;
+ }
+ else if (timeoutStatus == 2) {
+ if (addOneSecond) {
+ retStr = strcat("Timeout ends in ", ftos(remainingTimeoutTime + 1), " seconds!\n");
+ //don't show messages like "Timeout ends in 0 seconds"...
+ if ((remainingTimeoutTime + 1) > 0)
+ return retStr;
+ else
+ return "";
+ }
+ else {
+ retStr = strcat("Timeout ends in ", ftos(remainingTimeoutTime), " seconds!\n");
+ //don't show messages like "Timeout ends in 0 seconds"...
+ if (remainingTimeoutTime > 0)
+ return retStr;
+ else
+ return "";
+ }
+ }
+ else return "";
+}
+
void player_powerups (void)
{
if (g_minstagib)
@@ -1652,6 +1694,13 @@
if(frametime > 0) // don't do this in cl_movement frames, just in server ticks
UpdateSelectedPlayer();
+ //don't allow the player to turn around while game is paused!
+ if(timeoutStatus == 2) {
+ self.v_angle = self.lastV_angle;
+ self.angles = self.lastV_angle;
+ self.fixangle = TRUE;
+ }
+
if (self.deadflag != DEAD_NO)
{
float button_pressed, force_respawn;
Modified: trunk/data/qcsrc/server/cl_impulse.qc
===================================================================
--- trunk/data/qcsrc/server/cl_impulse.qc 2008-05-24 19:55:22 UTC (rev 3660)
+++ trunk/data/qcsrc/server/cl_impulse.qc 2008-05-25 11:18:15 UTC (rev 3661)
@@ -63,6 +63,10 @@
if (!imp || gameover)
return;
self.impulse = 0;
+
+ if (timeoutStatus == 2) //don't allow any impulses while the game is paused
+ return;
+
if (imp >= 1 && imp <= 12)
{
// weapon switching impulses
Modified: trunk/data/qcsrc/server/cl_weaponsystem.qc
===================================================================
--- trunk/data/qcsrc/server/cl_weaponsystem.qc 2008-05-24 19:55:22 UTC (rev 3660)
+++ trunk/data/qcsrc/server/cl_weaponsystem.qc 2008-05-25 11:18:15 UTC (rev 3661)
@@ -437,6 +437,10 @@
self.cnt = self.weapon;
return FALSE;
}
+
+ if (timeoutStatus == 2) //don't allow the player to shoot while game is paused
+ return FALSE;
+
// don't fire if previous attack is not finished
if (ATTACK_FINISHED(self) > time + frametime * 0.5)
return FALSE;
Modified: trunk/data/qcsrc/server/clientcommands.qc
===================================================================
--- trunk/data/qcsrc/server/clientcommands.qc 2008-05-24 19:55:22 UTC (rev 3660)
+++ trunk/data/qcsrc/server/clientcommands.qc 2008-05-25 11:18:15 UTC (rev 3661)
@@ -242,6 +242,9 @@
if(tourneyInMatchStage && cvar("g_tourney_disable_spec_vote") && self.classname != "player") {
sprint(self, "^1Error: Only players can call a vote during the match-stage.\n");
}
+ else if(timeoutStatus) { //don't allow a vote call during a timeout
+ sprint(self, "^1Error: You can not call a vote while a timeout is active.\n");
+ }
else if(votecalled) {
sprint(self, "^1There is already a vote called.\n");
} else {
@@ -504,6 +507,10 @@
} else if(argv(0) == "ready") {
if(cvar("sv_ready_restart"))
{
+ if(timeoutStatus) {
+ return sprint(self, "^1You cannot reset the game while a timeout is active!\n");
+ }
+
if(!restart_countdown || cvar("sv_ready_restart_repeatable"))
{
self.ready = TRUE;
@@ -558,6 +565,21 @@
wordwrap_sprint(cmd, 1111);
} else if(argv(0) == "suggestmap") {
sprint(self, strcat(MapVote_Suggest(argv(1)), "\n"));
+ } else if(argv(0) == "calltimeout") {
+ if(cvar("sv_timeout")) {
+ if(self.classname == "player") {
+ if(votecalled)
+ sprint(self, "^7Error: you can not call a timeout while a vote is active!\n");
+ else
+ evaluateTimeoutCall();
+ }
+ else
+ sprint(self, "^7Error: only players can call a timeout!\n");
+ }
+ } else if(argv(0) == "resumegame") {
+ if(cvar("sv_timeout")) {
+ evaluateResumeGame();
+ }
} else {
cmd = argv(0);
/* checks not needed any more since DP has separated clientcommands and regular commands
@@ -951,6 +973,15 @@
restartAnnouncer.think = restartAnnouncer_Think;
restartAnnouncer.nextthink = time;
restartAnnouncer.cnt = RESTART_COUNTDOWN;
+
+ //after a restart every players number of allowed timeouts gets reset, too
+ if(cvar("sv_timeout"))
+ {
+ FOR_EACH_REALPLAYER(e)
+ {
+ e.allowedTimeouts = cvar("sv_timeout_number");
+ }
+ }
//play the prepareforbattle sound to everyone
sound(world, CHAN_AUTO, "announcer/robotic/prepareforbattle.wav", 1, ATTN_NONE);
@@ -1038,3 +1069,87 @@
self.cnt -= 1;
}
}
+
+/**
+ * Checks whether the player who calls the timeout is allowed to do so.
+ * If so, it initializes the timeout countdown. It also checks whether another
+ * timeout was already running at this time and reacts correspondingly.
+ *
+ * affected globals/fields: .allowedTimeouts, remainingTimeoutTime, remainingLeadTime,
+ * timeoutInitiator, timeoutStatus, timeoutHandler
+ *
+ * This function is called when a player issues the calltimeout command.
+ */
+void evaluateTimeoutCall() {
+ if (g_tourney && !tourneyInMatchStage && !cvar("g_tourney_warmup_allow_timeout"))
+ return sprint(self, "^7Error: You can not call a timeout in warmup-stage!\n");
+ if (time < restart_countdown )
+ return sprint(self, "^7Error: You can not call a timeout while the map is being restarted!\n");
+ if (timeoutStatus != 2) {
+ //if the map uses a timelimit make sure that timeout cannot be called right before the map ends
+ if (cvar("timelimit")) {
+ //a timelimit was used
+ local float myTl;
+ if (cvar("timelimit"))
+ myTl = cvar("timelimit");
+ else
+ myTl = timelimit_orig;
+
+ local float lastPossibleTimeout;
+ lastPossibleTimeout = (myTl*60) - cvar("sv_timeout_leadtime") - 1;
+
+ if (lastPossibleTimeout < time)
+ return sprint(self, "^7Error: It is too late to call a timeout now!\n");
+ }
+ }
+ //player may not call a timeout if he has no calls left
+ if (self.allowedTimeouts < 1)
+ return sprint(self, "^7Error: You already used all your timeout calls for this map!\n");
+ //now all required checks are passed
+ self.allowedTimeouts -= 1;
+ bprint(self.netname, " ^7called a timeout (", ftos(self.allowedTimeouts), " timeouts left)!\n"); //write a bprint who started the timeout (and how many he has left)
+ remainingTimeoutTime = cvar("sv_timeout_length");
+ remainingLeadTime = cvar("sv_timeout_leadtime");
+ timeoutInitiator = self;
+ if (timeoutStatus == 0) { //if another timeout was already active, don't change its status (which was 1 or 2) to 1, only change it to 1 if no timeout was active yet
+ timeoutStatus = 1;
+ //create the timeout indicator which centerprints the information to all players and takes care of pausing/unpausing
+ timeoutHandler = spawn();
+ timeoutHandler.think = timeoutHandler_Think;
+ }
+ timeoutHandler.nextthink = time; //always let the entity think asap
+
+ //inform all connected clients about the timeout call
+ sound(world, CHAN_AUTO, "announcer/robotic/timeoutcalled.wav", 1, ATTN_NONE);
+}
+
+/**
+ * Checks whether a player is allowed to resume the game. If he is allowed to do it,
+ * and the lead time for the timeout is still active, this countdown just will be aborted (the
+ * game will never be paused). Otherwise the remainingTimeoutTime will be set to the corresponding
+ * value of the cvar sv_timeout_resumetime.
+ *
+ * This function is called when a player issues the resumegame command.
+ */
+void evaluateResumeGame() {
+ if (!timeoutStatus)
+ return sprint(self, "^7Error: There is no active timeout which could be aborted!\n");
+ if (self != timeoutInitiator)
+ return sprint(self, "^7Error: You may not abort the active timeout. Only the player who called it can do that!\n");
+ if (timeoutStatus == 1) {
+ remainingTimeoutTime = timeoutStatus = 0;
+ timeoutHandler.nextthink = time; //timeoutHandler has to take care of it immediately
+ bprint(strcat("^7The timeout was aborted by ", self.netname, " !\n"));
+ }
+ else if (timeoutStatus == 2) {
+ //only shorten the remainingTimeoutTime if it makes sense
+ if( remainingTimeoutTime > (cvar("sv_timeout_resumetime") + 1) ) {
+ bprint(strcat("^1Attention: ^7", self.netname, " resumed the game! Prepare for battle!\n"));
+ remainingTimeoutTime = cvar("sv_timeout_resumetime");
+ timeoutHandler.nextthink = time; //timeoutHandler has to take care of it immediately
+ }
+ else
+ sprint(self, "^7Error: Your resumegame call was discarded!\n");
+
+ }
+}
Modified: trunk/data/qcsrc/server/defs.qh
===================================================================
--- trunk/data/qcsrc/server/defs.qh 2008-05-24 19:55:22 UTC (rev 3660)
+++ trunk/data/qcsrc/server/defs.qh 2008-05-25 11:18:15 UTC (rev 3661)
@@ -292,6 +292,23 @@
float isJoinAllowed();
#define PREVENT_JOIN_TEXT "^1You may not join the game at this time.\n\nThe player limit reached maximum capacity."
+
+//sv_timeout: pauses the game by setting the gamespeed to a really low value (see TIMEOUT_SLOWMO_VALUE)
+#define TIMEOUT_SLOWMO_VALUE 0.0001
+float sys_ticrate; // gets initialised in worlspawn, saves the value from cvar("sys_ticrate")
+float remainingTimeoutTime; // contains the time in seconds that the active timeout has left
+float remainingLeadTime; // contains the number of seconds left of the leadtime (before the timeout starts)
+float timeoutStatus; // (values: 0, 1, 2) contains whether a timeout is not active (0), was called but still at leadtime (1) or is active (2)
+.float allowedTimeouts; // contains the number of allowed timeouts for each player
+entity timeoutInitiator; // contains the entity of the player who started the last timeout
+float orig_slowmo; // contains the value of cvar("slowmo") so that, after timeout finished, it isn't set to slowmo 1 necessarily
+.vector lastV_angle; //used when pausing the game in order to force the player to keep his old view angle fixed
+entity timeoutHandler; //responsible for centerprinting the timeout countdowns and playing sounds
+void timeoutHandler_Think();
+void evaluateTimeoutCall();
+void evaluateResumeGame();
+string getTimeoutText(float addOneSecond);
+
.float spawnshieldtime;
.float lms_nextcheck;
Modified: trunk/data/qcsrc/server/g_hook.qc
===================================================================
--- trunk/data/qcsrc/server/g_hook.qc 2008-05-24 19:55:22 UTC (rev 3660)
+++ trunk/data/qcsrc/server/g_hook.qc 2008-05-25 11:18:15 UTC (rev 3661)
@@ -249,7 +249,8 @@
if (self.button6 && g_grappling_hook)
{
if (!self.hook && self.hook_time <= time && !self.button6_pressed_before)
- FireGrapplingHook();
+ if (timeoutStatus != 2) //only allow the player to fire the grappling hook if the game is not paused (timeout)
+ FireGrapplingHook();
}
else
{
Modified: trunk/data/qcsrc/server/g_world.qc
===================================================================
--- trunk/data/qcsrc/server/g_world.qc 2008-05-24 19:55:22 UTC (rev 3660)
+++ trunk/data/qcsrc/server/g_world.qc 2008-05-25 11:18:15 UTC (rev 3661)
@@ -55,6 +55,82 @@
world.frags = 0;
}
+/**
+ * Takes care of pausing and unpausing the game.
+ * Centerprints the information about an upcoming or active timeout to all active
+ * players. Also plays reminder sounds.
+ */
+void timeoutHandler_Think() {
+ local string timeStr;
+ local entity plr;
+ if (timeoutStatus == 1) {
+ if (remainingLeadTime > 0) {
+ //centerprint the information to every player
+ timeStr = getTimeoutText(0);
+ FOR_EACH_REALCLIENT(plr) {
+ if(plr.classname == "player") {
+ centerprint_atprio(plr, CENTERPRIO_SPAM, timeStr);
+ }
+ }
+ remainingLeadTime -= 1;
+ //think again in 1 second:
+ self.nextthink = time + 1;
+ }
+ else {
+ //now pause the game:
+ timeoutStatus = 2;
+ cvar_set("slowmo", ftos(TIMEOUT_SLOWMO_VALUE));
+ //copy .v_angle to .lastV_angle for every player in order to fix their view during pause (see PlayerPreThink)
+ FOR_EACH_REALPLAYER(plr) {
+ plr.lastV_angle = plr.v_angle;
+ }
+ self.nextthink = time;
+ }
+ }
+ else if (timeoutStatus == 2) {
+ if (remainingTimeoutTime > 0) {
+ timeStr = getTimeoutText(0);
+ FOR_EACH_REALCLIENT(plr) {
+ if(plr.classname == "player") {
+ centerprint_atprio(plr, CENTERPRIO_SPAM, timeStr);
+ }
+ }
+ if(remainingTimeoutTime == cvar("sv_timeout_resumetime")) { //play a warning sound when only <sv_timeout_resumetime> seconds are left
+ sound(world, CHAN_AUTO, "announcer/robotic/prepareforbattle.wav", 1, ATTN_NONE);
+ }
+ remainingTimeoutTime -= 1;
+ self.nextthink = time + TIMEOUT_SLOWMO_VALUE;
+ }
+ else {
+ //unpause the game again
+ remainingTimeoutTime = timeoutStatus = 0;
+ cvar_set("slowmo", ftos(orig_slowmo));
+ //and unlock the fixed view again once there is no timeout active anymore
+ FOR_EACH_REALPLAYER(plr) {
+ plr.fixangle = FALSE;
+ }
+ //get rid of the countdown message
+ FOR_EACH_REALCLIENT(plr) {
+ if(plr.classname == "player") {
+ centerprint_atprio(plr, CENTERPRIO_SPAM, "");
+ }
+ }
+ remove(self);
+ return;
+ }
+
+ }
+ else if (timeoutStatus == 0) { //if a player called the resumegame command (which set timeoutStatus to 0 already)
+ FOR_EACH_REALCLIENT(plr) {
+ if(plr.classname == "player") {
+ centerprint_atprio(plr, CENTERPRIO_SPAM, "");
+ }
+ }
+ remove(self);
+ return;
+ }
+}
+
float GotoFirstMap()
{
if(cvar("_sv_init"))
@@ -240,6 +316,10 @@
Ban_LoadBans();
+ //initialise globals related to sv_timeout
+ sys_ticrate = cvar("sys_ticrate");
+ orig_slowmo = cvar("slowmo");
+
#ifdef MAPINFO
MapInfo_Enumerate();
MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), 1);
Modified: trunk/data/qcsrc/server/miscfunctions.qc
===================================================================
--- trunk/data/qcsrc/server/miscfunctions.qc 2008-05-24 19:55:22 UTC (rev 3660)
+++ trunk/data/qcsrc/server/miscfunctions.qc 2008-05-25 11:18:15 UTC (rev 3661)
@@ -638,7 +638,10 @@
if(prio >= e.centerprint_priority)
{
e.centerprint_priority = prio;
- e.centerprint_expires = time + e.cvar_scr_centertime;
+ if(timeoutStatus == 2)
+ e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
+ else
+ e.centerprint_expires = time + e.cvar_scr_centertime;
centerprint_builtin(e, s);
}
}
@@ -1038,8 +1041,9 @@
precache_sound ("announcer/male/yoda.wav");
// announcer sounds - robotic
+ precache_sound ("announcer/robotic/prepareforbattle.wav");
precache_sound ("announcer/robotic/begin.wav");
- precache_sound ("announcer/robotic/prepareforbattle.wav");
+ precache_sound ("announcer/robotic/timeoutcalled.wav");
precache_sound ("announcer/robotic/1fragleft.wav");
precache_sound ("announcer/robotic/1minuteremains.wav");
precache_sound ("announcer/robotic/2fragsleft.wav");
Modified: trunk/data/qcsrc/server/teamplay.qc
===================================================================
--- trunk/data/qcsrc/server/teamplay.qc 2008-05-24 19:55:22 UTC (rev 3660)
+++ trunk/data/qcsrc/server/teamplay.qc 2008-05-25 11:18:15 UTC (rev 3661)
@@ -354,7 +354,7 @@
self.welcomemessage_time = time + 0.8; */
if(self.cvar_scr_centertime == 0) return;
- 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( !(timeoutStatus >= 1 || (time < restart_countdown) ) ) { //really print the WelcomeMessage to the player every frame when timeout-seconds are shown or 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;
}
@@ -378,14 +378,15 @@
{
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
- {
- 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");
-
+ else {
+ local string specString;
+ specString = 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);
+ specString = strcat(specString, "\n\n^1Game starts in ", ftos(restartAnnouncer.cnt + 1), " seconds^7");
+ else if (timeoutStatus != 0)
+ specString = strcat(specString, "\n\n", getTimeoutText(1));
+ return centerprint_atprio(self, CENTERPRIO_SPAM, specString);
}
}
}
@@ -444,6 +445,9 @@
if(time < restart_countdown)
s = strcat(s, "\n^1Game starts in ", ftos(restartAnnouncer.cnt + 1), " seconds^7");
+ if(timeoutStatus != 0)
+ s = strcat(s, "\n\n", getTimeoutText(1));
+
s = strzone(s);
if (g_grappling_hook)
More information about the nexuiz-commits
mailing list