home *** CD-ROM | disk | FTP | other *** search
/ Amiga ACS 1998 #6 / amigaacscoverdisc1998-061998.iso / games / descent / source / main / modem.c < prev    next >
Text File  |  1998-06-08  |  65KB  |  2,722 lines

  1. /*
  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-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  12. */
  13. /*
  14.  * $Source: f:/miner/source/main/rcs/modem.c $
  15.  * $Revision: 2.13 $
  16.  * $Author: john $
  17.  * $Date: 1995/11/28 16:25:05 $
  18.  * 
  19.  * Modem support code
  20.  *
  21.  * $Log: modem.c $
  22.  * Revision 2.13  1995/11/28  16:25:05  john
  23.  * Added fixed for Rockwell voice modems thats waits for OK after sending
  24.  * initial AT to modem to detect if there is a modem on the port.
  25.  * 
  26.  * Revision 2.12  1995/06/14  16:32:09  john
  27.  * Fixed bug where all modem games were anarchy.
  28.  * 
  29.  * Revision 2.11  1995/05/29  16:17:59  john
  30.  * Added support for Rockwell that takes out all net modes except for anarchy.
  31.  * 
  32.  * Revision 2.10  1995/04/23  16:06:38  john
  33.  * Moved rinvul into modem/null modem menu.
  34.  * 
  35.  * Revision 2.9  1995/04/09  14:43:20  john
  36.  * Took out mem-overwrite error when > 25 char phone numbers.
  37.  * 
  38.  * Revision 2.8  1995/04/06  12:13:36  john
  39.  * Made phone numbers be 32 characters max.
  40.  * 
  41.  * Revision 2.7  1995/03/31  14:16:33  john
  42.  * Mode phone numbers 30 chars long.
  43.  * 
  44.  * Revision 2.6  1995/03/30  16:39:44  john
  45.  * Incread phone numbers to 25 characters.
  46.  * 
  47.  * Revision 2.5  1995/03/30  16:04:49  john
  48.  * Increased modem number length.
  49.  *  
  50.  * 
  51.  * Revision 2.4  1995/03/29  15:33:23  john
  52.  * Took out RTS/CTS handshaking. Made it enabled with -RTSCTS.
  53.  * 
  54.  * Revision 2.3  1995/03/21  14:41:10  john
  55.  * Ifdef'd out the NETWORK code.
  56.  * 
  57.  * Revision 2.2  1995/03/06  16:47:50  mike
  58.  * destination saturn
  59.  * 
  60.  * Revision 2.1  1995/03/02  15:58:31  john
  61.  * Made menu support modex.
  62.  * 
  63.  * Revision 2.0  1995/02/27  11:32:34  john
  64.  * New version 2.0, which has no anonymous unions, builds with
  65.  * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
  66.  * 
  67.  * Revision 1.157  1995/02/15  15:35:12  john
  68.  * Added code to support modem tracking of kills during endlevel..
  69.  * 
  70.  * Revision 1.156  1995/02/15  15:27:16  john
  71.  * Mode modem code work with kills after you leave mine.
  72.  * 
  73.  * Revision 1.155  1995/02/12  19:53:04  rob
  74.  * Fixed up com flush.
  75.  * 
  76.  * Revision 1.154  1995/02/11  17:13:25  rob
  77.  * Took out code fill stuff.
  78.  * 
  79.  * Revision 1.153  1995/02/11  14:26:25  rob
  80.  * Added support for invul. controlcen.
  81.  * 
  82.  * Revision 1.152  1995/02/09  02:59:44  mike
  83.  * check code for 00066xxx bugs.
  84.  * 
  85.  * Revision 1.151  1995/02/08  19:18:56  rob
  86.  * Took out show IDs on HUD option.
  87.  * 
  88.  * Revision 1.150  1995/02/08  11:30:29  rob
  89.  * Fixed some pretty minor bugs.
  90.  * 
  91.  * Revision 1.149  1995/02/07  20:39:54  rob
  92.  * Added new modem game options for hud id's and automap show.
  93.  * 
  94.  * Revision 1.148  1995/02/06  18:42:09  rob
  95.  * Removed debugging crap.
  96.  * 
  97.  * Revision 1.147  1995/02/06  18:17:17  rob
  98.  * Fixed bug in killed tallys in modem game.
  99.  * 
  100.  * Revision 1.146  1995/02/05  17:04:29  rob
  101.  * Removed performance monitoring debug message.
  102.  * 
  103.  * Revision 1.145  1995/02/05  17:02:48  rob
  104.  * Speeding up cooperative/robo-anarchy modes.
  105.  * 
  106.  * Revision 1.142  1995/02/03  18:09:40  rob
  107.  * Small fixes to modem setup menu..
  108.  * 
  109.  * Revision 1.141  1995/02/03  11:12:15  rob
  110.  * Fixed a bug (again).  Somehow last checkin got lost.
  111.  * 
  112.  * Revision 1.140  1995/02/02  22:06:20  rob
  113.  * Removed a function frm shareware.
  114.  * 
  115.  * Revision 1.139  1995/02/01  23:20:32  rob
  116.  * Put baud rate in connect subtitle.
  117.  * 
  118.  * Revision 1.138  1995/01/30  21:22:11  rob
  119.  * Fixed bug in sync pack memcpy len.
  120.  * 
  121.  * Revision 1.137  1995/01/30  21:18:31  rob
  122.  * Simplified mission loading support.
  123.  * 
  124.  * Revision 1.136  1995/01/30  18:32:08  rob
  125.  * Added include of multibot.h
  126.  * 
  127.  * Revision 1.135  1995/01/30  17:16:04  rob
  128.  * Added new system for sending robots positions in modem games.
  129.  * 
  130.  * Revision 1.134  1995/01/30  15:29:44  rob
  131.  * Localizing.
  132.  * 
  133.  * Revision 1.133  1995/01/29  15:20:21  rob
  134.  * Fixed a bug in secret level sequencing for modem games.
  135.  * 
  136.  * Revision 1.132  1995/01/24  15:48:51  rob
  137.  * fixed error dialog.
  138.  * 
  139.  * Revision 1.131  1995/01/23  18:51:59  rob
  140.  * Added RTS/CTS to null-modem games to help IHHD folks.
  141.  * Fixed a bad assert.
  142.  * Added support for user-written mission files.
  143.  * 
  144.  * Revision 1.130  1995/01/23  16:01:36  rob
  145.  * More reliable syncing.
  146.  * Added mission selection.
  147.  * 
  148.  * Revision 1.129  1995/01/20  22:47:35  matt
  149.  * Mission system implemented, though imcompletely
  150.  * 
  151.  * Revision 1.128  1995/01/19  18:18:05  rob
  152.  * Fixed kill list display.
  153.  * 
  154.  * Revision 1.127  1995/01/19  16:33:48  rob
  155.  * removed ambigous variable name in check_message.
  156.  * 
  157.  * Revision 1.126  1995/01/19  11:56:38  rob
  158.  * Take out Int3 that could never get hit.
  159.  * 
  160.  * Revision 1.125  1995/01/18  19:01:46  rob
  161.  * Added new asserts to check for proper game staus.
  162.  * 
  163.  * 
  164.  * Revision 1.124  1995/01/18  15:39:25  rob
  165.  * Fixed title of serial menu.
  166.  * 
  167.  * Revision 1.123  1995/01/18  11:54:23  rob
  168.  * Fixing a bug with connect status.
  169.  * 
  170.  * Revision 1.122  1995/01/17  22:16:07  rob
  171.  * Changed timer calls.
  172.  * 
  173.  * Revision 1.121  1995/01/17  13:58:31  rob
  174.  * Fixed problem with not allowing non-anarchy modes in registered.
  175.  * 
  176.  * Revision 1.120  1995/01/15  15:46:18  rob
  177.  * Fixed lost character on serial menu messages.
  178.  * 
  179.  * Revision 1.119  1995/01/12  22:03:15  rob
  180.  * Added sound cue when connection is established.
  181.  * 
  182.  * Revision 1.118  1995/01/12  11:42:05  john
  183.  * added better UART detection.
  184.  * /.
  185.  * Works under OS/2 and DOS.
  186.  * 
  187.  * 
  188.  */
  189.  
  190. #ifdef NETWORK
  191.  
  192. #define DOS4G
  193.  
  194. #pragma off (unreferenced)
  195. static char rcsid[] = "$Id: modem.c 2.13 1995/11/28 16:25:05 john Exp $";
  196. #pragma on (unreferenced)
  197.  
  198. #include <stdio.h>
  199. #include <stdlib.h>
  200. #include <dos.h>
  201. #include <conio.h>
  202. #include <string.h>
  203. #include <time.h>
  204. #include <io.h>
  205.  
  206. //#include "fast.h" // Commlib stuff //Don't have fast.h -KRB
  207. #include "game.h"
  208. #include "scores.h"
  209. #include "modem.h"
  210. #include "object.h"
  211. #include "player.h"
  212. #include "laser.h"
  213. #include "error.h"
  214. #include "collide.h"
  215. #include "endlevel.h"
  216. #include "network.h"
  217. #include "mono.h"
  218. #include "gauges.h"
  219. #include "newmenu.h"
  220. #include "menu.h"
  221. #include "gamesave.h"
  222. #include "netmisc.h"
  223. #include "fuelcen.h"
  224. #include "dpmi.h"
  225. //#include "commlib.h" -Don't have these either! -KRB
  226. //#include "glfmodem.h" -Don't have these either! -KRB
  227. #include "multi.h"
  228. #include "timer.h"
  229. #include "text.h"
  230. #include "pcx.h"
  231. #include "palette.h"
  232. #include "sounds.h"
  233. #include "digi.h"
  234. #include "multibot.h"
  235. #include "args.h"
  236.  
  237. //This include is just to allow compiling. It doesn't mean it will work. Values in here are only dummy values
  238. #include "nocomlib.h"
  239.  
  240. #define MIN_COMM_GAP 8000
  241. #define INIT_STRING_LEN 20
  242. #define LEN_PHONE_NUM_OLD 15
  243. #define LEN_PHONE_NUM     32
  244. #define LEN_PHONE_NAME 12
  245. #define NUM_PHONE_NUM 8
  246.  
  247. // How many times to repeat 'reliable' messages
  248.  
  249. #define EOR_MARK 0xaa
  250.  
  251. #define COM_PROCESS_NORMAL 0
  252. #define COM_PROCESS_ENDLEVEL 1
  253. #define COM_PROCESS_SYNC 2
  254. #define COM_PROCESS_MENU 3
  255.  
  256. #define SELECTION_STARTGAME        1
  257. #define SELECTION_NO_START            2
  258. #define SELECTION_YES_START        3
  259. #define SELECTION_STARTGAME_ABORT    4
  260. #define SELECTION_CLOSE_LINK        5
  261.  
  262. int default_base[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
  263. int default_irq[4] = { 4, 3, 4, 3 };
  264.  
  265. //    Code to support modem/null-modem play
  266.  
  267. // Program determined variables for serial play
  268.  
  269. typedef struct com_sync_pack {
  270.     char type;
  271.     byte proto_version;
  272.     long sync_time;
  273.     byte level_num;
  274.     char difficulty;
  275.     char game_mode;
  276.     char callsign[CALLSIGN_LEN+1];
  277.     short kills[2];
  278.     ushort seg_checksum;
  279. #ifndef SHAREWARE
  280.     byte sync_id;
  281.     char mission_name[9];
  282.     short killed;
  283.     byte game_flags;
  284. #endif
  285.     char    dummy[3]; // Extra space for checksum & sequence number
  286. } com_sync_pack;
  287.  
  288. PORT *com_port;
  289. int serial_active;
  290. int com_baud_rate = 0;
  291. //--unused-- int sync_time = 0;
  292. int com_open = 0;
  293. int got_sync = 0;
  294. int other_got_sync = 0;
  295. int carrier_on = 0;
  296. long com_type = -1; /* What type of UART is available */
  297. static long synccnt;
  298. static ubyte rx_seqnum = 0xff;
  299. static ubyte tx_seqnum = 0;
  300. int OtherPlayer; // Player num for modem opponent
  301. int com_process_mode = COM_PROCESS_NORMAL;
  302. int master = -1; // Am I the master or is the other guy the master?
  303. com_sync_pack my_sync, other_sync;
  304. int start_level_num;
  305.  
  306. int com_custom_port = -1;
  307. int com_custom_irq = -1;
  308. int com_custom_base = -1;
  309.  
  310. int other_menu_choice = 0;
  311.  
  312. int chars_sent = 0;
  313.  
  314. // Com buffers
  315.  
  316. static char syncbuffer[MAX_MULTI_MESSAGE_LEN+4];
  317. static char sendbuf[MAX_MULTI_MESSAGE_LEN+4]; // +4 because of +1 for null and +3 for checksum/sequence
  318.  
  319. // Serial setup variables
  320.  
  321. int com_port_num = -1;
  322. int com_speed = -1;
  323. char modem_init_string[INIT_STRING_LEN+1];
  324. char phone_num[NUM_PHONE_NUM+1][LEN_PHONE_NUM+1];
  325. char phone_name[NUM_PHONE_NUM][LEN_PHONE_NAME+1];
  326.  
  327. fix  SerialLastMessage = 0;
  328.  
  329. /* Function prototypes for functions not exported through modem.h */
  330.  
  331. void com_param_setup(void);
  332. void com_start_game(void);
  333. void modem_dialout(void);
  334. void modem_answer(void);
  335. int com_sync(int id);
  336. void com_sync_poll(int nitem, newmenu_item *menus, int *key, int citem);
  337.  
  338. #if 0
  339. #define    codex(name_start, name_end)    \
  340. void name_start(void)    \
  341. {    \
  342.     int    a,b,i;    \
  343.     \
  344.     a = 3;    \
  345.     b = a + 4;    \
  346.     \
  347.     for (i=0; i<123; i++)    \
  348.         a += i;    \
  349.     \
  350.     if (a < b)    \
  351.         b += a;    \
  352.     else if (a == b)    \
  353.         a += b;    \
  354.     else    \
  355.         a += a + b;    \
  356.     \
  357.     while (a < b)    \
  358.         a = b;    \
  359.     \
  360. }    \
  361.     \
  362. void name_end(void)    \
  363. {    \
  364. }
  365. #else
  366. #define codex(name_start, name_end)
  367. #endif
  368.  
  369. codex(code_01s, code_01e)
  370.  
  371. int detect_UART(unsigned baseaddr, int * loc, int * code )
  372. {
  373.    // this function returns 0 if no UART is installed.
  374.    // 1: 8250, 2: 16450 or 8250 with scratch reg., 3: 16550, 4: 16550A
  375.    int x,olddata,temp;
  376.  
  377.     *loc = 0; *code = 0;
  378.  
  379.    // check if a UART is present.  This is code John hacked by looking at the return
  380.     // values from peoples computers.  
  381.    olddata=inp(baseaddr+4);    
  382.    outp(baseaddr+4,0x1f);            // Enable Loopback mode, sets RTS & DTR to 1.
  383.     delay(1);
  384.     _disable();
  385.     temp = inp(baseaddr+6);            // Read the state of RTS and DTR.
  386.     temp = inp(baseaddr+6);            // Do this twice, so that lower 4 bits are clear. OS/2 returns 0xB0 after this,
  387.                                             // instead of 0xff if no port is there.
  388.     _enable();
  389.    if ((temp&0x3f)!=0x30) {
  390.         *loc = 1; *code = temp;
  391.         return 0;
  392.     }
  393.    outp(baseaddr+4,olddata);        // Restore RTS & DTR
  394.     delay(1);
  395.    // next thing to do is look for the scratch register
  396.    olddata=inp(baseaddr+7);
  397.    outp(baseaddr+7,0x55);
  398.     delay(1);
  399.    if (inp(baseaddr+7)!=0x55) return 1;
  400.    outp(baseaddr+7,0xAA);
  401.     delay(1);
  402.    if (inp(baseaddr+7)!=0xAA) return 1;
  403.    outp(baseaddr+7,olddata); // we don't need to restore it if it's not there
  404.     delay(1);
  405.    // then check if there's a FIFO
  406.    outp(baseaddr+2,1);
  407.     delay(1);
  408.    x=inp(baseaddr+2);
  409.    // some old-fashioned software relies on this!
  410.    outp(baseaddr+2,0x0);
  411.     delay(1);
  412.    if ((x&0x80)==0) return 2;
  413.    if ((x&0x40)==0) return 3;
  414.    return 4;
  415. }
  416.     
  417. codex(code_02s, code_02e)
  418.  
  419. int 
  420. com_type_detect()
  421. {
  422. //    static long port;
  423. //    short *ptr;
  424.     int loc, code;
  425.  
  426.     long port_addr[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
  427.     long portaddr;
  428.  
  429.     if (com_type != -1)
  430.         return com_type;
  431.  
  432.     if ( (com_port_num != 0) && (com_port_num != 1) && (com_port_num != 2) && (com_port_num != 3) )
  433.     {
  434.         Int3();
  435.         return -1; // Error, set com_port_num before calling this!    
  436.     }
  437.  
  438.     if (com_port_num == com_custom_port)
  439.         portaddr = com_custom_base;
  440.     else
  441.         portaddr = port_addr[com_port_num];
  442.  
  443.     mprintf((0, "com port %x.\n", portaddr));
  444.     
  445.     switch( detect_UART(portaddr, &loc, &code) )    {                                                 
  446.     case 0:  // No UART
  447.         mprintf((0, "No UART detected. (LOC:%d, CODE:0x%x)\n", loc, code));
  448.         return -1;
  449.     case 1:    // 8250
  450.         mprintf((0, "UART type is 8250.\n" ));
  451.         return(16450);
  452.     case 2:    // 16450 or 8250 with scratch reg.
  453.         mprintf((0, "UART is type 16450, or an 8250 with scratch register.\n" ));
  454.         return(16450);
  455.     case 3:    // 16550
  456.         mprintf((0, "UART is type 16550, with FIFO bug.\n" ));
  457.         return(16450);        // 16550's FIFOs don't work. This is not a typo. (detected 16550, returned 16450 )
  458.     case 4:    // 16550A,  which is the only UART the FIFO mode works with.
  459.         mprintf((0, "UART is type 16550A, no FIFO bug.\n" ));
  460.         return(16550);
  461.     }
  462.     return (-1);
  463. }
  464.  
  465. #if !defined(NDEBUG) && !defined(NMONO)
  466. void
  467. com_dump_string(char *string)
  468. {
  469.     mprintf((0, "%s\n", string));
  470. }
  471. #else
  472. #define com_dump_string()
  473. #endif
  474.  
  475. codex(code_03s, code_03e)
  476.  
  477. int
  478. com_enable() 
  479. {
  480.     // Detect and enable the COM port selected by the user
  481.  
  482.     int rc;
  483.  
  484.     if (com_open)
  485.         return 0;
  486.  
  487.     rx_seqnum = 0xff;
  488.     tx_seqnum = 0;
  489.  
  490.     // com_port_num and com_speed should be set before calling this func
  491.     com_type = com_type_detect();
  492.  
  493.     if (com_custom_port == com_port_num)
  494.         rc = Change8259Priority(com_custom_irq);
  495.     else if ((com_port_num == COM2) || (com_port_num == COM4))
  496.         rc = Change8259Priority( IRQ3 );
  497.     else
  498.         rc = Change8259Priority( IRQ4 );
  499.  
  500.     if (rc != ASSUCCESS) 
  501.     {
  502.         nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_SERIAL_OPEN_ERROR);
  503.         return -1;
  504.     }
  505.  
  506. // If our detection is wrong, we want them to be able to go ahead and play anyway.
  507. //    if (com_type == -1)
  508. //    {
  509. //        nm_messagebox(TXT_ERROR, 1, TXT_OK, "Error 2\n%s", TXT_SERIAL_OPEN_ERROR);
  510. //        return -1;
  511. //    }
  512.  
  513.     if (com_port_num == com_custom_port )
  514.     {
  515.         rc = FastSetPortHardware(com_port_num, com_custom_irq, com_custom_base);
  516.         if (rc != ASSUCCESS)
  517.         {
  518.             nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_SERIAL_OPEN_ERROR);
  519.             return -1;
  520.         }
  521.     }
  522.  
  523.     if (com_type == 16550)
  524.     {
  525.         FastSet16550TriggerLevel( TRIGGER_04 );
  526.         FastSet16550UseTXFifos( ON );
  527.     }
  528.  
  529.     FastSavePortParameters(com_port_num);
  530.  
  531.     com_port = PortOpenGreenleafFast(com_port_num, com_speed, 'N', 8, 1);
  532.  
  533. #ifndef NDEBUG
  534.     {
  535.         int curr_irq, curr_base;
  536.         //FastGetPortHardware(com_port_num, &curr_irq, &curr_base); //Removed , we don't have this function! -KRB
  537.         mprintf((0, "Current port settings: base %x, irq %x.\n", curr_base, curr_irq ));
  538.     }
  539. #endif
  540.  
  541.     if ((com_port == 0) || (com_port->status != 0))
  542.     {
  543.         nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_SERIAL_OPEN_ERROR);
  544.         return -1;
  545.     }
  546.  
  547.     #ifndef NDEBUG
  548.     mprintf((0, "Com port opened.  Status = %d\n", com_port->status));
  549.     #endif
  550.  
  551.     SetDtr(com_port, ON);
  552.     
  553.     if ( FindArg( "-ctsrts" ) || FindArg( "-rtscts" )  )    
  554.         UseRtsCts(com_port, ON); // Now used for null-modem as well, helps IHHD!
  555.     else
  556.         UseRtsCts(com_port, OFF);
  557.  
  558.     com_open = 1;
  559.  
  560.      master = -1;
  561.  
  562. //    DumpPortStatus(com_port, com_dump_string);
  563.  
  564.     return 0;
  565. }
  566.  
  567. void
  568. com_disable()
  569. {
  570.     // close the com port and free associated structures.
  571.  
  572.     int rc;
  573.  
  574.     if (!com_open) 
  575.         return;
  576.  
  577. // SetDtr(com_port, OFF);
  578. //    UseRtsCts(com_port, OFF);
  579.  
  580.     rc = PortClose(com_port);
  581.  
  582.     FastRestorePortParameters(com_port_num);
  583.  
  584.     if (com_custom_port == com_port_num)
  585.     {
  586.         // Custom settings were in effect, roll them back
  587.         rc = FastSetPortHardware(com_port_num, default_irq[com_port_num], default_base[com_port_num]);
  588.     }
  589.  
  590.     if (rc != ASSUCCESS) {
  591.         #ifndef NDEBUG
  592.         mprintf((1, "PortClose returned %d!\n", rc));
  593.         #endif
  594.     }
  595.  
  596.     com_port = 0;
  597.     com_open = 0;
  598.  
  599.     master = -1;
  600.  
  601.     #ifndef NDEBUG
  602.     mprintf((0, "Com port released.\n"));
  603.     #endif
  604. }
  605.  
  606. codex(code_04s, code_04e)
  607.  
  608. void
  609. com_abort(void)
  610. {
  611.     // this is the safest way to get out of some modem/serial negotiation
  612.     // and back to the main menu.  Use this whenever this have gone too far
  613.     // awry to repair.
  614.  
  615.     com_disable();
  616.  
  617.     N_players = 0;
  618.  
  619.     change_playernum_to(0);
  620.  
  621.     Viewer = ConsoleObject = &Objects[0];
  622.     Game_mode = GM_GAME_OVER; // Force main menu selection
  623. }
  624.  
  625. void
  626. com_hangup(void)
  627. {
  628.     // Close the serial link
  629.  
  630.     com_send_choice(SELECTION_CLOSE_LINK);
  631.     com_abort();
  632. }
  633.  
  634. void
  635. com_carrier_lost(void)
  636. {
  637.     // Carrier lost, inform and abort
  638.  
  639.     if (multi_in_menu > 0)
  640.     {
  641.         multi_leave_menu = 1;
  642.         return;
  643.     }
  644.  
  645.     Function_mode = FMODE_MENU;
  646.  
  647.     multi_quit_game = 1;
  648.     nm_messagebox(NULL, 1, TXT_OK, TXT_CARRIER_LOST);
  649.     com_abort();
  650. }
  651.  
  652. codex(code_05s, code_05e)
  653.  
  654. extern ubyte cockpit_mode_save; // From object.c
  655. extern int old_cockpit_mode; // From game.c
  656.  
  657. com_reset_game(void)
  658. {
  659.     int i;
  660.  
  661.     // Reset various parameters before starting a new game
  662.     
  663.     N_players = 2;
  664.  
  665.     for (i = 0; i < N_players; i++)
  666.     {
  667.         Players[i].connected = 1;
  668.     }
  669.  
  670. //    master = -1;
  671.  
  672.     multi_new_game(); // Reset kill list, among other things
  673.     Fuelcen_control_center_destroyed = 0;
  674.     Endlevel_sequence = 0;
  675.     
  676.     // Yes, this really IS as ugly as it gets, kids...
  677.  
  678.     if (Cockpit_mode == CM_LETTERBOX)
  679.     {
  680.         select_cockpit(cockpit_mode_save);
  681.     }
  682.     else if (Cockpit_mode == CM_REAR_VIEW)
  683.     {
  684.         select_cockpit(old_cockpit_mode);
  685.     }
  686. }
  687.  
  688. codex(code_06s, code_06e)
  689.  
  690. void
  691. com_save_settings(void)
  692. {
  693.     FILE *settings;
  694.     int i;
  695.  
  696.     if ( (settings = fopen("serial.cfg", "wb")) == NULL)
  697.         goto error;
  698.  
  699.     if (fwrite(&com_speed, sizeof(int), 1, settings) != 1)
  700.         goto error;
  701.     
  702.     if (fwrite(&com_port_num, sizeof(int), 1, settings) != 1)
  703.         goto error;
  704.  
  705.     if (fwrite(modem_init_string, 1, INIT_STRING_LEN+1, settings) != INIT_STRING_LEN+1)
  706.         goto error;
  707.  
  708.     for (i = 0; i < NUM_PHONE_NUM; i++)
  709.     {
  710.         if (fwrite(phone_num[i], 1, LEN_PHONE_NUM+1, settings) != LEN_PHONE_NUM+1)
  711.             goto error;
  712.         if (fwrite(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1)
  713.             goto error;
  714.     }
  715.  
  716.     if (fwrite(&com_custom_port, sizeof(int), 1, settings) != 1)
  717.         goto error;
  718.     if (fwrite(&com_custom_irq, sizeof(int), 1, settings) != 1)
  719.         goto error;
  720.     if (fwrite(&com_custom_base, sizeof(int), 1, settings) != 1)
  721.         goto error;
  722.     // 100 % success!
  723.  
  724.     fclose(settings);
  725.     return;
  726.  
  727. error:
  728.     nm_messagebox(NULL, 1, TXT_OK, TXT_ERROR_SERIAL_CFG);
  729.  
  730.     if (settings) {
  731.         fclose(settings);
  732.         unlink("serial.cfg");
  733.     }
  734.     
  735.     return;
  736. }
  737.  
  738. codex(code_07s, code_07e)
  739.  
  740. void
  741. com_load_settings(void)
  742. {
  743.     FILE *settings;
  744.     int i, cfg_size;
  745.  
  746.     if ((settings = fopen("serial.cfg", "rb")) == NULL)
  747.         goto defaults;
  748.  
  749.     cfg_size = filelength(fileno(settings));
  750.  
  751.     // Read the data from the file
  752.     
  753.     if (fread(&com_speed, sizeof(int), 1, settings) != 1)
  754.         goto error;
  755.     if (! ((com_speed == 9600) || (com_speed == 19200) || (com_speed == 38400)) )
  756.         goto error;
  757.  
  758.     if (fread(&com_port_num, sizeof(int), 1, settings) != 1)
  759.         goto error;
  760.     if ( (com_port_num < COM1) || (com_port_num > COM4) )
  761.         goto error;
  762.  
  763.     if (fread(modem_init_string, 1, INIT_STRING_LEN+1, settings) != INIT_STRING_LEN+1)
  764.         goto error;
  765.     modem_init_string[INIT_STRING_LEN] = '\0';
  766.  
  767.     if (cfg_size <= 273 )    {                // Old 15 char LEN_PHONE_NUM's
  768.         mprintf(( 1, "Reading old pre 1.1 phone.cfg\n" ));
  769.         for (i = 0; i < NUM_PHONE_NUM; i++)
  770.         {
  771.             if (fread(phone_num[i], 1, LEN_PHONE_NUM_OLD+1, settings) != LEN_PHONE_NUM_OLD+1)
  772.                 goto error;
  773.             phone_num[i][LEN_PHONE_NUM_OLD] = '\0';
  774.             if (fread(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1)
  775.                 goto error;
  776.             phone_name[i][LEN_PHONE_NAME] = '\0';
  777.         }
  778.     } else {            // Normal Phone nums
  779.         for (i = 0; i < NUM_PHONE_NUM; i++)
  780.         {
  781.             if (fread(phone_num[i], 1, LEN_PHONE_NUM+1, settings) != LEN_PHONE_NUM+1)
  782.                 goto error;
  783.             phone_num[i][LEN_PHONE_NUM] = '\0';
  784.             if (fread(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1)
  785.                 goto error;
  786.             phone_name[i][LEN_PHONE_NAME] = '\0';
  787.         }
  788.     }
  789.  
  790.     if (fread(&com_custom_port, sizeof(int), 1, settings) != 1) {
  791.         mprintf((0, "Reading old file format for serial.cfg.\n"));
  792.         goto close;
  793.     }
  794.  
  795.     if ( (com_custom_port < -1) || (com_custom_port > COM4) )
  796.         goto error;
  797.  
  798.     if (fread(&com_custom_irq, sizeof(int), 1, settings) != 1)
  799.         goto error;
  800.     if ( (com_custom_port < -1) || (com_custom_port > IRQ15) )
  801.         goto error;
  802.  
  803.     if (fread(&com_custom_base, sizeof(int), 1, settings) != 1)
  804.         goto error;
  805.     if (com_custom_base < -1)
  806.         goto error;
  807.  
  808.     //Everything was A-Ok!
  809. close:
  810.     fclose(settings);
  811.  
  812.     return;
  813.  
  814. error:
  815.     nm_messagebox(NULL, 1, TXT_OK, TXT_ERR_SER_SETTINGS);
  816.         
  817. defaults:
  818.     // Return some defaults
  819.     com_speed = 19200; // UART speed
  820.     strcpy(modem_init_string, "ATZ");
  821.     com_port_num = COM2;
  822.     com_custom_port = -1;
  823.     for (i = 0; i < NUM_PHONE_NUM; i++)
  824.     {
  825.         phone_num[i][0] = '\0';
  826.         strcpy(phone_name[i], TXT_EMPTY);
  827.     }
  828.  
  829.     if (settings)
  830.         fclose(settings);
  831.  
  832.     return;
  833. }
  834.  
  835. void
  836. serial_leave_game(void)
  837. {
  838.     #ifndef NDEBUG
  839.     mprintf((0, "Called serial_leave_game.\n"));
  840.     #endif
  841. //    com_abort();
  842.     serial_sync_abort(0); // Just in case the other guy is in sync mode
  843.     Game_mode |= GM_GAME_OVER;
  844.     Function_mode = FMODE_MENU;
  845. }
  846.  
  847. codex(code_08s, code_08e)
  848.  
  849. void
  850. com_send_data(char *ptr, int len, int repeat)
  851. {
  852.     int i;
  853.  
  854.     // Take the raw packet data specified by ptr and append the sequence
  855.     // number and checksum, and pass it to com_send_ptr
  856.  
  857.     if (!com_port)
  858.         return;
  859.  
  860.     if (Game_mode & GM_MODEM)
  861.     {
  862.         i = GetCd(com_port);
  863.         if (i == 0)
  864.             mprintf((0, "CARRIER LOST!\n"));
  865.     }
  866.     
  867.     len += 3; // Checksum data is 3 bytes
  868.  
  869.     *(ubyte *)(ptr+(len-3)) = (tx_seqnum+1)%256;
  870.     tx_seqnum = (tx_seqnum+1)%256;
  871.  
  872.     *(ushort *)(ptr+(len-2)) = netmisc_calc_checksum(ptr, len-2);
  873.  
  874.     com_send_ptr(ptr, len);
  875.     if (repeat)
  876.         for (i = 0; i < repeat; i++)
  877.             com_send_ptr(ptr, len);
  878. }
  879.  
  880. com_send_ptr(char *ptr, int len)
  881. {
  882.     register    int count;
  883.     register char dat;
  884.  
  885.     for (count = 0, dat=ptr[0]; count < len; dat=ptr[++count])
  886.     {
  887.         WriteChar(com_port, dat);
  888.         if (dat == EOR_MARK)
  889.             WriteChar(com_port, EOR_MARK); // double in-band endmarkers
  890.     }
  891.     WriteChar(com_port, EOR_MARK);  // EOR
  892.     WriteChar(com_port, 0);         // EOR
  893.     chars_sent += len;
  894. }
  895.  
  896. codex(code_09s, code_09e)
  897.  
  898. void
  899. com_flush()
  900. {
  901.     // Get rid of all waiting data in the serial buffer
  902.  
  903.     int i = 0;
  904.  
  905.     if (!com_open)    
  906.         return;
  907.  
  908.     mprintf((0, "COM FLUSH:"));
  909.  
  910.     while (ReadCharTimed(com_port, 100) >= 0)
  911.         i++;
  912.     mprintf((0, "%d characters.\n", i));
  913. }
  914.  
  915. int
  916. com_getchar()
  917. {
  918.     register int i;
  919.     static int eor_recv = 0;
  920.  
  921.     // return values:
  922.     //  -1 = Nothing in buffer
  923.      //  -2 = End of record
  924.  
  925.     if (!com_open)
  926.         return(-1);
  927.  
  928.     i = ReadChar(com_port);
  929.  
  930. //    mprintf((0, "%c", i));
  931.  
  932.     if (i == ASBUFREMPTY)
  933.         return (-1);
  934.  
  935.     if ((i == EOR_MARK) || eor_recv)
  936.     {
  937.         if (!eor_recv)
  938.             i = ReadChar(com_port);
  939.  
  940.         if (i == ASBUFREMPTY)
  941.         {
  942. //            Assert(eor_recv == 0);
  943.             eor_recv = 1;
  944.             return(-1);
  945.         }
  946.         else if (i == EOR_MARK) 
  947.         {
  948.             eor_recv = 0;
  949.             return(EOR_MARK); // Doubled EOR returns the character
  950.         }
  951.         else
  952.         {
  953. #ifndef NDEBUG
  954.             if (i != 0) {
  955.                 mprintf((0, "EOR followed by unexpected value %d.\n", i));
  956.             }
  957. #endif
  958.             eor_recv = 0;
  959.             return(-2);                            
  960.         }
  961.     }
  962.     return(i);
  963. }
  964.  
  965. #define SERIAL_IDLE_TIMEOUT F1_0*10
  966.  
  967. codex(code_10s, code_10e)
  968.  
  969. void
  970. com_do_frame(void)
  971. {
  972.     static fix last_comm_time = 0;
  973.     static int last_pos_skipped = 0;
  974.     int rval = 0;
  975.  
  976.     if (Endlevel_sequence || (com_process_mode==COM_PROCESS_ENDLEVEL)) {    // Only recieve during endlevel
  977.         int old_Endlevel_sequence = Endlevel_sequence;
  978.         Endlevel_sequence = 1;
  979.         com_process_input();
  980.         Endlevel_sequence = old_Endlevel_sequence;
  981.         return;
  982.     }
  983.  
  984.     last_comm_time += FrameTime;
  985.  
  986.     if ((last_comm_time > MIN_COMM_GAP) || Network_laser_fired)
  987.     {
  988. #ifndef SHAREWARE
  989.         if ((Game_mode & GM_MULTI_ROBOTS) && !last_pos_skipped) {
  990.             rval = multi_send_robot_frame(0);
  991.         }
  992.         if (rval && !Network_laser_fired)
  993.         {
  994.             last_pos_skipped = 1;
  995.             goto skippos;
  996.         }
  997. #endif
  998.         last_pos_skipped = 0;
  999.         multi_send_position(Players[Player_num].objnum);
  1000.         multi_send_fire(); // Will return w/o sending if we haven't fired
  1001.  
  1002. skippos:
  1003. //        mprintf((0, "%d chars sent, %f cps.\n", chars_sent, f2fl(fixdiv((chars_sent*F1_0),last_comm_time)) ));
  1004.         chars_sent = 0;
  1005.         last_comm_time = 0;
  1006.     }
  1007. //    else
  1008. //        multi_send_robot_frame(1);
  1009.  
  1010.     com_process_input();
  1011.  
  1012.     if (!Fuelcen_control_center_destroyed && (Function_mode == FMODE_GAME) && (SerialLastMessage+SERIAL_IDLE_TIMEOUT < GameTime))
  1013.     {
  1014.         SerialLastMessage = 0x7fffffff-SERIAL_IDLE_TIMEOUT; // Give no further warnings until next message arrives!
  1015.         nm_messagebox(TXT_WARNING, 1, TXT_OK, TXT_CONNECT_LOST, Players[OtherPlayer].callsign);
  1016.     }
  1017.  
  1018.     return;
  1019. }
  1020.  
  1021. int
  1022. com_check_message(char *checkbuf, int len)
  1023. {
  1024.     ushort check;
  1025.     int seqnum;
  1026.  
  1027.     if (len < 4)
  1028.     {
  1029.         #ifndef NDEBUG
  1030.         mprintf((0, "message type %d too short to be a real message!\n", checkbuf[0]));
  1031.         #endif
  1032.         goto error;
  1033.     }
  1034.  
  1035.     if (checkbuf[0] > MULTI_MAX_TYPE)
  1036.     {
  1037.         #ifndef NDEBUG
  1038.         mprintf((1, "message type %d out of range.\n", checkbuf[0]));
  1039.         #endif
  1040.         goto error;
  1041.     }
  1042.  
  1043.     if ( (len-3) != message_length[checkbuf[0]])
  1044.     {
  1045.         #ifndef NDEBUG
  1046.         mprintf((1, "message length %d != %d.\n", len-3, message_length[checkbuf[0]]));
  1047.         #endif
  1048.         goto error;
  1049.     }
  1050.  
  1051.     check = netmisc_calc_checksum(checkbuf, len-2);
  1052.     if (check != *(ushort *)(checkbuf+(len-2)))
  1053.     {
  1054.         #ifndef NDEBUG
  1055.         mprintf((0, "error in message type %d, length %d, checksum %d != %d\n", checkbuf[0], len, check, *(ushort *)(checkbuf+(len-2))));    
  1056.         #endif
  1057.         goto error;
  1058.     }
  1059.  
  1060.     seqnum = checkbuf[(len-3)];
  1061.  
  1062.     if (seqnum == rx_seqnum)
  1063.     {
  1064.         return -1;
  1065.     }
  1066.     
  1067.     if (seqnum != (rx_seqnum+1)%256)
  1068.     {
  1069.         #ifndef NDEBUG
  1070.         mprintf((0, "Warning, missed 1 or more messages.\n"));    
  1071.         #endif
  1072.     }
  1073.     rx_seqnum = seqnum;
  1074. //    mprintf((0, "message type %d len %d OK!\n", checkbuf[0], len));
  1075.     return 0; 
  1076.  
  1077. error:
  1078.     mprintf((1,"Line status: %d.\n", GetLineStatus(com_port)));
  1079.     ClearLineStatus(com_port);
  1080.     return -1;
  1081. }
  1082.     
  1083. codex(code_11s, code_11e)
  1084.  
  1085. void
  1086. com_process_menu(char *buf, int len)
  1087. {
  1088.     char text[80];
  1089.  
  1090.     len = len;
  1091.  
  1092.     mprintf((0, "com_process_menu: type %d.\n", buf[0]));
  1093.  
  1094.     switch(buf[0])
  1095.     {
  1096.         case MULTI_MESSAGE:
  1097. #ifndef SHAREWARE
  1098.             sprintf(text, "%s %s\n'%s'", Players[OtherPlayer].callsign, TXT_SAYS, buf+2);
  1099. #else
  1100.             sprintf(text, "%s %s\n'%s'", Players[OtherPlayer].callsign, TXT_SAYS, buf+3);
  1101. #endif
  1102.             nm_messagebox(NULL, 1, TXT_OK, text);
  1103.             break;
  1104.         case MULTI_MENU_CHOICE:
  1105.             other_menu_choice = buf[1];
  1106.             mprintf((0, "Other menu choice = %d.\n", other_menu_choice));
  1107.             break;
  1108.         case MULTI_BEGIN_SYNC:
  1109.             // If we get a sync at the menu, send an abort sync, we're not ready yet!
  1110.             serial_sync_abort(0);
  1111.             break;
  1112.     }
  1113. }
  1114.  
  1115. void
  1116. com_process_input(void)
  1117. {
  1118.     // Read all complete messages from the serial buffer and process
  1119.     // the contents.  Messages are read into global array snycbuffer.
  1120.  
  1121.     static int len = 0;
  1122.     int entry_com_mode = com_process_mode;
  1123.     register    int dat;
  1124.  
  1125.     if (!com_port)
  1126.         return;
  1127.  
  1128. nextmessage:
  1129.     if (Game_mode & GM_MODEM)
  1130.     {
  1131.         if (!GetCd(com_port))
  1132.             com_carrier_lost();
  1133.     }
  1134.  
  1135.     if (!com_port) {
  1136.         if (!multi_in_menu) {
  1137.             multi_quit_game = 1;
  1138.         }
  1139.         else {
  1140.             multi_leave_menu = 1;
  1141.         }
  1142.     }
  1143.  
  1144.     if (com_process_mode != entry_com_mode)
  1145.     {
  1146. //        mprintf((0, "Exiting com_process_input due to mode switch.\n"));
  1147.         return;
  1148.     }
  1149.  
  1150.     while ( (len <= MAX_MULTI_MESSAGE_LEN) && (dat = com_getchar()) > -1) // Returns -1 when serial pipe empty
  1151.     {
  1152.         syncbuffer[len++] = dat;
  1153.     }
  1154.  
  1155.     if ((dat == -2) || (len > MAX_MULTI_MESSAGE_LEN)) // Returns -2 when end of message reached
  1156.     {
  1157.         // End of message
  1158.         SerialLastMessage = GameTime;
  1159.  
  1160.         if (!com_check_message(syncbuffer, len))
  1161.         {
  1162.             switch(com_process_mode)
  1163.             {
  1164.                 case COM_PROCESS_NORMAL:
  1165.                 case COM_PROCESS_ENDLEVEL:
  1166.                     multi_process_data(syncbuffer, len); break;
  1167.                 case COM_PROCESS_MENU:
  1168.                     if (!Endlevel_sequence) com_process_menu(syncbuffer, len); break;
  1169.                 case COM_PROCESS_SYNC:
  1170.                     if (!Endlevel_sequence) com_process_sync(syncbuffer, len); break;
  1171.                 default:
  1172.                     Int3(); // Bad com_process_mode switch set!
  1173.             }
  1174.         }
  1175.         len = 0;
  1176.         goto nextmessage;
  1177.     }
  1178.     if (dat == -3) // Returns -3 when carrier lost
  1179.     {
  1180.         com_abort();
  1181.         len = 0;
  1182.     }
  1183.     return ;
  1184. }
  1185.  
  1186. int
  1187. com_connect()
  1188. {
  1189.     my_sync.type = MULTI_BEGIN_SYNC;
  1190.     my_sync.difficulty = 0;
  1191.     memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
  1192.     my_sync.seg_checksum = 0;
  1193.     my_sync.game_mode = Game_mode;
  1194.     my_sync.level_num = 0;
  1195.                                  
  1196.     #ifndef NDEBUG
  1197.     mprintf((0, "com_connect()\n"));
  1198.     #endif
  1199.  
  1200.     if(com_sync(-1))
  1201.         return(-1); // Failure in sync
  1202.  
  1203.     if (master == -1)
  1204.     {
  1205.         #ifndef NDEBUG
  1206.         mprintf((0, "My rand = %d, other rand = %d.\n", my_sync.sync_time, other_sync.sync_time));
  1207.         #endif
  1208.  
  1209.         // Figure out who is the master
  1210.         if (my_sync.sync_time > other_sync.sync_time)
  1211.         {
  1212.             mprintf((0, "Swtiching player to master.\n"));
  1213.             master=1;
  1214.             change_playernum_to(0);
  1215.         }
  1216.         else if (my_sync.sync_time < other_sync.sync_time)
  1217.         {
  1218.             mprintf((0, "Switching player to slave.\n"));
  1219.             master = 0;
  1220.             change_playernum_to(1);
  1221.         }
  1222.         else
  1223.             return(-1);  // Didn't sync properly, try again
  1224.     }
  1225.     
  1226.     // Copy the remote sync data into local variables
  1227.     
  1228.     OtherPlayer = (Player_num+1)%2;
  1229.     mprintf((0, "Other player is #%d.\n", OtherPlayer));
  1230.     memcpy(Players[OtherPlayer].callsign, other_sync.callsign, CALLSIGN_LEN+1);
  1231.  
  1232.     return(0);
  1233. }
  1234.  
  1235. #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)
  1236.  
  1237. #define MENU_MODEM_CALL                0
  1238. #define MENU_MODEM_ANSWER            1
  1239. #define MENU_SERIAL_LINK_START     2
  1240. #define MENU_SERIAL_SETUP             3
  1241. #define MENU_MODEM_HANGUP            4
  1242. #define MENU_SERIAL_GAME_START    5
  1243. #define MENU_SEND_MESSAGE            6
  1244.  
  1245. codex(code_12s, code_12e)
  1246.  
  1247. void
  1248. com_menu_poll(int nitems, newmenu_item *menus, int *key, int citem)
  1249. {
  1250.     // Watch the serial stream if we are connected and take appropriate actions
  1251.  
  1252.     int old_game_mode;
  1253.  
  1254.     menus = menus;
  1255.     citem = citem;
  1256.     nitems = nitems;
  1257.  
  1258.     if (! ( (Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM) ) )
  1259.         return;
  1260.  
  1261.     com_process_mode = COM_PROCESS_MENU;
  1262.     old_game_mode = Game_mode;
  1263.     other_menu_choice = 0;    
  1264.  
  1265.     com_process_input();
  1266.  
  1267.     if ((old_game_mode != Game_mode) || other_menu_choice || (com_process_mode != COM_PROCESS_MENU))
  1268.         *key = -2;
  1269.     if (multi_leave_menu)
  1270.         *key = -2;
  1271. }
  1272.  
  1273. void
  1274. com_send_choice(int choice)
  1275. {
  1276.     sendbuf[0] = (char)MULTI_MENU_CHOICE;
  1277.     sendbuf[1] = (char)choice;
  1278.  
  1279.     com_send_data(sendbuf, 2, 1);
  1280. }
  1281.  
  1282. void
  1283. com_ready_to_start(void)
  1284. {
  1285.     newmenu_item m[2];
  1286.     int choice;
  1287.  
  1288.     m[0].type = m[1].type = NM_TYPE_MENU;
  1289.     m[0].text = TXT_YES;
  1290.     m[1].text = TXT_NO;
  1291.  
  1292.     choice = newmenu_do1(NULL, TXT_READY_DESCENT, 2, m, com_menu_poll, 0 );
  1293.     if (choice == 0)
  1294.     {
  1295.         // Yes
  1296.         com_send_choice(SELECTION_YES_START);
  1297.         other_menu_choice = SELECTION_STARTGAME;
  1298.         com_start_game();
  1299.     }        
  1300.     else 
  1301.     {
  1302.         com_send_choice(SELECTION_NO_START);
  1303.     }
  1304. }
  1305.  
  1306. void
  1307. com_process_other_menu_choice(void)
  1308. {
  1309.     if (other_menu_choice == SELECTION_STARTGAME)    
  1310.         com_ready_to_start();
  1311.     else if (other_menu_choice == SELECTION_CLOSE_LINK) 
  1312.     {
  1313.         nm_messagebox(NULL, 1, TXT_OK, TXT_CLOSED_LINK);
  1314.         com_hangup();
  1315.     }
  1316. }
  1317.  
  1318. #define SUBTITLE_LEN 120
  1319.  
  1320. void
  1321. com_main_menu(void)
  1322. {
  1323.     newmenu_item m[10];
  1324.     int menu_choice[10];
  1325.     int num_options = 0;
  1326.     int choice=0;
  1327.     int old_game_mode;
  1328.     char subtitle[SUBTITLE_LEN];
  1329.     int pcx_error;
  1330.  
  1331.     if (com_port_num == -1)
  1332.         com_load_settings();
  1333.  
  1334.     setjmp(LeaveGame);
  1335.  
  1336.     gr_set_current_canvas(NULL);
  1337.     pcx_error = pcx_read_bitmap(Menu_pcx_name,&grd_curcanv->cv_bitmap,grd_curcanv->cv_bitmap.bm_type,NULL);
  1338.     Assert(pcx_error == PCX_ERROR_NONE);
  1339.  
  1340.     com_process_mode = COM_PROCESS_MENU;
  1341.  
  1342. newmenu:
  1343.     num_options = 0;
  1344.  
  1345.     if (! ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)) )
  1346.     {
  1347.         // We haven't established any type of link
  1348.         ADD_ITEM(TXT_DIAL_MODEM, MENU_MODEM_CALL, KEY_D);
  1349.         ADD_ITEM(TXT_ANSWER_MODEM, MENU_MODEM_ANSWER, KEY_A);
  1350.         ADD_ITEM(TXT_NULL_MODEM, MENU_SERIAL_LINK_START, KEY_E);
  1351.         ADD_ITEM(TXT_COM_SETTINGS, MENU_SERIAL_SETUP, KEY_C);
  1352.     }
  1353.     else
  1354.     {
  1355.         ADD_ITEM(TXT_START_GAME, MENU_SERIAL_GAME_START, KEY_S);
  1356.         ADD_ITEM(TXT_SEND_MESSAGEP, MENU_SEND_MESSAGE, KEY_S);
  1357.     }
  1358.     if (Game_mode & GM_MODEM)
  1359.         ADD_ITEM(TXT_HANGUP_MODEM, MENU_MODEM_HANGUP, KEY_H);
  1360.     
  1361.     if (Game_mode & GM_SERIAL)
  1362.         ADD_ITEM(TXT_CLOSE_LINK, MENU_MODEM_HANGUP, KEY_C);
  1363.  
  1364.     sprintf(subtitle, "%s\n\n", TXT_SERIAL_GAME);
  1365.  
  1366.     if (Game_mode & GM_SERIAL)
  1367.         sprintf(subtitle+strlen(subtitle), "%s %s\n%s", TXT_SERIAL, TXT_LINK_ACTIVE, Players[OtherPlayer].callsign);
  1368.     else if (Game_mode & GM_MODEM)
  1369.         sprintf(subtitle+strlen(subtitle), "%d %s %s %s\n%s", com_baud_rate, TXT_BAUD, TXT_MODEM, TXT_LINK_ACTIVE, Players[OtherPlayer].callsign);    
  1370.     else
  1371.         sprintf(subtitle+strlen(subtitle), TXT_NOT_CONNECTED);
  1372.  
  1373.     multi_leave_menu = 0;
  1374.  
  1375.  
  1376.     Assert(strlen(subtitle) < SUBTITLE_LEN);
  1377.  
  1378.     choice = newmenu_do1(NULL, subtitle, num_options, m, com_menu_poll, 0);
  1379.  
  1380.     mprintf((0, "main menu choice was %d.\n", choice));
  1381.  
  1382.     if (choice == -1)
  1383.     {
  1384.         if (!((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)))
  1385.         {
  1386.             com_disable();
  1387.             return;
  1388.         }
  1389.         m[0].text = TXT_YES; m[1].text = TXT_NO;
  1390.         m[0].type = m[1].type = NM_TYPE_MENU;
  1391.  
  1392.         choice = newmenu_do1(NULL, TXT_EXIT_WILL_CLOSE, 2, m, com_menu_poll, 0);
  1393.         if (choice == 0)
  1394.         {
  1395.             com_send_choice(SELECTION_CLOSE_LINK);
  1396.             com_hangup();
  1397.             return;
  1398.         }
  1399.         if ((choice == -2) && (other_menu_choice))
  1400.             com_process_other_menu_choice();
  1401.  
  1402.         goto newmenu;
  1403.     }
  1404.  
  1405.     if (choice == -2)
  1406.     {
  1407.         // Menu poll loop caused a re-draw
  1408.         if (other_menu_choice == SELECTION_STARTGAME)    
  1409.             com_ready_to_start();
  1410.         else if (other_menu_choice == SELECTION_CLOSE_LINK) 
  1411.         {
  1412.             nm_messagebox(NULL, 1, TXT_OK, TXT_CLOSED_LINK);
  1413.             com_hangup();
  1414.         }
  1415.             
  1416.         if (Function_mode == FMODE_GAME)
  1417.             return;    
  1418.  
  1419.         if (!com_port)
  1420.             Game_mode = GM_GAME_OVER;
  1421.  
  1422.         goto newmenu;
  1423.     }        
  1424.  
  1425.     if (choice > -1) 
  1426.     {
  1427.         old_game_mode=Game_mode;
  1428.         switch (menu_choice[choice])
  1429.         {
  1430.             case MENU_SERIAL_SETUP:
  1431.                  com_param_setup();
  1432.                 goto newmenu;
  1433.                 break;
  1434.             case MENU_SERIAL_GAME_START:
  1435.                 com_start_game();
  1436.                 if (Function_mode != FMODE_GAME) 
  1437.                     goto newmenu;
  1438.                 break;
  1439.             case MENU_MODEM_CALL:
  1440.                 modem_dialout();
  1441.                 goto newmenu;
  1442.                 break;
  1443.             case MENU_MODEM_ANSWER:
  1444.                 modem_answer();
  1445.                 goto newmenu;
  1446.                 break;
  1447.             case MENU_SEND_MESSAGE:
  1448.                 multi_send_message_dialog();
  1449.                 if (Network_message_reciever != -1)
  1450.                     multi_send_message();
  1451.                 multi_sending_message = 0;
  1452.                 goto newmenu;
  1453.                 break;
  1454.             case MENU_SERIAL_LINK_START:
  1455.                 serial_link_start();
  1456.                 goto newmenu;
  1457.                 break;
  1458.             case MENU_MODEM_HANGUP:
  1459.                 com_hangup();
  1460.                 goto newmenu;
  1461.                 break;
  1462.             default: 
  1463.                 Int3();
  1464.                 return;
  1465.         }
  1466.     }
  1467. }
  1468.  
  1469. codex(code_13s, code_13e)
  1470.  
  1471. void com_custom_param_setup(void)
  1472. {
  1473.     // User menu for setting up custom IRQ/Base settings for a COM port
  1474.  
  1475.     newmenu_item mm[6];
  1476.     int loc;
  1477.  
  1478.     char base[10]; 
  1479.     char irq[3];
  1480.     char title[60];
  1481.     int new_irq, new_base;
  1482.     int menu_save, menu_reset;
  1483.     int mmn;
  1484.  
  1485.     sprintf(title, "%s%d", TXT_COM_CUSTOM_SETTINGS, com_port_num+1);
  1486.     
  1487.     if (com_port_num != com_custom_port) 
  1488.     {
  1489.         new_irq = default_irq[com_port_num];
  1490.         new_base = default_base[com_port_num];
  1491.     }
  1492.     else
  1493.     {
  1494.         new_irq = com_custom_irq;
  1495.         new_base = com_custom_base;
  1496.     }
  1497.  
  1498. newmenu:
  1499.     sprintf(base, "%x", new_base);
  1500.     sprintf(irq, "%d", new_irq);
  1501.  
  1502.     loc = 0;
  1503.     mm[loc].type = NM_TYPE_TEXT; mm[loc].text = TXT_COM_BASE; loc++;
  1504.     mm[loc].type = NM_TYPE_INPUT; mm[loc].text = base; mm[loc].text_len = 9; loc++;
  1505.     mm[loc].type = NM_TYPE_TEXT; mm[loc].text = TXT_COM_IRQ; loc++;
  1506.     mm[loc].type = NM_TYPE_INPUT; mm[loc].text = irq, mm[loc].text_len = 2; loc++;
  1507.     menu_reset = loc;
  1508.     mm[loc].type = NM_TYPE_MENU; mm[loc].text = TXT_RESET_DEFAULTS; loc++;
  1509.     menu_save = loc;
  1510.     mm[loc].type = NM_TYPE_MENU; mm[loc].text = TXT_ACCEPT; loc++;    
  1511.  
  1512.     mmn = newmenu_do1(NULL, title, loc, mm, NULL, menu_save);
  1513.  
  1514.     if (mmn == -1)
  1515.         return; // All changes lost
  1516.  
  1517.     new_irq = strtol(irq, NULL, 0);
  1518.     new_base = strtol(base, NULL, 16);
  1519.  
  1520.     if (mmn == menu_reset) 
  1521.     {
  1522.         new_irq = default_irq[com_port_num];
  1523.         new_base = default_base[com_port_num];
  1524.     }
  1525.     if (mmn == menu_save) 
  1526.     {
  1527.         if ((new_irq == default_irq[com_port_num]) && (new_base == default_base[com_port_num])) {
  1528.             com_custom_port = -1;
  1529.             mprintf((0, "Custom com settings not changed.\n"));
  1530.             return;
  1531.         }
  1532.         if ((new_irq < IRQ2) || (new_irq > IRQ7)) {
  1533.            new_irq = default_irq[com_port_num];
  1534.             nm_messagebox(NULL, 1, TXT_OK, TXT_VALID_IRQS);
  1535.             goto newmenu;
  1536.         }
  1537.  
  1538.         com_custom_irq = new_irq;
  1539.         com_custom_base = new_base;
  1540.         com_custom_port = com_port_num;
  1541.         return;
  1542.     }
  1543.     goto newmenu;
  1544. }
  1545.  
  1546. void com_param_setup_poll(int nitems, newmenu_item *menus, int *key, int citem)
  1547. {
  1548.     nitems = nitems;
  1549.     key = key;
  1550.     citem = citem;
  1551.     
  1552.     if ((com_custom_port == -1) && menus[4].value)
  1553.     {
  1554.         menus[4].value = 0; menus[4].redraw = 1;
  1555.         return;
  1556.     }
  1557.  
  1558.     if (com_custom_port == -1)
  1559.         return;
  1560.  
  1561.     if (menus[com_custom_port].value && !menus[4].value) 
  1562.     {
  1563.         menus[4].value = 1; menus[4].redraw = 1;
  1564.     }
  1565.     else if (menus[4].value && !menus[com_custom_port].value)
  1566.     {
  1567.         menus[4].value = 0; menus[4].redraw = 1;
  1568.     }
  1569.     
  1570. }
  1571.  
  1572. void com_param_setup(void)
  1573. {
  1574.     int mmn;
  1575.     int was_enabled = 0;
  1576.     newmenu_item mm[12];
  1577.     char init_string[INIT_STRING_LEN+1];
  1578.     int changed = 0;
  1579.     int menu_baud, menu_custom, menu_save;
  1580.     int loc;
  1581.  
  1582.     strcpy (init_string, modem_init_string);
  1583.  
  1584.     com_type = -1;
  1585.  
  1586.     if (com_open)
  1587.     {
  1588.         was_enabled = 1;
  1589.         com_disable();
  1590.     }
  1591.  
  1592. setupmenu:    
  1593.     loc = 0;
  1594.     mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM1); mm[loc].text="COM1"; mm[loc].group=0; loc++;
  1595.     mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM2); mm[loc].text="COM2"; mm[loc].group=0; loc++;
  1596.     mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM3); mm[loc].text="COM3"; mm[loc].group=0; loc++;
  1597.     mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM4); mm[loc].text="COM4"; mm[loc].group=0; loc++;
  1598.     menu_custom = loc;
  1599.     mm[loc].type=NM_TYPE_CHECK; mm[loc].value=(com_port_num == com_custom_port); mm[loc].text=TXT_COM_CUSTOM_SETTINGS; loc++;
  1600.     mm[loc].type=NM_TYPE_TEXT; mm[loc].text = TXT_BAUD_RATE; loc++;
  1601.     menu_baud = loc;
  1602.     mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_speed == 9600); mm[loc].text="9600"; mm[loc].group=1; loc++;
  1603.     mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_speed == 19200); mm[loc].text="19200"; mm[loc].group=1; loc++;
  1604.     mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_speed == 38400); mm[loc].text="38400"; mm[loc].group=1; loc++;
  1605.     mm[loc].type=NM_TYPE_TEXT; mm[loc].text = TXT_MODEM_INIT_STRING; loc++;
  1606.     mm[loc].type=NM_TYPE_INPUT; mm[loc].text_len = INIT_STRING_LEN; mm[loc].text = init_string; loc++;
  1607.     menu_save = loc;
  1608.     mm[loc].type=NM_TYPE_MENU; mm[loc].text = TXT_ACCEPT_SAVE; loc++;
  1609.  
  1610.     mmn = newmenu_do1(NULL, TXT_SERIAL_SETTINGS, loc, mm, com_param_setup_poll, menu_save);
  1611.  
  1612.     if (mmn > -1 ) {
  1613.         changed = 1;
  1614.  
  1615.         if (mm[0].value)
  1616.             com_port_num = COM1;
  1617.         else if (mm[1].value)
  1618.             com_port_num = COM2;
  1619.         else if (mm[2].value) 
  1620.             com_port_num = COM3;
  1621.         else 
  1622.             com_port_num = COM4;
  1623.         
  1624.         if (mmn == menu_custom)
  1625.         {
  1626.             com_custom_param_setup();
  1627.         }
  1628.  
  1629.         com_type = -1;
  1630.         com_type = com_type_detect();
  1631.  
  1632.         if (com_type == -1)
  1633.         {
  1634.             nm_messagebox(NULL, 1, TXT_OK, "%s\n%s", TXT_WARNING, TXT_NO_UART);
  1635.         }
  1636.  
  1637.         if ((mm[menu_baud].value) || (mmn == menu_baud)) 
  1638.             com_speed = 9600;
  1639.         else if ((mm[menu_baud+1].value) || (mmn == menu_baud+1))
  1640.             com_speed = 19200;
  1641.         else
  1642.         {
  1643.             if (com_type == 16550)
  1644.                 com_speed = 38400;
  1645.             else
  1646.             {
  1647.                 nm_messagebox(NULL, 1, TXT_OK, TXT_WARNING_16550);
  1648.                 com_speed = 19200;
  1649.             }
  1650.         }
  1651.                 
  1652.         //mprintf((0, "%s\n", init_string));
  1653.  
  1654.         if ((strnicmp("AT", init_string, 2)) && (strlen(init_string) < (INIT_STRING_LEN-2)))
  1655.             sprintf(modem_init_string, "AT%s", init_string);
  1656.         else
  1657.             strcpy(modem_init_string, init_string);
  1658.  
  1659.         if (mmn != menu_save)
  1660.             goto setupmenu;
  1661.     }
  1662.     
  1663.     if (was_enabled)
  1664.         com_enable();
  1665.  
  1666.     if (changed)
  1667.         com_save_settings();
  1668.  
  1669. }
  1670.     
  1671. codex(code_14s, code_14e)
  1672.  
  1673. extern int opt_cinvul;
  1674. extern int last_cinvul;
  1675.  
  1676. void modem_game_param_poll( int nitems, newmenu_item * menus, int * key, int citem )
  1677. {
  1678.     nitems = nitems;
  1679.     key = key;
  1680.     citem = citem;
  1681.     if ( last_cinvul != menus[opt_cinvul].value )    {
  1682.         sprintf( menus[opt_cinvul].text, "%s: %d %s", TXT_REACTOR_LIFE, menus[opt_cinvul].value*5, TXT_MINUTES_ABBREV );
  1683.         last_cinvul = menus[opt_cinvul].value;
  1684.         menus[opt_cinvul].redraw = 1;
  1685.     }        
  1686. }
  1687.  
  1688. // Handshaking to start a serial game, 2 players only
  1689.  
  1690. int com_start_game_menu(void)
  1691. {
  1692.     newmenu_item m[13];
  1693.     char level[5];
  1694.     int choice = 0;
  1695.     int opt, diff_opt, mode_opt, options_opt;
  1696.     char level_text[32];
  1697.     char srinvul[32];
  1698.  
  1699. #ifndef SHAREWARE
  1700.     int new_mission_num, anarchy_only = 0;
  1701.  
  1702.     new_mission_num = multi_choose_mission(&anarchy_only);
  1703.  
  1704.     if (new_mission_num < 0)
  1705.         return 0;
  1706.  
  1707.     strcpy(my_sync.mission_name, Mission_list[new_mission_num].filename);
  1708. #endif
  1709.  
  1710.     sprintf(level, "1");
  1711.  
  1712.     Game_mode &= ~GM_MULTI_COOP;
  1713.     Game_mode &= ~GM_MULTI_ROBOTS;
  1714.     Netgame.game_flags = 0;
  1715.  
  1716.     sprintf(level_text, "%s (1-%d)", TXT_LEVEL_, Last_level);
  1717.     if (Last_secret_level < -1)
  1718.         sprintf(level_text+strlen(level_text)-1, ", S1-S%d)", -Last_secret_level);
  1719.     else if (Last_secret_level == -1)
  1720.         sprintf(level_text+strlen(level_text)-1, ", S1)");
  1721.  
  1722.     Assert(strlen(level_text) < 32);
  1723.  
  1724.     // Put up menu for user choices controlling gameplay
  1725.  
  1726. newmenu:
  1727.     opt = 0;
  1728.     m[opt].type = NM_TYPE_TEXT; m[opt].text = level_text; opt++;
  1729.     m[opt].type = NM_TYPE_INPUT; m[opt].text_len = 4; m[opt].text = level; opt++;
  1730. #ifdef ROCKWELL_CODE
  1731.     mode_opt = 0;
  1732. #else
  1733.     m[opt].type = NM_TYPE_TEXT; m[opt].text = TXT_MODE;
  1734.     mode_opt = opt; 
  1735.     m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_ANARCHY; m[opt].value=!(Game_mode & GM_MULTI_ROBOTS); m[opt].group = 0; opt++;
  1736.     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++;
  1737.     m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_COOPERATIVE; m[opt].value=(Game_mode & GM_MULTI_COOP);m[opt].group = 0; opt++;
  1738. #endif
  1739.     diff_opt = opt;
  1740.     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++;
  1741.  
  1742. #ifndef SHAREWARE
  1743.     options_opt = opt;
  1744. //    m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_IDS; m[opt].value=0; opt++;
  1745.     m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_ON_MAP; m[opt].value=0; opt++;
  1746.  
  1747.     opt_cinvul = opt;
  1748.     sprintf( srinvul, "%s: %d %s", TXT_REACTOR_LIFE, 5*control_invul_time, TXT_MINUTES_ABBREV );
  1749.     last_cinvul = control_invul_time;
  1750.     m[opt].type = NM_TYPE_SLIDER; m[opt].value=control_invul_time; m[opt].text= srinvul; m[opt].min_value=0; m[opt].max_value=15; opt++;
  1751.  
  1752. #endif
  1753.  
  1754.     Assert(opt <= 13);
  1755.  
  1756.     choice = newmenu_do1(NULL, TXT_SERIAL_GAME_SETUP, opt, m, modem_game_param_poll, 1);
  1757.     if (choice > -1) 
  1758.     {
  1759. #ifdef ROCKWELL_CODE
  1760.         Game_mode |= (GM_MULTI_COOP | GM_MULTI_ROBOTS);
  1761. #else
  1762.         if (m[mode_opt].value)
  1763.             Game_mode &= ~(GM_MULTI_COOP | GM_MULTI_ROBOTS);
  1764. #ifdef SHAREWARE
  1765.         else {
  1766.             nm_messagebox(NULL, 1, TXT_OK, TXT_ONLY_ANARCHY);
  1767.             goto newmenu;
  1768.         }
  1769. #else
  1770.         else if (anarchy_only) {
  1771.             nm_messagebox(NULL, 1, TXT_OK, TXT_ANARCHY_ONLY_MISSION);
  1772.             goto newmenu;
  1773.         }
  1774.         else if (m[mode_opt+1].value)
  1775.         {
  1776.             Game_mode &= ~GM_MULTI_COOP;
  1777.             Game_mode |= GM_MULTI_ROBOTS;
  1778.         }
  1779.         else
  1780.             Game_mode |= (GM_MULTI_COOP | GM_MULTI_ROBOTS);
  1781. #endif
  1782. #endif     // ifdef ROCKWELL_CODE
  1783.  
  1784. //        if (m[options_opt].value)
  1785. //            Netgame.game_flags |= NETGAME_FLAG_SHOW_ID;
  1786.         if (m[options_opt].value)
  1787.             Netgame.game_flags |= NETGAME_FLAG_SHOW_MAP;
  1788.         if (!strnicmp(level, "s", 1))
  1789.             start_level_num = -atoi(level+1);
  1790.         else
  1791.             start_level_num = atoi(level);
  1792.  
  1793.         if ((start_level_num < Last_secret_level) || (start_level_num > Last_level) || (start_level_num == 0))
  1794.         {
  1795.             nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_LEVEL_OUT_RANGE);
  1796.             sprintf(level, "1");
  1797.             goto newmenu;
  1798.         }
  1799.  
  1800.         Difficulty_level = m[diff_opt].value;
  1801.         control_invul_time = m[opt_cinvul].value;
  1802.         Netgame.control_invul_time = control_invul_time*5*F1_0*60;
  1803.  
  1804.         return(1); // Go for game!
  1805.     }
  1806.     return 0; // No game
  1807. }
  1808.  
  1809. int
  1810. com_ask_to_start()
  1811. {    
  1812.     // Ask the other player if its OK to start now
  1813.  
  1814.     newmenu_item m[1];
  1815.     int choice;
  1816.  
  1817.     com_send_choice(SELECTION_STARTGAME);
  1818.  
  1819.     m[0].type = NM_TYPE_TEXT; m[0].text = TXT_ESC_ABORT;
  1820. menu:
  1821.     choice = newmenu_do(NULL, TXT_WAIT_FOR_OK, 1, m, com_menu_poll);
  1822.  
  1823.     if (choice == -1)
  1824.     {
  1825.         com_send_choice(SELECTION_STARTGAME_ABORT);
  1826.         return(0);
  1827.     }
  1828.     if (choice == -2)
  1829.     {
  1830.         if (other_menu_choice == SELECTION_YES_START)
  1831.             return(1);
  1832.         else if (other_menu_choice == SELECTION_STARTGAME)
  1833.         {
  1834.             com_send_choice(SELECTION_YES_START);
  1835.             return(1);
  1836.         }
  1837.         else 
  1838.             return(0);
  1839.     }
  1840.     goto menu;
  1841. }
  1842.         
  1843. codex(code_15s, code_15e)
  1844.  
  1845. void
  1846. com_start_game()
  1847. {
  1848.     // Start a serial game after being linked
  1849.  
  1850.     mprintf((0, "Entered com_start_game\n"));
  1851.  
  1852.     com_reset_game();
  1853.  
  1854.     if (! ( (Game_mode & GM_MODEM) || (Game_mode & GM_SERIAL) ) ) 
  1855.         return;
  1856.     
  1857.     Assert (master != -1);
  1858.  
  1859.     if (other_menu_choice != SELECTION_STARTGAME)
  1860.     {
  1861.         if (!com_ask_to_start())
  1862.             return;
  1863.     }
  1864.  
  1865.     if (master == 1) // Master chooses options
  1866.     {
  1867.         if (com_start_game_menu())
  1868.         {
  1869.             OtherPlayer = 1;
  1870.             change_playernum_to(0);
  1871.             my_sync.level_num = start_level_num;
  1872.             my_sync.difficulty = Difficulty_level;
  1873.             my_sync.game_mode = Game_mode;
  1874.             memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
  1875. #ifndef SHAREWARE
  1876.             my_sync.sync_time = control_invul_time*5*F1_0*60;
  1877.             my_sync.game_flags = Netgame.game_flags;
  1878.             Netgame.control_invul_time = control_invul_time*5*F1_0*60;
  1879. #endif
  1880.             com_sync(0);
  1881.         }
  1882.     }
  1883.     else // Slave
  1884.     {
  1885.         OtherPlayer = 0;
  1886.         change_playernum_to(1);
  1887.         memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
  1888.     
  1889.         my_sync.level_num = 1;
  1890.         
  1891.         com_sync(0);
  1892.         if (com_process_mode == COM_PROCESS_NORMAL)
  1893.         {
  1894.             Difficulty_level = other_sync.difficulty;
  1895.             start_level_num = other_sync.level_num;
  1896.             Game_mode = other_sync.game_mode;
  1897. #ifndef SHAREWARE
  1898.             Netgame.game_flags = other_sync.game_flags;
  1899.             Netgame.control_invul_time = other_sync.sync_time;
  1900.             if (!load_mission_by_name(other_sync.mission_name))
  1901.             {
  1902.                 mprintf((0, "Mission not found: %s!\n", other_sync.mission_name));
  1903.                 nm_messagebox(NULL, 1, TXT_OK, TXT_MISSION_NOT_FOUND);
  1904.                 my_sync.sync_id = start_level_num;
  1905.                 serial_sync_abort(0);
  1906.                 return;
  1907.             }
  1908. #endif
  1909.         }
  1910.     }
  1911.     if (com_process_mode != COM_PROCESS_NORMAL)
  1912.         return;
  1913.  
  1914.     memcpy(Players[OtherPlayer].callsign, other_sync.callsign, CALLSIGN_LEN+1);
  1915.     Function_mode = FMODE_GAME;
  1916.     Game_mode &= ~GM_GAME_OVER;
  1917.     Show_kill_list = 1;
  1918.     init_player_stats_game();
  1919.     init_player_stats_level();
  1920. //    Assert(start_level_num > 0);
  1921.       Assert((start_level_num >= Last_secret_level) && (start_level_num <= Last_level));
  1922.       StartNewLevel(start_level_num);
  1923. }
  1924.  
  1925. //
  1926. // Modem control functions, dialing, answering, etc.
  1927. //
  1928.  
  1929. void modem_edit_phonebook(newmenu_item *m)
  1930. {
  1931.     int choice, choice2;
  1932.     newmenu_item menu[5];
  1933.     char text[2][100];
  1934.     int default_choice = 0;
  1935.  
  1936.     m[NUM_PHONE_NUM].text = TXT_SAVE;
  1937.  
  1938.     menu[0].text = TXT_NAME; menu[0].type = NM_TYPE_TEXT;
  1939.     menu[1].type = NM_TYPE_INPUT; menu[1].text = text[0]; menu[1].text_len = LEN_PHONE_NAME;
  1940.     menu[2].text = TXT_PHONE_NUM; menu[2].type = NM_TYPE_TEXT;
  1941.     menu[3].type = NM_TYPE_INPUT; menu[3].text = text[1]; menu[3].text_len = LEN_PHONE_NUM;
  1942.     menu[4].text = TXT_ACCEPT; menu[4].type = NM_TYPE_MENU;
  1943.  
  1944. menu:
  1945.     choice = newmenu_do1(NULL, TXT_SEL_NUMBER_EDIT, NUM_PHONE_NUM+1, m, NULL, default_choice);
  1946.     if (choice == -1)
  1947.     {
  1948.         com_load_settings();
  1949.         return;
  1950.     }
  1951.     if (choice == NUM_PHONE_NUM)
  1952.     {
  1953.         // Finished
  1954.         com_save_settings();
  1955.         return;
  1956.     }
  1957.     
  1958.     default_choice = 1;
  1959. edit:
  1960.     // Edit an entry
  1961.     strcpy(menu[1].text, phone_name[choice]);
  1962.     strcpy(menu[3].text, phone_num[choice]);
  1963.  
  1964.     choice2 = newmenu_do1(NULL, TXT_EDIT_PHONE_ENTRY, 5, menu, NULL, default_choice);
  1965.     if (choice2 != -1)
  1966.     {    
  1967.         strcpy(phone_name[choice], menu[1].text);
  1968.         strcpy(phone_num[choice], menu[3].text);
  1969.         sprintf(m[choice].text, "%d. %s \t", choice+1, phone_name[choice]);
  1970.         add_phone_number(m[choice].text, phone_num[choice] );
  1971.     }
  1972.     if (choice2 != 4)
  1973.     {
  1974.         default_choice += 2; if (default_choice > 4) default_choice = 4;
  1975.         goto edit;
  1976.     }
  1977.  
  1978.     default_choice = NUM_PHONE_NUM;
  1979.     goto menu;
  1980. }
  1981.  
  1982.  
  1983. codex(code_16s, code_16e)
  1984.  
  1985. void add_phone_number( char * src, char * num )
  1986. {
  1987.     char p;
  1988.     int l;
  1989.     l = strlen(num);
  1990.     if ( l<15)    {
  1991.         strcat( src, num );
  1992.         return;
  1993.     }
  1994.     p = num[15];
  1995.     num[15] = 0;
  1996.     strcat( src, num );
  1997.     num[15] = p;
  1998.     strcat( src, "..." );
  1999. }
  2000.  
  2001. int modem_dial_menu(void)
  2002. {
  2003.     newmenu_item m[NUM_PHONE_NUM+2];
  2004.     char menu_text[NUM_PHONE_NUM][80];
  2005.     int choice = 0;
  2006.     int i;
  2007.  
  2008. menu:
  2009.     for (i = 0; i < NUM_PHONE_NUM; i++)
  2010.     {
  2011.         m[i].text = menu_text[i];
  2012.         sprintf(m[i].text, "%d. %s \t", i+1, phone_name[i]);
  2013.         add_phone_number(m[i].text, phone_num[i] );
  2014.         m[i].type = NM_TYPE_MENU;
  2015.     }
  2016.  
  2017.     strcat(m[i-1].text, "\n");
  2018.  
  2019.     m[NUM_PHONE_NUM].type = NM_TYPE_MENU; 
  2020.     m[NUM_PHONE_NUM].text = TXT_MANUAL_ENTRY;
  2021.     m[NUM_PHONE_NUM+1].text = TXT_EDIT_PHONEBOOK;
  2022.     m[NUM_PHONE_NUM+1].type = NM_TYPE_MENU;
  2023.  
  2024.     choice = newmenu_do1(NULL, TXT_SEL_NUMBER_DIAL, NUM_PHONE_NUM+2, m, NULL, 0);
  2025.     if (choice == -1) 
  2026.         return -1; // user abort
  2027.  
  2028.     if (choice == NUM_PHONE_NUM+1)
  2029.     {
  2030.         // Edit phonebook
  2031.         modem_edit_phonebook(m);
  2032.         goto menu;
  2033.     }
  2034.  
  2035.     if (choice == NUM_PHONE_NUM)
  2036.     {
  2037.         // Manual entry
  2038.         newmenu_item m2[1];
  2039.         m2[0].type = NM_TYPE_INPUT; m2[0].text = phone_num[NUM_PHONE_NUM]; m2[0].text_len = LEN_PHONE_NUM;
  2040.         choice = newmenu_do(NULL, TXT_ENTER_NUMBER_DIAL, 1, m2, NULL);
  2041.         if (choice == -1)
  2042.             goto menu;
  2043.         else
  2044.             return NUM_PHONE_NUM;
  2045.     }
  2046.  
  2047.     // A phone number was chosen
  2048.     return(choice);
  2049. }
  2050.  
  2051. void
  2052. com_wait_for_connect(int nitems, newmenu_item *menus, int *key, int citem)
  2053. {
  2054.     int result;
  2055.     char input_buffer[81];
  2056.     int baud;
  2057.     char error_mess[5][15] = 
  2058.         {"NO DIAL TONE",
  2059.          "BUSY",
  2060.          "NO ANSWER",
  2061.          "NO CARRIER",
  2062.          "VOICE"};
  2063.     char text[100];
  2064.     int i;
  2065.  
  2066.     int num_error_messages = 5;
  2067.  
  2068.     menus = menus;
  2069.     nitems = nitems;
  2070.     citem = citem;
  2071.  
  2072.     
  2073.     if (GetCd(com_port))
  2074.     {
  2075.         carrier_on = 1;
  2076.     }
  2077.     else
  2078.     {
  2079.         if (carrier_on)
  2080.         {
  2081.             *key = -3;
  2082.             nm_messagebox(NULL, 1, TXT_OK, TXT_CARRIER_LOST);
  2083.             carrier_on = 0;
  2084.             return;
  2085.         }
  2086.     }
  2087.  
  2088.     result = HMInputLine(com_port, 500, input_buffer, 80);
  2089.     
  2090.     if (result == 0) 
  2091.         return;        // Timed out
  2092.  
  2093.     mprintf((0, "Modem string: '%s'\n", input_buffer));
  2094.  
  2095.     for (i = 0; i < num_error_messages; i++)
  2096.     {
  2097.         if (!strncmp(input_buffer, error_mess[i], strlen(error_mess[i])))
  2098.         {
  2099.             sprintf(text, "%s %s", TXT_ERR_MODEM_RETURN, input_buffer);
  2100.             nm_messagebox(NULL, 1, TXT_OK, text);
  2101.             *key = -3;
  2102.             return;
  2103.         }
  2104.     }
  2105.  
  2106.     if (strncmp(input_buffer, TXT_CONNECT, 7))
  2107.     {
  2108.         mprintf((0, "Non-connect message found.\n"));
  2109.         return; // some other string.  Not an error, but not a connect
  2110.     }
  2111.  
  2112.     sscanf(input_buffer, "CONNECT %d", &baud);
  2113.  
  2114.     mprintf((0, "Connect at %d baud.\n", baud));
  2115.  
  2116.     if (baud < 9600)
  2117.     {
  2118.         nm_messagebox(NULL, 1, TXT_OK, TXT_BAUD_GREATER_9600);
  2119.         *key = -3;
  2120.         return;
  2121.     }
  2122.  
  2123.     com_baud_rate = baud;
  2124.     *key = -2;
  2125.     
  2126.     return;
  2127. }
  2128.  
  2129. codex(code_17s, code_17e)
  2130.  
  2131. void
  2132. com_wait_for_ring(int nitems, newmenu_item *menus, int *key, int citem)
  2133. {
  2134.     int result;
  2135.     char input_buffer[81];
  2136.  
  2137.     menus = menus;
  2138.     nitems = nitems;
  2139.     citem = citem;
  2140.  
  2141.     result = HMInputLine(com_port, 500, input_buffer, 80);
  2142.     
  2143.     if ((result <= 0)    || strncmp(input_buffer, TXT_RING, 4))
  2144.         return;
  2145.     
  2146.     *key = -2;
  2147.  
  2148.     return;
  2149.     
  2150. }
  2151.  
  2152. int modem_verify(void)
  2153. {
  2154.     // Is there a modem on this com port or not?
  2155.  
  2156.     int result;
  2157.  
  2158.     HMWaitForOK( 5000, NULL);
  2159.  
  2160. //=================================================
  2161. // This was changed by John in response to a 
  2162. // Creative voice modem not working, since this
  2163. // code doesn't wait for an OK.
  2164.  
  2165. //OLD    HMSendStringNoWait(com_port, "AT", -2);
  2166. //OLD    result = HMSendString(com_port, "AT");
  2167.  
  2168.     HMSendString(com_port, "AT" );
  2169.     result = HMSendString(com_port, "AT");
  2170. //=================================================
  2171.  
  2172.     if (result != 0)
  2173.         return (0);
  2174.     return(1);
  2175. }
  2176.     
  2177. void modem_dialout(void)
  2178. {
  2179.     newmenu_item m[5];
  2180.     char text[50];
  2181.     int choice;
  2182.     
  2183.     if (!serial_active)
  2184.     {
  2185.         nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT);
  2186.         return;
  2187.     }
  2188.  
  2189.     com_enable(); // Open COM port as configured
  2190.  
  2191.     if (!com_open)
  2192.         return;
  2193.  
  2194. //    UseRtsCts(com_port, ON); // use hardware handshaking
  2195.  
  2196. main:
  2197.     if ((choice = modem_dial_menu()) == -1)
  2198.         return; // user aborted
  2199.  
  2200.     show_boxed_message(TXT_RESET_MODEM);
  2201.  
  2202.     // Verify presence of modem
  2203.     if (!modem_verify())
  2204.     {
  2205.         clear_boxed_message();
  2206.         nm_messagebox(NULL, 1, TXT_OK, TXT_NO_MODEM);
  2207.         com_abort();
  2208.         return;
  2209.     }
  2210.  
  2211.     if (strlen(phone_num[choice]) == 0)
  2212.     {
  2213.         clear_boxed_message();
  2214.         nm_messagebox(NULL, 1, TXT_OK, TXT_NO_PHONENUM);
  2215.         goto main;
  2216.     }
  2217.  
  2218.     clear_boxed_message();
  2219.  
  2220.     sprintf(text, "%s\n%s", TXT_DIALING, phone_num[choice]);
  2221.     show_boxed_message(text);
  2222.  
  2223.     // Proceed with the call
  2224.  
  2225.     HMReset( com_port );
  2226.  
  2227.     HMSendString( com_port, modem_init_string );
  2228.     
  2229.     HMDial( com_port, phone_num[choice] );
  2230.     
  2231.     carrier_on = 0;
  2232.  
  2233.     clear_boxed_message();
  2234.  
  2235.     m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT;
  2236.  
  2237. //repeat:
  2238.     choice = newmenu_do(NULL, TXT_WAITING_FOR_ANS, 1, m, com_wait_for_connect);
  2239.     if (choice != -2) {
  2240.         HMSendStringNoWait(com_port, "", -2);
  2241.         com_abort();
  2242.         return;
  2243.     }
  2244.  
  2245.     // We are now connected to the other modem
  2246.  
  2247.     N_players = 2;
  2248.  
  2249.     master = 1;   // The person who dialed is the master of the connection
  2250.     change_playernum_to(0);
  2251.  
  2252.     if (!com_connect())
  2253.     {
  2254.         Game_mode |= GM_MODEM;
  2255.         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
  2256.     }
  2257.     else {
  2258.         HMSendStringNoWait(com_port, "", -2);
  2259.         com_abort();
  2260.     }
  2261. }
  2262.                                          
  2263. codex(code_18s, code_18e)
  2264.  
  2265. void modem_answer(void)
  2266. {
  2267.     int choice;
  2268.     newmenu_item m[3];
  2269.  
  2270.     if (!serial_active)
  2271.     {
  2272.         nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT);
  2273.         return;
  2274.     }
  2275.  
  2276.     com_enable(); // Open COM port as configured
  2277.  
  2278.     if (!com_open)
  2279.         return;
  2280.  
  2281. //    UseRtsCts(com_port, ON); // use hardware handshaking
  2282.     
  2283.     show_boxed_message(TXT_RESET_MODEM);
  2284.  
  2285.     // Verify presence of modem
  2286.     if (!modem_verify())
  2287.     {
  2288.         clear_boxed_message();
  2289.         nm_messagebox(NULL, 1, TXT_OK, TXT_NO_MODEM);
  2290.         com_abort();
  2291.         return;
  2292.     }
  2293.  
  2294.     HMReset( com_port );
  2295.     
  2296.     HMSendString( com_port, modem_init_string );
  2297.  
  2298.     HMSendString( com_port, "AT"); // To set the DTE rate for RING notification
  2299.  
  2300.     clear_boxed_message();
  2301.  
  2302.     m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT;
  2303.  
  2304. repeat:
  2305.     choice = newmenu_do(NULL, TXT_WAITING_FOR_CALL, 1, m, com_wait_for_ring);
  2306.     if (choice == -1) {
  2307.         HMSendStringNoWait(com_port, "", -2);
  2308.         com_abort();
  2309.         return;
  2310.     }
  2311.     if (choice != -2)    
  2312.         goto repeat;
  2313.  
  2314.     // Now answer the phone and wait for carrier
  2315.  
  2316.     HMAnswer(com_port);
  2317.  
  2318.     carrier_on = 0;
  2319.  
  2320.     choice = newmenu_do(NULL, TXT_WAITING_FOR_CARR, 1, m, com_wait_for_connect);
  2321.     if (choice != -2) {
  2322.         HMSendStringNoWait(com_port, "", -2);
  2323.         com_abort();
  2324.         return;
  2325.     }
  2326.  
  2327.     // We are now connected to the other modem
  2328.     
  2329.     N_players = 2;
  2330.  
  2331.     master = 0;
  2332.     change_playernum_to(1);
  2333.  
  2334.     if (!com_connect()) 
  2335.     {
  2336.         Game_mode |= GM_MODEM;
  2337.         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
  2338.     }
  2339.     else {
  2340.         HMSendStringNoWait(com_port, "", -2);
  2341.         com_abort();
  2342.     }
  2343. }
  2344.  
  2345. void serial_link_start(void)
  2346. {
  2347.     if (!serial_active)
  2348.     {
  2349.         nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT);
  2350.         return;
  2351.     }
  2352.  
  2353.     com_enable(); // Open COM port as configured
  2354.  
  2355.     if (!com_open)
  2356.         return;
  2357.  
  2358.     N_players = 2;
  2359.  
  2360.     synccnt = 0;
  2361.  
  2362.     srand(clock());
  2363.     my_sync.sync_time = rand();
  2364.     mprintf((0, "My rand set to %d.\n", my_sync.sync_time));
  2365.  
  2366.     if (!com_connect()) 
  2367.     {
  2368.         Game_mode |= GM_SERIAL;
  2369.         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
  2370.     } 
  2371.     else
  2372.     {
  2373.         nm_messagebox(NULL, 1, TXT_OK, "%s\n%s", TXT_ERROR, TXT_FAILED_TO_NEGOT);
  2374.     }
  2375. }
  2376.  
  2377. //
  2378. // Syncronization functions
  2379. //
  2380.  
  2381. void
  2382. serial_sync_abort(int val)
  2383. {
  2384.     // Send "I got Sync but it was no good!" packet
  2385.  
  2386.     sendbuf[0] = (char)MULTI_END_SYNC;
  2387.     sendbuf[1] = Player_num;
  2388.     sendbuf[2] = (char)val; // Indicated failure
  2389. #ifndef SHAREWARE
  2390.     sendbuf[3] = my_sync.sync_id;
  2391.     com_send_data(sendbuf, 4, 1);
  2392. #else
  2393.     com_send_data(sendbuf, 3, 1);
  2394. #endif
  2395. }
  2396.     
  2397. int
  2398. com_level_sync(void)
  2399. {
  2400.     // Send between-level sync stuff
  2401.  
  2402.     mprintf((0, "entered com_level_sync()\n"));
  2403.  
  2404.     Function_mode = FMODE_MENU; // Prevent the game loop from running during the menus!
  2405.  
  2406.     // At this point, the new level is loaded but the extra objects or players have not 
  2407.     // been removed
  2408.  
  2409.     my_sync.level_num = Current_level_num;
  2410.     my_sync.seg_checksum = netmisc_calc_checksum(Segments, (Highest_segment_index+1) * sizeof(segment));
  2411.     my_sync.kills[0] = kill_matrix[Player_num][0];
  2412.     my_sync.kills[1] = kill_matrix[Player_num][1];
  2413.     my_sync.proto_version = MULTI_PROTO_VERSION;
  2414. #ifndef SHAREWARE
  2415.     my_sync.killed = Players[Player_num].net_killed_total;
  2416. #endif
  2417.     srand(clock());
  2418.  
  2419.     if (Game_mode & GM_MULTI_COOP)
  2420.         my_sync.difficulty = Player_num;
  2421.     else
  2422.         my_sync.difficulty = rand()%MAX_NUM_NET_PLAYERS; // My starting position
  2423.  
  2424.     if (com_sync(Current_level_num))
  2425.     {
  2426.         com_process_mode = COM_PROCESS_MENU;
  2427.         nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NEGOTIATION_FAIL );
  2428.         longjmp(LeaveGame, 0);
  2429.     }
  2430.  
  2431.     if (my_sync.level_num != other_sync.level_num)
  2432.     {
  2433.         // Fatal error
  2434.         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);
  2435.         longjmp(LeaveGame, 0);
  2436.     }
  2437.  
  2438.     if (my_sync.seg_checksum != other_sync.seg_checksum)
  2439.     {
  2440.         // Checksum failure
  2441.         mprintf((1, "My check %d, other check %d.\n", my_sync.seg_checksum, other_sync.seg_checksum));
  2442.         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);
  2443.         longjmp(LeaveGame, 0);
  2444.     }
  2445.  
  2446.     if (my_sync.proto_version != other_sync.proto_version)
  2447.     {
  2448.         // Version mismatch
  2449.         nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_DESCENT_NO_MATCH);
  2450.         longjmp(LeaveGame, 0);
  2451.     }
  2452.  
  2453.     mprintf((0, "My pos = %d, other pos = %d.\n", my_sync.difficulty, other_sync.difficulty));
  2454.  
  2455.     if ((other_sync.difficulty == my_sync.difficulty) && !master)
  2456.     {
  2457.         // If we chose the same position and I am the slave, choose another
  2458.         my_sync.difficulty = (my_sync.difficulty+1) % MAX_NUM_NET_PLAYERS;
  2459.     }
  2460.  
  2461.     Objects[Players[OtherPlayer].objnum].pos = Player_init[other_sync.difficulty].pos;
  2462.     Objects[Players[OtherPlayer].objnum].orient = Player_init[other_sync.difficulty].orient;
  2463.     obj_relink(Players[OtherPlayer].objnum,Player_init[other_sync.difficulty].segnum);
  2464.     Objects[Players[OtherPlayer].objnum].type = OBJ_PLAYER;
  2465.  
  2466.     Objects[Players[Player_num].objnum].pos = Player_init[my_sync.difficulty].pos;
  2467.     Objects[Players[Player_num].objnum].orient = Player_init[my_sync.difficulty].orient;
  2468.     obj_relink(Players[Player_num].objnum, Player_init[my_sync.difficulty].segnum);
  2469.     Objects[Players[Player_num].objnum].type = OBJ_PLAYER;
  2470.  
  2471.     SerialLastMessage = GameTime;
  2472.  
  2473.     kill_matrix[OtherPlayer][0] = other_sync.kills[0];
  2474.     kill_matrix[OtherPlayer][1] = other_sync.kills[1];
  2475.     Players[Player_num].net_kills_total = kill_matrix[Player_num][OtherPlayer] - kill_matrix[Player_num][Player_num];
  2476.     Players[OtherPlayer].net_kills_total = kill_matrix[OtherPlayer][Player_num] - kill_matrix[OtherPlayer][OtherPlayer];
  2477. //    Players[Player_num].net_killed_total = kill_matrix[0][Player_num] + kill_matrix[1][Player_num];
  2478. //    Players[OtherPlayer].net_killed_total = kill_matrix[0][OtherPlayer] + kill_matrix[1][OtherPlayer];
  2479.     Players[OtherPlayer].net_killed_total = other_sync.killed;
  2480.     Players[OtherPlayer].connected = Players[Player_num].connected = 1;
  2481.  
  2482.     Assert(N_players == 2);
  2483.     Assert(Player_num != OtherPlayer);
  2484.  
  2485.     gr_palette_fade_out(gr_palette, 32, 0);
  2486.  
  2487.     Function_mode = FMODE_GAME;
  2488.     com_process_mode = COM_PROCESS_NORMAL;
  2489.     multi_sort_kill_list();
  2490.     return(0);
  2491. }
  2492.  
  2493. codex(code_19s, code_19e)
  2494.     
  2495. void
  2496. com_send_end_sync(void)
  2497. {
  2498.     // Send "I got Sync" packet
  2499.  
  2500.     sendbuf[0] = (char)MULTI_END_SYNC;
  2501.     sendbuf[1] = Player_num;
  2502.     sendbuf[2] = 1; // Indicates success
  2503. #ifndef SHAREWARE
  2504.     sendbuf[3] = my_sync.sync_id;
  2505.     com_send_data(sendbuf, 4, 2);
  2506. #else
  2507.     com_send_data(sendbuf, 3, 2);
  2508. #endif
  2509. }
  2510.  
  2511. void
  2512. com_send_begin_sync(void)
  2513. {
  2514.     mprintf((0, "Sending my sync.\n"));
  2515.     com_send_data((char *)&my_sync, sizeof(com_sync_pack)-3, 1);
  2516. }
  2517.  
  2518. void
  2519. com_process_end_sync(byte *buf)
  2520. {
  2521.     // Process incoming end-sync packet
  2522.  
  2523.     if (buf[2] != 1) {
  2524.         com_process_mode = COM_PROCESS_MENU;
  2525.         return;
  2526.     }
  2527.  
  2528. #ifndef SHAREWARE
  2529.     if (buf[3] == my_sync.sync_id)
  2530. #endif
  2531.         other_got_sync = 1;
  2532. }
  2533.  
  2534. void
  2535. com_process_sync(char *buf, int len)
  2536. {
  2537.     len = len;
  2538.     switch(buf[0])
  2539.     {
  2540.         case MULTI_END_SYNC:
  2541.         {
  2542.             com_process_end_sync(buf);
  2543.             break;
  2544.         }
  2545.         case MULTI_BEGIN_SYNC:
  2546.         {
  2547.             mprintf((0, "Incoming begin_sync message.\n"));
  2548.             if (got_sync)
  2549.                 break;
  2550.  
  2551.             memcpy(&other_sync, buf, sizeof(com_sync_pack)-3);
  2552. #ifndef SHAREWARE
  2553.             if (other_sync.sync_id != my_sync.sync_id) 
  2554.             {
  2555.                 mprintf((0, "Other sync invalid id, %d != %d.\n", other_sync.sync_id, my_sync.sync_id));
  2556.             }
  2557.             else
  2558. #endif
  2559.             {            
  2560.                 mprintf((0, "got other sync size %d.\n", sizeof(com_sync_pack)-3));
  2561.                 got_sync = 1;
  2562.                 com_send_end_sync();
  2563.             }
  2564.             break;
  2565.         }
  2566.     } // switch
  2567.  
  2568.     if (got_sync && other_got_sync)
  2569.     {
  2570.         // OK to start game
  2571. //        mprintf((1, "Starting game.\n"));
  2572.         got_sync = 0;
  2573.         other_got_sync = 0;
  2574.         com_process_mode = COM_PROCESS_NORMAL;
  2575.     }
  2576. }
  2577.     
  2578. void
  2579. com_send_sync(void)
  2580. {
  2581.     // Send sync information, depending on the situation
  2582.  
  2583.     if (!other_got_sync)
  2584.     {
  2585.         com_send_begin_sync();
  2586.     }
  2587.     if (got_sync)
  2588.     {
  2589.         com_send_end_sync();
  2590.     }
  2591. }
  2592.  
  2593. codex(code_20s, code_20e)
  2594.  
  2595. void com_sync_poll(int nitems, newmenu_item *menus, int *key, int citem)
  2596. {
  2597.     static fix t1 = 0;
  2598.  
  2599.     menus = menus;
  2600.     nitems = nitems;
  2601.     citem = citem;
  2602.  
  2603.     if (!com_open)
  2604.     {
  2605.         *key = -3;
  2606.         return;
  2607.     }
  2608.  
  2609.     if (timer_get_approx_seconds() > t1+F1_0)
  2610.     {
  2611.         com_send_sync();
  2612.         t1 = timer_get_approx_seconds();
  2613.     }
  2614.  
  2615.     Assert(com_process_mode == COM_PROCESS_SYNC);
  2616.         
  2617.     com_process_input();
  2618.  
  2619.     if (com_process_mode == COM_PROCESS_NORMAL)
  2620.     {
  2621.         *key = -2;
  2622.         com_send_sync();
  2623.         mprintf((0, "Sync finished.\n"));
  2624.         return;
  2625.     }
  2626.     if (com_process_mode == COM_PROCESS_MENU)
  2627.     {
  2628.         *key = -3;
  2629.         mprintf((0, "Sync denied by other side.\n"));
  2630.         return;
  2631.     }
  2632.     return;
  2633. }
  2634.  
  2635. int
  2636. com_sync(int id)
  2637. {
  2638.     // How to handle the end of the level and start of the next level
  2639.     // returns 0 for success or 1 for failure
  2640.  
  2641.     int choice;
  2642.     newmenu_item m[3];
  2643.     int pcx_error;
  2644.  
  2645.     mprintf((0, "Entered com_sync\n"));
  2646.  
  2647.     gr_set_current_canvas(NULL);
  2648.     pcx_error = pcx_read_bitmap(Menu_pcx_name, &grd_curcanv->cv_bitmap,grd_curcanv->cv_bitmap.bm_type,NULL);
  2649.     Assert(pcx_error == PCX_ERROR_NONE);
  2650.  
  2651.     com_process_mode = COM_PROCESS_SYNC;
  2652.     got_sync = other_got_sync = 0;
  2653.  
  2654.     com_flush();
  2655.     com_flush();
  2656.  
  2657. #ifndef SHAREWARE
  2658.     my_sync.sync_id = id;
  2659. #endif
  2660.  
  2661.     m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT;
  2662.     m[1].type = m[2].type = NM_TYPE_MENU;
  2663.     m[1].text = TXT_YES; 
  2664.     m[2].text = TXT_NO;
  2665.  
  2666. repeat:
  2667.     choice = newmenu_do(NULL, TXT_WAIT_OPPONENT, 1, m, com_sync_poll);
  2668.  
  2669.     if (choice == -1) 
  2670.     {
  2671.         choice = newmenu_do1(NULL, TXT_SURE_ABORT_SYNC, 2, m+1, com_sync_poll, 1);
  2672.         if (choice == -1)
  2673.             choice = 1;
  2674.         if (choice == 0)
  2675.             choice = -1;
  2676.     }
  2677.  
  2678.     if ((choice == -1) || (choice == -3)) {
  2679.         return(-1);
  2680.     }
  2681.     else if (choice != -2)
  2682.         goto repeat;
  2683.  
  2684.     return(0);
  2685. }
  2686.  
  2687. void
  2688. com_endlevel(int *secret)
  2689. {
  2690.     // What do we do between levels?
  2691.  
  2692.     Function_mode = FMODE_MENU;
  2693.  
  2694.     gr_palette_fade_out(gr_palette, 32, 0);
  2695.  
  2696.     my_sync.level_num = multi_goto_secret;
  2697.  
  2698.     if (com_sync(-3))
  2699.     {
  2700.         com_process_mode = COM_PROCESS_MENU;
  2701.         nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NEGOTIATION_FAIL);
  2702.         longjmp(LeaveGame, 0);
  2703.     }
  2704.  
  2705.     com_process_mode = COM_PROCESS_ENDLEVEL;
  2706.  
  2707.     if ((multi_goto_secret == 1) || (other_sync.level_num == 1))
  2708.         *secret = 1;
  2709.     else
  2710.         *secret = 0;
  2711.  
  2712.     multi_goto_secret = 0;
  2713.  
  2714.     return;
  2715. }
  2716.  
  2717. codex(code_21s, code_21e)
  2718.  
  2719. #endif
  2720.  
  2721. 
  2722.