home *** CD-ROM | disk | FTP | other *** search
/ Amiga ACS 1998 #6 / amigaacscoverdisc1998-061998.iso / games / descent / source / main / multi.c < prev    next >
C/C++ Source or Header  |  1998-06-08  |  76KB  |  2,910 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/multi.c $
  15.  * $Revision: 2.10 $
  16.  * $Author: john $
  17.  * $Date: 1995/05/29 16:18:26 $
  18.  * 
  19.  * Multiplayer code shared by serial and network play.
  20.  * 
  21.  */
  22.  
  23. #ifdef NETWORK
  24.  
  25. #define DOS4G
  26.  
  27. #pragma off (unreferenced)
  28. static char rcsid[] = "$Id: multi.c 2.10 1995/05/29 16:18:26 john Exp $";
  29. #pragma on (unreferenced)
  30.  
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <dos.h>
  34. #include <conio.h>
  35. #include <string.h>
  36. #include <time.h>
  37.  
  38. #include "game.h"
  39. #include "modem.h"
  40. #include "network.h"
  41. #include "multi.h"
  42. #include "object.h"
  43. #include "laser.h"
  44. #include "fuelcen.h"
  45. #include "scores.h"
  46. #include "gauges.h"
  47. #include "collide.h"
  48. #include "error.h"
  49. #include "fireball.h"
  50. #include "newmenu.h"
  51. #include "mono.h"
  52. #include "wall.h"
  53. #include "cntrlcen.h"
  54. #include "powerup.h"
  55. #include "polyobj.h"
  56. #include "bm.h"
  57. #include "endlevel.h"
  58. #include "key.h"
  59. #include "playsave.h"
  60. #include "timer.h"
  61. #include "digi.h"
  62. #include "sounds.h"
  63. #include "newdemo.h"
  64. #include "text.h"
  65. #include "kmatrix.h"
  66. //#include "glfmodem.h"//This and the next file aren't part of the public release -KRB
  67. //#include "commlib.h"
  68. #include "multibot.h"
  69. #include "gameseq.h"
  70. #include "physics.h"
  71. #include "config.h"
  72. #include "state.h"
  73.  
  74. //*******************************************
  75. typedef struct  {
  76.     char buffer[256];
  77.     unsigned char write_index;
  78.     unsigned char read_index;
  79. } BUFFER;
  80.  
  81. typedef struct  {
  82.     void (interrupt far * old_vector)();
  83.     int uart_base;
  84.     int irq_mask;
  85.     int interrupt_number;
  86.     BUFFER in;
  87.     BUFFER out;
  88. } PORT; //I added this from serial.c, it will compile, but I doubt it works. -KRB
  89. //*******************************************
  90. //
  91. // Local macros and prototypes
  92. //
  93.  
  94. // LOCALIZE ME!!
  95.  
  96. #define vm_angvec_zero(v) (v)->p=(v)->b=(v)->h=0
  97.  
  98. void reset_player_object(void); // In object.c but not in object.h
  99. void drop_player_eggs(object *player); // from collide.c
  100. void StartLevel(void); // From gameseq.c
  101. void GameLoop(int, int); // From game.c
  102.  
  103. //
  104. // Global variables
  105. //
  106.  
  107. int control_invul_time = 0;
  108. int who_killed_controlcen = -1;  // -1 = noone
  109.  
  110. //do we draw the kill list on the HUD?
  111. int Show_kill_list = 1;
  112. int Show_reticle_name = 1;
  113. fix Show_kill_list_timer = 0;
  114.  
  115. int multi_sending_message = 0;
  116. int multi_defining_message = 0;
  117. int multi_message_index = 0;
  118.  
  119. char multibuf[MAX_MULTI_MESSAGE_LEN+4];                // This is where multiplayer message are built
  120.  
  121. short remote_to_local[MAX_NUM_NET_PLAYERS][MAX_OBJECTS];  // Remote object number for each local object
  122. short local_to_remote[MAX_OBJECTS]; 
  123. byte  object_owner[MAX_OBJECTS];   // Who created each object in my universe, -1 = loaded at start
  124.  
  125. int     Net_create_objnums[MAX_NET_CREATE_OBJECTS]; // For tracking object creation that will be sent to remote
  126. int     Net_create_loc = 0;  // pointer into previous array
  127. int    Network_laser_fired = 0; // How many times we shot
  128. int    Network_laser_gun; // Which gun number we shot
  129. int   Network_laser_flags; // Special flags for the shot
  130. int   Network_laser_level; // What level
  131. short    Network_laser_track; // Who is it tracking?
  132. char    Network_message[MAX_MESSAGE_LEN];
  133. char  Network_message_macro[4][MAX_MESSAGE_LEN];
  134. int    Network_message_reciever=-1;
  135. int    sorted_kills[MAX_NUM_NET_PLAYERS];
  136. short kill_matrix[MAX_NUM_NET_PLAYERS][MAX_NUM_NET_PLAYERS];
  137. int     multi_goto_secret = 0;
  138. short    team_kills[2];
  139. int     multi_in_menu = 0;
  140. int     multi_leave_menu = 0;
  141. int     multi_quit_game = 0;
  142.  
  143. netgame_info Netgame;
  144.  
  145. bitmap_index multi_player_textures[MAX_NUM_NET_PLAYERS][N_PLAYER_SHIP_TEXTURES];
  146.  
  147. typedef struct netplayer_stats {
  148.     ubyte        message_type;
  149.     ubyte        Player_num;                        // Who am i?
  150.     uint        flags;                            // Powerup flags, see below...
  151.     fix        energy;                            // Amount of energy remaining.
  152.     fix        shields;                            // shields remaining (protection) 
  153.     ubyte        lives;                            // Lives remaining, 0 = game over.
  154.      ubyte        laser_level;                    //    Current level of the laser.
  155.     ubyte        primary_weapon_flags;                    //    bit set indicates the player has this weapon.
  156.     ubyte        secondary_weapon_flags;                    //    bit set indicates the player has this weapon.
  157.     ushort    primary_ammo[MAX_PRIMARY_WEAPONS];    // How much ammo of each type.
  158.     ushort    secondary_ammo[MAX_SECONDARY_WEAPONS]; // How much ammo of each type.
  159.     int        last_score;                    // Score at beginning of current level.
  160.     int        score;                            // Current score.
  161.     fix        cloak_time;                        // Time cloaked
  162.     fix        invulnerable_time;            // Time invulnerable
  163.     fix        homing_object_dist;            //    Distance of nearest homing object.
  164.     short        net_killed_total;                // Number of times killed total
  165.      short        net_kills_total;                // Number of net kills total
  166.     short        num_kills_level;                // Number of kills this level
  167.      short        num_kills_total;                // Number of kills total
  168.     short        num_robots_level;             // Number of initial robots this level
  169.     short        num_robots_total;             // Number of robots total
  170.     ushort     hostages_rescued_total;        // Total number of hostages rescued.
  171.     ushort    hostages_total;                // Total number of hostages.
  172.     ubyte        hostages_on_board;            //    Number of hostages on ship.
  173.     ubyte        unused[16];
  174. } netplayer_stats;                              
  175.  
  176. int message_length[MULTI_MAX_TYPE+1] = {
  177.     24, // POSITION
  178.     3,  // REAPPEAR
  179.     8,  // FIRE
  180. #ifdef SHAREWARE
  181.     7,  // KILL
  182. #else
  183.     5,  // KILL
  184. #endif
  185.     4,  // REMOVE_OBJECT
  186. #ifdef SHAREWARE
  187.     56, // PLAYER_EXPLODE
  188. #else
  189.     57, // PLAYER_EXPLODE
  190. #endif
  191. #ifdef SHAREWARE
  192.     28, // MESSAGE (MAX_MESSAGE_LENGTH = 25)
  193. #else
  194.     37, // MESSAGE (MAX_MESSAGE_LENGTH = 40)
  195. #endif
  196.     2,  // QUIT
  197. #ifdef SHAREWARE
  198.     10, // PLAY_SOUND
  199.     24, // BEGIN_SYNC
  200. #else
  201.     4,  // PLAY_SOUND
  202.     37, // BEGIN_SYNC
  203. #endif
  204.     4,  // CONTROLCEN
  205.     5,  // CLAIM ROBOT
  206. #ifdef SHAREWARE
  207.     3,  // END_SYNC
  208. #else
  209.     4,  // END_SYNC
  210. #endif
  211.    2,  // CLOAK
  212.     3,  // ENDLEVEL_START
  213. #ifdef SHAREWARE
  214.     7,  // DOOR_OPEN
  215. #else
  216.    4,  // DOOR_OPEN
  217. #endif
  218.     2,  // CREATE_EXPLOSION
  219.     16, // CONTROLCEN_FIRE
  220. #ifdef SHAREWARE
  221.     56, // PLAYER_DROP
  222.     7,  // CREATE_POWERUP
  223. #else
  224.     57, // PLAYER_DROP
  225.     19, // CREATE_POWERUP
  226. #endif
  227.     9,  // MISSILE_TRACK
  228.     2,  // DE-CLOAK
  229. #ifndef SHAREWARE
  230.     2,     // MENU_CHOICE
  231.     28, // ROBOT_POSITION  (shortpos_length (23) + 5 = 28)
  232.     8,  // ROBOT_EXPLODE
  233.     5,     // ROBOT_RELEASE
  234.     18, // ROBOT_FIRE
  235.     6,  // SCORE
  236.     6,  // CREATE_ROBOT
  237.     3,  // TRIGGER
  238.     10, // BOSS_ACTIONS    
  239.     27, // ROBOT_POWERUPS
  240.     7,  // HOSTAGE_DOOR
  241. #else
  242.     2,     // MENU_CHOICE
  243. #endif
  244.     2+24,   //SAVE_GAME         (ubyte slot, uint id, char name[20])
  245.     2+4,   //RESTORE_GAME    (ubyte slot, uint id)
  246.     1+1,     // MULTI_REQ_PLAYER
  247.     sizeof(netplayer_stats),            // MULTI_SEND_PLAYER
  248. };
  249.  
  250. //
  251. //  Functions that replace what used to be macros
  252. //        
  253.  
  254. int objnum_remote_to_local(int remote_objnum, int owner)
  255. {
  256.     // Map a remote object number from owner to a local object number
  257.  
  258.     int result;    
  259.  
  260.     if ((owner >= N_players) || (owner < -1)) {
  261.         Int3(); // Illegal!
  262.         return(remote_objnum);
  263.     }
  264.  
  265.     if (owner == -1)
  266.         return(remote_objnum);
  267.     
  268.     if ((remote_objnum < 0) || (remote_objnum >= MAX_OBJECTS))
  269.         return(-1);
  270.  
  271.     result = remote_to_local[owner][remote_objnum];
  272.  
  273.     if (result < 0)
  274.     {
  275.         mprintf((1, "Remote object owner %d number %d mapped to -1!\n", owner, remote_objnum));
  276.         return(-1);
  277.     }
  278.  
  279. #ifndef NDEBUG
  280.     if (object_owner[result] != owner)
  281.     {
  282.         mprintf((1, "Remote object owner %d number %d doesn't match owner %d.\n", owner, remote_objnum, object_owner[result]));
  283.     }
  284. #endif    
  285. //    Assert(object_owner[result] == owner);
  286.  
  287.     return(result);
  288. }
  289.  
  290. int objnum_local_to_remote(int local_objnum, byte *owner)
  291. {
  292.     // Map a local object number to a remote + owner
  293.  
  294.     int result;
  295.  
  296.     if ((local_objnum < 0) || (local_objnum > Highest_object_index)) {
  297.         *owner = -1;
  298.         return(-1);
  299.     }
  300.  
  301.     *owner = object_owner[local_objnum];
  302.  
  303.     if (*owner == -1)
  304.         return(local_objnum);
  305.  
  306.     if ((*owner >= N_players) || (*owner < -1)) {
  307.         Int3(); // Illegal!
  308.         *owner = -1;
  309.         return local_objnum;
  310.     }
  311.     
  312.     result = local_to_remote[local_objnum];
  313.     
  314. //    mprintf((0, "Local object %d mapped to owner %d objnum %d.\n", local_objnum,
  315. //        *owner, result));
  316.  
  317.     if (result < 0)
  318.     {
  319.         Int3(); // See Rob, object has no remote number!
  320.     }
  321.  
  322.     return(result);
  323. }
  324.  
  325. void
  326. map_objnum_local_to_remote(int local_objnum, int remote_objnum, int owner)
  327. {
  328.     // Add a mapping from a network remote object number to a local one
  329.  
  330.     Assert(local_objnum > -1);
  331.     Assert(remote_objnum > -1);
  332.     Assert(owner > -1);
  333.     Assert(owner != Player_num);
  334.     Assert(local_objnum < MAX_OBJECTS);
  335.     Assert(remote_objnum < MAX_OBJECTS);
  336.  
  337.     object_owner[local_objnum] = owner;
  338.  
  339.     remote_to_local[owner][remote_objnum] = local_objnum;
  340.     local_to_remote[local_objnum] = remote_objnum;
  341.  
  342.     return;
  343. }
  344.  
  345. void
  346. map_objnum_local_to_local(int local_objnum)
  347. {
  348.     // Add a mapping for our locally created objects
  349.  
  350.     Assert(local_objnum > -1);
  351.     Assert(local_objnum < MAX_OBJECTS);
  352.  
  353.     object_owner[local_objnum] = Player_num;
  354.     remote_to_local[Player_num][local_objnum] = local_objnum;
  355.     local_to_remote[local_objnum] = local_objnum;
  356.  
  357.     return;
  358. }
  359.  
  360. //
  361. // Part 1 : functions whose main purpose in life is to divert the flow
  362. //          of execution to either network or serial specific code based
  363. //          on the curretn Game_mode value.
  364. //
  365.  
  366. void
  367. multi_endlevel_score(void)
  368. {
  369.     int old_connect;
  370.     int i;
  371. #ifdef SHAREWARE
  372.     return; // DEBUG
  373. #endif
  374.  
  375.     // Show a score list to end of net players
  376.  
  377.     // Save connect state and change to new connect state
  378. #ifdef NETWORK
  379.     if (Game_mode & GM_NETWORK)
  380.     {
  381.         old_connect = Players[Player_num].connected;
  382.         Players[Player_num].connected = CONNECT_END_MENU;
  383.     }
  384. #endif
  385.  
  386.     // Do the actual screen we wish to show
  387.  
  388.     Function_mode = FMODE_MENU;
  389. #ifdef NETWORK
  390.     Network_status = NETSTAT_ENDLEVEL;
  391. #endif
  392.  
  393.     if (Game_mode & GM_MULTI_COOP)
  394.         DoEndLevelScoreGlitz(1);
  395.     else
  396.         kmatrix_view(1);
  397.  
  398.     Function_mode = FMODE_GAME;
  399.  
  400.     // Restore connect state
  401.  
  402.     if (Game_mode & GM_NETWORK)
  403.     {
  404.         Players[Player_num].connected = old_connect;
  405.     }
  406.  
  407. #ifndef SHAREWARE
  408.     if (Game_mode & GM_MULTI_COOP)
  409.     {
  410.         for (i = 0; i < MaxNumNetPlayers; i++)
  411.         // Reset keys
  412.             Players[i].flags &= ~(PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_GOLD_KEY);
  413.     }
  414. #endif
  415. }
  416.  
  417. int
  418. get_team(int pnum)
  419. {
  420.     if (Netgame.team_vector & (1 << pnum))
  421.         return 1;
  422.     else
  423.         return 0;
  424. }
  425.  
  426. int
  427. multi_choose_mission(int *anarchy_only)
  428. {
  429.     int i, n_missions;
  430.     int default_mission;
  431.    char *m[MAX_MISSIONS];
  432.     int new_mission_num = 0;
  433.  
  434.     *anarchy_only = 0;
  435.  
  436.     n_missions = build_mission_list(1);
  437.  
  438.     if (n_missions > 1) {
  439.  
  440.         default_mission = 0;
  441.         for (i=0;i<n_missions;i++) {
  442.             m[i] = Mission_list[i].mission_name;
  443.             if ( !stricmp( m[i], config_last_mission ) )    
  444.                 default_mission = i;
  445.         }
  446.  
  447.         new_mission_num = newmenu_listbox1(TXT_MULTI_MISSION, n_missions, m, 1, default_mission, NULL );
  448.  
  449.         if (new_mission_num == -1)
  450.             return -1;     //abort!
  451.  
  452.         strcpy(config_last_mission, m[new_mission_num]  );
  453.         
  454.         if (!load_mission(new_mission_num)) {
  455.             nm_messagebox( NULL, 1, TXT_OK, TXT_MISSION_ERROR); 
  456.             return -1;
  457.         }
  458.  
  459.        *anarchy_only = Mission_list[new_mission_num].anarchy_only_flag;
  460.     }
  461.     return(new_mission_num);
  462. }
  463.  
  464. extern void game_disable_cheats();
  465.     
  466. void
  467. multi_new_game(void)
  468. {
  469.     int i;
  470.     
  471.     // Reset variables for a new net game
  472.  
  473.     memset(kill_matrix, 0, MAX_NUM_NET_PLAYERS*MAX_NUM_NET_PLAYERS*2); // Clear kill matrix
  474.  
  475.     for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
  476.     {
  477.         sorted_kills[i] = i;
  478.         Players[i].net_killed_total = 0;
  479.         Players[i].net_kills_total = 0;
  480.         Players[i].flags = 0;
  481.     }
  482.  
  483. #ifndef SHAREWARE
  484.     for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
  485.     {
  486.         robot_controlled[i] = -1;    
  487.         robot_agitation[i] = 0;
  488.         robot_fired[i] = 0;
  489.     }
  490. #endif
  491.  
  492.     team_kills[0] = team_kills[1] = 0;
  493.     Endlevel_sequence = 0;
  494.     Player_is_dead = 0;
  495.     multi_leave_menu = 0;
  496.     multi_quit_game = 0;
  497.     Show_kill_list = 1;
  498.     game_disable_cheats();
  499.     Player_exploded = 0;
  500.     Dead_player_camera = 0;
  501. }
  502.     
  503. void
  504. multi_make_player_ghost(int playernum)
  505. {
  506.     object *obj;
  507.  
  508. //    Assert(playernum != Player_num);
  509. //    Assert(playernum < MAX_NUM_NET_PLAYERS);
  510.  
  511.     if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS) || (playernum < 0))
  512.     {
  513.         Int3(); // Non-terminal, see Rob
  514.         return;
  515.     }
  516.  
  517. //    if (Objects[Players[playernum].objnum].type != OBJ_PLAYER)
  518. //        mprintf((1, "Warning: Player %d is not currently a player.\n", playernum));
  519.  
  520.     obj = &Objects[Players[playernum].objnum];
  521.  
  522.     obj->type = OBJ_GHOST;
  523.     obj->render_type = RT_NONE;
  524.     obj->movement_type = MT_NONE;
  525.     multi_reset_player_object(obj);
  526.  
  527.     if (Game_mode & GM_MULTI_ROBOTS)
  528.         multi_strip_robots(playernum);
  529. }
  530.  
  531. void
  532. multi_make_ghost_player(int playernum)
  533. {
  534.     object *obj;
  535.  
  536. //    Assert(playernum != Player_num);
  537. // Assert(playernum < MAX_NUM_NET_PLAYERS);
  538.  
  539.     if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS))
  540.     {
  541.         Int3(); // Non-terminal, see rob
  542.         return;
  543.     }
  544.  
  545. //    if(Objects[Players[playernum].objnum].type != OBJ_GHOST)
  546. //        mprintf((1, "Warning: Player %d is not currently a ghost.\n", playernum));
  547.  
  548.     obj = &Objects[Players[playernum].objnum];
  549.  
  550.     obj->type = OBJ_PLAYER;
  551.     obj->movement_type = MT_PHYSICS;
  552.     multi_reset_player_object(obj);
  553. }
  554.  
  555. int multi_get_kill_list(int *plist)
  556. {
  557.     // Returns the number of active net players and their
  558.     // sorted order of kills
  559.     int i;
  560.     int n = 0;
  561.  
  562.     for (i = 0; i < N_players; i++)
  563. //        if (Players[sorted_kills[i]].connected)
  564.             plist[n++] = sorted_kills[i];
  565.  
  566.     if (n == 0)
  567.         Int3(); // SEE ROB OR MATT
  568.  
  569. //    memcpy(plist, sorted_kills, N_players*sizeof(int));    
  570.  
  571.     return(n);
  572. }
  573.     
  574. void
  575. multi_sort_kill_list(void)
  576. {
  577.     // Sort the kills list each time a new kill is added
  578.  
  579.     int kills[MAX_NUM_NET_PLAYERS];
  580.     int i;
  581.     int changed = 1;
  582.     
  583.     for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
  584.     {
  585. #ifndef SHAREWARE
  586.         if (Game_mode & GM_MULTI_COOP)
  587.             kills[i] = Players[i].score;
  588.         else
  589. #endif
  590.             kills[i] = Players[i].net_kills_total;
  591.     }
  592.  
  593.     while (changed)
  594.     {
  595.         changed = 0;
  596.         for (i = 0; i < N_players-1; i++)
  597.         {
  598.             if (kills[sorted_kills[i]] < kills[sorted_kills[i+1]])
  599.             {
  600.                 changed = sorted_kills[i];
  601.                 sorted_kills[i] = sorted_kills[i+1];
  602.                 sorted_kills[i+1] = changed;
  603.                 changed = 1;
  604.             }
  605.         }
  606.     }
  607. //    mprintf((0, "Sorted kills %d %d.\n", sorted_kills[0], sorted_kills[1]));
  608. }
  609.  
  610. void multi_compute_kill(int killer, int killed)
  611. {
  612.     // Figure out the results of a network kills and add it to the
  613.     // appropriate player's tally.
  614.  
  615.     int killed_pnum, killed_type;
  616.     int killer_pnum, killer_type;
  617.     char killed_name[(CALLSIGN_LEN*2)+4];
  618.     char killer_name[(CALLSIGN_LEN*2)+4];
  619.  
  620.     kmatrix_kills_changed = 1;
  621.  
  622.     // Both object numbers are localized already!
  623.  
  624.     mprintf((0, "compute_kill passed: object %d killed object %d.\n", killer, killed));
  625.  
  626.     if ((killed < 0) || (killed > Highest_object_index) || (killer < 0) || (killer > Highest_object_index))
  627.     {
  628.         Int3(); // See Rob, illegal value passed to compute_kill;
  629.         return;
  630.     }
  631.  
  632.     killed_type = Objects[killed].type;
  633.     killer_type = Objects[killer].type;
  634.  
  635.     if ((killed_type != OBJ_PLAYER) && (killed_type != OBJ_GHOST)) 
  636.     {
  637.         Int3(); // compute_kill passed non-player object!
  638.         return;
  639.     }
  640.  
  641.     killed_pnum = Objects[killed].id;
  642.  
  643.     Assert ((killed_pnum >= 0) && (killed_pnum < N_players));
  644.  
  645.     if (Game_mode & GM_TEAM)
  646.         sprintf(killed_name, "%s (%s)", Players[killed_pnum].callsign, Netgame.team_name[get_team(killed_pnum)]);
  647.     else
  648.         sprintf(killed_name, "%s", Players[killed_pnum].callsign);
  649.  
  650. #ifndef SHAREWARE
  651.     if (Newdemo_state == ND_STATE_RECORDING)
  652.         newdemo_record_multi_death(killed_pnum);
  653. #endif
  654.  
  655.     digi_play_sample( SOUND_HUD_KILL, F3_0 );
  656.  
  657.     if (killer_type == OBJ_CNTRLCEN)
  658.     {
  659.         Players[killed_pnum].net_killed_total++;
  660.         Players[killed_pnum].net_kills_total--;
  661.  
  662. #ifndef SHAREWARE
  663.         if (Newdemo_state == ND_STATE_RECORDING)
  664.             newdemo_record_multi_kill(killed_pnum, -1);
  665. #endif
  666.  
  667.         if (killed_pnum == Player_num)
  668.             HUD_init_message("%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_NONPLAY);
  669.         else
  670.             HUD_init_message("%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_NONPLAY );
  671.         return;        
  672.     }
  673.  
  674. #ifndef SHAREWARE
  675.     else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST))
  676.     {
  677.         if (killed_pnum == Player_num)
  678.             HUD_init_message("%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_ROBOT);
  679.         else
  680.             HUD_init_message("%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_ROBOT );
  681.         Players[killed_pnum].net_killed_total++;
  682.         return;        
  683.     }
  684. #else
  685.     else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST))
  686.     {
  687.         Int3(); // Illegal killer type?
  688.         return;
  689.     }
  690. #endif
  691.  
  692.     killer_pnum = Objects[killer].id;
  693.  
  694.     if (Game_mode & GM_TEAM)
  695.         sprintf(killer_name, "%s (%s)", Players[killer_pnum].callsign, Netgame.team_name[get_team(killer_pnum)]);
  696.     else
  697.         sprintf(killer_name, "%s", Players[killer_pnum].callsign);
  698.  
  699.     // Beyond this point, it was definitely a player-player kill situation
  700.  
  701.     if ((killer_pnum < 0) || (killer_pnum >= N_players))
  702.         Int3(); // See rob, tracking down bug with kill HUD messages
  703.     if ((killed_pnum < 0) || (killed_pnum >= N_players))
  704.         Int3(); // See rob, tracking down bug with kill HUD messages
  705.  
  706.     if (killer_pnum == killed_pnum)
  707.     {
  708.         if (Game_mode & GM_TEAM)
  709.         {
  710.             team_kills[get_team(killed_pnum)] -= 1;
  711.         }
  712.         Players[killed_pnum].net_killed_total += 1;
  713.         Players[killed_pnum].net_kills_total -= 1;
  714.  
  715. #ifndef SHAREWARE
  716.         if (Newdemo_state == ND_STATE_RECORDING)
  717.             newdemo_record_multi_kill(killed_pnum, -1);
  718. #endif
  719.  
  720.         kill_matrix[killed_pnum][killed_pnum] += 1; // # of suicides
  721.         if (killer_pnum == Player_num)
  722.             HUD_init_message("%s %s %s!", TXT_YOU, TXT_KILLED, TXT_YOURSELF );
  723.         else
  724.             HUD_init_message("%s %s", killed_name, TXT_SUICIDE);
  725.     }
  726.  
  727.     else
  728.     {
  729.         if (Game_mode & GM_TEAM)
  730.         {
  731.             if (get_team(killed_pnum) == get_team(killer_pnum))
  732.                 team_kills[get_team(killed_pnum)] -= 1;
  733.             else
  734.                 team_kills[get_team(killer_pnum)] += 1;
  735.         }
  736.         Players[killer_pnum].net_kills_total += 1;
  737.  
  738. #ifndef SHAREWARE
  739.         if (Newdemo_state == ND_STATE_RECORDING)
  740.             newdemo_record_multi_kill(killer_pnum, 1);
  741. #endif
  742.  
  743.         Players[killed_pnum].net_killed_total += 1;
  744.         kill_matrix[killer_pnum][killed_pnum] += 1;
  745.         if (killer_pnum == Player_num) {
  746.             HUD_init_message("%s %s %s!", TXT_YOU, TXT_KILLED, killed_name);
  747.             if ((Game_mode & GM_MULTI_COOP) && (Players[Player_num].score >= 1000))
  748.                 add_points_to_score(-1000);
  749.         }
  750.         else if (killed_pnum == Player_num)
  751.             HUD_init_message("%s %s %s!", killer_name, TXT_KILLED, TXT_YOU);
  752.         else
  753.             HUD_init_message("%s %s %s!", killer_name, TXT_KILLED, killed_name);
  754.     }
  755.     multi_sort_kill_list();
  756.     multi_show_player_list();
  757. }
  758.  
  759. void
  760. multi_do_frame(void)
  761. {
  762.     if (!(Game_mode & GM_MULTI))
  763.     {
  764.         Int3();
  765.         return;
  766.     }
  767.     multi_send_message(); // Send any waiting messages
  768.  
  769.     if (!multi_in_menu)
  770.         multi_leave_menu = 0;
  771.  
  772. #ifndef SHAREWARE
  773.     if (Game_mode & GM_MULTI_ROBOTS)
  774.     {
  775.         multi_check_robot_timeout();
  776.     }
  777. #endif    
  778.  
  779.     if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
  780.     {
  781.         com_do_frame();
  782.     }
  783.     else
  784.     {
  785.         network_do_frame(0, 1);
  786.     }
  787.  
  788.     if (multi_quit_game && !multi_in_menu)
  789.     {
  790.         multi_quit_game = 0;
  791.         longjmp(LeaveGame, 0);
  792.     }
  793. }
  794.         
  795. void
  796. multi_send_data(char *buf, int len, int repeat)
  797. {
  798.     Assert(len == message_length[buf[0]]);
  799.     Assert(buf[0] <= MULTI_MAX_TYPE);
  800. //    Assert(buf[0] >= 0);
  801.  
  802.     if (Game_mode & GM_NETWORK)
  803.         Assert(buf[0] > 0);
  804.  
  805.     if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
  806.         com_send_data(buf, len, repeat);
  807.     else if (Game_mode & GM_NETWORK)
  808.         network_send_data(buf, len, repeat);
  809. }
  810.  
  811. void
  812. multi_leave_game(void)
  813. {
  814.  
  815. //    if (Function_mode != FMODE_GAME)
  816. //        return;
  817.  
  818.     if (!(Game_mode & GM_MULTI))
  819.         return;
  820.  
  821.     if (Game_mode & GM_NETWORK)
  822.     {
  823.         mprintf((0, "Sending explosion message.\n"));
  824.  
  825.         Net_create_loc = 0;
  826.         drop_player_eggs(ConsoleObject);
  827.         multi_send_position(Players[Player_num].objnum);
  828.         multi_send_player_explode(MULTI_PLAYER_DROP);
  829.     }
  830.  
  831.     mprintf((1, "Sending leave game.\n"));
  832.     multi_send_quit(MULTI_QUIT);
  833.  
  834.     if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
  835.         serial_leave_game();
  836.     if (Game_mode & GM_NETWORK)
  837.         network_leave_game();
  838.  
  839.     Game_mode |= GM_GAME_OVER;
  840.     Function_mode = FMODE_MENU;
  841.  
  842. //    N_players = 0;
  843.  
  844. //    change_playernum_to(0);
  845. //    Viewer = ConsoleObject = &Objects[0];
  846.  
  847. }
  848.         
  849. void 
  850. multi_show_player_list()
  851. {
  852.     if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_COOP))
  853.         return;
  854.  
  855.     if (Show_kill_list)
  856.         return;
  857.  
  858.     Show_kill_list_timer = F1_0*5; // 5 second timer
  859.     Show_kill_list = 1;
  860. }
  861.  
  862. int 
  863. multi_endlevel(int *secret)
  864. {
  865.     int result = 0;
  866.  
  867.     if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
  868.         com_endlevel(secret);          // an opportunity to re-sync or whatever
  869.     else if (Game_mode & GM_NETWORK)
  870.         result = network_endlevel(secret);
  871.     
  872.     return(result);        
  873. }
  874.  
  875. //
  876. // Part 2 : functions that act on network/serial messages and change the
  877. //          the state of the game in some way.
  878. //
  879.  
  880. extern PORT *com_port;
  881.  
  882. int 
  883. multi_menu_poll(void)
  884. {
  885.     fix old_shields;
  886.     int t1;        
  887.     int was_fuelcen_alive;
  888.  
  889.     was_fuelcen_alive = Fuelcen_control_center_destroyed;
  890.  
  891.     // Special polling function for in-game menus for multiplayer and serial
  892.  
  893.     if (! ((Game_mode & GM_MULTI) && (Function_mode == FMODE_GAME)) )
  894.         return(0);
  895.  
  896.     if (multi_leave_menu)
  897.         return(-1);
  898.  
  899.     old_shields = Players[Player_num].shields;
  900.  
  901.     multi_in_menu++; // Track level of menu nesting
  902.  
  903.     GameLoop( 0, 0 );            
  904.  
  905.     multi_in_menu--;
  906.  
  907. //    t1 = timer_get_fixed_seconds();
  908. //    while (timer_get_fixed_seconds() < t1+F1_0/20)
  909. //        ;
  910.     t1 = TICKER + 1;        // Wait 1/18th of a second...
  911.     while (TICKER < t1)
  912.         ;
  913.     
  914.         
  915.     if (Endlevel_sequence || (Fuelcen_control_center_destroyed && !was_fuelcen_alive) || Player_is_dead || (Players[Player_num].shields < old_shields))
  916.     {
  917.         multi_leave_menu = 1;
  918.         return(-1);
  919.     }
  920.     if ((Fuelcen_control_center_destroyed) && (Fuelcen_seconds_left < 10))
  921.     {
  922.         multi_leave_menu = 1;
  923.         return(-1);
  924.     }
  925.     if ((Game_mode & GM_MODEM) && (!GetCd(com_port)))
  926.     {
  927.         multi_leave_menu = 1;
  928.         return(-1);
  929.     }
  930.  
  931.     return(0);
  932. }
  933.  
  934. void
  935. multi_define_macro(int key)
  936. {
  937.     if (!(Game_mode & GM_MULTI))
  938.         return;
  939.  
  940.     key &= (~KEY_SHIFTED);
  941.  
  942.     switch(key) 
  943.     {
  944.         case KEY_F9:
  945.             multi_defining_message = 1; break;
  946.         case KEY_F10:
  947.             multi_defining_message = 2; break;
  948.         case KEY_F11:
  949.             multi_defining_message = 3; break;
  950.         case KEY_F12:
  951.             multi_defining_message = 4; break;
  952.         default:
  953.             Int3();
  954.     }
  955.  
  956.     if (multi_defining_message)    {
  957.         multi_message_index = 0;
  958.         Network_message[multi_message_index] = 0;
  959.     }
  960.  
  961. }
  962.  
  963. char feedback_result[200];
  964.  
  965. void
  966. multi_message_feedback(void)
  967. {
  968.     char *colon;
  969.     int found = 0;
  970.     int i;
  971.  
  972.     if (!( ((colon = strrchr(Network_message, ':')) == NULL) || (colon-Network_message < 1) || (colon-Network_message > CALLSIGN_LEN) ))
  973.     {
  974.         sprintf(feedback_result, "%s ", TXT_MESSAGE_SENT_TO);
  975.         if ((Game_mode & GM_TEAM) && (atoi(Network_message) > 0) && (atoi(Network_message) < 3))
  976.         {
  977.             sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[atoi(Network_message)-1]);
  978.             found = 1;
  979.         }
  980.         if (Game_mode & GM_TEAM)
  981.         {
  982.             for (i = 0; i < N_players; i++)
  983.             {
  984.                 if (!strnicmp(Netgame.team_name[i], Network_message, colon-Network_message))
  985.                 {
  986.                     if (found)
  987.                         strcat(feedback_result, ", ");
  988.                     found++;
  989.                     if (!(found % 4))
  990.                         strcat(feedback_result, "\n");
  991.                     sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[i]);
  992.                 }
  993.             }
  994.         }
  995.         for (i = 0; i < N_players; i++)
  996.         {
  997.             if ((!strnicmp(Players[i].callsign, Network_message, colon-Network_message)) && (i != Player_num) && (Players[i].connected))
  998.             {
  999.                 if (found)
  1000.                     strcat(feedback_result, ", ");
  1001.                 found++;
  1002.                 if (!(found % 4))
  1003.                     strcat(feedback_result, "\n");
  1004.                 sprintf(feedback_result+strlen(feedback_result), "%s", Players[i].callsign);
  1005.             }
  1006.         }
  1007.         if (!found)
  1008.             strcat(feedback_result, TXT_NOBODY);
  1009.         else
  1010.             strcat(feedback_result, ".");
  1011.  
  1012.         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
  1013.  
  1014.         Assert(strlen(feedback_result) < 200);
  1015.  
  1016.         HUD_init_message(feedback_result);
  1017.     }
  1018. }
  1019.  
  1020. void
  1021. multi_send_macro(int key)
  1022. {
  1023.     if (! (Game_mode & GM_MULTI) )
  1024.         return;
  1025.  
  1026.     switch(key) 
  1027.     {
  1028.         case KEY_F9:
  1029.             key = 0; break;
  1030.         case KEY_F10:
  1031.             key = 1; break;
  1032.         case KEY_F11:
  1033.             key = 2; break;
  1034.         case KEY_F12:
  1035.             key = 3; break;
  1036.         default:
  1037.             Int3();
  1038.     }
  1039.  
  1040.     if (!Network_message_macro[key][0])
  1041.     {
  1042.         HUD_init_message(TXT_NO_MACRO);
  1043.         return;
  1044.     }
  1045.  
  1046.     strcpy(Network_message, Network_message_macro[key]);
  1047.     Network_message_reciever = 100;
  1048.  
  1049.     HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
  1050.     multi_message_feedback();
  1051. }
  1052.  
  1053.  
  1054. void 
  1055. multi_send_message_start()
  1056. {
  1057.     if (Game_mode&GM_MULTI)    {
  1058.         multi_sending_message = 1;
  1059.         multi_message_index = 0;
  1060.         Network_message[multi_message_index] = 0;
  1061.     }
  1062. }
  1063.  
  1064. void multi_send_message_end()
  1065. {
  1066.     Network_message_reciever = 100;
  1067.     HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
  1068.     multi_send_message();
  1069.     multi_message_feedback();        
  1070.  
  1071.     multi_message_index = 0;
  1072.     multi_sending_message = 0;
  1073. }
  1074.  
  1075. void multi_define_macro_end()
  1076. {
  1077.     Assert( multi_defining_message > 0 );
  1078.  
  1079.     strcpy( Network_message_macro[multi_defining_message-1], Network_message );
  1080.     write_player_file();
  1081.  
  1082.     multi_message_index = 0;
  1083.     multi_defining_message = 0;
  1084. }
  1085.  
  1086. void multi_message_input_sub( int key )
  1087. {
  1088.     switch( key )    {
  1089.     case KEY_F8:
  1090.     case KEY_ESC:
  1091.         multi_sending_message = 0;
  1092.         multi_defining_message = 0;
  1093.         game_flush_inputs();
  1094.         break;
  1095.     case KEY_LEFT:
  1096.     case KEY_BACKSP:
  1097.     case KEY_PAD4:
  1098.         if (multi_message_index > 0)
  1099.             multi_message_index--;
  1100.         Network_message[multi_message_index] = 0;
  1101.         break;
  1102.     case KEY_ENTER:
  1103.         if ( multi_sending_message )    
  1104.             multi_send_message_end();
  1105.         else if ( multi_defining_message )
  1106.             multi_define_macro_end();
  1107.         game_flush_inputs();
  1108.         break;
  1109.     default:
  1110.         if ( key > 0 )    {
  1111.             int ascii = key_to_ascii(key);
  1112.             if ((ascii < 255 ))    {
  1113.                 if (multi_message_index < MAX_MESSAGE_LEN-2 )    {
  1114.                     Network_message[multi_message_index++] = ascii;
  1115.                     Network_message[multi_message_index] = 0;
  1116.                 } else if ( multi_sending_message )    {        
  1117.                     int i;
  1118.                     char * ptext, * pcolon;
  1119.                     ptext = NULL;
  1120.                     Network_message[multi_message_index++] = ascii;
  1121.                     Network_message[multi_message_index] = 0;
  1122.                     for (i=multi_message_index-1; i>=0; i-- )    {
  1123.                         if ( Network_message[i]==32 )    {
  1124.                             ptext = &Network_message[i+1];
  1125.                             Network_message[i] = 0;
  1126.                             break;
  1127.                         }
  1128.                     }
  1129.                     multi_send_message_end();
  1130.                     if ( ptext )    {
  1131.                         multi_sending_message = 1;
  1132.                         pcolon = strchr( Network_message, ':' );
  1133.                         if ( pcolon )
  1134.                             strcpy( pcolon+1, ptext );
  1135.                         else
  1136.                             strcpy( Network_message, ptext );
  1137.                         multi_message_index = strlen( Network_message );
  1138.                     } 
  1139.                 }
  1140.             }
  1141.         }
  1142.     }
  1143. }
  1144.  
  1145. void 
  1146. multi_send_message_dialog(void)
  1147. {
  1148.     newmenu_item m[1];
  1149.     int choice;
  1150.  
  1151.     if (!(Game_mode&GM_MULTI))
  1152.         return;
  1153.  
  1154.     Network_message[0] = 0;             // Get rid of old contents
  1155.  
  1156.     m[0].type=NM_TYPE_INPUT; m[0].text = Network_message; m[0].text_len = MAX_MESSAGE_LEN-1;
  1157.     choice = newmenu_do( NULL, TXT_SEND_MESSAGE, 1, m, NULL );
  1158.  
  1159.     if ((choice > -1) && (strlen(Network_message) > 0)) {
  1160.         Network_message_reciever = 100;
  1161.         HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
  1162.         multi_message_feedback();        
  1163.     }
  1164. }
  1165.  
  1166.  
  1167.  
  1168. void
  1169. multi_do_death(int objnum)
  1170. {
  1171.     // Do any miscellaneous stuff for a new network player after death
  1172.  
  1173.     objnum = objnum;
  1174.  
  1175.     if (!(Game_mode & GM_MULTI_COOP)) 
  1176.     {
  1177.         mprintf((0, "Setting all keys for player %d.\n", Player_num));
  1178.         Players[Player_num].flags |= (PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_GOLD_KEY);
  1179.     }
  1180. }
  1181.  
  1182. void
  1183. multi_do_fire(char *buf)
  1184. {
  1185.     ubyte weapon;
  1186.     char pnum;
  1187.     byte flags;
  1188.     fix save_charge = Fusion_charge;
  1189.     
  1190.     // Act out the actual shooting
  1191.     pnum = buf[1];
  1192.     weapon = (int)buf[2];    
  1193.     flags = buf[4];
  1194.     Network_laser_track = *(short *)(buf+6);
  1195.     
  1196.     Assert (pnum < N_players);
  1197.  
  1198.     if (Objects[Players[pnum].objnum].type == OBJ_GHOST)
  1199.         multi_make_ghost_player(pnum);
  1200.         
  1201.     if (weapon >= MISSILE_ADJUST) 
  1202.         net_missile_firing(pnum, weapon, (int)buf[4]);
  1203.     else {
  1204.         if (weapon == FUSION_INDEX) {
  1205.             Fusion_charge = buf[4] << 12;
  1206.             mprintf((0, "Fusion charge X%f.\n", f2fl(Fusion_charge)));
  1207.         }
  1208.         if (weapon == LASER_INDEX) {
  1209.             if (flags & LASER_QUAD)
  1210.                 Players[pnum].flags |= PLAYER_FLAGS_QUAD_LASERS;
  1211.             else
  1212.                 Players[pnum].flags &= ~PLAYER_FLAGS_QUAD_LASERS;
  1213.         }
  1214.  
  1215.         do_laser_firing(Players[pnum].objnum, weapon, (int)buf[3], flags, (int)buf[5]);
  1216.  
  1217.         if (weapon == FUSION_INDEX)
  1218.             Fusion_charge = save_charge;
  1219.     }
  1220. }
  1221.  
  1222. void 
  1223. multi_do_message(char *buf)
  1224. {
  1225.     char *colon;
  1226.  
  1227. #ifdef SHAREWARE
  1228.     int loc = 3;
  1229. #else
  1230.     int loc = 2;
  1231. #endif
  1232.  
  1233.     if (((colon = strrchr(buf+loc, ':')) == NULL) || (colon-(buf+loc) < 1) || (colon-(buf+loc) > CALLSIGN_LEN))
  1234.     {
  1235.         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
  1236.         HUD_init_message("%s %s '%s'", Players[buf[1]].callsign, TXT_SAYS, buf+loc);
  1237.     }
  1238.     else
  1239.     {
  1240.         if ( (!strnicmp(Players[Player_num].callsign, buf+loc, colon-(buf+loc))) ||
  1241.               ((Game_mode & GM_TEAM) && ( (get_team(Player_num) == atoi(buf+loc)-1) || !strnicmp(Netgame.team_name[get_team(Player_num)], buf+loc, colon-(buf+loc)))) )
  1242.         {
  1243.             digi_play_sample(SOUND_HUD_MESSAGE, F1_0);    
  1244.             HUD_init_message("%s %s '%s'", Players[buf[1]].callsign, TXT_TELLS_YOU, (colon+1));
  1245.         }
  1246.     }
  1247. }
  1248.  
  1249. void
  1250. multi_do_position(char *buf)
  1251. {        
  1252.     // This routine does only player positions, mode game only
  1253.     //    mprintf((0, "Got position packet.\n"));
  1254.  
  1255.     int pnum = (Player_num+1)%2;
  1256.  
  1257.     Assert(&Objects[Players[pnum].objnum] != ConsoleObject);
  1258.  
  1259.     Assert(!(Game_mode & GM_NETWORK));
  1260.  
  1261.     extract_shortpos(&Objects[Players[pnum].objnum], (shortpos *)(buf+1));
  1262.  
  1263.     if (Objects[Players[pnum].objnum].movement_type == MT_PHYSICS)
  1264.         set_thrust_from_velocity(&Objects[Players[pnum].objnum]);
  1265. }
  1266.  
  1267. void
  1268. multi_do_reappear(char *buf)
  1269. {
  1270.     short objnum;
  1271.  
  1272.     objnum = *(short *)(buf+1);
  1273.  
  1274.     Assert(objnum >= 0);
  1275. //    Assert(Players[Objects[objnum].id]].objnum == objnum);
  1276.     
  1277. // mprintf((0, "Switching rendering back on for object %d.\n", objnum));
  1278.  
  1279.     multi_make_ghost_player(Objects[objnum].id);
  1280.  
  1281.     create_player_appearance_effect(&Objects[objnum]);
  1282. }
  1283.     
  1284. void
  1285. multi_do_player_explode(char *buf)
  1286. {
  1287.     // Only call this for players, not robots.  pnum is player number, not
  1288.     // Object number.
  1289.  
  1290.     object *objp;
  1291.     int count;
  1292.     int pnum;
  1293.     int i;
  1294.     char remote_created;
  1295.  
  1296.     pnum = buf[1];
  1297.  
  1298. #ifdef NDEBUG
  1299.     if ((pnum < 0) || (pnum >= N_players))
  1300.         return;
  1301. #else
  1302.     Assert(pnum >= 0);
  1303.     Assert(pnum < N_players);
  1304. #endif
  1305.  
  1306. #ifdef NETWORK
  1307.     // If we are in the process of sending objects to a new player, reset that process
  1308.     if (Network_send_objects)
  1309.     {
  1310.         mprintf((0, "Resetting object sync due to player explosion.\n"));
  1311.         Network_send_objnum = -1;
  1312.     }
  1313. #endif
  1314.  
  1315.     // Stuff the Players structure to prepare for the explosion
  1316.     
  1317.     count = 2;
  1318.     Players[pnum].primary_weapon_flags = buf[count];                 count++;
  1319.     Players[pnum].secondary_weapon_flags = buf[count];                count++;
  1320.     Players[pnum].laser_level = buf[count];                            count++;
  1321.     Players[pnum].secondary_ammo[HOMING_INDEX] = buf[count];        count++;
  1322.     Players[pnum].secondary_ammo[CONCUSSION_INDEX] = buf[count];count++;
  1323.     Players[pnum].secondary_ammo[SMART_INDEX] = buf[count];        count++;
  1324.     Players[pnum].secondary_ammo[MEGA_INDEX] = buf[count];        count++;
  1325.     Players[pnum].secondary_ammo[PROXIMITY_INDEX] = buf[count]; count++;
  1326.     Players[pnum].primary_ammo[VULCAN_INDEX] = *(ushort *)(buf+count); count += 2;
  1327.     Players[pnum].flags = *(uint *)(buf+count);                        count += 4;
  1328.  
  1329.     objp = Objects+Players[pnum].objnum;
  1330.  
  1331. //    objp->phys_info.velocity = *(vms_vector *)(buf+16); // 12 bytes
  1332. //    objp->pos = *(vms_vector *)(buf+28);                // 12 bytes
  1333.  
  1334.     remote_created = buf[count++]; // How many did the other guy create?
  1335.  
  1336.     Net_create_loc = 0;
  1337.  
  1338.     drop_player_eggs(objp);
  1339.  
  1340.     // Create mapping from remote to local numbering system
  1341.  
  1342.     mprintf((0, "I Created %d powerups, remote created %d.\n", Net_create_loc, remote_created));
  1343.  
  1344.     // We now handle this situation gracefully, Int3 not required
  1345.     //    if (Net_create_loc != remote_created)
  1346.     //        Int3(); // Probably out of object array space, see Rob
  1347.  
  1348.     for (i = 0; i < remote_created; i++)
  1349.     {
  1350.         if ((i < Net_create_loc) && (*(short *)(buf+count) > 0))
  1351.             map_objnum_local_to_remote((short)Net_create_objnums[i], *(short *)(buf+count), pnum);        
  1352.         else if (*(short *)(buf+count) <= 0)
  1353.         {
  1354.             mprintf((0, "WARNING: Remote created object has non-valid number %d (player %d)", *(short *)(buf+count), pnum));
  1355.         }
  1356.         else 
  1357.         {
  1358.             mprintf((0, "WARNING: Could not create all powerups created by player %d.\n", pnum));
  1359.         }
  1360. //        Assert(*(short *)(buf+count) > 0);
  1361.         count += 2;
  1362.     }
  1363.     for (i = remote_created; i < Net_create_loc; i++) {
  1364.         mprintf((0, "WARNING: I Created more powerups than player %d, deleting.\n", pnum));
  1365.         Objects[Net_create_objnums[i]].flags |= OF_SHOULD_BE_DEAD;
  1366.     }
  1367.  
  1368.     if (buf[0] == MULTI_PLAYER_EXPLODE)
  1369.     {
  1370.         explode_badass_player(objp);
  1371.         
  1372.         objp->flags &= ~OF_SHOULD_BE_DEAD;        //don't really kill player
  1373.         multi_make_player_ghost(pnum);
  1374.     }
  1375.     else
  1376.     {
  1377.         create_player_appearance_effect(objp);
  1378.     }
  1379.  
  1380.     Players[pnum].flags &= ~(PLAYER_FLAGS_CLOAKED | PLAYER_FLAGS_INVULNERABLE);
  1381.     Players[pnum].cloak_time = 0;
  1382. }
  1383.  
  1384. void
  1385. multi_do_kill(char *buf)
  1386. {
  1387.     int killer, killed;
  1388.     int count = 1;
  1389.     
  1390. #ifndef SHAREWARE
  1391.     int pnum;
  1392.     pnum = buf[count];
  1393.     if ((pnum < 0) || (pnum >= N_players))
  1394.     {
  1395.         Int3(); // Invalid player number killed
  1396.         return;
  1397.     }
  1398.     killed = Players[pnum].objnum;            
  1399.     count += 1;
  1400. #else
  1401.     killed = objnum_remote_to_local(*(short *)(buf+count), (byte)buf[count+2]);
  1402.     count += 3;
  1403. #endif
  1404.     killer = *(short *)(buf+count); 
  1405.     if (killer > 0)
  1406.         killer = objnum_remote_to_local(killer, (byte)buf[count+2]);
  1407.  
  1408. #ifdef SHAREWARE
  1409.     if ((Objects[killed].type != OBJ_PLAYER) && (Objects[killed].type != OBJ_GHOST))
  1410.     {
  1411.         Int3();
  1412.         mprintf( (1, "SOFT INT3: MULTI.C Non-player object %d of type %d killed! (JOHN)\n", killed, Objects[killed].type ));
  1413.         return;
  1414.     }
  1415. #endif        
  1416.  
  1417.     multi_compute_kill(killer, killed);
  1418.  
  1419. }
  1420.  
  1421.  
  1422. //    Changed by MK on 10/20/94 to send NULL as object to net_destroy_controlcen if it got -1
  1423. // which means not a controlcen object, but contained in another object
  1424. void multi_do_controlcen_destroy(char *buf)
  1425. {
  1426.     byte who;
  1427.     short objnum;
  1428.  
  1429.     objnum = *(short *)(buf+1);
  1430.     who = buf[3];
  1431.  
  1432.     if (Fuelcen_control_center_destroyed != 1) 
  1433.     {
  1434.         if ((who < N_players) && (who != Player_num)) {
  1435.             HUD_init_message("%s %s", Players[who].callsign, TXT_HAS_DEST_CONTROL);
  1436.         }
  1437.         else if (who == Player_num)
  1438.             HUD_init_message(TXT_YOU_DEST_CONTROL);
  1439.         else 
  1440.             HUD_init_message(TXT_CONTROL_DESTROYED);
  1441.  
  1442.         if (objnum != -1)
  1443.             net_destroy_controlcen(Objects+objnum);
  1444.         else
  1445.             net_destroy_controlcen(NULL);
  1446.     }
  1447. }
  1448.  
  1449. void 
  1450. multi_do_escape(char *buf)
  1451. {
  1452.     int objnum;
  1453.  
  1454.     objnum = Players[buf[1]].objnum;
  1455.  
  1456.     digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
  1457.  
  1458.     if (buf[2] == 0)
  1459.     {
  1460.         HUD_init_message("%s %s", Players[buf[1]].callsign, TXT_HAS_ESCAPED);
  1461. #ifndef SHAREWARE
  1462.         if (Game_mode & GM_NETWORK)
  1463.             Players[buf[1]].connected = CONNECT_ESCAPE_TUNNEL;
  1464. #endif
  1465.         if (!multi_goto_secret)
  1466.             multi_goto_secret = 2;
  1467.     }
  1468.     else if (buf[2] == 1) 
  1469.     {
  1470.         HUD_init_message("%s %s", Players[buf[1]].callsign, TXT_HAS_FOUND_SECRET);
  1471. #ifndef SHAREWARE
  1472.         if (Game_mode & GM_NETWORK)
  1473.             Players[buf[1]].connected = CONNECT_FOUND_SECRET;
  1474. #endif
  1475.         if (!multi_goto_secret)
  1476.             multi_goto_secret = 1;
  1477.     }
  1478.     create_player_appearance_effect(&Objects[objnum]);
  1479.     multi_make_player_ghost(buf[1]);
  1480. }
  1481.  
  1482.  
  1483. void
  1484. multi_do_remobj(char *buf)
  1485. {
  1486.     short objnum; // which object to remove
  1487.     short local_objnum;
  1488.     byte obj_owner; // which remote list is it entered in
  1489.  
  1490.     objnum = *(short *)(buf+1);
  1491.     obj_owner = buf[3];
  1492.  
  1493.     Assert(objnum >= 0);
  1494.  
  1495.     if (objnum < 1)
  1496.         return;
  1497.  
  1498.     local_objnum = objnum_remote_to_local(objnum, obj_owner); // translate to local objnum
  1499.  
  1500. //    mprintf((0, "multi_do_remobj: %d owner %d = %d.\n", objnum, obj_owner, local_objnum));
  1501.  
  1502.     if (local_objnum < 0)
  1503.     {
  1504.         mprintf((0, "multi_do_remobj: Could not remove referenced object.\n"));
  1505.         return;
  1506.     }
  1507.  
  1508.     if ((Objects[local_objnum].type != OBJ_POWERUP) && (Objects[local_objnum].type != OBJ_HOSTAGE))
  1509.     {
  1510.         mprintf((0, "multi_get_remobj: tried to remove invalid type %d.\n", Objects[local_objnum].type));
  1511.         return;
  1512.     }
  1513.     
  1514.     if (Network_send_objects && network_objnum_is_past(local_objnum))
  1515.     {
  1516.         mprintf((0, "Resetting object sync due to object removal.\n"));
  1517.         Network_send_objnum = -1;
  1518.     }
  1519.  
  1520.     Objects[local_objnum].flags |= OF_SHOULD_BE_DEAD; // quick and painless
  1521.     
  1522. }
  1523.  
  1524. void
  1525. multi_do_quit(char *buf)
  1526. {
  1527.     
  1528.     if (Game_mode & GM_NETWORK)
  1529.     {
  1530.         int i, n = 0;
  1531.  
  1532.         digi_play_sample( SOUND_HUD_MESSAGE, F1_0 );
  1533.  
  1534.         HUD_init_message( "%s %s", Players[buf[1]].callsign, TXT_HAS_LEFT_THE_GAME);
  1535.         
  1536.         network_disconnect_player(buf[1]);
  1537.  
  1538.         if (multi_in_menu)
  1539.             return;
  1540.  
  1541.         for (i = 0; i < N_players; i++)
  1542.             if (Players[i].connected) n++;
  1543.         if (n == 1)
  1544.         {
  1545.             nm_messagebox(NULL, 1, TXT_OK, TXT_YOU_ARE_ONLY);
  1546.         }
  1547.     }
  1548.  
  1549.     if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
  1550.     {
  1551.         Function_mode = FMODE_MENU;
  1552.         multi_quit_game = 1;
  1553.         multi_leave_menu = 1;
  1554.         nm_messagebox(NULL, 1, TXT_OK, TXT_OPPONENT_LEFT);
  1555.         Function_mode = FMODE_GAME;
  1556.         multi_reset_stuff();
  1557.     }
  1558.     return;
  1559. }
  1560.  
  1561. void
  1562. multi_do_cloak(char *buf)
  1563. {
  1564.     int pnum;
  1565.  
  1566.     pnum = buf[1];
  1567.  
  1568.     Assert(pnum < N_players);
  1569.     
  1570.     mprintf((0, "Cloaking player %d\n", pnum));
  1571.  
  1572.     Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
  1573.     Players[pnum].cloak_time = GameTime;
  1574.     ai_do_cloak_stuff();
  1575.  
  1576. #ifndef SHAREWARE
  1577.     if (Game_mode & GM_MULTI_ROBOTS)
  1578.         multi_strip_robots(pnum);
  1579. #endif
  1580.  
  1581.     if (Newdemo_state == ND_STATE_RECORDING)
  1582.         newdemo_record_multi_cloak(pnum);
  1583. }
  1584.     
  1585. void
  1586. multi_do_decloak(char *buf)
  1587. {
  1588.     int pnum;
  1589.  
  1590.     pnum = buf[1];
  1591.  
  1592.     if (Newdemo_state == ND_STATE_RECORDING)
  1593.         newdemo_record_multi_decloak(pnum);
  1594.  
  1595. }
  1596.     
  1597. void
  1598. multi_do_door_open(char *buf)
  1599. {
  1600.     int segnum;
  1601.     short side;
  1602.     segment *seg;
  1603.     wall *w;
  1604.  
  1605. #ifdef SHAREWARE
  1606.     segnum = *(int *)(buf+1);
  1607.     side = *(short *)(buf+5);
  1608. #else
  1609.     segnum = *(short *)(buf+1);
  1610.     side = buf[3];
  1611.     
  1612. #endif
  1613.     
  1614. //    mprintf((0, "Opening door on side %d of segment # %d.\n", side, segnum));
  1615.  
  1616.     if ((segnum < 0) || (segnum > Highest_segment_index) || (side < 0) || (side > 5))
  1617.     {
  1618.         Int3();
  1619.         return;
  1620.     }
  1621.  
  1622.     seg = &Segments[segnum];
  1623.  
  1624.     if (seg->sides[side].wall_num == -1) {    //Opening door on illegal wall
  1625.         Int3();
  1626.         return;
  1627.     }
  1628.  
  1629.     w = &Walls[seg->sides[side].wall_num];
  1630.  
  1631.     if (w->type == WALL_BLASTABLE)
  1632.     {
  1633.         if (!(w->flags & WALL_BLASTED))
  1634.         {
  1635.             mprintf((0, "Blasting wall by remote command.\n"));
  1636.             wall_destroy(seg, side);
  1637.         }
  1638.         return;
  1639.     }
  1640.     else if (w->state != WALL_DOOR_OPENING)
  1641.     {
  1642.         wall_open_door(seg, side);
  1643.     }
  1644. //    else
  1645. //        mprintf((0, "Door already opening!\n"));
  1646. }
  1647.  
  1648. void
  1649. multi_do_create_explosion(char *buf)
  1650. {
  1651.     int pnum;
  1652.     int count = 1;
  1653.  
  1654.     pnum = buf[count++];
  1655.  
  1656. //    mprintf((0, "Creating small fireball.\n"));
  1657.     create_small_fireball_on_object(&Objects[Players[pnum].objnum], F1_0, 1);
  1658. }
  1659.     
  1660. void
  1661. multi_do_controlcen_fire(char *buf)
  1662. {
  1663.     vms_vector to_target;
  1664.     char gun_num;
  1665.     short objnum;
  1666.     int count = 1;
  1667.  
  1668.     memcpy(&to_target, buf+count, 12);    count += 12;
  1669.     gun_num = buf[count];                    count += 1;
  1670.     objnum = *(short *)(buf+count);        count += 2;
  1671.  
  1672.      Laser_create_new_easy(&to_target, &Gun_pos[gun_num], objnum, CONTROLCEN_WEAPON_NUM, 1);
  1673. }
  1674.  
  1675. void
  1676. multi_do_create_powerup(char *buf)
  1677. {
  1678.     short segnum;
  1679.     short objnum;
  1680.     int my_objnum;
  1681.     char pnum;
  1682.     int count = 1;
  1683.     vms_vector new_pos;
  1684.     char powerup_type;
  1685.  
  1686.     if (Endlevel_sequence || Fuelcen_control_center_destroyed)
  1687.         return;
  1688.  
  1689.     pnum = buf[count++];
  1690.     powerup_type = buf[count++];
  1691.     segnum = *(short *)(buf+count); count+=2;
  1692.     objnum = *(short *)(buf+count); count+=2;
  1693.  
  1694.     if ((segnum < 0) || (segnum > Highest_segment_index)) {
  1695.         Int3();
  1696.         return;
  1697.     }
  1698.     
  1699. #ifndef SHAREWARE
  1700.     new_pos = *(vms_vector *)(buf+count); count+=sizeof(vms_vector);
  1701. #else
  1702.     compute_segment_center(&new_pos, &Segments[segnum]);
  1703. #endif
  1704.  
  1705.     Net_create_loc = 0;
  1706.     my_objnum = call_object_create_egg(&Objects[Players[pnum].objnum], 1, OBJ_POWERUP, powerup_type);
  1707.  
  1708.     if (my_objnum < 0) {
  1709.         mprintf((0, "Could not create new powerup!\n"));
  1710.         return;
  1711.     }
  1712.  
  1713.     if (Network_send_objects && network_objnum_is_past(my_objnum))
  1714.     {
  1715.         mprintf((0, "Resetting object sync due to powerup creation.\n"));
  1716.         Network_send_objnum = -1;
  1717.     }
  1718.  
  1719.     Objects[my_objnum].pos = new_pos;
  1720.  
  1721.     vm_vec_zero(&Objects[my_objnum].mtype.phys_info.velocity);
  1722.     
  1723.     obj_relink(my_objnum, segnum);
  1724.  
  1725.     map_objnum_local_to_remote(my_objnum, objnum, pnum);
  1726.  
  1727.     object_create_explosion(segnum, &new_pos, i2f(5), VCLIP_POWERUP_DISAPPEARANCE);
  1728.     mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));
  1729. }        
  1730.  
  1731. void
  1732. multi_do_play_sound(char *buf)
  1733. {
  1734.     int pnum = buf[1];
  1735. #ifdef SHAREWARE
  1736.     int sound_num = *(int *)(buf+2);
  1737.     fix volume = *(fix *)(buf+6);
  1738. #else
  1739.     int sound_num = buf[2];
  1740.     fix volume = buf[3] << 12;
  1741. #endif
  1742.  
  1743.     if (!Players[pnum].connected)
  1744.         return;
  1745.  
  1746.     Assert(Players[pnum].objnum >= 0);
  1747.     Assert(Players[pnum].objnum <= Highest_object_index);
  1748.  
  1749.     digi_link_sound_to_object( sound_num, Players[pnum].objnum, 0, volume);    
  1750. }
  1751.  
  1752. #ifndef SHAREWARE
  1753. void
  1754. multi_do_score(char *buf)
  1755. {
  1756.     int pnum = buf[1];
  1757.     
  1758.     if ((pnum < 0) || (pnum >= N_players))
  1759.     {
  1760.         Int3(); // Non-terminal, see rob
  1761.         return;
  1762.     }
  1763.  
  1764.     if (Newdemo_state == ND_STATE_RECORDING)
  1765.         newdemo_record_multi_score(pnum, *(int *)(buf+2));
  1766.  
  1767.     Players[pnum].score = *(int *)(buf+2);
  1768.  
  1769.     multi_sort_kill_list();
  1770. }
  1771.  
  1772. void
  1773. multi_do_trigger(char *buf)
  1774. {
  1775.     int pnum = buf[1];
  1776.     int trigger = buf[2];
  1777.  
  1778.     if ((pnum < 0) || (pnum >= N_players) || (pnum == Player_num))
  1779.     {
  1780.         Int3(); // Got trigger from illegal playernum
  1781.         return;
  1782.     }
  1783.     if ((trigger < 0) || (trigger >= Num_triggers))
  1784.     {
  1785.         Int3(); // Illegal trigger number in multiplayer
  1786.         return;
  1787.     }
  1788.     check_trigger_sub(trigger, pnum);
  1789. }
  1790.  
  1791. void multi_do_hostage_door_status(char *buf)
  1792. {
  1793.     // Update hit point status of a door
  1794.  
  1795.     int count = 1;
  1796.     int wallnum; 
  1797.     fix hps;
  1798.  
  1799.     wallnum = *(short *)(buf+count);        count += 2;
  1800.     hps = *(fix *)(buf+count);        count += 4;
  1801.  
  1802.     if ((wallnum < 0) || (wallnum > Num_walls) || (hps < 0) || (Walls[wallnum].type != WALL_BLASTABLE))
  1803.     {
  1804.         Int3(); // Non-terminal, see Rob
  1805.         return;
  1806.     }
  1807.  
  1808. //    mprintf((0, "Damaging wall number %d to %f points.\n", wallnum, f2fl(hps)));
  1809.  
  1810.     if (hps < Walls[wallnum].hps)
  1811.         wall_damage(&Segments[Walls[wallnum].segnum], Walls[wallnum].sidenum, Walls[wallnum].hps - hps);
  1812. }
  1813. #endif
  1814.  
  1815. void multi_do_save_game(char *buf)
  1816. {
  1817.     int count = 1;
  1818.     ubyte slot;
  1819.     uint id;
  1820.     char desc[25];
  1821.  
  1822.     slot = *(ubyte *)(buf+count);        count += 1;
  1823.     id = *(uint *)(buf+count);        count += 4;
  1824.     memcpy( desc, &buf[count], 20 );    count += 20;
  1825.  
  1826.     multi_save_game( slot, id, desc );
  1827. }
  1828.  
  1829. void multi_do_restore_game(char *buf)
  1830. {
  1831.     int count = 1;
  1832.     ubyte slot;
  1833.     uint id;
  1834.  
  1835.     slot = *(ubyte *)(buf+count);        count += 1;
  1836.     id = *(uint *)(buf+count);        count += 4;
  1837.  
  1838.     multi_restore_game( slot, id );
  1839. }
  1840.  
  1841. // 
  1842. void multi_do_req_player(char *buf)
  1843. {
  1844.     netplayer_stats ps;
  1845.     ubyte player_n;
  1846.     // Send my netplayer_stats to everyone!
  1847.     player_n = *(ubyte *)(buf+1);
  1848.     if ( (player_n == Player_num) || (player_n == 255)  )    {
  1849.         extract_netplayer_stats( &ps, &Players[Player_num] );
  1850.         ps.Player_num = Player_num;
  1851.         ps.message_type = MULTI_SEND_PLAYER;        // SET
  1852.         multi_send_data((ubyte*)&ps, sizeof(netplayer_stats), 1);
  1853.     }
  1854. }
  1855.  
  1856. void multi_do_send_player(char *buf)
  1857. {
  1858.     // Got a player packet from someone!!!
  1859.     netplayer_stats * p;
  1860.     p = (netplayer_stats *)buf;    
  1861.  
  1862.     Assert( p->Player_num >= 0 );
  1863.     Assert( p->Player_num <= N_players );
  1864.  
  1865.     mprintf(( 0, "Got netplayer_stats for player %d (I'm %d)\n", p->Player_num, Player_num ));
  1866.     mprintf(( 0, "Their shields are: %d\n", f2i(p->shields) ));
  1867.  
  1868. //    use_netplayer_stats( &Players[p->Player_num], p );
  1869. }
  1870.  
  1871. void
  1872. multi_reset_stuff(void)
  1873. {
  1874.     // A generic, emergency function to solve problems that crop up
  1875.     // when a player exits quick-out from the game because of a 
  1876.     // serial connection loss.  Fixes several weird bugs!
  1877.  
  1878.     dead_player_end();
  1879.  
  1880.     Players[Player_num].homing_object_dist = -F1_0; // Turn off homing sound.
  1881.  
  1882.     Dead_player_camera = 0;
  1883.     Endlevel_sequence = 0;
  1884.     reset_rear_view();
  1885. }
  1886.  
  1887. void
  1888. multi_reset_player_object(object *objp)
  1889. {
  1890.     int i;
  1891.     int id;
  1892.  
  1893.     //Init physics for a non-console player
  1894.  
  1895.     Assert(objp >= Objects);
  1896.     Assert(objp <= Objects+Highest_object_index);
  1897.     Assert((objp->type == OBJ_PLAYER) || (objp->type == OBJ_GHOST));
  1898.  
  1899.     vm_vec_zero(&objp->mtype.phys_info.velocity);
  1900.     vm_vec_zero(&objp->mtype.phys_info.thrust);
  1901.     vm_vec_zero(&objp->mtype.phys_info.rotvel);
  1902.     vm_vec_zero(&objp->mtype.phys_info.rotthrust);
  1903.     objp->mtype.phys_info.brakes = objp->mtype.phys_info.turnroll = 0;
  1904.     objp->mtype.phys_info.mass = Player_ship->mass;
  1905.     objp->mtype.phys_info.drag = Player_ship->drag;
  1906. //    objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE | PF_USES_THRUST);
  1907.     objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE);
  1908.  
  1909.     //Init render info
  1910.  
  1911.     objp->render_type = RT_POLYOBJ;
  1912.     objp->rtype.pobj_info.model_num = Player_ship->model_num;        //what model is this?
  1913.     objp->rtype.pobj_info.subobj_flags = 0;        //zero the flags
  1914.     for (i=0;i<MAX_SUBMODELS;i++)
  1915.         vm_angvec_zero(&objp->rtype.pobj_info.anim_angles[i]);
  1916.  
  1917.     //reset textures for this, if not player 0
  1918.  
  1919.     if (Game_mode & GM_TEAM)
  1920.         id = get_team(objp->id);
  1921.     else
  1922.         id = objp->id;
  1923.  
  1924.     if (id == 0)
  1925.         objp->rtype.pobj_info.alt_textures=0;
  1926.     else {
  1927.         Assert(N_PLAYER_SHIP_TEXTURES == Polygon_models[objp->rtype.pobj_info.model_num].n_textures);
  1928.  
  1929.         for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
  1930.             multi_player_textures[id-1][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[objp->rtype.pobj_info.model_num].first_texture+i]];
  1931.  
  1932.         multi_player_textures[id-1][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2]];
  1933.         multi_player_textures[id-1][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2+1]];
  1934.  
  1935.         objp->rtype.pobj_info.alt_textures = id;
  1936.     }
  1937.  
  1938.     // Clear misc
  1939.  
  1940.     objp->flags = 0;
  1941.     
  1942.     if (objp->type == OBJ_GHOST)
  1943.         objp->render_type = RT_NONE;
  1944.  
  1945. }
  1946.  
  1947. void
  1948. multi_process_data(char *buf, int len)
  1949. {
  1950.     // Take an entire message (that has already been checked for validity,
  1951.     // if necessary) and act on it.  
  1952.  
  1953.     int type;
  1954.     len = len;
  1955.  
  1956.     type = buf[0];
  1957.     
  1958.     if (type > MULTI_MAX_TYPE)
  1959.     {
  1960.         mprintf((1, "multi_process_data: invalid type %d.\n", type));
  1961.         Int3();
  1962.         return;
  1963.     }
  1964.  
  1965.     switch(type) 
  1966.     {
  1967.         case MULTI_POSITION:
  1968.             if (!Endlevel_sequence) multi_do_position(buf); break;
  1969.         case MULTI_REAPPEAR:
  1970.             if (!Endlevel_sequence) multi_do_reappear(buf); break;
  1971.         case MULTI_FIRE:
  1972.             if (!Endlevel_sequence) multi_do_fire(buf); break;
  1973.         case MULTI_KILL:
  1974.             multi_do_kill(buf); break;
  1975.         case MULTI_REMOVE_OBJECT:
  1976.             if (!Endlevel_sequence) multi_do_remobj(buf); break;
  1977.         case MULTI_PLAYER_DROP:
  1978.         case MULTI_PLAYER_EXPLODE:
  1979.             if (!Endlevel_sequence) multi_do_player_explode(buf); break;
  1980.         case MULTI_MESSAGE:
  1981.             if (!Endlevel_sequence) multi_do_message(buf); break;
  1982.         case MULTI_QUIT:
  1983.             if (!Endlevel_sequence) multi_do_quit(buf); break;
  1984.         case MULTI_BEGIN_SYNC:
  1985.            break;
  1986.         case MULTI_CONTROLCEN:
  1987.             if (!Endlevel_sequence) multi_do_controlcen_destroy(buf); break;
  1988.         case MULTI_ENDLEVEL_START:
  1989.             if (!Endlevel_sequence) multi_do_escape(buf); break;
  1990.         case MULTI_END_SYNC:
  1991.             break;
  1992.         case MULTI_CLOAK:
  1993.             if (!Endlevel_sequence) multi_do_cloak(buf); break;
  1994.         case MULTI_DECLOAK:
  1995.             if (!Endlevel_sequence) multi_do_decloak(buf); break;
  1996.         case MULTI_DOOR_OPEN:
  1997.             if (!Endlevel_sequence) multi_do_door_open(buf); break;
  1998.         case MULTI_CREATE_EXPLOSION:
  1999.             if (!Endlevel_sequence) multi_do_create_explosion(buf); break;
  2000.         case MULTI_CONTROLCEN_FIRE:
  2001.             if (!Endlevel_sequence) multi_do_controlcen_fire(buf); break;
  2002.         case MULTI_CREATE_POWERUP:
  2003.             if (!Endlevel_sequence) multi_do_create_powerup(buf); break;
  2004.         case MULTI_PLAY_SOUND:
  2005.             if (!Endlevel_sequence) multi_do_play_sound(buf); break;
  2006.  
  2007. #ifndef SHAREWARE
  2008.         case MULTI_ROBOT_CLAIM:
  2009.             if (!Endlevel_sequence) multi_do_claim_robot(buf); break;
  2010.         case MULTI_ROBOT_POSITION:
  2011.             if (!Endlevel_sequence) multi_do_robot_position(buf); break;
  2012.         case MULTI_ROBOT_EXPLODE:
  2013.             if (!Endlevel_sequence) multi_do_robot_explode(buf); break;
  2014.         case MULTI_ROBOT_RELEASE:
  2015.             if (!Endlevel_sequence) multi_do_release_robot(buf); break;
  2016.         case MULTI_ROBOT_FIRE:
  2017.             if (!Endlevel_sequence) multi_do_robot_fire(buf); break;
  2018.         case MULTI_SCORE:
  2019.             if (!Endlevel_sequence) multi_do_score(buf); break;
  2020.         case MULTI_CREATE_ROBOT:
  2021.             if (!Endlevel_sequence) multi_do_create_robot(buf); break;
  2022.         case MULTI_TRIGGER:
  2023.             if (!Endlevel_sequence) multi_do_trigger(buf); break;
  2024.         case MULTI_BOSS_ACTIONS:
  2025.             if (!Endlevel_sequence) multi_do_boss_actions(buf); break;
  2026.         case MULTI_CREATE_ROBOT_POWERUPS:
  2027.             if (!Endlevel_sequence) multi_do_create_robot_powerups(buf); break;
  2028.         case MULTI_HOSTAGE_DOOR:
  2029.             if (!Endlevel_sequence) multi_do_hostage_door_status(buf); break;
  2030.         case MULTI_SAVE_GAME:
  2031.             if (!Endlevel_sequence) multi_do_save_game(buf); break;
  2032.         case MULTI_RESTORE_GAME:
  2033.             if (!Endlevel_sequence) multi_do_restore_game(buf); break;
  2034.         case MULTI_REQ_PLAYER:
  2035.             if (!Endlevel_sequence) multi_do_req_player(buf); break;
  2036.         case MULTI_SEND_PLAYER:
  2037.             if (!Endlevel_sequence) multi_do_send_player(buf); break;
  2038. #endif
  2039.         default:
  2040.             mprintf((1, "Invalid type in multi_process_input().\n"));
  2041.             Int3();
  2042.     }
  2043. }
  2044.  
  2045. void
  2046. multi_process_bigdata(char *buf, int len)
  2047. {
  2048.     // Takes a bunch of messages, check them for validity,
  2049.     // and pass them to multi_process_data. 
  2050.  
  2051.     int type, sub_len, bytes_processed = 0;
  2052.  
  2053.     while( bytes_processed < len )    {
  2054.         type = buf[bytes_processed];
  2055.  
  2056.         if ( (type<0) || (type>MULTI_MAX_TYPE))    {
  2057.             mprintf( (1, "multi_process_bigdata: Invalid packet type %d!\n", type ));
  2058.             return;
  2059.         }
  2060.         sub_len = message_length[type];
  2061.  
  2062.         Assert(sub_len > 0);
  2063.  
  2064.         if ( (bytes_processed+sub_len) > len )    {
  2065.             mprintf( (1, "multi_process_bigdata: packet type %d too short (%d>%d)!\n", type, (bytes_processed+sub_len), len ));
  2066.             Int3();
  2067.             return;
  2068.         }
  2069.  
  2070.         multi_process_data(&buf[bytes_processed], sub_len);
  2071.         bytes_processed += sub_len;
  2072.     }
  2073. }
  2074.  
  2075. //
  2076. // Part 2 : Functions that send communication messages to inform the other
  2077. //          players of something we did.
  2078. //
  2079.  
  2080. void
  2081. multi_send_fire(void)
  2082. {
  2083.     if (!Network_laser_fired)
  2084.         return;
  2085.  
  2086.     multibuf[0] = (char)MULTI_FIRE;
  2087.     multibuf[1] = (char)Player_num;
  2088.     multibuf[2] = (char)Network_laser_gun;
  2089.     multibuf[3] = (char)Network_laser_level;
  2090.     multibuf[4] = (char)Network_laser_flags;
  2091.     multibuf[5] = (char)Network_laser_fired;
  2092.     *(short *)(multibuf+6) = Network_laser_track;
  2093.  
  2094.     multi_send_data(multibuf, 8, 1);
  2095.     
  2096.     Network_laser_fired = 0;
  2097. }
  2098.  
  2099. void
  2100. multi_send_destroy_controlcen(int objnum, int player)
  2101. {
  2102.     if (player == Player_num)
  2103.         HUD_init_message(TXT_YOU_DEST_CONTROL);
  2104.     else if ((player > 0) && (player < N_players))
  2105.         HUD_init_message("%s %s", Players[player].callsign, TXT_HAS_DEST_CONTROL);
  2106.     else
  2107.         HUD_init_message(TXT_CONTROL_DESTROYED);
  2108.  
  2109.     multibuf[0] = (char)MULTI_CONTROLCEN;
  2110.     *(ushort *)(multibuf+1) = objnum;
  2111.     multibuf[3] = player;
  2112.    multi_send_data(multibuf, 4, 2);
  2113. }
  2114.  
  2115. void 
  2116. multi_send_endlevel_start(int secret)
  2117. {
  2118.     multibuf[0] = (char)MULTI_ENDLEVEL_START;
  2119.     multibuf[1] = Player_num;
  2120.     multibuf[2] = (char)secret;
  2121.     
  2122.     if ((secret) && !multi_goto_secret)
  2123.         multi_goto_secret = 1;
  2124.     else if (!multi_goto_secret)
  2125.         multi_goto_secret = 2;
  2126.  
  2127.     multi_send_data(multibuf, 3, 1);
  2128.     if (Game_mode & GM_NETWORK)
  2129.     {
  2130.         Players[Player_num].connected = 5;
  2131.         network_send_endlevel_packet();
  2132.     }
  2133. }
  2134.  
  2135. void
  2136. multi_send_player_explode(char type)
  2137. {
  2138.     int count = 0;
  2139.     int i;
  2140.  
  2141.     Assert( (type == MULTI_PLAYER_DROP) || (type == MULTI_PLAYER_EXPLODE) );
  2142.  
  2143.     multi_send_position(Players[Player_num].objnum);
  2144.  
  2145.     if (Network_send_objects)
  2146.     {
  2147.         mprintf((0, "Resetting object sync due to player explosion.\n"));
  2148.         Network_send_objnum = -1;
  2149.     }
  2150.  
  2151.     multibuf[count++] = type;
  2152.     multibuf[count++] = Player_num;
  2153.     multibuf[count++] = (char)Players[Player_num].primary_weapon_flags;
  2154.     multibuf[count++] = (char)Players[Player_num].secondary_weapon_flags;
  2155.     multibuf[count++] = (char)Players[Player_num].laser_level;
  2156.     multibuf[count++] = (char)Players[Player_num].secondary_ammo[HOMING_INDEX];
  2157.     multibuf[count++] = (char)Players[Player_num].secondary_ammo[CONCUSSION_INDEX];
  2158.     multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_INDEX];
  2159.     multibuf[count++] = (char)Players[Player_num].secondary_ammo[MEGA_INDEX];
  2160.     multibuf[count++] = (char)Players[Player_num].secondary_ammo[PROXIMITY_INDEX];
  2161.     *(ushort *)(multibuf+count) = (ushort)Players[Player_num].primary_ammo[VULCAN_INDEX];
  2162.     count += 2;
  2163.     *(uint *)(multibuf+count) = (uint)Players[Player_num].flags;
  2164.     count += 4;
  2165.     
  2166.     multibuf[count++] = Net_create_loc;
  2167.  
  2168.     Assert(Net_create_loc <= MAX_NET_CREATE_OBJECTS);
  2169.  
  2170.     memset(multibuf+count, -1, MAX_NET_CREATE_OBJECTS*sizeof(short));
  2171.     
  2172.     mprintf((0, "Created %d explosion objects.\n", Net_create_loc));
  2173.  
  2174.     for (i = 0; i < Net_create_loc; i++)
  2175.     {
  2176.         if (Net_create_objnums[i] <= 0) {
  2177.             Int3(); // Illegal value in created egg object numbers
  2178.             count +=2;
  2179.             continue;
  2180.         }
  2181.  
  2182.         *(short *)(multibuf+count) = (short)Net_create_objnums[i]; count += 2;
  2183.  
  2184.         // We created these objs so our local number = the network number
  2185.         map_objnum_local_to_local((short)Net_create_objnums[i]);
  2186.     }
  2187.  
  2188.     Net_create_loc = 0;
  2189.  
  2190. //    mprintf((1, "explode message size = %d, max = %d.\n", count, message_length[MULTI_PLAYER_EXPLODE]));
  2191.  
  2192.     if (count > message_length[MULTI_PLAYER_EXPLODE])
  2193.     {
  2194.         Int3(); // See Rob
  2195.     }
  2196.  
  2197.     multi_send_data(multibuf, message_length[MULTI_PLAYER_EXPLODE], 2);
  2198.     if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
  2199.         multi_send_decloak();
  2200.     multi_strip_robots(Player_num);
  2201. }
  2202.  
  2203. void
  2204. multi_send_message(void)
  2205. {
  2206.     int loc = 0;
  2207.     if (Network_message_reciever != -1)
  2208.     {
  2209.         multibuf[loc] = (char)MULTI_MESSAGE;         loc += 1;
  2210.         multibuf[loc] = (char)Player_num;            loc += 1;
  2211. #ifdef SHAREWARE
  2212.         loc += 1; // Dummy space for receiver (Which isn't used)
  2213. #endif
  2214.         strncpy(multibuf+loc, Network_message, MAX_MESSAGE_LEN); loc += MAX_MESSAGE_LEN;
  2215.         multibuf[loc-1] = '\0';
  2216.         multi_send_data(multibuf, loc, 1);
  2217.         Network_message_reciever = -1;
  2218.     }
  2219. }
  2220.  
  2221. void
  2222. multi_send_reappear()
  2223. {
  2224.     multibuf[0] = (char)MULTI_REAPPEAR;
  2225.     *(short *)(multibuf+1) = Players[Player_num].objnum;
  2226.     
  2227.     multi_send_data(multibuf, 3, 3);
  2228. }
  2229.  
  2230. void
  2231. multi_send_position(int objnum)
  2232. {
  2233.     int count=0;
  2234.  
  2235.     if (Game_mode & GM_NETWORK) {
  2236.         return;
  2237.     }
  2238.  
  2239.     multibuf[count++] = (char)MULTI_POSITION;
  2240.     create_shortpos((shortpos *)(multibuf+count), Objects+objnum);
  2241.     count += sizeof(shortpos);
  2242.     
  2243.     multi_send_data(multibuf, count, 0);
  2244. }
  2245.  
  2246. void
  2247. multi_send_kill(int objnum)
  2248. {
  2249.     // I died, tell the world.
  2250.  
  2251.     int killer_objnum;
  2252.     int count = 0;
  2253.  
  2254.     multibuf[count] = (char)MULTI_KILL;     count += 1;
  2255. #ifndef SHAREWARE
  2256.     multibuf[1] = Player_num;                    count += 1;
  2257. #else
  2258.     *(short *)(multibuf+count) = (short)objnum_local_to_remote(objnum, (byte *)&multibuf[count+2]);
  2259.     count += 3;
  2260. #endif
  2261.  
  2262.     Assert(Objects[objnum].id == Player_num);
  2263.     killer_objnum = Players[Player_num].killer_objnum;
  2264.     if (killer_objnum > -1)
  2265.         *(short *)(multibuf+count) = (short)objnum_local_to_remote(killer_objnum, (byte *)&multibuf[count+2]);
  2266.     else
  2267.     {
  2268.         *(short *)(multibuf+count) = -1;
  2269.         multibuf[count+2] = (char)-1;
  2270.     }
  2271.     count += 3;
  2272.  
  2273.     multi_compute_kill(killer_objnum, objnum);
  2274.     multi_send_data(multibuf, count, 1);
  2275.  
  2276. #ifndef SHAREWARE
  2277.     if (Game_mode & GM_MULTI_ROBOTS)
  2278.         multi_strip_robots(Player_num);
  2279. #endif
  2280. }
  2281.  
  2282. void
  2283. multi_send_remobj(int objnum)
  2284. {
  2285.     // Tell the other guy to remove an object from his list
  2286.  
  2287.     byte obj_owner;
  2288.     short remote_objnum;
  2289.  
  2290.     multibuf[0] = (char)MULTI_REMOVE_OBJECT;
  2291.  
  2292.     remote_objnum = objnum_local_to_remote((short)objnum, &obj_owner);
  2293.  
  2294.     *(short *)(multibuf+1) = remote_objnum; // Map to network objnums
  2295.  
  2296.     multibuf[3] = obj_owner;    
  2297.  
  2298. //    mprintf((0, "multi_send_remobj: %d = %d owner %d.\n", objnum, remote_objnum, obj_owner));
  2299.  
  2300.     multi_send_data(multibuf, 4, 1);
  2301.  
  2302.     if (Network_send_objects && network_objnum_is_past(objnum))
  2303.     {
  2304.         mprintf((0, "Resetting object sync due to object removal.\n"));
  2305.         Network_send_objnum = -1;
  2306.     }
  2307. }
  2308.     
  2309. void
  2310. multi_send_quit(int why)
  2311. {
  2312.     // I am quitting the game, tell the other guy the bad news.
  2313.  
  2314.     Assert (why == MULTI_QUIT);
  2315.  
  2316.     multibuf[0] = (char)why;
  2317.     multibuf[1] = Player_num;
  2318.     multi_send_data(multibuf, 2, 1);
  2319.  
  2320. }
  2321.  
  2322. void
  2323. multi_send_cloak(void)
  2324. {
  2325.     // Broadcast a change in our pflags (made to support cloaking)
  2326.  
  2327.     multibuf[0] = MULTI_CLOAK;
  2328.     multibuf[1] = (char)Player_num;
  2329.  
  2330.     multi_send_data(multibuf, 2, 1);
  2331.  
  2332. #ifndef SHAREWARE
  2333.     if (Game_mode & GM_MULTI_ROBOTS)
  2334.         multi_strip_robots(Player_num);
  2335. #endif
  2336. }
  2337.  
  2338. void
  2339. multi_send_decloak(void)
  2340. {
  2341.     // Broadcast a change in our pflags (made to support cloaking)
  2342.  
  2343.     multibuf[0] = MULTI_DECLOAK;
  2344.     multibuf[1] = (char)Player_num;
  2345.  
  2346.     multi_send_data(multibuf, 2, 1);
  2347. }
  2348.  
  2349. void
  2350. multi_send_door_open(int segnum, int side)
  2351. {
  2352.     // When we open a door make sure everyone else opens that door
  2353.  
  2354.     multibuf[0] = MULTI_DOOR_OPEN;
  2355. #ifdef SHAREWARE
  2356.     *(int *)(multibuf+1) = segnum;
  2357.     *(short *)(multibuf+5) = (short)side;
  2358.     multi_send_data(multibuf, 7, 1);
  2359. #else
  2360.     *(short *)(multibuf+1) = (short)segnum;
  2361.     multibuf[3] = (byte)side;
  2362.     multi_send_data(multibuf, 4, 1);
  2363. #endif
  2364.  
  2365. }
  2366.  
  2367. //
  2368. // Part 3 : Functions that change or prepare the game for multiplayer use.
  2369. //          Not including functions needed to syncronize or start the 
  2370. //          particular type of multiplayer game.  Includes preparing the
  2371. //             mines, player structures, etc.
  2372.  
  2373. void
  2374. multi_send_create_explosion(int pnum)
  2375. {
  2376.     // Send all data needed to create a remote explosion
  2377.  
  2378.     int count = 0;
  2379.  
  2380.     multibuf[count] = MULTI_CREATE_EXPLOSION;     count += 1;
  2381.     multibuf[count] = (byte)pnum;                    count += 1;
  2382.     //                                                    -----------
  2383.     //                                                    Total size = 2
  2384.  
  2385.     multi_send_data(multibuf, count, 0);
  2386. }
  2387.     
  2388. void
  2389. multi_send_controlcen_fire(vms_vector *to_goal, int best_gun_num, int objnum)
  2390. {
  2391.     int count = 0;
  2392.     multibuf[count] = MULTI_CONTROLCEN_FIRE;         count +=  1;
  2393.     memcpy(multibuf+count, to_goal, 12);            count += 12;
  2394.     multibuf[count] = (char)best_gun_num;            count +=  1;
  2395.     *(short *)(multibuf+count) = (short)objnum;    count +=  2;
  2396.     //                                                            ------------
  2397.     //                                                            Total  = 16
  2398.     multi_send_data(multibuf, count, 0);
  2399. }
  2400.  
  2401. void
  2402. multi_send_create_powerup(int powerup_type, int segnum, int objnum, vms_vector *pos)
  2403. {
  2404.     // Create a powerup on a remote machine, used for remote
  2405.     // placement of used powerups like missiles and cloaking
  2406.     // powerups.
  2407.  
  2408.     int count = 0;
  2409.     multibuf[count] = MULTI_CREATE_POWERUP;        count += 1;
  2410.     multibuf[count] = Player_num;                       count += 1;
  2411.     multibuf[count] = powerup_type;                    count += 1;
  2412.     *(short *)(multibuf+count) = (short)segnum;    count += 2;
  2413.     *(short *)(multibuf+count) = (short)objnum;    count += 2;
  2414. #ifndef SHAREWARE
  2415.     *(vms_vector *)(multibuf+count) = *pos;        count += sizeof(vms_vector);
  2416. #endif
  2417.     //                                                          -----------
  2418.     //                                                          Total =  19
  2419.     multi_send_data(multibuf, count, 1);
  2420.  
  2421.     if (Network_send_objects && network_objnum_is_past(objnum))
  2422.     {
  2423.         mprintf((0, "Resetting object sync due to powerup creation.\n"));
  2424.         Network_send_objnum = -1;
  2425.     }
  2426.  
  2427.     mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));
  2428.     map_objnum_local_to_local(objnum);
  2429. }    
  2430.  
  2431. void
  2432. multi_send_play_sound(int sound_num, fix volume)
  2433. {
  2434.     int count = 0;
  2435.     multibuf[count] = MULTI_PLAY_SOUND;            count += 1;
  2436.     multibuf[count] = Player_num;                    count += 1;
  2437. #ifdef SHAREWARE
  2438.     *(int *)(multibuf+count) = sound_num;            count += 4;
  2439.     *(fix *)(multibuf+count) = volume;                count += 4;
  2440.     //                                                            -----------
  2441.     //                                                            Total = 10
  2442. #else
  2443.     multibuf[count] = (char)sound_num;            count += 1;
  2444.     multibuf[count] = (char)(volume >> 12);    count += 1;
  2445.     //                                                       -----------
  2446.     //                                                       Total = 4
  2447. #endif
  2448.     multi_send_data(multibuf, count, 1);
  2449. }
  2450.  
  2451. #pragma off (unreferenced)
  2452. void
  2453. multi_send_audio_taunt(int taunt_num)
  2454. {
  2455.     return; // Taken out, awaiting sounds..
  2456.  
  2457. #if 0
  2458.     int audio_taunts[4] = {
  2459.         SOUND_CONTROL_CENTER_WARNING_SIREN,
  2460.         SOUND_HOSTAGE_RESCUED,
  2461.         SOUND_REFUEL_STATION_GIVING_FUEL,
  2462.         SOUND_BAD_SELECTION
  2463.     };
  2464.  
  2465.  
  2466.     Assert(taunt_num >= 0);
  2467.     Assert(taunt_num < 4);
  2468.  
  2469.     digi_play_sample( audio_taunts[taunt_num], F1_0 );
  2470.     multi_send_play_sound(audio_taunts[taunt_num], F1_0);
  2471. #endif
  2472. }
  2473. #pragma on (unreferenced)
  2474.  
  2475. #ifndef SHAREWARE
  2476. void
  2477. multi_send_score(void)
  2478. {
  2479.     // Send my current score to all other players so it will remain
  2480.     // synced.
  2481.     int count = 0;
  2482.  
  2483.     if (Game_mode & GM_MULTI_COOP) {
  2484.         multi_sort_kill_list();
  2485.         multibuf[count] = MULTI_SCORE;            count += 1;
  2486.         multibuf[count] = Player_num;                count += 1;
  2487.         *(int *)(multibuf+count) = Players[Player_num].score;  count += 4;
  2488.         multi_send_data(multibuf, count, 0);
  2489.     }
  2490. }    
  2491.  
  2492.  
  2493. void
  2494. multi_send_save_game(ubyte slot, uint id, char * desc)
  2495. {
  2496.     int count = 0;
  2497.     
  2498.     multibuf[count] = MULTI_SAVE_GAME;        count += 1;
  2499.     multibuf[count] = slot;                            count += 1;        // Save slot=0
  2500.     *(uint *)(multibuf+count) = id;     count += 4;        // Save id
  2501.     memcpy( &multibuf[count], desc, 20 ); count += 20;
  2502.  
  2503.     multi_send_data(multibuf, count, 2);
  2504. }
  2505.  
  2506. void
  2507. multi_send_restore_game(ubyte slot, uint id)
  2508. {
  2509.     int count = 0;
  2510.     
  2511.     multibuf[count] = MULTI_RESTORE_GAME;    count += 1;
  2512.     multibuf[count] = slot;                            count += 1;        // Save slot=0
  2513.     *(uint *)(multibuf+count) = id;     count += 4;        // Save id
  2514.  
  2515.     multi_send_data(multibuf, count, 2);
  2516. }
  2517.  
  2518. void
  2519. multi_send_netplayer_stats_request(ubyte player_num)
  2520. {
  2521.     int count = 0;
  2522.     
  2523.     multibuf[count] = MULTI_REQ_PLAYER;    count += 1;
  2524.     multibuf[count] = player_num;            count += 1;
  2525.  
  2526.     multi_send_data(multibuf, count, 2 );
  2527. }
  2528.  
  2529.  
  2530.  
  2531. void
  2532. multi_send_trigger(int triggernum)
  2533. {
  2534.     // Send an even to trigger something in the mine
  2535.     
  2536.     int count = 0;
  2537.     
  2538.     multibuf[count] = MULTI_TRIGGER;                count += 1;
  2539.     multibuf[count] = Player_num;                    count += 1;
  2540.     multibuf[count] = (ubyte)triggernum;        count += 1;
  2541.  
  2542.     multi_send_data(multibuf, count, 2);
  2543. }
  2544.  
  2545. void
  2546. multi_send_hostage_door_status(int wallnum)
  2547. {
  2548.     // Tell the other player what the hit point status of a hostage door
  2549.     // should be
  2550.  
  2551.     int count = 0;
  2552.     
  2553.     Assert(Walls[wallnum].type == WALL_BLASTABLE);
  2554.  
  2555.     multibuf[count] = MULTI_HOSTAGE_DOOR;        count += 1;
  2556.     *(short *)(multibuf+count) = wallnum;        count += 2;
  2557.     *(fix *)(multibuf+count) = Walls[wallnum].hps;     count += 4;
  2558.  
  2559. //    mprintf((0, "Door %d damaged by %f points.\n", wallnum, f2fl(Walls[wallnum].hps)));
  2560.  
  2561.     multi_send_data(multibuf, count, 0);
  2562. }
  2563. #endif
  2564.  
  2565. void
  2566. multi_prep_level(void)
  2567. {
  2568.     // Do any special stuff to the level required for serial games
  2569.     // before we begin playing in it.
  2570.  
  2571.     // Player_num MUST be set before calling this procedure.  
  2572.  
  2573.     // This function must be called before checksuming the Object array,
  2574.     // since the resulting checksum with depend on the value of Player_num
  2575.     // at the time this is called.
  2576.  
  2577.     int i;
  2578.     int    cloak_count, inv_count;
  2579.  
  2580.     Assert(Game_mode & GM_MULTI);
  2581.  
  2582.     Assert(NumNetPlayerPositions > 0);
  2583.  
  2584.     for (i = 0; i < NumNetPlayerPositions; i++)
  2585.     {
  2586.         if (i != Player_num)
  2587.             Objects[Players[i].objnum].control_type = CT_REMOTE;
  2588.         Objects[Players[i].objnum].movement_type = MT_PHYSICS;
  2589.         multi_reset_player_object(&Objects[Players[i].objnum]);
  2590.         LastPacketTime[i] = 0;
  2591.     }
  2592.  
  2593. #ifndef SHAREWARE
  2594.     for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
  2595.     {
  2596.         robot_controlled[i] = -1;    
  2597.         robot_agitation[i] = 0;
  2598.         robot_fired[i] = 0;
  2599.     }
  2600. #endif
  2601.  
  2602.     Viewer = ConsoleObject = &Objects[Players[Player_num].objnum];
  2603.  
  2604.     if (!(Game_mode & GM_MULTI_COOP))    
  2605.     {
  2606.         multi_delete_extra_objects(); // Removes monsters from level
  2607.     }
  2608.  
  2609.     if (Game_mode & GM_MULTI_ROBOTS)
  2610.     {
  2611.         multi_set_robot_ai(); // Set all Robot AI to types we can cope with
  2612.     }
  2613.  
  2614.     inv_count = 0;
  2615.     cloak_count = 0;
  2616.     for (i=0; i<=Highest_object_index; i++)
  2617.     {
  2618.         int objnum;
  2619.  
  2620.         if ((Objects[i].type == OBJ_HOSTAGE) && !(Game_mode & GM_MULTI_COOP))
  2621.         {
  2622.              objnum = obj_create(OBJ_POWERUP, POW_SHIELD_BOOST, Objects[i].segnum, &Objects[i].pos, &vmd_identity_matrix, Powerup_info[POW_SHIELD_BOOST].size, CT_POWERUP, MT_PHYSICS, RT_POWERUP);
  2623.             obj_delete(i);            
  2624.             if (objnum != -1)
  2625.             {
  2626.                 Objects[objnum].rtype.vclip_info.vclip_num = Powerup_info[POW_SHIELD_BOOST].vclip_num;
  2627.                 Objects[objnum].rtype.vclip_info.frametime = Vclip[Objects[objnum].rtype.vclip_info.vclip_num].frame_time;
  2628.                 Objects[objnum].rtype.vclip_info.framenum = 0;
  2629.                 Objects[objnum].mtype.phys_info.drag = 512;    //1024;
  2630.                 Objects[objnum].mtype.phys_info.mass = F1_0;
  2631.                 vm_vec_zero(&Objects[objnum].mtype.phys_info.velocity);
  2632.             }
  2633.             continue;
  2634.         }
  2635.  
  2636.         if (Objects[i].type == OBJ_POWERUP)
  2637.         {
  2638.             if (Objects[i].id == POW_EXTRA_LIFE) 
  2639.             {
  2640.                 Objects[i].id = POW_INVULNERABILITY;
  2641.                 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
  2642.                 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
  2643.             }
  2644.  
  2645.             if (Game_mode & GM_MULTI_COOP)
  2646.                 continue;
  2647.  
  2648.             if ((Objects[i].id >= POW_KEY_BLUE) && (Objects[i].id <= POW_KEY_GOLD))
  2649.             {
  2650.                 Objects[i].id = POW_SHIELD_BOOST;
  2651.                 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
  2652.                 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
  2653.             }
  2654.  
  2655.             if (Objects[i].id == POW_INVULNERABILITY) {
  2656.                 if (inv_count >= 3) {
  2657.                     mprintf((0, "Bashing Invulnerability object #%i to shield.\n", i));
  2658.                     Objects[i].id = POW_SHIELD_BOOST;
  2659.                     Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
  2660.                     Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
  2661.                 } else
  2662.                     inv_count++;
  2663.             }
  2664.  
  2665.             if (Objects[i].id == POW_CLOAK) {
  2666.                 if (cloak_count >= 3) {
  2667.                     mprintf((0, "Bashing Cloak object #%i to shield.\n", i));
  2668.                     Objects[i].id = POW_SHIELD_BOOST;
  2669.                     Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
  2670.                     Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
  2671.                 } else
  2672.                     cloak_count++;
  2673.             }
  2674.         }
  2675.     }
  2676.     
  2677.     multi_sort_kill_list();
  2678.  
  2679.     multi_show_player_list();
  2680.  
  2681.     ConsoleObject->control_type = CT_FLYING;
  2682.  
  2683.     reset_player_object();
  2684. }
  2685.  
  2686. void multi_set_robot_ai(void)
  2687. {
  2688.     // Go through the objects array looking for robots and setting
  2689.     // them to certain supported types of NET AI behavior.
  2690.  
  2691. //    int i;
  2692. //
  2693. //    for (i = 0; i <= Highest_object_index; i++)
  2694. //    {
  2695. //        if (Objects[i].type == OBJ_ROBOT) {
  2696. //            Objects[i].ai_info.REMOTE_OWNER = -1;
  2697. //            if (Objects[i].ai_info.behavior == AIB_STATION)
  2698. //                Objects[i].ai_info.behavior = AIB_NORMAL;
  2699. //        }
  2700. //    }
  2701. }
  2702.  
  2703. int multi_delete_extra_objects()
  2704. {
  2705.     int i;
  2706.     int nnp=0;
  2707.     object *objp;
  2708.     
  2709.     // Go through the object list and remove any objects not used in
  2710.     // 'Anarchy!' games.
  2711.  
  2712.     // This function also prints the total number of available multiplayer
  2713.     // positions in this level, even though this should always be 8 or more!
  2714.  
  2715.     objp = Objects;
  2716.     for (i=0;i<=Highest_object_index;i++) {
  2717.         if ((objp->type==OBJ_PLAYER) || (objp->type==OBJ_GHOST)) 
  2718.             nnp++;
  2719.         else if ((objp->type==OBJ_ROBOT) && (Game_mode & GM_MULTI_ROBOTS))
  2720.             ;
  2721.         else if ( (objp->type!=OBJ_NONE) && (objp->type!=OBJ_PLAYER) && (objp->type!=OBJ_POWERUP) && (objp->type!=OBJ_CNTRLCEN) && (objp->type!=OBJ_HOSTAGE) )
  2722.             obj_delete(i);
  2723.         objp++;
  2724.     }
  2725.  
  2726.     return nnp;
  2727. }
  2728.  
  2729. int
  2730. network_i_am_master(void)
  2731. {
  2732.     // I am the lowest numbered player in this game?
  2733.  
  2734.     int i;
  2735.  
  2736.     if (!(Game_mode & GM_NETWORK))
  2737.         return (Player_num == 0);
  2738.  
  2739.     for (i = 0; i < Player_num; i++)
  2740.         if (Players[i].connected)
  2741.             return 0;
  2742.     return 1;
  2743. }
  2744.  
  2745. void change_playernum_to( int new_Player_num )    
  2746. {
  2747.     if (Player_num > -1)
  2748.         memcpy( Players[new_Player_num].callsign, Players[Player_num].callsign, CALLSIGN_LEN+1 );
  2749.     Player_num = new_Player_num;
  2750. }
  2751.  
  2752. #endif
  2753.  
  2754. void multi_initiate_save_game()
  2755. {
  2756.     uint game_id;
  2757.     int i, slot;
  2758.     char filename[128];
  2759.     char desc[24];
  2760.  
  2761.     if ((Endlevel_sequence) || (Fuelcen_control_center_destroyed))
  2762.         return;
  2763.  
  2764. //    multi_send_netplayer_stats_request(255);
  2765. //    return;
  2766.  
  2767.     stop_time();
  2768.     
  2769.     slot = state_get_save_file(filename, desc, 1 );
  2770.     if (!slot)    {
  2771.         start_time();
  2772.         return;
  2773.     }
  2774.     slot--;
  2775.     start_time();
  2776.  
  2777.     // Make a unique game id
  2778.     game_id = timer_get_fixed_seconds();
  2779.     game_id ^= N_players<<4;
  2780.     for (i=0; i<N_players; i++ )
  2781.         game_id ^= *(uint *)Players[i].callsign;
  2782.     if ( game_id == 0 ) game_id = 1;        // 0 is invalid
  2783.  
  2784.     mprintf(( 1, "Game_id = %8x\n", game_id));
  2785.     multi_send_save_game(slot, game_id, desc );
  2786.     multi_do_frame();
  2787.     multi_save_game(slot,game_id, desc );
  2788. }
  2789.  
  2790. void multi_initiate_restore_game()
  2791. {
  2792.     int slot;
  2793.     char filename[128];
  2794.  
  2795.     if ((Endlevel_sequence) || (Fuelcen_control_center_destroyed))
  2796.         return;
  2797.  
  2798.     stop_time();
  2799.     slot = state_get_restore_file(filename,1);
  2800.     if (!slot)    {
  2801.         start_time();
  2802.         return;
  2803.     }
  2804.     slot--;
  2805.     start_time();
  2806.     multi_send_restore_game(slot,state_game_id);
  2807.     multi_do_frame();
  2808.     multi_restore_game(slot,state_game_id);
  2809. }
  2810.  
  2811. void multi_save_game(ubyte slot, uint id, char *desc)
  2812. {
  2813.     char filename[128];
  2814.  
  2815.     if ((Endlevel_sequence) || (Fuelcen_control_center_destroyed))
  2816.         return;
  2817.  
  2818.     sprintf( filename, "%s.mg%d", Players[Player_num].callsign, slot );
  2819.     mprintf(( 0, "Save game %x on slot %d\n", id, slot ));
  2820.     HUD_init_message( "Saving game #%d, '%s'", slot, desc );
  2821.     stop_time();
  2822.     state_game_id = id;
  2823.     state_save_all_sub(filename, desc, 0 );
  2824. }
  2825.  
  2826. void multi_restore_game(ubyte slot, uint id)
  2827. {
  2828.     char filename[128];
  2829.     player saved_player;
  2830.  
  2831.     if ((Endlevel_sequence) || (Fuelcen_control_center_destroyed))
  2832.         return;
  2833.  
  2834.     mprintf(( 0, "Restore game %x from slot %d\n", id, slot ));
  2835.     saved_player = Players[Player_num];
  2836.     sprintf( filename, "%s.mg%d", Players[Player_num].callsign, slot );
  2837.     state_game_id = 0;
  2838.     state_restore_all_sub( filename, 1 );
  2839.  
  2840.     if (state_game_id != id )    {
  2841.         // Game doesn't match!!!
  2842.         nm_messagebox( "Error", 1, "Ok", "Cannot restore saved game" );
  2843.         Game_mode |= GM_GAME_OVER;
  2844.         Function_mode = FMODE_MENU;
  2845.         longjmp(LeaveGame, 0);
  2846.     }
  2847.  
  2848.     memcpy( Players[Player_num].callsign, saved_player.callsign, CALLSIGN_LEN+1 );
  2849.     memcpy( Players[Player_num].net_address, saved_player.net_address, 6 );
  2850.     Players[Player_num].connected = saved_player.connected;
  2851.     Players[Player_num].n_packets_got  = saved_player.n_packets_got;                    
  2852.     Players[Player_num].n_packets_sent = saved_player.n_packets_sent;                
  2853. }
  2854.  
  2855.  
  2856. void extract_netplayer_stats( netplayer_stats *ps, player * pd )
  2857. {
  2858.     ps->flags = pd->flags;                            // Powerup flags, see below...
  2859.     ps->energy = pd->energy;                            // Amount of energy remaining.
  2860.     ps->shields = pd->shields;                            // shields remaining (protection) 
  2861.     ps->lives = pd->lives;                            // Lives remaining, 0 = game over.
  2862.     ps->laser_level = pd->laser_level;                    //    Current level of the laser.
  2863.     ps->primary_weapon_flags=pd->primary_weapon_flags;                    //    bit set indicates the player has this weapon.
  2864.     ps->secondary_weapon_flags=pd->secondary_weapon_flags;                    //    bit set indicates the player has this weapon.
  2865.     memcpy( ps->primary_ammo, pd->primary_ammo, MAX_PRIMARY_WEAPONS*sizeof(short) );    // How much ammo of each type.
  2866.     memcpy( ps->secondary_ammo, pd->secondary_ammo, MAX_SECONDARY_WEAPONS*sizeof(short) ); // How much ammo of each type.
  2867.     ps->last_score=pd->last_score;                            // Score at beginning of current level.
  2868.     ps->score=pd->score;                                            // Current score.
  2869.     ps->cloak_time=pd->cloak_time;                            // Time cloaked
  2870.     ps->homing_object_dist=pd->homing_object_dist;        //    Distance of nearest homing object.
  2871.     ps->invulnerable_time=pd->invulnerable_time;            // Time invulnerable
  2872.     ps->net_killed_total=pd->net_killed_total;            // Number of times killed total
  2873.      ps->net_kills_total=pd->net_kills_total;                // Number of net kills total
  2874.     ps->num_kills_level=pd->num_kills_level;                // Number of kills this level
  2875.      ps->num_kills_total=pd->num_kills_total;                // Number of kills total
  2876.     ps->num_robots_level=pd->num_robots_level;             // Number of initial robots this level
  2877.     ps->num_robots_total=pd->num_robots_total;             // Number of robots total
  2878.     ps->hostages_rescued_total=pd->hostages_rescued_total;    // Total number of hostages rescued.
  2879.     ps->hostages_total=pd->hostages_total;                    // Total number of hostages.
  2880.     ps->hostages_on_board=pd->hostages_on_board;            //    Number of hostages on ship.
  2881. }
  2882.  
  2883. void use_netplayer_stats( player * ps, netplayer_stats *pd )
  2884. {
  2885.     ps->flags = pd->flags;                            // Powerup flags, see below...
  2886.     ps->energy = pd->energy;                            // Amount of energy remaining.
  2887.     ps->shields = pd->shields;                            // shields remaining (protection) 
  2888.     ps->lives = pd->lives;                            // Lives remaining, 0 = game over.
  2889.     ps->laser_level = pd->laser_level;                    //    Current level of the laser.
  2890.     ps->primary_weapon_flags=pd->primary_weapon_flags;                    //    bit set indicates the player has this weapon.
  2891.     ps->secondary_weapon_flags=pd->secondary_weapon_flags;                    //    bit set indicates the player has this weapon.
  2892.     memcpy( ps->primary_ammo, pd->primary_ammo, MAX_PRIMARY_WEAPONS*sizeof(short) );    // How much ammo of each type.
  2893.     memcpy( ps->secondary_ammo, pd->secondary_ammo, MAX_SECONDARY_WEAPONS*sizeof(short) ); // How much ammo of each type.
  2894.     ps->last_score=pd->last_score;                            // Score at beginning of current level.
  2895.     ps->score=pd->score;                                            // Current score.
  2896.     ps->cloak_time=pd->cloak_time;                            // Time cloaked
  2897.     ps->homing_object_dist=pd->homing_object_dist;        //    Distance of nearest homing object.
  2898.     ps->invulnerable_time=pd->invulnerable_time;            // Time invulnerable
  2899.     ps->net_killed_total=pd->net_killed_total;            // Number of times killed total
  2900.      ps->net_kills_total=pd->net_kills_total;                // Number of net kills total
  2901.     ps->num_kills_level=pd->num_kills_level;                // Number of kills this level
  2902.      ps->num_kills_total=pd->num_kills_total;                // Number of kills total
  2903.     ps->num_robots_level=pd->num_robots_level;             // Number of initial robots this level
  2904.     ps->num_robots_total=pd->num_robots_total;             // Number of robots total
  2905.     ps->hostages_rescued_total=pd->hostages_rescued_total;    // Total number of hostages rescued.
  2906.     ps->hostages_total=pd->hostages_total;                    // Total number of hostages.
  2907.     ps->hostages_on_board=pd->hostages_on_board;            //    Number of hostages on ship.
  2908. }
  2909. 
  2910.