/* THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. */ /* * $Source: /cvsroot/dxx-rebirth/d1x-rebirth/main/modem.c,v $ * $Revision: 1.1.1.1 $ * $Author: zicodxx $ * $Date: 2006/03/17 19:43:00 $ * * Modem support code * * $Log: modem.c,v $ * Revision 1.1.1.1 2006/03/17 19:43:00 zicodxx * initial import * * Revision 1.3 2000/04/21 22:16:09 sekmu * temp removed irq restrictions * * Revision 1.2 1999/11/20 10:05:18 donut * variable size menu patch from Jan Bobrowski. Variable menu font size support and a bunch of fixes for menus that didn't work quite right, by me (MPM). * * Revision 1.1.1.1 1999/06/14 22:08:57 donut * Import of d1x 1.37 source. * * Revision 2.13 1995/11/28 16:25:05 john * Added fixed for Rockwell voice modems thats waits for OK after sending * initial AT to modem to detect if there is a modem on the port. * * Revision 2.12 1995/06/14 16:32:09 john * Fixed bug where all modem games were anarchy. * * Revision 2.11 1995/05/29 16:17:59 john * Added support for Rockwell that takes out all net modes except for anarchy. * * Revision 2.10 1995/04/23 16:06:38 john * Moved rinvul into modem/null modem menu. * * Revision 2.9 1995/04/09 14:43:20 john * Took out mem-overwrite error when > 25 char phone numbers. * * Revision 2.8 1995/04/06 12:13:36 john * Made phone numbers be 32 characters max. * * Revision 2.7 1995/03/31 14:16:33 john * Mode phone numbers 30 chars long. * * Revision 2.6 1995/03/30 16:39:44 john * Incread phone numbers to 25 characters. * * Revision 2.5 1995/03/30 16:04:49 john * Increased modem number length. * * * Revision 2.4 1995/03/29 15:33:23 john * Took out RTS/CTS handshaking. Made it enabled with -RTSCTS. * * Revision 2.3 1995/03/21 14:41:10 john * Ifdef'd out the NETWORK code. * * Revision 2.2 1995/03/06 16:47:50 mike * destination saturn * * Revision 2.1 1995/03/02 15:58:31 john * Made menu support modex. * * Revision 2.0 1995/02/27 11:32:34 john * New version 2.0, which has no anonymous unions, builds with * Watcom 10.0, and doesn't require parsing BITMAPS.TBL. * * Revision 1.157 1995/02/15 15:35:12 john * Added code to support modem tracking of kills during endlevel.. * * Revision 1.156 1995/02/15 15:27:16 john * Mode modem code work with kills after you leave mine. * * Revision 1.155 1995/02/12 19:53:04 rob * Fixed up com flush. * * Revision 1.154 1995/02/11 17:13:25 rob * Took out code fill stuff. * * Revision 1.153 1995/02/11 14:26:25 rob * Added support for invul. controlcen. * * Revision 1.152 1995/02/09 02:59:44 mike * check code for 00066xxx bugs. * * Revision 1.151 1995/02/08 19:18:56 rob * Took out show IDs on HUD option. * * Revision 1.150 1995/02/08 11:30:29 rob * Fixed some pretty minor bugs. * * Revision 1.149 1995/02/07 20:39:54 rob * Added new modem game options for hud id's and automap show. * * Revision 1.148 1995/02/06 18:42:09 rob * Removed debugging crap. * * Revision 1.147 1995/02/06 18:17:17 rob * Fixed bug in killed tallys in modem game. * * Revision 1.146 1995/02/05 17:04:29 rob * Removed performance monitoring debug message. * * Revision 1.145 1995/02/05 17:02:48 rob * Speeding up cooperative/robo-anarchy modes. * * Revision 1.142 1995/02/03 18:09:40 rob * Small fixes to modem setup menu.. * * Revision 1.141 1995/02/03 11:12:15 rob * Fixed a bug (again). Somehow last checkin got lost. * * Revision 1.140 1995/02/02 22:06:20 rob * Removed a function frm shareware. * * Revision 1.139 1995/02/01 23:20:32 rob * Put baud rate in connect subtitle. * * Revision 1.138 1995/01/30 21:22:11 rob * Fixed bug in sync pack memcpy len. * * Revision 1.137 1995/01/30 21:18:31 rob * Simplified mission loading support. * * Revision 1.136 1995/01/30 18:32:08 rob * Added include of multibot.h * * Revision 1.135 1995/01/30 17:16:04 rob * Added new system for sending robots positions in modem games. * * Revision 1.134 1995/01/30 15:29:44 rob * Localizing. * * Revision 1.133 1995/01/29 15:20:21 rob * Fixed a bug in secret level sequencing for modem games. * * Revision 1.132 1995/01/24 15:48:51 rob * fixed error dialog. * * Revision 1.131 1995/01/23 18:51:59 rob * Added RTS/CTS to null-modem games to help IHHD folks. * Fixed a bad assert. * Added support for user-written mission files. * * Revision 1.130 1995/01/23 16:01:36 rob * More reliable syncing. * Added mission selection. * * Revision 1.129 1995/01/20 22:47:35 matt * Mission system implemented, though imcompletely * * Revision 1.128 1995/01/19 18:18:05 rob * Fixed kill list display. * * Revision 1.127 1995/01/19 16:33:48 rob * removed ambigous variable name in check_message. * * Revision 1.126 1995/01/19 11:56:38 rob * Take out Int3 that could never get hit. * * Revision 1.125 1995/01/18 19:01:46 rob * Added new asserts to check for proper game staus. * * * Revision 1.124 1995/01/18 15:39:25 rob * Fixed title of serial menu. * * Revision 1.123 1995/01/18 11:54:23 rob * Fixing a bug with connect status. * * Revision 1.122 1995/01/17 22:16:07 rob * Changed timer calls. * * Revision 1.121 1995/01/17 13:58:31 rob * Fixed problem with not allowing non-anarchy modes in registered. * * Revision 1.120 1995/01/15 15:46:18 rob * Fixed lost character on serial menu messages. * * Revision 1.119 1995/01/12 22:03:15 rob * Added sound cue when connection is established. * * Revision 1.118 1995/01/12 11:42:05 john * added better UART detection. * /. * Works under OS/2 and DOS. * * */ #ifdef NETWORK #define DOS4G #ifdef RCS static char rcsid[] = "$Id: modem.c,v 1.1.1.1 2006/03/17 19:43:00 zicodxx Exp $"; #endif #include #include #include #include #ifdef __MSDOS__ #include #endif #include "game.h" #include "scores.h" #include "modem.h" #include "object.h" #include "player.h" #include "laser.h" #include "error.h" #include "collide.h" #include "endlevel.h" #include "network.h" #include "mono.h" #include "gauges.h" #include "newmenu.h" #include "menu.h" #include "gamesave.h" #include "netmisc.h" #include "fuelcen.h" #include "multi.h" #include "timer.h" #include "text.h" #include "pcx.h" #include "palette.h" #include "sounds.h" #include "digi.h" #include "multibot.h" #include "args.h" #include "d_io.h" #include "serial.h" //#ifndef __LINUX__ //#include "nocomlib.h" //#endif //added 04/19/99 Matt Mueller #include "multiver.h" //end addition -MM void com_send_choice(int choice); void add_phone_number( char * src, char * num ); void serial_link_start(void); void serial_sync_abort(int val); void com_process_sync(char *buf, int len); void com_send_ptr(char *ptr, int len); #define MIN_COMM_GAP 8000 #define INIT_STRING_LEN 20 #define LEN_PHONE_NUM_OLD 15 #define LEN_PHONE_NUM 32 #define LEN_PHONE_NAME 12 #define NUM_PHONE_NUM 8 // How many times to repeat 'reliable' messages #define EOR_MARK 0xaa #define COM_PROCESS_NORMAL 0 #define COM_PROCESS_ENDLEVEL 1 #define COM_PROCESS_SYNC 2 #define COM_PROCESS_MENU 3 #define SELECTION_STARTGAME 1 #define SELECTION_NO_START 2 #define SELECTION_YES_START 3 #define SELECTION_STARTGAME_ABORT 4 #define SELECTION_CLOSE_LINK 5 #ifdef __MSDOS__ int default_base[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; int default_irq[4] = { 4, 3, 4, 3 }; #else #define COM1 1 #define COM2 2 #define COM3 3 #define COM4 4 #endif // Code to support modem/null-modem play // Program determined variables for serial play typedef struct com_sync_pack { char type; byte proto_version; long sync_time; byte level_num; char difficulty; char game_mode; char callsign[CALLSIGN_LEN+1]; short kills[2]; ushort seg_checksum; #ifndef SHAREWARE byte sync_id; char mission_name[9]; short killed; byte game_flags; #endif char dummy[3]; // Extra space for checksum & sequence number } __pack__ com_sync_pack; //added on 11/5/98 by Victor Rachels for serial d1x /* until I know it works.. probably after 1.30 typedef struct d1x_com_sync_pack { char type; byte proto_version; long sync_time; byte level_num; char difficulty; char game_mode; char callsign[CALLSIGN_LEN+1]; short kills[2]; ushort seg_checksum; #ifndef SHAREWARE byte sync_id; char mission_name[9]; short killed; byte game_flags; #endif char dummy[3]; // Extra space for checksum & sequence number } __pack__ d1x_com_sync_pack; */ //end this section addition - VR int commlib_initialised = 0; int serial_active; int com_baud_rate = 0; //--unused-- int sync_time = 0; int com_open = 0; int got_sync = 0; int other_got_sync = 0; int carrier_on = 0; long com_type = 16550; /* What type of UART is available */ static long synccnt; static ubyte rx_seqnum = 0xff; static ubyte tx_seqnum = 0; int OtherPlayer; // Player num for modem opponent int com_process_mode = COM_PROCESS_NORMAL; int master = -1; // Am I the master or is the other guy the master? com_sync_pack my_sync, other_sync; int start_level_num; #ifdef __MSDOS__ PORT *com_port; int com_custom_port = -1; int com_custom_irq = -1; int com_custom_base = -1; #endif int other_menu_choice = 0; int chars_sent = 0; // Com buffers static char syncbuffer[MAX_MULTI_MESSAGE_LEN+4]; static char sendbuf[MAX_MULTI_MESSAGE_LEN+4]; // +4 because of +1 for null and +3 for checksum/sequence // Serial setup variables int com_port_num = -1; int com_speed = 19200; char modem_init_string[INIT_STRING_LEN+1]; char phone_num[NUM_PHONE_NUM+1][LEN_PHONE_NUM+1]; char phone_name[NUM_PHONE_NUM][LEN_PHONE_NAME+1]; fix SerialLastMessage = 0; /* Function prototypes for functions not exported through modem.h */ void com_param_setup(void); void com_start_game(void); void modem_dialout(void); void modem_answer(void); int com_sync(int id); void com_sync_poll(int nitem, newmenu_item *menus, int *key, int citem); int detect_UART(unsigned baseaddr, int * loc, int * code ) { #ifdef __MSDOS__ // this function returns 0 if no UART is installed. // 1: 8250, 2: 16450 or 8250 with scratch reg., 3: 16550, 4: 16550A int x,olddata,temp; *loc = 0; *code = 0; // check if a UART is present. This is code John hacked by looking at the return // values from peoples computers. olddata=inp(baseaddr+4); outp(baseaddr+4,0x1f); // Enable Loopback mode, sets RTS & DTR to 1. delay(1); _disable(); temp = inp(baseaddr+6); // Read the state of RTS and DTR. temp = inp(baseaddr+6); // Do this twice, so that lower 4 bits are clear. OS/2 returns 0xB0 after this, // instead of 0xff if no port is there. _enable(); if ((temp&0x3f)!=0x30) { *loc = 1; *code = temp; return 0; } outp(baseaddr+4,olddata); // Restore RTS & DTR delay(1); // next thing to do is look for the scratch register olddata=inp(baseaddr+7); outp(baseaddr+7,0x55); delay(1); if (inp(baseaddr+7)!=0x55) return 1; outp(baseaddr+7,0xAA); delay(1); if (inp(baseaddr+7)!=0xAA) return 1; outp(baseaddr+7,olddata); // we don't need to restore it if it's not there delay(1); // then check if there's a FIFO outp(baseaddr+2,1); delay(1); x=inp(baseaddr+2); // some old-fashioned software relies on this! outp(baseaddr+2,0x0); delay(1); if ((x&0x80)==0) return 2; if ((x&0x40)==0) return 3; #endif return 4; } int com_type_detect() { #ifdef __MSDOS__ // static long port; // short *ptr; int loc, code; long port_addr[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; long portaddr; if (com_type != -1) return com_type; if ( (com_port_num != 0) && (com_port_num != 1) && (com_port_num != 2) && (com_port_num != 3) ) { Int3(); return -1; // Error, set com_port_num before calling this! } if (com_port_num == com_custom_port) portaddr = com_custom_base; else portaddr = port_addr[com_port_num]; mprintf((0, "com port %x.\n", portaddr)); switch( detect_UART(portaddr, &loc, &code) ) { case 0: // No UART mprintf((0, "No UART detected. (LOC:%d, CODE:0x%x)\n", loc, code)); return -1; case 1: // 8250 mprintf((0, "UART type is 8250.\n" )); return(16450); case 2: // 16450 or 8250 with scratch reg. mprintf((0, "UART is type 16450, or an 8250 with scratch register.\n" )); return(16450); case 3: // 16550 mprintf((0, "UART is type 16550, with FIFO bug.\n" )); return(16450); // 16550's FIFOs don't work. This is not a typo. (detected 16550, returned 16450 ) case 4: // 16550A, which is the only UART the FIFO mode works with. mprintf((0, "UART is type 16550A, no FIFO bug.\n" )); return(16550); } return (-1); #endif return(16550); // DPH: FIXME } #if !defined(NDEBUG) && !defined(NMONO) void com_dump_string(char *string) { mprintf((0, "%s\n", string)); } #else #define com_dump_string() #endif int com_enable() { // Detect and enable the COM port selected by the user #ifdef __MSDOS__ int rc; if (com_open) return 0; rx_seqnum = 0xff; tx_seqnum = 0; // com_port_num and com_speed should be set before calling this func com_type = com_type_detect(); if (com_custom_port == com_port_num) rc = Change8259Priority(com_custom_irq); else if ((com_port_num == COM2) || (com_port_num == COM4)) rc = Change8259Priority( IRQ3 ); else rc = Change8259Priority( IRQ4 ); if (rc != ASSUCCESS) { nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_SERIAL_OPEN_ERROR); return -1; } // If our detection is wrong, we want them to be able to go ahead and play anyway. // if (com_type == -1) // { // nm_messagebox(TXT_ERROR, 1, TXT_OK, "Error 2\n%s", TXT_SERIAL_OPEN_ERROR); // return -1; // } if (com_port_num == com_custom_port ) { rc = FastSetPortHardware(com_port_num, com_custom_irq, com_custom_base); if (rc != ASSUCCESS) { nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_SERIAL_OPEN_ERROR); return -1; } } if (com_type == 16550) { FastSet16550TriggerLevel( TRIGGER_04 ); FastSet16550UseTXFifos( ON ); } FastSavePortParameters(com_port_num); com_port = PortOpenGreenleafFast(com_port_num, com_speed, 'N', 8, 1); #ifndef NDEBUG { int curr_irq=0, curr_base=0; FastGetPortHardware(com_port_num, &curr_irq, &curr_base); mprintf((0, "Current port settings: base %x, irq %x.\n", curr_base, curr_irq )); } #endif if (com_port == 0) // || (com_port->status != 0) { nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_SERIAL_OPEN_ERROR); return -1; } //#ifndef NDEBUG //mprintf((0, "Com port opened. Status = %d\n", com_port->status)); //#endif SetDtr(com_port, ON); if ( FindArg( "-ctsrts" ) || FindArg( "-rtscts" ) ) UseRtsCts(com_port, ON); // Now used for null-modem as well, helps IHHD! else UseRtsCts(com_port, OFF); com_open = 1; commlib_initialised=com_open; master = -1; // DumpPortStatus(com_port, com_dump_string); #endif com_open = commlib_initialised; return 0; } void com_disable() { // close the com port and free associated structures. #ifdef __MSDOS__ int rc; if (!com_open) return; // SetDtr(com_port, OFF); // UseRtsCts(com_port, OFF); rc = PortClose(com_port); FastRestorePortParameters(com_port_num); if (com_custom_port == com_port_num) { // Custom settings were in effect, roll them back rc = FastSetPortHardware(com_port_num, default_irq[com_port_num], default_base[com_port_num]); } if (rc != ASSUCCESS) { #ifndef NDEBUG mprintf((1, "PortClose returned %d!\n", rc)); #endif } com_port = 0; com_open = 0; master = -1; #ifndef NDEBUG mprintf((0, "Com port released.\n")); #endif #endif #ifdef __LINUX__ com_port_hangup(); #endif com_open=0; } void com_abort(void) { // this is the safest way to get out of some modem/serial negotiation // and back to the main menu. Use this whenever this have gone too far // awry to repair. com_disable(); N_players = 0; change_playernum_to(0); Viewer = ConsoleObject = &Objects[0]; Game_mode = GM_GAME_OVER; // Force main menu selection } void com_hangup(void) { // Close the serial link com_send_choice(SELECTION_CLOSE_LINK); com_abort(); } void com_carrier_lost(void) { // Carrier lost, inform and abort if (multi_in_menu > 0) { multi_leave_menu = 1; return; } Function_mode = FMODE_MENU; multi_quit_game = 1; nm_messagebox(NULL, 1, TXT_OK, TXT_CARRIER_LOST); com_abort(); } extern ubyte cockpit_mode_save; // From object.c extern int old_cockpit_mode; // From game.c void com_reset_game(void) { int i; // Reset various parameters before starting a new game N_players = 2; for (i = 0; i < N_players; i++) { Players[i].connected = 1; } // master = -1; multi_new_game(); // Reset kill list, among other things Fuelcen_control_center_destroyed = 0; Endlevel_sequence = 0; // Yes, this really IS as ugly as it gets, kids... if (Cockpit_mode == CM_LETTERBOX) { select_cockpit(cockpit_mode_save); } else if (Cockpit_mode == CM_REAR_VIEW) { select_cockpit(old_cockpit_mode); } } void com_save_settings(void) { FILE *settings; int i; if ( (settings = fopen("serial.cfg", "wb")) == NULL) goto error; if (fwrite(&com_speed, sizeof(int), 1, settings) != 1) goto error; if (fwrite(&com_port_num, sizeof(int), 1, settings) != 1) goto error; if (fwrite(modem_init_string, 1, INIT_STRING_LEN+1, settings) != INIT_STRING_LEN+1) goto error; for (i = 0; i < NUM_PHONE_NUM; i++) { if (fwrite(phone_num[i], 1, LEN_PHONE_NUM+1, settings) != LEN_PHONE_NUM+1) goto error; if (fwrite(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1) goto error; } #ifdef __MSDOS__ if (fwrite(&com_custom_port, sizeof(int), 1, settings) != 1) goto error; if (fwrite(&com_custom_irq, sizeof(int), 1, settings) != 1) goto error; if (fwrite(&com_custom_base, sizeof(int), 1, settings) != 1) goto error; // 100 % success! #endif fclose(settings); return; error: nm_messagebox(NULL, 1, TXT_OK, TXT_ERROR_SERIAL_CFG); if (settings) { fclose(settings); unlink("serial.cfg"); } return; } void com_load_settings(void) { FILE *settings; int i, cfg_size; if ((settings = fopen("serial.cfg", "rb")) == NULL) goto defaults; cfg_size = ffilelength(settings); // Read the data from the file if (fread(&com_speed, sizeof(int), 1, settings) != 1) goto error; if (! ((com_speed == 9600) || (com_speed == 19200) || (com_speed == 38400)) ) goto error; if (fread(&com_port_num, sizeof(int), 1, settings) != 1) goto error; if ( (com_port_num < COM1) || (com_port_num > COM4) ) goto error; if (fread(modem_init_string, 1, INIT_STRING_LEN+1, settings) != INIT_STRING_LEN+1) goto error; modem_init_string[INIT_STRING_LEN] = '\0'; if (cfg_size <= 273 ) { // Old 15 char LEN_PHONE_NUM's mprintf(( 1, "Reading old pre 1.1 phone.cfg\n" )); for (i = 0; i < NUM_PHONE_NUM; i++) { if (fread(phone_num[i], 1, LEN_PHONE_NUM_OLD+1, settings) != LEN_PHONE_NUM_OLD+1) goto error; phone_num[i][LEN_PHONE_NUM_OLD] = '\0'; if (fread(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1) goto error; phone_name[i][LEN_PHONE_NAME] = '\0'; } } else { // Normal Phone nums for (i = 0; i < NUM_PHONE_NUM; i++) { if (fread(phone_num[i], 1, LEN_PHONE_NUM+1, settings) != LEN_PHONE_NUM+1) goto error; phone_num[i][LEN_PHONE_NUM] = '\0'; if (fread(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1) goto error; phone_name[i][LEN_PHONE_NAME] = '\0'; } } #ifdef __MSDOS__ if (fread(&com_custom_port, sizeof(int), 1, settings) != 1) { mprintf((0, "Reading old file format for serial.cfg.\n")); goto close; } if ( (com_custom_port < -1) || (com_custom_port > COM4) ) goto error; if (fread(&com_custom_irq, sizeof(int), 1, settings) != 1) goto error; if ( (com_custom_port < -1) || (com_custom_port > IRQ15) ) goto error; if (fread(&com_custom_base, sizeof(int), 1, settings) != 1) goto error; if (com_custom_base < -1) goto error; //Everything was A-Ok! close: #endif fclose(settings); return; error: nm_messagebox(NULL, 1, TXT_OK, TXT_ERR_SER_SETTINGS); defaults: // Return some defaults com_speed = 19200; // UART speed strcpy(modem_init_string, "ATZ"); com_port_num = COM2; #ifdef __MSDOS__ com_custom_port = -1; #endif for (i = 0; i < NUM_PHONE_NUM; i++) { phone_num[i][0] = '\0'; strcpy(phone_name[i], TXT_EMPTY); } if (settings) fclose(settings); return; } void serial_leave_game(void) { #ifndef NDEBUG mprintf((0, "Called serial_leave_game.\n")); #endif // com_abort(); serial_sync_abort(0); // Just in case the other guy is in sync mode Game_mode |= GM_GAME_OVER; Function_mode = FMODE_MENU; } void com_send_data(char *ptr, int len, int repeat) { int i; // Take the raw packet data specified by ptr and append the sequence // number and checksum, and pass it to com_send_ptr if (!com_open) return; if (Game_mode & GM_MODEM) { #ifdef __MSDOS__ i = GetCd(com_port); #else i = com_getdcd(); #endif if (i == 0) mprintf((0, "CARRIER LOST!\n")); } len += 3; // Checksum data is 3 bytes *(ubyte *)(ptr+(len-3)) = (tx_seqnum+1)%256; tx_seqnum = (tx_seqnum+1)%256; *(ushort *)(ptr+(len-2)) = netmisc_calc_checksum(ptr, len-2); com_send_ptr(ptr, len); if (repeat) for (i = 0; i < repeat; i++) com_send_ptr(ptr, len); } void com_send_ptr(char *ptr, int len) { register int count; char dat; for (count = 0, dat=ptr[0]; count < len; dat=ptr[++count]) { #ifdef __MSDOS__ WriteChar(com_port, dat); #else com_write(&dat,1); #endif if ((unsigned char)dat == EOR_MARK) #ifdef __MSDOS__ WriteChar(com_port, EOR_MARK); // double in-band endmarkers #else com_write(&dat,1); #endif } #ifdef __MSDOS__ WriteChar(com_port, EOR_MARK); // EOR WriteChar(com_port, 0); // EOR #else dat = EOR_MARK; com_write(&dat,1); dat = 0; com_write(&dat,1); #endif chars_sent += len; } void com_flush() { // Get rid of all waiting data in the serial buffer #ifdef __MSDOS__ int i = 0; if (!com_open) return; mprintf((0, "COM FLUSH:")); while(ReadCharTimed(com_port,100) >= 0) i++; mprintf((0, "%d characters. \n", i)); #else if (!com_open) return; com_flushbuffers(); #endif } int com_getchar() { #ifdef __MSDOS__ register int i; #else unsigned char i; int j; #endif static int eor_recv = 0; // return values: // -1 = Nothing in buffer // -2 = End of record // if (!com_open) // return(-1); #ifdef __MSDOS__ i = ReadChar(com_port); if(i == ASBUFREMPTY) return (-1); #else j=com_read(&i,1,0); if (j < 1) return (-1); #endif if ((i == EOR_MARK) || eor_recv) { #ifdef __MSDOS__ if (!eor_recv) i = ReadChar(com_port); if( i == ASBUFREMPTY) { eor_recv = 1; return(-1); } #else if (!eor_recv) j=com_read(&i,1,0); if (j < 1) { // Assert(eor_recv == 0); eor_recv = 1; return(-1); } #endif if (i == EOR_MARK) { eor_recv = 0; return(EOR_MARK); // Doubled EOR returns the character } else { #ifndef NDEBUG if (i != 0) { mprintf((0, "EOR followed by unexpected value %d.\n", i)); } #endif eor_recv = 0; return(-2); } } return(i); } #define SERIAL_IDLE_TIMEOUT F1_0*10 void com_do_frame(void) { static fix last_comm_time = 0; static int last_pos_skipped = 0; #ifndef SHAREWARE int rval = 0; #endif if (Endlevel_sequence || (com_process_mode==COM_PROCESS_ENDLEVEL)) { // Only recieve during endlevel int old_Endlevel_sequence = Endlevel_sequence; Endlevel_sequence = 1; com_process_input(); Endlevel_sequence = old_Endlevel_sequence; return; } last_comm_time += FrameTime; if ((last_comm_time > MIN_COMM_GAP) || Network_laser_fired) { #ifndef SHAREWARE if ((Game_mode & GM_MULTI_ROBOTS) && !last_pos_skipped) { rval = multi_send_robot_frame(0); } if (rval && !Network_laser_fired) { last_pos_skipped = 1; //-killed- goto skippos; } else #endif { last_pos_skipped = 0; multi_send_position(Players[Player_num].objnum); multi_send_fire(100); // Will return w/o sending if we haven't fired } //-killed- skippos: // mprintf((0, "%d chars sent, %f cps.\n", chars_sent, f2fl(fixdiv((chars_sent*F1_0),last_comm_time)) )); chars_sent = 0; last_comm_time = 0; } // else // multi_send_robot_frame(1); com_process_input(); if (!Fuelcen_control_center_destroyed && (Function_mode == FMODE_GAME) && (SerialLastMessage+SERIAL_IDLE_TIMEOUT < GameTime)) { SerialLastMessage = 0x7fffffff-SERIAL_IDLE_TIMEOUT; // Give no further warnings until next message arrives! nm_messagebox(TXT_WARNING, 1, TXT_OK, TXT_CONNECT_LOST, Players[OtherPlayer].callsign); } return; } int com_check_message(char *checkbuf, int len) { ushort check; int seqnum; if (len < 4) { // #ifndef NDEBUG mprintf((0, "message type %d too short to be a real message!\n", checkbuf[0])); // #endif goto error; } if (checkbuf[0] > MULTI_MAX_TYPE) { // #ifndef NDEBUG mprintf((1, "message type %d out of range.\n", checkbuf[0])); // #endif goto error; } //edit 03/05/99 Matt Mueller - allow ack'd packets to work on modem games if ((checkbuf[0]!=MEKH_PACKET_NEEDACK && (len-3) != message_length[(int)checkbuf[0]]) ||(checkbuf[0]==MEKH_PACKET_NEEDACK &&( len<8 || (len-10)!=message_length[(int)checkbuf[7]] )) ) //end edit -MM { // #ifndef NDEBUG mprintf((1, "message length %d != %d. (%i != %i)\n", len-3, message_length[(int) checkbuf[0]],len-10,message_length[(int)checkbuf[7]])); // #endif goto error; } check = netmisc_calc_checksum(checkbuf, len-2); if (check != *(ushort *)(checkbuf+(len-2))) { // #ifndef NDEBUG mprintf((0, "error in message type %d, length %d, checksum %d != %d\n", checkbuf[0], len, check, *(ushort *)(checkbuf+(len-2)))); // #endif goto error; } seqnum = checkbuf[(len-3)]; if (seqnum == rx_seqnum) { return -1; } if (seqnum != (rx_seqnum+1)%256) { // #ifndef NDEBUG mprintf((0, "Warning, missed 1 or more messages.\n")); // #endif } rx_seqnum = seqnum; // mprintf((0, "message type %d len %d OK!\n", checkbuf[0], len)); return 0; error: // mprintf((1,"Line status: %d.\n", GetLineStatus(com_port))); // ClearLineStatus(com_port); return -1; } void com_process_menu(char *buf, int len) { char text[80]; len = len; mprintf((0, "com_process_menu: type %d.\n", buf[0])); switch(buf[0]) { case MULTI_MESSAGE: #ifndef SHAREWARE sprintf(text, "%s %s\n'%s'", Players[OtherPlayer].callsign, TXT_SAYS, buf+2); #else sprintf(text, "%s %s\n'%s'", Players[OtherPlayer].callsign, TXT_SAYS, buf+3); #endif nm_messagebox(NULL, 1, TXT_OK, text); break; case MULTI_MENU_CHOICE: other_menu_choice = buf[1]; mprintf((0, "Other menu choice = %d.\n", other_menu_choice)); break; case MULTI_BEGIN_SYNC: // If we get a sync at the menu, send an abort sync, we're not ready yet! serial_sync_abort(0); break; } } void com_process_input(void) { // Read all complete messages from the serial buffer and process // the contents. Messages are read into global array snycbuffer. static int len = 0; int entry_com_mode = com_process_mode; register int dat=0; if (!com_open) return; nextmessage: if (Game_mode & GM_MODEM) { #ifdef __MSDOS__ if (!GetCd(com_port)) #else if (!com_getdcd()) #endif com_carrier_lost(); } if (!com_open) { if (!multi_in_menu) { multi_quit_game = 1; } else { multi_leave_menu = 1; } } if (com_process_mode != entry_com_mode) { // mprintf((0, "Exiting com_process_input due to mode switch.\n")); return; } while ( (len <= MAX_MULTI_MESSAGE_LEN) && (dat = com_getchar()) > -1) // Returns -1 when serial pipe empty { syncbuffer[len++] = dat; } if ((dat == -2) || (len > MAX_MULTI_MESSAGE_LEN)) // Returns -2 when end of message reached { // End of message SerialLastMessage = GameTime; if (!com_check_message(syncbuffer, len)) { switch(com_process_mode) { case COM_PROCESS_NORMAL: case COM_PROCESS_ENDLEVEL: multi_process_data(syncbuffer, len); break; case COM_PROCESS_MENU: if (!Endlevel_sequence) com_process_menu(syncbuffer, len); break; case COM_PROCESS_SYNC: if (!Endlevel_sequence) com_process_sync(syncbuffer, len); break; default: Int3(); // Bad com_process_mode switch set! } } len = 0; goto nextmessage; } if (dat == -3) // Returns -3 when carrier lost { com_abort(); len = 0; } return ; } int com_connect() { my_sync.type = MULTI_BEGIN_SYNC; my_sync.difficulty = 0; memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1); my_sync.seg_checksum = 0; my_sync.game_mode = Game_mode; my_sync.level_num = 0; #ifndef NDEBUG mprintf((0, "com_connect()\n")); #endif if(com_sync(-1)) return(-1); // Failure in sync if (master == -1) { #ifndef NDEBUG mprintf((0, "My rand = %d, other rand = %d.\n", my_sync.sync_time, other_sync.sync_time)); #endif // Figure out who is the master if (my_sync.sync_time > other_sync.sync_time) { mprintf((0, "Swtiching player to master.\n")); master=1; change_playernum_to(0); } else if (my_sync.sync_time < other_sync.sync_time) { mprintf((0, "Switching player to slave.\n")); master = 0; change_playernum_to(1); } else return(-1); // Didn't sync properly, try again } // Copy the remote sync data into local variables OtherPlayer = (Player_num+1)%2; mprintf((0, "Other player is #%d.\n", OtherPlayer)); memcpy(Players[OtherPlayer].callsign, other_sync.callsign, CALLSIGN_LEN+1); return(0); } #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) #define MENU_MODEM_CALL 0 #define MENU_MODEM_ANSWER 1 #define MENU_SERIAL_LINK_START 2 #define MENU_SERIAL_SETUP 3 #define MENU_MODEM_HANGUP 4 #define MENU_SERIAL_GAME_START 5 #define MENU_SEND_MESSAGE 6 void com_menu_poll(int nitems, newmenu_item *menus, int *key, int citem) { // Watch the serial stream if we are connected and take appropriate actions int old_game_mode; menus = menus; citem = citem; nitems = nitems; if (! ( (Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM) ) ) return; com_process_mode = COM_PROCESS_MENU; old_game_mode = Game_mode; other_menu_choice = 0; com_process_input(); if ((old_game_mode != Game_mode) || other_menu_choice || (com_process_mode != COM_PROCESS_MENU)) *key = -2; if (multi_leave_menu) *key = -2; } void com_send_choice(int choice) { sendbuf[0] = (char)MULTI_MENU_CHOICE; sendbuf[1] = (char)choice; com_send_data(sendbuf, 2, 1); } void com_ready_to_start(void) { newmenu_item m[2]; int choice; m[0].type = m[1].type = NM_TYPE_MENU; m[0].text = TXT_YES; m[1].text = TXT_NO; choice = newmenu_do1(NULL, TXT_READY_DESCENT, 2, m, com_menu_poll, 0 ); if (choice == 0) { // Yes com_send_choice(SELECTION_YES_START); other_menu_choice = SELECTION_STARTGAME; com_start_game(); } else { com_send_choice(SELECTION_NO_START); } } void com_process_other_menu_choice(void) { if (other_menu_choice == SELECTION_STARTGAME) com_ready_to_start(); else if (other_menu_choice == SELECTION_CLOSE_LINK) { nm_messagebox(NULL, 1, TXT_OK, TXT_CLOSED_LINK); com_hangup(); } } #define SUBTITLE_LEN 120 void com_main_menu(void) { newmenu_item m[10]; int menu_choice[10]; int num_options = 0; int choice=0; int old_game_mode; char subtitle[SUBTITLE_LEN]; int pcx_error; if (com_port_num == -1) com_load_settings(); setjmp(LeaveGame); gr_set_current_canvas(NULL); pcx_error = pcx_read_fullscr(Menu_pcx_name, NULL); Assert(pcx_error == PCX_ERROR_NONE); com_process_mode = COM_PROCESS_MENU; newmenu: num_options = 0; if (! ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)) ) { // We haven't established any type of link ADD_ITEM(TXT_DIAL_MODEM, MENU_MODEM_CALL, KEY_D); ADD_ITEM(TXT_ANSWER_MODEM, MENU_MODEM_ANSWER, KEY_A); ADD_ITEM(TXT_NULL_MODEM, MENU_SERIAL_LINK_START, KEY_E); ADD_ITEM(TXT_COM_SETTINGS, MENU_SERIAL_SETUP, KEY_C); } else { ADD_ITEM(TXT_START_GAME, MENU_SERIAL_GAME_START, KEY_S); ADD_ITEM(TXT_SEND_MESSAGEP, MENU_SEND_MESSAGE, KEY_S); } if (Game_mode & GM_MODEM) ADD_ITEM(TXT_HANGUP_MODEM, MENU_MODEM_HANGUP, KEY_H); if (Game_mode & GM_SERIAL) ADD_ITEM(TXT_CLOSE_LINK, MENU_MODEM_HANGUP, KEY_C); sprintf(subtitle, "%s\n\n", TXT_SERIAL_GAME); if (Game_mode & GM_SERIAL) sprintf(subtitle+strlen(subtitle), "%s %s\n%s", TXT_SERIAL, TXT_LINK_ACTIVE, Players[OtherPlayer].callsign); else if (Game_mode & GM_MODEM) sprintf(subtitle+strlen(subtitle), "%d %s %s %s\n%s", com_baud_rate, TXT_BAUD, TXT_MODEM, TXT_LINK_ACTIVE, Players[OtherPlayer].callsign); else sprintf(subtitle+strlen(subtitle), TXT_NOT_CONNECTED); multi_leave_menu = 0; Assert(strlen(subtitle) < SUBTITLE_LEN); choice = newmenu_do1(NULL, subtitle, num_options, m, com_menu_poll, 0); mprintf((0, "main menu choice was %d.\n", choice)); if (choice == -1) { if (!((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))) { com_disable(); return; } m[0].text = TXT_YES; m[1].text = TXT_NO; m[0].type = m[1].type = NM_TYPE_MENU; choice = newmenu_do1(NULL, TXT_EXIT_WILL_CLOSE, 2, m, com_menu_poll, 0); if (choice == 0) { com_send_choice(SELECTION_CLOSE_LINK); com_hangup(); return; } if ((choice == -2) && (other_menu_choice)) com_process_other_menu_choice(); goto newmenu; } if (choice == -2) { // Menu poll loop caused a re-draw if (other_menu_choice == SELECTION_STARTGAME) com_ready_to_start(); else if (other_menu_choice == SELECTION_CLOSE_LINK) { nm_messagebox(NULL, 1, TXT_OK, TXT_CLOSED_LINK); com_hangup(); } if (Function_mode == FMODE_GAME) return; if (!com_open) Game_mode = GM_GAME_OVER; goto newmenu; } if (choice > -1) { old_game_mode=Game_mode; switch (menu_choice[choice]) { case MENU_SERIAL_SETUP: com_param_setup(); goto newmenu; break; case MENU_SERIAL_GAME_START: com_start_game(); if (Function_mode != FMODE_GAME) goto newmenu; break; case MENU_MODEM_CALL: modem_dialout(); goto newmenu; break; case MENU_MODEM_ANSWER: modem_answer(); goto newmenu; break; case MENU_SEND_MESSAGE: multi_send_message_dialog(); if (Network_message_reciever != -1) multi_send_message(); multi_sending_message = 0; goto newmenu; break; case MENU_SERIAL_LINK_START: serial_link_start(); goto newmenu; break; case MENU_MODEM_HANGUP: com_hangup(); goto newmenu; break; default: Int3(); return; } } } void com_custom_param_setup(void) { // User menu for setting up custom IRQ/Base settings for a COM port newmenu_item mm[6]; int loc; char title[60]; #ifdef __MSDOS__ char base[10]; char irq[3]; int new_irq, new_base; #endif int menu_save, menu_reset; int mmn; sprintf(title, "%s%d", TXT_COM_CUSTOM_SETTINGS, com_port_num+1); #ifdef __MSDOS__ if (com_port_num != com_custom_port) { new_irq = default_irq[com_port_num]; new_base = default_base[com_port_num]; } else { new_irq = com_custom_irq; new_base = com_custom_base; } #endif newmenu: loc = 0; #ifdef __MSDOS__ sprintf(base, "%x", new_base); sprintf(irq, "%d", new_irq); mm[loc].type = NM_TYPE_TEXT; mm[loc].text = TXT_COM_BASE; loc++; mm[loc].type = NM_TYPE_INPUT; mm[loc].text = base; mm[loc].text_len = 9; loc++; mm[loc].type = NM_TYPE_TEXT; mm[loc].text = TXT_COM_IRQ; loc++; mm[loc].type = NM_TYPE_INPUT; mm[loc].text = irq, mm[loc].text_len = 2; loc++; #endif menu_reset = loc; mm[loc].type = NM_TYPE_MENU; mm[loc].text = TXT_RESET_DEFAULTS; loc++; menu_save = loc; mm[loc].type = NM_TYPE_MENU; mm[loc].text = TXT_ACCEPT; loc++; mmn = newmenu_do1(NULL, title, loc, mm, NULL, menu_save); if (mmn == -1) return; // All changes lost #ifdef __MSDOS__ new_irq = strtol(irq, NULL, 0); new_base = strtol(base, NULL, 16); if (mmn == menu_reset) { new_irq = default_irq[com_port_num]; new_base = default_base[com_port_num]; } if (mmn == menu_save) { if ((new_irq == default_irq[com_port_num]) && (new_base == default_base[com_port_num])) { com_custom_port = -1; mprintf((0, "Custom com settings not changed.\n")); return; } /* if ((new_irq < IRQ2) || (new_irq > IRQ7)) { new_irq = default_irq[com_port_num]; nm_messagebox(NULL, 1, TXT_OK, TXT_VALID_IRQS); goto newmenu; } */ com_custom_irq = new_irq; com_custom_base = new_base; com_custom_port = com_port_num; return; } #endif goto newmenu; } void com_param_setup_poll(int nitems, newmenu_item *menus, int *key, int citem) { #ifdef __MSDOS__ nitems = nitems; key = key; citem = citem; if ((com_custom_port == -1) && menus[4].value) { menus[4].value = 0; menus[4].redraw = 1; return; } if (com_custom_port == -1) return; if (menus[com_custom_port].value && !menus[4].value) { menus[4].value = 1; menus[4].redraw = 1; } else if (menus[4].value && !menus[com_custom_port].value) { menus[4].value = 0; menus[4].redraw = 1; } #endif } void com_param_setup(void) { int mmn; int was_enabled = 0; newmenu_item mm[12]; char init_string[INIT_STRING_LEN+1]; int changed = 0; int menu_baud, menu_custom, menu_save; int loc; strcpy (init_string, modem_init_string); com_type = -1; if (com_open) { was_enabled = 1; com_disable(); } setupmenu: loc = 0; mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM1); mm[loc].text="COM1"; mm[loc].group=0; loc++; mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM2); mm[loc].text="COM2"; mm[loc].group=0; loc++; mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM3); mm[loc].text="COM3"; mm[loc].group=0; loc++; mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_port_num == COM4); mm[loc].text="COM4"; mm[loc].group=0; loc++; menu_custom = loc; #ifdef __MSDOS__ mm[loc].type=NM_TYPE_CHECK; mm[loc].value=(com_port_num == com_custom_port); mm[loc].text=TXT_COM_CUSTOM_SETTINGS; loc++; #endif mm[loc].type=NM_TYPE_TEXT; mm[loc].text = TXT_BAUD_RATE; loc++; menu_baud = loc; mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_speed == 9600); mm[loc].text="9600"; mm[loc].group=1; loc++; mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_speed == 19200); mm[loc].text="19200"; mm[loc].group=1; loc++; mm[loc].type=NM_TYPE_RADIO; mm[loc].value=(com_speed == 38400); mm[loc].text="38400"; mm[loc].group=1; loc++; mm[loc].type=NM_TYPE_TEXT; mm[loc].text = TXT_MODEM_INIT_STRING; loc++; mm[loc].type=NM_TYPE_INPUT; mm[loc].text_len = INIT_STRING_LEN; mm[loc].text = init_string; loc++; menu_save = loc; mm[loc].type=NM_TYPE_MENU; mm[loc].text = TXT_ACCEPT_SAVE; loc++; mmn = newmenu_do1(NULL, TXT_SERIAL_SETTINGS, loc, mm, com_param_setup_poll, menu_save); if (mmn > -1 ) { changed = 1; if (mm[0].value) com_port_num = COM1; else if (mm[1].value) com_port_num = COM2; else if (mm[2].value) com_port_num = COM3; else com_port_num = COM4; if (mmn == menu_custom) { com_custom_param_setup(); } com_type = -1; com_type = com_type_detect(); if (com_type == -1) { nm_messagebox(NULL, 1, TXT_OK, "%s\n%s", TXT_WARNING, TXT_NO_UART); } if ((mm[menu_baud].value) || (mmn == menu_baud)) com_speed = 9600; else if ((mm[menu_baud+1].value) || (mmn == menu_baud+1)) com_speed = 19200; else { if (com_type == 16550) com_speed = 38400; else { nm_messagebox(NULL, 1, TXT_OK, TXT_WARNING_16550); com_speed = 19200; } } //mprintf((0, "%s\n", init_string)); if ((strncasecmp("AT", init_string, 2)) && (strlen(init_string) < (INIT_STRING_LEN-2))) sprintf(modem_init_string, "AT%s", init_string); else strcpy(modem_init_string, init_string); if (mmn != menu_save) goto setupmenu; } if (was_enabled) com_enable(); if (changed) com_save_settings(); } extern int opt_cinvul; extern int last_cinvul; void modem_game_param_poll( int nitems, newmenu_item * menus, int * key, int citem ) { nitems = nitems; key = key; citem = citem; #ifndef SHAREWARE if ( last_cinvul != menus[opt_cinvul].value ) { sprintf( menus[opt_cinvul].text, "%s: %d %s", TXT_REACTOR_LIFE, menus[opt_cinvul].value*5, TXT_MINUTES_ABBREV ); last_cinvul = menus[opt_cinvul].value; menus[opt_cinvul].redraw = 1; } #endif } // Handshaking to start a serial game, 2 players only int com_start_game_menu(void) { newmenu_item m[13]; char level[5]; int choice = 0; int opt, diff_opt, mode_opt; char level_text[32]; #ifndef SHAREWARE int options_opt; char srinvul[32]; int new_mission_num, anarchy_only = 0; new_mission_num = multi_choose_mission(&anarchy_only); if (new_mission_num < 0) return 0; strcpy(my_sync.mission_name, Mission_list[new_mission_num].filename); #endif sprintf(level, "1"); Game_mode &= ~GM_MULTI_COOP; Game_mode &= ~GM_MULTI_ROBOTS; Netgame.game_flags = 0; sprintf(level_text, "%s (1-%d)", TXT_LEVEL_, Last_level); if (Last_secret_level < -1) sprintf(level_text+strlen(level_text)-1, ", S1-S%d)", -Last_secret_level); else if (Last_secret_level == -1) sprintf(level_text+strlen(level_text)-1, ", S1)"); Assert(strlen(level_text) < 32); // Put up menu for user choices controlling gameplay newmenu: opt = 0; m[opt].type = NM_TYPE_TEXT; m[opt].text = level_text; opt++; m[opt].type = NM_TYPE_INPUT; m[opt].text_len = 4; m[opt].text = level; opt++; #ifdef ROCKWELL_CODE mode_opt = 0; #else m[opt].type = NM_TYPE_TEXT; m[opt].text = TXT_MODE; mode_opt = opt; m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_ANARCHY; m[opt].value=!(Game_mode & GM_MULTI_ROBOTS); m[opt].group = 0; opt++; 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++; m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_COOPERATIVE; m[opt].value=(Game_mode & GM_MULTI_COOP);m[opt].group = 0; opt++; #endif diff_opt = opt; 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++; #ifndef SHAREWARE options_opt = opt; // m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_IDS; m[opt].value=0; opt++; m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_ON_MAP; m[opt].value=0; opt++; opt_cinvul = opt; sprintf( srinvul, "%s: %d %s", TXT_REACTOR_LIFE, 5*control_invul_time, TXT_MINUTES_ABBREV ); last_cinvul = control_invul_time; 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++; #endif Assert(opt <= 13); choice = newmenu_do1(NULL, TXT_SERIAL_GAME_SETUP, opt, m, modem_game_param_poll, 1); if (choice > -1) { #ifdef ROCKWELL_CODE Game_mode |= (GM_MULTI_COOP | GM_MULTI_ROBOTS); #else if (m[mode_opt].value) Game_mode &= ~(GM_MULTI_COOP | GM_MULTI_ROBOTS); #ifdef SHAREWARE else { nm_messagebox(NULL, 1, TXT_OK, TXT_ONLY_ANARCHY); goto newmenu; } #else else if (anarchy_only) { nm_messagebox(NULL, 1, TXT_OK, TXT_ANARCHY_ONLY_MISSION); goto newmenu; } else if (m[mode_opt+1].value) { Game_mode &= ~GM_MULTI_COOP; Game_mode |= GM_MULTI_ROBOTS; } else Game_mode |= (GM_MULTI_COOP | GM_MULTI_ROBOTS); #endif #endif // ifdef ROCKWELL_CODE // if (m[options_opt].value) // Netgame.game_flags |= NETGAME_FLAG_SHOW_ID; #ifndef SHAREWARE if (m[options_opt].value) Netgame.game_flags |= NETGAME_FLAG_SHOW_MAP; #endif if (!strncasecmp(level, "s", 1)) start_level_num = -atoi(level+1); else start_level_num = atoi(level); if ((start_level_num < Last_secret_level) || (start_level_num > Last_level) || (start_level_num == 0)) { nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_LEVEL_OUT_RANGE); sprintf(level, "1"); goto newmenu; } Difficulty_level = m[diff_opt].value; #ifndef SHAREWARE control_invul_time = m[opt_cinvul].value; Netgame.control_invul_time = control_invul_time*5*F1_0*60; #endif return(1); // Go for game! } return 0; // No game } int com_ask_to_start() { // Ask the other player if its OK to start now newmenu_item m[1]; int choice; com_send_choice(SELECTION_STARTGAME); m[0].type = NM_TYPE_TEXT; m[0].text = TXT_ESC_ABORT; menu: choice = newmenu_do(NULL, TXT_WAIT_FOR_OK, 1, m, com_menu_poll); if (choice == -1) { com_send_choice(SELECTION_STARTGAME_ABORT); return(0); } if (choice == -2) { if (other_menu_choice == SELECTION_YES_START) return(1); else if (other_menu_choice == SELECTION_STARTGAME) { com_send_choice(SELECTION_YES_START); return(1); } else return(0); } goto menu; } void com_start_game() { // Start a serial game after being linked mprintf((0, "Entered com_start_game\n")); com_reset_game(); if (! ( (Game_mode & GM_MODEM) || (Game_mode & GM_SERIAL) ) ) return; Assert (master != -1); if (other_menu_choice != SELECTION_STARTGAME) { if (!com_ask_to_start()) return; } if (master == 1) // Master chooses options { if (com_start_game_menu()) { OtherPlayer = 1; change_playernum_to(0); my_sync.level_num = start_level_num; my_sync.difficulty = Difficulty_level; my_sync.game_mode = Game_mode; memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1); #ifndef SHAREWARE my_sync.sync_time = control_invul_time*5*F1_0*60; my_sync.game_flags = Netgame.game_flags; Netgame.control_invul_time = control_invul_time*5*F1_0*60; #endif com_sync(0); } } else // Slave { OtherPlayer = 0; change_playernum_to(1); memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1); my_sync.level_num = 1; com_sync(0); if (com_process_mode == COM_PROCESS_NORMAL) { Difficulty_level = other_sync.difficulty; start_level_num = other_sync.level_num; Game_mode = other_sync.game_mode; #ifndef SHAREWARE Netgame.game_flags = other_sync.game_flags; Netgame.control_invul_time = other_sync.sync_time; if (!load_mission_by_name(other_sync.mission_name)) { mprintf((0, "Mission not found: %s!\n", other_sync.mission_name)); nm_messagebox(NULL, 1, TXT_OK, TXT_MISSION_NOT_FOUND); my_sync.sync_id = start_level_num; serial_sync_abort(0); return; } #endif } } if (com_process_mode != COM_PROCESS_NORMAL) return; memcpy(Players[OtherPlayer].callsign, other_sync.callsign, CALLSIGN_LEN+1); Function_mode = FMODE_GAME; Game_mode &= ~GM_GAME_OVER; Show_kill_list = 1; init_player_stats_game(); init_player_stats_level(); // Assert(start_level_num > 0); Assert((start_level_num >= Last_secret_level) && (start_level_num <= Last_level)); StartNewLevel(start_level_num); //added 03/06/99 Matt Mueller - send d1x info in modem games too. // network_send_config_messages(100,2); //edited 04/19/99 Matt Mueller multi_d1x_ver_queue_init(2,1); //end edit -MM //end addition -MM } // // Modem control functions, dialing, answering, etc. // void modem_edit_phonebook(newmenu_item *m) { int choice, choice2; newmenu_item menu[5]; char text[2][100]; int default_choice = 0; m[NUM_PHONE_NUM].text = TXT_SAVE; menu[0].text = TXT_NAME; menu[0].type = NM_TYPE_TEXT; menu[1].type = NM_TYPE_INPUT; menu[1].text = text[0]; menu[1].text_len = LEN_PHONE_NAME; menu[2].text = TXT_PHONE_NUM; menu[2].type = NM_TYPE_TEXT; menu[3].type = NM_TYPE_INPUT; menu[3].text = text[1]; menu[3].text_len = LEN_PHONE_NUM; menu[4].text = TXT_ACCEPT; menu[4].type = NM_TYPE_MENU; menu: choice = newmenu_do1(NULL, TXT_SEL_NUMBER_EDIT, NUM_PHONE_NUM+1, m, NULL, default_choice); if (choice == -1) { com_load_settings(); return; } if (choice == NUM_PHONE_NUM) { // Finished com_save_settings(); return; } default_choice = 1; edit: // Edit an entry strcpy(menu[1].text, phone_name[choice]); strcpy(menu[3].text, phone_num[choice]); choice2 = newmenu_do1(NULL, TXT_EDIT_PHONE_ENTRY, 5, menu, NULL, default_choice); if (choice2 != -1) { strcpy(phone_name[choice], menu[1].text); strcpy(phone_num[choice], menu[3].text); sprintf(m[choice].text, "%d. %s \t", choice+1, phone_name[choice]); add_phone_number(m[choice].text, phone_num[choice] ); } if (choice2 != 4) { default_choice += 2; if (default_choice > 4) default_choice = 4; goto edit; } default_choice = NUM_PHONE_NUM; goto menu; } void add_phone_number( char * src, char * num ) { char p; int l; l = strlen(num); if ( l<15) { strcat( src, num ); return; } p = num[15]; num[15] = 0; strcat( src, num ); num[15] = p; strcat( src, "..." ); } int modem_dial_menu(void) { newmenu_item m[NUM_PHONE_NUM+2]; char menu_text[NUM_PHONE_NUM][80]; int choice = 0; int i; menu: for (i = 0; i < NUM_PHONE_NUM; i++) { m[i].text = menu_text[i]; sprintf(m[i].text, "%d. %s \t", i+1, phone_name[i]); add_phone_number(m[i].text, phone_num[i] ); m[i].type = NM_TYPE_MENU; } strcat(m[i-1].text, "\n"); m[NUM_PHONE_NUM].type = NM_TYPE_MENU; m[NUM_PHONE_NUM].text = TXT_MANUAL_ENTRY; m[NUM_PHONE_NUM+1].text = TXT_EDIT_PHONEBOOK; m[NUM_PHONE_NUM+1].type = NM_TYPE_MENU; choice = newmenu_do1(NULL, TXT_SEL_NUMBER_DIAL, NUM_PHONE_NUM+2, m, NULL, 0); if (choice == -1) return -1; // user abort if (choice == NUM_PHONE_NUM+1) { // Edit phonebook modem_edit_phonebook(m); goto menu; } if (choice == NUM_PHONE_NUM) { // Manual entry newmenu_item m2[1]; m2[0].type = NM_TYPE_INPUT; m2[0].text = phone_num[NUM_PHONE_NUM]; m2[0].text_len = LEN_PHONE_NUM; choice = newmenu_do(NULL, TXT_ENTER_NUMBER_DIAL, 1, m2, NULL); if (choice == -1) goto menu; else return NUM_PHONE_NUM; } // A phone number was chosen return(choice); } void com_wait_for_connect(int nitems, newmenu_item *menus, int *key, int citem) { int result; char input_buffer[81]; int baud; char error_mess[5][15] = {"NO DIAL TONE", "BUSY", "NO ANSWER", "NO CARRIER", "VOICE"}; char text[100]; int i; int num_error_messages = 5; menus = menus; nitems = nitems; citem = citem; #ifdef __MSDOS__ if (GetCd(com_port)) #else if (com_getdcd()) #endif { carrier_on = 1; } else { if (carrier_on) { *key = -3; nm_messagebox(NULL, 1, TXT_OK, TXT_CARRIER_LOST); carrier_on = 0; return; } } #ifdef __MSDOS__ result = HMInputLine(com_port, 500, input_buffer, 80); #else result = com_readline(500,input_buffer,80); #endif if (result == 0) return; // Timed out mprintf((0, "Modem string: '%s'\n", input_buffer)); for (i = 0; i < num_error_messages; i++) { if (!strncmp(input_buffer, error_mess[i], strlen(error_mess[i]))) { sprintf(text, "%s %s", TXT_ERR_MODEM_RETURN, input_buffer); nm_messagebox(NULL, 1, TXT_OK, text); *key = -3; return; } } if (strncmp(input_buffer, TXT_CONNECT, 7)) { mprintf((0, "Non-connect message found.\n")); return; // some other string. Not an error, but not a connect } sscanf(input_buffer, "CONNECT %d", &baud); mprintf((0, "Connect at %d baud.\n", baud)); if (baud < 9600) { nm_messagebox(NULL, 1, TXT_OK, TXT_BAUD_GREATER_9600); *key = -3; return; } com_baud_rate = baud; *key = -2; return; } void com_wait_for_ring(int nitems, newmenu_item *menus, int *key, int citem) { int result; char input_buffer[81]; menus = menus; nitems = nitems; citem = citem; #ifdef __MSDOS__ result = HMInputLine(com_port, 500, input_buffer, 80); #else result = com_readline(500,input_buffer,80); #endif if ((result <= 0) || strncmp(input_buffer, TXT_RING, 4)) return; *key = -2; return; } int modem_verify(void) { #ifdef __MSDOS__ // Is there a modem on this com port or not? int result; //need to make HMWaitforOK -DO- something HMWaitForOK( 5000, NULL); //================================================= // This was changed by John in response to a // Creative voice modem not working, since this // code doesn't wait for an OK. //OLD HMSendStringNoWait(com_port, "AT", -2); //OLD result = HMSendString(com_port, "AT"); HMSendString(com_port, "AT" ); result = HMSendString(com_port, "AT"); //================================================= if (result != 0) return (0); #endif return(1); } void modem_dialout(void) { newmenu_item m[5]; char text[50]; int choice; if (!serial_active) { nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT); return; } com_enable(); // Open COM port as configured if (!com_open) return; // UseRtsCts(com_port, ON); // use hardware handshaking main: if ((choice = modem_dial_menu()) == -1) return; // user aborted show_boxed_message(TXT_RESET_MODEM); // Verify presence of modem if (!modem_verify()) { clear_boxed_message(); nm_messagebox(NULL, 1, TXT_OK, TXT_NO_MODEM); com_abort(); return; } if (strlen(phone_num[choice]) == 0) { clear_boxed_message(); nm_messagebox(NULL, 1, TXT_OK, TXT_NO_PHONENUM); goto main; } clear_boxed_message(); sprintf(text, "%s\n%s", TXT_DIALING, phone_num[choice]); show_boxed_message(text); // Proceed with the call #ifdef __MSDOS__ HMReset( com_port ); HMSendString( com_port, modem_init_string ); HMDial( com_port, phone_num[choice] ); #else { char buffer[1024]; //edited 02/06/99 Matt Mueller - added preceding CR, and extra readlines, modem won't dial until after the OK has been printed sprintf(buffer,"\r\n%s\r\n",modem_init_string); com_write(buffer,strlen(buffer)); com_readline(1000,buffer,1024);//first CR com_readline(1000,buffer,1024);//init string com_readline(2000,buffer,1024);//OK //end edit -MM sprintf(buffer,"ATDT%s\r\n", phone_num[choice]); com_write(buffer,strlen(buffer)); } #endif carrier_on = 0; clear_boxed_message(); m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT; //repeat: choice = newmenu_do(NULL, TXT_WAITING_FOR_ANS, 1, m, com_wait_for_connect); if (choice != -2) { // HMSendStringNoWait(com_port, "", -2); com_abort(); return; } // We are now connected to the other modem N_players = 2; master = 1; // The person who dialed is the master of the connection change_playernum_to(0); if (!com_connect()) { Game_mode |= GM_MODEM; digi_play_sample(SOUND_HUD_MESSAGE, F1_0); } else { #ifdef __MSDOS__ HMSendStringNoWait(com_port, "", -2); #endif com_abort(); } } void modem_answer(void) { int choice; newmenu_item m[3]; if (!serial_active) { nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT); return; } com_enable(); // Open COM port as configured if (!com_open) return; // UseRtsCts(com_port, ON); // use hardware handshaking show_boxed_message(TXT_RESET_MODEM); // Verify presence of modem if (!modem_verify()) { clear_boxed_message(); nm_messagebox(NULL, 1, TXT_OK, TXT_NO_MODEM); com_abort(); return; } #ifdef __MSDOS__ HMReset( com_port ); HMSendStringWait( com_port, modem_init_string,1000); HMSendStringWait( com_port,"AT",1000); // to set the DTE rate for RING notification #else { char buffer[1024]; //edited 02/06/99 Matt Mueller - added preceding CR, and extra readlines, modem won't dial until after the OK has been printed sprintf(buffer,"\r\n%s\r\n",modem_init_string); com_write(buffer,strlen(buffer)); com_readline(1000,buffer,1024);//first CR com_readline(1000,buffer,1024);//init string com_readline(2000,buffer,1024);//OK //end edit -MM strcpy(buffer,"AT\r\n"); com_write(buffer,strlen(buffer)); } #endif clear_boxed_message(); m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT; repeat: choice = newmenu_do(NULL, TXT_WAITING_FOR_CALL, 1, m, com_wait_for_ring); if (choice == -1) { // HMSendStringNoWait(com_port, "", -2); com_abort(); return; } if (choice != -2) goto repeat; // Now answer the phone and wait for carrier #ifdef __MSDOS__ HMAnswer(com_port); #else { char buffer[8]; strcpy(buffer,"ATA\r\n"); com_write(buffer,strlen(buffer)); } #endif carrier_on = 0; choice = newmenu_do(NULL, TXT_WAITING_FOR_CARR, 1, m, com_wait_for_connect); if (choice != -2) { #ifdef __MSDOS__ HMSendStringNoWait(com_port, "", -2); #endif com_abort(); return; } // We are now connected to the other modem N_players = 2; master = 0; change_playernum_to(1); if (!com_connect()) { Game_mode |= GM_MODEM; digi_play_sample(SOUND_HUD_MESSAGE, F1_0); } else { #ifdef __MSDOS__ HMSendStringNoWait(com_port, "", -2); #endif com_abort(); } } void serial_link_start(void) { if (!serial_active) { nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT); return; } com_enable(); // Open COM port as configured if (!com_open) { nm_messagebox(NULL, 1, TXT_OK, "Failed to open com port."); return; } N_players = 2; synccnt = 0; d_srand(clock()); my_sync.sync_time = d_rand(); mprintf((0, "My rand set to %d.\n", my_sync.sync_time)); if (!com_connect()) { Game_mode |= GM_SERIAL; digi_play_sample(SOUND_HUD_MESSAGE, F1_0); } else { nm_messagebox(NULL, 1, TXT_OK, "%s\n%s", TXT_ERROR, TXT_FAILED_TO_NEGOT); } } // // Syncronization functions // void serial_sync_abort(int val) { // Send "I got Sync but it was no good!" packet sendbuf[0] = (char)MULTI_END_SYNC; sendbuf[1] = Player_num; sendbuf[2] = (char)val; // Indicated failure #ifndef SHAREWARE sendbuf[3] = my_sync.sync_id; com_send_data(sendbuf, 4, 1); #else com_send_data(sendbuf, 3, 1); #endif } int com_level_sync(void) { // Send between-level sync stuff mprintf((0, "entered com_level_sync()\n")); Function_mode = FMODE_MENU; // Prevent the game loop from running during the menus! // At this point, the new level is loaded but the extra objects or players have not // been removed my_sync.level_num = Current_level_num; my_sync.seg_checksum = netmisc_calc_checksum(Segments, (Highest_segment_index+1) * sizeof(segment)); my_sync.kills[0] = kill_matrix[Player_num][0]; my_sync.kills[1] = kill_matrix[Player_num][1]; my_sync.proto_version = MULTI_PROTO_VERSION; #ifndef SHAREWARE my_sync.killed = Players[Player_num].net_killed_total; #endif d_srand(clock()); if (Game_mode & GM_MULTI_COOP) my_sync.difficulty = Player_num; else my_sync.difficulty = d_rand()%MAX_NUM_NET_PLAYERS; // My starting position if (com_sync(Current_level_num)) { com_process_mode = COM_PROCESS_MENU; nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NEGOTIATION_FAIL ); longjmp(LeaveGame, 1); } if (my_sync.level_num != other_sync.level_num) { // Fatal error 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); longjmp(LeaveGame, 1); } if (my_sync.seg_checksum != other_sync.seg_checksum) { // Checksum failure mprintf((1, "My check %d, other check %d.\n", my_sync.seg_checksum, other_sync.seg_checksum)); 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); longjmp(LeaveGame, 1); } if (my_sync.proto_version != other_sync.proto_version) { // Version mismatch nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_DESCENT_NO_MATCH); longjmp(LeaveGame, 1); } mprintf((0, "My pos = %d, other pos = %d.\n", my_sync.difficulty, other_sync.difficulty)); if ((other_sync.difficulty == my_sync.difficulty) && !master) { // If we chose the same position and I am the slave, choose another my_sync.difficulty = (my_sync.difficulty+1) % MAX_NUM_NET_PLAYERS; } Objects[Players[OtherPlayer].objnum].pos = Player_init[(int)other_sync.difficulty].pos; Objects[Players[OtherPlayer].objnum].orient = Player_init[(int)other_sync.difficulty].orient; obj_relink(Players[OtherPlayer].objnum,Player_init[(int)other_sync.difficulty].segnum); Objects[Players[OtherPlayer].objnum].type = OBJ_PLAYER; Objects[Players[Player_num].objnum].pos = Player_init[(int)my_sync.difficulty].pos; Objects[Players[Player_num].objnum].orient = Player_init[(int)my_sync.difficulty].orient; obj_relink(Players[Player_num].objnum, Player_init[(int)my_sync.difficulty].segnum); Objects[Players[Player_num].objnum].type = OBJ_PLAYER; SerialLastMessage = GameTime; kill_matrix[OtherPlayer][0] = other_sync.kills[0]; kill_matrix[OtherPlayer][1] = other_sync.kills[1]; Players[Player_num].net_kills_total = kill_matrix[Player_num][OtherPlayer] - kill_matrix[Player_num][Player_num]; Players[OtherPlayer].net_kills_total = kill_matrix[OtherPlayer][Player_num] - kill_matrix[OtherPlayer][OtherPlayer]; // Players[Player_num].net_killed_total = kill_matrix[0][Player_num] + kill_matrix[1][Player_num]; #ifdef SHAREWARE Players[OtherPlayer].net_killed_total = kill_matrix[0][OtherPlayer] + kill_matrix[1][OtherPlayer]; #else Players[OtherPlayer].net_killed_total = other_sync.killed; #endif Players[OtherPlayer].connected = Players[Player_num].connected = 1; Assert(N_players == 2); Assert(Player_num != OtherPlayer); gr_palette_fade_out(gr_palette, 32, 0); Function_mode = FMODE_GAME; com_process_mode = COM_PROCESS_NORMAL; multi_sort_kill_list(); return(0); } void com_send_end_sync(void) { // Send "I got Sync" packet sendbuf[0] = (char)MULTI_END_SYNC; sendbuf[1] = Player_num; sendbuf[2] = 1; // Indicates success #ifndef SHAREWARE sendbuf[3] = my_sync.sync_id; com_send_data(sendbuf, 4, 2); #else com_send_data(sendbuf, 3, 2); #endif } void com_send_begin_sync(void) { mprintf((0, "Sending my sync.\n")); com_send_data((char *)&my_sync, sizeof(com_sync_pack)-3, 1); } void com_process_end_sync(byte *buf) { // Process incoming end-sync packet if (buf[2] != 1) { com_process_mode = COM_PROCESS_MENU; return; } #ifndef SHAREWARE if (buf[3] == my_sync.sync_id) #endif other_got_sync = 1; } void com_process_sync(char *buf, int len) { len = len; switch(buf[0]) { case MULTI_END_SYNC: { com_process_end_sync(buf); break; } case MULTI_BEGIN_SYNC: { mprintf((0, "Incoming begin_sync message.\n")); if (got_sync) break; memcpy(&other_sync, buf, sizeof(com_sync_pack)-3); #ifndef SHAREWARE if (other_sync.sync_id != my_sync.sync_id) { mprintf((0, "Other sync invalid id, %d != %d.\n", other_sync.sync_id, my_sync.sync_id)); } else #endif { mprintf((0, "got other sync size %d.\n", sizeof(com_sync_pack)-3)); got_sync = 1; com_send_end_sync(); } break; } } // switch if (got_sync && other_got_sync) { // OK to start game // mprintf((1, "Starting game.\n")); got_sync = 0; other_got_sync = 0; com_process_mode = COM_PROCESS_NORMAL; } } void com_send_sync(void) { // Send sync information, depending on the situation if (!other_got_sync) { com_send_begin_sync(); } if (got_sync) { com_send_end_sync(); } } void com_sync_poll(int nitems, newmenu_item *menus, int *key, int citem) { static fix t1 = 0; menus = menus; nitems = nitems; citem = citem; if (!com_open) { *key = -3; return; } if (timer_get_approx_seconds() > t1+F1_0) { com_send_sync(); t1 = timer_get_approx_seconds(); } Assert(com_process_mode == COM_PROCESS_SYNC); com_process_input(); if (com_process_mode == COM_PROCESS_NORMAL) { *key = -2; com_send_sync(); mprintf((0, "Sync finished.\n")); return; } if (com_process_mode == COM_PROCESS_MENU) { *key = -3; mprintf((0, "Sync denied by other side.\n")); return; } return; } int com_sync(int id) { // How to handle the end of the level and start of the next level // returns 0 for success or 1 for failure int choice; newmenu_item m[3]; int pcx_error; mprintf((0, "Entered com_sync\n")); gr_set_current_canvas(NULL); pcx_error = pcx_read_bitmap(Menu_pcx_name, &grd_curcanv->cv_bitmap,grd_curcanv->cv_bitmap.bm_type,NULL); Assert(pcx_error == PCX_ERROR_NONE); com_process_mode = COM_PROCESS_SYNC; got_sync = other_got_sync = 0; com_flush(); com_flush(); #ifndef SHAREWARE my_sync.sync_id = id; #endif m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT; m[1].type = m[2].type = NM_TYPE_MENU; m[1].text = TXT_YES; m[2].text = TXT_NO; repeat: choice = newmenu_do(NULL, TXT_WAIT_OPPONENT, 1, m, com_sync_poll); if (choice == -1) { choice = newmenu_do1(NULL, TXT_SURE_ABORT_SYNC, 2, m+1, com_sync_poll, 1); if (choice == -1) choice = 1; if (choice == 0) choice = -1; } if ((choice == -1) || (choice == -3)) { return(-1); } else if (choice != -2) goto repeat; return(0); } void com_endlevel(int *secret) { // What do we do between levels? Function_mode = FMODE_MENU; gr_palette_fade_out(gr_palette, 32, 0); my_sync.level_num = multi_goto_secret; if (com_sync(-3)) { com_process_mode = COM_PROCESS_MENU; nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NEGOTIATION_FAIL); longjmp(LeaveGame, 1); } com_process_mode = COM_PROCESS_ENDLEVEL; if ((multi_goto_secret == 1) || (other_sync.level_num == 1)) *secret = 1; else *secret = 0; multi_goto_secret = 0; return; } #endif