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