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 "cfile/cfile.h"
14 #include "gamesnd/gamesnd.h"
15 #include "globalincs/alphacolors.h"
16 #include "graphics/font.h"
17 #include "io/key.h"
18 #include "io/timer.h"
19 #include "menuui/optionsmenu.h"
20 #include "menuui/optionsmenumulti.h"
21 #include "network/multi.h"
22 #include "network/multi_voice.h"
23 #include "osapi/osregistry.h"
24 #include "parse/parselo.h"
25 #include "playerman/player.h"
26 #include "popup/popup.h"
27 #include "sound/ds.h"
28 #include "sound/rtvoice.h"
29
30
31
32 // general data section ------------------------------------------------
33 UI_WINDOW *Om_window = NULL;
34
35 static const char* Om_background_0_fname[GR_NUM_RESOLUTIONS] = {
36 "OptionsMultiGen", // GR_640
37 "2_OptionsMultiGen" // GR_1024
38 };
39
40 static const char* Om_background_0_mask_fname[GR_NUM_RESOLUTIONS] = {
41 "OptionsMultiGen-M", // GR_640
42 "2_OptionsMultiGen-M" // GR_1024
43 };
44
45 static const char* Om_background_1_fname[GR_NUM_RESOLUTIONS] = {
46 "OptionsMultiVox", // GR_640
47 "2_OptionsMultiVox" // GR_1024
48 };
49
50 static const char* Om_background_1_mask_fname[GR_NUM_RESOLUTIONS] = {
51 "OptionsMultiVox-M", // GR_640
52 "2_OptionsMultiVox-M" // GR_1024
53 };
54
55 int Om_background_0 = -1;
56 int Om_mask_0 = -1;
57
58 int Om_background_1 = -1;
59 int Om_mask_1 = -1;
60
61 #define OM_NOTIFICATION_LINE_LEN 255
62
63 // screen modes
64 #define OM_MODE_NONE -1 // no mode (unintialized)
65 #define OM_MODE_GENERAL 0 // general tab
66 #define OM_MODE_VOX 1 // voice tab
67 int Om_mode = OM_MODE_NONE;
68
69 // notification stuff
70 #define OM_NOTIFY_TIME 8000
71 #define OM_NOTIFY_Y 430
72 #define OM_NOTIFY_Y2 440
73 int Om_notify_stamp = -1;
74 char Om_notify_string[255];
75
76 // load all background bitmaps
77 void options_multi_load_bmaps();
78
79 // unload all the background bitmaps
80 void options_multi_unload_bmaps();
81
82 // add a notification message
83 void options_multi_add_notify(const char *str);
84
85 // process and blit any notification messages
86 void options_multi_notify_process();
87
88
89 // protocol options section -------------------------------------------
90 #define OM_PRO_NUM_BUTTONS 10
91
92 #define OM_PRO_TCP 0
93 #define OM_PRO_IPX 1
94 #define OM_PRO_SCROLL_IP_UP 2
95 #define OM_PRO_SCROLL_IP_DOWN 3
96 #define OM_PRO_ADD_IP 4
97 #define OM_PRO_DELETE_IP 5
98 #define OM_PRO_LOCAL_BROADCAST 6
99 #define OM_PRO_VMT 7
100 #define OM_PRO_VOX_TAB 8
101 #define OM_PRO_GEN_TAB 9
102
103 ui_button_info Om_pro_buttons[GR_NUM_RESOLUTIONS][OM_PRO_NUM_BUTTONS] = {
104 { // GR_640
105 ui_button_info("OMuB_07", 7, 66, -1, -1, 7),
106 ui_button_info("OMuB_08", 7, 84, -1, -1, 8),
107 ui_button_info("OMuB_09", 1, 124, -1, -1, 9),
108 ui_button_info("OMuB_10", 1, 157, -1, -1, 10),
109 ui_button_info("OMuB_11", 20, 207, -1, -1, 11),
110 ui_button_info("OMuB_12", 64, 207, -1, -1, 12),
111 ui_button_info("OMuB_13", 9, 251, -1, -1, 13),
112 ui_button_info("OMuB_14", 9, 282, -1, -1, 14),
113 ui_button_info("OMuB_15", 610, 53, -1, -1, 15),
114 ui_button_info("OMuB_16", 610, 72, -1, -1, 16),
115 },
116 { // GR_1024
117 ui_button_info("2_OMuB_07", 12, 105, -1, -1, 7),
118 ui_button_info("2_OMuB_08", 12, 134, -1, -1, 8),
119 ui_button_info("2_OMuB_09", 2, 198, -1, -1, 9),
120 ui_button_info("2_OMuB_10", 2, 252, -1, -1, 10),
121 ui_button_info("2_OMuB_11", 32, 332, -1, -1, 11),
122 ui_button_info("2_OMuB_12", 103, 332, -1, -1, 12),
123 ui_button_info("2_OMuB_13", 14, 402, -1, -1, 13),
124 ui_button_info("2_OMuB_14", 14, 452, -1, -1, 14),
125 ui_button_info("2_OMuB_15", 976, 85, -1, -1, 15),
126 ui_button_info("2_OMuB_16", 976, 114, -1, -1, 16),
127 }
128 };
129
130 UI_GADGET Om_pro_bogus;
131
132 // test
133 #define OM_PRO_NUM_TEXT 12
134 UI_XSTR Om_pro_text[GR_NUM_RESOLUTIONS][OM_PRO_NUM_TEXT] = {
135 { // GR_640
136 { "TCP", 1378, 38, 70, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_TCP].button },
137 { "IPX", 1379, 38, 88, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_IPX].button },
138 { "IP Address", 1380, 30, 128, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
139 { "add", 1381, 22, 235, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_ADD_IP].button },
140 { "rem.", 1382, 68, 235, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_DELETE_IP].button },
141 { "Broadcast Locally", 1387, 42, 260, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_LOCAL_BROADCAST].button },
142 { "PXO", 1383, 42, 291, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_VMT].button },
143 { "Login", 1384, 14, 309, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
144 { "Password", 1385, 14, 336, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
145 { "Squadron", 1386, 14, 363, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
146 { "Voice", 1528, 557, 60, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_VOX_TAB].button },
147 { "General", 1388, 542, 77, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_GEN_TAB].button },
148 },
149 { // GR_1024
150 { "TCP", 1378, 61, 113, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_TCP].button },
151 { "IPX", 1379, 61, 141, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_IPX].button },
152 { "IP Address", 1380, 47, 206, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
153 { "add", 1381, 36, 375, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_ADD_IP].button },
154 { "rem.", 1382, 109, 375, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_DELETE_IP].button },
155 { "Broadcast Locally", 1387, 68, 417, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_LOCAL_BROADCAST].button },
156 { "PXO", 1383, 68, 467, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_VMT].button },
157 { "Login", 1384, 23, 495, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
158 { "Password", 1385, 23, 538, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
159 { "Squadron", 1386, 23, 582, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
160 { "Voice", 1528, 921, 96, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_VOX_TAB].button },
161 { "General", 1388, 902, 123, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_GEN_TAB].button },
162 }
163 };
164
165 // defines for the tracker input boxes
166 int Om_tracker_login_coords[GR_NUM_RESOLUTIONS][4] = {
167 {
168 19, 322, 226, -1 // GR_640
169 },
170 {
171 31, 518, 361, -1 // GR_1024
172 }
173 };
174 int Om_tracker_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
175 {
176 19, 350, 226, -1 // GR_640
177 },
178 {
179 31, 562, 361, -1 // GR_1024
180 }
181 };
182 int Om_tracker_squad_name_coords[GR_NUM_RESOLUTIONS][4] = {
183 {
184 19, 378, 226, -1 // GR_640
185 },
186 {
187 31, 607, 361, -1 // GR_1024
188 }
189 };
190
191 // protocol section tracker login input box
192 UI_INPUTBOX Om_tracker_login;
193
194 // protocol section tracker passwd input box
195 UI_INPUTBOX Om_tracker_passwd;
196
197 // pxo squad name/password
198 UI_INPUTBOX Om_tracker_squad_name;
199
200 #define TRACKER_FOCUS_NONE 0
201 #define TRACKER_FOCUS_LOGIN 1
202 #define TRACKER_FOCUS_PASSWORD 2
203 #define TRACKER_FOCUS_SQUADRON 3
204 static int Om_tracker_focus = 0;
205
206 // ip address list vars
207 #define IP_STRING_LEN 60
208 #define MAX_IP_ADDRS 100
209
210 #define IP_CONFIG_FNAME NOX("Tcp.cfg")
211
212 #define IP_EMPTY_STRING ""
213
214 int Ip_list_coords[GR_NUM_RESOLUTIONS][4] = {
215 {
216 29, 137, 227, 67 // GR_640
217 },
218 {
219 46, 220, 364, 106 // GR_1024
220 }
221 };
222
223
224 int Ip_list_max_display[GR_NUM_RESOLUTIONS] = {
225 5,
226 5
227 };
228
229 static int Ip_input_coords[GR_NUM_RESOLUTIONS][4] = {
230 {
231 109, 128, 140, -1 // GR_640
232 },
233 {
234 132, 206, 261, -1 // GR_640
235 }
236 };
237
238 int Om_input_mode = 0;
239 int Om_ip_start; // index of the first element to be displayed in the list box
240 int Om_ip_selected; // the selected default IP address
241 int Om_ip_disp_count; // how many items are currently being displayed
242 int Om_num_ips; // # of ip addresses we have
243 char Om_ip_addrs[MAX_IP_ADDRS][IP_STRING_LEN]; // the ip addresses themselves
244 UI_BUTTON Om_ip_button; // button for detecting clicks on the ip address list
245 UI_INPUTBOX Om_ip_input; // input box for adding new ip addresses
246
247 // setting vars
248 int Om_local_broadcast; // whether the player has local broadcast selected or not
249 int Om_tracker_flag; // if the guy has the tracker selected
250 int Om_protocol; // protocol in use
251
252 // load all the controls for the protocol section
253 void options_multi_load_protocol_controls();
254
255 // disable/hide all the controls for the protocol section
256 void options_multi_disable_protocol_controls();
257
258 // enable/unhide all the controls for the protocol section
259 void options_multi_enable_protocol_controls();
260
261 // intialize the protocol section vars
262 void options_multi_init_protocol_vars();
263
264 // do frame for the protocol section
265 void options_multi_protocol_do(int key);
266
267 // if the accept button was hit
268 void options_multi_protocol_accept();
269
270 // check for button presses
271 void options_multi_protocol_check_buttons();
272
273 // if a button was pressed
274 void options_multi_protocol_button_pressed(int n);
275
276 // load the ip address file
277 void options_multi_protocol_load_ip_file();
278
279 // save the ip address file
280 void options_multi_protocol_save_ip_file();
281
282 // draw the list of ip addresses
283 void options_multi_protocol_display_ips();
284
285 // scroll the list of ip addresses down
286 void options_multi_protocol_scroll_ip_down();
287
288 // scroll the list of ip addresses up
289 void options_multi_protocol_scroll_ip_up();
290
291 // check the ip list to see if the user has selected a new item
292 void options_multi_protocol_check_ip_list();
293
294 // delete the currently selected ip if any
295 void options_multi_protocol_delete_ip();
296
297 // attempt to add the currently entered ip address
298 void options_multi_protocol_add_current_ip();
299
300
301 // general options tab section -------------------------------------------
302 #define OM_GEN_NUM_BUTTONS 10
303
304 #define OM_GEN_OBJ_LOW 0
305 #define OM_GEN_OBJ_MED 1
306 #define OM_GEN_OBJ_HIGH 2
307 #define OM_GEN_OBJ_LAN 3
308 #define OM_GEN_PIX_YES 4
309 #define OM_GEN_PIX_NO 5
310 #define OM_GEN_XFER_MULTIDATA_YES 6
311 #define OM_GEN_XFER_MULTIDATA_NO 7
312 #define OM_GEN_FLUSH_NO 8
313 #define OM_GEN_FLUSH_YES 9
314
315 ui_button_info Om_gen_buttons[GR_NUM_RESOLUTIONS][OM_GEN_NUM_BUTTONS] = {
316 { // GR_640
317 ui_button_info("OGB_17", 598, 117, -1, -1, 17),
318 ui_button_info("OGB_18", 598, 139, -1, -1, 18),
319 ui_button_info("OGB_19", 598, 161, -1, -1, 19),
320 ui_button_info("OGB_20", 598, 183, -1, -1, 20),
321 ui_button_info("OGB_21", 549, 229, -1, -1, 21),
322 ui_button_info("OGB_22", 598, 229, -1, -1, 22),
323 ui_button_info("OGB_23", 598, 286, -1, -1, 23),
324 ui_button_info("OGB_24", 598, 307, -1, -1, 24),
325 ui_button_info("OGB_25", 598, 347, -1, -1, 25),
326 ui_button_info("OGB_26", 598, 368, -1, -1, 26),
327 },
328 { // GR_1024
329 ui_button_info("2_OGB_17", 957, 188, -1, -1, 17),
330 ui_button_info("2_OGB_18", 957, 223, -1, -1, 18),
331 ui_button_info("2_OGB_19", 957, 258, -1, -1, 19),
332 ui_button_info("2_OGB_20", 957, 293, -1, -1, 20),
333 ui_button_info("2_OGB_21", 879, 366, -1, -1, 21),
334 ui_button_info("2_OGB_22", 957, 366, -1, -1, 22),
335 ui_button_info("2_OGB_23", 957, 457, -1, -1, 23),
336 ui_button_info("2_OGB_24", 957, 491, -1, -1, 24),
337 ui_button_info("2_OGB_25", 957, 555, -1, -1, 25),
338 ui_button_info("2_OGB_26", 957, 589, -1, -1, 26),
339 }
340 };
341
342 UI_GADGET Om_gen_bogus;
343
344 // text
345 #define OM_GEN_NUM_TEXT 14
346 UI_XSTR Om_gen_text[GR_NUM_RESOLUTIONS][OM_GEN_NUM_TEXT] = {
347 { // GR_640
348 { "Object Update", 1391, 511, 104, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus },
349 { "Low", 1160, 558, 127, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_OBJ_LOW].button },
350 { "Medium", 1161, 538, 149, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_OBJ_MED].button },
351 { "High", 1162, 556, 171, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_OBJ_HIGH].button },
352 { "Lan", 1392, 561, 193, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_OBJ_LAN].button },
353 { "Pilot / Squad Images", 1393, 463, 214, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus },
354 { "Yes", 1394, 555, 257, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_PIX_YES].button },
355 { "No", 1395, 604, 257, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_PIX_NO].button },
356 { "Transfer Missions", 1396, 478, 271, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus },
357 { "/multidata", 1397, 519, 292, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_XFER_MULTIDATA_YES].button },
358 { "/missions", 1398, 527, 314, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_XFER_MULTIDATA_NO].button },
359 { "Flush Cache", 1399, 529, 334, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus },
360 { "Never", 1400, 548, 355, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_FLUSH_NO].button },
361 { "Before Game", 1401, 502, 377, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_FLUSH_YES].button },
362 },
363 { // GR_1024
364 { "Object Update", 1391, 818, 166, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus },
365 { "Low", 1160, 913, 204, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_OBJ_LOW].button },
366 { "Medium", 1161, 892, 239, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_OBJ_MED].button },
367 { "High", 1162, 909, 274, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_OBJ_HIGH].button },
368 { "Lan", 1392, 916, 310, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_OBJ_LAN].button },
369 { "Pilot / Squad Images", 1393, 821, 345, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus },
370 { "Yes", 1394, 887, 411, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_PIX_YES].button },
371 { "No", 1395, 966, 411, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_PIX_NO].button },
372 { "Transfer Missions", 1396, 844, 435, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus },
373 { "/multidata", 1397, 858, 468, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_XFER_MULTIDATA_YES].button },
374 { "/missions", 1398, 870, 503, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_XFER_MULTIDATA_NO].button },
375 { "Flush Cache", 1399, 886, 533, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus },
376 { "Never", 1400, 897, 568, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_FLUSH_NO].button },
377 { "Before Game", 1401, 849, 603, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_FLUSH_YES].button },
378 }
379 };
380
381 // setting vars
382 int Om_gen_obj_update; // object update level
383 int Om_gen_pix; // accept pilot pix or not
384 int Om_gen_xfer_multidata; // xfer missions to multidata or not
385 int Om_gen_flush_cache; // flush multidata directory before every game
386
387 // load all the general tab controls
388 void options_multi_load_gen_controls();
389
390 // disable/hide all the general tab controls
391 void options_multi_disable_gen_controls();
392
393 // enable/unhide all the general tab controls
394 void options_multi_enable_gen_controls();
395
396 // initialize the general tab vars
397 void options_multi_init_gen_vars();
398
399 // accept function for the general tab
400 void options_multi_gen_accept();
401
402 // do frame for the general tab
403 void options_multi_gen_do();
404
405 // check for button presses
406 void options_multi_gen_check_buttons();
407
408 // a button was pressed
409 void options_multi_gen_button_pressed(int n);
410
411
412 // voice options tab section -------------------------------------------
413 #define OM_VOX_NUM_BUTTONS 6
414
415 #define OM_VOX_VOICE_TEST 0
416 #define OM_VOX_VOICE_YES 1
417 #define OM_VOX_VOICE_NO 2
418 #define OM_VOX_PLIST_UP 3
419 #define OM_VOX_PLIST_DOWN 4
420 #define OM_VOX_VOICE_MUTE 5
421
422 UI_GADGET Om_vox_bogus;
423
424 ui_button_info Om_vox_buttons[GR_NUM_RESOLUTIONS][OM_VOX_NUM_BUTTONS] = {
425 { // GR_640
426 ui_button_info("OVB_17", 562, 118, -1, -1, 17),
427 ui_button_info("OVB_19", 551, 208, -1, -1, 19),
428 ui_button_info("OVB_20", 599, 208, -1, -1, 20),
429 ui_button_info("OVB_21", 614, 256, -1, -1, 21),
430 ui_button_info("OVB_22", 614, 290, -1, -1, 22),
431 ui_button_info("OVB_23", 599, 354, -1, -1, 23),
432 },
433 { // GR_640
434 ui_button_info("2_OVB_17", 900, 189, -1, -1, 17),
435 ui_button_info("2_OVB_19", 882, 333, -1, -1, 19),
436 ui_button_info("2_OVB_20", 959, 333, -1, -1, 20),
437 ui_button_info("2_OVB_21", 983, 410, -1, -1, 21),
438 ui_button_info("2_OVB_22", 983, 464, -1, -1, 22),
439 ui_button_info("2_OVB_23", 959, 566, -1, -1, 23),
440 }
441 };
442
443 // text
444 #define OM_VOX_NUM_TEXT 6
445 UI_XSTR Om_vox_text[GR_NUM_RESOLUTIONS][OM_VOX_NUM_TEXT] = {
446 { // GR_640
447 { "Mic test", 1389, 567, 104, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[0][OM_VOX_VOICE_TEST].button },
448 { "Voice Quality", 1531, 439, 149, UI_XSTR_COLOR_GREEN, -1, &Om_vox_bogus },
449 { "Voice Transmission", 1530, 439, 193, UI_XSTR_COLOR_GREEN, -1, &Om_vox_bogus },
450 { "On", 1285, 556, 233, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[0][OM_VOX_VOICE_YES].button },
451 { "Off", 1286, 604, 233, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[0][OM_VOX_VOICE_NO].button },
452 { "Mute", 1390, 594, 381, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[0][OM_VOX_VOICE_MUTE].button },
453 },
454 { // GR_1024
455 { "mic test", 1389, 908, 166, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[1][OM_VOX_VOICE_TEST].button },
456 { "Voice Quality", 1531, 703, 239, UI_XSTR_COLOR_GREEN, -1, &Om_vox_bogus },
457 { "Voice Transmission", 1530, 783, 310, UI_XSTR_COLOR_GREEN, -1, &Om_vox_bogus },
458 { "On", 1285, 890, 373, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[1][OM_VOX_VOICE_YES].button },
459 { "Off", 1286, 967, 373, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[1][OM_VOX_VOICE_NO].button },
460 { "Mute", 1390, 950, 609, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[1][OM_VOX_VOICE_MUTE].button },
461 }
462 };
463
464 #define NUM_OM_VOX_SLIDERS 1
465 #define OM_VOX_QOS_SLIDER 0
466
467 op_sliders Om_vox_sliders[GR_NUM_RESOLUTIONS][NUM_OM_VOX_SLIDERS] = {
468 { // GR_640
469 op_sliders("OVB_18", 429, 162, -1, -1, 18, 20, 10, NULL, -1, -1, -1, NULL, -1, -1, -1), // voice QOS
470 },
471 { // GR_1024
472 op_sliders("2_OVB_18", 686, 259, -1, -1, 18, 31, 10, NULL, -1, -1, -1, NULL, -1, -1, -1), // voice QOS
473 }
474 };
475
476 // player list area
477 int Om_vox_plist_coords[GR_NUM_RESOLUTIONS][4] = {
478 { // GR_640
479 377, 270, 232, 79
480 },
481 { // GR_1024
482 604, 432, 371, 127
483 }
484 };
485 int Om_vox_plist_max_display[GR_NUM_RESOLUTIONS] = {
486 6,
487 6
488 };
489
490 int Om_vox_plist_start;
491 UI_BUTTON Om_vox_plist_button;
492
493 // voice test buffer
494 #define OM_VOX_BUF_SIZE (1<<12)
495 #define OM_VOX_COMP_SIZE ((1<<15) + (1<<14))
496
497 #define OM_VOX_WAVE_Y 240
498 #define OM_VOX_WAVE_WIDTH 300
499
500 #define OM_VOX_DROP_ICON_X 100
501 #define OM_VOX_DROP_ICON_Y 100
502
503 #define OM_VOX_RECORD_INT 175
504
505 unsigned char Om_vox_voice_buffer[OM_VOX_BUF_SIZE];
506 int Om_vox_voice_buffer_size = -1;
507
508 unsigned char Om_vox_comp_buffer[OM_VOX_COMP_SIZE];
509 int Om_vox_voice_comp_size = -1;
510
511 sound_handle Om_vox_playback_handle;
512
513 // status of any test voice recording
514 #define OM_VOX_TEST_NONE -1
515 #define OM_VOX_TEST_RECORDING 0
516 #define OM_VOX_TEST_PLAYBACK 1
517 int Om_vox_test_status = OM_VOX_TEST_NONE;
518
519 // setting vars
520 int Om_vox_accept_voice;
521
522 // simple list of players we are looking through
523 net_player *Om_vox_players[MAX_PLAYERS];
524
525 // selected player
526 net_player *Om_vox_player_select;
527
528 // mute or don't mute for each player
529 int Om_vox_player_flags[MAX_PLAYERS];
530
531 // the # of players
532 int Om_vox_num_players;
533
534 // load all the voice tab controls
535 void options_multi_load_vox_controls();
536
537 // disable/hide all the voice tab controls
538 void options_multi_disable_vox_controls();
539
540 // enable/unhide all the voice tab controls
541 void options_multi_enable_vox_controls();
542
543 // initialize the voice tab vars
544 void options_multi_init_vox_vars();
545
546 // accept function for the voice tab
547 void options_multi_vox_accept();
548
549 // do frame for the voice tab
550 void options_multi_vox_do();
551
552 // check for button presses
553 void options_multi_vox_check_buttons();
554
555 // a button was pressed
556 void options_multi_vox_button_pressed(int n);
557
558 // process/display the player list
559 void options_multi_vox_process_player_list();
560
561 // scroll the player list down
562 void options_multi_vox_plist_scroll_down();
563
564 // scroll the player list up
565 void options_multi_vox_plist_scroll_up();
566
567 // get the index into the player list of the passed netplayer
568 int options_multi_vox_plist_get(net_player *pl);
569
570
571 // general data section ------------------------------------------------
572
573 // load all background bitmaps
options_multi_load_bmaps()574 void options_multi_load_bmaps()
575 {
576 // load both background bitmaps
577 Om_background_0 = bm_load(Om_background_0_fname[gr_screen.res]);
578 if(Om_background_0 == -1){
579 nprintf(("Network","Error loading options background %s\n",Om_background_0_fname[gr_screen.res]));
580 }
581
582 Om_background_1 = bm_load(Om_background_1_fname[gr_screen.res]);
583 if(Om_background_1 == -1){
584 nprintf(("Network","Error loading options background %s\n",Om_background_1_fname[gr_screen.res]));
585 }
586
587 // load in both mask bitmaps
588 Om_mask_0 = bm_load(Om_background_0_mask_fname[gr_screen.res]);
589 if(Om_mask_0 == -1){
590 nprintf(("Network","Error loading options background mask %s\n",Om_background_0_mask_fname[gr_screen.res]));
591 }
592
593 Om_mask_1 = bm_load(Om_background_1_mask_fname[gr_screen.res]);
594 if(Om_mask_1 == -1){
595 nprintf(("Network","Error loading options background mask %s\n",Om_background_1_mask_fname[gr_screen.res]));
596 }
597 }
598
599 // unload all the background bitmaps
options_multi_unload_bmaps()600 void options_multi_unload_bmaps()
601 {
602 // unload all background bitmaps
603 if(Om_background_0 != -1){
604 bm_release(Om_background_0);
605 Om_background_0 = -1;
606 }
607 if(Om_background_1 != -1){
608 bm_release(Om_background_1);
609 Om_background_1 = -1;
610 }
611
612 // unload all mask bitmaps
613 if(Om_mask_0 != -1){
614 bm_release(Om_mask_0);
615 Om_mask_0 = -1;
616 }
617 if(Om_mask_1 != -1){
618 bm_release(Om_mask_1);
619 Om_mask_1 = -1;
620 }
621 }
622
623 // add a notification message
options_multi_add_notify(const char * str)624 void options_multi_add_notify(const char *str)
625 {
626 // copy the string
627 memset(Om_notify_string,0,255);
628 if(str != NULL){
629 strcpy_s(Om_notify_string,str);
630 }
631
632 // set the timestamp
633 Om_notify_stamp = timestamp(OM_NOTIFY_TIME);
634 }
635
636 // process and blit any notification messages
options_multi_notify_process()637 void options_multi_notify_process()
638 {
639 int w;
640 const char *p_str[3];
641 int n_chars[3];
642 char line[OM_NOTIFICATION_LINE_LEN];
643 int line_count;
644 int y_start;
645 int idx;
646 int line_height;
647
648 // if there is no timestamp, do nothing
649 if(Om_notify_stamp == -1){
650 return;
651 }
652
653 // otherwise, if it has elapsed, unset it
654 if(timestamp_elapsed(Om_notify_stamp)){
655 Om_notify_stamp = -1;
656 return;
657 }
658
659 // otherwise display the string
660 line_height = gr_get_font_height() + 1;
661 line_count = split_str(Om_notify_string, 600, n_chars, p_str, 3, OM_NOTIFICATION_LINE_LEN);
662 y_start = OM_NOTIFY_Y;
663 gr_set_color_fast(&Color_bright);
664 for(idx=0;idx<line_count;idx++){
665 memset(line, 0, OM_NOTIFICATION_LINE_LEN);
666 strncpy(line, p_str[idx], n_chars[idx]);
667
668 gr_get_string_size(&w,NULL,line);
669 gr_string((600 - w)/2,y_start,line,GR_RESIZE_MENU);
670
671 y_start += line_height;
672 }
673 }
674
675
676 // protocol section --------------------------------------------------------
677
678 // load all the controls for the protocol section
options_multi_load_protocol_controls()679 void options_multi_load_protocol_controls()
680 {
681 int idx;
682
683 Assert(Om_window != NULL);
684
685 // instantiate all the buttons
686 for(idx=0; idx<OM_PRO_NUM_BUTTONS; idx++){
687 // create the object
688 Om_pro_buttons[gr_screen.res][idx].button.create(Om_window, "", Om_pro_buttons[gr_screen.res][idx].x, Om_pro_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
689
690 // set the sound to play when highlighted
691 Om_pro_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
692
693 // set the ani for the button
694 Om_pro_buttons[gr_screen.res][idx].button.set_bmaps(Om_pro_buttons[gr_screen.res][idx].filename);
695
696 // set the hotspot
697 Om_pro_buttons[gr_screen.res][idx].button.link_hotspot(Om_pro_buttons[gr_screen.res][idx].hotspot);
698 }
699
700 // text
701 for(idx=0; idx<OM_PRO_NUM_TEXT; idx++){
702 Om_window->add_XSTR(&Om_pro_text[gr_screen.res][idx]);
703 }
704
705 // create the tracker input boxes
706 Om_tracker_login.create(Om_window, Om_tracker_login_coords[gr_screen.res][0], Om_tracker_login_coords[gr_screen.res][1], Om_tracker_login_coords[gr_screen.res][2], LOGIN_LEN - 1, Multi_tracker_login, UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_NO_BACK);
707 Om_tracker_passwd.create(Om_window, Om_tracker_passwd_coords[gr_screen.res][0], Om_tracker_passwd_coords[gr_screen.res][1], Om_tracker_passwd_coords[gr_screen.res][2], LOGIN_LEN - 1, Multi_tracker_passwd, UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_PASSWD | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_NO_BACK);
708 Om_tracker_squad_name.create(Om_window, Om_tracker_squad_name_coords[gr_screen.res][0], Om_tracker_squad_name_coords[gr_screen.res][1], Om_tracker_squad_name_coords[gr_screen.res][2], LOGIN_LEN - 1, Multi_tracker_squad_name, UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_NO_BACK);
709
710 // create the invisible button for checking for clicks on the ip address list
711 Om_ip_button.create(Om_window, "", Ip_list_coords[gr_screen.res][0], Ip_list_coords[gr_screen.res][1], Ip_list_coords[gr_screen.res][2], Ip_list_coords[gr_screen.res][3], 0, 1);
712 Om_ip_button.hide();
713
714 // create the new ip address input box
715 Om_ip_input.create(Om_window, Ip_input_coords[gr_screen.res][0], Ip_input_coords[gr_screen.res][1], Ip_input_coords[gr_screen.res][2], IP_STRING_LEN, IP_EMPTY_STRING, UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU);
716 Om_ip_input.hide();
717 Om_ip_input.disable();
718
719 // bogus control
720 Om_pro_bogus.base_create(Om_window, UI_KIND_ICON, 0, 0, 0, 0);
721 }
722
723 // disable/hide all the controls for the protocol section
options_multi_disable_protocol_controls()724 void options_multi_disable_protocol_controls()
725 {
726 int idx;
727
728 // hide and disable all the protocol buttons
729 for(idx=0;idx<OM_PRO_NUM_BUTTONS;idx++){
730 // hide the button
731 Om_pro_buttons[gr_screen.res][idx].button.hide();
732
733 // disable the button
734 Om_pro_buttons[gr_screen.res][idx].button.disable();
735 }
736
737 // hide and disable the tracker input boxes
738 Om_tracker_login.hide();
739 Om_tracker_login.disable();
740 Om_tracker_passwd.hide();
741 Om_tracker_passwd.disable();
742 Om_tracker_squad_name.hide();
743 Om_tracker_squad_name.disable();
744
745 // disable the click detection button
746 Om_ip_button.disable();
747
748 // disable and hide the ip address inputbox
749 Om_ip_input.hide();
750 Om_ip_input.disable();
751
752 // undo input mode if necessary
753 Om_input_mode = 0;
754
755 // bogus control
756 Om_pro_bogus.hide();
757 Om_pro_bogus.disable();
758 }
759
760 // enable/unhide all the controls for the protocol section
options_multi_enable_protocol_controls()761 void options_multi_enable_protocol_controls()
762 {
763 int idx;
764
765 // unhide and enable all the protocol buttons
766 for(idx=0;idx<OM_PRO_NUM_BUTTONS;idx++){
767 // enable the button
768 Om_pro_buttons[gr_screen.res][idx].button.enable();
769
770 // unhide the button
771 Om_pro_buttons[gr_screen.res][idx].button.unhide();
772 }
773
774 // unhide and enable the tracker input boxes
775 if(Om_tracker_flag){
776 Om_tracker_login.enable();
777 Om_tracker_passwd.enable();
778 Om_tracker_squad_name.enable();
779 }
780 Om_tracker_login.unhide();
781 Om_tracker_passwd.unhide();
782 Om_tracker_squad_name.unhide();
783
784 // enable the click detection button
785 Om_ip_button.enable();
786
787 // bogus control
788 Om_pro_bogus.enable();
789 Om_pro_bogus.unhide();
790 }
791
792 // intialize the protocol section vars
options_multi_init_protocol_vars()793 void options_multi_init_protocol_vars()
794 {
795 // current protocol
796 Om_protocol = Multi_options_g.protocol;
797
798 // whether or not the user has the local broadcast button selected
799 Om_local_broadcast = (Player->m_local_options.flags & MLO_FLAG_LOCAL_BROADCAST) ? 1 : 0;
800
801 // whether or not we're playing on the tracker
802 Om_tracker_flag = Multi_options_g.pxo ? 1 : 0;
803
804 // load the ip address list
805 Om_ip_disp_count = 0;
806 options_multi_protocol_load_ip_file();
807 Om_ip_selected = Om_num_ips - 1;
808 Om_ip_start = Om_num_ips - 1;
809 }
810
811 // do frame for the protocol section
options_multi_protocol_do(int key)812 void options_multi_protocol_do(int key)
813 {
814 // check for button presses
815 options_multi_protocol_check_buttons();
816
817 // force draw the correct "local broadcast" button
818 if(Om_local_broadcast){
819 Om_pro_buttons[gr_screen.res][OM_PRO_LOCAL_BROADCAST].button.draw_forced(2);
820 }
821
822 // draw the "vmt" button if it is selected
823 if(Om_tracker_flag){
824 Om_pro_buttons[gr_screen.res][OM_PRO_VMT].button.draw_forced(2);
825 }
826
827 // see if he hit any interesting key presses
828 switch(key){
829 case KEY_ENTER:
830 // add a new ip string if we're in "input" mode
831 if(Om_input_mode){
832 options_multi_protocol_add_current_ip();
833
834 // clear the text control and input mode
835 Om_ip_input.set_text("");
836 Om_ip_input.clear_focus();
837 Om_ip_input.disable();
838 Om_input_mode = 0;
839 }
840
841 // if the tracker login inputbox has focus, lose it
842 if(Om_tracker_login.has_focus()){
843 Om_tracker_login.clear_focus();
844 gamesnd_play_iface(InterfaceSounds::COMMIT_PRESSED);
845 }
846 // if the tracker password inputbox has focus, lose it
847 if(Om_tracker_passwd.has_focus()){
848 Om_tracker_passwd.clear_focus();
849 gamesnd_play_iface(InterfaceSounds::COMMIT_PRESSED);
850 }
851 // if the tracker squad name inputbox has focus, lose it
852 if(Om_tracker_squad_name.has_focus()){
853 Om_tracker_squad_name.clear_focus();
854 gamesnd_play_iface(InterfaceSounds::COMMIT_PRESSED);
855 }
856 break;
857
858 case KEY_ESC:
859 // if we're in input mode, cancel out
860 if(Om_input_mode){
861 // clear the text control and input mode
862 Om_ip_input.set_text("");
863 Om_ip_input.clear_focus();
864 Om_ip_input.disable();
865 Om_input_mode = 0;
866 }
867 // otherwise quit the options screen altogether
868 else {
869 options_cancel_exit();
870 }
871 break;
872
873 case KEY_TAB:
874 // tab through the tracker input controls
875 if(Om_tracker_login.has_focus()){
876 Om_tracker_passwd.set_focus();
877 } else if(Om_tracker_passwd.has_focus()){
878 Om_tracker_squad_name.set_focus();
879 } else if(Om_tracker_squad_name.has_focus()){
880 Om_tracker_login.set_focus();
881 }
882 break;
883 }
884
885 // force draw the proper protocol
886 Om_pro_buttons[gr_screen.res][OM_PRO_TCP].button.draw_forced(2);
887
888 // force draw the proper tab button
889 switch (Om_mode) {
890 case OM_MODE_GENERAL:
891 Om_pro_buttons[gr_screen.res][OM_PRO_GEN_TAB].button.draw_forced(2);
892 break;
893
894 case OM_MODE_VOX:
895 Om_pro_buttons[gr_screen.res][OM_PRO_VOX_TAB].button.draw_forced(2);
896 break;
897 }
898
899 // check to see if the user has clicked on the ip list and selected a new item
900 options_multi_protocol_check_ip_list();
901
902 // draw the list of ip addresses
903 options_multi_protocol_display_ips();
904
905 // hack to play sound when input boxes gain focus
906 if (Om_tracker_login.has_focus()) {
907 if (Om_tracker_focus != TRACKER_FOCUS_LOGIN) {
908 Om_tracker_focus = TRACKER_FOCUS_LOGIN;
909 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
910 }
911 } else if (Om_tracker_passwd.has_focus()) {
912 if (Om_tracker_focus != TRACKER_FOCUS_PASSWORD) {
913 Om_tracker_focus = TRACKER_FOCUS_PASSWORD;
914 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
915 }
916 } else if (Om_tracker_squad_name.has_focus()) {
917 if (Om_tracker_focus != TRACKER_FOCUS_SQUADRON) {
918 Om_tracker_focus = TRACKER_FOCUS_SQUADRON;
919 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
920 }
921 } else {
922 Om_tracker_focus = TRACKER_FOCUS_NONE;
923 }
924 }
925
926 // if the accept button was hit
options_multi_protocol_accept()927 void options_multi_protocol_accept()
928 {
929 // if the user has selected local broadcast write it into his options struct
930 Player->m_local_options.flags &= ~(MLO_FLAG_LOCAL_BROADCAST);
931 if(Om_local_broadcast){
932 Player->m_local_options.flags |= MLO_FLAG_LOCAL_BROADCAST;
933 }
934
935 // active protocol
936 Multi_options_g.protocol = Om_protocol;
937
938 // VMT status
939 Multi_options_g.pxo = Om_tracker_flag;
940
941 // copy the VMT login and password data
942 Om_tracker_login.get_text(Multi_tracker_login);
943 Om_tracker_passwd.get_text(Multi_tracker_passwd);
944 Om_tracker_squad_name.get_text(Multi_tracker_squad_name);
945
946 // write out the tracker login and passwd values to the registry
947 os_config_write_string( "PXO", "Login", Multi_tracker_login );
948 os_config_write_string( "PXO", "Password", Multi_tracker_passwd );
949
950 // write out the PXO squad name and passwd values to the registry
951 os_config_write_string( "PXO", "SquadName", Multi_tracker_squad_name );
952
953 // save the ip address list
954 options_multi_protocol_save_ip_file();
955 }
956
957 // check for button presses
options_multi_protocol_check_buttons()958 void options_multi_protocol_check_buttons()
959 {
960 int idx;
961
962 // go through each button
963 for(idx=0;idx<OM_PRO_NUM_BUTTONS;idx++){
964 if(Om_pro_buttons[gr_screen.res][idx].button.pressed()){
965 options_multi_protocol_button_pressed(idx);
966 break;
967 }
968 }
969 }
970
971 // if a button was pressed
options_multi_protocol_button_pressed(int n)972 void options_multi_protocol_button_pressed(int n)
973 {
974 switch(n){
975 // add an ip address
976 case OM_PRO_ADD_IP:
977 // don't process if we're in input mode
978 if(Om_input_mode){
979 break;
980 }
981
982 // setup the input mode
983 Om_input_mode = 1;
984 Om_ip_input.enable();
985 Om_ip_input.unhide();
986 Om_ip_input.set_text(IP_EMPTY_STRING);
987 Om_ip_input.set_focus();
988 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
989 break;
990
991 // delete the currently selected ip
992 case OM_PRO_DELETE_IP:
993 // don't process if we're in input mode
994 if(Om_input_mode){
995 break;
996 }
997
998 options_multi_protocol_delete_ip();
999 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1000 break;
1001
1002 // the "local" broadcast button - toggle
1003 case OM_PRO_LOCAL_BROADCAST:
1004 // don't process if we're in input mode
1005 if(Om_input_mode){
1006 break;
1007 }
1008
1009 if(!Om_local_broadcast){
1010 Om_local_broadcast = 1;
1011 } else {
1012 Om_local_broadcast = 0;
1013 }
1014
1015 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1016 break;
1017
1018 // scroll ips down
1019 case OM_PRO_SCROLL_IP_DOWN:
1020 // don't process if we're in input mode
1021 if(Om_input_mode){
1022 break;
1023 }
1024
1025 options_multi_protocol_scroll_ip_down();
1026 break;
1027
1028 // scroll ips up
1029 case OM_PRO_SCROLL_IP_UP:
1030 // don't process if we're in input mode
1031 if(Om_input_mode){
1032 break;
1033 }
1034
1035 options_multi_protocol_scroll_ip_up();
1036 break;
1037
1038 // the vmt button
1039 case OM_PRO_VMT:
1040 // for the multiplayer beta, always force tracker mode
1041 // don't process if we're in input mode
1042 if(Om_input_mode){
1043 break;
1044 }
1045
1046 // toggle the stupid thing
1047 Om_tracker_flag = !Om_tracker_flag;
1048
1049 // if the thing is toggled on - enable the inputbox controls, else diable them
1050 if(Om_tracker_flag){
1051 Om_tracker_login.enable();
1052 Om_tracker_passwd.enable();
1053 Om_tracker_squad_name.enable();
1054 } else {
1055 Om_tracker_login.disable();
1056 Om_tracker_passwd.disable();
1057 Om_tracker_squad_name.disable();
1058 }
1059
1060 // play a sound
1061 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1062 break;
1063
1064 // general tab button
1065 case OM_PRO_GEN_TAB:
1066 if(Om_mode != OM_MODE_GENERAL){
1067 // set the general tab
1068 Om_mode = OM_MODE_GENERAL;
1069
1070 // disable the voice controls
1071 options_multi_disable_vox_controls();
1072
1073 // enable the general controls
1074 options_multi_enable_gen_controls();
1075
1076 // set the general screen mask
1077 Assert(Om_mask_0 >= 0);
1078 Om_window->set_mask_bmap(Om_mask_0, Om_background_0_mask_fname[gr_screen.res]);
1079 }
1080
1081 // play a sound
1082 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1083
1084 break;
1085
1086 // voice tab button
1087 case OM_PRO_VOX_TAB:
1088 if(Om_mode != OM_MODE_VOX){
1089 // set the voice tab
1090 Om_mode = OM_MODE_VOX;
1091
1092 // disable the general controls
1093 options_multi_disable_gen_controls();
1094
1095 // enable the voice controls
1096 options_multi_enable_vox_controls();
1097
1098 // set the voice screen mask
1099 Assert(Om_mask_1 >= 0);
1100 Om_window->set_mask_bmap(Om_mask_1, Om_background_1_mask_fname[gr_screen.res]);
1101 }
1102 // play a sound
1103 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1104
1105 break;
1106
1107 // tcp mode
1108 case OM_PRO_TCP:
1109 Om_protocol = NET_TCP;
1110 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1111 break;
1112
1113 // ipx mode, no longer supported
1114 case OM_PRO_IPX:
1115 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1116 popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "The old IPX protocol is no longer supported.");
1117 break;
1118 }
1119 }
1120
1121 // load the ip address file
options_multi_protocol_load_ip_file()1122 void options_multi_protocol_load_ip_file()
1123 {
1124 char line[IP_STRING_LEN];
1125 CFILE *file = NULL;
1126
1127 // reset the ip address count
1128 Om_num_ips = 0;
1129
1130 // attempt to open the ip list file
1131 file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);
1132 if(file == NULL){
1133 nprintf(("Network","Error loading tcp.cfg file!\n"));
1134 return;
1135 }
1136
1137 // read in all the strings in the file
1138 while(!cfeof(file)){
1139 line[0] = '\0';
1140 cfgets(line,IP_STRING_LEN,file);
1141
1142 // strip off any newline character
1143 if(line[strlen(line) - 1] == '\n'){
1144 line[strlen(line) - 1] = '\0';
1145 }
1146
1147 // 0 length lines don't get processed
1148 if((line[0] == '\0') || (line[0] == '\n') )
1149 continue;
1150
1151 if ( !psnet_is_valid_ip_string(line) ) {
1152 nprintf(("Network","Invalid ip string (%s)\n",line));
1153 } else {
1154 if(Om_num_ips < MAX_IP_ADDRS-1){
1155 strcpy_s(Om_ip_addrs[Om_num_ips++],line);
1156 }
1157 }
1158 }
1159
1160 cfclose(file);
1161 }
1162
1163 // save the ip address file
options_multi_protocol_save_ip_file()1164 void options_multi_protocol_save_ip_file()
1165 {
1166 int idx;
1167 CFILE *file = NULL;
1168
1169 // attempt to open the ip list file for writing
1170 file = cfopen(IP_CONFIG_FNAME,"wt",CFILE_NORMAL,CF_TYPE_DATA );
1171 if(file == NULL){
1172 nprintf(("Network","Error loading tcp.cfg file\n"));
1173 return;
1174 }
1175
1176 // write out all the string we have
1177 for(idx=0;idx<Om_num_ips;idx++){
1178 // make _absolutely_ sure its a valid address
1179 // MWA -- commented out next line because name resolution might fail when
1180 // it was added. We'll only grab games that we can actually get to.
1181 //Assert(psnet_is_valid_ip_string(Multi_ip_addrs[idx]));
1182
1183 cfputs(Om_ip_addrs[idx],file);
1184
1185 // make sure to tack on a newline if necessary
1186 if(Om_ip_addrs[idx][strlen(&Om_ip_addrs[idx][0]) - 1] != '\n'){
1187 cfputs(NOX("\n"),file);
1188 }
1189 }
1190
1191 cfclose(file);
1192 }
1193
1194 // draw the list of ip addresses
options_multi_protocol_display_ips()1195 void options_multi_protocol_display_ips()
1196 {
1197 int idx;
1198 int y_start = Ip_list_coords[gr_screen.res][1];
1199 int line_height = gr_get_font_height() + 1;
1200
1201 // get the # of items we should be displaying based upon the # of addresses and the starting display point
1202 if(Om_ip_start >= Ip_list_max_display[gr_screen.res]){
1203 Om_ip_disp_count = Ip_list_max_display[gr_screen.res];
1204 } else {
1205 Om_ip_disp_count = Om_ip_start + 1;
1206 }
1207
1208 // display the addresses
1209 for(idx=Om_ip_start; idx >= Om_ip_start - Om_ip_disp_count + 1 ; idx--){
1210 if(idx == Om_ip_selected){
1211 gr_set_color_fast(&Color_bright);
1212 } else {
1213 gr_set_color_fast(&Color_white);
1214 }
1215
1216 gr_printf_menu(Ip_list_coords[gr_screen.res][0], y_start, "%s", Om_ip_addrs[idx]);
1217 y_start += line_height;
1218 }
1219 }
1220
1221 // scroll the list of ip addresses down
options_multi_protocol_scroll_ip_down()1222 void options_multi_protocol_scroll_ip_down()
1223 {
1224 if(Om_ip_start >= Ip_list_max_display[gr_screen.res]){
1225 gamesnd_play_iface(InterfaceSounds::SCROLL);
1226 Om_ip_start--;
1227 } else {
1228 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1229 }
1230 }
1231
1232 // scroll the list of ip addresses up
options_multi_protocol_scroll_ip_up()1233 void options_multi_protocol_scroll_ip_up()
1234 {
1235 if(Om_ip_start < Om_num_ips-1){
1236 gamesnd_play_iface(InterfaceSounds::SCROLL);
1237 Om_ip_start++;
1238 } else {
1239 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1240 }
1241 }
1242
1243 // check the ip list to see if the user has selected a new item
options_multi_protocol_check_ip_list()1244 void options_multi_protocol_check_ip_list()
1245 {
1246 int click_y;
1247 int item;
1248
1249 if(Om_ip_button.pressed()){
1250 // determine which item he clicked on
1251 Om_ip_button.get_mouse_pos(NULL, &click_y);
1252 item = click_y / (gr_get_font_height() + 1);
1253
1254 // determine if there is an item in this location, and select it if so
1255 if(item < Om_ip_disp_count){
1256 Om_ip_selected = Om_ip_start - item;
1257 }
1258 }
1259 }
1260
1261 // delete the currently selected ip if any
options_multi_protocol_delete_ip()1262 void options_multi_protocol_delete_ip()
1263 {
1264 int idx;
1265
1266 // attempt to delete the currently highlighted item
1267 if(Om_ip_selected != -1){
1268
1269 // move down all the other items
1270 for(idx=Om_ip_selected; idx < Om_num_ips; idx++){
1271 strcpy_s(Om_ip_addrs[idx],Om_ip_addrs[idx+1]);
1272 }
1273
1274 // make sure to decrement the starting index
1275 Om_ip_start--;
1276
1277 // check to make sure that the selected item is valid
1278 Om_num_ips--;
1279 if(Om_num_ips <= 0){
1280 Om_ip_selected = -1;
1281 } else {
1282 if(Om_ip_selected > 0){
1283 Om_ip_selected--;
1284 }
1285 }
1286 }
1287 }
1288
1289 // return 10, if successflu
1290 char Ip_str[IP_STRING_LEN+1];
1291 int Ip_validated_already = 0;
options_multi_verify_ip()1292 int options_multi_verify_ip()
1293 {
1294 bool result;
1295
1296 if(!Ip_validated_already){
1297 // see if its a valid ip address
1298 result = psnet_is_valid_ip_string(Ip_str);
1299
1300 // if the result is a valid ip string, return immediately
1301 if(result){
1302 return 10;
1303 }
1304
1305 // otherwise, change the popup text to indicate that it is invalid and wait for the user to click ok
1306 popup_change_text(XSTR( "Ip string is invalid!", 386));
1307 }
1308
1309 Ip_validated_already = 1;
1310
1311 // always wait for the user to hit the "cancel" button
1312 return 0;
1313 }
1314
1315 // attempt to add the currently entered ip address
options_multi_protocol_add_current_ip()1316 void options_multi_protocol_add_current_ip()
1317 {
1318 // get the entered string
1319 Om_ip_input.get_text(Ip_str);
1320
1321 // this popup wil do several things.
1322 // 1.) It will display a popup so the user isn't left scratching his head
1323 // 2.) If the address
1324 Ip_validated_already = 0;
1325 if(popup_till_condition(options_multi_verify_ip, XSTR( "Cancel", 387), XSTR( "Verifying ip address", 388)) == 10){
1326 if(Om_num_ips < MAX_IP_ADDRS){
1327 strcpy_s(Om_ip_addrs[Om_num_ips],Ip_str);
1328 Om_ip_start = Om_num_ips;
1329 Om_num_ips++;
1330
1331 // if this is the first item on the list, select it
1332 if(Om_num_ips == 1){
1333 Om_ip_selected = 0;
1334 }
1335 } else {
1336 options_multi_add_notify(XSTR( "Max # of IP addresses reached!", 389));
1337 }
1338 }
1339 }
1340
1341 // general options tab section -------------------------------------------
1342
1343 // load all the general tab controls
options_multi_load_gen_controls()1344 void options_multi_load_gen_controls()
1345 {
1346 int idx;
1347
1348 Assert(Om_window != NULL);
1349
1350 // instantiate all the buttons
1351 for(idx=0; idx<OM_GEN_NUM_BUTTONS; idx++){
1352 // create the object
1353 Om_gen_buttons[gr_screen.res][idx].button.create(Om_window, "", Om_gen_buttons[gr_screen.res][idx].x, Om_gen_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
1354
1355 // set the sound to play when highlighted
1356 Om_gen_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1357
1358 // set the ani for the button
1359 Om_gen_buttons[gr_screen.res][idx].button.set_bmaps(Om_gen_buttons[gr_screen.res][idx].filename);
1360
1361 // set the hotspot
1362 Om_gen_buttons[gr_screen.res][idx].button.link_hotspot(Om_gen_buttons[gr_screen.res][idx].hotspot);
1363 }
1364
1365 // text
1366 for(idx=0; idx<OM_GEN_NUM_TEXT; idx++){
1367 Om_window->add_XSTR(&Om_gen_text[gr_screen.res][idx]);
1368 }
1369
1370 // bogus control
1371 Om_gen_bogus.base_create(Om_window, UI_KIND_ICON, 0, 0, 0, 0);
1372 }
1373
1374 // disable/hide all the general tab controls
options_multi_disable_gen_controls()1375 void options_multi_disable_gen_controls()
1376 {
1377 int idx;
1378
1379 // go through all the controls
1380 for(idx=0;idx<OM_GEN_NUM_BUTTONS;idx++){
1381 Om_gen_buttons[gr_screen.res][idx].button.hide();
1382 Om_gen_buttons[gr_screen.res][idx].button.disable();
1383 }
1384
1385 // bogus control
1386 Om_gen_bogus.hide();
1387 Om_gen_bogus.disable();
1388 }
1389
1390 // enable/unhide all the general tab controls
options_multi_enable_gen_controls()1391 void options_multi_enable_gen_controls()
1392 {
1393 int idx;
1394
1395 // go through all the controls
1396 for(idx=0;idx<OM_GEN_NUM_BUTTONS;idx++){
1397 Om_gen_buttons[gr_screen.res][idx].button.enable();
1398 Om_gen_buttons[gr_screen.res][idx].button.unhide();
1399 }
1400
1401 // bogus control
1402 Om_gen_bogus.enable();
1403 Om_gen_bogus.unhide();
1404 }
1405
1406 // initialize the general tab vars
options_multi_init_gen_vars()1407 void options_multi_init_gen_vars()
1408 {
1409 // initialize the object update
1410 Om_gen_obj_update = Player->m_local_options.obj_update_level;
1411
1412 // initialize the accept pix var
1413 if(Player->m_local_options.flags & MLO_FLAG_ACCEPT_PIX){
1414 Om_gen_pix = 1;
1415 } else {
1416 Om_gen_pix = 0;
1417 }
1418
1419 // initialize the xfer_multidata var
1420 if(Player->m_local_options.flags & MLO_FLAG_XFER_MULTIDATA){
1421 Om_gen_xfer_multidata = 1;
1422 } else {
1423 Om_gen_xfer_multidata = 0;
1424 }
1425
1426 // initialize the flush cache var
1427 if(Player->m_local_options.flags & MLO_FLAG_FLUSH_CACHE){
1428 Om_gen_flush_cache = 1;
1429 } else {
1430 Om_gen_flush_cache = 0;
1431 }
1432 }
1433
1434 // accept function for the general tab
options_multi_gen_accept()1435 void options_multi_gen_accept()
1436 {
1437 // apply the object update level
1438 Player->m_local_options.obj_update_level = Om_gen_obj_update;
1439
1440 // apply the accept pix var
1441 Player->m_local_options.flags &= ~(MLO_FLAG_ACCEPT_PIX);
1442 if(Om_gen_pix){
1443 Player->m_local_options.flags |= MLO_FLAG_ACCEPT_PIX;
1444 }
1445
1446 // apply the xfer multidata var
1447 Player->m_local_options.flags &= ~(MLO_FLAG_XFER_MULTIDATA);
1448 if(Om_gen_xfer_multidata){
1449 Player->m_local_options.flags |= MLO_FLAG_XFER_MULTIDATA;
1450 }
1451
1452 // apply the flush cache var
1453 Player->m_local_options.flags &= ~(MLO_FLAG_FLUSH_CACHE);
1454 if(Om_gen_flush_cache){
1455 Player->m_local_options.flags |= MLO_FLAG_FLUSH_CACHE;
1456 }
1457 }
1458
1459 // do frame for the general tab
options_multi_gen_do()1460 void options_multi_gen_do()
1461 {
1462 // check for button presses
1463 options_multi_gen_check_buttons();
1464
1465 // draw the proper object update button
1466 switch(Om_gen_obj_update){
1467 case OBJ_UPDATE_LOW:
1468 Om_gen_buttons[gr_screen.res][OM_GEN_OBJ_LOW].button.draw_forced(2);
1469 break;
1470 case OBJ_UPDATE_MEDIUM:
1471 Om_gen_buttons[gr_screen.res][OM_GEN_OBJ_MED].button.draw_forced(2);
1472 break;
1473 case OBJ_UPDATE_HIGH:
1474 Om_gen_buttons[gr_screen.res][OM_GEN_OBJ_HIGH].button.draw_forced(2);
1475 break;
1476 case OBJ_UPDATE_LAN:
1477 Om_gen_buttons[gr_screen.res][OM_GEN_OBJ_LAN].button.draw_forced(2);
1478 break;
1479 default :
1480 Int3();
1481 }
1482
1483 // draw the proper pix button
1484 if(Om_gen_pix){
1485 Om_gen_buttons[gr_screen.res][OM_GEN_PIX_YES].button.draw_forced(2);
1486 } else {
1487 Om_gen_buttons[gr_screen.res][OM_GEN_PIX_NO].button.draw_forced(2);
1488 }
1489
1490 // draw the proper xfer multidata button
1491 if(Om_gen_xfer_multidata){
1492 Om_gen_buttons[gr_screen.res][OM_GEN_XFER_MULTIDATA_YES].button.draw_forced(2);
1493 } else {
1494 Om_gen_buttons[gr_screen.res][OM_GEN_XFER_MULTIDATA_NO].button.draw_forced(2);
1495 }
1496
1497 // draw the proper flush cache button
1498 if(Om_gen_flush_cache){
1499 Om_gen_buttons[gr_screen.res][OM_GEN_FLUSH_YES].button.draw_forced(2);
1500 } else {
1501 Om_gen_buttons[gr_screen.res][OM_GEN_FLUSH_NO].button.draw_forced(2);
1502 }
1503 }
1504
1505 // check for button presses
options_multi_gen_check_buttons()1506 void options_multi_gen_check_buttons()
1507 {
1508 int idx;
1509
1510 // go through all the buttons
1511 for(idx=0;idx<OM_GEN_NUM_BUTTONS;idx++){
1512 if(Om_gen_buttons[gr_screen.res][idx].button.pressed()){
1513 options_multi_gen_button_pressed(idx);
1514 break;
1515 }
1516 }
1517 }
1518
1519 // a button was pressed
options_multi_gen_button_pressed(int n)1520 void options_multi_gen_button_pressed(int n)
1521 {
1522 switch(n){
1523 // low object update level
1524 case OM_GEN_OBJ_LOW:
1525 if(Om_gen_obj_update != OBJ_UPDATE_LOW){
1526 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1527 Om_gen_obj_update = OBJ_UPDATE_LOW;
1528 } else {
1529 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1530 }
1531 break;
1532
1533 // medium object update level
1534 case OM_GEN_OBJ_MED:
1535 if(Om_gen_obj_update != OBJ_UPDATE_MEDIUM){
1536 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1537 Om_gen_obj_update = OBJ_UPDATE_MEDIUM;
1538 } else {
1539 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1540 }
1541 break;
1542
1543 // high object update level
1544 case OM_GEN_OBJ_HIGH:
1545 if(Om_gen_obj_update != OBJ_UPDATE_HIGH){
1546 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1547 Om_gen_obj_update = OBJ_UPDATE_HIGH;
1548 } else {
1549 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1550 }
1551 break;
1552
1553 // lan object update level
1554 case OM_GEN_OBJ_LAN:
1555 if(Om_gen_obj_update != OBJ_UPDATE_LAN){
1556 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1557 Om_gen_obj_update = OBJ_UPDATE_LAN;
1558 } else {
1559 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1560 }
1561 break;
1562
1563 // accept pix
1564 case OM_GEN_PIX_YES:
1565 if(!Om_gen_pix){
1566 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1567 Om_gen_pix = 1;
1568 } else {
1569 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1570 }
1571 break;
1572
1573 // don't accept pix
1574 case OM_GEN_PIX_NO:
1575 if(Om_gen_pix){
1576 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1577 Om_gen_pix = 0;
1578 } else {
1579 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1580 }
1581 break;
1582
1583 // put missions in the multidate directory
1584 case OM_GEN_XFER_MULTIDATA_YES:
1585 if(!Om_gen_xfer_multidata){
1586 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1587 Om_gen_xfer_multidata = 1;
1588 } else {
1589 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1590 }
1591 break;
1592
1593 // don't put missions in the multidata directory
1594 case OM_GEN_XFER_MULTIDATA_NO:
1595 if(Om_gen_xfer_multidata){
1596 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1597 Om_gen_xfer_multidata = 0;
1598 } else {
1599 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1600 }
1601 break;
1602
1603 // flush the cache before each mission
1604 case OM_GEN_FLUSH_YES:
1605 if(!Om_gen_flush_cache){
1606 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1607 Om_gen_flush_cache = 1;
1608 } else {
1609 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1610 }
1611 break;
1612
1613 // don't flush the cache before each mission
1614 case OM_GEN_FLUSH_NO:
1615 if(Om_gen_flush_cache){
1616 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1617 Om_gen_flush_cache = 0;
1618 } else {
1619 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1620 }
1621 break;
1622 }
1623 }
1624
1625 // voice options tab section -------------------------------------------
1626
1627 // load all the voice tab controls
options_multi_load_vox_controls()1628 void options_multi_load_vox_controls()
1629 {
1630 int idx;
1631
1632 Assert(Om_window != NULL);
1633
1634 // instantiate all the buttons
1635 for(idx=0; idx<OM_VOX_NUM_BUTTONS; idx++){
1636 // create the object
1637 Om_vox_buttons[gr_screen.res][idx].button.create(Om_window, "", Om_vox_buttons[gr_screen.res][idx].x, Om_vox_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
1638
1639 // set the sound to play when highlighted
1640 Om_vox_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1641
1642 // set the ani for the button
1643 Om_vox_buttons[gr_screen.res][idx].button.set_bmaps(Om_vox_buttons[gr_screen.res][idx].filename);
1644
1645 // set the hotspot
1646 Om_vox_buttons[gr_screen.res][idx].button.link_hotspot(Om_vox_buttons[gr_screen.res][idx].hotspot);
1647 }
1648
1649 // text
1650 for(idx=0; idx<OM_VOX_NUM_TEXT; idx++){
1651 Om_window->add_XSTR(&Om_vox_text[gr_screen.res][idx]);
1652 }
1653
1654 // sliders
1655 for ( idx = 0; idx < NUM_OM_VOX_SLIDERS; idx++ ) {
1656 Om_vox_sliders[gr_screen.res][idx].slider.create(Om_window, Om_vox_sliders[gr_screen.res][idx].x, Om_vox_sliders[gr_screen.res][idx].y,
1657 Om_vox_sliders[gr_screen.res][idx].dots, Om_vox_sliders[gr_screen.res][idx].filename,
1658 Om_vox_sliders[gr_screen.res][idx].hotspot,
1659 Om_vox_sliders[gr_screen.res][idx].left_filename, Om_vox_sliders[gr_screen.res][idx].left_mask, Om_vox_sliders[gr_screen.res][idx].left_x, Om_vox_sliders[gr_screen.res][idx].left_y,
1660 Om_vox_sliders[gr_screen.res][idx].right_filename, Om_vox_sliders[gr_screen.res][idx].right_mask, Om_vox_sliders[gr_screen.res][idx].right_x, Om_vox_sliders[gr_screen.res][idx].right_y,
1661 Om_vox_sliders[gr_screen.res][idx].dot_w);
1662 }
1663
1664 // create the player list select button
1665 Om_vox_plist_button.create(Om_window, "", Om_vox_plist_coords[gr_screen.res][0], Om_vox_plist_coords[gr_screen.res][1], Om_vox_plist_coords[gr_screen.res][2], Om_vox_plist_coords[gr_screen.res][3], 0, 1);
1666 Om_vox_plist_button.hide();
1667
1668 // build a list of net players
1669 Om_vox_num_players = 0;
1670 for(idx=0;idx<MAX_PLAYERS;idx++){
1671 Om_vox_players[idx] = NULL;
1672
1673 // if i'm not connected, do nothing
1674 if((Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_CONNECTED)){
1675 continue;
1676 }
1677
1678 // add all players I know about
1679 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
1680 // set the netplayer pointer
1681 Om_vox_players[Om_vox_num_players] = &Net_players[idx];
1682
1683 // set his mute flag
1684 Om_vox_player_flags[Om_vox_num_players] = (Multi_voice_local_prefs & (1<<idx)) ? 1 : 0;
1685
1686 // increment the count
1687 Om_vox_num_players++;
1688 }
1689 }
1690
1691 // bogus control
1692 Om_vox_bogus.base_create(Om_window, UI_KIND_ICON, 0, 0, 0, 0);
1693 }
1694
1695 // disable/hide all the voice tab controls
options_multi_disable_vox_controls()1696 void options_multi_disable_vox_controls()
1697 {
1698 int idx;
1699
1700 // go through all the controls
1701 for(idx=0; idx<OM_VOX_NUM_BUTTONS; idx++){
1702 Om_vox_buttons[gr_screen.res][idx].button.hide();
1703 Om_vox_buttons[gr_screen.res][idx].button.disable();
1704 }
1705
1706 // hide the qos control
1707 Om_vox_sliders[gr_screen.res][OM_VOX_QOS_SLIDER].slider.hide();
1708 Om_vox_sliders[gr_screen.res][OM_VOX_QOS_SLIDER].slider.disable();
1709
1710 // unset the sound buffer size so we don't display any waveforms
1711 Om_vox_voice_buffer_size = -1;
1712 Om_vox_voice_comp_size = -1;
1713 Om_vox_playback_handle = sound_handle::invalid();
1714 Om_vox_test_status = OM_VOX_TEST_NONE;
1715
1716 // disable the player list select button
1717 Om_vox_plist_button.disable();
1718
1719 // bogus controls
1720 Om_vox_bogus.hide();
1721 Om_vox_bogus.disable();
1722 }
1723
1724 // enable/unhide all the voice tab controls
options_multi_enable_vox_controls()1725 void options_multi_enable_vox_controls()
1726 {
1727 int idx;
1728
1729 // go through all the controls
1730 for(idx=0; idx<OM_VOX_NUM_BUTTONS; idx++){
1731 Om_vox_buttons[gr_screen.res][idx].button.enable();
1732 Om_vox_buttons[gr_screen.res][idx].button.unhide();
1733 }
1734
1735 // unhide the qos control
1736 Om_vox_sliders[gr_screen.res][OM_VOX_QOS_SLIDER].slider.enable();
1737 Om_vox_sliders[gr_screen.res][OM_VOX_QOS_SLIDER].slider.unhide();
1738
1739 // unset the sound buffer size so we don't display any waveforms
1740 Om_vox_voice_buffer_size = -1;
1741 Om_vox_voice_comp_size = -1;
1742 Om_vox_playback_handle = sound_handle::invalid();
1743 Om_vox_test_status = OM_VOX_TEST_NONE;
1744
1745 // select the first player on the list
1746 Om_vox_player_select = Om_vox_players[0];
1747 Om_vox_plist_start = 0;
1748
1749 // enable the player list select button
1750 Om_vox_plist_button.enable();
1751
1752 // bogus controls
1753 Om_vox_bogus.enable();
1754 Om_vox_bogus.unhide();
1755 }
1756
1757 // initialize the voice tab vars
options_multi_init_vox_vars()1758 void options_multi_init_vox_vars()
1759 {
1760 // intialize the accept voice var
1761 if(Player->m_local_options.flags & MLO_FLAG_NO_VOICE){
1762 Om_vox_accept_voice = 0;
1763 } else {
1764 Om_vox_accept_voice = 1;
1765 }
1766 }
1767
1768 // accept function for the voice tab
options_multi_vox_accept()1769 void options_multi_vox_accept()
1770 {
1771 int idx;
1772 int voice_pref_flags;
1773
1774 // set the accept voice flag
1775 Player->m_local_options.flags &= ~(MLO_FLAG_NO_VOICE);
1776 if(!Om_vox_accept_voice){
1777 Player->m_local_options.flags |= MLO_FLAG_NO_VOICE;
1778 }
1779
1780 // build the voice preferences stuff
1781 voice_pref_flags = 0xffffffff;
1782 for(idx=0;idx<Om_vox_num_players;idx++){
1783 // if this guy is muted
1784 if(!Om_vox_player_flags[idx]){
1785 voice_pref_flags &= ~(1 << NET_PLAYER_INDEX(Om_vox_players[idx]));
1786 }
1787 }
1788 multi_voice_set_prefs(voice_pref_flags);
1789 }
1790
1791 // do frame for the voice tab
options_multi_vox_do()1792 void options_multi_vox_do()
1793 {
1794 int handle;
1795
1796 // check for button presses
1797 options_multi_vox_check_buttons();
1798
1799 // draw the proper accept voice button
1800 if(Om_vox_accept_voice){
1801 Om_vox_buttons[gr_screen.res][OM_VOX_VOICE_YES].button.draw_forced(2);
1802 } else {
1803 Om_vox_buttons[gr_screen.res][OM_VOX_VOICE_NO].button.draw_forced(2);
1804 }
1805
1806 // if the currently selected player is muted
1807 if((Om_vox_player_select != NULL) && !Om_vox_player_flags[options_multi_vox_plist_get(Om_vox_player_select)]){
1808 Om_vox_buttons[gr_screen.res][OM_VOX_VOICE_MUTE].button.draw_forced(2);
1809 }
1810
1811 // process and display the player list
1812 options_multi_vox_process_player_list();
1813
1814 // if we're currently doing a voice test recording, process some stuff
1815 switch(Om_vox_test_status){
1816 case OM_VOX_TEST_RECORDING:
1817 multi_voice_test_process();
1818
1819 // force draw the mic test button
1820 Om_vox_buttons[gr_screen.res][OM_VOX_VOICE_TEST].button.draw_forced(2);
1821
1822 // if we are no longer recording, switch to playback if possible
1823 if(!multi_voice_test_recording()){
1824 Om_vox_test_status = OM_VOX_TEST_PLAYBACK;
1825
1826 if(Om_vox_voice_comp_size != -1){
1827 // stop any playing back sounds
1828 rtvoice_stop_playback_all();
1829
1830 // attempt to get a playback handle
1831 handle = multi_voice_test_get_playback_buffer();
1832 if(handle != -1){
1833 Om_vox_playback_handle = rtvoice_play(handle, Om_vox_comp_buffer, Om_vox_voice_comp_size);
1834
1835 // mark us as playing back
1836 Om_vox_test_status = OM_VOX_TEST_PLAYBACK;
1837 }
1838 // on error, notify the user something is wrong
1839 else {
1840 options_multi_add_notify(XSTR( "Error trying to playback recorded voice! Check your hardware", 390));
1841
1842 // mark us as doing nothing
1843 Om_vox_test_status = OM_VOX_TEST_NONE;
1844 }
1845 } else {
1846 // mark us as doing nothing
1847 Om_vox_test_status = OM_VOX_TEST_NONE;
1848 }
1849 }
1850 break;
1851
1852 case OM_VOX_TEST_PLAYBACK:
1853 // if we were playing a sound back, but now the sound is done
1854 if ((Om_vox_playback_handle.isValid()) && !ds_is_channel_playing(ds_get_channel(Om_vox_playback_handle))) {
1855 // flush all playing sounds safely
1856 rtvoice_stop_playback_all();
1857
1858 // null the sound handle
1859 Om_vox_playback_handle = sound_handle::invalid();
1860
1861 // set this so we know not to display any more waveforms
1862 Om_vox_voice_buffer_size = -1;
1863 Om_vox_voice_comp_size = -1;
1864
1865 // free the status up
1866 Om_vox_test_status = OM_VOX_TEST_NONE;
1867 }
1868 break;
1869 }
1870 }
1871
1872 // check for button presses
options_multi_vox_check_buttons()1873 void options_multi_vox_check_buttons()
1874 {
1875 int idx;
1876
1877 // go through all the buttons
1878 for(idx=0; idx<OM_VOX_NUM_BUTTONS; idx++){
1879 if(Om_vox_buttons[gr_screen.res][idx].button.pressed()){
1880 options_multi_vox_button_pressed(idx);
1881 break;
1882 }
1883 }
1884 }
1885
1886 // a button was pressed
options_multi_vox_button_pressed(int n)1887 void options_multi_vox_button_pressed(int n)
1888 {
1889 switch(n){
1890 // accept voice button
1891 case OM_VOX_VOICE_YES:
1892 if(!Om_vox_accept_voice){
1893 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1894 Om_vox_accept_voice = 1;
1895 } else {
1896 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1897 }
1898 break;
1899
1900 // don't accept voice button
1901 case OM_VOX_VOICE_NO:
1902 if(Om_vox_accept_voice){
1903 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1904 Om_vox_accept_voice = 0;
1905 } else {
1906 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1907 }
1908 break;
1909
1910 // mute/unmute button
1911 case OM_VOX_VOICE_MUTE:
1912 if(Om_vox_player_select != NULL){
1913 Om_vox_player_flags[options_multi_vox_plist_get(Om_vox_player_select)] = !Om_vox_player_flags[options_multi_vox_plist_get(Om_vox_player_select)];
1914 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
1915 } else {
1916 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
1917 }
1918 break;
1919
1920 // scroll the player list up
1921 case OM_VOX_PLIST_UP:
1922 options_multi_vox_plist_scroll_up();
1923 break;
1924
1925 // scroll the player list down
1926 case OM_VOX_PLIST_DOWN:
1927 options_multi_vox_plist_scroll_down();
1928 break;
1929
1930 // mic test button
1931 case OM_VOX_VOICE_TEST:
1932 // if in a multiplayer game, don't allow testing
1933 if((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_CONNECTED)){
1934 options_multi_add_notify(XSTR( "Cannot test mic while in a multiplayer game!", 391));
1935 break;
1936 }
1937
1938 // if this machine is not capable of playing
1939 if(!Multi_voice_can_record){
1940 options_multi_add_notify(XSTR( "DirectSoundCapture could not be initialized. To initialize DirectSoundCapture your sound card must be full duplex and your sound card drivers must support DirectSoundCapture", 392));
1941 } else {
1942 // if we're not already doing a record test
1943 if(Om_vox_test_status == OM_VOX_TEST_NONE){
1944 // set the quality of sound
1945 rtvoice_set_qos(Om_vox_sliders[gr_screen.res][OM_VOX_QOS_SLIDER].slider.pos + 1);
1946
1947 // clear the comp buffer
1948 memset(Om_vox_comp_buffer,128,OM_VOX_COMP_SIZE);
1949
1950 Om_vox_test_status = OM_VOX_TEST_RECORDING;
1951 multi_voice_test_record_start();
1952 }
1953 }
1954 break;
1955 }
1956 }
1957
1958 // screen shader
1959 extern shader Grey_shader;
1960
1961 // process and blit any voice waveform if necessary
options_multi_vox_process_waveform()1962 void options_multi_vox_process_waveform()
1963 {
1964 int c_width = OM_VOX_WAVE_WIDTH;
1965 int avg_len;
1966 int buf_offset;
1967 int idx,a_idx,running_avg;
1968
1969 // if we're not in recording or playback mode
1970 if(Om_vox_test_status == OM_VOX_TEST_NONE){
1971 return;
1972 }
1973
1974 // grey the screen
1975 gr_set_shader(&Grey_shader);
1976 gr_shade(0,0,gr_screen.clip_width, gr_screen.clip_height, GR_RESIZE_NONE);
1977
1978 switch(Om_vox_test_status){
1979 case OM_VOX_TEST_RECORDING:
1980 // if we have no sound buffer size, do nothing
1981 if(Om_vox_voice_buffer_size <= 0){
1982 return;
1983 }
1984
1985 // if we are not recording, do nothing
1986 if(Om_vox_test_status != OM_VOX_TEST_RECORDING){
1987 return;
1988 }
1989
1990 // get the # of samples we'll average for one line
1991 avg_len = Om_vox_voice_buffer_size / c_width;
1992
1993 // blit the waveform
1994 gr_set_color_fast(&Color_green);
1995 buf_offset = 0;
1996 for(idx=0; idx < c_width; idx++){
1997 // reset the running average
1998 running_avg = 0;
1999 for(a_idx = 0; a_idx < avg_len; a_idx++){
2000 running_avg += (int)Om_vox_voice_buffer[buf_offset] - 128;
2001
2002 // increment the buffer offset
2003 buf_offset++;
2004 }
2005
2006 running_avg /= avg_len;
2007 gr_line((gr_screen.max_w_unscaled - OM_VOX_WAVE_WIDTH)/2 + idx, OM_VOX_WAVE_Y, (gr_screen.max_w_unscaled - OM_VOX_WAVE_WIDTH)/2 + idx, OM_VOX_WAVE_Y + running_avg, GR_RESIZE_MENU);
2008 }
2009
2010 // if this packet would have been dropped, notify the user
2011 if(multi_voice_test_packet_tossed()){
2012 gr_set_color_fast(&Color_bright);
2013 gr_string(OM_VOX_DROP_ICON_X,OM_VOX_DROP_ICON_Y, XSTR( "Packet Overflow", 393), GR_RESIZE_MENU);
2014 }
2015 break;
2016
2017 case OM_VOX_TEST_PLAYBACK:
2018 // get the offset into the playing direct sound buffer
2019 buf_offset = ds_get_play_position(ds_get_channel(Om_vox_playback_handle));
2020
2021 // get the # of samples we'll average for one line
2022 avg_len = (int)((float)OM_VOX_RECORD_INT * ((1024.0f * 11.0f) / 1000.0f)) / c_width;
2023
2024 // blit the waveform
2025 gr_set_color_fast(&Color_red);
2026 for(idx=0; idx < c_width; idx++){
2027 // reset the running average
2028 running_avg = 0;
2029 for(a_idx = 0; a_idx < avg_len; a_idx++){
2030 if(buf_offset < (OM_VOX_COMP_SIZE - 2)){
2031 running_avg += (int)Om_vox_comp_buffer[buf_offset] - 128;
2032
2033 // increment the buffer offset
2034 buf_offset++;
2035 }
2036 }
2037
2038 running_avg /= avg_len;
2039 gr_line((gr_screen.max_w_unscaled - OM_VOX_WAVE_WIDTH)/2 + idx, OM_VOX_WAVE_Y, (gr_screen.max_w_unscaled - OM_VOX_WAVE_WIDTH)/2 + idx, OM_VOX_WAVE_Y + running_avg, GR_RESIZE_MENU);
2040 }
2041 break;
2042 }
2043 }
2044
2045 // process/display the player list
options_multi_vox_process_player_list()2046 void options_multi_vox_process_player_list()
2047 {
2048 int idx;
2049 int y_start,p_count;
2050 int selected_index,click_y;
2051 char str[CALLSIGN_LEN+2];
2052
2053 int line_height = gr_get_font_height() + 1;
2054
2055 // check for mouse clicks
2056 if(Om_vox_plist_button.pressed()){
2057 Om_vox_plist_button.get_mouse_pos(NULL,&click_y);
2058 selected_index = (click_y / line_height) + Om_vox_plist_start;
2059
2060 // if he clicked on a valid player, select him
2061 if(Om_vox_players[selected_index] != NULL){
2062 Om_vox_player_select = Om_vox_players[selected_index];
2063
2064 nprintf(("Network","Selecting player %s\n",Om_vox_player_select->m_player->callsign));
2065 }
2066 }
2067
2068 // draw the list of players
2069 p_count = 0;
2070 y_start = Om_vox_plist_coords[gr_screen.res][1];
2071 for(idx = Om_vox_plist_start; idx < Om_vox_num_players; idx++){
2072 if(Om_vox_players[idx] != NULL){
2073 // if he's the selected player, highlight him
2074 if(Om_vox_players[idx] == Om_vox_player_select){
2075 gr_set_color_fast(&Color_bright);
2076 } else {
2077 gr_set_color_fast(&Color_normal);
2078 }
2079
2080 // force fit his callsign
2081 strcpy_s(str,Om_vox_players[idx]->m_player->callsign);
2082 font::force_fit_string(str, CALLSIGN_LEN+1, Om_vox_plist_coords[gr_screen.res][2]);
2083
2084 // blit the callsign
2085 gr_string(Om_vox_plist_coords[gr_screen.res][0], y_start, str, GR_RESIZE_MENU);
2086
2087 // increment the y index
2088 y_start += line_height;
2089
2090 // increment the player count
2091 p_count++;
2092 }
2093
2094 // if we've reached max display, break out
2095 if(p_count >= Om_vox_plist_max_display[gr_screen.res]){
2096 break;
2097 }
2098 }
2099 }
2100
2101 // get the index into the player list of the passed netplayer
options_multi_vox_plist_get(net_player * pl)2102 int options_multi_vox_plist_get(net_player *pl)
2103 {
2104 int idx;
2105
2106 for(idx=0;idx<Om_vox_num_players;idx++){
2107 if(pl == Om_vox_players[idx]){
2108 return idx;
2109 }
2110 }
2111
2112 // should neve get here. hmmm.
2113 Int3();
2114 return -1;
2115 }
2116
2117 // scroll the player list down
options_multi_vox_plist_scroll_down()2118 void options_multi_vox_plist_scroll_down()
2119 {
2120 if(Om_vox_num_players < Om_vox_plist_max_display[gr_screen.res]){
2121 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
2122 return;
2123 }
2124
2125 if((Om_vox_num_players - Om_vox_plist_start) >= Om_vox_plist_max_display[gr_screen.res]){
2126 Om_vox_plist_start++;
2127 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
2128 } else {
2129 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
2130 }
2131 }
2132
2133 // scroll the player list up
options_multi_vox_plist_scroll_up()2134 void options_multi_vox_plist_scroll_up()
2135 {
2136 if(Om_vox_plist_start > 0){
2137 Om_vox_plist_start--;
2138 gamesnd_play_iface(InterfaceSounds::USER_SELECT);
2139 } else {
2140 gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
2141 }
2142 }
2143
2144
2145 // extern functions -----------------------------------------------------
2146
2147 // called when the options screen is initialized, pass in the UI window
options_multi_init(UI_WINDOW * options_window)2148 void options_multi_init(UI_WINDOW *options_window)
2149 {
2150 // assign the options window
2151 Om_window = options_window;
2152
2153 // load the background bitmaps
2154 options_multi_load_bmaps();
2155
2156 // load the controls for the protocol area
2157 options_multi_load_protocol_controls();
2158
2159 // load the controls for the general tab
2160 options_multi_load_gen_controls();
2161
2162 // load the controls for the voice tab
2163 options_multi_load_vox_controls();
2164
2165 // disable all the protocol controls
2166 options_multi_disable_protocol_controls();
2167
2168 // disable all the general tab controls
2169 options_multi_disable_gen_controls();
2170
2171 // disable all the voice tab controls
2172 options_multi_disable_vox_controls();
2173
2174 // intialize the protocol section vars
2175 options_multi_init_protocol_vars();
2176
2177 // initialize the general tab vars
2178 options_multi_init_gen_vars();
2179
2180 // initialize the voice tab vars
2181 options_multi_init_vox_vars();
2182
2183 // intialize the multiplayer voice recording system
2184 if( !((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_CONNECTED)) ){
2185 multi_voice_init();
2186 }
2187
2188 // set the default screen mode
2189 Om_mode = OM_MODE_NONE;
2190 }
2191
2192 // do frame for the multi options screen
options_multi_do(int key)2193 void options_multi_do(int key)
2194 {
2195 // do frame for the protocol section
2196 options_multi_protocol_do(key);
2197
2198 // process and blit any notification messages
2199 options_multi_notify_process();
2200
2201 // process the proper tab control
2202 switch(Om_mode){
2203 case OM_MODE_GENERAL:
2204 options_multi_gen_do();
2205 break;
2206 case OM_MODE_VOX:
2207 options_multi_vox_do();
2208 break;
2209 default :
2210 Int3();
2211 }
2212 }
2213
2214 // called when the entire options screen is closed
options_multi_close()2215 void options_multi_close()
2216 {
2217 // null out the window handle
2218 Om_window = NULL;
2219
2220 // unload all background bitmaps
2221 options_multi_unload_bmaps();
2222
2223 // stop any playing voice
2224 rtvoice_stop_playback_all();
2225
2226 // unset the screen mode
2227 Om_mode = OM_MODE_NONE;
2228 }
2229
2230 /**
2231 * Checks if the multiplayer config screen is in a legal state to exit
2232 **/
options_multi_ok_to_accept()2233 bool options_multi_ok_to_accept()
2234 {
2235 // if PXO is turned on, do we have a username and password?
2236 if (Multi_options_g.pxo) {
2237 if (strlen(Multi_tracker_login) == 0) {
2238 return false;
2239 }
2240 else if (strlen(Multi_tracker_passwd) == 0) {
2241 return false;
2242 }
2243 }
2244
2245 return true;
2246 }
2247
2248 /**
2249 * Called if the accept button on the main options screen was hit.
2250 * Returns false if the multi option screen is not in a legal state
2251 **/
options_multi_accept()2252 bool options_multi_accept()
2253 {
2254 // accept function for the protocol section
2255 options_multi_protocol_accept();
2256
2257 // is it legal to leave this screen?
2258 if (!options_multi_ok_to_accept()) {
2259 return false;
2260 }
2261
2262 // accept function for the general tab
2263 options_multi_gen_accept();
2264
2265 // accept function for the voice tab
2266 options_multi_vox_accept();
2267
2268 // if Net_player is not null, copy these new settings to him
2269 if(Net_player != NULL){
2270 multi_options_local_load(&Net_player->p_info.options, NULL);
2271 }
2272 multi_options_local_load(&Player->m_local_options, NULL);
2273
2274 // if we're connected to a game server, update our options on the server now
2275 if((Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && MULTI_CONNECTED(Net_players[MY_NET_PLAYER_NUM]) ){
2276 multi_options_update_local();
2277 }
2278
2279 return true;
2280 }
2281
2282 // called when the multiplayer tab is hit - initializes/switches all necessary data.
2283 // NOTE : this is different from the initialization function, which is called only when the options menu is started
options_multi_select()2284 void options_multi_select()
2285 {
2286 // set the windows mask bitmap
2287 Assert(Om_mask_0 >= 0);
2288 Om_window->set_mask_bmap(Om_mask_0, Om_background_0_mask_fname[gr_screen.res]);
2289
2290 // set the default screen mode
2291 Om_mode = OM_MODE_GENERAL;
2292
2293 // clear any notification messages
2294 Om_notify_stamp = -1;
2295
2296 // enable all the protocol controls
2297 options_multi_enable_protocol_controls();
2298
2299 // enable the general tab controls
2300 options_multi_enable_gen_controls();
2301 }
2302
2303 // return the bitmap handle of the current background bitmap, or -1 if the multiplayer tab is not active
options_multi_background_bitmap()2304 int options_multi_background_bitmap()
2305 {
2306 // return the background bitmap mode based upon the current mode
2307 switch(Om_mode){
2308 case OM_MODE_GENERAL:
2309 return Om_background_0;
2310
2311 case OM_MODE_VOX:
2312 return Om_background_1;
2313 }
2314
2315 // unknown mode of some kind
2316 return -1;
2317 }
2318
2319 // called when the multiplayer tab has been switched from
options_multi_unselect()2320 void options_multi_unselect()
2321 {
2322 // unset the mode
2323 Om_mode = OM_MODE_NONE;
2324
2325 // disable all the protocol controls
2326 options_multi_disable_protocol_controls();
2327
2328 // disable all the general tab controls
2329 options_multi_disable_gen_controls();
2330
2331 // disable all the vox tab controls
2332 options_multi_disable_vox_controls();
2333
2334 // stop any test voice recording
2335 multi_voice_test_record_stop();
2336 }
2337
2338 // set voice sound buffer for display
options_multi_set_voice_data(unsigned char * sound_buf,int buf_size,double)2339 void options_multi_set_voice_data(unsigned char *sound_buf, int buf_size, double /*gain*/)
2340 {
2341 if ( (sound_buf == NULL) || (buf_size <= 0) ) {
2342 return;
2343 }
2344
2345 // copy the buffer to the vox tab data
2346 Om_vox_voice_buffer_size = MIN(buf_size, OM_VOX_BUF_SIZE);
2347 memcpy(Om_vox_voice_buffer, sound_buf, Om_vox_voice_buffer_size);
2348
2349 // copy and uncompress the compressed buffer
2350 if(Om_vox_voice_comp_size == -1){
2351 Om_vox_voice_comp_size = 0;
2352 }
2353
2354 if ( (Om_vox_voice_comp_size + buf_size) < OM_VOX_COMP_SIZE ) {
2355 memcpy(Om_vox_comp_buffer + Om_vox_voice_comp_size, sound_buf, buf_size);
2356 Om_vox_voice_comp_size += buf_size;
2357 }
2358 }
2359
2360 // return whether we want to eat a tabbed keypress
options_multi_eat_tab()2361 int options_multi_eat_tab()
2362 {
2363 // do we want to eat the tab key or not
2364 if(Om_tracker_passwd.has_focus() || Om_tracker_login.has_focus() || Om_tracker_squad_name.has_focus()){
2365 return 1;
2366 }
2367
2368 return 0;
2369 }
2370