2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
15 #pragma off (unreferenced)
16 static char rcsid[] = "$Id: winmodem.c,v 1.1.1.1 2001-01-19 03:30:14 bradleyb Exp $";
17 #pragma on (unreferenced)
23 #include "win\xtapi.h"
61 #define MIN_COMM_GAP 8000
62 #define INIT_STRING_LEN 20
63 #define LEN_PHONE_NUM_OLD 15
64 #define LEN_PHONE_NUM 30
65 #define LEN_PHONE_NAME 12
66 #define NUM_PHONE_NUM 8
73 // How many times to repeat 'reliable' messages
77 #define COM_PROCESS_NORMAL 0
78 #define COM_PROCESS_ENDLEVEL 1
79 #define COM_PROCESS_SYNC 2
80 #define COM_PROCESS_MENU 3
82 #define SELECTION_STARTGAME 1
83 #define SELECTION_NO_START 2
84 #define SELECTION_YES_START 3
85 #define SELECTION_STARTGAME_ABORT 4
86 #define SELECTION_CLOSE_LINK 5
89 // Code to support modem/null-modem play
90 // Program determined variables for serial play
92 typedef struct com_sync_pack {
99 char callsign[CALLSIGN_LEN+1];
103 char mission_name[9];
112 short DoAfterburner:1;
113 short DoInvulnerability:1;
119 short DoSuperLaser:1;
125 short DoEarthShaker:1;
127 short Allow_marker_view:1;
128 short RefusePlayers:1;
129 short AlwaysLighting:1;
134 char dummy[3]; // Extra space for checksum & sequence number
138 int default_base[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
139 int default_irq[4] = { 4, 3, 4, 3 };
143 BOOL ModemDetect = FALSE;
148 int com_baud_rate = 0;
149 //--unused-- int sync_time = 0;
152 int other_got_sync = 0;
154 long com_type = -1; /* What type of UART is available */
156 static ubyte rx_seqnum = 0xff;
157 static ubyte tx_seqnum = 0;
158 int OtherPlayer; // Player num for modem opponent
159 int com_process_mode = COM_PROCESS_NORMAL;
160 int master = -1; // Am I the master or is the other guy the master?
161 com_sync_pack my_sync, other_sync;
164 int com_custom_port = -1;
165 int com_custom_irq = -1;
166 int com_custom_base = -1;
168 int other_menu_choice = 0;
174 static char syncbuffer[MAX_MULTI_MESSAGE_LEN+4];
175 static char sendbuf[MAX_MULTI_MESSAGE_LEN+4]; // +4 because of +1 for null and +3 for checksum/sequence
177 // Serial setup variables
179 int com_port_num = -1;
181 char modem_init_string[INIT_STRING_LEN+1];
182 char phone_num[NUM_PHONE_NUM+1][LEN_PHONE_NUM+1];
183 char phone_name[NUM_PHONE_NUM][LEN_PHONE_NAME+1];
185 #define MAX_MODEMS 200
186 #define MODEM_NAME_LEN 22
188 char ModemNames[MAX_MODEMS][MODEM_NAME_LEN+1],ModemStrings[MAX_MODEMS][INIT_STRING_LEN+1]; // for init strings
189 char ModemInitString[INIT_STRING_LEN+1];
192 fix SerialLastMessage = 0;
195 /* Function prototypes for functions not exported through modem.h */
197 void modem_main_menu(int modem);
198 void modem_dialout(int modem);
200 void modem_answer(int modem);
201 void modem_wait_for_connect(int nitems, newmenu_item *menus, int *key, int citem);
202 void modem_wait_for_ring(int nitems, newmenu_item *menus, int *key, int citem);
204 int serial_link_menu(int port);
205 void serial_link_start();
207 int com_game_menu(void);
208 void com_start_game(void);
209 void com_menu_poll(int nitems, newmenu_item *menus, int *key, int citem);
210 int com_sync(int id);
211 void com_sync_poll(int nitem, newmenu_item *menus, int *key, int citem);
213 int GetModemList(void);
220 #if !defined(NDEBUG) && !defined(NMONO)
222 com_dump_string(char *string)
224 mprintf((0, "%s\n", string));
227 #define com_dump_string()
240 Comm.baud = com_speed;
242 if (!comm_open_connection(&Comm)) {
243 nm_messagebox(TXT_ERROR, 1, TXT_OK, "Failed to open COM port.");
244 comm_close_connection(&Comm);
248 comm_set_dtr(&Comm, 1);
250 if ( FindArg( "-ctsrts" ) || FindArg( "-rtscts" ) )
251 comm_set_rtscts(&Comm, 1); // Now used for null-modem as well
253 comm_set_rtscts(&Comm, 0);
259 mprintf((0, "Comm object created.\n"));
261 comm_dump_info(&Comm);
269 // close the com port and free associated structures.
274 comm_close_connection(&Comm);
281 mprintf((0, "Comm object released.\n"));
289 // this is the safest way to get out of some modem/serial negotiation
290 // and back to the main menu. Use this whenever this have gone too far
297 change_playernum_to(0);
299 Viewer = ConsoleObject = &Objects[0];
300 Game_mode = GM_GAME_OVER; // Force main menu selection
306 // Close the serial link
308 com_send_choice(SELECTION_CLOSE_LINK);
314 com_carrier_lost(void)
316 // Carrier lost, inform and abort
318 if (multi_in_menu > 0)
320 multi_leave_menu = 1;
324 Function_mode = FMODE_MENU;
327 nm_messagebox(NULL, 1, TXT_OK, TXT_CARRIER_LOST);
333 extern ubyte Cockpit_mode_save; // From object.c
339 // Reset various parameters before starting a new game
343 for (i = 0; i < N_players; i++)
345 Players[i].connected = 1;
350 multi_new_game(); // Reset kill list, among other things
351 Control_center_destroyed = 0;
352 Endlevel_sequence = 0;
354 // Yes, this really IS as ugly as it gets, kids...
356 if (Cockpit_mode == CM_LETTERBOX || Cockpit_mode == CM_REAR_VIEW)
358 select_cockpit(Cockpit_mode_save);
363 // WE SHOULD CHANGE THIS TO BE MORE WINDOW LIKE?
366 com_save_settings(void)
372 if ( (settings = fopen("serial.cfg", "wb")) == NULL)
375 if (fwrite(&com_speed, sizeof(int), 1, settings) != 1)
378 if (fwrite(&com_port_num, sizeof(int), 1, settings) != 1)
381 if (fwrite(modem_init_string, 1, INIT_STRING_LEN+1, settings) != INIT_STRING_LEN+1)
384 for (i = 0; i < NUM_PHONE_NUM; i++)
386 if (fwrite(phone_num[i], 1, LEN_PHONE_NUM+1, settings) != LEN_PHONE_NUM+1)
388 if (fwrite(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1)
392 if (fwrite(&com_custom_port, sizeof(int), 1, settings) != 1)
394 if (fwrite(&com_custom_irq, sizeof(int), 1, settings) != 1)
396 if (fwrite(&com_custom_base, sizeof(int), 1, settings) != 1)
404 nm_messagebox(NULL, 1, TXT_OK, TXT_ERROR_SERIAL_CFG);
408 unlink("serial.cfg");
416 com_load_settings(void)
422 if ((settings = fopen("serial.cfg", "rb")) == NULL)
425 cfg_size = filelength(fileno(settings));
427 // Read the data from the file
429 if (fread(&com_speed, sizeof(int), 1, settings) != 1)
431 if (! ((com_speed == 9600) || (com_speed == 19200) || (com_speed == 38400)) )
434 mprintf((0, "Here?"));
436 /* Not really used for Windows version */
437 if (fread(&com_port_num, sizeof(int), 1, settings) != 1)
439 if ( (com_port_num < COM1) || (com_port_num > COM4) )
442 mprintf((0, "Here?"));
445 /* Not really used for Windows version */
446 if (fread(modem_init_string, 1, INIT_STRING_LEN+1, settings) != INIT_STRING_LEN+1)
448 modem_init_string[INIT_STRING_LEN] = '\0';
450 mprintf((0, "Here?"));
453 /* Used in Windows version */
454 if (cfg_size <= 273 ) { // Old 15 char LEN_PHONE_NUM's
455 mprintf(( 1, "Reading old pre 1.1 phone.cfg\n" ));
456 for (i = 0; i < NUM_PHONE_NUM; i++)
458 if (fread(phone_num[i], 1, LEN_PHONE_NUM_OLD+1, settings) != LEN_PHONE_NUM_OLD+1)
460 phone_num[i][LEN_PHONE_NUM_OLD] = '\0';
461 if (fread(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1)
463 phone_name[i][LEN_PHONE_NAME] = '\0';
465 } else { // Normal Phone nums
466 for (i = 0; i < NUM_PHONE_NUM; i++)
468 if (fread(phone_num[i], 1, LEN_PHONE_NUM+1, settings) != LEN_PHONE_NUM+1)
470 phone_num[i][LEN_PHONE_NUM] = '\0';
471 if (fread(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1)
473 phone_name[i][LEN_PHONE_NAME] = '\0';
477 mprintf((0, "Here?"));
480 /* Not really used in Windows version */
481 if (fread(&com_custom_port, sizeof(int), 1, settings) != 1) {
482 mprintf((0, "Reading old file format for serial.cfg.\n"));
486 if ( (com_custom_port < -1) || (com_custom_port > COM4) )
489 if (fread(&com_custom_irq, sizeof(int), 1, settings) != 1)
492 //!! if ( (com_custom_port < -1) || (com_custom_port > IRQ15) )
495 if (fread(&com_custom_base, sizeof(int), 1, settings) != 1)
497 if (com_custom_base < -1)
501 mprintf((0, "Here?"));
502 //Everything was A-Ok!
511 nm_messagebox(NULL, 1, TXT_OK, TXT_ERR_SER_SETTINGS);
514 // Return some defaults
515 com_speed = CBR_19200;
517 /* Here so we can save them back to config file. */
518 strcpy(modem_init_string, "ATZ");
520 com_custom_port = -1;
522 for (i = 0; i < NUM_PHONE_NUM; i++)
524 phone_num[i][0] = '\0';
525 strcpy(phone_name[i], TXT_EMPTY);
535 serial_leave_game(void)
538 mprintf((0, "Called serial_leave_game.\n"));
541 serial_sync_abort(0); // Just in case the other guy is in sync mode
542 Game_mode |= GM_GAME_OVER;
543 Function_mode = FMODE_MENU;
548 com_send_data(char *ptr, int len, int repeat)
552 // Take the raw packet data specified by ptr and append the sequence
553 // number and checksum, and pass it to com_send_ptr
558 if (Game_mode & GM_MODEM)
560 i = comm_get_cd(&Comm);
562 mprintf((0, "CARRIER LOST!\n"));
568 len += 3; // Checksum data is 3 bytes
570 *(ubyte *)(ptr+(len-3)) = (tx_seqnum+1)%256;
571 tx_seqnum = (tx_seqnum+1)%256;
573 *(ushort *)(ptr+(len-2)) = netmisc_calc_checksum(ptr, len-2);
575 com_send_ptr(ptr, len);
577 for (i = 0; i < repeat; i++)
578 com_send_ptr(ptr, len);
581 com_send_ptr(char *ptr, int len)
586 for (count = 0, dat=ptr[0]; count < len; dat=ptr[++count])
588 comm_write_char(&Comm, dat);
590 comm_write_char(&Comm, EOR_MARK); // double in-band endmarkers
592 comm_write_char(&Comm, EOR_MARK);
593 comm_write_char(&Comm, 0);
601 // Get rid of all waiting data in the serial buffer
608 mprintf((0, "COM FLUSH:"));
610 while (comm_read_char_timed(&Comm, 100) >= 0)
612 mprintf((0, "%d characters.\n", i));
619 static int eor_recv = 0;
622 // -1 = Nothing in buffer
623 // -2 = End of record
628 i = comm_read_char(&Comm);;
630 // mprintf((0, "%d", i));
632 if (i == COMM_BUF_EMPTY)
635 if ((i == EOR_MARK) || eor_recv)
638 i = comm_read_char(&Comm);
640 if (i == COMM_BUF_EMPTY)
642 // Assert(eor_recv == 0);
646 else if (i == EOR_MARK)
649 return(EOR_MARK); // Doubled EOR returns the character
655 mprintf((0, "EOR followed by unexpected value %d.\n", i));
665 #define SERIAL_IDLE_TIMEOUT F1_0*10
671 static fix last_comm_time = 0;
672 static int last_pos_skipped = 0;
675 if (Endlevel_sequence || (com_process_mode==COM_PROCESS_ENDLEVEL)) { // Only recieve during endlevel
676 int old_Endlevel_sequence = Endlevel_sequence;
677 Endlevel_sequence = 1;
679 Endlevel_sequence = old_Endlevel_sequence;
683 last_comm_time += FrameTime;
685 if ((last_comm_time > MIN_COMM_GAP) || Network_laser_fired)
688 if ((Game_mode & GM_MULTI_ROBOTS) && !last_pos_skipped) {
689 rval = multi_send_robot_frame(0);
691 if (rval && !Network_laser_fired)
693 last_pos_skipped = 1;
697 last_pos_skipped = 0;
698 multi_send_position(Players[Player_num].objnum);
699 multi_send_fire(); // Will return w/o sending if we haven't fired
702 // mprintf((0, "%d chars sent, %f cps.\n", chars_sent, f2fl(fixdiv((chars_sent*F1_0),last_comm_time)) ));
709 if (!Control_center_destroyed && (Function_mode == FMODE_GAME) && (SerialLastMessage+SERIAL_IDLE_TIMEOUT < GameTime))
711 SerialLastMessage = 0x7fffffff-SERIAL_IDLE_TIMEOUT; // Give no further warnings until next message arrives!
712 nm_messagebox(TXT_WARNING, 1, TXT_OK, TXT_CONNECT_LOST, Players[OtherPlayer].callsign);
719 com_check_message(char *checkbuf, int len)
724 //mprintf ((0,"TYPE=%d!\n",checkbuf[0]));
728 mprintf((0, "message type %d too short to be a real message!\n", checkbuf[0]));
732 if (checkbuf[0] > MULTI_MAX_TYPE)
734 mprintf((0, "message type %d out of range.\n", checkbuf[0]));
738 if ( (len-3) != message_length[checkbuf[0]])
740 mprintf((0, "id:%d message length %d != %d.\n",checkbuf[0], len-3, message_length[checkbuf[0]]));
744 check = netmisc_calc_checksum(checkbuf, len-2);
745 if (check != *(ushort *)(checkbuf+(len-2)))
748 mprintf((0, "error in message type %d, length %d, checksum %d != %d\n", checkbuf[0], len, check, *(ushort *)(checkbuf+(len-2))));
753 seqnum = checkbuf[(len-3)];
755 if (seqnum == rx_seqnum)
757 // mprintf ((0,"returning due to bad checksum! type=%d seq=%d rx=%d\n",checkbuf[0],seqnum,rx_seqnum));
761 if (seqnum != (rx_seqnum+1)%256)
764 mprintf((0, "Warning, missed 1 or more messages.\n"));
768 //mprintf((0, "message type %d len %d OK!\n", checkbuf[0], len));
772 mprintf((1,"Line status: %d.\n", comm_get_line_status(&Comm)));
773 comm_clear_line_status(&Comm);
780 com_process_menu(char *buf, int len)
786 mprintf((0, "com_process_menu: type %d.\n", buf[0]));
791 sprintf(text, "%s %s\n'%s'", Players[OtherPlayer].callsign, TXT_SAYS, buf+2);
792 nm_messagebox(NULL, 1, TXT_OK, text);
794 case MULTI_MENU_CHOICE:
795 other_menu_choice = buf[1];
796 mprintf((0, "Other menu choice = %d.\n", other_menu_choice));
798 case MULTI_BEGIN_SYNC:
799 // If we get a sync at the menu, send an abort sync, we're not ready yet!
800 serial_sync_abort(0);
806 com_process_input(void)
808 // Read all complete messages from the serial buffer and process
809 // the contents. Messages are read into global array snycbuffer.
812 int entry_com_mode = com_process_mode;
819 if (Game_mode & GM_MODEM)
821 if (!comm_get_cd(&Comm))
826 if (!multi_in_menu) {
830 multi_leave_menu = 1;
834 if (com_process_mode != entry_com_mode)
836 mprintf((0, "Exiting com_process_input due to mode switch.\n"));
840 while ( (len <= MAX_MULTI_MESSAGE_LEN) && (dat = com_getchar()) > -1) // Returns -1 when serial pipe empty
842 syncbuffer[len++] = dat;
845 if ((dat == -2) || (len > MAX_MULTI_MESSAGE_LEN)) // Returns -2 when end of message reached
848 SerialLastMessage = GameTime;
850 if (!com_check_message(syncbuffer, len))
852 switch(com_process_mode)
854 case COM_PROCESS_NORMAL:
855 case COM_PROCESS_ENDLEVEL:
856 multi_process_data(syncbuffer, len); break;
857 case COM_PROCESS_MENU:
858 if (!Endlevel_sequence) com_process_menu(syncbuffer, len); break;
859 case COM_PROCESS_SYNC:
860 if (!Endlevel_sequence) com_process_sync(syncbuffer, len); break;
862 Int3(); // Bad com_process_mode switch set!
865 else mprintf ((0,"Huh?\n"));
870 if (dat == -3) // Returns -3 when carrier lost
883 my_sync.type = MULTI_BEGIN_SYNC;
884 my_sync.difficulty = 0;
885 memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
886 my_sync.seg_checksum = 0;
887 my_sync.game_mode = Game_mode;
888 my_sync.level_num = 0;
891 mprintf((0, "com_connect()\n"));
895 return(-1); // Failure in sync
900 mprintf((0, "My rand = %d, other rand = %d.\n", my_sync.sync_time, other_sync.sync_time));
903 // Figure out who is the master
904 if (my_sync.sync_time > other_sync.sync_time)
906 mprintf((0, "Swtiching player to master.\n"));
908 change_playernum_to(0);
910 else if (my_sync.sync_time < other_sync.sync_time)
912 mprintf((0, "Switching player to slave.\n"));
914 change_playernum_to(1);
917 return(-1); // Didn't sync properly, try again
920 // Copy the remote sync data into local variables
922 OtherPlayer = (Player_num+1)%2;
923 mprintf((0, "Other player is #%d.\n", OtherPlayer));
924 memcpy(Players[OtherPlayer].callsign, other_sync.callsign, CALLSIGN_LEN+1);
931 // ----------------------------------------------------------------------------
933 // ----------------------------------------------------------------------------
935 #define ADD_ITEM(t,value,key) do { m[num_options].type=NM_TYPE_MENU; menu_choice[num_options]=value; m[num_options].text=t; num_options++; } while (0)
936 #define SUBTITLE_LEN 192
938 #define MENU_XTAPI_DEVICE 0
939 #define MENU_COMM_DEVICE 2
941 #define MENU_SERIAL_GAME_START 5
942 #define MENU_SEND_MESSAGE 6
943 #define MENU_SERIAL_CLOSE 7
944 #define MENU_MODEM_HANGUP 8
947 static TapiDevice *tapiDevices=NULL;
948 static int xtapiCurrentModem = -1;
949 static int numTapiDevs = 0;
951 extern void nm_draw_background1(char * filename);
954 void com_main_menu(void)
958 int device_type[6]; // 0 = serial, 1 = modem
960 int i, num_options=0, choice;
967 // setjmp(LeaveGame);
969 nm_draw_background1(Menu_pcx_name);
970 com_process_mode = COM_PROCESS_MENU;
972 if (Game_mode & GM_SERIAL) { // HACK to get menu system working
973 Assert(Comm.port >= 1 && Comm.port <= 4);
974 mprintf((0, "WINMODEM: Going to serial link menu!\n"));
975 sel = MENU_COMM_DEVICE + (Comm.port-1);
976 goto skip_to_com_submenus;
978 else if (Game_mode & GM_MODEM) {
979 Assert(xtapiCurrentModem != -1);
980 mprintf((0, "WINMODEM: Going to modem menu!\n"));
981 sel = xtapiCurrentModem;
982 goto skip_to_com_submenus;
985 // Get devices for TAPI.
986 retval = xtapi_init(WINAPP_NAME, &numTapiDevs);
987 if (retval == XTAPI_NODEVICES) {
990 else if (retval == XTAPI_APPCONFLICT) {
993 else if (retval == XTAPI_INCOMPATIBLE_VERSION) {
997 if (numTapiDevs > 2) numTapiDevs = 2;
998 if (numTapiDevs == 0) tapiDevices = NULL;
1000 tapiDevices = (TapiDevice *)malloc(numTapiDevs*sizeof(TapiDevice));
1002 Error("Unable to allocate memory for com_main_menu.");
1004 retval = xtapi_enumdevices(tapiDevices, numTapiDevs);
1005 if (retval != XTAPI_SUCCESS) {
1012 for (i = 0; i < numTapiDevs; i++)
1014 if (tapiDevices[i].type == XTAPI_MODEM_DEVICE) {
1015 sprintf(buf, "Modem (%d baud)", tapiDevices[i].max_baud);
1019 sprintf(buf, "Unsupported device (%d baud)", tapiDevices[i].max_baud);
1023 m[i].type=NM_TYPE_MENU; menu_choice[i]=MENU_XTAPI_DEVICE+i;
1024 m[i].text=buf; num_options++;
1027 device_type[num_options] = 0;
1028 m[num_options].type=NM_TYPE_MENU; menu_choice[num_options]=MENU_COMM_DEVICE;
1029 m[num_options].text="Direct to Com 1"; num_options++;
1031 device_type[num_options] = 0;
1032 m[num_options].type=NM_TYPE_MENU; menu_choice[num_options]=MENU_COMM_DEVICE+1;
1033 m[num_options].text="Direct to Com 2"; num_options++;
1035 device_type[num_options] = 0;
1036 m[num_options].type=NM_TYPE_MENU; menu_choice[num_options]=MENU_COMM_DEVICE+2;
1037 m[num_options].text="Direct to Com 3"; num_options++;
1039 device_type[num_options] = 0;
1040 m[num_options].type=NM_TYPE_MENU; menu_choice[num_options]=MENU_COMM_DEVICE+3;
1041 m[num_options].text="Direct to Com 4"; num_options++;
1043 choice = newmenu_do1(NULL, "Select Serial Device", num_options, m, NULL, 0);
1046 if (tapiDevices) free(tapiDevices);
1050 else if (choice < -1) {
1055 sel = menu_choice[choice];
1057 skip_to_com_submenus:
1061 case MENU_XTAPI_DEVICE:
1062 case MENU_XTAPI_DEVICE+1:
1063 // start a xtapi game
1064 modem_main_menu(sel);
1065 if (Function_mode == FMODE_GAME) return;
1068 case MENU_COMM_DEVICE:
1069 case MENU_COMM_DEVICE+1:
1070 case MENU_COMM_DEVICE+2:
1071 case MENU_COMM_DEVICE+3:
1072 // start a serial game
1073 mprintf((0, "Selected a null-modem game.\n"));
1074 if (!serial_link_menu(sel-MENU_COMM_DEVICE+1))
1075 goto com_menu_redraw;
1076 mprintf((0, "WINMODEM: starting game...\n"));
1081 if (tapiDevices) free(tapiDevices);
1083 nm_draw_background1(Menu_pcx_name);
1087 // if 1, then we are starting a game
1089 int com_game_menu(void)
1092 int menu_choice[12];
1093 int num_options = 0;
1095 char subtitle[SUBTITLE_LEN];
1100 com_game_menu_redraw:
1103 ADD_ITEM(TXT_START_GAME, MENU_SERIAL_GAME_START, KEY_S);
1104 ADD_ITEM(TXT_SEND_MESSAGEP, MENU_SEND_MESSAGE, KEY_S);
1105 if (Game_mode & GM_MODEM)
1106 ADD_ITEM(TXT_HANGUP_MODEM, MENU_MODEM_HANGUP, KEY_H);
1108 if (Game_mode & GM_SERIAL)
1109 ADD_ITEM(TXT_CLOSE_LINK, MENU_MODEM_HANGUP, KEY_C);
1111 m[num_options].type=NM_TYPE_TEXT; m[num_options].text=""; num_options++;
1113 if (Game_mode & GM_SERIAL)
1114 sprintf(subtitle, "%s\n\n%s %s\n%s", TXT_SERIAL_GAME, TXT_SERIAL, TXT_LINK_ACTIVE, Players[OtherPlayer].callsign);
1115 else if (Game_mode & GM_MODEM)
1116 sprintf(subtitle, "%s\n\n%d %s %s\n%s", TXT_SERIAL_GAME, com_baud_rate, TXT_BAUD, TXT_LINK_ACTIVE, Players[OtherPlayer].callsign);
1118 mprintf((1, "Game mode isn't set correctly.\n"));
1122 multi_leave_menu = 0;
1124 Assert(strlen(subtitle) < SUBTITLE_LEN);
1126 choice = newmenu_do1(NULL, subtitle, num_options, m, com_menu_poll, 0);
1130 m[0].text = TXT_YES; m[1].text = TXT_NO;
1131 m[0].type = m[1].type = NM_TYPE_MENU;
1133 choice = newmenu_do1(NULL, TXT_EXIT_WILL_CLOSE, 2, m, com_menu_poll, 0);
1136 com_send_choice(SELECTION_CLOSE_LINK);
1140 if ((choice == -2) && (other_menu_choice))
1141 com_process_other_menu_choice();
1143 goto com_game_menu_redraw;
1148 // Menu poll loop caused a re-draw
1149 if (other_menu_choice == SELECTION_STARTGAME)
1150 com_ready_to_start();
1151 else if (other_menu_choice == SELECTION_CLOSE_LINK)
1153 nm_messagebox(NULL, 1, TXT_OK, TXT_CLOSED_LINK);
1155 mprintf((0, "Leaving modem game (hanging up)...\n"));
1158 if (Function_mode == FMODE_GAME) {
1159 mprintf((0, "We think we're still in a modem game!.\n"));
1164 mprintf((0, "At this point we're taking off..\n"));
1165 Game_mode = GM_GAME_OVER;
1169 goto com_game_menu_redraw;
1172 comm_dump_info(&Comm);
1176 // old_game_mode=Game_mode;
1177 switch (menu_choice[choice])
1179 case MENU_SERIAL_GAME_START:
1181 if (Function_mode != FMODE_GAME)
1182 goto com_game_menu_redraw;
1185 case MENU_SEND_MESSAGE:
1186 multi_send_message_dialog();
1187 if (Network_message_reciever != -1)
1188 multi_send_message();
1189 multi_sending_message = 0;
1190 goto com_game_menu_redraw;
1193 case MENU_MODEM_HANGUP:
1198 mprintf((1, "Bad com_game_menu choice\n"));
1209 * Serial Link/Null Modem Functions
1212 // 1 = starting game
1214 int serial_link_menu(int port)
1216 /* This menu will give a listing of possible com_speeds. The default will
1217 be given, but the user should be given the option to change to a lower
1221 int com_max_setting, loc, i, mmn, menu_link;
1222 int com_speed_setting;
1223 int com_speeds[5] = {
1224 CBR_9600, CBR_19200, CBR_38400, CBR_57600, CBR_115200
1226 char *com_speeds_text[5] = {
1227 "9600", "19200", "38400", "57600", "115200"
1230 // HACK to get menu system working when leaving serial game to
1231 // get to correct modem.
1232 if (Game_mode & GM_SERIAL) {
1233 mprintf((0, "WINMODEM:Go directly to com_game menu.\n"));
1234 goto skip_to_game_menu;
1236 else mprintf((0, "WINMODEM:Not going directly to com_game menu.\n"));
1238 /* First check what speed the selected port is set to */
1239 if (com_open) com_disable();
1241 memset(&Comm, 0, sizeof(COMM_OBJ));
1243 Comm.baud = com_speed;
1245 if (!comm_open_connection(&Comm)) {
1246 nm_messagebox(TXT_ERROR, 1, TXT_OK, "Unable to open selected COM port");
1252 case CBR_9600: com_max_setting = 1; break;
1253 case CBR_19200: com_max_setting = 2; break;
1254 case CBR_38400: com_max_setting = 3; break;
1256 mprintf((0, "COM%d baud = %d\n", Comm.port, Comm.baud));
1257 if (Comm.baud > CBR_38400) com_max_setting = 3;
1259 nm_messagebox(TXT_ERROR, 1, TXT_OK, "Your serial system must be\n capable of at least 9600 baud.");
1260 com_max_setting = -1;
1263 comm_close_connection(&Comm);
1265 if (com_max_setting == -1) return 0;
1270 mm[0].type=NM_TYPE_TEXT; mm[0].text = TXT_BAUD_RATE; loc++;
1272 for (i = 0; i < 3; i++) // Limit to 38.4K
1274 mm[i+1].type=NM_TYPE_RADIO; mm[i+1].value=(i==(com_max_setting-1));
1275 mm[i+1].text=com_speeds_text[i]; mm[i+1].group=1; loc++;
1277 mm[loc].type=NM_TYPE_TEXT; mm[loc].text = ""; loc++;
1280 mm[loc].type=NM_TYPE_MENU; mm[loc].text = "Establish Link"; loc++;
1282 mmn = newmenu_do1(NULL, TXT_SERIAL_SETTINGS, loc, mm, NULL, menu_link);
1284 /* Take care of selection */
1286 for (i = 0; i < 3; i++)
1287 if (mm[i+1].value) {
1288 com_speed_setting = com_speeds[i];
1292 if(menu_link != mmn) goto setup_serial_menu;
1296 com_speed = com_speed_setting;
1299 serial_link_start(); // We should be in GM_SERIAL after this
1301 com_save_settings();
1304 if (Game_mode & GM_SERIAL) {
1305 if (com_game_menu()) return 1;
1312 void serial_link_start(void)
1316 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT);
1320 com_enable(); // Open COM port as configured
1330 my_sync.sync_time = rand();
1331 mprintf((0, "My rand set to %d.\n", my_sync.sync_time));
1335 Game_mode |= GM_SERIAL;
1336 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1340 nm_messagebox(NULL, 1, TXT_OK, "%s\n%s", TXT_ERROR, TXT_FAILED_TO_NEGOT);
1348 * modem class functions
1351 void modem_main_menu(int modem)
1357 if (Game_mode & GM_MODEM) {
1358 mprintf((0, "WINMODEM:Going directly to com_game_menu.\n"));
1365 m[num_options].type=NM_TYPE_MENU; m[num_options].text="Dial"; num_options++;
1366 m[num_options].type=NM_TYPE_MENU; m[num_options].text="Answer"; num_options++;
1367 m[num_options].type=NM_TYPE_TEXT; m[num_options].text=""; num_options++;
1369 choice = newmenu_do1(NULL, "Modem Options", num_options, m, NULL, 0);
1374 modem_dialout(modem);
1375 if (Game_mode & GM_MODEM) com_game_menu();
1376 if (Function_mode == FMODE_GAME) return;
1380 modem_answer(modem);
1381 if (Game_mode & GM_MODEM) com_game_menu();
1382 if (Function_mode == FMODE_GAME) return;
1387 else if (choice == -1) return;
1389 goto modem_menu_redraw;
1395 if (xtapiCurrentModem > -1) {
1396 mprintf((0, "WINMODEM: Hanging up..\n"));
1398 xtapi_device_hangup();
1399 xtapi_unlock(&tapiDevices[xtapiCurrentModem]);
1400 xtapiCurrentModem = -1;
1401 mprintf((0, "WINMODEM: Complete!\n"));
1406 void modem_dialout(int modem)
1411 // At this point, xtapi is initialized, so we just need to get a number
1416 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT);
1420 retval = xtapi_lock(&tapiDevices[modem]);
1421 if (retval != XTAPI_SUCCESS) {
1422 mprintf((1, "Failed to lock down an XTAPI device.\n"));
1429 xtapiCurrentModem = modem; // Modem controls com functions
1432 // Select a number to dial
1433 if ((choice = modem_dial_menu()) == -1) {
1438 if (strlen(phone_num[choice]) == 0)
1440 nm_messagebox(NULL, 1, TXT_OK, TXT_NO_PHONENUM);
1445 show_boxed_message(TXT_RESET_MODEM);
1446 retval = xtapi_device_dialout(phone_num[choice]);
1447 if (retval != XTAPI_SUCCESS) {
1448 clear_boxed_message();
1449 nm_messagebox(NULL, 1, TXT_OK, "Unable to initiate dialing!");
1450 mprintf((1, "XTAPI: err %d\n", retval));
1454 clear_boxed_message();
1456 m[0].type=NM_TYPE_TEXT; m[0].text="Initializing\n";
1457 m[1].type=NM_TYPE_TEXT; m[1].text=TXT_ESC_ABORT;
1460 choice = newmenu_do(NULL, TXT_WAITING_FOR_ANS, 2, m, modem_wait_for_connect);
1466 // We are now connected to the other modem
1469 my_sync.sync_time=32000;
1470 // -- commented out by mk, 2/21/96 -- master = 1; // The person who dialed is the master of the connection
1471 change_playernum_to(0);
1475 Game_mode |= GM_MODEM;
1476 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1485 void modem_answer(int modem)
1490 // At this point, xtapi is initialized, so we just need to get a number
1495 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT);
1499 retval = xtapi_lock(&tapiDevices[modem]);
1500 if (retval != XTAPI_SUCCESS) {
1501 mprintf((1, "Failed to lock down an XTAPI device.\n"));
1508 xtapiCurrentModem = modem; // Modem controls com functions
1510 // start up modem and wait for connect
1511 show_boxed_message(TXT_RESET_MODEM);
1512 retval = xtapi_device_dialin();
1513 if (retval != XTAPI_SUCCESS) {
1514 clear_boxed_message();
1515 nm_messagebox(NULL, 1, TXT_OK, "Unable to initialize modem.");
1516 mprintf((1, "XTAPI: err %d\n", retval));
1520 clear_boxed_message();
1522 // wait for a ring first.
1523 modem_answer_redraw:
1524 m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT;
1526 choice = newmenu_do(NULL, TXT_WAITING_FOR_CALL, 1, m, modem_wait_for_ring);
1532 goto modem_answer_redraw;
1534 // Now answer the phone and wait for carrier
1535 retval = xtapi_device_answer();
1539 choice = newmenu_do(NULL, TXT_WAITING_FOR_CARR, 1, m, modem_wait_for_connect);
1545 // We are now connected to the other modem
1546 // We are now connected to the other modem
1549 my_sync.sync_time=0;
1551 change_playernum_to(1);
1555 Game_mode |= GM_MODEM;
1556 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1564 void modem_wait_for_connect(int nitems, newmenu_item *menus, int *key, int citem)
1573 retval = xtapi_device_poll_callstate(&modem_state);
1574 if (retval != XTAPI_SUCCESS) {
1578 switch (modem_state)
1580 case XTAPI_LINE_DIALING:
1581 menus[0].text = "Dialing\n";
1582 menus[0].redraw = 1;
1585 case XTAPI_LINE_PROCEEDING:
1586 case XTAPI_LINE_RINGBACK:
1587 menus[0].text = "Calling\n";
1588 menus[0].redraw = 1;
1591 case XTAPI_LINE_BUSY:
1592 nm_messagebox(NULL, 1, TXT_OK, "Got a busy signal.");
1596 case XTAPI_LINE_FEEDBACK:
1597 nm_messagebox(NULL, 1, TXT_OK, "Unable to connect.");
1601 case XTAPI_LINE_DISCONNECTED:
1602 nm_messagebox(NULL, 1, TXT_OK, "Disconnected!");
1606 case XTAPI_LINE_CONNECTED:
1607 // perform connection scheme: we should create a comm object!
1608 xtapi_device_create_comm_object(&Comm);
1612 mprintf((0, "Connect at %d baud.\n", Comm.baud));
1613 if (Comm.baud < CBR_9600) {
1614 nm_messagebox(NULL, 1, TXT_OK, TXT_BAUD_GREATER_9600);
1619 com_baud_rate = Comm.baud;
1625 case XTAPI_LINE_IDLE:
1626 menus[0].text = "Idle\n";
1627 menus[0].redraw = 1;
1631 case XTAPI_LINE_DIALTONE:
1632 menus[0].text = "Dialtone\n";
1633 menus[0].redraw = 1;
1639 mprintf((0, "xtapi undefined call state.\n"));
1646 void modem_wait_for_ring(int nitems, newmenu_item *menus, int *key, int citem)
1655 result = xtapi_device_poll_callstate(&modem_state);
1657 if (modem_state == XTAPI_LINE_RINGING) {
1658 mprintf((0, "Line is ringing. Answering...\n"));
1659 result = xtapi_device_answer();
1660 if (result != XTAPI_SUCCESS) return;
1666 void modem_edit_phonebook(newmenu_item *m)
1668 int choice, choice2;
1669 newmenu_item menu[5];
1671 int default_choice = 0;
1673 m[NUM_PHONE_NUM].text = TXT_SAVE;
1675 menu[0].text = TXT_NAME; menu[0].type = NM_TYPE_TEXT;
1676 menu[1].type = NM_TYPE_INPUT; menu[1].text = text[0]; menu[1].text_len = LEN_PHONE_NAME;
1677 menu[2].text = TXT_PHONE_NUM; menu[2].type = NM_TYPE_TEXT;
1678 menu[3].type = NM_TYPE_INPUT; menu[3].text = text[1]; menu[3].text_len = LEN_PHONE_NUM;
1679 menu[4].text = TXT_ACCEPT; menu[4].type = NM_TYPE_MENU;
1682 choice = newmenu_do1(NULL, TXT_SEL_NUMBER_EDIT, NUM_PHONE_NUM+1, m, NULL, default_choice);
1685 com_load_settings();
1688 if (choice == NUM_PHONE_NUM)
1691 com_save_settings();
1698 strcpy(menu[1].text, phone_name[choice]);
1699 strcpy(menu[3].text, phone_num[choice]);
1701 choice2 = newmenu_do1(NULL, TXT_EDIT_PHONE_ENTRY, 5, menu, NULL, default_choice);
1704 strcpy(phone_name[choice], menu[1].text);
1705 strcpy(phone_num[choice], menu[3].text);
1706 sprintf(m[choice].text, "%d. %s \t", choice+1, phone_name[choice]);
1707 add_phone_number(m[choice].text, phone_num[choice] );
1711 default_choice += 2; if (default_choice > 4) default_choice = 4;
1715 default_choice = NUM_PHONE_NUM;
1721 void add_phone_number( char * src, char * num )
1734 strcat( src, "..." );
1738 int modem_dial_menu(void)
1740 newmenu_item m[NUM_PHONE_NUM+2];
1741 char menu_text[NUM_PHONE_NUM][80];
1746 for (i = 0; i < NUM_PHONE_NUM; i++)
1748 m[i].text = menu_text[i];
1749 sprintf(m[i].text, "%d. %s \t", i+1, phone_name[i]);
1750 add_phone_number(m[i].text, phone_num[i] );
1751 m[i].type = NM_TYPE_MENU;
1754 strcat(m[i-1].text, "\n");
1756 m[NUM_PHONE_NUM].type = NM_TYPE_MENU;
1757 m[NUM_PHONE_NUM].text = TXT_MANUAL_ENTRY;
1758 m[NUM_PHONE_NUM+1].text = TXT_EDIT_PHONEBOOK;
1759 m[NUM_PHONE_NUM+1].type = NM_TYPE_MENU;
1761 choice = newmenu_do1(NULL, TXT_SEL_NUMBER_DIAL, NUM_PHONE_NUM+2, m, NULL, 0);
1763 return -1; // user abort
1765 if (choice == NUM_PHONE_NUM+1)
1768 modem_edit_phonebook(m);
1772 if (choice == NUM_PHONE_NUM)
1776 m2[0].type = NM_TYPE_INPUT; m2[0].text = phone_num[NUM_PHONE_NUM]; m2[0].text_len = LEN_PHONE_NUM;
1777 choice = newmenu_do(NULL, TXT_ENTER_NUMBER_DIAL, 1, m2, NULL);
1781 return NUM_PHONE_NUM;
1784 // A phone number was chosen
1795 com_menu_poll(int nitems, newmenu_item *menus, int *key, int citem)
1797 // Watch the serial stream if we are connected and take appropriate actions
1805 com_process_mode = COM_PROCESS_MENU;
1806 old_game_mode = Game_mode;
1807 other_menu_choice = 0;
1809 com_process_input();
1811 if ((old_game_mode != Game_mode) || other_menu_choice || (com_process_mode != COM_PROCESS_MENU))
1813 if (multi_leave_menu)
1818 com_send_choice(int choice)
1820 sendbuf[0] = (char)MULTI_MENU_CHOICE;
1821 sendbuf[1] = (char)choice;
1823 com_send_data(sendbuf, 2, 1);
1827 com_ready_to_start(void)
1832 m[0].type = m[1].type = NM_TYPE_MENU;
1833 m[0].text = TXT_YES;
1836 choice = newmenu_do1(NULL, TXT_READY_DESCENT, 2, m, com_menu_poll, 0 );
1840 com_send_choice(SELECTION_YES_START);
1841 other_menu_choice = SELECTION_STARTGAME;
1846 com_send_choice(SELECTION_NO_START);
1851 com_process_other_menu_choice(void)
1853 if (other_menu_choice == SELECTION_STARTGAME)
1854 com_ready_to_start();
1855 else if (other_menu_choice == SELECTION_CLOSE_LINK)
1857 nm_messagebox(NULL, 1, TXT_OK, TXT_CLOSED_LINK);
1863 // Handshaking to start a serial game, 2 players only
1865 int com_start_game_menu(void)
1870 int opt, diff_opt, mode_opt, options_opt,allow_opt,extraopts_opt;
1871 char level_text[32];
1874 int new_mission_num, anarchy_only = 0;
1876 new_mission_num = multi_choose_mission(&anarchy_only);
1878 if (new_mission_num < 0)
1881 strcpy(my_sync.mission_name, Mission_list[new_mission_num].filename);
1884 sprintf(level, "1");
1886 Game_mode &= ~GM_MULTI_COOP;
1887 Game_mode &= ~GM_MULTI_ROBOTS;
1888 Game_mode &= ~GM_CAPTURE;
1891 Netgame.game_flags = 0;
1892 SetAllAllowablesTo(1);
1894 sprintf(level_text, "%s (1-%d)", TXT_LEVEL_, Last_level);
1895 // if (Last_secret_level < -1)
1896 // sprintf(level_text+strlen(level_text)-1, ", S1-S%d)", -Last_secret_level);
1897 // else if (Last_secret_level == -1)
1898 // sprintf(level_text+strlen(level_text)-1, ", S1)");
1900 Assert(strlen(level_text) < 32);
1902 // Put up menu for user choices controlling gameplay
1906 m[opt].type = NM_TYPE_TEXT; m[opt].text = level_text; opt++;
1907 m[opt].type = NM_TYPE_INPUT; m[opt].text_len = 4; m[opt].text = level; opt++;
1908 m[opt].type = NM_TYPE_TEXT; m[opt].text = TXT_MODE;
1910 m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_ANARCHY; m[opt].value=!(Game_mode & GM_MULTI_ROBOTS); m[opt].group = 0; opt++;
1911 m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_ANARCHY_W_ROBOTS; m[opt].value=(!(Game_mode & GM_MULTI_COOP) && (Game_mode & GM_MULTI_ROBOTS)); m[opt].group = 0; opt++;
1912 m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_COOPERATIVE; m[opt].value=(Game_mode & GM_MULTI_COOP);m[opt].group = 0; opt++;
1913 m[opt].type = NM_TYPE_RADIO; m[opt].text = "Capture the Flag"; m[opt].value=(Game_mode & GM_CAPTURE); m[opt].group = 0; opt++;
1915 m[opt].type = NM_TYPE_SLIDER; m[opt].text = TXT_DIFFICULTY; m[opt].value = Player_default_difficulty; m[opt].min_value = 0; m[opt].max_value = (NDL-1); opt++;
1916 m[opt].type = NM_TYPE_TEXT; m[opt].text = " "; opt ++;
1920 m[opt].type = NM_TYPE_CHECK; m[opt].text = "Marker camera views"; m[opt].value=Netgame.Allow_marker_view; opt++;
1921 m[opt].type = NM_TYPE_CHECK; m[opt].text = "Indestructable lights"; m[opt].value=Netgame.AlwaysLighting; opt++;
1924 m[opt].type = NM_TYPE_MENU; m[opt].text = "Choose objects allowed"; opt++;
1929 // m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_IDS; m[opt].value=0; opt++;
1930 m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_ON_MAP; m[opt].value=0; opt++;
1937 choice = newmenu_do1(NULL, TXT_SERIAL_GAME_SETUP, opt, m, NULL, 1);
1942 if (choice==allow_opt)
1944 network_set_power();
1947 Netgame.Allow_marker_view=m[extraopts_opt].value;
1948 Netgame.AlwaysLighting=m[extraopts_opt+1].value;
1950 if (m[mode_opt].value)
1951 Game_mode &= ~(GM_MULTI_COOP | GM_MULTI_ROBOTS);
1954 nm_messagebox(NULL, 1, TXT_OK, TXT_ONLY_ANARCHY);
1958 else if (anarchy_only) {
1959 if (m[mode_opt+3].value)
1960 Game_mode |= (GM_CAPTURE);
1963 nm_messagebox(NULL, 1, TXT_OK, TXT_ANARCHY_ONLY_MISSION);
1967 else if (m[mode_opt+1].value)
1969 Game_mode &= ~GM_MULTI_COOP;
1970 Game_mode |= GM_MULTI_ROBOTS;
1972 else if (m[mode_opt+2].value)
1973 Game_mode |= (GM_MULTI_COOP | GM_MULTI_ROBOTS);
1974 else if (m[mode_opt+3].value)
1975 Game_mode |= (GM_CAPTURE);
1978 // if (m[options_opt].value)
1979 // Netgame.game_flags |= NETGAME_FLAG_SHOW_ID;
1980 if (m[options_opt].value)
1981 Netgame.game_flags |= NETGAME_FLAG_SHOW_MAP;
1982 if (!strnicmp(level, "s", 1))
1983 start_level_num = -atoi(level+1);
1986 start_level_num = atoi(level);
1988 if ((start_level_num < 1) || (start_level_num > Last_level))
1990 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_LEVEL_OUT_RANGE);
1991 sprintf(level, "1");
1995 Difficulty_level = m[diff_opt].value;
1997 return(1); // Go for game!
1999 return 0; // No game
2005 // Ask the other player if its OK to start now
2010 com_send_choice(SELECTION_STARTGAME);
2012 m[0].type = NM_TYPE_TEXT; m[0].text = TXT_ESC_ABORT;
2014 choice = newmenu_do(NULL, TXT_WAIT_FOR_OK, 1, m, com_menu_poll);
2018 com_send_choice(SELECTION_STARTGAME_ABORT);
2023 if (other_menu_choice == SELECTION_YES_START)
2025 else if (other_menu_choice == SELECTION_STARTGAME)
2027 com_send_choice(SELECTION_YES_START);
2040 // Start a serial game after being linked
2042 mprintf((0, "Entered com_start_game\n"));
2046 if (! ( (Game_mode & GM_MODEM) || (Game_mode & GM_SERIAL) ) )
2049 Assert (master != -1);
2051 if (other_menu_choice != SELECTION_STARTGAME)
2053 if (!com_ask_to_start())
2057 if (master == 1) // Master chooses options
2059 if (com_start_game_menu())
2062 change_playernum_to(0);
2063 my_sync.level_num = start_level_num;
2064 my_sync.difficulty = Difficulty_level;
2065 my_sync.game_mode = Game_mode;
2066 if (Game_mode & GM_CAPTURE)
2067 my_sync.game_mode |=GM_UNKNOWN;
2068 memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
2069 my_sync.sync_time = control_invul_time;
2070 my_sync.game_flags = Netgame.game_flags;
2071 Netgame.control_invul_time = control_invul_time;
2072 memcpy ((&my_sync.game_flags)+1,(&Netgame.team_vector)+1,4);
2075 if (Game_mode & GM_CAPTURE)
2076 my_sync.game_mode &=GM_UNKNOWN;
2082 change_playernum_to(1);
2083 memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
2085 my_sync.level_num = 1;
2088 if (com_process_mode == COM_PROCESS_NORMAL)
2090 Difficulty_level = other_sync.difficulty;
2091 start_level_num = other_sync.level_num;
2092 Game_mode = other_sync.game_mode;
2093 if (Game_mode & GM_UNKNOWN) // Super HACK! Out of bit fields, doubling up
2095 Game_mode &=~GM_UNKNOWN;
2096 Game_mode |=GM_CAPTURE;
2099 memcpy ((&Netgame.team_vector)+1,(&other_sync.game_flags)+1,4);
2102 Netgame.game_flags = other_sync.game_flags;
2103 Netgame.control_invul_time = other_sync.sync_time;
2104 if (!load_mission_by_name(other_sync.mission_name))
2106 mprintf((0, "Mission not found: %s!\n", other_sync.mission_name));
2107 nm_messagebox(NULL, 1, TXT_OK, TXT_MISSION_NOT_FOUND);
2108 my_sync.sync_id = start_level_num;
2109 serial_sync_abort(0);
2115 if (com_process_mode != COM_PROCESS_NORMAL)
2118 memcpy(Players[OtherPlayer].callsign, other_sync.callsign, CALLSIGN_LEN+1);
2119 Function_mode = FMODE_GAME;
2120 Game_mode &= ~GM_GAME_OVER;
2122 init_player_stats_game();
2123 init_player_stats_level(0);
2124 // Assert(start_level_num > 0);
2125 Assert((start_level_num >= Last_secret_level) && (start_level_num <= Last_level));
2126 StartNewLevel(start_level_num, 0);
2132 // Syncronization functions
2136 serial_sync_abort(int val)
2138 // Send "I got Sync but it was no good!" packet
2140 sendbuf[0] = (char)MULTI_END_SYNC;
2141 sendbuf[1] = Player_num;
2142 sendbuf[2] = (char)val; // Indicated failure
2144 sendbuf[3] = my_sync.sync_id;
2145 com_send_data(sendbuf, 4, 1);
2147 // com_send_data(sendbuf, 3, 1);
2152 com_level_sync(void)
2154 // Send between-level sync stuff
2156 mprintf((0, "entered com_level_sync()\n"));
2158 Function_mode = FMODE_MENU; // Prevent the game loop from running during the menus!
2160 // At this point, the new level is loaded but the extra objects or players have not
2163 my_sync.level_num = Current_level_num;
2164 my_sync.seg_checksum = netmisc_calc_checksum(Segments, (Highest_segment_index+1) * sizeof(segment));
2165 my_sync.kills[0] = kill_matrix[Player_num][0];
2166 my_sync.kills[1] = kill_matrix[Player_num][1];
2167 my_sync.proto_version = MULTI_PROTO_VERSION;
2169 my_sync.killed = Players[Player_num].net_killed_total;
2173 if (Game_mode & GM_MULTI_COOP)
2174 my_sync.difficulty = Player_num;
2176 my_sync.difficulty = rand()%MAX_NUM_NET_PLAYERS; // My starting position
2178 if (com_sync(Current_level_num))
2180 com_process_mode = COM_PROCESS_MENU;
2181 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NEGOTIATION_FAIL );
2182 longjmp(LeaveGame, 0);
2185 if (my_sync.level_num != other_sync.level_num)
2188 nm_messagebox(TXT_ERROR, 1, TXT_OK, "%s %d\n%s %d", TXT_FATAL_ERROR_LEVEL, my_sync.level_num, TXT_OTHER_LEVEL, other_sync.level_num);
2189 longjmp(LeaveGame, 0);
2192 if (my_sync.seg_checksum != other_sync.seg_checksum)
2195 mprintf((1, "My check %d, other check %d.\n", my_sync.seg_checksum, other_sync.seg_checksum));
2196 nm_messagebox(TXT_ERROR, 1, TXT_OK, "%s %d %s %s%s", TXT_YOUR_LEVEL, my_sync.level_num, TXT_LVL_NO_MATCH, other_sync.callsign, TXT_CHECK_VERSION);
2197 longjmp(LeaveGame, 0);
2200 if (my_sync.proto_version != other_sync.proto_version)
2203 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_DESCENT_NO_MATCH);
2204 longjmp(LeaveGame, 0);
2207 mprintf((0, "My pos = %d, other pos = %d.\n", my_sync.difficulty, other_sync.difficulty));
2209 if ((other_sync.difficulty == my_sync.difficulty) && !master)
2211 // If we chose the same position and I am the slave, choose another
2212 my_sync.difficulty = (my_sync.difficulty+1) % MAX_NUM_NET_PLAYERS;
2215 Objects[Players[OtherPlayer].objnum].pos = Player_init[other_sync.difficulty].pos;
2216 Objects[Players[OtherPlayer].objnum].orient = Player_init[other_sync.difficulty].orient;
2217 obj_relink(Players[OtherPlayer].objnum,Player_init[other_sync.difficulty].segnum);
2218 Objects[Players[OtherPlayer].objnum].type = OBJ_PLAYER;
2220 Objects[Players[Player_num].objnum].pos = Player_init[my_sync.difficulty].pos;
2221 Objects[Players[Player_num].objnum].orient = Player_init[my_sync.difficulty].orient;
2222 obj_relink(Players[Player_num].objnum, Player_init[my_sync.difficulty].segnum);
2223 Objects[Players[Player_num].objnum].type = OBJ_PLAYER;
2225 SerialLastMessage = GameTime;
2227 kill_matrix[OtherPlayer][0] = other_sync.kills[0];
2228 kill_matrix[OtherPlayer][1] = other_sync.kills[1];
2229 Players[Player_num].net_kills_total = kill_matrix[Player_num][OtherPlayer] - kill_matrix[Player_num][Player_num];
2230 Players[OtherPlayer].net_kills_total = kill_matrix[OtherPlayer][Player_num] - kill_matrix[OtherPlayer][OtherPlayer];
2231 // Players[Player_num].net_killed_total = kill_matrix[0][Player_num] + kill_matrix[1][Player_num];
2232 // Players[OtherPlayer].net_killed_total = kill_matrix[0][OtherPlayer] + kill_matrix[1][OtherPlayer];
2233 Players[OtherPlayer].net_killed_total = other_sync.killed;
2234 Players[OtherPlayer].connected = Players[Player_num].connected = 1;
2236 Assert(N_players == 2);
2237 Assert(Player_num != OtherPlayer);
2239 gr_palette_fade_out(gr_palette, 32, 0);
2241 Function_mode = FMODE_GAME;
2242 com_process_mode = COM_PROCESS_NORMAL;
2243 multi_sort_kill_list();
2249 com_send_end_sync(void)
2251 // Send "I got Sync" packet
2253 sendbuf[0] = (char)MULTI_END_SYNC;
2254 sendbuf[1] = Player_num;
2255 sendbuf[2] = 1; // Indicates success
2257 sendbuf[3] = my_sync.sync_id;
2258 com_send_data(sendbuf, 4, 2);
2260 // com_send_data(sendbuf, 3, 2);
2265 com_send_begin_sync(void)
2267 mprintf((0, "Sending my sync.\n"));
2268 com_send_data((char *)&my_sync, sizeof(com_sync_pack)-3, 1);
2272 com_process_end_sync(byte *buf)
2274 // Process incoming end-sync packet
2277 com_process_mode = COM_PROCESS_MENU;
2282 if (buf[3] == my_sync.sync_id)
2288 com_process_sync(char *buf, int len)
2290 mprintf ((0,"Processing sync!"));
2295 case MULTI_END_SYNC:
2297 com_process_end_sync(buf);
2300 case MULTI_BEGIN_SYNC:
2302 mprintf((0, "Incoming begin_sync message.\n"));
2306 memcpy(&other_sync, buf, sizeof(com_sync_pack)-3);
2308 if (other_sync.sync_id != my_sync.sync_id)
2310 mprintf((0, "Other sync invalid id, %d != %d.\n", other_sync.sync_id, my_sync.sync_id));
2315 mprintf((0, "got other sync size %d.\n", sizeof(com_sync_pack)-3));
2317 com_send_end_sync();
2323 if (got_sync && other_got_sync)
2326 mprintf((1, "Starting game.\n"));
2329 com_process_mode = COM_PROCESS_NORMAL;
2336 // Send sync information, depending on the situation
2338 if (!other_got_sync)
2340 com_send_begin_sync();
2344 com_send_end_sync();
2349 void com_sync_poll(int nitems, newmenu_item *menus, int *key, int citem)
2363 if (timer_get_approx_seconds() > t1+F1_0)
2366 t1 = timer_get_approx_seconds();
2369 Assert(com_process_mode == COM_PROCESS_SYNC);
2371 com_process_input();
2373 if (com_process_mode == COM_PROCESS_NORMAL)
2377 mprintf((0, "Sync finished.\n"));
2380 if (com_process_mode == COM_PROCESS_MENU)
2383 mprintf((0, "Sync denied by other side.\n"));
2392 // How to handle the end of the level and start of the next level
2393 // returns 0 for success or 1 for failure
2399 mprintf((0, "Entered com_sync\n"));
2401 //@@gr_set_current_canvas(NULL);
2402 //@@pcx_error = pcx_read_bitmap(Menu_pcx_name, &grd_curcanv->cv_bitmap,grd_curcanv->cv_bitmap.bm_type,NULL);
2403 //@@Assert(pcx_error == PCX_ERROR_NONE);
2405 com_process_mode = COM_PROCESS_SYNC;
2406 got_sync = other_got_sync = 0;
2412 my_sync.sync_id = id;
2415 m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT;
2416 m[1].type = m[2].type = NM_TYPE_MENU;
2417 m[1].text = TXT_YES;
2421 choice = newmenu_do(NULL, TXT_WAIT_OPPONENT, 1, m, com_sync_poll);
2425 choice = newmenu_do1(NULL, TXT_SURE_ABORT_SYNC, 2, m+1, com_sync_poll, 1);
2432 if ((choice == -1) || (choice == -3)) {
2435 else if (choice != -2)
2442 com_endlevel(int *secret)
2444 // What do we do between levels?
2446 Function_mode = FMODE_MENU;
2448 gr_palette_fade_out(gr_palette, 32, 0);
2450 my_sync.level_num = multi_goto_secret;
2454 com_process_mode = COM_PROCESS_MENU;
2455 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NEGOTIATION_FAIL);
2456 longjmp(LeaveGame, 0);
2459 com_process_mode = COM_PROCESS_ENDLEVEL;
2461 if ((multi_goto_secret == 1) || (other_sync.level_num == 1))
2466 multi_goto_secret = 0;
2474 int ReadModemList ()
2476 int num=0,i=0,namemode=1;
2480 if ((MyFile=fopen ("modem.lst","rb"))==NULL)
2483 ModemNames[num][0] = ModemStrings[num][0] = 0;
2485 while (!feof(MyFile))
2489 mprintf ((0,"%c",c));
2500 ModemStrings[num][i]=0;
2503 if (strlen(ModemNames[num]) && strlen(ModemStrings[num]))
2505 if (num >= MAX_MODEMS)
2506 break; //too many! abort!
2507 ModemNames[num][0] = ModemStrings[num][0] = 0;
2511 while (fgetc(MyFile)!=10)
2516 else if (c=='=' && namemode)
2518 ModemNames[num][i]=0;
2526 if (i<MODEM_NAME_LEN)
2527 ModemNames[num][i++]=c;
2531 if (i<INIT_STRING_LEN)
2532 ModemStrings[num][i++]=c;
2537 ModemStrings[num][i]=0; //terminate in case no final newline
2543 void com_choose_string ()
2546 newmenu_item m[MAX_MODEMS];
2548 if ((num=ReadModemList())==0)
2550 nm_messagebox ("Error",1,TXT_OK,"No valid modem file found!");
2554 mprintf ((0,"Number of strings=%d\n",num));
2559 m[i].type = NM_TYPE_MENU; m[i].text = &ModemNames[i];
2560 mprintf ((0,"Text(%d)=%s\n",i,ModemNames[i]));
2562 choice = newmenu_do(NULL, "Choose a modem", num, m, NULL);
2567 strcpy (ModemInitString,ModemStrings[choice]);