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.
26 #include "pa_enabl.h" //$$POLY_ACC
27 #include "fast.h" // Commlib stuff
62 #define MIN_COMM_GAP 8000
63 #define INIT_STRING_LEN 20
64 #define LEN_PHONE_NUM_OLD 15
65 #define LEN_PHONE_NUM 30
66 #define LEN_PHONE_NAME 12
67 #define NUM_PHONE_NUM 8
69 // How many times to repeat 'reliable' messages
73 #define COM_PROCESS_NORMAL 0
74 #define COM_PROCESS_ENDLEVEL 1
75 #define COM_PROCESS_SYNC 2
76 #define COM_PROCESS_MENU 3
78 #define SELECTION_STARTGAME 1
79 #define SELECTION_NO_START 2
80 #define SELECTION_YES_START 3
81 #define SELECTION_STARTGAME_ABORT 4
82 #define SELECTION_CLOSE_LINK 5
84 int default_base[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
85 int default_irq[4] = { 4, 3, 4, 3 };
87 // Code to support modem/null-modem play
89 // Program determined variables for serial play
91 typedef struct com_sync_pack {
98 char callsign[CALLSIGN_LEN+1];
102 char mission_name[9];
111 short DoAfterburner:1;
112 short DoInvulnerability:1;
118 short DoSuperLaser:1;
124 short DoEarthShaker:1;
126 short Allow_marker_view:1;
127 short RefusePlayers:1;
128 short AlwaysLighting:1;
133 char dummy[3]; // Extra space for checksum & sequence number
138 int com_baud_rate = 0;
139 //--unused-- int sync_time = 0;
142 int other_got_sync = 0;
144 long com_type = -1; /* What type of UART is available */
146 static ubyte rx_seqnum = 0xff;
147 static ubyte tx_seqnum = 0;
148 int OtherPlayer; // Player num for modem opponent
149 int com_process_mode = COM_PROCESS_NORMAL;
150 int master = -1; // Am I the master or is the other guy the master?
151 com_sync_pack my_sync, other_sync;
154 int com_custom_port = -1;
155 int com_custom_irq = -1;
156 int com_custom_base = -1;
158 int other_menu_choice = 0;
164 static char syncbuffer[MAX_MULTI_MESSAGE_LEN+4];
165 static char sendbuf[MAX_MULTI_MESSAGE_LEN+4]; // +4 because of +1 for null and +3 for checksum/sequence
167 // Serial setup variables
169 int com_port_num = -1;
171 char modem_init_string[INIT_STRING_LEN+1];
172 char phone_num[NUM_PHONE_NUM+1][LEN_PHONE_NUM+1];
173 char phone_name[NUM_PHONE_NUM][LEN_PHONE_NAME+1];
175 #define MAX_MODEMS 200
176 #define MODEM_NAME_LEN 22
178 char ModemNames[MAX_MODEMS][MODEM_NAME_LEN+1],ModemStrings[MAX_MODEMS][INIT_STRING_LEN+1]; // for init strings
179 char ModemInitString[INIT_STRING_LEN+1];
182 fix SerialLastMessage = 0;
184 /* Function prototypes for functions not exported through modem.h */
186 int GetModemList(void);
187 void com_param_setup(void);
188 void com_start_game(void);
189 void modem_dialout(void);
190 void modem_answer(void);
191 int com_sync(int id);
192 void com_sync_poll(int nitem, newmenu_item *menus, int *key, int citem);
195 #define codex(name_start, name_end) \
196 void name_start(void) \
203 for (i=0; i<123; i++) \
218 void name_end(void) \
222 #define codex(name_start, name_end)
225 codex(code_01s, code_01e)
227 int detect_UART(unsigned baseaddr, int * loc, int * code )
229 // this function returns 0 if no UART is installed.
230 // 1: 8250, 2: 16450 or 8250 with scratch reg., 3: 16550, 4: 16550A
235 // check if a UART is present. This is code John hacked by looking at the return
236 // values from peoples computers.
237 olddata=inp(baseaddr+4);
238 outp(baseaddr+4,0x1f); // Enable Loopback mode, sets RTS & DTR to 1.
241 temp = inp(baseaddr+6); // Read the state of RTS and DTR.
242 temp = inp(baseaddr+6); // Do this twice, so that lower 4 bits are clear. OS/2 returns 0xB0 after this,
243 // instead of 0xff if no port is there.
245 if ((temp&0x3f)!=0x30) {
246 *loc = 1; *code = temp;
249 outp(baseaddr+4,olddata); // Restore RTS & DTR
251 // next thing to do is look for the scratch register
252 olddata=inp(baseaddr+7);
253 outp(baseaddr+7,0x55);
255 if (inp(baseaddr+7)!=0x55) return 1;
256 outp(baseaddr+7,0xAA);
258 if (inp(baseaddr+7)!=0xAA) return 1;
259 outp(baseaddr+7,olddata); // we don't need to restore it if it's not there
261 // then check if there's a FIFO
265 // some old-fashioned software relies on this!
266 outp(baseaddr+2,0x0);
268 if ((x&0x80)==0) return 2;
269 if ((x&0x40)==0) return 3;
273 codex(code_02s, code_02e)
282 long port_addr[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
288 if ( (com_port_num != 0) && (com_port_num != 1) && (com_port_num != 2) && (com_port_num != 3) )
291 return -1; // Error, set com_port_num before calling this!
294 if (com_port_num == com_custom_port)
295 portaddr = com_custom_base;
297 portaddr = port_addr[com_port_num];
299 mprintf((0, "com port %x.\n", portaddr));
301 switch( detect_UART(portaddr, &loc, &code) ) {
303 mprintf((0, "No UART detected. (LOC:%d, CODE:0x%x)\n", loc, code));
306 mprintf((0, "UART type is 8250.\n" ));
308 case 2: // 16450 or 8250 with scratch reg.
309 mprintf((0, "UART is type 16450, or an 8250 with scratch register.\n" ));
312 mprintf((0, "UART is type 16550, with FIFO bug.\n" ));
313 return(16450); // 16550's FIFOs don't work. This is not a typo. (detected 16550, returned 16450 )
314 case 4: // 16550A, which is the only UART the FIFO mode works with.
315 mprintf((0, "UART is type 16550A, no FIFO bug.\n" ));
321 #if !defined(NDEBUG) && !defined(NMONO)
323 com_dump_string(char *string)
325 mprintf((0, "%s\n", string));
328 #define com_dump_string()
331 codex(code_03s, code_03e)
336 // Detect and enable the COM port selected by the user
346 // com_port_num and com_speed should be set before calling this func
347 com_type = com_type_detect();
349 if (com_custom_port == com_port_num)
350 rc = Change8259Priority(com_custom_irq);
351 else if ((com_port_num == COM2) || (com_port_num == COM4))
352 rc = Change8259Priority( IRQ3 );
354 rc = Change8259Priority( IRQ4 );
358 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_SERIAL_OPEN_ERROR);
362 // If our detection is wrong, we want them to be able to go ahead and play anyway.
363 // if (com_type == -1)
365 // nm_messagebox(TXT_ERROR, 1, TXT_OK, "Error 2\n%s", TXT_SERIAL_OPEN_ERROR);
369 if (com_port_num == com_custom_port )
371 rc = FastSetPortHardware(com_port_num, com_custom_irq, com_custom_base);
374 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_SERIAL_OPEN_ERROR);
379 if (com_type == 16550)
381 FastSet16550TriggerLevel( TRIGGER_04 );
382 FastSet16550UseTXFifos( ON );
385 FastSavePortParameters(com_port_num);
387 com_port = PortOpenGreenleafFast(com_port_num, com_speed, 'N', 8, 1);
391 int curr_irq, curr_base;
392 FastGetPortHardware(com_port_num, &curr_irq, &curr_base);
393 mprintf((0, "Current port settings: base %x, irq %x.\n", curr_base, curr_irq ));
397 if ((com_port == 0) || (com_port->status != 0))
399 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_SERIAL_OPEN_ERROR);
404 mprintf((0, "Com port opened. Status = %d\n", com_port->status));
407 SetDtr(com_port, ON);
409 if ( FindArg( "-ctsrts" ) || FindArg( "-rtscts" ) )
410 UseRtsCts(com_port, ON); // Now used for null-modem as well, helps IHHD!
412 UseRtsCts(com_port, OFF);
418 // DumpPortStatus(com_port, com_dump_string);
426 // close the com port and free associated structures.
433 // SetDtr(com_port, OFF);
434 // UseRtsCts(com_port, OFF);
436 rc = PortClose(com_port);
438 FastRestorePortParameters(com_port_num);
440 if (com_custom_port == com_port_num)
442 // Custom settings were in effect, roll them back
443 rc = FastSetPortHardware(com_port_num, default_irq[com_port_num], default_base[com_port_num]);
446 if (rc != ASSUCCESS) {
448 mprintf((1, "PortClose returned %d!\n", rc));
458 mprintf((0, "Com port released.\n"));
462 codex(code_04s, code_04e)
467 // this is the safest way to get out of some modem/serial negotiation
468 // and back to the main menu. Use this whenever this have gone too far
475 change_playernum_to(0);
477 Viewer = ConsoleObject = &Objects[0];
478 Game_mode = GM_GAME_OVER; // Force main menu selection
484 // Close the serial link
486 com_send_choice(SELECTION_CLOSE_LINK);
491 com_carrier_lost(void)
493 // Carrier lost, inform and abort
495 if (multi_in_menu > 0)
497 multi_leave_menu = 1;
501 Function_mode = FMODE_MENU;
504 nm_messagebox(NULL, 1, TXT_OK, TXT_CARRIER_LOST);
508 codex(code_05s, code_05e)
510 extern void SetAllAllowablesTo (int on);
511 extern ubyte Cockpit_mode_save; // From object.c
512 extern void network_set_power (void);
518 // Reset various parameters before starting a new game
522 for (i = 0; i < N_players; i++)
524 Players[i].connected = 1;
529 multi_new_game(); // Reset kill list, among other things
530 Control_center_destroyed = 0;
531 Endlevel_sequence = 0;
533 // Yes, this really IS as ugly as it gets, kids...
535 if (Cockpit_mode == CM_LETTERBOX || Cockpit_mode == CM_REAR_VIEW)
537 select_cockpit(Cockpit_mode_save);
541 codex(code_06s, code_06e)
544 com_save_settings(void)
549 if ( (settings = fopen("serial.cfg", "wb")) == NULL)
552 if (fwrite(&com_speed, sizeof(int), 1, settings) != 1)
555 if (fwrite(&com_port_num, sizeof(int), 1, settings) != 1)
558 if (fwrite(modem_init_string, 1, INIT_STRING_LEN+1, settings) != INIT_STRING_LEN+1)
561 for (i = 0; i < NUM_PHONE_NUM; i++)
563 if (fwrite(phone_num[i], 1, LEN_PHONE_NUM+1, settings) != LEN_PHONE_NUM+1)
565 if (fwrite(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1)
569 if (fwrite(&com_custom_port, sizeof(int), 1, settings) != 1)
571 if (fwrite(&com_custom_irq, sizeof(int), 1, settings) != 1)
573 if (fwrite(&com_custom_base, sizeof(int), 1, settings) != 1)
581 nm_messagebox(NULL, 1, TXT_OK, TXT_ERROR_SERIAL_CFG);
585 unlink("serial.cfg");
591 codex(code_07s, code_07e)
594 com_load_settings(void)
599 if ((settings = fopen("serial.cfg", "rb")) == NULL)
602 cfg_size = filelength(fileno(settings));
604 // Read the data from the file
606 if (fread(&com_speed, sizeof(int), 1, settings) != 1)
608 if (! ((com_speed == 9600) || (com_speed == 19200) || (com_speed == 38400)) )
611 if (fread(&com_port_num, sizeof(int), 1, settings) != 1)
613 if ( (com_port_num < COM1) || (com_port_num > COM4) )
616 if (fread(modem_init_string, 1, INIT_STRING_LEN+1, settings) != INIT_STRING_LEN+1)
618 modem_init_string[INIT_STRING_LEN] = '\0';
620 if (cfg_size <= 273 ) { // Old 15 char LEN_PHONE_NUM's
621 mprintf(( 1, "Reading old pre 1.1 phone.cfg\n" ));
622 for (i = 0; i < NUM_PHONE_NUM; i++)
624 if (fread(phone_num[i], 1, LEN_PHONE_NUM_OLD+1, settings) != LEN_PHONE_NUM_OLD+1)
626 phone_num[i][LEN_PHONE_NUM_OLD] = '\0';
627 if (fread(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1)
629 phone_name[i][LEN_PHONE_NAME] = '\0';
631 } else { // Normal Phone nums
632 for (i = 0; i < NUM_PHONE_NUM; i++)
634 if (fread(phone_num[i], 1, LEN_PHONE_NUM+1, settings) != LEN_PHONE_NUM+1)
636 phone_num[i][LEN_PHONE_NUM] = '\0';
637 if (fread(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1)
639 phone_name[i][LEN_PHONE_NAME] = '\0';
643 if (fread(&com_custom_port, sizeof(int), 1, settings) != 1) {
644 mprintf((0, "Reading old file format for serial.cfg.\n"));
648 if ( (com_custom_port < -1) || (com_custom_port > COM4) )
651 if (fread(&com_custom_irq, sizeof(int), 1, settings) != 1)
653 if ( (com_custom_port < -1) || (com_custom_port > IRQ15) )
656 if (fread(&com_custom_base, sizeof(int), 1, settings) != 1)
658 if (com_custom_base < -1)
661 //Everything was A-Ok!
668 nm_messagebox(NULL, 1, TXT_OK, TXT_ERR_SER_SETTINGS);
671 // Return some defaults
672 com_speed = 19200; // UART speed
673 strcpy(modem_init_string, "ATZ");
675 com_custom_port = -1;
676 for (i = 0; i < NUM_PHONE_NUM; i++)
678 phone_num[i][0] = '\0';
679 strcpy(phone_name[i], TXT_EMPTY);
689 serial_leave_game(void)
692 mprintf((0, "Called serial_leave_game.\n"));
695 serial_sync_abort(0); // Just in case the other guy is in sync mode
696 Game_mode |= GM_GAME_OVER;
697 Function_mode = FMODE_MENU;
700 codex(code_08s, code_08e)
703 com_send_data(char *ptr, int len, int repeat)
707 // Take the raw packet data specified by ptr and append the sequence
708 // number and checksum, and pass it to com_send_ptr
713 if (Game_mode & GM_MODEM)
717 mprintf((0, "CARRIER LOST!\n"));
720 len += 3; // Checksum data is 3 bytes
722 *(ubyte *)(ptr+(len-3)) = (tx_seqnum+1)%256;
723 tx_seqnum = (tx_seqnum+1)%256;
725 *(ushort *)(ptr+(len-2)) = netmisc_calc_checksum(ptr, len-2);
727 com_send_ptr(ptr, len);
729 for (i = 0; i < repeat; i++)
730 com_send_ptr(ptr, len);
733 com_send_ptr(char *ptr, int len)
738 for (count = 0, dat=ptr[0]; count < len; dat=ptr[++count])
740 WriteChar(com_port, dat);
742 WriteChar(com_port, EOR_MARK); // double in-band endmarkers
744 WriteChar(com_port, EOR_MARK); // EOR
745 WriteChar(com_port, 0); // EOR
749 codex(code_09s, code_09e)
754 // Get rid of all waiting data in the serial buffer
761 mprintf((0, "COM FLUSH:"));
763 while (ReadCharTimed(com_port, 100) >= 0)
765 mprintf((0, "%d characters.\n", i));
772 static int eor_recv = 0;
775 // -1 = Nothing in buffer
776 // -2 = End of record
781 i = ReadChar(com_port);
783 // mprintf((0, "%c", i));
785 if (i == ASBUFREMPTY)
788 if ((i == EOR_MARK) || eor_recv)
791 i = ReadChar(com_port);
793 if (i == ASBUFREMPTY)
795 // Assert(eor_recv == 0);
799 else if (i == EOR_MARK)
802 return(EOR_MARK); // Doubled EOR returns the character
808 mprintf((0, "EOR followed by unexpected value %d.\n", i));
818 #define SERIAL_IDLE_TIMEOUT F1_0*10
820 codex(code_10s, code_10e)
825 static fix last_comm_time = 0;
826 static int last_pos_skipped = 0;
829 if (Endlevel_sequence || (com_process_mode==COM_PROCESS_ENDLEVEL)) { // Only recieve during endlevel
830 int old_Endlevel_sequence = Endlevel_sequence;
831 Endlevel_sequence = 1;
833 Endlevel_sequence = old_Endlevel_sequence;
837 last_comm_time += FrameTime;
839 if ((last_comm_time > MIN_COMM_GAP) || Network_laser_fired)
842 if ((Game_mode & GM_MULTI_ROBOTS) && !last_pos_skipped) {
843 rval = multi_send_robot_frame(0);
845 if (rval && !Network_laser_fired)
847 last_pos_skipped = 1;
851 last_pos_skipped = 0;
852 multi_send_position(Players[Player_num].objnum);
853 multi_send_fire(); // Will return w/o sending if we haven't fired
856 // mprintf((0, "%d chars sent, %f cps.\n", chars_sent, f2fl(fixdiv((chars_sent*F1_0),last_comm_time)) ));
861 // multi_send_robot_frame(1);
865 if (!Control_center_destroyed && (Function_mode == FMODE_GAME) && (SerialLastMessage+SERIAL_IDLE_TIMEOUT < GameTime))
867 SerialLastMessage = 0x7fffffff-SERIAL_IDLE_TIMEOUT; // Give no further warnings until next message arrives!
868 nm_messagebox(TXT_WARNING, 1, TXT_OK, TXT_CONNECT_LOST, Players[OtherPlayer].callsign);
875 com_check_message(char *checkbuf, int len)
880 //mprintf ((0,"TYPE=%d!\n",checkbuf[0]));
884 mprintf((0, "message type %d too short to be a real message!\n", checkbuf[0]));
888 if (checkbuf[0] > MULTI_MAX_TYPE)
890 mprintf((0, "message type %d out of range.\n", checkbuf[0]));
894 if ( (len-3) != message_length[checkbuf[0]])
896 mprintf((0, "id:%d message length %d != %d.\n",checkbuf[0], len-3, message_length[checkbuf[0]]));
900 check = netmisc_calc_checksum(checkbuf, len-2);
901 if (check != *(ushort *)(checkbuf+(len-2)))
904 mprintf((0, "error in message type %d, length %d, checksum %d != %d\n", checkbuf[0], len, check, *(ushort *)(checkbuf+(len-2))));
909 seqnum = checkbuf[(len-3)];
911 if (seqnum == rx_seqnum)
913 mprintf ((0,"returning due to bad checksum! type=%d seq=%d rx=%d\n",checkbuf[0],seqnum,rx_seqnum));
917 if (seqnum != (rx_seqnum+1)%256)
920 mprintf((0, "Warning, missed 1 or more messages.\n"));
924 //mprintf((0, "message type %d len %d OK!\n", checkbuf[0], len));
928 mprintf((1,"Line status: %d.\n", GetLineStatus(com_port)));
929 ClearLineStatus(com_port);
933 codex(code_11s, code_11e)
936 com_process_menu(char *buf, int len)
942 mprintf((0, "com_process_menu: type %d.\n", buf[0]));
948 sprintf(text, "%s %s\n'%s'", Players[OtherPlayer].callsign, TXT_SAYS, buf+2);
950 // sprintf(text, "%s %s\n'%s'", Players[OtherPlayer].callsign, TXT_SAYS, buf+3);
952 nm_messagebox(NULL, 1, TXT_OK, text);
954 case MULTI_MENU_CHOICE:
955 other_menu_choice = buf[1];
956 mprintf((0, "Other menu choice = %d.\n", other_menu_choice));
958 case MULTI_BEGIN_SYNC:
959 // If we get a sync at the menu, send an abort sync, we're not ready yet!
960 serial_sync_abort(0);
966 com_process_input(void)
968 // Read all complete messages from the serial buffer and process
969 // the contents. Messages are read into global array snycbuffer.
972 int entry_com_mode = com_process_mode;
979 if (Game_mode & GM_MODEM)
981 if (!GetCd(com_port))
986 if (!multi_in_menu) {
990 multi_leave_menu = 1;
994 if (com_process_mode != entry_com_mode)
996 mprintf((0, "Exiting com_process_input due to mode switch.\n"));
1000 while ( (len <= MAX_MULTI_MESSAGE_LEN) && (dat = com_getchar()) > -1) // Returns -1 when serial pipe empty
1002 syncbuffer[len++] = dat;
1005 if ((dat == -2) || (len > MAX_MULTI_MESSAGE_LEN)) // Returns -2 when end of message reached
1008 SerialLastMessage = GameTime;
1010 if (!com_check_message(syncbuffer, len))
1012 switch(com_process_mode)
1014 case COM_PROCESS_NORMAL:
1015 case COM_PROCESS_ENDLEVEL:
1016 multi_process_data(syncbuffer, len); break;
1017 case COM_PROCESS_MENU:
1018 if (!Endlevel_sequence) com_process_menu(syncbuffer, len); break;
1019 case COM_PROCESS_SYNC:
1020 if (!Endlevel_sequence) com_process_sync(syncbuffer, len); break;
1022 Int3(); // Bad com_process_mode switch set!
1025 else mprintf ((0,"Huh?\n"));
1030 if (dat == -3) // Returns -3 when carrier lost
1043 my_sync.type = MULTI_BEGIN_SYNC;
1044 my_sync.difficulty = 0;
1045 memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
1046 my_sync.seg_checksum = 0;
1047 my_sync.game_mode = Game_mode;
1048 my_sync.level_num = 0;
1051 mprintf((0, "com_connect()\n"));
1055 return(-1); // Failure in sync
1060 mprintf((0, "My rand = %d, other rand = %d.\n", my_sync.sync_time, other_sync.sync_time));
1063 // Figure out who is the master
1064 if (my_sync.sync_time > other_sync.sync_time)
1066 mprintf((0, "Swtiching player to master.\n"));
1068 change_playernum_to(0);
1070 else if (my_sync.sync_time < other_sync.sync_time)
1072 mprintf((0, "Switching player to slave.\n"));
1074 change_playernum_to(1);
1077 return(-1); // Didn't sync properly, try again
1080 // Copy the remote sync data into local variables
1082 OtherPlayer = (Player_num+1)%2;
1083 mprintf((0, "Other player is #%d.\n", OtherPlayer));
1084 memcpy(Players[OtherPlayer].callsign, other_sync.callsign, CALLSIGN_LEN+1);
1089 #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)
1091 #define MENU_MODEM_CALL 0
1092 #define MENU_MODEM_ANSWER 1
1093 #define MENU_SERIAL_LINK_START 2
1094 #define MENU_SERIAL_SETUP 3
1095 #define MENU_MODEM_HANGUP 4
1096 #define MENU_SERIAL_GAME_START 5
1097 #define MENU_SEND_MESSAGE 6
1099 codex(code_12s, code_12e)
1102 com_menu_poll(int nitems, newmenu_item *menus, int *key, int citem)
1104 // Watch the serial stream if we are connected and take appropriate actions
1112 if (! ( (Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM) ) )
1115 com_process_mode = COM_PROCESS_MENU;
1116 old_game_mode = Game_mode;
1117 other_menu_choice = 0;
1119 com_process_input();
1121 if ((old_game_mode != Game_mode) || other_menu_choice || (com_process_mode != COM_PROCESS_MENU))
1123 if (multi_leave_menu)
1128 com_send_choice(int choice)
1130 sendbuf[0] = (char)MULTI_MENU_CHOICE;
1131 sendbuf[1] = (char)choice;
1133 com_send_data(sendbuf, 2, 1);
1137 com_ready_to_start(void)
1142 m[0].type = m[1].type = NM_TYPE_MENU;
1143 m[0].text = TXT_YES;
1146 choice = newmenu_do1(NULL, TXT_READY_DESCENT, 2, m, com_menu_poll, 0 );
1150 com_send_choice(SELECTION_YES_START);
1151 other_menu_choice = SELECTION_STARTGAME;
1156 com_send_choice(SELECTION_NO_START);
1161 com_process_other_menu_choice(void)
1163 if (other_menu_choice == SELECTION_STARTGAME)
1164 com_ready_to_start();
1165 else if (other_menu_choice == SELECTION_CLOSE_LINK)
1167 nm_messagebox(NULL, 1, TXT_OK, TXT_CLOSED_LINK);
1172 extern void nm_draw_background1(char * filename);
1174 #define SUBTITLE_LEN 120
1180 int menu_choice[10];
1181 int num_options = 0;
1184 char subtitle[SUBTITLE_LEN];
1187 if (com_port_num == -1)
1188 com_load_settings();
1192 //@@gr_set_current_canvas(NULL);
1193 //@@pcx_error = pcx_read_bitmap(Menu_pcx_name,&grd_curcanv->cv_bitmap,grd_curcanv->cv_bitmap.bm_type,NULL);
1194 //@@Assert(pcx_error == PCX_ERROR_NONE);
1196 nm_draw_background1(Menu_pcx_name);
1199 pa_update_clut(gr_palette, 0, 256, 0);
1202 com_process_mode = COM_PROCESS_MENU;
1207 if (! ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)) )
1209 // We haven't established any type of link
1210 ADD_ITEM(TXT_DIAL_MODEM, MENU_MODEM_CALL, KEY_D);
1211 ADD_ITEM(TXT_ANSWER_MODEM, MENU_MODEM_ANSWER, KEY_A);
1212 ADD_ITEM(TXT_NULL_MODEM, MENU_SERIAL_LINK_START, KEY_E);
1213 ADD_ITEM(TXT_COM_SETTINGS, MENU_SERIAL_SETUP, KEY_C);
1217 ADD_ITEM(TXT_START_GAME, MENU_SERIAL_GAME_START, KEY_S);
1218 ADD_ITEM(TXT_SEND_MESSAGEP, MENU_SEND_MESSAGE, KEY_S);
1220 if (Game_mode & GM_MODEM)
1221 ADD_ITEM(TXT_HANGUP_MODEM, MENU_MODEM_HANGUP, KEY_H);
1223 if (Game_mode & GM_SERIAL)
1224 ADD_ITEM(TXT_CLOSE_LINK, MENU_MODEM_HANGUP, KEY_C);
1226 sprintf(subtitle, "Serial/Modem Game\n\n");
1228 if (Game_mode & GM_SERIAL)
1229 sprintf(subtitle+strlen(subtitle), "%s %s\n%s", TXT_SERIAL, TXT_LINK_ACTIVE, Players[OtherPlayer].callsign);
1230 else if (Game_mode & GM_MODEM)
1231 sprintf(subtitle+strlen(subtitle), "%d %s %s %s\n%s", com_baud_rate, TXT_BAUD, TXT_MODEM, TXT_LINK_ACTIVE, Players[OtherPlayer].callsign);
1233 sprintf(subtitle+strlen(subtitle), TXT_NOT_CONNECTED);
1235 multi_leave_menu = 0;
1237 Assert(strlen(subtitle) < SUBTITLE_LEN);
1239 choice = newmenu_do1(NULL, subtitle, num_options, m, com_menu_poll, 0);
1241 mprintf((0, "main menu choice was %d.\n", choice));
1245 if (!((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)))
1250 m[0].text = TXT_YES; m[1].text = TXT_NO;
1251 m[0].type = m[1].type = NM_TYPE_MENU;
1253 choice = newmenu_do1(NULL, TXT_EXIT_WILL_CLOSE, 2, m, com_menu_poll, 0);
1256 com_send_choice(SELECTION_CLOSE_LINK);
1260 if ((choice == -2) && (other_menu_choice))
1261 com_process_other_menu_choice();
1268 // Menu poll loop caused a re-draw
1269 if (other_menu_choice == SELECTION_STARTGAME)
1270 com_ready_to_start();
1271 else if (other_menu_choice == SELECTION_CLOSE_LINK)
1273 nm_messagebox(NULL, 1, TXT_OK, TXT_CLOSED_LINK);
1277 if (Function_mode == FMODE_GAME)
1281 Game_mode = GM_GAME_OVER;
1288 old_game_mode=Game_mode;
1289 switch (menu_choice[choice])
1291 case MENU_SERIAL_SETUP:
1295 case MENU_SERIAL_GAME_START:
1297 if (Function_mode != FMODE_GAME)
1300 case MENU_MODEM_CALL:
1304 case MENU_MODEM_ANSWER:
1308 case MENU_SEND_MESSAGE:
1309 multi_send_message_dialog();
1310 if (Network_message_reciever != -1)
1311 multi_send_message();
1312 multi_sending_message = 0;
1315 case MENU_SERIAL_LINK_START:
1316 serial_link_start();
1319 case MENU_MODEM_HANGUP:
1330 codex(code_13s, code_13e)
1332 void com_custom_param_setup(void)
1334 // User menu for setting up custom IRQ/Base settings for a COM port
1342 int new_irq, new_base;
1343 int menu_save, menu_reset;
1346 sprintf(title, "%s%d", TXT_COM_CUSTOM_SETTINGS, com_port_num+1);
1348 if (com_port_num != com_custom_port)
1350 new_irq = default_irq[com_port_num];
1351 new_base = default_base[com_port_num];
1355 new_irq = com_custom_irq;
1356 new_base = com_custom_base;
1360 sprintf(base, "%x", new_base);
1361 sprintf(irq, "%d", new_irq);
1364 mm[loc].type = NM_TYPE_TEXT; mm[loc].text = TXT_COM_BASE; loc++;
1365 mm[loc].type = NM_TYPE_INPUT; mm[loc].text = base; mm[loc].text_len = 9; loc++;
1366 mm[loc].type = NM_TYPE_TEXT; mm[loc].text = TXT_COM_IRQ; loc++;
1367 mm[loc].type = NM_TYPE_INPUT; mm[loc].text = irq, mm[loc].text_len = 2; loc++;
1369 mm[loc].type = NM_TYPE_MENU; mm[loc].text = TXT_RESET_DEFAULTS; loc++;
1371 mm[loc].type = NM_TYPE_MENU; mm[loc].text = TXT_ACCEPT; loc++;
1373 mmn = newmenu_do1(NULL, title, loc, mm, NULL, menu_save);
1376 return; // All changes lost
1378 new_irq = strtol(irq, NULL, 0);
1379 new_base = strtol(base, NULL, 16);
1381 if (mmn == menu_reset)
1383 new_irq = default_irq[com_port_num];
1384 new_base = default_base[com_port_num];
1386 if (mmn == menu_save)
1388 if ((new_irq == default_irq[com_port_num]) && (new_base == default_base[com_port_num])) {
1389 com_custom_port = -1;
1390 mprintf((0, "Custom com settings not changed.\n"));
1393 if ((new_irq < IRQ2) || (new_irq > IRQ7)) {
1394 new_irq = default_irq[com_port_num];
1395 nm_messagebox(NULL, 1, TXT_OK, TXT_VALID_IRQS);
1399 com_custom_irq = new_irq;
1400 com_custom_base = new_base;
1401 com_custom_port = com_port_num;
1407 void com_param_setup_poll(int nitems, newmenu_item *menus, int *key, int citem)
1413 if ((com_custom_port == -1) && menus[4].value)
1415 menus[4].value = 0; menus[4].redraw = 1;
1419 if (com_custom_port == -1)
1422 if (menus[com_custom_port].value && !menus[4].value)
1424 menus[4].value = 1; menus[4].redraw = 1;
1426 else if (menus[4].value && !menus[com_custom_port].value)
1428 menus[4].value = 0; menus[4].redraw = 1;
1433 void com_param_setup(void)
1436 int was_enabled = 0;
1437 newmenu_item mm[20];
1439 int menu_baud, menu_custom, menu_save,menu_choose_string;
1442 strcpy (ModemInitString, modem_init_string);
1454 mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM1); mm[loc].text="COM1"; mm[loc].group=0; loc++;
1455 mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM2); mm[loc].text="COM2"; mm[loc].group=0; loc++;
1456 mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM3); mm[loc].text="COM3"; mm[loc].group=0; loc++;
1457 mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM4); mm[loc].text="COM4"; mm[loc].group=0; loc++;
1459 mm[loc].type=NM_TYPE_CHECK; mm[loc].value=(com_port_num == com_custom_port); mm[loc].text=TXT_COM_CUSTOM_SETTINGS; loc++;
1460 mm[loc].type=NM_TYPE_TEXT; mm[loc].text = TXT_BAUD_RATE; loc++;
1462 mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_speed == 9600); mm[loc].text="9600"; mm[loc].group=1; loc++;
1463 mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_speed == 19200); mm[loc].text="19200"; mm[loc].group=1; loc++;
1464 mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_speed == 38400); mm[loc].text="38400"; mm[loc].group=1; loc++;
1466 menu_choose_string=loc;
1467 mm[loc].type=NM_TYPE_MENU; mm[loc].text = "Choose modem init string"; loc++;
1469 mm[loc].type=NM_TYPE_TEXT; mm[loc].text = TXT_MODEM_INIT_STRING; loc++;
1470 mm[loc].type=NM_TYPE_INPUT; mm[loc].text_len = INIT_STRING_LEN; mm[loc].text = ModemInitString; loc++;
1472 mm[loc].type=NM_TYPE_MENU; mm[loc].text = TXT_ACCEPT_SAVE; loc++;
1474 mmn = newmenu_do1(NULL, TXT_SERIAL_SETTINGS, loc, mm, com_param_setup_poll, menu_save);
1480 com_port_num = COM1;
1481 else if (mm[1].value)
1482 com_port_num = COM2;
1483 else if (mm[2].value)
1484 com_port_num = COM3;
1486 com_port_num = COM4;
1488 if (mmn == menu_custom)
1490 com_custom_param_setup();
1492 if (mmn==menu_choose_string)
1494 com_choose_string();
1499 com_type = com_type_detect();
1503 nm_messagebox(NULL, 1, TXT_OK, "%s\n%s", TXT_WARNING, TXT_NO_UART);
1506 if ((mm[menu_baud].value) || (mmn == menu_baud))
1508 else if ((mm[menu_baud+1].value) || (mmn == menu_baud+1))
1512 if (com_type == 16550)
1516 nm_messagebox(NULL, 1, TXT_OK, TXT_WARNING_16550);
1521 //mprintf((0, "%s\n", ModemInitString));
1523 if ((strnicmp("AT", ModemInitString, 2)) && (strlen(ModemInitString) < (INIT_STRING_LEN-2)))
1524 sprintf(modem_init_string, "AT%s", ModemInitString);
1526 strcpy(modem_init_string, ModemInitString);
1528 if (mmn != menu_save)
1536 com_save_settings();
1540 codex(code_14s, code_14e)
1542 // Handshaking to start a serial game, 2 players only
1544 int com_start_game_menu(void)
1549 int opt, diff_opt, mode_opt, options_opt,allow_opt,extraopts_opt;
1550 char level_text[32];
1553 int new_mission_num, anarchy_only = 0;
1555 new_mission_num = multi_choose_mission(&anarchy_only);
1557 if (new_mission_num < 0)
1560 strcpy(my_sync.mission_name, Mission_list[new_mission_num].filename);
1563 sprintf(level, "1");
1565 Game_mode &= ~GM_MULTI_COOP;
1566 Game_mode &= ~GM_MULTI_ROBOTS;
1567 Game_mode &= ~GM_CAPTURE;
1568 Netgame.game_flags = 0;
1569 SetAllAllowablesTo(1);
1571 sprintf(level_text, "%s (1-%d)", TXT_LEVEL_, Last_level);
1572 // if (Last_secret_level < -1)
1573 // sprintf(level_text+strlen(level_text)-1, ", S1-S%d)", -Last_secret_level);
1574 // else if (Last_secret_level == -1)
1575 // sprintf(level_text+strlen(level_text)-1, ", S1)");
1577 Assert(strlen(level_text) < 32);
1579 // Put up menu for user choices controlling gameplay
1583 m[opt].type = NM_TYPE_TEXT; m[opt].text = level_text; opt++;
1584 m[opt].type = NM_TYPE_INPUT; m[opt].text_len = 4; m[opt].text = level; opt++;
1585 m[opt].type = NM_TYPE_TEXT; m[opt].text = TXT_MODE;
1587 m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_ANARCHY; m[opt].value=!(Game_mode & GM_MULTI_ROBOTS); m[opt].group = 0; opt++;
1588 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++;
1589 m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_COOPERATIVE; m[opt].value=(Game_mode & GM_MULTI_COOP);m[opt].group = 0; opt++;
1590 m[opt].type = NM_TYPE_RADIO; m[opt].text = "Capture the Flag"; m[opt].value=(Game_mode & GM_CAPTURE); m[opt].group = 0; opt++;
1592 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++;
1593 m[opt].type = NM_TYPE_TEXT; m[opt].text = " "; opt ++;
1597 m[opt].type = NM_TYPE_CHECK; m[opt].text = "Marker camera views"; m[opt].value=Netgame.Allow_marker_view; opt++;
1598 m[opt].type = NM_TYPE_CHECK; m[opt].text = "Indestructable lights"; m[opt].value=Netgame.AlwaysLighting; opt++;
1601 m[opt].type = NM_TYPE_MENU; m[opt].text = "Choose objects allowed"; opt++;
1606 // m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_IDS; m[opt].value=0; opt++;
1607 m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_ON_MAP; m[opt].value=0; opt++;
1614 choice = newmenu_do1(NULL, TXT_SERIAL_GAME_SETUP, opt, m, NULL, 1);
1619 if (choice==allow_opt)
1621 network_set_power();
1624 Netgame.Allow_marker_view=m[extraopts_opt].value;
1625 Netgame.AlwaysLighting=m[extraopts_opt+1].value;
1627 if (m[mode_opt].value)
1629 mprintf ((0,"Undoing capture in modem.c\n"));
1630 Game_mode &= ~(GM_MULTI_COOP | GM_MULTI_ROBOTS | GM_CAPTURE);
1634 nm_messagebox(NULL, 1, TXT_OK, TXT_ONLY_ANARCHY);
1638 else if (anarchy_only) {
1639 if (m[mode_opt+3].value)
1641 mprintf ((0,"Setting capture 1\n"));
1642 Game_mode |= (GM_CAPTURE);
1646 nm_messagebox(NULL, 1, TXT_OK, TXT_ANARCHY_ONLY_MISSION);
1650 else if (m[mode_opt+1].value)
1652 Game_mode &= ~GM_MULTI_COOP;
1653 Game_mode |= GM_MULTI_ROBOTS;
1655 else if (m[mode_opt+2].value)
1656 Game_mode |= (GM_MULTI_COOP | GM_MULTI_ROBOTS);
1657 else if (m[mode_opt+3].value)
1659 mprintf ((0,"Setting capture 2\n"));
1660 Game_mode |= (GM_CAPTURE);
1663 // if (m[options_opt].value)
1664 // Netgame.game_flags |= NETGAME_FLAG_SHOW_ID;
1665 if (m[options_opt].value)
1666 Netgame.game_flags |= NETGAME_FLAG_SHOW_MAP;
1668 start_level_num = atoi(level);
1670 if ((start_level_num < 1) || (start_level_num > Last_level))
1672 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_LEVEL_OUT_RANGE);
1673 sprintf(level, "1");
1677 Difficulty_level = m[diff_opt].value;
1679 return(1); // Go for game!
1681 return 0; // No game
1687 // Ask the other player if its OK to start now
1692 com_send_choice(SELECTION_STARTGAME);
1694 m[0].type = NM_TYPE_TEXT; m[0].text = TXT_ESC_ABORT;
1696 choice = newmenu_do(NULL, TXT_WAIT_FOR_OK, 1, m, com_menu_poll);
1700 com_send_choice(SELECTION_STARTGAME_ABORT);
1705 if (other_menu_choice == SELECTION_YES_START)
1707 else if (other_menu_choice == SELECTION_STARTGAME)
1709 com_send_choice(SELECTION_YES_START);
1718 codex(code_15s, code_15e)
1723 // Start a serial game after being linked
1725 mprintf((0, "Entered com_start_game\n"));
1729 if (! ( (Game_mode & GM_MODEM) || (Game_mode & GM_SERIAL) ) )
1732 Assert (master != -1);
1734 if (other_menu_choice != SELECTION_STARTGAME)
1736 if (!com_ask_to_start())
1740 if (master == 1) // Master chooses options
1742 if (com_start_game_menu())
1745 change_playernum_to(0);
1746 my_sync.level_num = start_level_num;
1747 my_sync.difficulty = Difficulty_level;
1748 my_sync.game_mode = Game_mode;
1749 if (Game_mode & GM_CAPTURE)
1750 my_sync.game_mode |=GM_UNKNOWN;
1751 memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
1753 my_sync.sync_time = control_invul_time;
1754 my_sync.game_flags = Netgame.game_flags;
1755 Netgame.control_invul_time = control_invul_time;
1756 memcpy ((&my_sync.game_flags)+1,(&Netgame.team_vector)+1,4);
1760 if (Game_mode & GM_CAPTURE)
1761 my_sync.game_mode &=GM_UNKNOWN;
1767 change_playernum_to(1);
1768 memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
1770 my_sync.level_num = 1;
1773 if (com_process_mode == COM_PROCESS_NORMAL)
1775 Difficulty_level = other_sync.difficulty;
1776 start_level_num = other_sync.level_num;
1777 Game_mode = other_sync.game_mode;
1778 if (Game_mode & GM_UNKNOWN) // Super HACK! Out of bit fields, doubling up
1780 Game_mode &=~GM_UNKNOWN;
1781 Game_mode |=GM_CAPTURE;
1784 memcpy ((&Netgame.team_vector)+1,(&other_sync.game_flags)+1,4);
1787 Netgame.game_flags = other_sync.game_flags;
1788 Netgame.control_invul_time = other_sync.sync_time;
1789 if (!load_mission_by_name(other_sync.mission_name))
1791 mprintf((0, "Mission not found: %s!\n", other_sync.mission_name));
1792 nm_messagebox(NULL, 1, TXT_OK, TXT_MISSION_NOT_FOUND);
1793 my_sync.sync_id = start_level_num;
1794 serial_sync_abort(0);
1800 if (com_process_mode != COM_PROCESS_NORMAL)
1803 memcpy(Players[OtherPlayer].callsign, other_sync.callsign, CALLSIGN_LEN+1);
1804 Function_mode = FMODE_GAME;
1805 Game_mode &= ~GM_GAME_OVER;
1807 init_player_stats_game();
1808 init_player_stats_level(0);
1809 // Assert(start_level_num > 0);
1810 Assert((start_level_num >= Last_secret_level) && (start_level_num <= Last_level));
1811 StartNewLevel(start_level_num, 0);
1815 // Modem control functions, dialing, answering, etc.
1818 void modem_edit_phonebook(newmenu_item *m)
1820 int choice, choice2;
1821 newmenu_item menu[5];
1823 int default_choice = 0;
1825 m[NUM_PHONE_NUM].text = TXT_SAVE;
1827 menu[0].text = TXT_NAME; menu[0].type = NM_TYPE_TEXT;
1828 menu[1].type = NM_TYPE_INPUT; menu[1].text = text[0]; menu[1].text_len = LEN_PHONE_NAME;
1829 menu[2].text = TXT_PHONE_NUM; menu[2].type = NM_TYPE_TEXT;
1830 menu[3].type = NM_TYPE_INPUT; menu[3].text = text[1]; menu[3].text_len = LEN_PHONE_NUM;
1831 menu[4].text = TXT_ACCEPT; menu[4].type = NM_TYPE_MENU;
1834 choice = newmenu_do1(NULL, TXT_SEL_NUMBER_EDIT, NUM_PHONE_NUM+1, m, NULL, default_choice);
1837 com_load_settings();
1840 if (choice == NUM_PHONE_NUM)
1843 com_save_settings();
1850 strcpy(menu[1].text, phone_name[choice]);
1851 strcpy(menu[3].text, phone_num[choice]);
1853 choice2 = newmenu_do1(NULL, TXT_EDIT_PHONE_ENTRY, 5, menu, NULL, default_choice);
1856 strcpy(phone_name[choice], menu[1].text);
1857 strcpy(phone_num[choice], menu[3].text);
1858 sprintf(m[choice].text, "%d. %s \t", choice+1, phone_name[choice]);
1859 add_phone_number(m[choice].text, phone_num[choice] );
1863 default_choice += 2; if (default_choice > 4) default_choice = 4;
1867 default_choice = NUM_PHONE_NUM;
1872 codex(code_16s, code_16e)
1874 void add_phone_number( char * src, char * num )
1887 strcat( src, "..." );
1890 int modem_dial_menu(void)
1892 newmenu_item m[NUM_PHONE_NUM+2];
1893 char menu_text[NUM_PHONE_NUM][80];
1898 for (i = 0; i < NUM_PHONE_NUM; i++)
1900 m[i].text = menu_text[i];
1901 sprintf(m[i].text, "%d. %s \t", i+1, phone_name[i]);
1902 add_phone_number(m[i].text, phone_num[i] );
1903 m[i].type = NM_TYPE_MENU;
1906 strcat(m[i-1].text, "\n");
1908 m[NUM_PHONE_NUM].type = NM_TYPE_MENU;
1909 m[NUM_PHONE_NUM].text = TXT_MANUAL_ENTRY;
1910 m[NUM_PHONE_NUM+1].text = TXT_EDIT_PHONEBOOK;
1911 m[NUM_PHONE_NUM+1].type = NM_TYPE_MENU;
1913 choice = newmenu_do1(NULL, TXT_SEL_NUMBER_DIAL, NUM_PHONE_NUM+2, m, NULL, 0);
1915 return -1; // user abort
1917 if (choice == NUM_PHONE_NUM+1)
1920 modem_edit_phonebook(m);
1924 if (choice == NUM_PHONE_NUM)
1928 m2[0].type = NM_TYPE_INPUT; m2[0].text = phone_num[NUM_PHONE_NUM]; m2[0].text_len = LEN_PHONE_NUM;
1929 choice = newmenu_do(NULL, TXT_ENTER_NUMBER_DIAL, 1, m2, NULL);
1933 return NUM_PHONE_NUM;
1936 // A phone number was chosen
1941 com_wait_for_connect(int nitems, newmenu_item *menus, int *key, int citem)
1944 char input_buffer[81];
1946 char error_mess[5][15] =
1955 int num_error_messages = 5;
1962 if (GetCd(com_port))
1971 nm_messagebox(NULL, 1, TXT_OK, TXT_CARRIER_LOST);
1977 result = HMInputLine(com_port, 500, input_buffer, 80);
1980 return; // Timed out
1982 mprintf((0, "Modem string: '%s'\n", input_buffer));
1984 for (i = 0; i < num_error_messages; i++)
1986 if (!strncmp(input_buffer, error_mess[i], strlen(error_mess[i])))
1988 sprintf(text, "%s %s", TXT_ERR_MODEM_RETURN, input_buffer);
1989 nm_messagebox(NULL, 1, TXT_OK, text);
1995 if (strncmp(input_buffer, TXT_CONNECT, 7))
1997 mprintf((0, "Non-connect message found.\n"));
1998 return; // some other string. Not an error, but not a connect
2001 sscanf(input_buffer, "CONNECT %d", &baud);
2003 mprintf((0, "Connect at %d baud.\n", baud));
2007 nm_messagebox(NULL, 1, TXT_OK, TXT_BAUD_GREATER_9600);
2012 com_baud_rate = baud;
2018 codex(code_17s, code_17e)
2021 com_wait_for_ring(int nitems, newmenu_item *menus, int *key, int citem)
2024 char input_buffer[81];
2030 result = HMInputLine(com_port, 500, input_buffer, 80);
2032 if ((result <= 0) || strncmp(input_buffer, TXT_RING, 4))
2041 int modem_verify(void)
2043 // Is there a modem on this com port or not?
2047 HMWaitForOK( 5000, NULL);
2049 HMSendString(com_port, "AT");
2050 result = HMSendString(com_port, "AT");
2057 void modem_dialout(void)
2065 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT);
2069 com_enable(); // Open COM port as configured
2074 // UseRtsCts(com_port, ON); // use hardware handshaking
2077 if ((choice = modem_dial_menu()) == -1)
2078 return; // user aborted
2080 show_boxed_message(TXT_RESET_MODEM);
2082 // Verify presence of modem
2083 if (!modem_verify())
2085 clear_boxed_message();
2086 nm_messagebox(NULL, 1, TXT_OK, TXT_NO_MODEM);
2091 if (strlen(phone_num[choice]) == 0)
2093 clear_boxed_message();
2094 nm_messagebox(NULL, 1, TXT_OK, TXT_NO_PHONENUM);
2098 clear_boxed_message();
2100 sprintf(text, "%s\n%s", TXT_DIALING, phone_num[choice]);
2101 show_boxed_message(text);
2103 // Proceed with the call
2105 HMReset( com_port );
2107 HMSendString( com_port, modem_init_string );
2109 HMDial( com_port, phone_num[choice] );
2113 clear_boxed_message();
2115 m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT;
2118 choice = newmenu_do(NULL, TXT_WAITING_FOR_ANS, 1, m, com_wait_for_connect);
2120 HMSendStringNoWait(com_port, "", -2);
2125 // We are now connected to the other modem
2129 my_sync.sync_time=32000;
2130 // -- commented out by mk, 2/21/96 -- master = 1; // The person who dialed is the master of the connection
2131 change_playernum_to(0);
2135 Game_mode |= GM_MODEM;
2136 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
2139 HMSendStringNoWait(com_port, "", -2);
2144 codex(code_18s, code_18e)
2146 void modem_answer(void)
2153 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT);
2157 com_enable(); // Open COM port as configured
2162 // UseRtsCts(com_port, ON); // use hardware handshaking
2164 show_boxed_message(TXT_RESET_MODEM);
2166 // Verify presence of modem
2167 if (!modem_verify())
2169 clear_boxed_message();
2170 nm_messagebox(NULL, 1, TXT_OK, TXT_NO_MODEM);
2175 HMReset( com_port );
2177 HMSendString( com_port, modem_init_string );
2179 HMSendString( com_port, "AT"); // To set the DTE rate for RING notification
2181 clear_boxed_message();
2183 m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT;
2186 choice = newmenu_do(NULL, TXT_WAITING_FOR_CALL, 1, m, com_wait_for_ring);
2188 HMSendStringNoWait(com_port, "", -2);
2195 // Now answer the phone and wait for carrier
2201 choice = newmenu_do(NULL, TXT_WAITING_FOR_CARR, 1, m, com_wait_for_connect);
2203 HMSendStringNoWait(com_port, "", -2);
2208 // We are now connected to the other modem
2212 my_sync.sync_time=0;
2213 // -- commented out by mk, 2/21/96 -- master = 0;
2214 change_playernum_to(1);
2218 Game_mode |= GM_MODEM;
2219 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
2222 HMSendStringNoWait(com_port, "", -2);
2227 void serial_link_start(void)
2231 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT);
2235 com_enable(); // Open COM port as configured
2245 my_sync.sync_time = rand();
2246 mprintf((0, "My rand set to %d.\n", my_sync.sync_time));
2250 Game_mode |= GM_SERIAL;
2251 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
2255 nm_messagebox(NULL, 1, TXT_OK, "%s\n%s", TXT_ERROR, TXT_FAILED_TO_NEGOT);
2260 // Syncronization functions
2264 serial_sync_abort(int val)
2266 // Send "I got Sync but it was no good!" packet
2268 sendbuf[0] = (char)MULTI_END_SYNC;
2269 sendbuf[1] = Player_num;
2270 sendbuf[2] = (char)val; // Indicated failure
2272 sendbuf[3] = my_sync.sync_id;
2273 com_send_data(sendbuf, 4, 1);
2275 // com_send_data(sendbuf, 3, 1);
2280 com_level_sync(void)
2282 // Send between-level sync stuff
2284 mprintf((0, "entered com_level_sync()\n"));
2286 Function_mode = FMODE_MENU; // Prevent the game loop from running during the menus!
2288 // At this point, the new level is loaded but the extra objects or players have not
2291 my_sync.level_num = Current_level_num;
2292 my_sync.seg_checksum = netmisc_calc_checksum(Segments, (Highest_segment_index+1) * sizeof(segment));
2293 my_sync.kills[0] = kill_matrix[Player_num][0];
2294 my_sync.kills[1] = kill_matrix[Player_num][1];
2295 my_sync.proto_version = MULTI_PROTO_VERSION;
2297 my_sync.killed = Players[Player_num].net_killed_total;
2301 if (Game_mode & GM_MULTI_COOP)
2302 my_sync.difficulty = Player_num;
2304 my_sync.difficulty = rand()%MAX_NUM_NET_PLAYERS; // My starting position
2306 if (com_sync(Current_level_num))
2308 com_process_mode = COM_PROCESS_MENU;
2309 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NEGOTIATION_FAIL );
2310 gr_palette_clear ();
2311 longjmp(LeaveGame, 0);
2314 if (my_sync.level_num != other_sync.level_num)
2317 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);
2318 gr_palette_clear ();
2319 longjmp(LeaveGame, 0);
2322 if (my_sync.seg_checksum != other_sync.seg_checksum)
2325 mprintf((1, "My check %d, other check %d.\n", my_sync.seg_checksum, other_sync.seg_checksum));
2326 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);
2327 gr_palette_clear ();
2330 longjmp(LeaveGame, 0);
2334 if (my_sync.proto_version != other_sync.proto_version)
2337 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_DESCENT_NO_MATCH);
2338 gr_palette_clear ();
2339 longjmp(LeaveGame, 0);
2342 mprintf((0, "My pos = %d, other pos = %d.\n", my_sync.difficulty, other_sync.difficulty));
2344 if ((other_sync.difficulty == my_sync.difficulty) && !master)
2346 // If we chose the same position and I am the slave, choose another
2347 my_sync.difficulty = (my_sync.difficulty+1) % MAX_NUM_NET_PLAYERS;
2350 Objects[Players[OtherPlayer].objnum].pos = Player_init[other_sync.difficulty].pos;
2351 Objects[Players[OtherPlayer].objnum].orient = Player_init[other_sync.difficulty].orient;
2352 obj_relink(Players[OtherPlayer].objnum,Player_init[other_sync.difficulty].segnum);
2353 Objects[Players[OtherPlayer].objnum].type = OBJ_PLAYER;
2355 Objects[Players[Player_num].objnum].pos = Player_init[my_sync.difficulty].pos;
2356 Objects[Players[Player_num].objnum].orient = Player_init[my_sync.difficulty].orient;
2357 obj_relink(Players[Player_num].objnum, Player_init[my_sync.difficulty].segnum);
2358 Objects[Players[Player_num].objnum].type = OBJ_PLAYER;
2360 SerialLastMessage = GameTime;
2362 kill_matrix[OtherPlayer][0] = other_sync.kills[0];
2363 kill_matrix[OtherPlayer][1] = other_sync.kills[1];
2364 Players[Player_num].net_kills_total = kill_matrix[Player_num][OtherPlayer] - kill_matrix[Player_num][Player_num];
2365 Players[OtherPlayer].net_kills_total = kill_matrix[OtherPlayer][Player_num] - kill_matrix[OtherPlayer][OtherPlayer];
2366 // Players[Player_num].net_killed_total = kill_matrix[0][Player_num] + kill_matrix[1][Player_num];
2367 // Players[OtherPlayer].net_killed_total = kill_matrix[0][OtherPlayer] + kill_matrix[1][OtherPlayer];
2368 Players[OtherPlayer].net_killed_total = other_sync.killed;
2369 Players[OtherPlayer].connected = Players[Player_num].connected = 1;
2371 Assert(N_players == 2);
2372 Assert(Player_num != OtherPlayer);
2374 gr_palette_fade_out(gr_palette, 32, 0);
2376 Function_mode = FMODE_GAME;
2377 com_process_mode = COM_PROCESS_NORMAL;
2378 multi_sort_kill_list();
2382 codex(code_19s, code_19e)
2385 com_send_end_sync(void)
2387 // Send "I got Sync" packet
2389 sendbuf[0] = (char)MULTI_END_SYNC;
2390 sendbuf[1] = Player_num;
2391 sendbuf[2] = 1; // Indicates success
2393 sendbuf[3] = my_sync.sync_id;
2394 com_send_data(sendbuf, 4, 2);
2396 // com_send_data(sendbuf, 3, 2);
2401 com_send_begin_sync(void)
2403 mprintf((0, "Sending my sync.\n"));
2404 com_send_data((char *)&my_sync, sizeof(com_sync_pack)-3, 1);
2408 com_process_end_sync(byte *buf)
2410 // Process incoming end-sync packet
2413 com_process_mode = COM_PROCESS_MENU;
2418 if (buf[3] == my_sync.sync_id)
2424 com_process_sync(char *buf, int len)
2426 mprintf ((0,"Processing sync!"));
2431 case MULTI_END_SYNC:
2433 com_process_end_sync(buf);
2436 case MULTI_BEGIN_SYNC:
2438 mprintf((0, "Incoming begin_sync message.\n"));
2442 memcpy(&other_sync, buf, sizeof(com_sync_pack)-3);
2444 if (other_sync.sync_id != my_sync.sync_id)
2446 mprintf((0, "Other sync invalid id, %d != %d.\n", other_sync.sync_id, my_sync.sync_id));
2451 mprintf((0, "got other sync size %d.\n", sizeof(com_sync_pack)-3));
2453 com_send_end_sync();
2459 if (got_sync && other_got_sync)
2462 mprintf((1, "Starting game.\n"));
2465 com_process_mode = COM_PROCESS_NORMAL;
2472 // Send sync information, depending on the situation
2474 if (!other_got_sync)
2476 com_send_begin_sync();
2480 com_send_end_sync();
2484 codex(code_20s, code_20e)
2486 void com_sync_poll(int nitems, newmenu_item *menus, int *key, int citem)
2500 if (timer_get_approx_seconds() > t1+F1_0)
2503 t1 = timer_get_approx_seconds();
2506 Assert(com_process_mode == COM_PROCESS_SYNC);
2508 com_process_input();
2510 if (com_process_mode == COM_PROCESS_NORMAL)
2514 mprintf((0, "Sync finished.\n"));
2517 if (com_process_mode == COM_PROCESS_MENU)
2520 mprintf((0, "Sync denied by other side.\n"));
2529 // How to handle the end of the level and start of the next level
2530 // returns 0 for success or 1 for failure
2536 mprintf((0, "Entered com_sync\n"));
2538 //@@gr_set_current_canvas(NULL);
2539 //@@pcx_error = pcx_read_bitmap(Menu_pcx_name, &grd_curcanv->cv_bitmap,grd_curcanv->cv_bitmap.bm_type,NULL);
2540 //@@Assert(pcx_error == PCX_ERROR_NONE);
2542 com_process_mode = COM_PROCESS_SYNC;
2543 got_sync = other_got_sync = 0;
2549 my_sync.sync_id = id;
2552 m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT;
2553 m[1].type = m[2].type = NM_TYPE_MENU;
2554 m[1].text = TXT_YES;
2558 choice = newmenu_do(NULL, TXT_WAIT_OPPONENT, 1, m, com_sync_poll);
2562 choice = newmenu_do1(NULL, TXT_SURE_ABORT_SYNC, 2, m+1, com_sync_poll, 1);
2569 if ((choice == -1) || (choice == -3)) {
2572 else if (choice != -2)
2579 com_endlevel(int *secret)
2581 // What do we do between levels?
2583 Function_mode = FMODE_MENU;
2585 gr_palette_fade_out(gr_palette, 32, 0);
2587 my_sync.level_num = multi_goto_secret;
2591 com_process_mode = COM_PROCESS_MENU;
2592 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NEGOTIATION_FAIL);
2593 longjmp(LeaveGame, 0);
2596 com_process_mode = COM_PROCESS_ENDLEVEL;
2598 if ((multi_goto_secret == 1) || (other_sync.level_num == 1))
2603 multi_goto_secret = 0;
2608 codex(code_21s, code_21e)
2612 int ReadModemList ()
2614 int num=0,i=0,namemode=1;
2618 if ((MyFile=fopen ("modem.lst","rb"))==NULL)
2621 ModemNames[num][0] = ModemStrings[num][0] = 0;
2623 while (!feof(MyFile))
2627 mprintf ((0,"%c",c));
2638 ModemStrings[num][i]=0;
2641 if (strlen(ModemNames[num]) && strlen(ModemStrings[num]))
2643 if (num >= MAX_MODEMS)
2644 break; //too many! abort!
2645 ModemNames[num][0] = ModemStrings[num][0] = 0;
2649 while (fgetc(MyFile)!=10)
2654 else if (c=='=' && namemode)
2656 ModemNames[num][i]=0;
2664 if (i<MODEM_NAME_LEN)
2665 ModemNames[num][i++]=c;
2669 if (i<INIT_STRING_LEN)
2670 ModemStrings[num][i++]=c;
2675 ModemStrings[num][i]=0; //terminate in case no final newline
2681 void com_choose_string ()
2684 newmenu_item m[MAX_MODEMS];
2686 if ((num=ReadModemList())==0)
2688 nm_messagebox ("Error",1,TXT_OK,"No valid modem file found!");
2692 mprintf ((0,"Number of strings=%d\n",num));
2697 m[i].type = NM_TYPE_MENU; m[i].text = &ModemNames[i];
2698 mprintf ((0,"Text(%d)=%s\n",i,ModemNames[i]));
2700 choice = newmenu_do(NULL, "Choose a modem", num, m, NULL);
2705 strcpy (ModemInitString,ModemStrings[choice]);