1 /*
2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
3 *
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
6 * source.
7 *
8 */
9
10
11
12
13 #include "network/multi_pause.h"
14 #include "missionui/chatbox.h"
15 #include "io/key.h"
16 #include "popup/popup.h"
17 #include "gamesequence/gamesequence.h"
18 #include "network/stand_gui.h"
19 #include "gamesnd/gamesnd.h"
20 #include "network/multiutil.h"
21 #include "network/multiui.h"
22 #include "network/multimsgs.h"
23 #include "network/multi_endgame.h"
24 #include "network/multi_pmsg.h"
25 #include "playerman/player.h"
26 #include "network/multi.h"
27 #include "globalincs/alphacolors.h"
28 #include "io/timer.h"
29
30
31
32 // ----------------------------------------------------------------------------------
33 // PAUSE DEFINES/VARS
34 //
35
36 // state of the game (paused or not) on _my_ machine. Obviously this is important for the server
37 // call multi_pause_reset() to reinitialize
38 int Multi_pause_status = 0;
39
40 // who paused the game
41 net_player *Multi_pause_pauser = NULL;
42
43 // timestamp for eating keypresses for a while after
44 float Multi_pause_eat = -1.0f;
45
46 // pause ui screen stuff
47 #define MULTI_PAUSED_NUM_BUTTONS 3
48
49 // button defs
50 #define MP_SCROLL_UP 0
51 #define MP_SCROLL_DOWN 1
52 #define MP_EXIT_MISSION 2
53
54 char *Multi_paused_bg_fname[GR_NUM_RESOLUTIONS] = {
55 "MPPause",
56 "2_MPPause"
57 };
58
59 char *Multi_paused_bg_mask[GR_NUM_RESOLUTIONS] = {
60 "MPPause-m",
61 "2_MPPause-m"
62 };
63
64 // where to place the pauser's callsign
65 int Mp_callsign_coords[GR_NUM_RESOLUTIONS][2] = {
66 { // GR_640
67 110, 132
68 },
69 { // GR_1024
70 171, 218
71 }
72 };
73
74 ui_button_info Multi_paused_buttons[GR_NUM_RESOLUTIONS][MULTI_PAUSED_NUM_BUTTONS] = {
75 { // GR_640
76 ui_button_info("PB00", 519, 212, -1, -1, 0),
77 ui_button_info("PB01", 519, 252, -1, -1, 1),
78 ui_button_info("PB02", 488, 321, -1, -1, 2),
79 },
80 { // GR_1024
81 ui_button_info("2_PB00", 831, 339, -1, -1, 0),
82 ui_button_info("2_PB01", 831, 403, -1, -1, 1),
83 ui_button_info("2_PB02", 781, 514, -1, -1, 2),
84 }
85 };
86
87 // text
88 #define MULTI_PAUSED_NUM_TEXT 3
89 UI_XSTR Multi_paused_text[GR_NUM_RESOLUTIONS][MULTI_PAUSED_NUM_BUTTONS] = {
90 { // GR_640
91 { "Exit", 1059, 493, 297, UI_XSTR_COLOR_PINK, -1, &Multi_paused_buttons[0][MP_EXIT_MISSION].button },
92 { "Mission", 1063, 482, 306, UI_XSTR_COLOR_PINK, -1, &Multi_paused_buttons[0][MP_EXIT_MISSION].button },
93 { "Mission Paused", 1440, 107, 356, UI_XSTR_COLOR_PINK, -1, NULL },
94 },
95 { // GR_1024
96 { "Exit", 1059, 787, 478, UI_XSTR_COLOR_PINK, -1, &Multi_paused_buttons[1][MP_EXIT_MISSION].button },
97 { "Mission", 1063, 778, 490, UI_XSTR_COLOR_PINK, -1, &Multi_paused_buttons[1][MP_EXIT_MISSION].button },
98 { "Mission Paused", 1440, 171, 567, UI_XSTR_COLOR_PINK, -1, NULL },
99 }
100 };
101
102
103 UI_WINDOW Multi_paused_window;
104 int Multi_paused_screen_id = -1; // backed up screen data
105 int Multi_paused_background = -1; // pause background
106
107 void multi_pause_check_buttons();
108 void multi_pause_button_pressed(int n);
109
110 // (server) evaluate a pause request from the given player (should call for himself as well)
111 void multi_pause_server_eval_request(net_player *pl, int pause);
112
113 // if this player can unpause
114 int multi_pause_can_unpause(net_player *p);
115
116 // render the callsign of the guy who paused
117 void multi_pause_render_callsign();
118
119 int Multi_paused = 0;
120
121 // ----------------------------------------------------------------------------------
122 // EXTERNAL FUNCTIONS/VARIABLES
123 //
124
125 extern void game_flush();
126
127 extern void weapon_pause_sounds();
128 extern void weapon_unpause_sounds();
129
130 extern void audiostream_pause_all();
131 extern void audiostream_unpause_all();
132
133 // ----------------------------------------------------------------------------------
134 // PAUSE FUNCTIONS
135 //
136
137 // re-initializes the pause system. call before entering the mission to reset
multi_pause_reset()138 void multi_pause_reset()
139 {
140 // set the pause status to 0
141 Multi_pause_status = 0;
142
143 // null out the pause pointer
144 Multi_pause_pauser = NULL;
145
146 // eat keys timestamp
147 Multi_pause_eat = -1.0f;
148 }
149
150 // (client) call when receiving a packet indicating we should pause
multi_pause_pause()151 void multi_pause_pause()
152 {
153 int idx;
154
155 // if we're already paused, don't do anything
156 if(Multi_pause_status){
157 return;
158 }
159
160 // sanity check
161 Assert(!Multi_pause_status);
162
163 // mark the game as being paused
164 Multi_pause_status = 1;
165
166 // if we're not already in the pause state
167 if(gameseq_get_state() != GS_STATE_MULTI_PAUSED){
168 // jump into the paused state
169 gameseq_post_event(GS_EVENT_MULTI_PAUSE);
170
171 // mark the netgame state
172 Netgame.game_state = NETGAME_STATE_PAUSED;
173 }
174
175 // if we're the server of the game, send a packet which will pause the clients in the game now
176 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
177 for(idx=0;idx<MAX_PLAYERS;idx++){
178 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
179 send_client_update_packet(&Net_players[idx]);
180 }
181 }
182 }
183 }
184
185 // (client) call when receiving a packet indicating we should unpause
multi_pause_unpause()186 void multi_pause_unpause()
187 {
188 int idx;
189
190 // if we're already unpaused, don't do anything
191 if(!Multi_pause_status){
192 return;
193 }
194
195 // sanity check
196 Assert(Multi_pause_status);
197
198 // mark the game as being unpaused
199 Multi_pause_status = 0;
200
201 // pop us out of any necessary states (including the pause state !!)
202 multi_handle_state_special();
203
204 // mark the netgame state
205 Netgame.game_state = NETGAME_STATE_IN_MISSION;
206
207 // if we're the server of the game, send a packet which will unpause the clients in the game now
208 // if we're the server of the game, send a packet which will pause the clients in the game now
209 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
210 for(idx=0;idx<MAX_PLAYERS;idx++){
211 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
212 send_client_update_packet(&Net_players[idx]);
213 }
214 }
215 }
216 }
217
218 // send a request to pause or unpause a game (all players should use this function)
multi_pause_request(int pause)219 void multi_pause_request(int pause)
220 {
221 // if i'm the server, run it through the eval function right now
222 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
223 multi_pause_server_eval_request(Net_player,pause);
224 }
225 // otherwise, send a reliable request packet to the server
226 else {
227 send_multi_pause_packet(pause);
228 }
229 }
230
231 // (server) evaluate a pause request from the given player (should call for himself as well)
multi_pause_server_eval_request(net_player * pl,int pause)232 void multi_pause_server_eval_request(net_player *pl, int pause)
233 {
234 int cur_state;
235
236 // if this is a pause request and we're already in the pause state, do nothing
237 if(pause && Multi_pause_status){
238 return;
239 }
240
241 // if this is an unpause request and we're already unpaused, do nothing
242 if(!pause && !Multi_pause_status){
243 return;
244 }
245
246 // get the current state (don't allow pausing from certain states
247 cur_state = gameseq_get_state();
248 if((cur_state == GS_STATE_DEBRIEF) || (cur_state == GS_STATE_MULTI_MISSION_SYNC) || (cur_state == GS_STATE_BRIEFING) ||
249 (cur_state == GS_STATE_STANDALONE_POSTGAME) || (cur_state == GS_STATE_MULTI_STD_WAIT) || (cur_state == GS_STATE_WEAPON_SELECT) ||
250 (cur_state == GS_STATE_TEAM_SELECT) || (cur_state == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
251 return;
252 }
253
254 // if this is a pause request
255 if(pause){
256 // record who the pauser is
257 Multi_pause_pauser = pl;
258
259 // pause the game
260 multi_pause_pause();
261 }
262 // if this is an unpause request
263 else {
264 // if this guy is allowed to unpause the game, do so
265 if(multi_pause_can_unpause(pl)){
266 // unmark the "pauser"
267 Multi_pause_pauser = NULL;
268
269 // unpause the game
270 multi_pause_unpause();
271 }
272 }
273 }
274
275 // if this player can unpause
multi_pause_can_unpause(net_player * p)276 int multi_pause_can_unpause(net_player *p)
277 {
278 if(!(p->flags & NETINFO_FLAG_GAME_HOST) && (p != Multi_pause_pauser)){
279 return 0;
280 }
281
282 return 1;
283 }
284
285 // if we still want to eat keys
multi_pause_eat_keys()286 int multi_pause_eat_keys()
287 {
288 // if the eat timestamp is negative, don't eat keys
289 if(Multi_pause_eat < 0.0f){
290 return 0;
291 }
292
293 // if less than 1 second has passed, continue eating keys
294 if((f2fl(timer_get_fixed_seconds()) - Multi_pause_eat) < 1.0f){
295 nprintf(("Network","PAUSE EATING KEYS\n"));
296
297 control_config_clear_used_status();
298 key_flush();
299
300 return 1;
301 }
302
303 // otherwise, disable the timestamp
304 Multi_pause_eat = -1.0f;
305
306 return 0;
307 }
308
309
310 // ----------------------------------------------------------------------------------
311 // PAUSE UI FUNCTIONS
312 //
313
multi_pause_init()314 void multi_pause_init()
315 {
316 int i;
317
318 // if we're already paused. do nothing
319 if ( Multi_paused ) {
320 return;
321 }
322
323 Assert( Game_mode & GM_MULTIPLAYER );
324
325 if ( !(Game_mode & GM_MULTIPLAYER) )
326 return;
327
328 // pause all beam weapon sounds
329 weapon_pause_sounds();
330
331 // standalone shouldn't be doing any freespace interface stuff
332 if (Game_mode & GM_STANDALONE_SERVER) {
333 std_debug_set_standalone_state_string("Multi paused do");
334 }
335 // everyone else should be doing UI stuff
336 else {
337 // pause all game music
338 audiostream_pause_all();
339
340 // switch off the text messaging system if it is active
341 multi_msg_text_flush();
342
343 if ( Multi_paused_screen_id == -1 )
344 Multi_paused_screen_id = gr_save_screen();
345
346 // create ui window
347 Multi_paused_window.create(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0);
348 Multi_paused_window.set_mask_bmap(Multi_paused_bg_mask[gr_screen.res]);
349 Multi_paused_background = bm_load(Multi_paused_bg_fname[gr_screen.res]);
350
351 for (i=0; i<MULTI_PAUSED_NUM_BUTTONS; i++) {
352 // create the button
353 Multi_paused_buttons[gr_screen.res][i].button.create(&Multi_paused_window, "", Multi_paused_buttons[gr_screen.res][i].x, Multi_paused_buttons[gr_screen.res][i].y, 1, 1, 0, 1);
354
355 // set the highlight action
356 Multi_paused_buttons[gr_screen.res][i].button.set_highlight_action(common_play_highlight_sound);
357
358 // set the ani
359 Multi_paused_buttons[gr_screen.res][i].button.set_bmaps(Multi_paused_buttons[gr_screen.res][i].filename);
360
361 // set the hotspot
362 Multi_paused_buttons[gr_screen.res][i].button.link_hotspot(Multi_paused_buttons[gr_screen.res][i].hotspot);
363 }
364
365 // add text
366 for(i=0; i<MULTI_PAUSED_NUM_TEXT; i++){
367 Multi_paused_window.add_XSTR(&Multi_paused_text[gr_screen.res][i]);
368 }
369
370 // close any instances of a chatbox
371 chatbox_close();
372
373 // intialize our custom chatbox
374 chatbox_create(CHATBOX_FLAG_MULTI_PAUSED);
375 }
376
377 Multi_paused = 1;
378
379 // reset timestamps
380 multi_reset_timestamps();
381 }
382
multi_pause_do()383 void multi_pause_do()
384 {
385 int k;
386
387 // make sure we don't enter this state unless we're in the mission itself
388 Netgame.game_state = NETGAME_STATE_PAUSED;
389
390 // server of the game should periodically be sending pause packets for good measure
391 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
392 }
393
394 if (!(Game_mode & GM_STANDALONE_SERVER)) {
395 // restore saved screen data if any
396 if (Multi_paused_screen_id >= 0) {
397 gr_restore_screen(Multi_paused_screen_id);
398 }
399
400 // set the background image
401 if (Multi_paused_background >= 0) {
402 gr_set_bitmap(Multi_paused_background);
403 gr_bitmap(0, 0, GR_RESIZE_MENU);
404 }
405
406 // if we're inside of popup code right now, don't process the window
407 if(!popup_active()){
408 // process chatbox and window stuff
409 k = chatbox_process();
410 k = Multi_paused_window.process(k);
411
412 switch (k) {
413 case KEY_ESC:
414 case KEY_PAUSE:
415 multi_pause_request(0);
416 break;
417 }
418 }
419
420 // check for any button presses
421 multi_pause_check_buttons();
422
423 // render the callsign of the guy who paused
424 multi_pause_render_callsign();
425
426 // render the chatbox
427 chatbox_render();
428
429 // draw tooltips
430 // Multi_paused_window.draw_tooltip();
431 Multi_paused_window.draw();
432
433 // display the voice status indicator
434 multi_common_voice_display_status();
435
436 // don't flip screen if we are in the popup code right now
437 if (!popup_active()) {
438 gr_flip();
439 }
440 }
441 // standalone pretty much does nothing here
442 else {
443 Sleep(1);
444 }
445 }
446
multi_pause_close(int end_mission)447 void multi_pause_close(int end_mission)
448 {
449 if ( !Multi_paused )
450 return;
451
452 // set the standalonest
453 if (Game_mode & GM_STANDALONE_SERVER) {
454 std_debug_set_standalone_state_string("Game play");
455 } else {
456 // free the screen up
457 if ( end_mission && (Multi_paused_screen_id >= 0) ) {
458 gr_free_screen(Multi_paused_screen_id);
459 Multi_paused_screen_id = -1;
460 }
461
462 if (Multi_paused_background >= 0) {
463 bm_release(Multi_paused_background);
464 Multi_paused_background = -1;
465 }
466
467 Multi_paused_window.destroy();
468 game_flush();
469
470 // unpause all the music
471 audiostream_unpause_all();
472 }
473
474 // unpause beam weapon sounds
475 weapon_unpause_sounds();
476
477 // eat keys timestamp
478 Multi_pause_eat = f2fl(timer_get_fixed_seconds());
479
480 // reset timestamps
481 multi_reset_timestamps();
482
483 // clear out control config and keypress info
484 control_config_clear_used_status();
485 key_flush();
486
487 Multi_paused = 0;
488 }
489
multi_pause_check_buttons()490 void multi_pause_check_buttons()
491 {
492 int idx;
493
494 // process any pause buttons which may have been pressed
495 for (idx=0; idx<MULTI_PAUSED_NUM_BUTTONS; idx++){
496 if (Multi_paused_buttons[gr_screen.res][idx].button.pressed()){
497 multi_pause_button_pressed(idx);
498 }
499 }
500 }
501
multi_pause_button_pressed(int n)502 void multi_pause_button_pressed(int n)
503 {
504 switch (n) {
505 // the scroll up button
506 case MP_SCROLL_UP :
507 chatbox_scroll_up();
508 break;
509
510 // the scroll down button
511 case MP_SCROLL_DOWN :
512 chatbox_scroll_down();
513 break;
514
515 // the exit mission
516 case MP_EXIT_MISSION :
517 multi_quit_game(PROMPT_ALL);
518 break;
519 }
520 }
521
522 // render the callsign of the guy who paused
multi_pause_render_callsign()523 void multi_pause_render_callsign()
524 {
525 char pause_str[100];
526
527 // write out the callsign of the player who paused the game
528 if((Multi_pause_pauser != NULL) && (Multi_pause_pauser->m_player != NULL)){
529 memset(pause_str,0,100);
530 strcpy_s(pause_str,Multi_pause_pauser->m_player->callsign);
531
532 // blit it
533 gr_set_color_fast(&Color_bright);
534 gr_string(Mp_callsign_coords[gr_screen.res][0], Mp_callsign_coords[gr_screen.res][1], pause_str, GR_RESIZE_MENU);
535 }
536 }
537