1 /** @file pause.cpp Pausing the game.
2 *
3 * @authors Copyright © 1999-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2006-2014 Daniel Swanson <danij@dengine.net>
5 * @authors Copyright © 1993-1996 id Software, Inc.
6 *
7 * @par License
8 * GPL: http://www.gnu.org/licenses/gpl.html
9 *
10 * <small>This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version. This program is distributed in the hope that it
14 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16 * Public License for more details. You should have received a copy of the GNU
17 * General Public License along with this program; if not, write to the Free
18 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19 * 02110-1301 USA</small>
20 */
21
22 #include "common.h"
23 #include "pause.h"
24
25 #include "d_net.h"
26 #include "d_netcl.h"
27 #include "d_netsv.h"
28 #include "g_common.h"
29 #include "hu_menu.h"
30 #include "hu_msg.h"
31
32 using namespace de;
33 using namespace common;
34
35 #define PAUSEF_PAUSED 0x1
36 #define PAUSEF_FORCED_PERIOD 0x2
37
38 int paused;
39
40 static int gamePauseWhenFocusLost; // cvar
41 static int gameUnpauseWhenFocusGained; // cvar
42
43 #ifdef __JDOOM__
44 /// How long to pause the game after a map has been loaded (cvar).
45 /// - -1: matches the engine's busy transition tics.
46 static int gamePauseAfterMapStartTics = -1; // cvar
47 #else
48 // Crossfade doesn't require a very long pause.
49 static int gamePauseAfterMapStartTics = 7; // cvar
50 #endif
51
52 static int forcedPeriodTicsRemaining;
53
beginPause(int flags)54 static void beginPause(int flags)
55 {
56 if(!paused)
57 {
58 paused = PAUSEF_PAUSED | flags;
59
60 // This will stop all sounds from all origins.
61 /// @todo Would be nice if the engine supported actually pausing the sounds. -jk
62 S_StopSound(0, 0);
63
64 // Servers are responsible for informing clients about
65 // pauses in the game.
66 NetSv_Paused(paused);
67 }
68 }
69
endPause()70 static void endPause()
71 {
72 if(paused)
73 {
74 LOG_VERBOSE("Pause ends (state:%i)") << paused;
75
76 forcedPeriodTicsRemaining = 0;
77
78 if(!(paused & PAUSEF_FORCED_PERIOD))
79 {
80 // Any impulses or accumulated relative offsets that occured
81 // during the pause should be ignored.
82 DD_Execute(true, "resetctlaccum");
83 }
84
85 NetSv_Paused(0);
86 }
87 paused = 0;
88 }
89
checkForcedPeriod()90 static void checkForcedPeriod()
91 {
92 if((paused != 0) && (paused & PAUSEF_FORCED_PERIOD))
93 {
94 if(forcedPeriodTicsRemaining-- <= 0)
95 {
96 endPause();
97 }
98 }
99 }
100
Pause_IsPaused()101 dd_bool Pause_IsPaused()
102 {
103 return (paused != 0) || (!IS_NETGAME && (Hu_MenuIsActive() || Hu_IsMessageActive()));
104 }
105
Pause_IsUserPaused()106 dd_bool Pause_IsUserPaused()
107 {
108 return (paused != 0) && !(paused & PAUSEF_FORCED_PERIOD);
109 }
110
D_CMD(Pause)111 D_CMD(Pause)
112 {
113 DENG2_UNUSED3(src, argc, argv);
114
115 if(G_QuitInProgress())
116 return false;
117
118 // Toggle pause.
119 Pause_Set(!(paused & PAUSEF_PAUSED));
120 return true;
121 }
122
Pause_Set(dd_bool yes)123 void Pause_Set(dd_bool yes)
124 {
125 // Can we start a pause?
126 if(Hu_MenuIsActive() || Hu_IsMessageActive() || IS_CLIENT)
127 return; // Nope.
128
129 if(yes)
130 beginPause(0);
131 else
132 endPause();
133 }
134
Pause_End()135 void Pause_End()
136 {
137 endPause();
138 }
139
Pause_SetForcedPeriod(int tics)140 void Pause_SetForcedPeriod(int tics)
141 {
142 if(tics <= 0) return;
143
144 LOG_MSG("Forced pause for %i tics") << tics;
145
146 forcedPeriodTicsRemaining = tics;
147 beginPause(PAUSEF_FORCED_PERIOD);
148 }
149
Pause_Ticker()150 void Pause_Ticker()
151 {
152 checkForcedPeriod();
153 }
154
Pause_Responder(event_t * ev)155 dd_bool Pause_Responder(event_t *ev)
156 {
157 if(ev->type == EV_FOCUS)
158 {
159 if(gamePauseWhenFocusLost && !ev->data1)
160 {
161 Pause_Set(true);
162 return true;
163 }
164 else if(gameUnpauseWhenFocusGained && ev->data1)
165 {
166 Pause_Set(false);
167 return true;
168 }
169 }
170 return false;
171 }
172
Pause_MapStarted()173 void Pause_MapStarted()
174 {
175 if(!IS_CLIENT)
176 {
177 if(gamePauseAfterMapStartTics < 0)
178 {
179 // Use the engine's transition visualization duration.
180 Pause_SetForcedPeriod(Con_GetInteger("con-transition-tics"));
181 }
182 else
183 {
184 // Use the configured time.
185 Pause_SetForcedPeriod(gamePauseAfterMapStartTics);
186 }
187 }
188 }
189
Pause_Register()190 void Pause_Register()
191 {
192 forcedPeriodTicsRemaining = 0;
193
194 // Default values (overridden by values from .cfg files).
195 gamePauseWhenFocusLost = true;
196 gameUnpauseWhenFocusGained = false;
197
198 C_CMD("pause", "", Pause);
199
200 #define READONLYCVAR (CVF_READ_ONLY|CVF_NO_MAX|CVF_NO_MIN|CVF_NO_ARCHIVE)
201
202 C_VAR_INT("game-paused", &paused, READONLYCVAR, 0, 0);
203 C_VAR_INT("game-pause-focuslost", &gamePauseWhenFocusLost, 0, 0, 1);
204 C_VAR_INT("game-unpause-focusgained", &gameUnpauseWhenFocusGained, 0, 0, 1);
205 C_VAR_INT("game-pause-mapstart-tics", &gamePauseAfterMapStartTics, 0, -1, 70);
206
207 #undef READONLYCVAR
208 }
209
NetSv_Paused(int pauseState)210 void NetSv_Paused(int pauseState)
211 {
212 if(!IS_SERVER || !IS_NETGAME)
213 return;
214
215 writer_s *writer = D_NetWrite();
216 Writer_WriteByte(writer,
217 (pauseState & PAUSEF_PAUSED? 1 : 0) |
218 (pauseState & PAUSEF_FORCED_PERIOD? 2 : 0));
219 Net_SendPacket(DDSP_ALL_PLAYERS, GPT_PAUSE, Writer_Data(writer), Writer_Size(writer));
220 }
221
NetCl_Paused(reader_s * msg)222 void NetCl_Paused(reader_s *msg)
223 {
224 byte flags = Reader_ReadByte(msg);
225 paused = 0;
226 if(flags & 1)
227 {
228 paused |= PAUSEF_PAUSED;
229 }
230 if(flags & 2)
231 {
232 paused |= PAUSEF_FORCED_PERIOD;
233 }
234 DD_SetInteger(DD_CLIENT_PAUSED, paused != 0);
235 }
236