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.
18 #pragma off (unreferenced)
19 static char rcsid[] = "$Id: modem.c,v 1.1.1.1 2001-01-19 03:30:07 bradleyb Exp $";
20 #pragma on (unreferenced)
30 #include "pa_enabl.h" //$$POLY_ACC
31 #include "fast.h" // Commlib stuff
66 #define MIN_COMM_GAP 8000
67 #define INIT_STRING_LEN 20
68 #define LEN_PHONE_NUM_OLD 15
69 #define LEN_PHONE_NUM 30
70 #define LEN_PHONE_NAME 12
71 #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
88 int default_base[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
89 int default_irq[4] = { 4, 3, 4, 3 };
91 // Code to support modem/null-modem play
93 // Program determined variables for serial play
95 typedef struct com_sync_pack {
102 char callsign[CALLSIGN_LEN+1];
106 char mission_name[9];
115 short DoAfterburner:1;
116 short DoInvulnerability:1;
122 short DoSuperLaser:1;
128 short DoEarthShaker:1;
130 short Allow_marker_view:1;
131 short RefusePlayers:1;
132 short AlwaysLighting:1;
137 char dummy[3]; // Extra space for checksum & sequence number
142 int com_baud_rate = 0;
143 //--unused-- int sync_time = 0;
146 int other_got_sync = 0;
148 long com_type = -1; /* What type of UART is available */
150 static ubyte rx_seqnum = 0xff;
151 static ubyte tx_seqnum = 0;
152 int OtherPlayer; // Player num for modem opponent
153 int com_process_mode = COM_PROCESS_NORMAL;
154 int master = -1; // Am I the master or is the other guy the master?
155 com_sync_pack my_sync, other_sync;
158 int com_custom_port = -1;
159 int com_custom_irq = -1;
160 int com_custom_base = -1;
162 int other_menu_choice = 0;
168 static char syncbuffer[MAX_MULTI_MESSAGE_LEN+4];
169 static char sendbuf[MAX_MULTI_MESSAGE_LEN+4]; // +4 because of +1 for null and +3 for checksum/sequence
171 // Serial setup variables
173 int com_port_num = -1;
175 char modem_init_string[INIT_STRING_LEN+1];
176 char phone_num[NUM_PHONE_NUM+1][LEN_PHONE_NUM+1];
177 char phone_name[NUM_PHONE_NUM][LEN_PHONE_NAME+1];
179 #define MAX_MODEMS 200
180 #define MODEM_NAME_LEN 22
182 char ModemNames[MAX_MODEMS][MODEM_NAME_LEN+1],ModemStrings[MAX_MODEMS][INIT_STRING_LEN+1]; // for init strings
183 char ModemInitString[INIT_STRING_LEN+1];
186 fix SerialLastMessage = 0;
188 /* Function prototypes for functions not exported through modem.h */
190 int GetModemList(void);
191 void com_param_setup(void);
192 void com_start_game(void);
193 void modem_dialout(void);
194 void modem_answer(void);
195 int com_sync(int id);
196 void com_sync_poll(int nitem, newmenu_item *menus, int *key, int citem);
199 #define codex(name_start, name_end) \
200 void name_start(void) \
207 for (i=0; i<123; i++) \
222 void name_end(void) \
226 #define codex(name_start, name_end)
229 codex(code_01s, code_01e)
231 int detect_UART(unsigned baseaddr, int * loc, int * code )
233 // this function returns 0 if no UART is installed.
234 // 1: 8250, 2: 16450 or 8250 with scratch reg., 3: 16550, 4: 16550A
239 // check if a UART is present. This is code John hacked by looking at the return
240 // values from peoples computers.
241 olddata=inp(baseaddr+4);
242 outp(baseaddr+4,0x1f); // Enable Loopback mode, sets RTS & DTR to 1.
245 temp = inp(baseaddr+6); // Read the state of RTS and DTR.
246 temp = inp(baseaddr+6); // Do this twice, so that lower 4 bits are clear. OS/2 returns 0xB0 after this,
247 // instead of 0xff if no port is there.
249 if ((temp&0x3f)!=0x30) {
250 *loc = 1; *code = temp;
253 outp(baseaddr+4,olddata); // Restore RTS & DTR
255 // next thing to do is look for the scratch register
256 olddata=inp(baseaddr+7);
257 outp(baseaddr+7,0x55);
259 if (inp(baseaddr+7)!=0x55) return 1;
260 outp(baseaddr+7,0xAA);
262 if (inp(baseaddr+7)!=0xAA) return 1;
263 outp(baseaddr+7,olddata); // we don't need to restore it if it's not there
265 // then check if there's a FIFO
269 // some old-fashioned software relies on this!
270 outp(baseaddr+2,0x0);
272 if ((x&0x80)==0) return 2;
273 if ((x&0x40)==0) return 3;
277 codex(code_02s, code_02e)
286 long port_addr[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
292 if ( (com_port_num != 0) && (com_port_num != 1) && (com_port_num != 2) && (com_port_num != 3) )
295 return -1; // Error, set com_port_num before calling this!
298 if (com_port_num == com_custom_port)
299 portaddr = com_custom_base;
301 portaddr = port_addr[com_port_num];
303 mprintf((0, "com port %x.\n", portaddr));
305 switch( detect_UART(portaddr, &loc, &code) ) {
307 mprintf((0, "No UART detected. (LOC:%d, CODE:0x%x)\n", loc, code));
310 mprintf((0, "UART type is 8250.\n" ));
312 case 2: // 16450 or 8250 with scratch reg.
313 mprintf((0, "UART is type 16450, or an 8250 with scratch register.\n" ));
316 mprintf((0, "UART is type 16550, with FIFO bug.\n" ));
317 return(16450); // 16550's FIFOs don't work. This is not a typo. (detected 16550, returned 16450 )
318 case 4: // 16550A, which is the only UART the FIFO mode works with.
319 mprintf((0, "UART is type 16550A, no FIFO bug.\n" ));
325 #if !defined(NDEBUG) && !defined(NMONO)
327 com_dump_string(char *string)
329 mprintf((0, "%s\n", string));
332 #define com_dump_string()
335 codex(code_03s, code_03e)
340 // Detect and enable the COM port selected by the user
350 // com_port_num and com_speed should be set before calling this func
351 com_type = com_type_detect();
353 if (com_custom_port == com_port_num)
354 rc = Change8259Priority(com_custom_irq);
355 else if ((com_port_num == COM2) || (com_port_num == COM4))
356 rc = Change8259Priority( IRQ3 );
358 rc = Change8259Priority( IRQ4 );
362 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_SERIAL_OPEN_ERROR);
366 // If our detection is wrong, we want them to be able to go ahead and play anyway.
367 // if (com_type == -1)
369 // nm_messagebox(TXT_ERROR, 1, TXT_OK, "Error 2\n%s", TXT_SERIAL_OPEN_ERROR);
373 if (com_port_num == com_custom_port )
375 rc = FastSetPortHardware(com_port_num, com_custom_irq, com_custom_base);
378 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_SERIAL_OPEN_ERROR);
383 if (com_type == 16550)
385 FastSet16550TriggerLevel( TRIGGER_04 );
386 FastSet16550UseTXFifos( ON );
389 FastSavePortParameters(com_port_num);
391 com_port = PortOpenGreenleafFast(com_port_num, com_speed, 'N', 8, 1);
395 int curr_irq, curr_base;
396 FastGetPortHardware(com_port_num, &curr_irq, &curr_base);
397 mprintf((0, "Current port settings: base %x, irq %x.\n", curr_base, curr_irq ));
401 if ((com_port == 0) || (com_port->status != 0))
403 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_SERIAL_OPEN_ERROR);
408 mprintf((0, "Com port opened. Status = %d\n", com_port->status));
411 SetDtr(com_port, ON);
413 if ( FindArg( "-ctsrts" ) || FindArg( "-rtscts" ) )
414 UseRtsCts(com_port, ON); // Now used for null-modem as well, helps IHHD!
416 UseRtsCts(com_port, OFF);
422 // DumpPortStatus(com_port, com_dump_string);
430 // close the com port and free associated structures.
437 // SetDtr(com_port, OFF);
438 // UseRtsCts(com_port, OFF);
440 rc = PortClose(com_port);
442 FastRestorePortParameters(com_port_num);
444 if (com_custom_port == com_port_num)
446 // Custom settings were in effect, roll them back
447 rc = FastSetPortHardware(com_port_num, default_irq[com_port_num], default_base[com_port_num]);
450 if (rc != ASSUCCESS) {
452 mprintf((1, "PortClose returned %d!\n", rc));
462 mprintf((0, "Com port released.\n"));
466 codex(code_04s, code_04e)
471 // this is the safest way to get out of some modem/serial negotiation
472 // and back to the main menu. Use this whenever this have gone too far
479 change_playernum_to(0);
481 Viewer = ConsoleObject = &Objects[0];
482 Game_mode = GM_GAME_OVER; // Force main menu selection
488 // Close the serial link
490 com_send_choice(SELECTION_CLOSE_LINK);
495 com_carrier_lost(void)
497 // Carrier lost, inform and abort
499 if (multi_in_menu > 0)
501 multi_leave_menu = 1;
505 Function_mode = FMODE_MENU;
508 nm_messagebox(NULL, 1, TXT_OK, TXT_CARRIER_LOST);
512 codex(code_05s, code_05e)
514 extern void SetAllAllowablesTo (int on);
515 extern ubyte Cockpit_mode_save; // From object.c
516 extern void network_set_power (void);
522 // Reset various parameters before starting a new game
526 for (i = 0; i < N_players; i++)
528 Players[i].connected = 1;
533 multi_new_game(); // Reset kill list, among other things
534 Control_center_destroyed = 0;
535 Endlevel_sequence = 0;
537 // Yes, this really IS as ugly as it gets, kids...
539 if (Cockpit_mode == CM_LETTERBOX || Cockpit_mode == CM_REAR_VIEW)
541 select_cockpit(Cockpit_mode_save);
545 codex(code_06s, code_06e)
548 com_save_settings(void)
553 if ( (settings = fopen("serial.cfg", "wb")) == NULL)
556 if (fwrite(&com_speed, sizeof(int), 1, settings) != 1)
559 if (fwrite(&com_port_num, sizeof(int), 1, settings) != 1)
562 if (fwrite(modem_init_string, 1, INIT_STRING_LEN+1, settings) != INIT_STRING_LEN+1)
565 for (i = 0; i < NUM_PHONE_NUM; i++)
567 if (fwrite(phone_num[i], 1, LEN_PHONE_NUM+1, settings) != LEN_PHONE_NUM+1)
569 if (fwrite(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1)
573 if (fwrite(&com_custom_port, sizeof(int), 1, settings) != 1)
575 if (fwrite(&com_custom_irq, sizeof(int), 1, settings) != 1)
577 if (fwrite(&com_custom_base, sizeof(int), 1, settings) != 1)
585 nm_messagebox(NULL, 1, TXT_OK, TXT_ERROR_SERIAL_CFG);
589 unlink("serial.cfg");
595 codex(code_07s, code_07e)
598 com_load_settings(void)
603 if ((settings = fopen("serial.cfg", "rb")) == NULL)
606 cfg_size = filelength(fileno(settings));
608 // Read the data from the file
610 if (fread(&com_speed, sizeof(int), 1, settings) != 1)
612 if (! ((com_speed == 9600) || (com_speed == 19200) || (com_speed == 38400)) )
615 if (fread(&com_port_num, sizeof(int), 1, settings) != 1)
617 if ( (com_port_num < COM1) || (com_port_num > COM4) )
620 if (fread(modem_init_string, 1, INIT_STRING_LEN+1, settings) != INIT_STRING_LEN+1)
622 modem_init_string[INIT_STRING_LEN] = '\0';
624 if (cfg_size <= 273 ) { // Old 15 char LEN_PHONE_NUM's
625 mprintf(( 1, "Reading old pre 1.1 phone.cfg\n" ));
626 for (i = 0; i < NUM_PHONE_NUM; i++)
628 if (fread(phone_num[i], 1, LEN_PHONE_NUM_OLD+1, settings) != LEN_PHONE_NUM_OLD+1)
630 phone_num[i][LEN_PHONE_NUM_OLD] = '\0';
631 if (fread(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1)
633 phone_name[i][LEN_PHONE_NAME] = '\0';
635 } else { // Normal Phone nums
636 for (i = 0; i < NUM_PHONE_NUM; i++)
638 if (fread(phone_num[i], 1, LEN_PHONE_NUM+1, settings) != LEN_PHONE_NUM+1)
640 phone_num[i][LEN_PHONE_NUM] = '\0';
641 if (fread(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1)
643 phone_name[i][LEN_PHONE_NAME] = '\0';
647 if (fread(&com_custom_port, sizeof(int), 1, settings) != 1) {
648 mprintf((0, "Reading old file format for serial.cfg.\n"));
652 if ( (com_custom_port < -1) || (com_custom_port > COM4) )
655 if (fread(&com_custom_irq, sizeof(int), 1, settings) != 1)
657 if ( (com_custom_port < -1) || (com_custom_port > IRQ15) )
660 if (fread(&com_custom_base, sizeof(int), 1, settings) != 1)
662 if (com_custom_base < -1)
665 //Everything was A-Ok!
672 nm_messagebox(NULL, 1, TXT_OK, TXT_ERR_SER_SETTINGS);
675 // Return some defaults
676 com_speed = 19200; // UART speed
677 strcpy(modem_init_string, "ATZ");
679 com_custom_port = -1;
680 for (i = 0; i < NUM_PHONE_NUM; i++)
682 phone_num[i][0] = '\0';
683 strcpy(phone_name[i], TXT_EMPTY);
693 serial_leave_game(void)
696 mprintf((0, "Called serial_leave_game.\n"));
699 serial_sync_abort(0); // Just in case the other guy is in sync mode
700 Game_mode |= GM_GAME_OVER;
701 Function_mode = FMODE_MENU;
704 codex(code_08s, code_08e)
707 com_send_data(char *ptr, int len, int repeat)
711 // Take the raw packet data specified by ptr and append the sequence
712 // number and checksum, and pass it to com_send_ptr
717 if (Game_mode & GM_MODEM)
721 mprintf((0, "CARRIER LOST!\n"));
724 len += 3; // Checksum data is 3 bytes
726 *(ubyte *)(ptr+(len-3)) = (tx_seqnum+1)%256;
727 tx_seqnum = (tx_seqnum+1)%256;
729 *(ushort *)(ptr+(len-2)) = netmisc_calc_checksum(ptr, len-2);
731 com_send_ptr(ptr, len);
733 for (i = 0; i < repeat; i++)
734 com_send_ptr(ptr, len);
737 com_send_ptr(char *ptr, int len)
742 for (count = 0, dat=ptr[0]; count < len; dat=ptr[++count])
744 WriteChar(com_port, dat);
746 WriteChar(com_port, EOR_MARK); // double in-band endmarkers
748 WriteChar(com_port, EOR_MARK); // EOR
749 WriteChar(com_port, 0); // EOR
753 codex(code_09s, code_09e)
758 // Get rid of all waiting data in the serial buffer
765 mprintf((0, "COM FLUSH:"));
767 while (ReadCharTimed(com_port, 100) >= 0)
769 mprintf((0, "%d characters.\n", i));
776 static int eor_recv = 0;
779 // -1 = Nothing in buffer
780 // -2 = End of record
785 i = ReadChar(com_port);
787 // mprintf((0, "%c", i));
789 if (i == ASBUFREMPTY)
792 if ((i == EOR_MARK) || eor_recv)
795 i = ReadChar(com_port);
797 if (i == ASBUFREMPTY)
799 // Assert(eor_recv == 0);
803 else if (i == EOR_MARK)
806 return(EOR_MARK); // Doubled EOR returns the character
812 mprintf((0, "EOR followed by unexpected value %d.\n", i));
822 #define SERIAL_IDLE_TIMEOUT F1_0*10
824 codex(code_10s, code_10e)
829 static fix last_comm_time = 0;
830 static int last_pos_skipped = 0;
833 if (Endlevel_sequence || (com_process_mode==COM_PROCESS_ENDLEVEL)) { // Only recieve during endlevel
834 int old_Endlevel_sequence = Endlevel_sequence;
835 Endlevel_sequence = 1;
837 Endlevel_sequence = old_Endlevel_sequence;
841 last_comm_time += FrameTime;
843 if ((last_comm_time > MIN_COMM_GAP) || Network_laser_fired)
846 if ((Game_mode & GM_MULTI_ROBOTS) && !last_pos_skipped) {
847 rval = multi_send_robot_frame(0);
849 if (rval && !Network_laser_fired)
851 last_pos_skipped = 1;
855 last_pos_skipped = 0;
856 multi_send_position(Players[Player_num].objnum);
857 multi_send_fire(); // Will return w/o sending if we haven't fired
860 // mprintf((0, "%d chars sent, %f cps.\n", chars_sent, f2fl(fixdiv((chars_sent*F1_0),last_comm_time)) ));
865 // multi_send_robot_frame(1);
869 if (!Control_center_destroyed && (Function_mode == FMODE_GAME) && (SerialLastMessage+SERIAL_IDLE_TIMEOUT < GameTime))
871 SerialLastMessage = 0x7fffffff-SERIAL_IDLE_TIMEOUT; // Give no further warnings until next message arrives!
872 nm_messagebox(TXT_WARNING, 1, TXT_OK, TXT_CONNECT_LOST, Players[OtherPlayer].callsign);
879 com_check_message(char *checkbuf, int len)
884 //mprintf ((0,"TYPE=%d!\n",checkbuf[0]));
888 mprintf((0, "message type %d too short to be a real message!\n", checkbuf[0]));
892 if (checkbuf[0] > MULTI_MAX_TYPE)
894 mprintf((0, "message type %d out of range.\n", checkbuf[0]));
898 if ( (len-3) != message_length[checkbuf[0]])
900 mprintf((0, "id:%d message length %d != %d.\n",checkbuf[0], len-3, message_length[checkbuf[0]]));
904 check = netmisc_calc_checksum(checkbuf, len-2);
905 if (check != *(ushort *)(checkbuf+(len-2)))
908 mprintf((0, "error in message type %d, length %d, checksum %d != %d\n", checkbuf[0], len, check, *(ushort *)(checkbuf+(len-2))));
913 seqnum = checkbuf[(len-3)];
915 if (seqnum == rx_seqnum)
917 mprintf ((0,"returning due to bad checksum! type=%d seq=%d rx=%d\n",checkbuf[0],seqnum,rx_seqnum));
921 if (seqnum != (rx_seqnum+1)%256)
924 mprintf((0, "Warning, missed 1 or more messages.\n"));
928 //mprintf((0, "message type %d len %d OK!\n", checkbuf[0], len));
932 mprintf((1,"Line status: %d.\n", GetLineStatus(com_port)));
933 ClearLineStatus(com_port);
937 codex(code_11s, code_11e)
940 com_process_menu(char *buf, int len)
946 mprintf((0, "com_process_menu: type %d.\n", buf[0]));
952 sprintf(text, "%s %s\n'%s'", Players[OtherPlayer].callsign, TXT_SAYS, buf+2);
954 // sprintf(text, "%s %s\n'%s'", Players[OtherPlayer].callsign, TXT_SAYS, buf+3);
956 nm_messagebox(NULL, 1, TXT_OK, text);
958 case MULTI_MENU_CHOICE:
959 other_menu_choice = buf[1];
960 mprintf((0, "Other menu choice = %d.\n", other_menu_choice));
962 case MULTI_BEGIN_SYNC:
963 // If we get a sync at the menu, send an abort sync, we're not ready yet!
964 serial_sync_abort(0);
970 com_process_input(void)
972 // Read all complete messages from the serial buffer and process
973 // the contents. Messages are read into global array snycbuffer.
976 int entry_com_mode = com_process_mode;
983 if (Game_mode & GM_MODEM)
985 if (!GetCd(com_port))
990 if (!multi_in_menu) {
994 multi_leave_menu = 1;
998 if (com_process_mode != entry_com_mode)
1000 mprintf((0, "Exiting com_process_input due to mode switch.\n"));
1004 while ( (len <= MAX_MULTI_MESSAGE_LEN) && (dat = com_getchar()) > -1) // Returns -1 when serial pipe empty
1006 syncbuffer[len++] = dat;
1009 if ((dat == -2) || (len > MAX_MULTI_MESSAGE_LEN)) // Returns -2 when end of message reached
1012 SerialLastMessage = GameTime;
1014 if (!com_check_message(syncbuffer, len))
1016 switch(com_process_mode)
1018 case COM_PROCESS_NORMAL:
1019 case COM_PROCESS_ENDLEVEL:
1020 multi_process_data(syncbuffer, len); break;
1021 case COM_PROCESS_MENU:
1022 if (!Endlevel_sequence) com_process_menu(syncbuffer, len); break;
1023 case COM_PROCESS_SYNC:
1024 if (!Endlevel_sequence) com_process_sync(syncbuffer, len); break;
1026 Int3(); // Bad com_process_mode switch set!
1029 else mprintf ((0,"Huh?\n"));
1034 if (dat == -3) // Returns -3 when carrier lost
1047 my_sync.type = MULTI_BEGIN_SYNC;
1048 my_sync.difficulty = 0;
1049 memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
1050 my_sync.seg_checksum = 0;
1051 my_sync.game_mode = Game_mode;
1052 my_sync.level_num = 0;
1055 mprintf((0, "com_connect()\n"));
1059 return(-1); // Failure in sync
1064 mprintf((0, "My rand = %d, other rand = %d.\n", my_sync.sync_time, other_sync.sync_time));
1067 // Figure out who is the master
1068 if (my_sync.sync_time > other_sync.sync_time)
1070 mprintf((0, "Swtiching player to master.\n"));
1072 change_playernum_to(0);
1074 else if (my_sync.sync_time < other_sync.sync_time)
1076 mprintf((0, "Switching player to slave.\n"));
1078 change_playernum_to(1);
1081 return(-1); // Didn't sync properly, try again
1084 // Copy the remote sync data into local variables
1086 OtherPlayer = (Player_num+1)%2;
1087 mprintf((0, "Other player is #%d.\n", OtherPlayer));
1088 memcpy(Players[OtherPlayer].callsign, other_sync.callsign, CALLSIGN_LEN+1);
1093 #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)
1095 #define MENU_MODEM_CALL 0
1096 #define MENU_MODEM_ANSWER 1
1097 #define MENU_SERIAL_LINK_START 2
1098 #define MENU_SERIAL_SETUP 3
1099 #define MENU_MODEM_HANGUP 4
1100 #define MENU_SERIAL_GAME_START 5
1101 #define MENU_SEND_MESSAGE 6
1103 codex(code_12s, code_12e)
1106 com_menu_poll(int nitems, newmenu_item *menus, int *key, int citem)
1108 // Watch the serial stream if we are connected and take appropriate actions
1116 if (! ( (Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM) ) )
1119 com_process_mode = COM_PROCESS_MENU;
1120 old_game_mode = Game_mode;
1121 other_menu_choice = 0;
1123 com_process_input();
1125 if ((old_game_mode != Game_mode) || other_menu_choice || (com_process_mode != COM_PROCESS_MENU))
1127 if (multi_leave_menu)
1132 com_send_choice(int choice)
1134 sendbuf[0] = (char)MULTI_MENU_CHOICE;
1135 sendbuf[1] = (char)choice;
1137 com_send_data(sendbuf, 2, 1);
1141 com_ready_to_start(void)
1146 m[0].type = m[1].type = NM_TYPE_MENU;
1147 m[0].text = TXT_YES;
1150 choice = newmenu_do1(NULL, TXT_READY_DESCENT, 2, m, com_menu_poll, 0 );
1154 com_send_choice(SELECTION_YES_START);
1155 other_menu_choice = SELECTION_STARTGAME;
1160 com_send_choice(SELECTION_NO_START);
1165 com_process_other_menu_choice(void)
1167 if (other_menu_choice == SELECTION_STARTGAME)
1168 com_ready_to_start();
1169 else if (other_menu_choice == SELECTION_CLOSE_LINK)
1171 nm_messagebox(NULL, 1, TXT_OK, TXT_CLOSED_LINK);
1176 extern void nm_draw_background1(char * filename);
1178 #define SUBTITLE_LEN 120
1184 int menu_choice[10];
1185 int num_options = 0;
1188 char subtitle[SUBTITLE_LEN];
1191 if (com_port_num == -1)
1192 com_load_settings();
1196 //@@gr_set_current_canvas(NULL);
1197 //@@pcx_error = pcx_read_bitmap(Menu_pcx_name,&grd_curcanv->cv_bitmap,grd_curcanv->cv_bitmap.bm_type,NULL);
1198 //@@Assert(pcx_error == PCX_ERROR_NONE);
1200 nm_draw_background1(Menu_pcx_name);
1203 pa_update_clut(gr_palette, 0, 256, 0);
1206 com_process_mode = COM_PROCESS_MENU;
1211 if (! ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)) )
1213 // We haven't established any type of link
1214 ADD_ITEM(TXT_DIAL_MODEM, MENU_MODEM_CALL, KEY_D);
1215 ADD_ITEM(TXT_ANSWER_MODEM, MENU_MODEM_ANSWER, KEY_A);
1216 ADD_ITEM(TXT_NULL_MODEM, MENU_SERIAL_LINK_START, KEY_E);
1217 ADD_ITEM(TXT_COM_SETTINGS, MENU_SERIAL_SETUP, KEY_C);
1221 ADD_ITEM(TXT_START_GAME, MENU_SERIAL_GAME_START, KEY_S);
1222 ADD_ITEM(TXT_SEND_MESSAGEP, MENU_SEND_MESSAGE, KEY_S);
1224 if (Game_mode & GM_MODEM)
1225 ADD_ITEM(TXT_HANGUP_MODEM, MENU_MODEM_HANGUP, KEY_H);
1227 if (Game_mode & GM_SERIAL)
1228 ADD_ITEM(TXT_CLOSE_LINK, MENU_MODEM_HANGUP, KEY_C);
1230 sprintf(subtitle, "Serial/Modem Game\n\n");
1232 if (Game_mode & GM_SERIAL)
1233 sprintf(subtitle+strlen(subtitle), "%s %s\n%s", TXT_SERIAL, TXT_LINK_ACTIVE, Players[OtherPlayer].callsign);
1234 else if (Game_mode & GM_MODEM)
1235 sprintf(subtitle+strlen(subtitle), "%d %s %s %s\n%s", com_baud_rate, TXT_BAUD, TXT_MODEM, TXT_LINK_ACTIVE, Players[OtherPlayer].callsign);
1237 sprintf(subtitle+strlen(subtitle), TXT_NOT_CONNECTED);
1239 multi_leave_menu = 0;
1241 Assert(strlen(subtitle) < SUBTITLE_LEN);
1243 choice = newmenu_do1(NULL, subtitle, num_options, m, com_menu_poll, 0);
1245 mprintf((0, "main menu choice was %d.\n", choice));
1249 if (!((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)))
1254 m[0].text = TXT_YES; m[1].text = TXT_NO;
1255 m[0].type = m[1].type = NM_TYPE_MENU;
1257 choice = newmenu_do1(NULL, TXT_EXIT_WILL_CLOSE, 2, m, com_menu_poll, 0);
1260 com_send_choice(SELECTION_CLOSE_LINK);
1264 if ((choice == -2) && (other_menu_choice))
1265 com_process_other_menu_choice();
1272 // Menu poll loop caused a re-draw
1273 if (other_menu_choice == SELECTION_STARTGAME)
1274 com_ready_to_start();
1275 else if (other_menu_choice == SELECTION_CLOSE_LINK)
1277 nm_messagebox(NULL, 1, TXT_OK, TXT_CLOSED_LINK);
1281 if (Function_mode == FMODE_GAME)
1285 Game_mode = GM_GAME_OVER;
1292 old_game_mode=Game_mode;
1293 switch (menu_choice[choice])
1295 case MENU_SERIAL_SETUP:
1299 case MENU_SERIAL_GAME_START:
1301 if (Function_mode != FMODE_GAME)
1304 case MENU_MODEM_CALL:
1308 case MENU_MODEM_ANSWER:
1312 case MENU_SEND_MESSAGE:
1313 multi_send_message_dialog();
1314 if (Network_message_reciever != -1)
1315 multi_send_message();
1316 multi_sending_message = 0;
1319 case MENU_SERIAL_LINK_START:
1320 serial_link_start();
1323 case MENU_MODEM_HANGUP:
1334 codex(code_13s, code_13e)
1336 void com_custom_param_setup(void)
1338 // User menu for setting up custom IRQ/Base settings for a COM port
1346 int new_irq, new_base;
1347 int menu_save, menu_reset;
1350 sprintf(title, "%s%d", TXT_COM_CUSTOM_SETTINGS, com_port_num+1);
1352 if (com_port_num != com_custom_port)
1354 new_irq = default_irq[com_port_num];
1355 new_base = default_base[com_port_num];
1359 new_irq = com_custom_irq;
1360 new_base = com_custom_base;
1364 sprintf(base, "%x", new_base);
1365 sprintf(irq, "%d", new_irq);
1368 mm[loc].type = NM_TYPE_TEXT; mm[loc].text = TXT_COM_BASE; loc++;
1369 mm[loc].type = NM_TYPE_INPUT; mm[loc].text = base; mm[loc].text_len = 9; loc++;
1370 mm[loc].type = NM_TYPE_TEXT; mm[loc].text = TXT_COM_IRQ; loc++;
1371 mm[loc].type = NM_TYPE_INPUT; mm[loc].text = irq, mm[loc].text_len = 2; loc++;
1373 mm[loc].type = NM_TYPE_MENU; mm[loc].text = TXT_RESET_DEFAULTS; loc++;
1375 mm[loc].type = NM_TYPE_MENU; mm[loc].text = TXT_ACCEPT; loc++;
1377 mmn = newmenu_do1(NULL, title, loc, mm, NULL, menu_save);
1380 return; // All changes lost
1382 new_irq = strtol(irq, NULL, 0);
1383 new_base = strtol(base, NULL, 16);
1385 if (mmn == menu_reset)
1387 new_irq = default_irq[com_port_num];
1388 new_base = default_base[com_port_num];
1390 if (mmn == menu_save)
1392 if ((new_irq == default_irq[com_port_num]) && (new_base == default_base[com_port_num])) {
1393 com_custom_port = -1;
1394 mprintf((0, "Custom com settings not changed.\n"));
1397 if ((new_irq < IRQ2) || (new_irq > IRQ7)) {
1398 new_irq = default_irq[com_port_num];
1399 nm_messagebox(NULL, 1, TXT_OK, TXT_VALID_IRQS);
1403 com_custom_irq = new_irq;
1404 com_custom_base = new_base;
1405 com_custom_port = com_port_num;
1411 void com_param_setup_poll(int nitems, newmenu_item *menus, int *key, int citem)
1417 if ((com_custom_port == -1) && menus[4].value)
1419 menus[4].value = 0; menus[4].redraw = 1;
1423 if (com_custom_port == -1)
1426 if (menus[com_custom_port].value && !menus[4].value)
1428 menus[4].value = 1; menus[4].redraw = 1;
1430 else if (menus[4].value && !menus[com_custom_port].value)
1432 menus[4].value = 0; menus[4].redraw = 1;
1437 void com_param_setup(void)
1440 int was_enabled = 0;
1441 newmenu_item mm[20];
1443 int menu_baud, menu_custom, menu_save,menu_choose_string;
1446 strcpy (ModemInitString, modem_init_string);
1458 mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM1); mm[loc].text="COM1"; mm[loc].group=0; loc++;
1459 mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM2); mm[loc].text="COM2"; mm[loc].group=0; loc++;
1460 mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM3); mm[loc].text="COM3"; mm[loc].group=0; loc++;
1461 mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM4); mm[loc].text="COM4"; mm[loc].group=0; loc++;
1463 mm[loc].type=NM_TYPE_CHECK; mm[loc].value=(com_port_num == com_custom_port); mm[loc].text=TXT_COM_CUSTOM_SETTINGS; loc++;
1464 mm[loc].type=NM_TYPE_TEXT; mm[loc].text = TXT_BAUD_RATE; loc++;
1466 mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_speed == 9600); mm[loc].text="9600"; mm[loc].group=1; loc++;
1467 mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_speed == 19200); mm[loc].text="19200"; mm[loc].group=1; loc++;
1468 mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_speed == 38400); mm[loc].text="38400"; mm[loc].group=1; loc++;
1470 menu_choose_string=loc;
1471 mm[loc].type=NM_TYPE_MENU; mm[loc].text = "Choose modem init string"; loc++;
1473 mm[loc].type=NM_TYPE_TEXT; mm[loc].text = TXT_MODEM_INIT_STRING; loc++;
1474 mm[loc].type=NM_TYPE_INPUT; mm[loc].text_len = INIT_STRING_LEN; mm[loc].text = ModemInitString; loc++;
1476 mm[loc].type=NM_TYPE_MENU; mm[loc].text = TXT_ACCEPT_SAVE; loc++;
1478 mmn = newmenu_do1(NULL, TXT_SERIAL_SETTINGS, loc, mm, com_param_setup_poll, menu_save);
1484 com_port_num = COM1;
1485 else if (mm[1].value)
1486 com_port_num = COM2;
1487 else if (mm[2].value)
1488 com_port_num = COM3;
1490 com_port_num = COM4;
1492 if (mmn == menu_custom)
1494 com_custom_param_setup();
1496 if (mmn==menu_choose_string)
1498 com_choose_string();
1503 com_type = com_type_detect();
1507 nm_messagebox(NULL, 1, TXT_OK, "%s\n%s", TXT_WARNING, TXT_NO_UART);
1510 if ((mm[menu_baud].value) || (mmn == menu_baud))
1512 else if ((mm[menu_baud+1].value) || (mmn == menu_baud+1))
1516 if (com_type == 16550)
1520 nm_messagebox(NULL, 1, TXT_OK, TXT_WARNING_16550);
1525 //mprintf((0, "%s\n", ModemInitString));
1527 if ((strnicmp("AT", ModemInitString, 2)) && (strlen(ModemInitString) < (INIT_STRING_LEN-2)))
1528 sprintf(modem_init_string, "AT%s", ModemInitString);
1530 strcpy(modem_init_string, ModemInitString);
1532 if (mmn != menu_save)
1540 com_save_settings();
1544 codex(code_14s, code_14e)
1546 // Handshaking to start a serial game, 2 players only
1548 int com_start_game_menu(void)
1553 int opt, diff_opt, mode_opt, options_opt,allow_opt,extraopts_opt;
1554 char level_text[32];
1557 int new_mission_num, anarchy_only = 0;
1559 new_mission_num = multi_choose_mission(&anarchy_only);
1561 if (new_mission_num < 0)
1564 strcpy(my_sync.mission_name, Mission_list[new_mission_num].filename);
1567 sprintf(level, "1");
1569 Game_mode &= ~GM_MULTI_COOP;
1570 Game_mode &= ~GM_MULTI_ROBOTS;
1571 Game_mode &= ~GM_CAPTURE;
1572 Netgame.game_flags = 0;
1573 SetAllAllowablesTo(1);
1575 sprintf(level_text, "%s (1-%d)", TXT_LEVEL_, Last_level);
1576 // if (Last_secret_level < -1)
1577 // sprintf(level_text+strlen(level_text)-1, ", S1-S%d)", -Last_secret_level);
1578 // else if (Last_secret_level == -1)
1579 // sprintf(level_text+strlen(level_text)-1, ", S1)");
1581 Assert(strlen(level_text) < 32);
1583 // Put up menu for user choices controlling gameplay
1587 m[opt].type = NM_TYPE_TEXT; m[opt].text = level_text; opt++;
1588 m[opt].type = NM_TYPE_INPUT; m[opt].text_len = 4; m[opt].text = level; opt++;
1589 m[opt].type = NM_TYPE_TEXT; m[opt].text = TXT_MODE;
1591 m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_ANARCHY; m[opt].value=!(Game_mode & GM_MULTI_ROBOTS); m[opt].group = 0; opt++;
1592 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++;
1593 m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_COOPERATIVE; m[opt].value=(Game_mode & GM_MULTI_COOP);m[opt].group = 0; opt++;
1594 m[opt].type = NM_TYPE_RADIO; m[opt].text = "Capture the Flag"; m[opt].value=(Game_mode & GM_CAPTURE); m[opt].group = 0; opt++;
1596 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++;
1597 m[opt].type = NM_TYPE_TEXT; m[opt].text = " "; opt ++;
1601 m[opt].type = NM_TYPE_CHECK; m[opt].text = "Marker camera views"; m[opt].value=Netgame.Allow_marker_view; opt++;
1602 m[opt].type = NM_TYPE_CHECK; m[opt].text = "Indestructable lights"; m[opt].value=Netgame.AlwaysLighting; opt++;
1605 m[opt].type = NM_TYPE_MENU; m[opt].text = "Choose objects allowed"; opt++;
1610 // m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_IDS; m[opt].value=0; opt++;
1611 m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_ON_MAP; m[opt].value=0; opt++;
1618 choice = newmenu_do1(NULL, TXT_SERIAL_GAME_SETUP, opt, m, NULL, 1);
1623 if (choice==allow_opt)
1625 network_set_power();
1628 Netgame.Allow_marker_view=m[extraopts_opt].value;
1629 Netgame.AlwaysLighting=m[extraopts_opt+1].value;
1631 if (m[mode_opt].value)
1633 mprintf ((0,"Undoing capture in modem.c\n"));
1634 Game_mode &= ~(GM_MULTI_COOP | GM_MULTI_ROBOTS | GM_CAPTURE);
1638 nm_messagebox(NULL, 1, TXT_OK, TXT_ONLY_ANARCHY);
1642 else if (anarchy_only) {
1643 if (m[mode_opt+3].value)
1645 mprintf ((0,"Setting capture 1\n"));
1646 Game_mode |= (GM_CAPTURE);
1650 nm_messagebox(NULL, 1, TXT_OK, TXT_ANARCHY_ONLY_MISSION);
1654 else if (m[mode_opt+1].value)
1656 Game_mode &= ~GM_MULTI_COOP;
1657 Game_mode |= GM_MULTI_ROBOTS;
1659 else if (m[mode_opt+2].value)
1660 Game_mode |= (GM_MULTI_COOP | GM_MULTI_ROBOTS);
1661 else if (m[mode_opt+3].value)
1663 mprintf ((0,"Setting capture 2\n"));
1664 Game_mode |= (GM_CAPTURE);
1667 // if (m[options_opt].value)
1668 // Netgame.game_flags |= NETGAME_FLAG_SHOW_ID;
1669 if (m[options_opt].value)
1670 Netgame.game_flags |= NETGAME_FLAG_SHOW_MAP;
1672 start_level_num = atoi(level);
1674 if ((start_level_num < 1) || (start_level_num > Last_level))
1676 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_LEVEL_OUT_RANGE);
1677 sprintf(level, "1");
1681 Difficulty_level = m[diff_opt].value;
1683 return(1); // Go for game!
1685 return 0; // No game
1691 // Ask the other player if its OK to start now
1696 com_send_choice(SELECTION_STARTGAME);
1698 m[0].type = NM_TYPE_TEXT; m[0].text = TXT_ESC_ABORT;
1700 choice = newmenu_do(NULL, TXT_WAIT_FOR_OK, 1, m, com_menu_poll);
1704 com_send_choice(SELECTION_STARTGAME_ABORT);
1709 if (other_menu_choice == SELECTION_YES_START)
1711 else if (other_menu_choice == SELECTION_STARTGAME)
1713 com_send_choice(SELECTION_YES_START);
1722 codex(code_15s, code_15e)
1727 // Start a serial game after being linked
1729 mprintf((0, "Entered com_start_game\n"));
1733 if (! ( (Game_mode & GM_MODEM) || (Game_mode & GM_SERIAL) ) )
1736 Assert (master != -1);
1738 if (other_menu_choice != SELECTION_STARTGAME)
1740 if (!com_ask_to_start())
1744 if (master == 1) // Master chooses options
1746 if (com_start_game_menu())
1749 change_playernum_to(0);
1750 my_sync.level_num = start_level_num;
1751 my_sync.difficulty = Difficulty_level;
1752 my_sync.game_mode = Game_mode;
1753 if (Game_mode & GM_CAPTURE)
1754 my_sync.game_mode |=GM_UNKNOWN;
1755 memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
1757 my_sync.sync_time = control_invul_time;
1758 my_sync.game_flags = Netgame.game_flags;
1759 Netgame.control_invul_time = control_invul_time;
1760 memcpy ((&my_sync.game_flags)+1,(&Netgame.team_vector)+1,4);
1764 if (Game_mode & GM_CAPTURE)
1765 my_sync.game_mode &=GM_UNKNOWN;
1771 change_playernum_to(1);
1772 memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
1774 my_sync.level_num = 1;
1777 if (com_process_mode == COM_PROCESS_NORMAL)
1779 Difficulty_level = other_sync.difficulty;
1780 start_level_num = other_sync.level_num;
1781 Game_mode = other_sync.game_mode;
1782 if (Game_mode & GM_UNKNOWN) // Super HACK! Out of bit fields, doubling up
1784 Game_mode &=~GM_UNKNOWN;
1785 Game_mode |=GM_CAPTURE;
1788 memcpy ((&Netgame.team_vector)+1,(&other_sync.game_flags)+1,4);
1791 Netgame.game_flags = other_sync.game_flags;
1792 Netgame.control_invul_time = other_sync.sync_time;
1793 if (!load_mission_by_name(other_sync.mission_name))
1795 mprintf((0, "Mission not found: %s!\n", other_sync.mission_name));
1796 nm_messagebox(NULL, 1, TXT_OK, TXT_MISSION_NOT_FOUND);
1797 my_sync.sync_id = start_level_num;
1798 serial_sync_abort(0);
1804 if (com_process_mode != COM_PROCESS_NORMAL)
1807 memcpy(Players[OtherPlayer].callsign, other_sync.callsign, CALLSIGN_LEN+1);
1808 Function_mode = FMODE_GAME;
1809 Game_mode &= ~GM_GAME_OVER;
1811 init_player_stats_game();
1812 init_player_stats_level(0);
1813 // Assert(start_level_num > 0);
1814 Assert((start_level_num >= Last_secret_level) && (start_level_num <= Last_level));
1815 StartNewLevel(start_level_num, 0);
1819 // Modem control functions, dialing, answering, etc.
1822 void modem_edit_phonebook(newmenu_item *m)
1824 int choice, choice2;
1825 newmenu_item menu[5];
1827 int default_choice = 0;
1829 m[NUM_PHONE_NUM].text = TXT_SAVE;
1831 menu[0].text = TXT_NAME; menu[0].type = NM_TYPE_TEXT;
1832 menu[1].type = NM_TYPE_INPUT; menu[1].text = text[0]; menu[1].text_len = LEN_PHONE_NAME;
1833 menu[2].text = TXT_PHONE_NUM; menu[2].type = NM_TYPE_TEXT;
1834 menu[3].type = NM_TYPE_INPUT; menu[3].text = text[1]; menu[3].text_len = LEN_PHONE_NUM;
1835 menu[4].text = TXT_ACCEPT; menu[4].type = NM_TYPE_MENU;
1838 choice = newmenu_do1(NULL, TXT_SEL_NUMBER_EDIT, NUM_PHONE_NUM+1, m, NULL, default_choice);
1841 com_load_settings();
1844 if (choice == NUM_PHONE_NUM)
1847 com_save_settings();
1854 strcpy(menu[1].text, phone_name[choice]);
1855 strcpy(menu[3].text, phone_num[choice]);
1857 choice2 = newmenu_do1(NULL, TXT_EDIT_PHONE_ENTRY, 5, menu, NULL, default_choice);
1860 strcpy(phone_name[choice], menu[1].text);
1861 strcpy(phone_num[choice], menu[3].text);
1862 sprintf(m[choice].text, "%d. %s \t", choice+1, phone_name[choice]);
1863 add_phone_number(m[choice].text, phone_num[choice] );
1867 default_choice += 2; if (default_choice > 4) default_choice = 4;
1871 default_choice = NUM_PHONE_NUM;
1876 codex(code_16s, code_16e)
1878 void add_phone_number( char * src, char * num )
1891 strcat( src, "..." );
1894 int modem_dial_menu(void)
1896 newmenu_item m[NUM_PHONE_NUM+2];
1897 char menu_text[NUM_PHONE_NUM][80];
1902 for (i = 0; i < NUM_PHONE_NUM; i++)
1904 m[i].text = menu_text[i];
1905 sprintf(m[i].text, "%d. %s \t", i+1, phone_name[i]);
1906 add_phone_number(m[i].text, phone_num[i] );
1907 m[i].type = NM_TYPE_MENU;
1910 strcat(m[i-1].text, "\n");
1912 m[NUM_PHONE_NUM].type = NM_TYPE_MENU;
1913 m[NUM_PHONE_NUM].text = TXT_MANUAL_ENTRY;
1914 m[NUM_PHONE_NUM+1].text = TXT_EDIT_PHONEBOOK;
1915 m[NUM_PHONE_NUM+1].type = NM_TYPE_MENU;
1917 choice = newmenu_do1(NULL, TXT_SEL_NUMBER_DIAL, NUM_PHONE_NUM+2, m, NULL, 0);
1919 return -1; // user abort
1921 if (choice == NUM_PHONE_NUM+1)
1924 modem_edit_phonebook(m);
1928 if (choice == NUM_PHONE_NUM)
1932 m2[0].type = NM_TYPE_INPUT; m2[0].text = phone_num[NUM_PHONE_NUM]; m2[0].text_len = LEN_PHONE_NUM;
1933 choice = newmenu_do(NULL, TXT_ENTER_NUMBER_DIAL, 1, m2, NULL);
1937 return NUM_PHONE_NUM;
1940 // A phone number was chosen
1945 com_wait_for_connect(int nitems, newmenu_item *menus, int *key, int citem)
1948 char input_buffer[81];
1950 char error_mess[5][15] =
1959 int num_error_messages = 5;
1966 if (GetCd(com_port))
1975 nm_messagebox(NULL, 1, TXT_OK, TXT_CARRIER_LOST);
1981 result = HMInputLine(com_port, 500, input_buffer, 80);
1984 return; // Timed out
1986 mprintf((0, "Modem string: '%s'\n", input_buffer));
1988 for (i = 0; i < num_error_messages; i++)
1990 if (!strncmp(input_buffer, error_mess[i], strlen(error_mess[i])))
1992 sprintf(text, "%s %s", TXT_ERR_MODEM_RETURN, input_buffer);
1993 nm_messagebox(NULL, 1, TXT_OK, text);
1999 if (strncmp(input_buffer, TXT_CONNECT, 7))
2001 mprintf((0, "Non-connect message found.\n"));
2002 return; // some other string. Not an error, but not a connect
2005 sscanf(input_buffer, "CONNECT %d", &baud);
2007 mprintf((0, "Connect at %d baud.\n", baud));
2011 nm_messagebox(NULL, 1, TXT_OK, TXT_BAUD_GREATER_9600);
2016 com_baud_rate = baud;
2022 codex(code_17s, code_17e)
2025 com_wait_for_ring(int nitems, newmenu_item *menus, int *key, int citem)
2028 char input_buffer[81];
2034 result = HMInputLine(com_port, 500, input_buffer, 80);
2036 if ((result <= 0) || strncmp(input_buffer, TXT_RING, 4))
2045 int modem_verify(void)
2047 // Is there a modem on this com port or not?
2051 HMWaitForOK( 5000, NULL);
2053 HMSendString(com_port, "AT");
2054 result = HMSendString(com_port, "AT");
2061 void modem_dialout(void)
2069 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT);
2073 com_enable(); // Open COM port as configured
2078 // UseRtsCts(com_port, ON); // use hardware handshaking
2081 if ((choice = modem_dial_menu()) == -1)
2082 return; // user aborted
2084 show_boxed_message(TXT_RESET_MODEM);
2086 // Verify presence of modem
2087 if (!modem_verify())
2089 clear_boxed_message();
2090 nm_messagebox(NULL, 1, TXT_OK, TXT_NO_MODEM);
2095 if (strlen(phone_num[choice]) == 0)
2097 clear_boxed_message();
2098 nm_messagebox(NULL, 1, TXT_OK, TXT_NO_PHONENUM);
2102 clear_boxed_message();
2104 sprintf(text, "%s\n%s", TXT_DIALING, phone_num[choice]);
2105 show_boxed_message(text);
2107 // Proceed with the call
2109 HMReset( com_port );
2111 HMSendString( com_port, modem_init_string );
2113 HMDial( com_port, phone_num[choice] );
2117 clear_boxed_message();
2119 m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT;
2122 choice = newmenu_do(NULL, TXT_WAITING_FOR_ANS, 1, m, com_wait_for_connect);
2124 HMSendStringNoWait(com_port, "", -2);
2129 // We are now connected to the other modem
2133 my_sync.sync_time=32000;
2134 // -- commented out by mk, 2/21/96 -- master = 1; // The person who dialed is the master of the connection
2135 change_playernum_to(0);
2139 Game_mode |= GM_MODEM;
2140 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
2143 HMSendStringNoWait(com_port, "", -2);
2148 codex(code_18s, code_18e)
2150 void modem_answer(void)
2157 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT);
2161 com_enable(); // Open COM port as configured
2166 // UseRtsCts(com_port, ON); // use hardware handshaking
2168 show_boxed_message(TXT_RESET_MODEM);
2170 // Verify presence of modem
2171 if (!modem_verify())
2173 clear_boxed_message();
2174 nm_messagebox(NULL, 1, TXT_OK, TXT_NO_MODEM);
2179 HMReset( com_port );
2181 HMSendString( com_port, modem_init_string );
2183 HMSendString( com_port, "AT"); // To set the DTE rate for RING notification
2185 clear_boxed_message();
2187 m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT;
2190 choice = newmenu_do(NULL, TXT_WAITING_FOR_CALL, 1, m, com_wait_for_ring);
2192 HMSendStringNoWait(com_port, "", -2);
2199 // Now answer the phone and wait for carrier
2205 choice = newmenu_do(NULL, TXT_WAITING_FOR_CARR, 1, m, com_wait_for_connect);
2207 HMSendStringNoWait(com_port, "", -2);
2212 // We are now connected to the other modem
2216 my_sync.sync_time=0;
2217 // -- commented out by mk, 2/21/96 -- master = 0;
2218 change_playernum_to(1);
2222 Game_mode |= GM_MODEM;
2223 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
2226 HMSendStringNoWait(com_port, "", -2);
2231 void serial_link_start(void)
2235 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT);
2239 com_enable(); // Open COM port as configured
2249 my_sync.sync_time = rand();
2250 mprintf((0, "My rand set to %d.\n", my_sync.sync_time));
2254 Game_mode |= GM_SERIAL;
2255 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
2259 nm_messagebox(NULL, 1, TXT_OK, "%s\n%s", TXT_ERROR, TXT_FAILED_TO_NEGOT);
2264 // Syncronization functions
2268 serial_sync_abort(int val)
2270 // Send "I got Sync but it was no good!" packet
2272 sendbuf[0] = (char)MULTI_END_SYNC;
2273 sendbuf[1] = Player_num;
2274 sendbuf[2] = (char)val; // Indicated failure
2276 sendbuf[3] = my_sync.sync_id;
2277 com_send_data(sendbuf, 4, 1);
2279 // com_send_data(sendbuf, 3, 1);
2284 com_level_sync(void)
2286 // Send between-level sync stuff
2288 mprintf((0, "entered com_level_sync()\n"));
2290 Function_mode = FMODE_MENU; // Prevent the game loop from running during the menus!
2292 // At this point, the new level is loaded but the extra objects or players have not
2295 my_sync.level_num = Current_level_num;
2296 my_sync.seg_checksum = netmisc_calc_checksum(Segments, (Highest_segment_index+1) * sizeof(segment));
2297 my_sync.kills[0] = kill_matrix[Player_num][0];
2298 my_sync.kills[1] = kill_matrix[Player_num][1];
2299 my_sync.proto_version = MULTI_PROTO_VERSION;
2301 my_sync.killed = Players[Player_num].net_killed_total;
2305 if (Game_mode & GM_MULTI_COOP)
2306 my_sync.difficulty = Player_num;
2308 my_sync.difficulty = rand()%MAX_NUM_NET_PLAYERS; // My starting position
2310 if (com_sync(Current_level_num))
2312 com_process_mode = COM_PROCESS_MENU;
2313 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NEGOTIATION_FAIL );
2314 gr_palette_clear ();
2315 longjmp(LeaveGame, 0);
2318 if (my_sync.level_num != other_sync.level_num)
2321 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);
2322 gr_palette_clear ();
2323 longjmp(LeaveGame, 0);
2326 if (my_sync.seg_checksum != other_sync.seg_checksum)
2329 mprintf((1, "My check %d, other check %d.\n", my_sync.seg_checksum, other_sync.seg_checksum));
2330 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);
2331 gr_palette_clear ();
2334 longjmp(LeaveGame, 0);
2338 if (my_sync.proto_version != other_sync.proto_version)
2341 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_DESCENT_NO_MATCH);
2342 gr_palette_clear ();
2343 longjmp(LeaveGame, 0);
2346 mprintf((0, "My pos = %d, other pos = %d.\n", my_sync.difficulty, other_sync.difficulty));
2348 if ((other_sync.difficulty == my_sync.difficulty) && !master)
2350 // If we chose the same position and I am the slave, choose another
2351 my_sync.difficulty = (my_sync.difficulty+1) % MAX_NUM_NET_PLAYERS;
2354 Objects[Players[OtherPlayer].objnum].pos = Player_init[other_sync.difficulty].pos;
2355 Objects[Players[OtherPlayer].objnum].orient = Player_init[other_sync.difficulty].orient;
2356 obj_relink(Players[OtherPlayer].objnum,Player_init[other_sync.difficulty].segnum);
2357 Objects[Players[OtherPlayer].objnum].type = OBJ_PLAYER;
2359 Objects[Players[Player_num].objnum].pos = Player_init[my_sync.difficulty].pos;
2360 Objects[Players[Player_num].objnum].orient = Player_init[my_sync.difficulty].orient;
2361 obj_relink(Players[Player_num].objnum, Player_init[my_sync.difficulty].segnum);
2362 Objects[Players[Player_num].objnum].type = OBJ_PLAYER;
2364 SerialLastMessage = GameTime;
2366 kill_matrix[OtherPlayer][0] = other_sync.kills[0];
2367 kill_matrix[OtherPlayer][1] = other_sync.kills[1];
2368 Players[Player_num].net_kills_total = kill_matrix[Player_num][OtherPlayer] - kill_matrix[Player_num][Player_num];
2369 Players[OtherPlayer].net_kills_total = kill_matrix[OtherPlayer][Player_num] - kill_matrix[OtherPlayer][OtherPlayer];
2370 // Players[Player_num].net_killed_total = kill_matrix[0][Player_num] + kill_matrix[1][Player_num];
2371 // Players[OtherPlayer].net_killed_total = kill_matrix[0][OtherPlayer] + kill_matrix[1][OtherPlayer];
2372 Players[OtherPlayer].net_killed_total = other_sync.killed;
2373 Players[OtherPlayer].connected = Players[Player_num].connected = 1;
2375 Assert(N_players == 2);
2376 Assert(Player_num != OtherPlayer);
2378 gr_palette_fade_out(gr_palette, 32, 0);
2380 Function_mode = FMODE_GAME;
2381 com_process_mode = COM_PROCESS_NORMAL;
2382 multi_sort_kill_list();
2386 codex(code_19s, code_19e)
2389 com_send_end_sync(void)
2391 // Send "I got Sync" packet
2393 sendbuf[0] = (char)MULTI_END_SYNC;
2394 sendbuf[1] = Player_num;
2395 sendbuf[2] = 1; // Indicates success
2397 sendbuf[3] = my_sync.sync_id;
2398 com_send_data(sendbuf, 4, 2);
2400 // com_send_data(sendbuf, 3, 2);
2405 com_send_begin_sync(void)
2407 mprintf((0, "Sending my sync.\n"));
2408 com_send_data((char *)&my_sync, sizeof(com_sync_pack)-3, 1);
2412 com_process_end_sync(byte *buf)
2414 // Process incoming end-sync packet
2417 com_process_mode = COM_PROCESS_MENU;
2422 if (buf[3] == my_sync.sync_id)
2428 com_process_sync(char *buf, int len)
2430 mprintf ((0,"Processing sync!"));
2435 case MULTI_END_SYNC:
2437 com_process_end_sync(buf);
2440 case MULTI_BEGIN_SYNC:
2442 mprintf((0, "Incoming begin_sync message.\n"));
2446 memcpy(&other_sync, buf, sizeof(com_sync_pack)-3);
2448 if (other_sync.sync_id != my_sync.sync_id)
2450 mprintf((0, "Other sync invalid id, %d != %d.\n", other_sync.sync_id, my_sync.sync_id));
2455 mprintf((0, "got other sync size %d.\n", sizeof(com_sync_pack)-3));
2457 com_send_end_sync();
2463 if (got_sync && other_got_sync)
2466 mprintf((1, "Starting game.\n"));
2469 com_process_mode = COM_PROCESS_NORMAL;
2476 // Send sync information, depending on the situation
2478 if (!other_got_sync)
2480 com_send_begin_sync();
2484 com_send_end_sync();
2488 codex(code_20s, code_20e)
2490 void com_sync_poll(int nitems, newmenu_item *menus, int *key, int citem)
2504 if (timer_get_approx_seconds() > t1+F1_0)
2507 t1 = timer_get_approx_seconds();
2510 Assert(com_process_mode == COM_PROCESS_SYNC);
2512 com_process_input();
2514 if (com_process_mode == COM_PROCESS_NORMAL)
2518 mprintf((0, "Sync finished.\n"));
2521 if (com_process_mode == COM_PROCESS_MENU)
2524 mprintf((0, "Sync denied by other side.\n"));
2533 // How to handle the end of the level and start of the next level
2534 // returns 0 for success or 1 for failure
2540 mprintf((0, "Entered com_sync\n"));
2542 //@@gr_set_current_canvas(NULL);
2543 //@@pcx_error = pcx_read_bitmap(Menu_pcx_name, &grd_curcanv->cv_bitmap,grd_curcanv->cv_bitmap.bm_type,NULL);
2544 //@@Assert(pcx_error == PCX_ERROR_NONE);
2546 com_process_mode = COM_PROCESS_SYNC;
2547 got_sync = other_got_sync = 0;
2553 my_sync.sync_id = id;
2556 m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT;
2557 m[1].type = m[2].type = NM_TYPE_MENU;
2558 m[1].text = TXT_YES;
2562 choice = newmenu_do(NULL, TXT_WAIT_OPPONENT, 1, m, com_sync_poll);
2566 choice = newmenu_do1(NULL, TXT_SURE_ABORT_SYNC, 2, m+1, com_sync_poll, 1);
2573 if ((choice == -1) || (choice == -3)) {
2576 else if (choice != -2)
2583 com_endlevel(int *secret)
2585 // What do we do between levels?
2587 Function_mode = FMODE_MENU;
2589 gr_palette_fade_out(gr_palette, 32, 0);
2591 my_sync.level_num = multi_goto_secret;
2595 com_process_mode = COM_PROCESS_MENU;
2596 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NEGOTIATION_FAIL);
2597 longjmp(LeaveGame, 0);
2600 com_process_mode = COM_PROCESS_ENDLEVEL;
2602 if ((multi_goto_secret == 1) || (other_sync.level_num == 1))
2607 multi_goto_secret = 0;
2612 codex(code_21s, code_21e)
2616 int ReadModemList ()
2618 int num=0,i=0,namemode=1;
2622 if ((MyFile=fopen ("modem.lst","rb"))==NULL)
2625 ModemNames[num][0] = ModemStrings[num][0] = 0;
2627 while (!feof(MyFile))
2631 mprintf ((0,"%c",c));
2642 ModemStrings[num][i]=0;
2645 if (strlen(ModemNames[num]) && strlen(ModemStrings[num]))
2647 if (num >= MAX_MODEMS)
2648 break; //too many! abort!
2649 ModemNames[num][0] = ModemStrings[num][0] = 0;
2653 while (fgetc(MyFile)!=10)
2658 else if (c=='=' && namemode)
2660 ModemNames[num][i]=0;
2668 if (i<MODEM_NAME_LEN)
2669 ModemNames[num][i++]=c;
2673 if (i<INIT_STRING_LEN)
2674 ModemStrings[num][i++]=c;
2679 ModemStrings[num][i]=0; //terminate in case no final newline
2685 void com_choose_string ()
2688 newmenu_item m[MAX_MODEMS];
2690 if ((num=ReadModemList())==0)
2692 nm_messagebox ("Error",1,TXT_OK,"No valid modem file found!");
2696 mprintf ((0,"Number of strings=%d\n",num));
2701 m[i].type = NM_TYPE_MENU; m[i].text = &ModemNames[i];
2702 mprintf ((0,"Text(%d)=%s\n",i,ModemNames[i]));
2704 choice = newmenu_do(NULL, "Choose a modem", num, m, NULL);
2709 strcpy (ModemInitString,ModemStrings[choice]);