]> icculus.org git repositories - btb/d2x.git/blob - main/multi.c
these routines are in tmerge.c
[btb/d2x.git] / main / multi.c
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-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 /*
15  * $Source: /cvs/cvsroot/d2x/main/multi.c,v $
16  * $Revision: 1.6 $
17  * $Author: btb $
18  * $Date: 2002-08-30 01:01:18 $
19  *
20  * FIXME: put description here
21  *
22  * $Log: not supported by cvs2svn $
23  * Revision 1.5  2001/10/23 21:53:19  bradleyb
24  * No longer #ifdef'ing out the whole file.  RCS header added
25  *
26  *
27  */
28
29 #ifdef HAVE_CONFIG_H
30 #include <conf.h>
31 #endif
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <time.h>
37 #include <ctype.h>
38
39 #include "u_mem.h"
40 #include "strutil.h"
41 #include "game.h"
42 #include "modem.h"
43 #include "network.h"
44 #include "multi.h"
45 #include "object.h"
46 #include "laser.h"
47 #include "fuelcen.h"
48 #include "scores.h"
49 #include "gauges.h"
50 #include "collide.h"
51 #include "error.h"
52 #include "fireball.h"
53 #include "newmenu.h"
54 #include "mono.h"
55 #include "wall.h"
56 #include "cntrlcen.h"
57 #include "powerup.h"
58 #include "polyobj.h"
59 #include "bm.h"
60 #include "endlevel.h"
61 #include "key.h"
62 #include "playsave.h"
63 #include "timer.h"
64 #include "digi.h"
65 #include "sounds.h"
66 #include "kconfig.h"
67 #include "newdemo.h"
68 #include "text.h"
69 #include "kmatrix.h"
70 #include "multibot.h"
71 #include "gameseq.h"
72 #include "physics.h"
73 #include "config.h"
74 #include "state.h"
75 #include "ai.h"
76 #include "switch.h"
77 #include "textures.h"
78 #include "byteswap.h"
79 #include "sounds.h"
80 #include "args.h"
81 #include "d_delay.h"
82 #include "cfile.h"
83 #include "effects.h"
84
85 void multi_reset_player_object(object *objp);
86 void multi_reset_object_texture (object *objp);
87 void multi_add_lifetime_killed ();
88 void multi_add_lifetime_kills ();
89 void multi_send_play_by_play (int num,int spnum,int dpnum);
90 void multi_send_heartbeat ();
91 void multi_send_modem_ping ();
92 void multi_cap_objects ();
93 void multi_adjust_remote_cap (int pnum);
94 void multi_save_game(ubyte slot, uint id, char *desc);
95 void multi_restore_game(ubyte slot, uint id);
96 void multi_set_robot_ai(void);
97 void multi_send_powerup_update ();
98 void bash_to_shield (int i,char *s);
99 void init_hoard_data();
100 void multi_apply_goal_textures();
101 int find_goal_texture (ubyte t);
102 void multi_bad_restore ();
103 void multi_do_capture_bonus(char *buf);
104 void multi_do_orb_bonus(char *buf);
105 void multi_send_drop_flag (int objnum,int seed);
106 void multi_send_ranking ();
107 void multi_do_play_by_play (char *buf);
108
109 //
110 // Local macros and prototypes
111 //
112
113 // LOCALIZE ME!!
114
115 #define vm_angvec_zero(v) (v)->p=(v)->b=(v)->h=0
116
117 void drop_player_eggs(object *player); // from collide.c
118 void GameLoop(int, int); // From game.c
119
120 //
121 // Global variables
122 //
123
124 extern vms_vector MarkerPoint[];
125 extern char MarkerMessage[16][40];
126 extern char MarkerOwner[16][40];
127 extern int MarkerObject[];
128
129 int control_invul_time = 0;
130 int who_killed_controlcen = -1;  // -1 = noone
131
132 //do we draw the kill list on the HUD?
133 int Show_kill_list = 1;
134 int Show_reticle_name = 1;
135 fix Show_kill_list_timer = 0;
136
137 char Multi_is_guided=0;
138 char PKilledFlags[MAX_NUM_NET_PLAYERS];
139
140 int multi_sending_message = 0;
141 int multi_defining_message = 0;
142 int multi_message_index = 0;
143
144 char multibuf[MAX_MULTI_MESSAGE_LEN+4];                // This is where multiplayer message are built
145
146 short remote_to_local[MAX_NUM_NET_PLAYERS][MAX_OBJECTS];  // Remote object number for each local object
147 short local_to_remote[MAX_OBJECTS]; 
148 byte  object_owner[MAX_OBJECTS];   // Who created each object in my universe, -1 = loaded at start
149
150 int     Net_create_objnums[MAX_NET_CREATE_OBJECTS]; // For tracking object creation that will be sent to remote
151 int     Net_create_loc = 0;  // pointer into previous array
152 int     Network_laser_fired = 0; // How many times we shot
153 int     Network_laser_gun; // Which gun number we shot
154 int   Network_laser_flags; // Special flags for the shot
155 int   Network_laser_level; // What level
156 short   Network_laser_track; // Who is it tracking?
157 char    Network_message[MAX_MESSAGE_LEN];
158 char  Network_message_macro[4][MAX_MESSAGE_LEN];
159 int     Network_message_reciever=-1;
160 int     sorted_kills[MAX_NUM_NET_PLAYERS];
161 short kill_matrix[MAX_NUM_NET_PLAYERS][MAX_NUM_NET_PLAYERS];
162 int     multi_goto_secret = 0;
163 short   team_kills[2];
164 int     multi_in_menu = 0;
165 int     multi_leave_menu = 0;
166 int     multi_quit_game = 0;
167
168 netgame_info Netgame;
169 AllNetPlayers_info NetPlayers;
170
171 bitmap_index multi_player_textures[MAX_NUM_NET_PLAYERS][N_PLAYER_SHIP_TEXTURES];
172
173 typedef struct netplayer_stats {
174         ubyte           message_type;
175         ubyte           Player_num;                                             // Who am i?
176         uint            flags;                                                  // Powerup flags, see below...
177         fix             energy;                                                 // Amount of energy remaining.
178         fix             shields;                                                        // shields remaining (protection) 
179         ubyte           lives;                                                  // Lives remaining, 0 = game over.
180         ubyte           laser_level;                                    //      Current level of the laser.
181         ubyte           primary_weapon_flags;                                   //      bit set indicates the player has this weapon.
182         ubyte           secondary_weapon_flags;                                 //      bit set indicates the player has this weapon.
183         ushort  primary_ammo[MAX_PRIMARY_WEAPONS];      // How much ammo of each type.
184         ushort  secondary_ammo[MAX_SECONDARY_WEAPONS]; // How much ammo of each type.
185         int             last_score;                                     // Score at beginning of current level.
186         int             score;                                                  // Current score.
187         fix             cloak_time;                                             // Time cloaked
188         fix             invulnerable_time;                      // Time invulnerable
189         fix             homing_object_dist;                     //      Distance of nearest homing object.
190         short           KillGoalCount;
191         short           net_killed_total;                               // Number of times killed total
192         short           net_kills_total;                                // Number of net kills total
193         short           num_kills_level;                                // Number of kills this level
194         short           num_kills_total;                                // Number of kills total
195         short           num_robots_level;                       // Number of initial robots this level
196         short           num_robots_total;                       // Number of robots total
197         ushort  hostages_rescued_total;         // Total number of hostages rescued.
198         ushort  hostages_total;                         // Total number of hostages.
199         ubyte           hostages_on_board;                      //      Number of hostages on ship.
200         ubyte           unused[16];
201 } netplayer_stats;                                                      
202
203 int message_length[MULTI_MAX_TYPE+1] = {
204         24, // POSITION
205         3,  // REAPPEAR
206         8,  // FIRE
207         5,  // KILL
208         4,  // REMOVE_OBJECT
209         97+9, // PLAYER_EXPLODE
210         37, // MESSAGE (MAX_MESSAGE_LENGTH = 40)
211         2,  // QUIT
212         4,  // PLAY_SOUND
213         41, // BEGIN_SYNC
214         4,  // CONTROLCEN
215         5,  // CLAIM ROBOT
216         4,  // END_SYNC
217    2,  // CLOAK
218         3,  // ENDLEVEL_START
219    5,  // DOOR_OPEN
220         2,  // CREATE_EXPLOSION
221         16, // CONTROLCEN_FIRE
222         97+9, // PLAYER_DROP
223         19, // CREATE_POWERUP
224         9,  // MISSILE_TRACK
225         2,  // DE-CLOAK
226         2,       // MENU_CHOICE
227         28, // ROBOT_POSITION  (shortpos_length (23) + 5 = 28)
228         9,  // ROBOT_EXPLODE
229         5,       // ROBOT_RELEASE
230         18, // ROBOT_FIRE
231         6,  // SCORE
232         6,  // CREATE_ROBOT
233         3,  // TRIGGER
234         10, // BOSS_ACTIONS     
235         27, // ROBOT_POWERUPS
236         7,  // HOSTAGE_DOOR
237         2+24,   //SAVE_GAME             (ubyte slot, uint id, char name[20])
238         2+4,   //RESTORE_GAME   (ubyte slot, uint id)
239         1+1,    // MULTI_REQ_PLAYER
240         sizeof(netplayer_stats),                        // MULTI_SEND_PLAYER
241         55, // MULTI_MARKER
242         12, // MULTI_DROP_WEAPON
243 #ifndef MACINTOSH
244         3+sizeof(shortpos), // MULTI_GUIDED, IF SHORTPOS CHANGES, CHANGE MAC VALUE BELOW
245 #else
246         26,     //MULTI_GUIDED IF SIZE OF SHORTPOS CHANGES, CHANGE THIS VALUE AS WELL!!!!!!
247 #endif
248         11, // MULTI_STOLEN_ITEMS
249         6, // MULTI_WALL_STATUS
250         5, // MULTI_HEARTBEAT
251         9, // MULTI_KILLGOALS
252    9, // MULTI_SEISMIC
253         18, // MULTI_LIGHT
254    2, // MULTI_START_TRIGGER     
255         6, // MULTI_FLAGS
256         2, // MULTI_DROP_BLOB
257         MAX_POWERUP_TYPES+1, // MULTI_POWERUP_UPDATE
258         sizeof(active_door)+3, // MULTI_ACTIVE_DOOR
259         4, // MULTI_SOUND_FUNCTION
260         2, // MULTI_CAPTURE_BONUS
261    2, // MULTI_GOT_FLAG
262         12, // MULTI_DROP_FLAG
263    142, // MULTI_ROBOT_CONTROLS
264    2, // MULTI_FINISH_GAME
265    3, // MULTI_RANK
266    1, // MULTI_MODEM_PING
267    1, // MULTI_MODEM_PING_RETURN
268         3, // MULTI_ORB_BONUS
269    2, // MULTI_GOT_ORB
270         12, // MULTI_DROP_ORB
271         4,  // MULTI_PLAY_BY_PLAY
272 };
273
274 void extract_netplayer_stats( netplayer_stats *ps, player * pd );
275 void use_netplayer_stats( player * ps, netplayer_stats *pd );
276 char PowerupsInMine[MAX_POWERUP_TYPES],MaxPowerupsAllowed[MAX_POWERUP_TYPES];
277 extern fix ThisLevelTime;
278
279 //
280 //  Functions that replace what used to be macros
281 //              
282
283 int objnum_remote_to_local(int remote_objnum, int owner)
284 {
285         // Map a remote object number from owner to a local object number
286
287         int result;     
288
289         if ((owner >= N_players) || (owner < -1)) {
290                 Int3(); // Illegal!
291                 return(remote_objnum);
292         }
293
294         if (owner == -1)
295                 return(remote_objnum);
296         
297         if ((remote_objnum < 0) || (remote_objnum >= MAX_OBJECTS))
298                 return(-1);
299
300         result = remote_to_local[owner][remote_objnum];
301
302         if (result < 0)
303         {
304                 mprintf((1, "Remote object owner %d number %d mapped to -1!\n", owner, remote_objnum));
305                 return(-1);
306         }
307
308 #ifndef NDEBUG
309         if (object_owner[result] != owner)
310         {
311                 mprintf((1, "Remote object owner %d number %d doesn't match owner %d.\n", owner, remote_objnum, object_owner[result]));
312         }
313 #endif  
314 //      Assert(object_owner[result] == owner);
315
316         return(result);
317 }
318
319 int objnum_local_to_remote(int local_objnum, byte *owner)
320 {
321         // Map a local object number to a remote + owner
322
323         int result;
324
325         if ((local_objnum < 0) || (local_objnum > Highest_object_index)) {
326                 *owner = -1;
327                 return(-1);
328         }
329
330         *owner = object_owner[local_objnum];
331
332         if (*owner == -1)
333                 return(local_objnum);
334
335         if ((*owner >= N_players) || (*owner < -1)) {
336                 Int3(); // Illegal!
337                 *owner = -1;
338                 return local_objnum;
339         }
340         
341         result = local_to_remote[local_objnum];
342         
343 //      mprintf((0, "Local object %d mapped to owner %d objnum %d.\n", local_objnum,
344 //              *owner, result));
345
346         if (result < 0)
347         {
348                 Int3(); // See Rob, object has no remote number!
349         }
350
351         return(result);
352 }
353
354 void
355 map_objnum_local_to_remote(int local_objnum, int remote_objnum, int owner)
356 {
357         // Add a mapping from a network remote object number to a local one
358
359         Assert(local_objnum > -1);
360         Assert(remote_objnum > -1);
361         Assert(owner > -1);
362         Assert(owner != Player_num);
363         Assert(local_objnum < MAX_OBJECTS);
364         Assert(remote_objnum < MAX_OBJECTS);
365
366         object_owner[local_objnum] = owner;
367
368         remote_to_local[owner][remote_objnum] = local_objnum;
369         local_to_remote[local_objnum] = remote_objnum;
370
371         return;
372 }
373
374 void
375 map_objnum_local_to_local(int local_objnum)
376 {
377         // Add a mapping for our locally created objects
378
379         Assert(local_objnum > -1);
380         Assert(local_objnum < MAX_OBJECTS);
381
382         object_owner[local_objnum] = Player_num;
383         remote_to_local[Player_num][local_objnum] = local_objnum;
384         local_to_remote[local_objnum] = local_objnum;
385
386         return;
387 }
388
389 //
390 // Part 1 : functions whose main purpose in life is to divert the flow
391 //          of execution to either network or serial specific code based
392 //          on the curretn Game_mode value.
393 //
394
395 void
396 multi_endlevel_score(void)
397 {
398         int old_connect=0;
399         int i;
400
401         // Show a score list to end of net players
402
403         // Save connect state and change to new connect state
404 #ifdef NETWORK
405         if (Game_mode & GM_NETWORK)
406         {
407                 old_connect = Players[Player_num].connected;
408                 if (Players[Player_num].connected!=3)
409                  Players[Player_num].connected = CONNECT_END_MENU;
410                 Network_status = NETSTAT_ENDLEVEL;
411                 
412         }
413 #endif
414
415         // Do the actual screen we wish to show
416
417         Function_mode = FMODE_MENU;
418
419         kmatrix_view(Game_mode & GM_NETWORK);
420
421         Function_mode = FMODE_GAME;
422
423         // Restore connect state
424
425         if (Game_mode & GM_NETWORK)
426         {
427           Players[Player_num].connected = old_connect;
428         }
429
430 #ifndef SHAREWARE
431         if (Game_mode & GM_MULTI_COOP)
432         {
433            int i;
434                 for (i = 0; i < MaxNumNetPlayers; i++)
435                 // Reset keys
436                         Players[i].flags &= ~(PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_GOLD_KEY);
437         }
438         for (i = 0; i < MaxNumNetPlayers; i++)
439            Players[i].flags &= ~(PLAYER_FLAGS_FLAG);  // Clear capture flag
440         
441 #endif
442
443   for (i=0;i<MAX_PLAYERS;i++)
444     Players[i].KillGoalCount=0;
445
446   for (i=0;i<MAX_POWERUP_TYPES;i++)
447         {
448                 MaxPowerupsAllowed[i]=0;
449                 PowerupsInMine[i]=0;
450         }  
451
452 }
453
454 int
455 get_team(int pnum)
456 {
457         if ((Game_mode & GM_CAPTURE) && ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)))
458                 return pnum;
459
460         if (Netgame.team_vector & (1 << pnum))
461                 return 1;
462         else
463                 return 0;
464 }
465
466 int
467 multi_choose_mission(int *anarchy_only)
468 {
469         int i, n_missions;
470         int default_mission;
471    char *m[MAX_MISSIONS];
472         int new_mission_num = 0;
473
474         *anarchy_only = 0;
475
476         n_missions = build_mission_list(1);
477
478         if (n_missions > 1) {
479
480                 default_mission = 0;
481                 for (i=0;i<n_missions;i++) {
482                         m[i] = Mission_list[i].mission_name;
483                         if ( !stricmp( m[i], config_last_mission ) )    
484                                 default_mission = i;
485                 }
486
487       ExtGameStatus=GAMESTAT_START_MULTIPLAYER_MISSION;
488                 new_mission_num = newmenu_listbox1(TXT_MULTI_MISSION, n_missions, m, 1, default_mission, NULL );
489
490                 if (new_mission_num == -1)
491                         return -1;      //abort!
492
493                 strcpy(config_last_mission, m[new_mission_num]  );
494                 
495                 if (!load_mission(new_mission_num)) {
496                         nm_messagebox( NULL, 1, TXT_OK, TXT_MISSION_ERROR); 
497                         return -1;
498                 }
499
500            *anarchy_only = Mission_list[new_mission_num].anarchy_only_flag;
501         }
502         return(new_mission_num);
503 }
504
505 extern void game_disable_cheats();
506         
507 void
508 multi_new_game(void)
509 {
510         int i;
511         
512         // Reset variables for a new net game
513
514         memset(kill_matrix, 0, MAX_NUM_NET_PLAYERS*MAX_NUM_NET_PLAYERS*2); // Clear kill matrix
515
516         for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
517         {
518                 sorted_kills[i] = i;
519                 Players[i].net_killed_total = 0;
520                 Players[i].net_kills_total = 0;
521                 Players[i].flags = 0;
522                 Players[i].KillGoalCount=0;
523         }
524
525 #ifndef SHAREWARE
526         for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
527         {
528                 robot_controlled[i] = -1;       
529                 robot_agitation[i] = 0;
530                 robot_fired[i] = 0;
531         }
532 #endif
533
534         team_kills[0] = team_kills[1] = 0;
535         Endlevel_sequence = 0;
536         Player_is_dead = 0;
537         multi_leave_menu = 0;
538         multi_quit_game = 0;
539         Show_kill_list = 1;
540         game_disable_cheats();
541         Player_exploded = 0;
542         Dead_player_camera = 0;
543 }
544         
545 void
546 multi_make_player_ghost(int playernum)
547 {
548         object *obj;
549
550 //      Assert(playernum != Player_num);
551 //      Assert(playernum < MAX_NUM_NET_PLAYERS);
552
553         if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS) || (playernum < 0))
554         {
555                 Int3(); // Non-terminal, see Rob
556                 return;
557         }
558
559 //      if (Objects[Players[playernum].objnum].type != OBJ_PLAYER)
560 //              mprintf((1, "Warning: Player %d is not currently a player.\n", playernum));
561
562         obj = &Objects[Players[playernum].objnum];
563
564         obj->type = OBJ_GHOST;
565         obj->render_type = RT_NONE;
566         obj->movement_type = MT_NONE;
567         multi_reset_player_object(obj);
568
569         if (Game_mode & GM_MULTI_ROBOTS)
570                 multi_strip_robots(playernum);
571 }
572
573 void
574 multi_make_ghost_player(int playernum)
575 {
576         object *obj;
577
578 // Assert(playernum != Player_num);
579 // Assert(playernum < MAX_NUM_NET_PLAYERS);
580
581         if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS))
582         {
583                 Int3(); // Non-terminal, see rob
584                 return;
585         }
586
587 //      if(Objects[Players[playernum].objnum].type != OBJ_GHOST)
588 //              mprintf((1, "Warning: Player %d is not currently a ghost.\n", playernum));
589
590         obj = &Objects[Players[playernum].objnum];
591
592         obj->type = OBJ_PLAYER;
593         obj->movement_type = MT_PHYSICS;
594         multi_reset_player_object(obj);
595 }
596
597 int multi_get_kill_list(int *plist)
598 {
599         // Returns the number of active net players and their
600         // sorted order of kills
601         int i;
602         int n = 0;
603
604         for (i = 0; i < N_players; i++)
605 //              if (Players[sorted_kills[i]].connected)
606                         plist[n++] = sorted_kills[i];
607
608         if (n == 0)
609                 Int3(); // SEE ROB OR MATT
610
611 //      memcpy(plist, sorted_kills, N_players*sizeof(int));     
612
613         return(n);
614 }
615         
616 void
617 multi_sort_kill_list(void)
618 {
619         // Sort the kills list each time a new kill is added
620
621         int kills[MAX_NUM_NET_PLAYERS];
622         int i;
623         int changed = 1;
624         
625         for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
626         {
627 #ifndef SHAREWARE
628                 if (Game_mode & GM_MULTI_COOP)
629                         kills[i] = Players[i].score;
630                 else
631 #endif
632                 if (Show_kill_list==2)
633                  {
634                   if (Players[i].net_killed_total+Players[i].net_kills_total==0)
635                         kills[i]=-1;  // always draw the ones without any ratio last
636                   else
637                    kills[i]=(int)((float)((float)Players[i].net_kills_total/((float)Players[i].net_killed_total+(float)Players[i].net_kills_total))*100.0);             
638                  }      
639                 else                    
640                         kills[i] = Players[i].net_kills_total;
641         }
642
643         while (changed)
644         {
645                 changed = 0;
646                 for (i = 0; i < N_players-1; i++)
647                 {
648                         if (kills[sorted_kills[i]] < kills[sorted_kills[i+1]])
649                         {
650                                 changed = sorted_kills[i];
651                                 sorted_kills[i] = sorted_kills[i+1];
652                                 sorted_kills[i+1] = changed;
653                                 changed = 1;
654                         }
655                 }
656         }
657       mprintf((0, "Sorted kills %d %d.\n", sorted_kills[0], sorted_kills[1]));
658 }
659
660 extern object *obj_find_first_of_type (int);
661 char Multi_killed_yourself=0;
662
663 void multi_compute_kill(int killer, int killed)
664 {
665         // Figure out the results of a network kills and add it to the
666         // appropriate player's tally.
667
668         int killed_pnum, killed_type;
669         int killer_pnum, killer_type,killer_id;
670         int TheGoal;
671         char killed_name[(CALLSIGN_LEN*2)+4];
672         char killer_name[(CALLSIGN_LEN*2)+4];
673
674         kmatrix_kills_changed = 1;
675    Multi_killed_yourself=0;
676   
677         // Both object numbers are localized already!
678
679         mprintf((0, "compute_kill passed: object %d killed object %d.\n", killer, killed));
680
681         if ((killed < 0) || (killed > Highest_object_index) || (killer < 0) || (killer > Highest_object_index))
682         {
683                 Int3(); // See Rob, illegal value passed to compute_kill;
684                 return;
685         }
686
687         killed_type = Objects[killed].type;
688         killer_type = Objects[killer].type;
689    killer_id = Objects[killer].id;
690
691         if ((killed_type != OBJ_PLAYER) && (killed_type != OBJ_GHOST)) 
692         {
693                 Int3(); // compute_kill passed non-player object!
694                 return;
695         }
696
697         killed_pnum = Objects[killed].id;
698
699    PKilledFlags[killed_pnum]=1;
700
701         Assert ((killed_pnum >= 0) && (killed_pnum < N_players));
702
703         if (Game_mode & GM_TEAM)
704                 sprintf(killed_name, "%s (%s)", Players[killed_pnum].callsign, Netgame.team_name[get_team(killed_pnum)]);
705         else
706                 sprintf(killed_name, "%s", Players[killed_pnum].callsign);
707
708         if (Newdemo_state == ND_STATE_RECORDING)
709                 newdemo_record_multi_death(killed_pnum);
710
711         digi_play_sample( SOUND_HUD_KILL, F3_0 );
712
713    if (Control_center_destroyed)
714          Players[killed_pnum].connected=3;
715
716         if (killer_type == OBJ_CNTRLCEN)
717         {
718                 Players[killed_pnum].net_killed_total++;
719                 Players[killed_pnum].net_kills_total--;
720
721                 if (Newdemo_state == ND_STATE_RECORDING)
722                         newdemo_record_multi_kill(killed_pnum, -1);
723
724                 if (killed_pnum == Player_num)
725                  {
726                         HUD_init_message("%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_NONPLAY);
727                         multi_add_lifetime_killed ();
728                  }
729                 else
730                         HUD_init_message("%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_NONPLAY );
731                 return;         
732         }
733
734 #ifndef SHAREWARE
735         else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST))
736         {
737                 if (killer_id==PMINE_ID && killer_type!=OBJ_ROBOT)
738             {
739                   if (killed_pnum == Player_num) 
740                                 HUD_init_message("You were killed by a mine!");
741                         else
742                                 HUD_init_message("%s was killed by a mine!",killed_name);
743                  }
744                 else
745             {
746                   if (killed_pnum == Player_num) 
747                          {
748                                 HUD_init_message("%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_ROBOT);  
749                                 multi_add_lifetime_killed();
750                          }
751              else
752                                 HUD_init_message("%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_ROBOT );
753        }
754                 Players[killed_pnum].net_killed_total++;
755                 return;         
756         }
757 #else
758         else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST) && (killer_id!=PMINE_ID))
759         {
760                 Int3(); // Illegal killer type?
761                 return;
762         }
763   if (killer_id==PMINE_ID)
764    {
765     if (killed_pnum==Player_num)
766            HUD_init_message("You were killed by a mine!");
767     else
768                 HUD_init_message("%s was killed by a mine!",killed_name);
769          
770     Players[killed_pnum].net_killed_total++;
771         
772     return;
773    }
774 #endif
775
776         killer_pnum = Objects[killer].id;
777
778         if (Game_mode & GM_TEAM)
779                 sprintf(killer_name, "%s (%s)", Players[killer_pnum].callsign, Netgame.team_name[get_team(killer_pnum)]);
780         else
781                 sprintf(killer_name, "%s", Players[killer_pnum].callsign);
782
783         // Beyond this point, it was definitely a player-player kill situation
784
785         if ((killer_pnum < 0) || (killer_pnum >= N_players))
786                 Int3(); // See rob, tracking down bug with kill HUD messages
787         if ((killed_pnum < 0) || (killed_pnum >= N_players))
788                 Int3(); // See rob, tracking down bug with kill HUD messages
789
790         if (killer_pnum == killed_pnum)
791         {
792       if (!(Game_mode & GM_HOARD))
793                 {
794                         if (Game_mode & GM_TEAM)
795                                 team_kills[get_team(killed_pnum)] -= 1;
796
797                         Players[killed_pnum].net_killed_total += 1;
798                         Players[killed_pnum].net_kills_total -= 1;
799
800                         if (Newdemo_state == ND_STATE_RECORDING)
801                                 newdemo_record_multi_kill(killed_pnum, -1);
802                 }
803                 kill_matrix[killed_pnum][killed_pnum] += 1; // # of suicides
804
805                 if (killer_pnum == Player_num)
806                  {
807                         HUD_init_message("%s %s %s!", TXT_YOU, TXT_KILLED, TXT_YOURSELF );      
808                    Multi_killed_yourself=1;
809                         multi_add_lifetime_killed();
810                  }
811                 else
812                         HUD_init_message("%s %s", killed_name, TXT_SUICIDE);
813         }
814
815         else
816         {
817                 if (!(Game_mode & GM_HOARD))
818                 {
819                         if (Game_mode & GM_TEAM)
820                         {
821                                 if (get_team(killed_pnum) == get_team(killer_pnum))
822                                         team_kills[get_team(killed_pnum)] -= 1;
823                                 else
824                                         team_kills[get_team(killer_pnum)] += 1;
825                         }
826
827                         Players[killer_pnum].net_kills_total += 1;
828                         Players[killer_pnum].KillGoalCount+=1;
829
830                         if (Newdemo_state == ND_STATE_RECORDING)
831                                 newdemo_record_multi_kill(killer_pnum, 1);
832                 }
833                 else
834                 {
835                         if (Game_mode & GM_TEAM)
836                         {
837                                 if (killed_pnum==Player_num && get_team(killed_pnum) == get_team(killer_pnum))
838                                         Multi_killed_yourself=1;
839                         }
840                 }
841         
842                 kill_matrix[killer_pnum][killed_pnum] += 1;
843                 Players[killed_pnum].net_killed_total += 1;
844                 if (killer_pnum == Player_num) {
845                         HUD_init_message("%s %s %s!", TXT_YOU, TXT_KILLED, killed_name);
846                         multi_add_lifetime_kills();
847                         if ((Game_mode & GM_MULTI_COOP) && (Players[Player_num].score >= 1000))
848                                 add_points_to_score(-1000);
849                 }
850                 else if (killed_pnum == Player_num)
851                   {
852                         HUD_init_message("%s %s %s!", killer_name, TXT_KILLED, TXT_YOU);
853                         multi_add_lifetime_killed();
854                         if (Game_mode & GM_HOARD) 
855                          {
856                                 if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]>3)
857                                         multi_send_play_by_play (1,killer_pnum,Player_num);
858                                 else if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]>0)
859                                         multi_send_play_by_play (0,killer_pnum,Player_num);
860                          }
861                   }
862                 else
863                         HUD_init_message("%s %s %s!", killer_name, TXT_KILLED, killed_name);
864         }
865
866   TheGoal=Netgame.KillGoal*5;
867
868   if (Netgame.KillGoal>0)
869         {
870          if (Players[killer_pnum].KillGoalCount>=TheGoal)
871           {
872            if (killer_pnum==Player_num)
873             {
874              HUD_init_message("You reached the kill goal!");
875              Players[Player_num].shields=i2f(200);
876             }  
877            else
878             HUD_init_message ("%s has reached the kill goal!",Players[killer_pnum].callsign);
879
880            HUD_init_message ("The control center has been destroyed!");
881       net_destroy_controlcen (obj_find_first_of_type (OBJ_CNTRLCEN));
882           }
883         }
884
885         multi_sort_kill_list();
886         multi_show_player_list();
887    Players[killed_pnum].flags&=(~(PLAYER_FLAGS_HEADLIGHT_ON));  // clear the killed guys flags/headlights
888 }
889
890 void
891 multi_do_frame(void)
892 {
893      static int lasttime=0;
894      int i;
895
896         if (!(Game_mode & GM_MULTI))
897         {
898                 Int3();
899                 return;
900         }
901
902   if ((Game_mode & GM_NETWORK) && Netgame.PlayTimeAllowed && lasttime!=f2i (ThisLevelTime))
903         {
904          for (i=0;i<N_players;i++)
905           if (Players[i].connected)
906            {
907             if (i==Player_num)
908              {
909               multi_send_heartbeat();
910               lasttime=f2i(ThisLevelTime);
911              }
912             break;
913            }
914         }
915
916         multi_send_message(); // Send any waiting messages
917
918         if (!multi_in_menu)
919                 multi_leave_menu = 0;
920
921 #ifndef SHAREWARE
922         if (Game_mode & GM_MULTI_ROBOTS)
923         {
924                 multi_check_robot_timeout();
925         }
926 #endif  
927
928         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
929         {
930                 com_do_frame();
931         }
932         else
933         {
934                 network_do_frame(0, 1);
935         }
936
937         if (multi_quit_game && !multi_in_menu)
938         {
939                 multi_quit_game = 0;
940                 longjmp(LeaveGame, 0);
941         }
942 }
943                 
944 void
945 multi_send_data(char *buf, int len, int repeat)
946 {
947         Assert(len == message_length[(int)buf[0]]);
948         Assert(buf[0] <= MULTI_MAX_TYPE);
949 //      Assert(buf[0] >= 0);
950
951         if (Game_mode & GM_NETWORK)
952                 Assert(buf[0] > 0);
953
954         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
955                 com_send_data(buf, len, repeat);
956         else if (Game_mode & GM_NETWORK)
957                 network_send_data(buf, len, repeat);
958 }
959
960 void
961 multi_leave_game(void)
962 {
963
964 //      if (Function_mode != FMODE_GAME)
965 //              return;
966
967         if (!(Game_mode & GM_MULTI))
968                 return;
969
970         if (Game_mode & GM_NETWORK)
971         {
972                 mprintf((0, "Sending explosion message.\n"));
973
974                 Net_create_loc = 0;
975                 AdjustMineSpawn();
976                 multi_cap_objects();
977                 drop_player_eggs(ConsoleObject);
978                 multi_send_position(Players[Player_num].objnum);
979                 multi_send_player_explode(MULTI_PLAYER_DROP);
980         }
981
982         mprintf((1, "Sending leave game.\n"));
983         multi_send_quit(MULTI_QUIT);
984
985         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
986                 serial_leave_game();
987         if (Game_mode & GM_NETWORK)
988                 network_leave_game();
989
990         Game_mode |= GM_GAME_OVER;
991
992    if (Function_mode!=FMODE_EXIT)
993                 Function_mode = FMODE_MENU;
994
995 //      N_players = 0;
996
997 //      change_playernum_to(0);
998 //      Viewer = ConsoleObject = &Objects[0];
999
1000 }
1001                 
1002 void 
1003 multi_show_player_list()
1004 {
1005         if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_COOP))
1006                 return;
1007
1008         if (Show_kill_list)
1009                 return;
1010
1011         Show_kill_list_timer = F1_0*5; // 5 second timer
1012         Show_kill_list = 1;
1013 }
1014
1015 int 
1016 multi_endlevel(int *secret)
1017 {
1018         int result = 0;
1019
1020         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
1021                 com_endlevel(secret);          // an opportunity to re-sync or whatever
1022         else if (Game_mode & GM_NETWORK)
1023                 result = network_endlevel(secret);
1024         
1025         return(result);         
1026 }
1027
1028 //
1029 // Part 2 : functions that act on network/serial messages and change the
1030 //          the state of the game in some way.
1031 //
1032
1033 #ifndef MACINTOSH
1034 //extern PORT *com_port;
1035 #endif
1036
1037 int 
1038 multi_menu_poll(void)
1039 {
1040         fix old_shields;
1041         int was_fuelcen_alive;
1042         int player_was_dead;
1043
1044         was_fuelcen_alive = Control_center_destroyed;
1045
1046         // Special polling function for in-game menus for multiplayer and serial
1047
1048         if (! ((Game_mode & GM_MULTI) && (Function_mode == FMODE_GAME)) )
1049                 return(0);
1050
1051         if (multi_leave_menu)
1052                 return(-1);
1053
1054         old_shields = Players[Player_num].shields;
1055         player_was_dead = Player_is_dead;
1056
1057         multi_in_menu++; // Track level of menu nesting
1058
1059         GameLoop( 0, 0 );                       
1060
1061         multi_in_menu--;
1062
1063 //      timer_delay(f1_0/10);   changed by allender for portability
1064         d_delay(100);                           // delay 100 milliseconds
1065                 
1066         if (Endlevel_sequence || (Control_center_destroyed && !was_fuelcen_alive) || (Player_is_dead != player_was_dead) || (Players[Player_num].shields < old_shields))
1067         {
1068                 multi_leave_menu = 1;
1069                 return(-1);
1070         }
1071         if ((Control_center_destroyed) && (Countdown_seconds_left < 10))
1072         {
1073                 multi_leave_menu = 1;
1074                 return(-1);
1075         }
1076
1077 #if 0
1078         if ((Game_mode & GM_MODEM) && (!GetCd(com_port)))
1079         {
1080                 multi_leave_menu = 1;
1081                 return(-1);
1082         }
1083 #endif
1084
1085         return(0);
1086 }
1087
1088 void
1089 multi_define_macro(int key)
1090 {
1091         if (!(Game_mode & GM_MULTI))
1092                 return;
1093
1094         key &= (~KEY_SHIFTED);
1095
1096         switch(key) 
1097         {
1098                 case KEY_F9:
1099                         multi_defining_message = 1; break;
1100                 case KEY_F10:
1101                         multi_defining_message = 2; break;
1102                 case KEY_F11:
1103                         multi_defining_message = 3; break;
1104                 case KEY_F12:
1105                         multi_defining_message = 4; break;
1106                 default:
1107                         Int3();
1108         }
1109
1110         if (multi_defining_message)     {
1111                 multi_message_index = 0;
1112                 Network_message[multi_message_index] = 0;
1113         }
1114
1115 }
1116
1117 char feedback_result[200];
1118
1119 void
1120 multi_message_feedback(void)
1121 {
1122         char *colon;
1123         int found = 0;
1124         int i;
1125
1126         if (!( ((colon = strrchr(Network_message, ':')) == NULL) || (colon-Network_message < 1) || (colon-Network_message > CALLSIGN_LEN) ))
1127         {
1128                 sprintf(feedback_result, "%s ", TXT_MESSAGE_SENT_TO);
1129                 if ((Game_mode & GM_TEAM) && (atoi(Network_message) > 0) && (atoi(Network_message) < 3))
1130                 {
1131                         sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[atoi(Network_message)-1]);
1132                         found = 1;
1133                 }
1134                 if (Game_mode & GM_TEAM)
1135                 {
1136                         for (i = 0; i < N_players; i++)
1137                         {
1138                                 if (!strnicmp(Netgame.team_name[i], Network_message, colon-Network_message))
1139                                 {
1140                                         if (found)
1141                                                 strcat(feedback_result, ", ");
1142                                         found++;
1143                                         if (!(found % 4))
1144                                                 strcat(feedback_result, "\n");
1145                                         sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[i]);
1146                                 }
1147                         }
1148                 }
1149                 for (i = 0; i < N_players; i++)
1150                 {
1151                         if ((!strnicmp(Players[i].callsign, Network_message, colon-Network_message)) && (i != Player_num) && (Players[i].connected))
1152                         {
1153                                 if (found)
1154                                         strcat(feedback_result, ", ");
1155                                 found++;
1156                                 if (!(found % 4))
1157                                         strcat(feedback_result, "\n");
1158                                 sprintf(feedback_result+strlen(feedback_result), "%s", Players[i].callsign);
1159                         }
1160                 }
1161                 if (!found)
1162                         strcat(feedback_result, TXT_NOBODY);
1163                 else
1164                         strcat(feedback_result, ".");
1165
1166                 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1167
1168                 Assert(strlen(feedback_result) < 200);
1169
1170                 HUD_init_message(feedback_result);
1171 //      sprintf (temp,"%s",colon);
1172 //         sprintf (Network_message,"%s",temp);
1173  
1174         }
1175 }
1176
1177 void
1178 multi_send_macro(int key)
1179 {
1180         if (! (Game_mode & GM_MULTI) )
1181                 return;
1182
1183         switch(key) 
1184         {
1185                 case KEY_F9:
1186                         key = 0; break;
1187                 case KEY_F10:
1188                         key = 1; break;
1189                 case KEY_F11:
1190                         key = 2; break;
1191                 case KEY_F12:
1192                         key = 3; break;
1193                 default:
1194                         Int3();
1195         }
1196
1197         if (!Network_message_macro[key][0])
1198         {
1199                 HUD_init_message(TXT_NO_MACRO);
1200                 return;
1201         }
1202
1203         strcpy(Network_message, Network_message_macro[key]);
1204         Network_message_reciever = 100;
1205
1206         HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
1207         multi_message_feedback();
1208 }
1209
1210
1211 void 
1212 multi_send_message_start()
1213 {
1214         if (Game_mode&GM_MULTI) {
1215                 multi_sending_message = 1;
1216                 multi_message_index = 0;
1217                 Network_message[multi_message_index] = 0;
1218         }
1219 }
1220
1221 extern fix StartingShields;
1222 fix PingLaunchTime,PingReturnTime;
1223
1224 extern void network_send_ping (ubyte);
1225 extern int network_who_is_master();
1226 extern char NameReturning;
1227 extern int force_cockpit_redraw;
1228
1229 void network_dump_appletalk_player(ubyte node, ushort net, ubyte socket, int why);
1230
1231 void multi_send_message_end()
1232 {
1233         char *mytempbuf;
1234    int i,t;
1235         
1236         Network_message_reciever = 100;
1237
1238         if (!strnicmp (Network_message,"!Names",6))
1239                 {
1240                  NameReturning=1-NameReturning;
1241                  HUD_init_message ("Name returning is now %s.",NameReturning?"active":"disabled");
1242                 }
1243         else if (!strnicmp (Network_message,"Handicap:",9))
1244                 {
1245                  mytempbuf=&Network_message[9];          
1246                  mprintf ((0,"Networkhandi=%s\n",mytempbuf));
1247                  StartingShields=atol (mytempbuf);
1248                  if (StartingShields<10)
1249                         StartingShields=10;
1250                  if (StartingShields>100)
1251                         {
1252                                 sprintf (Network_message,"%s has tried to cheat!",Players[Player_num].callsign);
1253                                 StartingShields=100;
1254                         }
1255                  else
1256                         sprintf (Network_message,"%s handicap is now %d",Players[Player_num].callsign,StartingShields);
1257
1258                  HUD_init_message ("Telling others of your handicap of %d!",StartingShields);
1259                  StartingShields=i2f(StartingShields);
1260                 }
1261         else if (!strnicmp (Network_message,"NoBombs",7))
1262          Netgame.DoSmartMine=0;
1263         else if (!strnicmp (Network_message,"Ping:",5))
1264          {
1265           if (Game_mode & GM_NETWORK)
1266                 {
1267                         int name_index=5;
1268                         if (strlen(Network_message) > 5)
1269                                 while (Network_message[name_index] == ' ')
1270                                         name_index++;
1271
1272                         if (strlen(Network_message)<=name_index)
1273                          {
1274                                 HUD_init_message ("You must specify a name to ping");
1275                                 return;
1276                          }
1277
1278                         for (i = 0; i < N_players; i++)
1279                 if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (i != Player_num) && (Players[i].connected))
1280             {
1281                                  PingLaunchTime=timer_get_fixed_seconds();
1282                                  network_send_ping (i);
1283                                  HUD_init_message("Pinging %s...",Players[i].callsign);
1284                             multi_message_index = 0;
1285                                  multi_sending_message = 0;
1286                                  return;
1287                            }
1288                 }
1289           else // Modem/Serial ping
1290            {
1291                          PingLaunchTime=timer_get_fixed_seconds();
1292                          multi_send_modem_ping ();
1293                          HUD_init_message("Pinging opponent...");
1294                     multi_message_index = 0;
1295                          multi_sending_message = 0;
1296                          return;
1297            }
1298          }                                                                                               
1299         else if (!strnicmp (Network_message,"move:",5))
1300          { 
1301                 mprintf ((0,"moving?\n"));
1302                 
1303           if ((Game_mode & GM_NETWORK) && (Game_mode & GM_TEAM))
1304                 {
1305                         int name_index=5;
1306                         if (strlen(Network_message) > 5)
1307                                 while (Network_message[name_index] == ' ')
1308                                         name_index++;
1309
1310                   if (!network_i_am_master())
1311                         {       
1312                          HUD_init_message ("Only %s can move players!",Players[network_who_is_master()].callsign);
1313                          return;
1314                         }
1315         
1316                         if (strlen(Network_message)<=name_index)
1317                          {
1318                                 HUD_init_message ("You must specify a name to move");
1319                                 return;
1320                          }
1321
1322                         for (i = 0; i < N_players; i++)
1323                 if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (Players[i].connected))
1324             {
1325                                  if ((Game_mode & GM_CAPTURE) && (Players[i].flags & PLAYER_FLAGS_FLAG))
1326                                   {
1327                                         HUD_init_message ("Can't move player because s/he has a flag!");
1328                                         return;
1329                                   }
1330                 
1331                                  if (Netgame.team_vector & (1<<i))
1332                                    Netgame.team_vector&=(~(1<<i));
1333                                  else
1334                                    Netgame.team_vector|=(1<<i);
1335         
1336                                  for (t=0;t<N_players;t++)
1337                                  if (Players[t].connected)
1338                                 multi_reset_object_texture (&Objects[Players[t].objnum]);
1339
1340                                  network_send_netgame_update ();        
1341                                  sprintf (Network_message,"%s has changed teams!",Players[i].callsign);
1342                                  if (i==Player_num)
1343                                         {
1344                                     HUD_init_message ("You have changed teams!");
1345                                          reset_cockpit();       
1346                                         }
1347                                  else
1348                                          HUD_init_message ("Moving %s to other team.",Players[i].callsign);
1349                                  break;
1350                            }
1351                   }    
1352          }                                                                                               
1353
1354         else if (!strnicmp (Network_message,"kick:",5) && (Game_mode & GM_NETWORK))
1355          {
1356                 int name_index=5;
1357                 if (strlen(Network_message) > 5)
1358                         while (Network_message[name_index] == ' ')
1359                                 name_index++;
1360
1361           if (!network_i_am_master())
1362                 {       
1363                  HUD_init_message ("Only %s can kick others out!",Players[network_who_is_master()].callsign);
1364             multi_message_index = 0;
1365             multi_sending_message = 0;
1366                  return;
1367                 }
1368                 if (strlen(Network_message)<=name_index)
1369                  {
1370                         HUD_init_message ("You must specify a name to kick");
1371                         multi_message_index = 0;
1372                         multi_sending_message = 0;
1373                         return;
1374                  }
1375
1376                 if (Network_message[name_index] == '#' && isdigit(Network_message[name_index+1])) {
1377                         int players[MAX_NUM_NET_PLAYERS];
1378                         int listpos = Network_message[name_index+1] - '0';
1379
1380                         mprintf ((0,"Trying to kick %d , show_kill_list=%d\n",listpos,Show_kill_list));
1381
1382                         if (Show_kill_list==1 || Show_kill_list==2) {
1383                                 if (listpos == 0 || listpos >= N_players) {
1384                                         HUD_init_message ("Invalid player number for kick.");
1385                                    multi_message_index = 0;
1386                                    multi_sending_message = 0;
1387                                         return;
1388                                 }
1389                                 multi_get_kill_list(players);
1390                                 i = players[listpos];
1391                                 if ((i != Player_num) && (Players[i].connected))
1392                                         goto kick_player;
1393                         }
1394                         else HUD_init_message ("You cannot use # kicking with in team display.");
1395                                 
1396
1397                     multi_message_index = 0;
1398                     multi_sending_message = 0;
1399                          return;
1400                 }
1401
1402
1403                 for (i = 0; i < N_players; i++)
1404                 if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (i != Player_num) && (Players[i].connected)) {
1405 kick_player:;
1406                         if (Network_game_type == IPX_GAME)
1407                                         network_dump_player(NetPlayers.players[i].network.ipx.server,NetPlayers.players[i].network.ipx.node, 7);
1408                                 else
1409                                         network_dump_appletalk_player(NetPlayers.players[i].network.appletalk.node,NetPlayers.players[i].network.appletalk.net, NetPlayers.players[i].network.appletalk.socket, 7);
1410                                 
1411                                 HUD_init_message("Dumping %s...",Players[i].callsign);
1412                                 multi_message_index = 0;
1413                                 multi_sending_message = 0;
1414                                 return;
1415                         }
1416          }                                                                                               
1417
1418    else
1419                 HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
1420
1421         multi_send_message();
1422         multi_message_feedback();               
1423
1424         multi_message_index = 0;
1425         multi_sending_message = 0;
1426 }
1427
1428 void multi_define_macro_end()
1429 {
1430         Assert( multi_defining_message > 0 );
1431
1432         strcpy( Network_message_macro[multi_defining_message-1], Network_message );
1433         write_player_file();
1434
1435         multi_message_index = 0;
1436         multi_defining_message = 0;
1437 }
1438
1439 void multi_message_input_sub( int key )
1440 {
1441         switch( key )   {
1442         case KEY_F8:
1443         case KEY_ESC:
1444                 multi_sending_message = 0;
1445                 multi_defining_message = 0;
1446                 game_flush_inputs();
1447                 break;
1448         case KEY_LEFT:
1449         case KEY_BACKSP:
1450         case KEY_PAD4:
1451                 if (multi_message_index > 0)
1452                         multi_message_index--;
1453                 Network_message[multi_message_index] = 0;
1454                 break;
1455         case KEY_ENTER:
1456                 if ( multi_sending_message )    
1457                         multi_send_message_end();
1458                 else if ( multi_defining_message )
1459                         multi_define_macro_end();
1460                 game_flush_inputs();
1461                 break;
1462         default:
1463                 if ( key > 0 )  {
1464                         int ascii = key_to_ascii(key);
1465                         if ((ascii < 255 ))     {
1466                                 if (multi_message_index < MAX_MESSAGE_LEN-2 )   {
1467                                         Network_message[multi_message_index++] = ascii;
1468                                         Network_message[multi_message_index] = 0;
1469                                 } else if ( multi_sending_message )     {               
1470                                         int i;
1471                                         char * ptext, * pcolon;
1472                                         ptext = NULL;
1473                                         Network_message[multi_message_index++] = ascii;
1474                                         Network_message[multi_message_index] = 0;
1475                                         for (i=multi_message_index-1; i>=0; i-- )       {
1476                                                 if ( Network_message[i]==32 )   {
1477                                                         ptext = &Network_message[i+1];
1478                                                         Network_message[i] = 0;
1479                                                         break;
1480                                                 }
1481                                         }
1482                                         multi_send_message_end();
1483                                         if ( ptext )    {
1484                                                 multi_sending_message = 1;
1485                                                 pcolon = strchr( Network_message, ':' );
1486                                                 if ( pcolon )
1487                                                         strcpy( pcolon+1, ptext );
1488                                                 else
1489                                                         strcpy( Network_message, ptext );
1490                                                 multi_message_index = strlen( Network_message );
1491                                         } 
1492                                 }
1493                         }
1494                 }
1495         }
1496 }
1497
1498 void 
1499 multi_send_message_dialog(void)
1500 {
1501         newmenu_item m[1];
1502         int choice;
1503
1504         if (!(Game_mode&GM_MULTI))
1505                 return;
1506
1507         Network_message[0] = 0;             // Get rid of old contents
1508
1509         m[0].type=NM_TYPE_INPUT; m[0].text = Network_message; m[0].text_len = MAX_MESSAGE_LEN-1;
1510         choice = newmenu_do( NULL, TXT_SEND_MESSAGE, 1, m, NULL );
1511
1512         if ((choice > -1) && (strlen(Network_message) > 0)) {
1513                 Network_message_reciever = 100;
1514                 HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
1515                 multi_message_feedback();               
1516         }
1517 }
1518
1519
1520
1521 void
1522 multi_do_death(int objnum)
1523 {
1524         // Do any miscellaneous stuff for a new network player after death
1525
1526         objnum = objnum;
1527
1528         if (!(Game_mode & GM_MULTI_COOP)) 
1529         {
1530                 mprintf((0, "Setting all keys for player %d.\n", Player_num));
1531                 Players[Player_num].flags |= (PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_GOLD_KEY);
1532         }
1533 }
1534
1535 void
1536 multi_do_fire(char *buf)
1537 {
1538         ubyte weapon;
1539         char pnum;
1540         byte flags;
1541    //static dum=0;
1542         
1543         // Act out the actual shooting
1544         pnum = buf[1];
1545
1546 #ifndef MACINTOSH
1547         weapon = (int)buf[2];   
1548 #else
1549         weapon = buf[2];
1550 #endif
1551         flags = buf[4];
1552         Network_laser_track = INTEL_SHORT(*(short *)(buf+6));
1553         
1554         Assert (pnum < N_players);
1555
1556         if (Objects[Players[(int)pnum].objnum].type == OBJ_GHOST)
1557                 multi_make_ghost_player(pnum);
1558
1559    //  mprintf((0,"multi_do_fire, weapon = %d\n",weapon));
1560
1561         if (weapon == FLARE_ADJUST)
1562                 Laser_player_fire( Objects+Players[(int)pnum].objnum, FLARE_ID, 6, 1, 0);
1563         else if (weapon >= MISSILE_ADJUST) {
1564                 int weapon_id,weapon_gun;
1565
1566                 weapon_id = Secondary_weapon_to_weapon_info[weapon-MISSILE_ADJUST];
1567                 weapon_gun = Secondary_weapon_to_gun_num[weapon-MISSILE_ADJUST] + (flags & 1);
1568                 mprintf((0,"missile id = %d, gun = %d\n",weapon_id,weapon_gun));
1569
1570                 if (weapon-MISSILE_ADJUST==GUIDED_INDEX)
1571                  {
1572                   mprintf ((0,"Missile is guided!!!\n"));
1573                   Multi_is_guided=1;
1574                  }
1575
1576                 Laser_player_fire( Objects+Players[(int)pnum].objnum, weapon_id, weapon_gun, 1, 0 );
1577         }
1578         else {
1579                 fix save_charge = Fusion_charge;
1580
1581                 if (weapon == FUSION_INDEX) {
1582                         Fusion_charge = flags << 12;
1583                         mprintf((0, "Fusion charge X%f.\n", f2fl(Fusion_charge)));
1584                 }
1585                 if (weapon == LASER_ID) {
1586                         if (flags & LASER_QUAD)
1587                                 Players[(int)pnum].flags |= PLAYER_FLAGS_QUAD_LASERS;
1588                         else
1589                                 Players[(int)pnum].flags &= ~PLAYER_FLAGS_QUAD_LASERS;
1590                 }
1591         
1592                 do_laser_firing(Players[(int)pnum].objnum, weapon, (int)buf[3], flags, (int)buf[5]);
1593         
1594                 if (weapon == FUSION_INDEX)
1595                         Fusion_charge = save_charge;
1596         }
1597 }
1598
1599 void 
1600 multi_do_message(char *buf)
1601 {
1602         char *colon;
1603    char *tilde,mesbuf[100];
1604    int tloc,t;
1605
1606         int loc = 2;
1607         
1608    if ((tilde=strchr (buf+loc,'$')))  // do that stupid name stuff
1609          {                                                                                      // why'd I put this in?  Probably for the
1610           tloc=tilde-(buf+loc);                         // same reason you can name your guidebot
1611           mprintf ((0,"Tloc=%d\n",tloc));
1612    
1613           if (tloc>0)
1614           strncpy (mesbuf,buf+loc,tloc);
1615           strcpy (mesbuf+tloc,Players[Player_num].callsign);
1616      strcpy (mesbuf+strlen(Players[Player_num].callsign)+tloc,buf+loc+tloc+1);
1617      strcpy (buf+loc,mesbuf);
1618          }      
1619  
1620         if (((colon = strrchr(buf+loc, ':')) == NULL) || (colon-(buf+loc) < 1) || (colon-(buf+loc) > CALLSIGN_LEN))
1621         {
1622            mesbuf[0] = 1;
1623                 mesbuf[1] = BM_XRGB(28, 0, 0);
1624                 strcpy(&mesbuf[2], Players[(int)buf[1]].callsign);
1625                 t = strlen(mesbuf);
1626                 mesbuf[t] = ':';
1627                 mesbuf[t+1] = 1;
1628                 mesbuf[t+2] = BM_XRGB(0, 31, 0);
1629                 mesbuf[t+3] = 0;
1630
1631                 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1632                 HUD_init_message("%s %s", mesbuf, buf+2);
1633         }
1634         else
1635         {
1636                 if ( (!strnicmp(Players[Player_num].callsign, buf+loc, colon-(buf+loc))) ||
1637                           ((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)))) )
1638                 {
1639                    mesbuf[0] = 1;
1640                         mesbuf[1] = BM_XRGB(0, 32, 32);
1641                         strcpy(&mesbuf[2], Players[(int)buf[1]].callsign);
1642                         t = strlen(mesbuf);
1643                         mesbuf[t] = ':';
1644                         mesbuf[t+1] = 1;
1645                         mesbuf[t+2] = BM_XRGB(0, 31, 0);
1646                         mesbuf[t+3] = 0;
1647
1648                         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);      
1649                 HUD_init_message("%s %s", mesbuf, colon+1);
1650                 }
1651         }
1652 }
1653
1654 void
1655 multi_do_position(char *buf)
1656 {               
1657 #ifdef MACINTOSH
1658         shortpos sp;
1659 #endif
1660
1661         // This routine does only player positions, mode game only
1662         //      mprintf((0, "Got position packet.\n"));
1663
1664         int pnum = (Player_num+1)%2;
1665
1666         Assert(&Objects[Players[pnum].objnum] != ConsoleObject);
1667
1668         if (Game_mode & GM_NETWORK)
1669          {
1670           Int3(); // Get Jason, what the hell are we doing here?
1671           return;
1672     }
1673          
1674
1675 #ifndef MACINTOSH
1676         extract_shortpos(&Objects[Players[pnum].objnum], (shortpos *)(buf+1),0);
1677 #else
1678         memcpy((ubyte *)(sp.bytemat), (ubyte *)(buf + 1), 9);
1679         memcpy((ubyte *)&(sp.xo), (ubyte *)(buf + 10), 14);
1680         extract_shortpos(&Objects[Players[pnum].objnum], &sp, 1);
1681 #endif
1682
1683         if (Objects[Players[pnum].objnum].movement_type == MT_PHYSICS)
1684                 set_thrust_from_velocity(&Objects[Players[pnum].objnum]);
1685 }
1686
1687 void
1688 multi_do_reappear(char *buf)
1689 {
1690         short objnum;
1691
1692         objnum = INTEL_SHORT(*(short *)(buf+1));
1693
1694         Assert(objnum >= 0);
1695 //      Assert(Players[Objects[objnum].id]].objnum == objnum);
1696         
1697 // mprintf((0, "Switching rendering back on for object %d.\n", objnum));
1698
1699         multi_make_ghost_player(Objects[objnum].id);
1700         create_player_appearance_effect(&Objects[objnum]);
1701         PKilledFlags[Objects[objnum].id]=0;
1702 }
1703         
1704 void
1705 multi_do_player_explode(char *buf)
1706 {
1707         // Only call this for players, not robots.  pnum is player number, not
1708         // Object number.
1709
1710         object *objp;
1711         int count;
1712         int pnum;
1713         int i;
1714         char remote_created;
1715
1716         pnum = buf[1];
1717
1718 #ifdef NDEBUG
1719         if ((pnum < 0) || (pnum >= N_players))
1720                 return;
1721 #else
1722         Assert(pnum >= 0);
1723         Assert(pnum < N_players);
1724 #endif
1725
1726 #ifdef NETWORK
1727         // If we are in the process of sending objects to a new player, reset that process
1728         if (Network_send_objects)
1729         {
1730                 mprintf((0, "Resetting object sync due to player explosion.\n"));
1731                 Network_send_objnum = -1;
1732         }
1733 #endif
1734
1735         // Stuff the Players structure to prepare for the explosion
1736         
1737         count = 2;
1738         Players[pnum].primary_weapon_flags = INTEL_SHORT(*(ushort *)(buf+count)); count += 2;
1739         Players[pnum].secondary_weapon_flags = INTEL_SHORT(*(ushort *)(buf+count)); count += 2;
1740         Players[pnum].laser_level = buf[count];                                                 count++;
1741         Players[pnum].secondary_ammo[HOMING_INDEX] = buf[count];                count++;
1742         Players[pnum].secondary_ammo[CONCUSSION_INDEX] = buf[count];count++;
1743         Players[pnum].secondary_ammo[SMART_INDEX] = buf[count];         count++;
1744         Players[pnum].secondary_ammo[MEGA_INDEX] = buf[count];          count++;
1745         Players[pnum].secondary_ammo[PROXIMITY_INDEX] = buf[count]; count++;
1746
1747         Players[pnum].secondary_ammo[SMISSILE1_INDEX] = buf[count]; count++;
1748         Players[pnum].secondary_ammo[GUIDED_INDEX]    = buf[count]; count++;
1749         Players[pnum].secondary_ammo[SMART_MINE_INDEX]= buf[count]; count++;
1750         Players[pnum].secondary_ammo[SMISSILE4_INDEX] = buf[count]; count++;
1751         Players[pnum].secondary_ammo[SMISSILE5_INDEX] = buf[count]; count++;
1752
1753         Players[pnum].primary_ammo[VULCAN_INDEX] = INTEL_SHORT(*(ushort *)(buf+count)); count += 2;
1754         Players[pnum].primary_ammo[GAUSS_INDEX] = INTEL_SHORT(*(ushort *)(buf+count)); count += 2;
1755         Players[pnum].flags = INTEL_INT(*(uint *)(buf+count));                     count += 4;
1756     
1757         multi_adjust_remote_cap (pnum);
1758
1759         objp = Objects+Players[pnum].objnum;
1760
1761 //      objp->phys_info.velocity = *(vms_vector *)(buf+16); // 12 bytes
1762 //      objp->pos = *(vms_vector *)(buf+28);                // 12 bytes
1763
1764         remote_created = buf[count++]; // How many did the other guy create?
1765
1766         Net_create_loc = 0;
1767
1768         drop_player_eggs(objp);
1769  
1770         // Create mapping from remote to local numbering system
1771
1772         mprintf((0, "I Created %d powerups, remote created %d.\n", Net_create_loc, remote_created));
1773
1774         // We now handle this situation gracefully, Int3 not required
1775         //      if (Net_create_loc != remote_created)
1776         //              Int3(); // Probably out of object array space, see Rob
1777
1778         for (i = 0; i < remote_created; i++)
1779         {
1780                 short s;
1781                 
1782                 s = INTEL_SHORT(*(short *)(buf+count));
1783                 
1784                 if ((i < Net_create_loc) && (s > 0))
1785                         map_objnum_local_to_remote((short)Net_create_objnums[i], s, pnum);          
1786                 else if (*(short *)(buf+count) <= 0)
1787                 {
1788                         mprintf((0, "WARNING: Remote created object has non-valid number %d (player %d)", s, pnum));
1789                 }
1790                 else 
1791                 {
1792                         mprintf((0, "WARNING: Could not create all powerups created by player %d.\n", pnum));
1793                 }
1794 //              Assert(s > 0);
1795                 count += 2;
1796         }
1797         for (i = remote_created; i < Net_create_loc; i++) {
1798                 mprintf((0, "WARNING: I Created more powerups than player %d, deleting.\n", pnum));
1799                 Objects[Net_create_objnums[i]].flags |= OF_SHOULD_BE_DEAD;
1800         }
1801
1802         if (buf[0] == MULTI_PLAYER_EXPLODE)
1803         {
1804                 explode_badass_player(objp);
1805                 
1806                 objp->flags &= ~OF_SHOULD_BE_DEAD;              //don't really kill player
1807                 multi_make_player_ghost(pnum);
1808         }
1809         else
1810         {
1811                 create_player_appearance_effect(objp);
1812         }
1813
1814         Players[pnum].flags &= ~(PLAYER_FLAGS_CLOAKED | PLAYER_FLAGS_INVULNERABLE | PLAYER_FLAGS_FLAG);
1815         Players[pnum].cloak_time = 0;
1816 }
1817
1818 void
1819 multi_do_kill(char *buf)
1820 {
1821         int killer, killed;
1822         int count = 1;
1823         int pnum;
1824         
1825         pnum = (int)(buf[count]);
1826         if ((pnum < 0) || (pnum >= N_players))
1827         {
1828                 Int3(); // Invalid player number killed
1829                 return;
1830         }
1831         killed = Players[pnum].objnum;                  
1832         count += 1;
1833
1834         killer = INTEL_SHORT(*(short *)(buf+count)); 
1835         if (killer > 0)
1836                 killer = objnum_remote_to_local(killer, (byte)buf[count+2]);
1837
1838 #ifdef SHAREWARE
1839         if ((Objects[killed].type != OBJ_PLAYER) && (Objects[killed].type != OBJ_GHOST))
1840         {
1841                 Int3();
1842                 mprintf( (1, "SOFT INT3: MULTI.C Non-player object %d of type %d killed! (JOHN)\n", killed, Objects[killed].type ));
1843                 return;
1844         }
1845 #endif          
1846
1847         multi_compute_kill(killer, killed);
1848
1849 }
1850
1851
1852 //      Changed by MK on 10/20/94 to send NULL as object to net_destroy_controlcen if it got -1
1853 // which means not a controlcen object, but contained in another object
1854 void multi_do_controlcen_destroy(char *buf)
1855 {
1856         byte who;
1857         short objnum;
1858
1859         objnum = INTEL_SHORT(*(short *)(buf+1));
1860         who = buf[3];
1861
1862         if (Control_center_destroyed != 1) 
1863         {
1864                 if ((who < N_players) && (who != Player_num)) {
1865                         HUD_init_message("%s %s", Players[who].callsign, TXT_HAS_DEST_CONTROL);
1866                 }
1867                 else if (who == Player_num)
1868                         HUD_init_message(TXT_YOU_DEST_CONTROL);
1869                 else 
1870                         HUD_init_message(TXT_CONTROL_DESTROYED);
1871
1872                 if (objnum != -1)
1873                         net_destroy_controlcen(Objects+objnum);
1874                 else
1875                         net_destroy_controlcen(NULL);
1876         }
1877 }
1878
1879 void 
1880 multi_do_escape(char *buf)
1881 {
1882         int objnum;
1883
1884         objnum = Players[(int)buf[1]].objnum;
1885
1886         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1887         digi_kill_sound_linked_to_object (objnum);
1888         
1889         if (buf[2] == 0)
1890         {
1891                 HUD_init_message("%s %s", Players[(int)buf[1]].callsign, TXT_HAS_ESCAPED);
1892                 if (Game_mode & GM_NETWORK)
1893                         Players[(int)buf[1]].connected = CONNECT_ESCAPE_TUNNEL;
1894                 if (!multi_goto_secret)
1895                         multi_goto_secret = 2;
1896         }
1897         else if (buf[2] == 1) 
1898         {
1899                 HUD_init_message("%s %s", Players[(int)buf[1]].callsign, TXT_HAS_FOUND_SECRET);
1900                 if (Game_mode & GM_NETWORK)
1901                         Players[(int)buf[1]].connected = CONNECT_FOUND_SECRET;
1902                 if (!multi_goto_secret)
1903                         multi_goto_secret = 1;
1904         }
1905         create_player_appearance_effect(&Objects[objnum]);
1906         multi_make_player_ghost(buf[1]);
1907 }
1908
1909 void
1910 multi_do_remobj(char *buf)
1911 {
1912         short objnum; // which object to remove
1913         short local_objnum;
1914         byte obj_owner; // which remote list is it entered in
1915
1916         objnum = INTEL_SHORT(*(short *)(buf+1));
1917         obj_owner = buf[3];
1918
1919         Assert(objnum >= 0);
1920
1921         if (objnum < 1)
1922                 return;
1923
1924         local_objnum = objnum_remote_to_local(objnum, obj_owner); // translate to local objnum
1925
1926 //      mprintf((0, "multi_do_remobj: %d owner %d = %d.\n", objnum, obj_owner, local_objnum));
1927
1928         if (local_objnum < 0)
1929         {
1930                 mprintf((1, "multi_do_remobj: Could not remove referenced object.\n"));
1931                 return;
1932         }
1933
1934         if ((Objects[local_objnum].type != OBJ_POWERUP) && (Objects[local_objnum].type != OBJ_HOSTAGE))
1935         {
1936                 mprintf((1, "multi_get_remobj: tried to remove invalid type %d.\n", Objects[local_objnum].type));
1937                 return;
1938         }
1939         
1940         if (Network_send_objects && network_objnum_is_past(local_objnum))
1941         {
1942                 mprintf((0, "Resetting object sync due to object removal.\n"));
1943                 Network_send_objnum = -1;
1944         }
1945         if (Objects[local_objnum].type==OBJ_POWERUP)
1946          if (Game_mode & GM_NETWORK)    
1947                 {
1948                  if (PowerupsInMine[Objects[local_objnum].id]>0)
1949                         PowerupsInMine[Objects[local_objnum].id]--;
1950
1951                   if (multi_powerup_is_4pack (Objects[local_objnum].id))
1952                          {
1953                                 mprintf ((0,"Hey babe! Doing that wacky 4 pack stuff."));
1954                 
1955                                 if (PowerupsInMine[Objects[local_objnum].id-1]-4<0)
1956                                         PowerupsInMine[Objects[local_objnum].id-1]=0;
1957                                 else
1958                                         PowerupsInMine[Objects[local_objnum].id-1]-=4;
1959                          }
1960                 
1961                  mprintf ((0,"Decrementing powerups! %d\n",PowerupsInMine[Objects[local_objnum].id]));
1962                 }
1963
1964         Objects[local_objnum].flags |= OF_SHOULD_BE_DEAD; // quick and painless
1965         
1966 }
1967
1968 void
1969 multi_do_quit(char *buf)
1970 {
1971         
1972         if (Game_mode & GM_NETWORK)
1973         {
1974                 int i, n = 0;
1975
1976                 digi_play_sample( SOUND_HUD_MESSAGE, F1_0 );
1977
1978                 HUD_init_message( "%s %s", Players[(int)buf[1]].callsign, TXT_HAS_LEFT_THE_GAME);
1979                 
1980                 network_disconnect_player(buf[1]);
1981
1982                 if (multi_in_menu)
1983                         return;
1984
1985                 for (i = 0; i < N_players; i++)
1986                         if (Players[i].connected) n++;
1987                 if (n == 1)
1988                 {
1989                         nm_messagebox(NULL, 1, TXT_OK, TXT_YOU_ARE_ONLY);
1990                 }
1991         }
1992
1993         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
1994         {
1995                 Function_mode = FMODE_MENU;
1996                 multi_quit_game = 1;
1997                 multi_leave_menu = 1;
1998                 nm_messagebox(NULL, 1, TXT_OK, TXT_OPPONENT_LEFT);
1999                 Function_mode = FMODE_GAME;
2000                 multi_reset_stuff();
2001         }
2002         return;
2003 }
2004
2005 void
2006 multi_do_cloak(char *buf)
2007 {
2008         int pnum;
2009
2010         pnum = (int)(buf[1]);
2011
2012         Assert(pnum < N_players);
2013         
2014         mprintf((0, "Cloaking player %d\n", pnum));
2015
2016         Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2017         Players[pnum].cloak_time = GameTime;
2018         ai_do_cloak_stuff();
2019
2020 #ifndef SHAREWARE
2021         if (Game_mode & GM_MULTI_ROBOTS)
2022                 multi_strip_robots(pnum);
2023 #endif
2024
2025         if (Newdemo_state == ND_STATE_RECORDING)
2026                 newdemo_record_multi_cloak(pnum);
2027 }
2028         
2029 void
2030 multi_do_decloak(char *buf)
2031 {
2032         int pnum;
2033
2034         pnum = (int)(buf[1]);
2035
2036         if (Newdemo_state == ND_STATE_RECORDING)
2037                 newdemo_record_multi_decloak(pnum);
2038
2039 }
2040         
2041 void
2042 multi_do_door_open(char *buf)
2043 {
2044         int segnum;
2045         byte side;
2046         segment *seg;
2047         wall *w;
2048         ubyte flag;
2049
2050         segnum = INTEL_SHORT(*(short *)(buf+1));
2051         side = buf[3];
2052         flag= buf[4];
2053
2054 //      mprintf((0, "Opening door on side %d of segment # %d.\n", side, segnum));
2055
2056         if ((segnum < 0) || (segnum > Highest_segment_index) || (side < 0) || (side > 5))
2057         {
2058                 Int3();
2059                 return;
2060         }
2061
2062         seg = &Segments[segnum];
2063
2064         if (seg->sides[side].wall_num == -1) {  //Opening door on illegal wall
2065                 Int3();
2066                 return;
2067         }
2068
2069         w = &Walls[seg->sides[side].wall_num];
2070
2071         if (w->type == WALL_BLASTABLE)
2072         {
2073                 if (!(w->flags & WALL_BLASTED))
2074                 {
2075                         mprintf((0, "Blasting wall by remote command.\n"));
2076                         wall_destroy(seg, side);
2077                 }
2078                 return;
2079         }
2080         else if (w->state != WALL_DOOR_OPENING)
2081         {
2082                 wall_open_door(seg, side);
2083       w->flags=flag;
2084         }
2085         else
2086     w->flags=flag;
2087
2088 //      else
2089 //              mprintf((0, "Door already opening!\n"));
2090 }
2091
2092 void
2093 multi_do_create_explosion(char *buf)
2094 {
2095         int pnum;
2096         int count = 1;
2097
2098         pnum = buf[count++];
2099
2100 //      mprintf((0, "Creating small fireball.\n"));
2101         create_small_fireball_on_object(&Objects[Players[pnum].objnum], F1_0, 1);
2102 }
2103         
2104 void
2105 multi_do_controlcen_fire(char *buf)
2106 {
2107         vms_vector to_target;
2108         char gun_num;
2109         short objnum;
2110         int count = 1;
2111
2112         memcpy(&to_target, buf+count, 12);      count += 12;
2113         #ifdef MACINTOSH        // swap the vector to_target
2114         to_target.x = (fix)INTEL_INT((int)to_target.x);
2115         to_target.y = (fix)INTEL_INT((int)to_target.y);
2116         to_target.z = (fix)INTEL_INT((int)to_target.z);
2117         #endif
2118         gun_num = buf[count];                                   count += 1;
2119         objnum = INTEL_SHORT(*(short *)(buf+count));         count += 2;
2120
2121         Laser_create_new_easy(&to_target, &Gun_pos[(int)gun_num], objnum, CONTROLCEN_WEAPON_NUM, 1);
2122 }
2123
2124 void
2125 multi_do_create_powerup(char *buf)
2126 {
2127         short segnum;
2128         short objnum;
2129         int my_objnum;
2130         char pnum;
2131         int count = 1;
2132         vms_vector new_pos;
2133         char powerup_type;
2134
2135         if (Endlevel_sequence || Control_center_destroyed)
2136                 return;
2137
2138         pnum = buf[count++];
2139         powerup_type = buf[count++];
2140         segnum = INTEL_SHORT(*(short *)(buf+count)); count+=2;
2141         objnum = INTEL_SHORT(*(short *)(buf+count)); count+=2;
2142
2143         if ((segnum < 0) || (segnum > Highest_segment_index)) {
2144                 Int3();
2145                 return;
2146         }
2147         
2148         new_pos = *(vms_vector *)(buf+count); count+=sizeof(vms_vector);
2149 #ifdef MACINTOSH
2150         new_pos.x = (fix)SWAPINT((int)new_pos.x);
2151         new_pos.y = (fix)SWAPINT((int)new_pos.y);
2152         new_pos.z = (fix)SWAPINT((int)new_pos.z);
2153 #endif
2154
2155         Net_create_loc = 0;
2156         my_objnum = call_object_create_egg(&Objects[Players[(int)pnum].objnum], 1, OBJ_POWERUP, powerup_type);
2157
2158         if (my_objnum < 0) {
2159                 mprintf((0, "Could not create new powerup!\n"));
2160                 return;
2161         }
2162
2163         if (Network_send_objects && network_objnum_is_past(my_objnum))
2164         {
2165                 mprintf((0, "Resetting object sync due to powerup creation.\n"));
2166                 Network_send_objnum = -1;
2167         }
2168
2169         Objects[my_objnum].pos = new_pos;
2170
2171         vm_vec_zero(&Objects[my_objnum].mtype.phys_info.velocity);
2172         
2173         obj_relink(my_objnum, segnum);
2174
2175         map_objnum_local_to_remote(my_objnum, objnum, pnum);
2176
2177         object_create_explosion(segnum, &new_pos, i2f(5), VCLIP_POWERUP_DISAPPEARANCE);
2178         mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));
2179
2180    if (Game_mode & GM_NETWORK)
2181                 PowerupsInMine[(int)powerup_type]++;
2182 }               
2183
2184 void
2185 multi_do_play_sound(char *buf)
2186 {
2187         int pnum = (int)(buf[1]);
2188         int sound_num = (int)(buf[2]);
2189         fix volume = (int)(buf[3]) << 12;
2190
2191         if (!Players[pnum].connected)
2192                 return;
2193
2194         Assert(Players[pnum].objnum >= 0);
2195         Assert(Players[pnum].objnum <= Highest_object_index);
2196
2197         digi_link_sound_to_object( sound_num, Players[pnum].objnum, 0, volume); 
2198 }
2199
2200 void
2201 multi_do_score(char *buf)
2202 {
2203         int pnum = (int)(buf[1]);
2204         
2205         if ((pnum < 0) || (pnum >= N_players))
2206         {
2207                 Int3(); // Non-terminal, see rob
2208                 return;
2209         }
2210
2211         if (Newdemo_state == ND_STATE_RECORDING)
2212                 newdemo_record_multi_score(pnum, INTEL_INT(*(int *)(buf+2)) );
2213
2214         Players[pnum].score = INTEL_INT(*(int *)(buf+2));
2215
2216         multi_sort_kill_list();
2217 }
2218
2219 void
2220 multi_do_trigger(char *buf)
2221 {
2222         int pnum = (int)(buf[1]);
2223         int trigger = (int)(buf[2]);
2224    
2225    mprintf ((0,"MULTI doing trigger!\n"));
2226
2227         if ((pnum < 0) || (pnum >= N_players) || (pnum == Player_num))
2228         {
2229                 Int3(); // Got trigger from illegal playernum
2230                 return;
2231         }
2232         if ((trigger < 0) || (trigger >= Num_triggers))
2233         {
2234                 Int3(); // Illegal trigger number in multiplayer
2235                 return;
2236         }
2237         check_trigger_sub(trigger, pnum,0);
2238 }
2239
2240 void multi_do_drop_marker (char *buf)
2241  {
2242   int i;
2243   int pnum=(int)(buf[1]);
2244   int mesnum=(int)(buf[2]);
2245   vms_vector position;
2246
2247   if (pnum==Player_num)  // my marker? don't set it down cuz it might screw up the orientation
2248    return;
2249
2250   position.x=(fix)INTEL_INT(*(int *)(buf+3));
2251   position.y=(fix)INTEL_INT(*(int *)(buf+7));
2252   position.z=(fix)INTEL_INT(*(int *)(buf+11));
2253
2254   for (i=0;i<40;i++)
2255    MarkerMessage[(pnum*2)+mesnum][i]=buf[15+i];
2256
2257   MarkerPoint[(pnum*2)+mesnum]=position;
2258
2259   if (MarkerObject[(pnum*2)+mesnum] !=-1 && Objects[MarkerObject[(pnum*2)+mesnum]].type!=OBJ_NONE && MarkerObject[(pnum*2)+mesnum] !=0)
2260                 obj_delete(MarkerObject[(pnum*2)+mesnum]);
2261
2262   MarkerObject[(pnum*2)+mesnum] = drop_marker_object(&position,Objects[Players[Player_num].objnum].segnum,&Objects[Players[Player_num].objnum].orient,(pnum*2)+mesnum);
2263   strcpy (MarkerOwner[(pnum*2)+mesnum],Players[pnum].callsign);
2264   mprintf ((0,"Dropped player %d message: %s\n",pnum,MarkerMessage[(pnum*2)+mesnum]));
2265  }
2266
2267
2268 void multi_do_hostage_door_status(char *buf)
2269 {
2270         // Update hit point status of a door
2271
2272         int count = 1;
2273         int wallnum; 
2274         fix hps;
2275
2276         wallnum = INTEL_SHORT(*(short *)(buf+count));                count += 2;
2277         hps = (fix)INTEL_INT(*(int *)(buf+count));              count += 4;
2278
2279         if ((wallnum < 0) || (wallnum > Num_walls) || (hps < 0) || (Walls[wallnum].type != WALL_BLASTABLE))
2280         {
2281                 Int3(); // Non-terminal, see Rob
2282                 return;
2283         }
2284
2285 //      mprintf((0, "Damaging wall number %d to %f points.\n", wallnum, f2fl(hps)));
2286
2287         if (hps < Walls[wallnum].hps)
2288                 wall_damage(&Segments[Walls[wallnum].segnum], Walls[wallnum].sidenum, Walls[wallnum].hps - hps);
2289 }
2290
2291 void multi_do_save_game(char *buf)
2292 {
2293         int count = 1;
2294         ubyte slot;
2295         uint id;
2296         char desc[25];
2297
2298         slot = *(ubyte *)(buf+count);           count += 1;
2299         id = INTEL_INT(*(uint *)(buf+count));              count += 4;
2300         memcpy( desc, &buf[count], 20 );        count += 20;
2301
2302         multi_save_game( slot, id, desc );
2303 }
2304
2305 void multi_do_restore_game(char *buf)
2306 {
2307         int count = 1;
2308         ubyte slot;
2309         uint id;
2310
2311         slot = *(ubyte *)(buf+count);           count += 1;
2312         id = INTEL_INT(*(uint *)(buf+count));              count += 4;
2313
2314         multi_restore_game( slot, id );
2315 }
2316
2317  
2318 void multi_do_req_player(char *buf)
2319 {
2320         netplayer_stats ps;
2321         ubyte player_n;
2322         // Send my netplayer_stats to everyone!
2323         player_n = *(ubyte *)(buf+1);
2324         if ( (player_n == Player_num) || (player_n == 255)  )   {
2325                 extract_netplayer_stats( &ps, &Players[Player_num] );
2326                 ps.Player_num = Player_num;
2327                 ps.message_type = MULTI_SEND_PLAYER;            // SET
2328                 multi_send_data((ubyte*)&ps, sizeof(netplayer_stats), 0);
2329         }
2330 }
2331
2332 void multi_do_send_player(char *buf)
2333 {
2334         // Got a player packet from someone!!!
2335         netplayer_stats * p;
2336         p = (netplayer_stats *)buf;     
2337
2338         Assert( p->Player_num <= N_players );
2339
2340         mprintf(( 0, "Got netplayer_stats for player %d (I'm %d)\n", p->Player_num, Player_num ));
2341         mprintf(( 0, "Their shields are: %d\n", f2i(p->shields) ));
2342
2343         use_netplayer_stats( &Players[p->Player_num], p );
2344 }
2345
2346 void
2347 multi_reset_stuff(void)
2348 {
2349         // A generic, emergency function to solve problems that crop up
2350         // when a player exits quick-out from the game because of a 
2351         // serial connection loss.  Fixes several weird bugs!
2352
2353         dead_player_end();
2354
2355         Players[Player_num].homing_object_dist = -F1_0; // Turn off homing sound.
2356
2357         Dead_player_camera = 0;
2358         Endlevel_sequence = 0;
2359         reset_rear_view();
2360 }
2361
2362 void
2363 multi_reset_player_object(object *objp)
2364 {
2365         int i;
2366
2367         //Init physics for a non-console player
2368
2369         Assert(objp >= Objects);
2370         Assert(objp <= Objects+Highest_object_index);
2371         Assert((objp->type == OBJ_PLAYER) || (objp->type == OBJ_GHOST));
2372
2373         vm_vec_zero(&objp->mtype.phys_info.velocity);
2374         vm_vec_zero(&objp->mtype.phys_info.thrust);
2375         vm_vec_zero(&objp->mtype.phys_info.rotvel);
2376         vm_vec_zero(&objp->mtype.phys_info.rotthrust);
2377         objp->mtype.phys_info.brakes = objp->mtype.phys_info.turnroll = 0;
2378         objp->mtype.phys_info.mass = Player_ship->mass;
2379         objp->mtype.phys_info.drag = Player_ship->drag;
2380 //      objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE | PF_USES_THRUST);
2381         objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE);
2382
2383         //Init render info
2384
2385         objp->render_type = RT_POLYOBJ;
2386         objp->rtype.pobj_info.model_num = Player_ship->model_num;               //what model is this?
2387         objp->rtype.pobj_info.subobj_flags = 0;         //zero the flags
2388         for (i=0;i<MAX_SUBMODELS;i++)
2389                 vm_angvec_zero(&objp->rtype.pobj_info.anim_angles[i]);
2390
2391         //reset textures for this, if not player 0
2392
2393         multi_reset_object_texture (objp);      
2394         
2395         // Clear misc
2396
2397         objp->flags = 0;
2398         
2399         if (objp->type == OBJ_GHOST)
2400                 objp->render_type = RT_NONE;
2401
2402 }
2403
2404 void multi_reset_object_texture (object *objp)
2405  {
2406         int id,i;
2407
2408         if (Game_mode & GM_TEAM)
2409                 id = get_team(objp->id);
2410         else
2411                 id = objp->id;
2412
2413         if (id == 0)
2414                 objp->rtype.pobj_info.alt_textures=0;
2415         else {
2416                 Assert(N_PLAYER_SHIP_TEXTURES == Polygon_models[objp->rtype.pobj_info.model_num].n_textures);
2417
2418                 for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
2419                         multi_player_textures[id-1][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[objp->rtype.pobj_info.model_num].first_texture+i]];
2420
2421                 multi_player_textures[id-1][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2]];
2422                 multi_player_textures[id-1][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2+1]];
2423
2424                 objp->rtype.pobj_info.alt_textures = id;
2425    }
2426  }
2427
2428
2429
2430
2431 extern int TTRecv[];
2432 extern FILE *RecieveLogFile;
2433
2434 void
2435 multi_process_bigdata(char *buf, int len)
2436 {
2437         // Takes a bunch of messages, check them for validity,
2438         // and pass them to multi_process_data. 
2439
2440         int type, sub_len, bytes_processed = 0;
2441
2442         while( bytes_processed < len )  {
2443                 type = buf[bytes_processed];
2444
2445                 if ( (type<0) || (type>MULTI_MAX_TYPE)) {
2446                         mprintf( (1, "multi_process_bigdata: Invalid packet type %d!\n", type ));
2447                         return;
2448                 }
2449                 sub_len = message_length[type];
2450
2451                 Assert(sub_len > 0);
2452
2453                 if ( (bytes_processed+sub_len) > len )  {
2454                         mprintf( (1, "multi_process_bigdata: packet type %d too short (%d>%d)!\n", type, (bytes_processed+sub_len), len ));
2455                         Int3();
2456                         return;
2457                 }
2458
2459                 multi_process_data(&buf[bytes_processed], sub_len);
2460                 bytes_processed += sub_len;
2461         }
2462 }
2463
2464 //
2465 // Part 2 : Functions that send communication messages to inform the other
2466 //          players of something we did.
2467 //
2468
2469 void
2470 multi_send_fire(void)
2471 {
2472         if (!Network_laser_fired)
2473                 return;
2474
2475         multibuf[0] = (char)MULTI_FIRE;
2476         multibuf[1] = (char)Player_num;
2477         multibuf[2] = (char)Network_laser_gun;
2478         multibuf[3] = (char)Network_laser_level;
2479         multibuf[4] = (char)Network_laser_flags;
2480         multibuf[5] = (char)Network_laser_fired;
2481        
2482         *(short *)(multibuf+6) = INTEL_SHORT(Network_laser_track);
2483
2484         multi_send_data(multibuf, 8, 0);
2485         
2486         Network_laser_fired = 0;
2487 }
2488
2489 void
2490 multi_send_destroy_controlcen(int objnum, int player)
2491 {
2492         if (player == Player_num)
2493                 HUD_init_message(TXT_YOU_DEST_CONTROL);
2494         else if ((player > 0) && (player < N_players))
2495                 HUD_init_message("%s %s", Players[player].callsign, TXT_HAS_DEST_CONTROL);
2496         else
2497                 HUD_init_message(TXT_CONTROL_DESTROYED);
2498
2499         multibuf[0] = (char)MULTI_CONTROLCEN;
2500         *(ushort *)(multibuf+1) = INTEL_SHORT(objnum);
2501         multibuf[3] = player;
2502    multi_send_data(multibuf, 4, 2);
2503 }
2504
2505 void multi_send_drop_marker (int player,vms_vector position,char messagenum,char text[])
2506  {
2507   int i;
2508
2509    if (player<N_players)
2510      {
2511        mprintf ((0,"Sending MARKER drop!\n"));
2512        multibuf[0]=(char)MULTI_MARKER;
2513        multibuf[1]=(char)player;
2514        multibuf[2]=messagenum;
2515        *(fix *)(multibuf+3)=INTEL_INT(position.x);
2516        *(fix *)(multibuf+7)=INTEL_INT(position.y);
2517        *(fix *)(multibuf+11)=INTEL_INT(position.z);
2518        for (i=0;i<40;i++)
2519              multibuf[15+i]=text[i];
2520      }
2521    multi_send_data(multibuf, 55, 1);
2522  }
2523                            
2524 void 
2525 multi_send_endlevel_start(int secret)
2526 {
2527         multibuf[0] = (char)MULTI_ENDLEVEL_START;
2528         multibuf[1] = Player_num;
2529         multibuf[2] = (char)secret;
2530         
2531         if ((secret) && !multi_goto_secret)
2532                 multi_goto_secret = 1;
2533         else if (!multi_goto_secret)
2534                 multi_goto_secret = 2;
2535
2536         multi_send_data(multibuf, 3, 1);
2537         if (Game_mode & GM_NETWORK)
2538         {
2539                 Players[Player_num].connected = 5;
2540                 network_send_endlevel_packet();
2541         }
2542 }
2543
2544 void
2545 multi_send_player_explode(char type)
2546 {
2547         int count = 0;
2548         int i;
2549
2550         Assert( (type == MULTI_PLAYER_DROP) || (type == MULTI_PLAYER_EXPLODE) );
2551
2552         multi_send_position(Players[Player_num].objnum);
2553
2554         if (Network_send_objects)
2555         {
2556                 mprintf((0, "Resetting object sync due to player explosion.\n"));
2557                 Network_send_objnum = -1;
2558         }
2559         
2560         multibuf[count++] = type;
2561         multibuf[count++] = Player_num;
2562   
2563         *(ushort *)(multibuf+count) = INTEL_SHORT((ushort)Players[Player_num].primary_weapon_flags);
2564         count += 2;
2565         *(ushort *)(multibuf+count) = INTEL_SHORT((ushort)Players[Player_num].secondary_weapon_flags);
2566         count += 2;
2567         multibuf[count++] = (char)Players[Player_num].laser_level;
2568         
2569         multibuf[count++] = (char)Players[Player_num].secondary_ammo[HOMING_INDEX];
2570         multibuf[count++] = (char)Players[Player_num].secondary_ammo[CONCUSSION_INDEX];
2571         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_INDEX];
2572         multibuf[count++] = (char)Players[Player_num].secondary_ammo[MEGA_INDEX];
2573         multibuf[count++] = (char)Players[Player_num].secondary_ammo[PROXIMITY_INDEX];
2574
2575         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE1_INDEX];
2576         multibuf[count++] = (char)Players[Player_num].secondary_ammo[GUIDED_INDEX];
2577         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_MINE_INDEX];
2578         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE4_INDEX];
2579         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE5_INDEX];
2580
2581         *(ushort *)(multibuf+count) = INTEL_SHORT( (ushort)Players[Player_num].primary_ammo[VULCAN_INDEX] );
2582         count += 2;
2583         *(ushort *)(multibuf+count) = INTEL_SHORT( (ushort)Players[Player_num].primary_ammo[GAUSS_INDEX] );
2584         count += 2;
2585         *(uint *)(multibuf+count) = INTEL_INT( (uint)Players[Player_num].flags );
2586         count += 4;
2587         
2588         multibuf[count++] = Net_create_loc;
2589
2590         Assert(Net_create_loc <= MAX_NET_CREATE_OBJECTS);
2591
2592         memset(multibuf+count, -1, MAX_NET_CREATE_OBJECTS*sizeof(short));
2593         
2594         mprintf((0, "Created %d explosion objects.\n", Net_create_loc));
2595
2596         for (i = 0; i < Net_create_loc; i++)
2597         {
2598                 if (Net_create_objnums[i] <= 0) {
2599                         Int3(); // Illegal value in created egg object numbers
2600                         count +=2;
2601                         continue;
2602                 }
2603
2604                 *(short *)(multibuf+count) = INTEL_SHORT( (short)Net_create_objnums[i] ); count += 2;
2605
2606                 // We created these objs so our local number = the network number
2607                 map_objnum_local_to_local((short)Net_create_objnums[i]);
2608         }
2609
2610         Net_create_loc = 0;
2611
2612 //      mprintf((1, "explode message size = %d, max = %d.\n", count, message_length[MULTI_PLAYER_EXPLODE]));
2613
2614         if (count > message_length[MULTI_PLAYER_EXPLODE])
2615         {
2616                 Int3(); // See Rob
2617         }
2618
2619         multi_send_data(multibuf, message_length[MULTI_PLAYER_EXPLODE], 2);
2620         if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
2621                 multi_send_decloak();
2622         if (Game_mode & GM_MULTI_ROBOTS)
2623                 multi_strip_robots(Player_num);
2624 }
2625
2626 extern ubyte Secondary_weapon_to_powerup[];
2627 extern ubyte Primary_weapon_to_powerup[];
2628
2629 // put a lid on how many objects will be spewed by an exploding player
2630 // to prevent rampant powerups in netgames
2631
2632 void multi_cap_objects ()
2633  {
2634   char type,flagtype;
2635   int index;
2636
2637   if (!(Game_mode & GM_NETWORK))
2638         return;
2639
2640   for (index=0;index<MAX_PRIMARY_WEAPONS;index++)       
2641         {
2642                 type=Primary_weapon_to_powerup[index];
2643         if (PowerupsInMine[(int)type]>=MaxPowerupsAllowed[(int)type])
2644                  if(Players[Player_num].primary_weapon_flags & (1 << index))
2645                         {
2646                          mprintf ((0,"PIM=%d MPA=%d\n",PowerupsInMine[(int)type],MaxPowerupsAllowed[(int)type]));
2647                          mprintf ((0,"Killing a primary cuz there's too many! (%d)\n",(int)type));
2648                     Players[Player_num].primary_weapon_flags&=(~(1 << index));
2649                         }
2650         }
2651   
2652
2653   // Don't do the adjustment stuff for Hoard mode
2654   if (!(Game_mode & GM_HOARD))            
2655           Players[Player_num].secondary_ammo[2]/=4;
2656
2657   Players[Player_num].secondary_ammo[7]/=4;
2658         
2659   for (index=0;index<MAX_SECONDARY_WEAPONS;index++)     
2660         {
2661                 if ((Game_mode & GM_HOARD) && index==PROXIMITY_INDEX)
2662                         continue;
2663
2664                 type=Secondary_weapon_to_powerup[index];
2665                 
2666         if ((Players[Player_num].secondary_ammo[index]+PowerupsInMine[(int)type])>MaxPowerupsAllowed[(int)type])
2667                  {
2668         if (MaxPowerupsAllowed[(int)type]-PowerupsInMine[(int)type]<0)
2669                          Players[Player_num].secondary_ammo[index]=0;
2670                  else   
2671                  Players[Player_num].secondary_ammo[index]=(MaxPowerupsAllowed[(int)type]-PowerupsInMine[(int)type]);
2672                   
2673                         mprintf ((0,"Hey! I killed secondary type %d because PIM=%d MPA=%d\n",(int)type,PowerupsInMine[(int)type],MaxPowerupsAllowed[(int)type]));
2674                  }      
2675         }
2676
2677   if (!(Game_mode & GM_HOARD))            
2678           Players[Player_num].secondary_ammo[2]*=4;
2679   Players[Player_num].secondary_ammo[7]*=4;
2680     
2681                 if (Players[Player_num].laser_level > MAX_LASER_LEVEL)
2682                  if (PowerupsInMine[POW_SUPER_LASER]+1 > MaxPowerupsAllowed[POW_SUPER_LASER])
2683                         Players[Player_num].laser_level=0;
2684
2685                 if (Players[Player_num].flags & PLAYER_FLAGS_QUAD_LASERS)
2686                  if (PowerupsInMine[POW_QUAD_FIRE]+1 > MaxPowerupsAllowed[POW_QUAD_FIRE])
2687                         Players[Player_num].flags&=(~PLAYER_FLAGS_QUAD_LASERS);
2688
2689                 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
2690                  if (PowerupsInMine[POW_CLOAK]+1 > MaxPowerupsAllowed[POW_CLOAK])
2691                         Players[Player_num].flags&=(~PLAYER_FLAGS_CLOAKED);
2692
2693                 if (Players[Player_num].flags & PLAYER_FLAGS_MAP_ALL)
2694                  if (PowerupsInMine[POW_FULL_MAP]+1 > MaxPowerupsAllowed[POW_FULL_MAP])
2695                         Players[Player_num].flags&=(~PLAYER_FLAGS_MAP_ALL);
2696
2697                 if (Players[Player_num].flags & PLAYER_FLAGS_AFTERBURNER)
2698                  if (PowerupsInMine[POW_AFTERBURNER]+1 > MaxPowerupsAllowed[POW_AFTERBURNER])
2699                         Players[Player_num].flags&=(~PLAYER_FLAGS_AFTERBURNER);
2700
2701                 if (Players[Player_num].flags & PLAYER_FLAGS_AMMO_RACK)
2702                  if (PowerupsInMine[POW_AMMO_RACK]+1 > MaxPowerupsAllowed[POW_AMMO_RACK])
2703                         Players[Player_num].flags&=(~PLAYER_FLAGS_AMMO_RACK);
2704
2705                 if (Players[Player_num].flags & PLAYER_FLAGS_CONVERTER)
2706                  if (PowerupsInMine[POW_CONVERTER]+1 > MaxPowerupsAllowed[POW_CONVERTER])
2707                         Players[Player_num].flags&=(~PLAYER_FLAGS_CONVERTER);
2708
2709                 if (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT)
2710                  if (PowerupsInMine[POW_HEADLIGHT]+1 > MaxPowerupsAllowed[POW_HEADLIGHT])
2711                         Players[Player_num].flags&=(~PLAYER_FLAGS_HEADLIGHT);
2712                 
2713                 if (Game_mode & GM_CAPTURE)
2714                 {
2715                         if (Players[Player_num].flags & PLAYER_FLAGS_FLAG)
2716                          {
2717                           if (get_team(Player_num)==TEAM_RED)
2718                                 flagtype=POW_FLAG_BLUE;
2719                           else
2720                                 flagtype=POW_FLAG_RED;
2721                         
2722                           if (PowerupsInMine[(int)flagtype]+1 > MaxPowerupsAllowed[(int)flagtype])
2723                              Players[Player_num].flags&=(~PLAYER_FLAGS_FLAG);
2724                          }
2725                 }
2726
2727  }
2728
2729 // adds players inventory to multi cap
2730
2731 void multi_adjust_cap_for_player (int pnum)
2732  {
2733   char type;
2734   
2735   int index;
2736
2737   if (!(Game_mode & GM_NETWORK))
2738         return;
2739
2740   for (index=0;index<MAX_PRIMARY_WEAPONS;index++)       
2741         {
2742                 type=Primary_weapon_to_powerup[index];
2743              if (Players[pnum].primary_weapon_flags & (1 << index))
2744                     MaxPowerupsAllowed[(int)type]++;
2745         }
2746                         
2747   for (index=0;index<MAX_SECONDARY_WEAPONS;index++)     
2748         {
2749                 type=Secondary_weapon_to_powerup[index];
2750                 MaxPowerupsAllowed[(int)type]+=Players[pnum].secondary_ammo[index];
2751         }
2752
2753   if (Players[pnum].laser_level > MAX_LASER_LEVEL)
2754          MaxPowerupsAllowed[POW_SUPER_LASER]++;
2755
2756   if (Players[pnum].flags & PLAYER_FLAGS_QUAD_LASERS)
2757     MaxPowerupsAllowed[POW_QUAD_FIRE]++;
2758
2759   if (Players[pnum].flags & PLAYER_FLAGS_CLOAKED)
2760          MaxPowerupsAllowed[POW_CLOAK]++;
2761
2762   if (Players[pnum].flags & PLAYER_FLAGS_MAP_ALL)
2763          MaxPowerupsAllowed[POW_FULL_MAP]++;
2764
2765   if (Players[pnum].flags & PLAYER_FLAGS_AFTERBURNER)
2766         MaxPowerupsAllowed[POW_AFTERBURNER]++;
2767
2768   if (Players[pnum].flags & PLAYER_FLAGS_AMMO_RACK)
2769           MaxPowerupsAllowed[POW_AMMO_RACK]++;
2770
2771   if (Players[pnum].flags & PLAYER_FLAGS_CONVERTER)
2772          MaxPowerupsAllowed[POW_CONVERTER]++;
2773
2774   if (Players[pnum].flags & PLAYER_FLAGS_HEADLIGHT)
2775          MaxPowerupsAllowed[POW_HEADLIGHT]++;
2776  }
2777
2778 void multi_adjust_remote_cap (int pnum)
2779  {
2780   char type;
2781   
2782   int index;
2783
2784   if (!(Game_mode & GM_NETWORK))
2785         return;
2786
2787   for (index=0;index<MAX_PRIMARY_WEAPONS;index++)       
2788         {
2789                 type=Primary_weapon_to_powerup[index];
2790              if (Players[pnum].primary_weapon_flags & (1 << index))
2791                     PowerupsInMine[(int)type]++;
2792         }
2793                         
2794   for (index=0;index<MAX_SECONDARY_WEAPONS;index++)     
2795         {
2796                 type=Secondary_weapon_to_powerup[index];
2797
2798                 if ((Game_mode & GM_HOARD) && index==2)
2799                         continue;
2800    
2801       if (index==2 || index==7) // PROX or SMARTMINES? Those bastards...
2802                         PowerupsInMine[(int)type]+=(Players[pnum].secondary_ammo[index]/4);
2803                 else
2804                    PowerupsInMine[(int)type]+=Players[pnum].secondary_ammo[index];
2805  
2806         }
2807
2808   if (Players[pnum].laser_level > MAX_LASER_LEVEL)
2809          PowerupsInMine[POW_SUPER_LASER]++;
2810
2811   if (Players[pnum].flags & PLAYER_FLAGS_QUAD_LASERS)
2812     PowerupsInMine[POW_QUAD_FIRE]++;
2813
2814   if (Players[pnum].flags & PLAYER_FLAGS_CLOAKED)
2815          PowerupsInMine[POW_CLOAK]++;
2816
2817   if (Players[pnum].flags & PLAYER_FLAGS_MAP_ALL)
2818          PowerupsInMine[POW_FULL_MAP]++;
2819
2820   if (Players[pnum].flags & PLAYER_FLAGS_AFTERBURNER)
2821         PowerupsInMine[POW_AFTERBURNER]++;
2822
2823   if (Players[pnum].flags & PLAYER_FLAGS_AMMO_RACK)
2824           PowerupsInMine[POW_AMMO_RACK]++;
2825
2826   if (Players[pnum].flags & PLAYER_FLAGS_CONVERTER)
2827          PowerupsInMine[POW_CONVERTER]++;
2828
2829   if (Players[pnum].flags & PLAYER_FLAGS_HEADLIGHT)
2830          PowerupsInMine[POW_HEADLIGHT]++;
2831
2832  }
2833
2834 void
2835 multi_send_message(void)
2836 {
2837         int loc = 0;
2838         if (Network_message_reciever != -1)
2839         {
2840                 multibuf[loc] = (char)MULTI_MESSAGE;            loc += 1;
2841                 multibuf[loc] = (char)Player_num;                       loc += 1;
2842                 strncpy(multibuf+loc, Network_message, MAX_MESSAGE_LEN); loc += MAX_MESSAGE_LEN;
2843                 multibuf[loc-1] = '\0';
2844                 multi_send_data(multibuf, loc, 0);
2845                 Network_message_reciever = -1;
2846         }
2847 }
2848
2849 void
2850 multi_send_reappear()
2851 {
2852         multibuf[0] = (char)MULTI_REAPPEAR;
2853         *(short *)(multibuf+1) = INTEL_SHORT(Players[Player_num].objnum);
2854            
2855         multi_send_data(multibuf, 3, 2);
2856         PKilledFlags[Player_num]=0;
2857 }
2858
2859 void
2860 multi_send_position(int objnum)
2861 {
2862 #ifdef MACINTOSH
2863         shortpos sp;
2864 #endif
2865         int count=0;
2866
2867         if (Game_mode & GM_NETWORK) {
2868                 return;
2869         }
2870                      
2871         multibuf[count++] = (char)MULTI_POSITION;
2872 #ifndef MACINTOSH
2873         create_shortpos((shortpos *)(multibuf+count), Objects+objnum,0);
2874         count += sizeof(shortpos);
2875 #else
2876         create_shortpos(&sp, Objects+objnum, 1);
2877         memcpy(&(multibuf[count]), (ubyte *)(sp.bytemat), 9);
2878         count += 9;
2879         memcpy(&(multibuf[count]), (ubyte *)&(sp.xo), 14);
2880         count += 14;
2881 #endif
2882
2883         multi_send_data(multibuf, count, 0);
2884 }
2885
2886 void
2887 multi_send_kill(int objnum)
2888 {
2889         // I died, tell the world.
2890
2891         int killer_objnum;
2892         int count = 0;
2893
2894         Assert(Objects[objnum].id == Player_num);
2895         killer_objnum = Players[Player_num].killer_objnum;
2896
2897         multi_compute_kill(killer_objnum, objnum);
2898          
2899         multibuf[0] = (char)MULTI_KILL;     count += 1;
2900         multibuf[1] = Player_num;           count += 1;
2901    if (killer_objnum > -1) {
2902                 short s;                // do it with variable since INTEL_SHORT won't work on return val from function.
2903                 
2904                 s = (short)objnum_local_to_remote(killer_objnum, (byte *)&multibuf[count+2]);
2905                 *(short *)(multibuf+count) = INTEL_SHORT(s);
2906         } 
2907         else
2908         {
2909                 *(short *)(multibuf+count) = INTEL_SHORT((short)-1);
2910                 multibuf[count+2] = (char)-1;
2911         }
2912         count += 3;
2913         multi_send_data(multibuf, count, 1);
2914
2915 #ifndef SHAREWARE
2916         if (Game_mode & GM_MULTI_ROBOTS)
2917                 multi_strip_robots(Player_num);
2918 #endif
2919 }
2920
2921 void
2922 multi_send_remobj(int objnum)
2923 {
2924         // Tell the other guy to remove an object from his list
2925
2926         byte obj_owner;
2927         short remote_objnum;
2928
2929    if (Objects[objnum].type==OBJ_POWERUP && (Game_mode & GM_NETWORK))
2930     {
2931                 if (PowerupsInMine[Objects[objnum].id] > 0)
2932                  {
2933               PowerupsInMine[Objects[objnum].id]--;
2934                   if (multi_powerup_is_4pack (Objects[objnum].id))
2935                          {
2936                                 mprintf ((0,"Hey babe! Doing that wacky 4 pack stuff."));
2937                 
2938                                 if (PowerupsInMine[Objects[objnum].id-1]-4<0)
2939                                         PowerupsInMine[Objects[objnum].id-1]=0;
2940                                 else
2941                                         PowerupsInMine[Objects[objnum].id-1]-=4;
2942                          }
2943                  }
2944                         
2945          }
2946                         
2947         multibuf[0] = (char)MULTI_REMOVE_OBJECT;
2948
2949         remote_objnum = objnum_local_to_remote((short)objnum, &obj_owner);
2950
2951         *(short *)(multibuf+1) = INTEL_SHORT(remote_objnum); // Map to network objnums
2952
2953         multibuf[3] = obj_owner;        
2954
2955 //      mprintf((0, "multi_send_remobj: %d = %d owner %d.\n", objnum, remote_objnum, obj_owner));
2956
2957         multi_send_data(multibuf, 4, 0);
2958
2959         if (Network_send_objects && network_objnum_is_past(objnum))
2960         {
2961                 mprintf((0, "Resetting object sync due to object removal.\n"));
2962                 Network_send_objnum = -1;
2963         }
2964 }
2965         
2966 void
2967 multi_send_quit(int why)
2968 {
2969         // I am quitting the game, tell the other guy the bad news.
2970
2971         Assert (why == MULTI_QUIT);
2972
2973         multibuf[0] = (char)why;
2974         multibuf[1] = Player_num;
2975         multi_send_data(multibuf, 2, 1);
2976
2977 }
2978
2979 void
2980 multi_send_cloak(void)
2981 {
2982         // Broadcast a change in our pflags (made to support cloaking)
2983
2984         multibuf[0] = MULTI_CLOAK;
2985         multibuf[1] = (char)Player_num;
2986
2987         multi_send_data(multibuf, 2, 1);
2988
2989 #ifndef SHAREWARE
2990         if (Game_mode & GM_MULTI_ROBOTS)
2991                 multi_strip_robots(Player_num);
2992 #endif
2993 }
2994
2995 void
2996 multi_send_decloak(void)
2997 {
2998         // Broadcast a change in our pflags (made to support cloaking)
2999
3000         multibuf[0] = MULTI_DECLOAK;
3001         multibuf[1] = (char)Player_num;
3002
3003         multi_send_data(multibuf, 2, 1);
3004 }
3005
3006 void
3007 multi_send_door_open(int segnum, int side,ubyte flag)
3008 {
3009         // When we open a door make sure everyone else opens that door
3010
3011         multibuf[0] = MULTI_DOOR_OPEN;
3012         *(short *)(multibuf+1) = INTEL_SHORT( (short)segnum );
3013         multibuf[3] = (byte)side;
3014    multibuf[4] = flag;
3015    
3016         multi_send_data(multibuf, 5, 2);
3017 }
3018
3019 extern void network_send_naked_packet (char *,short,int);
3020
3021 void
3022 multi_send_door_open_specific(int pnum,int segnum, int side,ubyte flag)
3023 {
3024         // For sending doors only to a specific person (usually when they're joining)
3025
3026         Assert (Game_mode & GM_NETWORK);
3027 //   Assert (pnum>-1 && pnum<N_players);
3028  
3029         multibuf[0] = MULTI_DOOR_OPEN;
3030         *(short *)(multibuf+1) = INTEL_SHORT( (short)segnum );
3031         multibuf[3] = (byte)side;
3032    multibuf[4] = flag;
3033    
3034         network_send_naked_packet(multibuf, 5, pnum);
3035 }
3036
3037 //
3038 // Part 3 : Functions that change or prepare the game for multiplayer use.
3039 //          Not including functions needed to syncronize or start the 
3040 //          particular type of multiplayer game.  Includes preparing the
3041 //                      mines, player structures, etc.
3042
3043 void
3044 multi_send_create_explosion(int pnum)
3045 {
3046         // Send all data needed to create a remote explosion
3047
3048         int count = 0;
3049
3050         multibuf[count] = MULTI_CREATE_EXPLOSION;       count += 1;
3051         multibuf[count] = (byte)pnum;                                   count += 1;
3052         //                                                                                                      -----------
3053         //                                                                                                      Total size = 2
3054
3055         multi_send_data(multibuf, count, 0);
3056 }
3057         
3058 void
3059 multi_send_controlcen_fire(vms_vector *to_goal, int best_gun_num, int objnum)
3060 {
3061 #ifdef MACINTOSH
3062         vms_vector swapped_vec;
3063 #endif
3064         int count = 0;
3065
3066         multibuf[count] = MULTI_CONTROLCEN_FIRE;                count +=  1;
3067         #ifndef MACINTOSH
3068         memcpy(multibuf+count, to_goal, 12);                    count += 12;
3069         #else
3070         swapped_vec.x = (fix)INTEL_INT( (int)to_goal->x );
3071         swapped_vec.y = (fix)INTEL_INT( (int)to_goal->y );
3072         swapped_vec.z = (fix)INTEL_INT( (int)to_goal->z );
3073         memcpy(multibuf+count, &swapped_vec, 12);                               count += 12;
3074         #endif
3075         multibuf[count] = (char)best_gun_num;                   count +=  1;
3076         *(short *)(multibuf+count) = INTEL_SHORT( (short)objnum );     count +=  2;
3077         //                                                                                                                      ------------
3078         //                                                                                                                      Total  = 16
3079         multi_send_data(multibuf, count, 0);
3080 }
3081
3082 void
3083 multi_send_create_powerup(int powerup_type, int segnum, int objnum, vms_vector *pos)
3084 {
3085         // Create a powerup on a remote machine, used for remote
3086         // placement of used powerups like missiles and cloaking
3087         // powerups.
3088
3089 #ifdef MACINTOSH
3090         vms_vector swapped_vec;
3091 #endif
3092         int count = 0;
3093
3094    if (Game_mode & GM_NETWORK)
3095            PowerupsInMine[powerup_type]++;
3096
3097         multibuf[count] = MULTI_CREATE_POWERUP;         count += 1;
3098         multibuf[count] = Player_num;                                      count += 1;
3099         multibuf[count] = powerup_type;                                 count += 1;
3100         *(short *)(multibuf+count) = INTEL_SHORT( (short)segnum );     count += 2;
3101         *(short *)(multibuf+count) = INTEL_SHORT( (short)objnum );     count += 2;
3102 #ifndef MACINTOSH
3103         *(vms_vector *)(multibuf+count) = *pos;         count += sizeof(vms_vector);
3104 #else
3105         swapped_vec.x = (fix)INTEL_INT( (int)pos->x );
3106         swapped_vec.y = (fix)INTEL_INT( (int)pos->y );
3107         swapped_vec.z = (fix)INTEL_INT( (int)pos->z );
3108         memcpy(multibuf+count, &swapped_vec, 12);                               count += 12;
3109 #endif
3110         //                                                                                                            -----------
3111         //                                                                                                            Total =  19
3112         multi_send_data(multibuf, count, 2);
3113
3114         if (Network_send_objects && network_objnum_is_past(objnum))
3115         {
3116                 mprintf((0, "Resetting object sync due to powerup creation.\n"));
3117                 Network_send_objnum = -1;
3118         }
3119
3120         mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));
3121         map_objnum_local_to_local(objnum);
3122 }       
3123
3124 void
3125 multi_send_play_sound(int sound_num, fix volume)
3126 {
3127         int count = 0;
3128         multibuf[count] = MULTI_PLAY_SOUND;                     count += 1;
3129         multibuf[count] = Player_num;                                   count += 1;
3130         multibuf[count] = (char)sound_num;                      count += 1;
3131         multibuf[count] = (char)(volume >> 12); count += 1;
3132         //                                                                                                         -----------
3133         //                                                                                                         Total = 4
3134         multi_send_data(multibuf, count, 0);
3135 }
3136
3137 void
3138 multi_send_audio_taunt(int taunt_num)
3139 {
3140         return; // Taken out, awaiting sounds..
3141
3142 #if 0
3143         int audio_taunts[4] = {
3144                 SOUND_CONTROL_CENTER_WARNING_SIREN,
3145                 SOUND_HOSTAGE_RESCUED,
3146                 SOUND_REFUEL_STATION_GIVING_FUEL,
3147                 SOUND_BAD_SELECTION
3148         };
3149
3150
3151         Assert(taunt_num >= 0);
3152         Assert(taunt_num < 4);
3153
3154         digi_play_sample( audio_taunts[taunt_num], F1_0 );
3155         multi_send_play_sound(audio_taunts[taunt_num], F1_0);
3156 #endif
3157 }
3158
3159 void
3160 multi_send_score(void)
3161 {
3162         // Send my current score to all other players so it will remain
3163         // synced.
3164         int count = 0;
3165
3166         if (Game_mode & GM_MULTI_COOP) {
3167                 multi_sort_kill_list();
3168                 multibuf[count] = MULTI_SCORE;                  count += 1;
3169                 multibuf[count] = Player_num;                           count += 1;
3170                 *(int *)(multibuf+count) = INTEL_INT( Players[Player_num].score );  count += 4;
3171                 multi_send_data(multibuf, count, 0);
3172         }
3173 }       
3174
3175
3176 void
3177 multi_send_save_game(ubyte slot, uint id, char * desc)
3178 {
3179         int count = 0;
3180         
3181         multibuf[count] = MULTI_SAVE_GAME;              count += 1;
3182         multibuf[count] = slot;                         count += 1;    // Save slot=0
3183         *(uint *)(multibuf+count) = INTEL_INT( id );         count += 4;             // Save id
3184         memcpy( &multibuf[count], desc, 20 ); count += 20;
3185
3186         multi_send_data(multibuf, count, 2);
3187 }
3188
3189 void
3190 multi_send_restore_game(ubyte slot, uint id)
3191 {
3192         int count = 0;
3193         
3194         multibuf[count] = MULTI_RESTORE_GAME;   count += 1;
3195         multibuf[count] = slot;                                                 count += 1;             // Save slot=0
3196         *(uint *)(multibuf+count) = INTEL_INT( id );         count += 4;             // Save id
3197
3198         multi_send_data(multibuf, count, 2);
3199 }
3200
3201 void
3202 multi_send_netplayer_stats_request(ubyte player_num)
3203 {
3204         int count = 0;
3205         
3206         multibuf[count] = MULTI_REQ_PLAYER;     count += 1;
3207         multibuf[count] = player_num;                   count += 1;
3208
3209         multi_send_data(multibuf, count, 0 );
3210 }
3211
3212 void
3213 multi_send_trigger(int triggernum)
3214 {
3215         // Send an even to trigger something in the mine
3216         
3217         int count = 0;
3218         
3219         multibuf[count] = MULTI_TRIGGER;                                count += 1;
3220         multibuf[count] = Player_num;                                   count += 1;
3221         multibuf[count] = (ubyte)triggernum;            count += 1;
3222    
3223    mprintf ((0,"Sending trigger %d\n",triggernum));
3224
3225         multi_send_data(multibuf, count, 1); 
3226 //      multi_send_data(multibuf, count, 1); // twice? 
3227 }
3228
3229 void
3230 multi_send_hostage_door_status(int wallnum)
3231 {
3232         // Tell the other player what the hit point status of a hostage door
3233         // should be
3234
3235         int count = 0;
3236         
3237         Assert(Walls[wallnum].type == WALL_BLASTABLE);
3238
3239         multibuf[count] = MULTI_HOSTAGE_DOOR;           count += 1;
3240         *(short *)(multibuf+count) = INTEL_SHORT( (short)wallnum );           count += 2;
3241         *(fix *)(multibuf+count) = (fix)INTEL_INT( (int)Walls[wallnum].hps );  count += 4;
3242
3243 //      mprintf((0, "Door %d damaged by %f points.\n", wallnum, f2fl(Walls[wallnum].hps)));
3244
3245         multi_send_data(multibuf, count, 0);
3246 }
3247
3248 extern int ConsistencyCount;
3249 extern int Drop_afterburner_blob_flag;
3250 int PhallicLimit=0;
3251 int PhallicMan=-1;
3252
3253 void multi_prep_level(void)
3254 {
3255         // Do any special stuff to the level required for serial games
3256         // before we begin playing in it.
3257
3258         // Player_num MUST be set before calling this procedure.  
3259
3260         // This function must be called before checksuming the Object array,
3261         // since the resulting checksum with depend on the value of Player_num
3262         // at the time this is called.
3263
3264         int i,ng=0;
3265         int     cloak_count, inv_count;
3266
3267         Assert(Game_mode & GM_MULTI);
3268
3269         Assert(NumNetPlayerPositions > 0);
3270         
3271    PhallicLimit=0;
3272    PhallicMan=-1;
3273    Drop_afterburner_blob_flag=0;
3274    ConsistencyCount=0;
3275   
3276    for (i=0;i<MAX_NUM_NET_PLAYERS;i++)
3277          PKilledFlags[i]=0;
3278   
3279         for (i = 0; i < NumNetPlayerPositions; i++)
3280         {
3281                 if (i != Player_num)
3282                         Objects[Players[i].objnum].control_type = CT_REMOTE;
3283                 Objects[Players[i].objnum].movement_type = MT_PHYSICS;
3284                 multi_reset_player_object(&Objects[Players[i].objnum]);
3285                 LastPacketTime[i] = 0;
3286         }
3287
3288 #ifndef SHAREWARE
3289         for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
3290         {
3291                 robot_controlled[i] = -1;       
3292                 robot_agitation[i] = 0;
3293                 robot_fired[i] = 0;
3294         }
3295 #endif
3296
3297         Viewer = ConsoleObject = &Objects[Players[Player_num].objnum];
3298
3299         if (!(Game_mode & GM_MULTI_COOP))       
3300         {
3301                 multi_delete_extra_objects(); // Removes monsters from level
3302         }
3303
3304         if (Game_mode & GM_MULTI_ROBOTS)
3305         {
3306                 multi_set_robot_ai(); // Set all Robot AI to types we can cope with
3307         }
3308  
3309    if (Game_mode & GM_NETWORK)
3310          {
3311           multi_adjust_cap_for_player(Player_num);      
3312           multi_send_powerup_update();
3313           ng=1;  // ng means network game
3314          }
3315    ng=1;        
3316
3317         inv_count = 0;
3318         cloak_count = 0;
3319         for (i=0; i<=Highest_object_index; i++)
3320         {
3321                 int objnum;
3322
3323                 if ((Objects[i].type == OBJ_HOSTAGE) && !(Game_mode & GM_MULTI_COOP))
3324                 {
3325                         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);
3326                         obj_delete(i);                  
3327                         if (objnum != -1)
3328                         {
3329                                 Objects[objnum].rtype.vclip_info.vclip_num = Powerup_info[POW_SHIELD_BOOST].vclip_num;
3330                                 Objects[objnum].rtype.vclip_info.frametime = Vclip[Objects[objnum].rtype.vclip_info.vclip_num].frame_time;
3331                                 Objects[objnum].rtype.vclip_info.framenum = 0;
3332                                 Objects[objnum].mtype.phys_info.drag = 512;     //1024;
3333                                 Objects[objnum].mtype.phys_info.mass = F1_0;
3334                                 vm_vec_zero(&Objects[objnum].mtype.phys_info.velocity);
3335                         }
3336                         continue;
3337                 }
3338
3339                 if (Objects[i].type == OBJ_POWERUP)
3340                 {
3341                         if (Objects[i].id == POW_EXTRA_LIFE) 
3342                         {
3343                    if (ng && !Netgame.DoInvulnerability)
3344                       {
3345                                  Objects[i].id = POW_SHIELD_BOOST;
3346                                  Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3347                                  Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3348                       }
3349                    else
3350                       {
3351                                  Objects[i].id = POW_INVULNERABILITY;
3352                                  Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3353                                  Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3354                       } 
3355
3356               }
3357
3358                         if (!(Game_mode & GM_MULTI_COOP))
3359                                 if ((Objects[i].id >= POW_KEY_BLUE) && (Objects[i].id <= POW_KEY_GOLD))
3360                                         {
3361                                                 Objects[i].id = POW_SHIELD_BOOST;
3362                                                 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3363                                                 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3364                                         }
3365
3366                         if (Objects[i].id == POW_INVULNERABILITY) {
3367                            if (inv_count >= 3 || (ng && !Netgame.DoInvulnerability)) {
3368                                         mprintf((0, "Bashing Invulnerability object #%i to shield.\n", i));
3369                                         Objects[i].id = POW_SHIELD_BOOST;
3370                                         Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3371                                         Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3372                                 } else
3373                                         inv_count++;
3374                         }
3375
3376                         if (Objects[i].id == POW_CLOAK) {
3377            if (cloak_count >= 3 || (ng && !Netgame.DoCloak)) {
3378                                         mprintf((0, "Bashing Cloak object #%i to shield.\n", i));
3379                                         Objects[i].id = POW_SHIELD_BOOST;
3380                                         Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3381                                         Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3382                                 } else
3383                                         cloak_count++;
3384                         }
3385
3386         if (Objects[i].id == POW_AFTERBURNER && ng && !Netgame.DoAfterburner)
3387            bash_to_shield (i,"afterburner");
3388         if (Objects[i].id == POW_FUSION_WEAPON && ng &&  !Netgame.DoFusions)
3389            bash_to_shield (i,"fusion");
3390         if (Objects[i].id == POW_PHOENIX_WEAPON && ng && !Netgame.DoPhoenix)
3391            bash_to_shield (i,"phoenix");
3392
3393         if (Objects[i].id == POW_HELIX_WEAPON && ng && !Netgame.DoHelix)
3394            bash_to_shield (i,"helix");
3395
3396         if (Objects[i].id == POW_MEGA_WEAPON && ng && !Netgame.DoMegas)
3397            bash_to_shield (i,"mega");
3398
3399         if (Objects[i].id == POW_SMARTBOMB_WEAPON && ng && !Netgame.DoSmarts)
3400            bash_to_shield (i,"smartmissile");
3401
3402         if (Objects[i].id == POW_GAUSS_WEAPON && ng && !Netgame.DoGauss)
3403            bash_to_shield (i,"gauss");
3404
3405         if (Objects[i].id == POW_VULCAN_WEAPON && ng && !Netgame.DoVulcan)
3406            bash_to_shield (i,"vulcan");
3407                         
3408         if (Objects[i].id == POW_PLASMA_WEAPON && ng && !Netgame.DoPlasma)
3409            bash_to_shield (i,"plasma");
3410                  
3411         if (Objects[i].id == POW_OMEGA_WEAPON && ng && !Netgame.DoOmega)
3412            bash_to_shield (i,"omega");
3413
3414         if (Objects[i].id == POW_SUPER_LASER && ng && !Netgame.DoSuperLaser)
3415            bash_to_shield (i,"superlaser");
3416
3417         if (Objects[i].id == POW_PROXIMITY_WEAPON && ng && !Netgame.DoProximity)
3418            bash_to_shield (i,"proximity");
3419
3420 //    Special: Make all proximity bombs into shields if in hoard mode because
3421 //              we use the proximity slot in the player struct to signify how many orbs
3422 //              the player has.
3423
3424         if (Objects[i].id == POW_PROXIMITY_WEAPON && ng && (Game_mode & GM_HOARD))
3425            bash_to_shield (i,"proximity");
3426
3427         if (Objects[i].id==POW_VULCAN_AMMO && ng && (!Netgame.DoVulcan && !Netgame.DoGauss))
3428                 bash_to_shield(i,"vulcan ammo");
3429
3430         if (Objects[i].id == POW_SPREADFIRE_WEAPON && ng && !Netgame.DoSpread)
3431            bash_to_shield (i,"spread");
3432         if (Objects[i].id == POW_SMART_MINE && ng && !Netgame.DoSmartMine)
3433            bash_to_shield (i,"smartmine");
3434         if (Objects[i].id == POW_SMISSILE1_1 && ng &&  !Netgame.DoFlash)
3435            bash_to_shield (i,"flash");
3436         if (Objects[i].id == POW_SMISSILE1_4 && ng &&  !Netgame.DoFlash)
3437            bash_to_shield (i,"flash");
3438         if (Objects[i].id == POW_GUIDED_MISSILE_1 && ng &&  !Netgame.DoGuided)
3439            bash_to_shield (i,"guided");
3440         if (Objects[i].id == POW_GUIDED_MISSILE_4 && ng &&  !Netgame.DoGuided)
3441            bash_to_shield (i,"guided");
3442         if (Objects[i].id == POW_EARTHSHAKER_MISSILE && ng &&  !Netgame.DoEarthShaker)
3443            bash_to_shield (i,"earth");
3444         if (Objects[i].id == POW_MERCURY_MISSILE_1 && ng &&  !Netgame.DoMercury)
3445            bash_to_shield (i,"Mercury");
3446         if (Objects[i].id == POW_MERCURY_MISSILE_4 && ng &&  !Netgame.DoMercury)
3447            bash_to_shield (i,"Mercury");
3448         if (Objects[i].id == POW_CONVERTER && ng &&  !Netgame.DoConverter)
3449            bash_to_shield (i,"Converter");
3450         if (Objects[i].id == POW_AMMO_RACK && ng &&  !Netgame.DoAmmoRack)
3451            bash_to_shield (i,"Ammo rack");
3452         if (Objects[i].id == POW_HEADLIGHT && ng &&  !Netgame.DoHeadlight)
3453            bash_to_shield (i,"Headlight");
3454         if (Objects[i].id == POW_LASER && ng &&  !Netgame.DoLaserUpgrade)
3455            bash_to_shield (i,"Laser powerup");
3456         if (Objects[i].id == POW_HOMING_AMMO_1 && ng &&  !Netgame.DoHoming)
3457            bash_to_shield (i,"Homing");
3458         if (Objects[i].id == POW_HOMING_AMMO_4 && ng &&  !Netgame.DoHoming)
3459            bash_to_shield (i,"Homing");
3460         if (Objects[i].id == POW_QUAD_FIRE && ng &&  !Netgame.DoQuadLasers)
3461            bash_to_shield (i,"Quad Lasers");
3462    if (Objects[i].id == POW_FLAG_BLUE && !(Game_mode & GM_CAPTURE))
3463            bash_to_shield (i,"Blue flag");
3464    if (Objects[i].id == POW_FLAG_RED && !(Game_mode & GM_CAPTURE))
3465            bash_to_shield (i,"Red flag");
3466      }
3467    }
3468         
3469         if (Game_mode & GM_HOARD)
3470                 init_hoard_data();
3471
3472    if ((Game_mode & GM_CAPTURE) || (Game_mode & GM_HOARD))
3473          multi_apply_goal_textures();
3474   
3475         multi_sort_kill_list();
3476
3477         multi_show_player_list();
3478
3479         ConsoleObject->control_type = CT_FLYING;
3480
3481         reset_player_object();
3482
3483 }
3484
3485 int Goal_blue_segnum,Goal_red_segnum;
3486
3487 void multi_apply_goal_textures()
3488 {
3489         int             i,j,tex;
3490         segment *seg;
3491         segment2        *seg2;
3492  
3493         for (i=0; i <= Highest_segment_index; i++)
3494          {
3495                 seg = &Segments[i];
3496                 seg2 = &Segment2s[i];
3497  
3498       if (seg2->special==SEGMENT_IS_GOAL_BLUE)
3499                  {
3500                         
3501                         Goal_blue_segnum = i;
3502
3503                         if (Game_mode & GM_HOARD)
3504                                 tex=find_goal_texture (TMI_GOAL_HOARD);
3505                         else
3506                                 tex=find_goal_texture (TMI_GOAL_BLUE);
3507                         
3508                         if (tex>-1)
3509                                 for (j = 0; j < 6; j++) {
3510                                         int v;
3511                                         seg->sides[j].tmap_num=tex;
3512                                         for (v=0;v<4;v++)
3513                                                 seg->sides[j].uvls[v].l = i2f(100);             //max out
3514                                 }
3515
3516                         seg2->static_light = i2f(100);  //make static light bright
3517
3518                  }      
3519                         
3520       if (seg2->special==SEGMENT_IS_GOAL_RED)
3521                  {
3522                         Goal_red_segnum = i;
3523
3524                         // Make both textures the same if Hoard mode
3525
3526                         if (Game_mode & GM_HOARD)
3527                                 tex=find_goal_texture (TMI_GOAL_HOARD);
3528                         else
3529                                 tex=find_goal_texture (TMI_GOAL_RED);
3530                 
3531                         if (tex>-1)
3532                                 for (j = 0; j < 6; j++) {
3533                                         int v;
3534                                         seg->sides[j].tmap_num=tex;
3535                                         for (v=0;v<4;v++)
3536                                                 seg->sides[j].uvls[v].l = i2f(1000);            //max out
3537                                 }
3538
3539                         seg2->static_light = i2f(100);  //make static light bright
3540                  }
3541          }
3542  }
3543 int find_goal_texture (ubyte t)
3544  {
3545   int i;
3546  
3547   for (i=0;i<NumTextures;i++)
3548         if (TmapInfo[i].flags & t)
3549                 return i;
3550
3551   Int3(); // Hey, there is no goal texture for this PIG!!!!
3552                          // Edit bitmaps.tbl and designate two textures to be RED and BLUE
3553                          // goal textures
3554   return (-1);
3555  }
3556
3557
3558 /* DPH: Moved to gameseq.c 
3559 void bash_to_shield (int i,char *s)
3560  {
3561    int type=Objects[i].id;
3562   
3563     mprintf((0, "Bashing %s object #%i to shield.\n",s, i));
3564
3565          PowerupsInMine[type]=MaxPowerupsAllowed[type]=0;
3566                                   
3567     Objects[i].id = POW_SHIELD_BOOST;
3568     Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3569     Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3570  }
3571 */
3572
3573 void multi_set_robot_ai(void)
3574 {
3575         // Go through the objects array looking for robots and setting
3576         // them to certain supported types of NET AI behavior.
3577
3578 //      int i;
3579 //
3580 //      for (i = 0; i <= Highest_object_index; i++)
3581 //      {
3582 //              if (Objects[i].type == OBJ_ROBOT) {
3583 //                      Objects[i].ai_info.REMOTE_OWNER = -1;
3584 //                      if (Objects[i].ai_info.behavior == AIB_STATION)
3585 //                              Objects[i].ai_info.behavior = AIB_NORMAL;
3586 //              }
3587 //      }
3588 }
3589
3590 int multi_delete_extra_objects()
3591 {
3592         int i;
3593         int nnp=0;
3594         object *objp;
3595         
3596         // Go through the object list and remove any objects not used in
3597         // 'Anarchy!' games.
3598
3599         // This function also prints the total number of available multiplayer
3600         // positions in this level, even though this should always be 8 or more!
3601
3602         objp = Objects;
3603         for (i=0;i<=Highest_object_index;i++) {
3604                 if ((objp->type==OBJ_PLAYER) || (objp->type==OBJ_GHOST)) 
3605                         nnp++;
3606                 else if ((objp->type==OBJ_ROBOT) && (Game_mode & GM_MULTI_ROBOTS))
3607                         ;
3608                 else if ( (objp->type!=OBJ_NONE) && (objp->type!=OBJ_PLAYER) && (objp->type!=OBJ_POWERUP) && (objp->type!=OBJ_CNTRLCEN) && (objp->type!=OBJ_HOSTAGE) && !(objp->type==OBJ_WEAPON && objp->id==PMINE_ID) ) {
3609                         //      Before deleting object, if it's a robot, drop it's special powerup, if any
3610                         if (objp->type == OBJ_ROBOT)
3611                                 if (objp->contains_count && (objp->contains_type == OBJ_POWERUP))
3612                                         object_create_egg(objp);
3613                         obj_delete(i);
3614                 }
3615                 objp++;
3616         }
3617
3618         return nnp;
3619 }
3620
3621 void change_playernum_to( int new_Player_num )  
3622 {
3623         if (Player_num > -1)
3624                 memcpy( Players[new_Player_num].callsign, Players[Player_num].callsign, CALLSIGN_LEN+1 );
3625         Player_num = new_Player_num;
3626 }
3627
3628 int multi_all_players_alive()
3629  {
3630   int i;
3631   for (i=0;i<N_players;i++)
3632         {
3633          if (PKilledFlags[i] && Players[i].connected)
3634                 return (0);
3635         }
3636   return (1);
3637  }
3638
3639 void multi_initiate_save_game()
3640 {
3641         uint game_id;
3642         int i, slot;
3643         char filename[128];
3644         char desc[24];
3645
3646         if ((Endlevel_sequence) || (Control_center_destroyed))
3647                 return;
3648     
3649    if (!multi_all_players_alive())
3650          {
3651           HUD_init_message ("Can't save...all players must be alive!");
3652           return;
3653          }
3654
3655 //      multi_send_netplayer_stats_request(255);
3656 //      return;
3657
3658 //      stop_time();
3659         
3660         slot = state_get_save_file(filename, desc, 1 );
3661         if (!slot)      {
3662                 //start_time();
3663                 return;
3664         }
3665         slot--;
3666
3667 //      start_time();
3668
3669         // Make a unique game id
3670         game_id = timer_get_fixed_seconds();
3671         game_id ^= N_players<<4;
3672         for (i=0; i<N_players; i++ )
3673                 game_id ^= *(uint *)Players[i].callsign;
3674         if ( game_id == 0 ) game_id = 1;                // 0 is invalid
3675
3676         mprintf(( 1, "Game_id = %8x\n", game_id));
3677         multi_send_save_game(slot, game_id, desc );
3678         multi_do_frame();
3679         multi_save_game(slot,game_id, desc );
3680 }
3681
3682 extern int state_get_game_id(char *);
3683
3684 void multi_initiate_restore_game()
3685 {
3686         int slot;
3687         char filename[128];
3688
3689         if ((Endlevel_sequence) || (Control_center_destroyed))
3690                 return;
3691
3692    if (!multi_all_players_alive())
3693          {
3694           HUD_init_message ("Can't restore...all players must be alive!");
3695           return;
3696          }
3697
3698 //      stop_time();
3699         slot = state_get_restore_file(filename,1);
3700         if (!slot)      {
3701                 //start_time();
3702                 return;
3703         }
3704    state_game_id=state_get_game_id (filename);
3705    if (!state_game_id)
3706                 return;
3707   
3708         slot--;
3709 //      start_time();
3710         multi_send_restore_game(slot,state_game_id);
3711         multi_do_frame();
3712         multi_restore_game(slot,state_game_id);
3713 }
3714
3715 void multi_save_game(ubyte slot, uint id, char *desc)
3716 {
3717         char filename[128];
3718
3719         if ((Endlevel_sequence) || (Control_center_destroyed))
3720                 return;
3721
3722         #ifndef MACINTOSH
3723         sprintf( filename, "%s.mg%d", Players[Player_num].callsign, slot );
3724         #else
3725         sprintf( filename, ":Players:%s.mg%d", Players[Player_num].callsign, slot );
3726         #endif
3727         mprintf(( 0, "Save game %x on slot %d\n", id, slot ));
3728         HUD_init_message( "Saving game #%d, '%s'", slot, desc );
3729         stop_time();
3730         state_game_id = id;
3731         state_save_all_sub(filename, desc, 0 );
3732 }
3733
3734 void multi_restore_game(ubyte slot, uint id)
3735 {
3736         char filename[128];
3737         player saved_player;
3738         int pnum,i;
3739         int thisid;
3740
3741         if ((Endlevel_sequence) || (Control_center_destroyed))
3742                 return;
3743
3744         mprintf(( 0, "Restore game %x from slot %d\n", id, slot ));
3745         saved_player = Players[Player_num];
3746         #ifndef MACINTOSH
3747         sprintf( filename, "%s.mg%d", Players[Player_num].callsign, slot );
3748         #else
3749         sprintf( filename, ":Players:%s.mg%d", Players[Player_num].callsign, slot );
3750         #endif
3751    
3752    for (i=0;i<N_players;i++)
3753                 multi_strip_robots(i);
3754    
3755         thisid=state_get_game_id (filename);
3756    if (thisid!=id)
3757          {
3758                 multi_bad_restore ();
3759                 return;
3760          }
3761   
3762         pnum=state_restore_all_sub( filename, 1, 0 );
3763
3764         mprintf ((0,"StateId=%d ThisID=%d\n",state_game_id,id));
3765         
3766 /*        if (state_game_id != id )       {
3767                 // Game doesn't match!!!
3768                 nm_messagebox( "Error", 1, "Ok", "Cannot restore saved game" );
3769                 Game_mode |= GM_GAME_OVER;
3770                 Function_mode = FMODE_MENU;
3771                 longjmp(LeaveGame, 0);
3772         }
3773
3774         change_playernum_to(pnum-1);
3775         memcpy( Players[Player_num].callsign, saved_player.callsign, CALLSIGN_LEN+1 );
3776         memcpy( Players[Player_num].net_address, saved_player.net_address, 6 );
3777         Players[Player_num].connected = saved_player.connected;
3778         Players[Player_num].n_packets_got  = saved_player.n_packets_got;                                        
3779         Players[Player_num].n_packets_sent = saved_player.n_packets_sent;                               
3780         Viewer = ConsoleObject = &Objects[pnum-1]; */
3781 }
3782
3783
3784 void extract_netplayer_stats( netplayer_stats *ps, player * pd )
3785 {
3786         int i;
3787         
3788         ps->flags = INTEL_INT(pd->flags);                                                  // Powerup flags, see below...
3789         ps->energy = (fix)INTEL_INT(pd->energy);                                                        // Amount of energy remaining.
3790         ps->shields = (fix)INTEL_INT(pd->shields);                                                      // shields remaining (protection) 
3791         ps->lives = pd->lives;                                                  // Lives remaining, 0 = game over.
3792         ps->laser_level = pd->laser_level;                                      //      Current level of the laser.
3793         ps->primary_weapon_flags=pd->primary_weapon_flags;                                      //      bit set indicates the player has this weapon.
3794         ps->secondary_weapon_flags=pd->secondary_weapon_flags;                                  //      bit set indicates the player has this weapon.
3795         for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
3796                 ps->primary_ammo[i] = INTEL_SHORT(pd->primary_ammo[i]);
3797         for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
3798                 ps->secondary_ammo[i] = INTEL_SHORT(pd->secondary_ammo[i]);
3799
3800 //      memcpy( ps->primary_ammo, pd->primary_ammo, MAX_PRIMARY_WEAPONS*sizeof(short) );        // How much ammo of each type.
3801 //      memcpy( ps->secondary_ammo, pd->secondary_ammo, MAX_SECONDARY_WEAPONS*sizeof(short) ); // How much ammo of each type.
3802
3803         ps->last_score=INTEL_INT(pd->last_score);                                                               // Score at beginning of current level.
3804         ps->score=INTEL_INT(pd->score);                                                                                 // Current score.
3805         ps->cloak_time=(fix)INTEL_INT(pd->cloak_time);                                                  // Time cloaked
3806         ps->homing_object_dist=(fix)INTEL_INT(pd->homing_object_dist);          //      Distance of nearest homing object.
3807         ps->invulnerable_time=(fix)INTEL_INT(pd->invulnerable_time);                    // Time invulnerable
3808         ps->KillGoalCount=INTEL_SHORT(pd->KillGoalCount);
3809         ps->net_killed_total=INTEL_SHORT(pd->net_killed_total);                                 // Number of times killed total
3810         ps->net_kills_total=INTEL_SHORT(pd->net_kills_total);                                   // Number of net kills total
3811         ps->num_kills_level=INTEL_SHORT(pd->num_kills_level);                                   // Number of kills this level
3812         ps->num_kills_total=INTEL_SHORT(pd->num_kills_total);                                   // Number of kills total
3813         ps->num_robots_level=INTEL_SHORT(pd->num_robots_level);                                 // Number of initial robots this level
3814         ps->num_robots_total=INTEL_SHORT(pd->num_robots_total);                                 // Number of robots total
3815         ps->hostages_rescued_total=INTEL_SHORT(pd->hostages_rescued_total);             // Total number of hostages rescued.
3816         ps->hostages_total=INTEL_SHORT(pd->hostages_total);                                             // Total number of hostages.
3817         ps->hostages_on_board=pd->hostages_on_board;                                                    //      Number of hostages on ship.
3818 }
3819
3820 void use_netplayer_stats( player * ps, netplayer_stats *pd )
3821 {
3822         int i;
3823         
3824         ps->flags = INTEL_INT(pd->flags);                                                       // Powerup flags, see below...
3825         ps->energy = (fix)INTEL_INT((int)pd->energy);                           // Amount of energy remaining.
3826         ps->shields = (fix)INTEL_INT((int)pd->shields);                 // shields remaining (protection) 
3827         ps->lives = pd->lives;                                                                  // Lives remaining, 0 = game over.
3828         ps->laser_level = pd->laser_level;                                              //      Current level of the laser.
3829         ps->primary_weapon_flags=pd->primary_weapon_flags;              //      bit set indicates the player has this weapon.
3830         ps->secondary_weapon_flags=pd->secondary_weapon_flags;  //      bit set indicates the player has this weapon.
3831         for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
3832                 ps->primary_ammo[i] = INTEL_SHORT(pd->primary_ammo[i]);
3833         for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
3834                 ps->secondary_ammo[i] = INTEL_SHORT(pd->secondary_ammo[i]);
3835 //      memcpy( ps->primary_ammo, pd->primary_ammo, MAX_PRIMARY_WEAPONS*sizeof(short) );        // How much ammo of each type.
3836 //      memcpy( ps->secondary_ammo, pd->secondary_ammo, MAX_SECONDARY_WEAPONS*sizeof(short) ); // How much ammo of each type.
3837         ps->last_score = INTEL_INT(pd->last_score);                             // Score at beginning of current level.
3838         ps->score = INTEL_INT(pd->score);                                                       // Current score.
3839         ps->cloak_time = (fix)INTEL_INT((int)pd->cloak_time);           // Time cloaked
3840         ps->homing_object_dist = (fix)INTEL_INT((int)pd->homing_object_dist); //        Distance of nearest homing object.
3841         ps->invulnerable_time = (fix)INTEL_INT((int)pd->invulnerable_time);     // Time invulnerable
3842         ps->KillGoalCount=INTEL_SHORT(pd->KillGoalCount);
3843         ps->net_killed_total = INTEL_SHORT(pd->net_killed_total);       // Number of times killed total
3844         ps->net_kills_total = INTEL_SHORT(pd->net_kills_total); // Number of net kills total
3845         ps->num_kills_level = INTEL_SHORT(pd->num_kills_level); // Number of kills this level
3846         ps->num_kills_total = INTEL_SHORT(pd->num_kills_total); // Number of kills total
3847         ps->num_robots_level = INTEL_SHORT(pd->num_robots_level);       // Number of initial robots this level
3848         ps->num_robots_total = INTEL_SHORT(pd->num_robots_total);       // Number of robots total
3849         ps->hostages_rescued_total = INTEL_SHORT(pd->hostages_rescued_total);   // Total number of hostages rescued.
3850         ps->hostages_total = INTEL_SHORT(pd->hostages_total);           // Total number of hostages.
3851         ps->hostages_on_board=pd->hostages_on_board;                    //      Number of hostages on ship.
3852 }
3853
3854 void multi_send_drop_weapon (int objnum,int seed)
3855  {
3856         object *objp;
3857   int count=0;
3858         int ammo_count;
3859
3860         objp = &Objects[objnum];
3861
3862         ammo_count = objp->ctype.powerup_info.count;
3863
3864         if (objp->id == POW_OMEGA_WEAPON && ammo_count == F1_0)
3865                 ammo_count = F1_0 - 1;  //make fit in short
3866
3867         Assert(ammo_count < F1_0);      //make sure fits in short
3868
3869   multibuf[count++]=(char)MULTI_DROP_WEAPON;
3870   multibuf[count++]=(char)objp->id;
3871
3872   *(short *) (multibuf+count)=INTEL_SHORT(Player_num); count += 2;
3873   *(short *) (multibuf+count)=INTEL_SHORT(objnum); count += 2;
3874   *(short *) (multibuf+count)=INTEL_SHORT(ammo_count); count += 2;
3875   *(int *) (multibuf+count)=INTEL_INT(seed);
3876    
3877   map_objnum_local_to_local(objnum);
3878   
3879   if (Game_mode & GM_NETWORK)
3880            PowerupsInMine[objp->id]++;
3881
3882   multi_send_data(multibuf, 12, 2);
3883  }
3884
3885 void multi_do_drop_weapon (char *buf)
3886  {
3887   int pnum,ammo,objnum,remote_objnum,seed;
3888         object *objp;
3889   int powerup_id;
3890
3891         powerup_id=(int)(buf[1]);
3892         pnum = INTEL_SHORT(*(short *)(buf+2));
3893         remote_objnum = INTEL_SHORT(*(short *)(buf+4));
3894         ammo = INTEL_SHORT(*(ushort *)(buf+6));
3895         seed = INTEL_INT(*(int *)(buf+8));
3896
3897         objp = &Objects[Players[pnum].objnum];
3898
3899         objnum = spit_powerup(objp, powerup_id, seed);
3900
3901         map_objnum_local_to_remote(objnum, remote_objnum, pnum);                
3902
3903         if (objnum!=-1)
3904                 Objects[objnum].ctype.powerup_info.count = ammo;
3905    
3906    if (Game_mode & GM_NETWORK)
3907                 PowerupsInMine[powerup_id]++;
3908
3909   mprintf ((0,"Dropped weapon %d!\n"));
3910   
3911  }
3912
3913 void multi_send_guided_info (object *miss,char done)
3914 {
3915 #ifdef MACINTOSH
3916         shortpos sp;
3917 #endif
3918   int count=0;
3919
3920   mprintf ((0,"Sending guided info!\n"));
3921
3922   multibuf[count++]=(char)MULTI_GUIDED;
3923   multibuf[count++]=(char)Player_num;
3924   multibuf[count++]=done;
3925
3926 #ifndef MACINTOSH
3927   create_shortpos((shortpos *)(multibuf+count), miss,0);
3928   count+=sizeof(shortpos);
3929 #else
3930         create_shortpos(&sp, miss, 1);
3931         memcpy(&(multibuf[count]), (ubyte *)(sp.bytemat), 9);
3932         count += 9;
3933         memcpy(&(multibuf[count]), (ubyte *)&(sp.xo), 14);
3934         count += 14;
3935 #endif
3936
3937   multi_send_data(multibuf, count, 0);
3938  }
3939
3940 void multi_do_guided (char *buf)
3941  {
3942   char pnum=buf[1];
3943   int count=3;
3944   static int fun=200;
3945 #ifdef MACINTOSH
3946         shortpos sp;
3947 #endif
3948
3949   if (Guided_missile[(int)pnum]==NULL)
3950    {
3951     if (++fun>=50)
3952      {
3953       mprintf ((0,"Guided missile for %s is NULL!\n",Players[(int)pnum].callsign));
3954       fun=0;
3955      }
3956     return;
3957    }
3958   else if (++fun>=50)
3959    {
3960     mprintf ((0,"Got guided info for %d (%s)\n",pnum,Players[(int)pnum].callsign));
3961     fun=0;
3962    }
3963
3964   if (buf[2])
3965    {
3966          release_guided_missile(pnum);
3967          return;
3968         }
3969
3970
3971   if (Guided_missile[(int)pnum]-Objects<0 || Guided_missile[(int)pnum]-Objects > Highest_object_index)
3972         {       
3973          Int3();  // Get Jason immediately!
3974          return;
3975         }
3976
3977 #ifndef MACINTOSH       
3978   extract_shortpos(Guided_missile[(int)pnum], (shortpos *)(buf+count),0); 
3979 #else
3980         memcpy((ubyte *)(sp.bytemat), (ubyte *)(buf + count), 9);
3981         memcpy((ubyte *)&(sp.xo), (ubyte *)(buf + count + 9), 14);
3982         extract_shortpos(Guided_missile[(int)pnum], &sp, 1);
3983 #endif
3984
3985   count+=sizeof (shortpos);
3986
3987   update_object_seg(Guided_missile[(int)pnum]);
3988  }
3989
3990 void multi_send_stolen_items ()
3991  {
3992   int i,count=1;
3993   multibuf[0]=MULTI_STOLEN_ITEMS;
3994   
3995   for (i=0;i<MAX_STOLEN_ITEMS;i++)
3996    {
3997     multibuf[i+1]=Stolen_items[i];
3998          mprintf ((0,"[%d]=%d ",i,Stolen_items[i]));
3999     count++;      // So I like to break my stuff into smaller chunks, so what?
4000    }
4001   mprintf ((0,"\n"));
4002   multi_send_data(multibuf, count, 1);
4003  }
4004
4005 void multi_do_stolen_items (char *buf)
4006  {
4007   int i;
4008
4009   mprintf ((0,"Recieved a stolen item packet...\n"));
4010
4011   for (i=0;i<MAX_STOLEN_ITEMS;i++)
4012   {
4013    Stolen_items[i]=buf[i+1];
4014         mprintf ((0,"[%d]=%d ",i,Stolen_items[i]));
4015   }
4016   mprintf ((0,"\n"));
4017  }
4018
4019 extern void network_send_important_packet (char *,int);
4020
4021 void multi_send_wall_status (int wallnum,ubyte type,ubyte flags,ubyte state)
4022 {
4023   int count=0;
4024   multibuf[count]=MULTI_WALL_STATUS;        count++;
4025   *(short *)(multibuf+count)=INTEL_SHORT(wallnum);   count+=2;
4026   multibuf[count]=type;                 count++;
4027   multibuf[count]=flags;                count++;
4028   multibuf[count]=state;                count++;
4029
4030  /* if (Game_mode & GM_NETWORK)
4031         {
4032           network_send_important_packet (multibuf,count);
4033           network_send_important_packet (multibuf,count);
4034         } 
4035   else*/
4036         {
4037           multi_send_data(multibuf, count, 1); // twice, just to be sure
4038      multi_send_data(multibuf, count, 1);
4039         }
4040 }
4041 void multi_send_wall_status_specific (int pnum,int wallnum,ubyte type,ubyte flags,ubyte state)
4042 {
4043   // Send wall states a specific rejoining player
4044  
4045   int count=0;
4046
4047   Assert (Game_mode & GM_NETWORK);
4048 //  Assert (pnum>-1 && pnum<N_players);
4049   
4050   multibuf[count]=MULTI_WALL_STATUS;        count++;
4051   *(short *)(multibuf+count)=INTEL_SHORT(wallnum);   count+=2;
4052   multibuf[count]=type;                 count++;
4053   multibuf[count]=flags;                count++;
4054   multibuf[count]=state;                count++;
4055
4056   network_send_naked_packet(multibuf, count,pnum); // twice, just to be sure
4057   network_send_naked_packet(multibuf, count,pnum);
4058 }
4059
4060 void multi_do_wall_status (char *buf)
4061  {
4062   short wallnum;
4063   ubyte flag,type,state;
4064
4065   wallnum=INTEL_SHORT( *(short *)(buf+1) );
4066   type=buf[3];
4067   flag=buf[4];
4068   state=buf[5];
4069
4070   Assert (wallnum>=0);
4071   Walls[wallnum].type=type;
4072   Walls[wallnum].flags=flag;
4073 //  Assert(state <= 4);
4074   Walls[wallnum].state=state; 
4075   
4076   if (Walls[wallnum].type==WALL_OPEN)
4077         {  
4078           digi_kill_sound_linked_to_segment(Walls[wallnum].segnum,Walls[wallnum].sidenum,SOUND_FORCEFIELD_HUM);
4079  //  digi_kill_sound_linked_to_segment(csegp-Segments,cside,SOUND_FORCEFIELD_HUM);
4080    }
4081
4082         
4083 //  mprintf ((0,"Got a walls packet.\n"));
4084  }
4085
4086 void multi_send_jason_cheat (int num)
4087  {
4088   num=num;
4089   return;
4090  }
4091
4092 void multi_send_kill_goal_counts()
4093  {
4094   int i,count=1;
4095   multibuf[0]=MULTI_KILLGOALS;
4096   
4097   for (i=0;i<MAX_PLAYERS;i++)
4098    {
4099     *(char *)(multibuf+count)=(char)Players[i].KillGoalCount;
4100     count++;
4101    }
4102
4103   mprintf ((0,"MULTI: Sending KillGoalCounts...\n"));
4104   multi_send_data(multibuf, count, 1);
4105  }
4106
4107 void multi_do_kill_goal_counts(char *buf)
4108  {
4109   int i,count=1;
4110   
4111   for (i=0;i<MAX_PLAYERS;i++)
4112    {
4113     Players[i].KillGoalCount=*(char *)(buf+count);
4114     mprintf ((0,"KGC: %s has %d kills!\n",Players[i].callsign,Players[i].KillGoalCount));
4115     count++;
4116    }
4117
4118  }
4119
4120 void multi_send_heartbeat ()
4121  {
4122   if (!Netgame.PlayTimeAllowed)
4123         return;
4124  
4125   multibuf[0]=MULTI_HEARTBEAT;
4126   *(fix *)(multibuf+1)=(fix)INTEL_INT(ThisLevelTime);
4127   multi_send_data(multibuf, 5, 0);
4128  }
4129
4130 void multi_do_heartbeat (char *buf)
4131  {
4132   fix num;
4133
4134   num=(fix)INTEL_INT(*(int *)(buf+1));
4135
4136   ThisLevelTime=num;
4137  }
4138
4139 void multi_check_for_killgoal_winner ()
4140  {
4141   int i,best=0,bestnum=0;
4142   object *objp;
4143  
4144   
4145   if (Control_center_destroyed) 
4146         return;
4147  
4148   for (i=0;i<N_players;i++)
4149    {
4150     if (Players[i].KillGoalCount>best)
4151      {
4152       best=Players[i].KillGoalCount;
4153       bestnum=i;
4154      }
4155    }
4156
4157   if (bestnum==Player_num)
4158    {
4159     HUD_init_message("You have the best score at %d kills!",best);
4160 //    Players[Player_num].shields=i2f(200);
4161    }
4162   else
4163
4164    HUD_init_message ("%s has the best score with %d kills!",Players[bestnum].callsign,best);
4165
4166   HUD_init_message ("The control center has been destroyed!");
4167  
4168   objp=obj_find_first_of_type (OBJ_CNTRLCEN);
4169   net_destroy_controlcen (objp);
4170  }
4171   
4172 void multi_send_seismic (fix start,fix end)
4173  {
4174   int count=1; 
4175   
4176   multibuf[0]=MULTI_SEISMIC;
4177   *(fix *)(multibuf+count)=(fix)INTEL_INT(start); count+=(sizeof(fix));
4178   *(fix *)(multibuf+count)=(fix)INTEL_INT(end); count+=(sizeof(fix));
4179   
4180   multi_send_data(multibuf, count, 1);
4181  }
4182
4183 extern fix Seismic_disturbance_start_time;
4184 extern fix Seismic_disturbance_end_time;
4185      
4186 void multi_do_seismic (char *buf)
4187  {
4188   Seismic_disturbance_start_time=(fix)INTEL_INT( *(int *)(buf+1) );
4189   Seismic_disturbance_end_time=(fix)INTEL_INT( *(int *)(buf+5) );
4190   digi_play_sample (SOUND_SEISMIC_DISTURBANCE_START, F1_0);
4191  }
4192
4193 void multi_send_light (int segnum,ubyte val)
4194  {
4195   int count=1,i;
4196   multibuf[0]=MULTI_LIGHT;
4197   *(int *)(multibuf+count)=INTEL_INT(segnum); count+=(sizeof(int));
4198   *(char *)(multibuf+count)=val; count++;
4199   for (i=0;i<6;i++)
4200         {
4201     //mprintf ((0,"Sending %d!\n",Segments[segnum].sides[i].tmap_num2));
4202          *(short *)(multibuf+count)=INTEL_SHORT(Segments[segnum].sides[i].tmap_num2); count+=2;
4203         }
4204    multi_send_data(multibuf, count, 1);
4205  }
4206 void multi_send_light_specific (int pnum,int segnum,ubyte val)
4207  {
4208   int count=1,i;
4209
4210   Assert (Game_mode & GM_NETWORK);
4211 //  Assert (pnum>-1 && pnum<N_players);
4212  
4213   multibuf[0]=MULTI_LIGHT;
4214   *(int *)(multibuf+count)=INTEL_INT(segnum); count+=(sizeof(int));
4215   *(char *)(multibuf+count)=val; count++;
4216
4217   for (i=0;i<6;i++)
4218         {
4219     //mprintf ((0,"Sending %d!\n",Segments[segnum].sides[i].tmap_num2));
4220          *(short *)(multibuf+count)=INTEL_SHORT(Segments[segnum].sides[i].tmap_num2); count+=2;
4221         }
4222    network_send_naked_packet(multibuf, count, pnum);
4223  }
4224
4225 void multi_do_light (char *buf)
4226  {
4227   int i;
4228   int seg=INTEL_INT(*(int *)(buf+1));
4229   ubyte sides=*(char *)(buf+5);
4230
4231   for (i=0;i<6;i++)
4232         {
4233          if ((sides & (1<<i)))
4234      {
4235       subtract_light (seg,i);
4236       Segments[seg].sides[i].tmap_num2=INTEL_SHORT( *(short *)(buf+(6+(2*i))) );
4237   //            mprintf ((0,"Got %d!\n",Segments[seg].sides[i].tmap_num2));
4238           }     
4239    }
4240  }
4241
4242 //@@void multi_send_start_trigger(int triggernum)
4243 //@@{
4244 //@@    // Send an even to trigger something in the mine
4245 //@@    
4246 //@@    int count = 0;
4247 //@@    
4248 //@@    multibuf[count] = MULTI_START_TRIGGER;                          count += 1;
4249 //@@    multibuf[count] = Player_num;                                   count += 1;
4250 //@@    multibuf[count] = (ubyte)triggernum;            count += 1;
4251 //@@   
4252 //@@//   mprintf ((0,"Sending start trigger %d\n",triggernum));
4253 //@@    multi_send_data(multibuf, count, 2);
4254 //@@}
4255 //@@void multi_do_start_trigger(char *buf)
4256 //@@{
4257 //@@    int pnum = buf[1];
4258 //@@    int trigger = buf[2];
4259 //@@   
4260 //@@//   mprintf ((0,"MULTI doing start trigger!\n"));
4261 //@@
4262 //@@    if ((pnum < 0) || (pnum >= N_players) || (pnum == Player_num))
4263 //@@    {
4264 //@@            Int3(); // Got trigger from illegal playernum
4265 //@@            return;
4266 //@@    }
4267 //@@    if ((trigger < 0) || (trigger >= Num_triggers))
4268 //@@    {
4269 //@@            Int3(); // Illegal trigger number in multiplayer
4270 //@@            return;
4271 //@@    }
4272 //@@
4273 //@@  if (!(Triggers[trigger].flags & TF_SPRUNG))
4274 //@@    check_trigger_sub(trigger, pnum,0);
4275 //@@}
4276
4277
4278 void multi_do_flags (char *buf)
4279 {
4280         char pnum=buf[1];
4281         uint flags=INTEL_INT( *(uint *)(buf+2) );
4282
4283         if (pnum!=Player_num)
4284                 Players[(int)pnum].flags=flags;
4285 }
4286
4287 void multi_send_flags (char pnum)
4288  {
4289   multibuf[0]=MULTI_FLAGS;
4290   multibuf[1]=pnum;
4291   *(uint *)(multibuf+2)=INTEL_INT(Players[(int)pnum].flags);
4292   
4293   multi_send_data(multibuf, 6, 1);
4294  } 
4295   
4296 void multi_send_drop_blobs (char pnum)
4297  {
4298   multibuf[0]=MULTI_DROP_BLOB;
4299   multibuf[1]=pnum;
4300
4301   multi_send_data(multibuf, 2, 0);
4302  }
4303
4304 void multi_do_drop_blob (char *buf)
4305  {
4306    char pnum=buf[1];
4307    drop_afterburner_blobs (&Objects[Players[(int)pnum].objnum], 2, i2f(5)/2, -1);
4308  }
4309
4310 void multi_send_powerup_update ()
4311  {
4312   int i;        
4313
4314         
4315   multibuf[0]=MULTI_POWERUP_UPDATE;
4316   for (i=0;i<MAX_POWERUP_TYPES;i++)     
4317    multibuf[i+1]=MaxPowerupsAllowed[i];
4318
4319   multi_send_data(multibuf, MAX_POWERUP_TYPES+1, 1);
4320  }
4321 void multi_do_powerup_update (char *buf)
4322  {
4323   int i;
4324
4325   for (i=0;i<MAX_POWERUP_TYPES;i++)
4326         if (buf[i+1]>MaxPowerupsAllowed[i])
4327                 MaxPowerupsAllowed[i]=buf[i+1];
4328  }
4329
4330 extern active_door ActiveDoors[];
4331 extern int Num_open_doors;                                              // Number of open doors
4332   
4333 void multi_send_active_door (char i)
4334 {
4335         int count;
4336         
4337         multibuf[0]=MULTI_ACTIVE_DOOR;
4338         multibuf[1]=i;
4339         multibuf[2]=Num_open_doors;     
4340         count = 3;
4341 #ifndef MACINTOSH
4342         memcpy ((char *)(&multibuf[3]),&ActiveDoors[(int)i],sizeof(struct active_door));
4343         count += sizeof(active_door);
4344 #else
4345         *(int *)(multibuf + count) = INTEL_INT(ActiveDoors[i].n_parts);                                 count += 4;
4346         *(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].front_wallnum[0]);    count += 2;
4347         *(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].front_wallnum[1]);    count += 2;
4348         *(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].back_wallnum[0]);             count += 2;
4349         *(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].back_wallnum[1]);             count += 2;
4350         *(int *)(multibuf + count) = INTEL_INT(ActiveDoors[i].time);                                    count += 4;
4351 #endif
4352 //      multi_send_data (multibuf,sizeof(struct active_door)+3,1);
4353         multi_send_data (multibuf,count,1);
4354 }
4355
4356 void multi_do_active_door (char *buf)
4357 {
4358         int count;
4359         char i=multibuf[1];
4360         Num_open_doors=buf[2];
4361         
4362         count = 3;
4363 #ifndef MACINTOSH
4364         memcpy (&ActiveDoors[(int)i],buf+count,sizeof(struct active_door));
4365 #else
4366         ActiveDoors[i].n_parts = INTEL_INT( *(int *)(buf+count) );                              count += 4;
4367         ActiveDoors[i].front_wallnum[0] = INTEL_SHORT( *(short *)(buf+count) ); count +=2;
4368         ActiveDoors[i].front_wallnum[1] = INTEL_SHORT( *(short *)(buf+count) ); count +=2;
4369         ActiveDoors[i].back_wallnum[0] = INTEL_SHORT( *(short *)(buf+count) );  count +=2;
4370         ActiveDoors[i].back_wallnum[1] = INTEL_SHORT( *(short *)(buf+count) );  count +=2;
4371         ActiveDoors[i].time = INTEL_INT( *(int *)(buf+count) );                                 count += 4;
4372 #endif
4373 }       
4374
4375 void multi_send_sound_function (char whichfunc,char sound)
4376 {
4377         int count=0;
4378   
4379         multibuf[0]=MULTI_SOUND_FUNCTION;  count++;
4380         multibuf[1]=Player_num; count++;
4381         multibuf[2]=whichfunc;  count++;
4382         #ifndef MACINTOSH
4383         *(uint *)(multibuf+count)=sound;        count++;
4384         #else
4385         multibuf[3] = sound; count++;           // this would probably work on the PC as well.  Jason?
4386         #endif
4387         multi_send_data (multibuf,4,0);
4388 }
4389
4390 #define AFTERBURNER_LOOP_START  20098
4391 #define AFTERBURNER_LOOP_END            25776
4392
4393 void multi_do_sound_function (char *buf)
4394 {
4395    // for afterburner
4396   
4397         char pnum,whichfunc;
4398         int sound; 
4399
4400    if (Players[Player_num].connected!=1)
4401                 return; 
4402
4403    pnum=buf[1];
4404         whichfunc=buf[2];
4405         sound=buf[3];
4406         
4407         if (whichfunc==0)
4408                 digi_kill_sound_linked_to_object (Players[(int)pnum].objnum);
4409         else if (whichfunc==3)
4410            digi_link_sound_to_object3( sound, Players[(int)pnum].objnum, 1,F1_0, i2f(256), AFTERBURNER_LOOP_START, AFTERBURNER_LOOP_END);
4411 }  
4412
4413 void multi_send_capture_bonus (char pnum)
4414 {
4415   Assert (Game_mode & GM_CAPTURE);
4416   
4417   multibuf[0]=MULTI_CAPTURE_BONUS;
4418   multibuf[1]=pnum;
4419
4420   multi_send_data (multibuf,2,1); 
4421   multi_do_capture_bonus (multibuf);
4422 }
4423 void multi_send_orb_bonus (char pnum)
4424 {
4425   Assert (Game_mode & GM_HOARD);
4426   
4427   multibuf[0]=MULTI_ORB_BONUS;
4428   multibuf[1]=pnum;
4429   multibuf[2]=Players[Player_num].secondary_ammo[PROXIMITY_INDEX];
4430
4431   multi_send_data (multibuf,3,1); 
4432   multi_do_orb_bonus (multibuf);
4433 }
4434 void multi_do_capture_bonus(char *buf)
4435 {
4436         // Figure out the results of a network kills and add it to the
4437         // appropriate player's tally.
4438
4439    char pnum=buf[1];
4440    int TheGoal;
4441
4442         kmatrix_kills_changed = 1;
4443    
4444    if (pnum==Player_num)
4445                 HUD_init_message("You have Scored!");
4446         else
4447                 HUD_init_message("%s has Scored!",Players[(int)pnum].callsign);
4448
4449         if (pnum==Player_num)
4450                 digi_play_sample (SOUND_HUD_YOU_GOT_GOAL,F1_0*2);
4451         else if (get_team(pnum)==TEAM_RED)
4452                 digi_play_sample (SOUND_HUD_RED_GOT_GOAL,F1_0*2);
4453         else
4454                 digi_play_sample (SOUND_HUD_BLUE_GOT_GOAL,F1_0*2);
4455
4456         Players[(int)pnum].flags &= ~(PLAYER_FLAGS_FLAG);  // Clear capture flag
4457
4458         team_kills[get_team(pnum)] += 5;
4459         Players[(int)pnum].net_kills_total += 5;
4460         Players[(int)pnum].KillGoalCount+=5;
4461
4462   if (Netgame.KillGoal>0)
4463         {
4464     TheGoal=Netgame.KillGoal*5;
4465
4466          if (Players[(int)pnum].KillGoalCount>=TheGoal)
4467           {
4468            if (pnum==Player_num)
4469             {
4470              HUD_init_message("You reached the kill goal!");
4471              Players[Player_num].shields=i2f(200);
4472             }  
4473            else
4474             HUD_init_message ("%s has reached the kill goal!",Players[(int)pnum].callsign);
4475
4476            HUD_init_message ("The control center has been destroyed!");
4477       net_destroy_controlcen (obj_find_first_of_type (OBJ_CNTRLCEN));
4478           }
4479         }
4480
4481         multi_sort_kill_list();
4482         multi_show_player_list();
4483 }
4484
4485 int GetOrbBonus (char num)
4486  {
4487   int bonus;
4488
4489   bonus=num*(num+1)/2;
4490   return (bonus);
4491  }
4492
4493 void multi_do_orb_bonus(char *buf)
4494 {
4495         // Figure out the results of a network kills and add it to the
4496         // appropriate player's tally.
4497
4498    char pnum=buf[1];
4499    int TheGoal;
4500         int bonus=GetOrbBonus (buf[2]);
4501
4502         kmatrix_kills_changed = 1;
4503    
4504    if (pnum==Player_num)
4505                 HUD_init_message("You have scored %d points!",bonus);
4506         else
4507                 HUD_init_message("%s has scored with %d orbs!",Players[(int)pnum].callsign,buf[2]);
4508
4509         if (pnum==Player_num)
4510                 digi_start_sound_queued (SOUND_HUD_YOU_GOT_GOAL,F1_0*2);
4511         else if (Game_mode & GM_TEAM)
4512          {
4513           if (get_team(pnum)==TEAM_RED)
4514                 digi_play_sample (SOUND_HUD_RED_GOT_GOAL,F1_0*2);
4515           else
4516                 digi_play_sample (SOUND_HUD_BLUE_GOT_GOAL,F1_0*2);
4517          }
4518         else
4519                 digi_play_sample (SOUND_OPPONENT_HAS_SCORED,F1_0*2);
4520
4521    if (bonus>PhallicLimit)
4522         {
4523           if (pnum==Player_num)
4524                 HUD_init_message ("You have the record with %d points!",bonus);
4525           else
4526                 HUD_init_message ("%s has the record with %d points!",Players[(int)pnum].callsign,bonus);
4527      digi_play_sample (SOUND_BUDDY_MET_GOAL,F1_0*2);
4528           PhallicMan=pnum;
4529           PhallicLimit=bonus;
4530    }
4531
4532         Players[(int)pnum].flags &= ~(PLAYER_FLAGS_FLAG);  // Clear orb flag
4533         
4534         team_kills[get_team(pnum)] += bonus;
4535         Players[(int)pnum].net_kills_total += bonus;
4536         Players[(int)pnum].KillGoalCount+=bonus;
4537
4538         team_kills[get_team(pnum)]%=1000;
4539         Players[(int)pnum].net_kills_total%=1000;
4540         Players[(int)pnum].KillGoalCount%=1000;
4541
4542   if (Netgame.KillGoal>0)
4543         {
4544     TheGoal=Netgame.KillGoal*5;
4545
4546          if (Players[(int)pnum].KillGoalCount>=TheGoal)
4547           {
4548            if (pnum==Player_num)
4549             {
4550              HUD_init_message("You reached the kill goal!");
4551              Players[Player_num].shields=i2f(200);
4552             }  
4553            else
4554             HUD_init_message ("%s has reached the kill goal!",Players[(int)pnum].callsign);
4555
4556            HUD_init_message ("The control center has been destroyed!");
4557       net_destroy_controlcen (obj_find_first_of_type (OBJ_CNTRLCEN));
4558           }
4559         }
4560         multi_sort_kill_list();
4561         multi_show_player_list();
4562 }
4563
4564 void multi_send_got_flag (char pnum)
4565 {
4566   multibuf[0]=MULTI_GOT_FLAG;
4567   multibuf[1]=pnum;
4568   
4569   digi_start_sound_queued (SOUND_HUD_YOU_GOT_FLAG,F1_0*2);
4570
4571   multi_send_data (multibuf,2,1);
4572   multi_send_flags (Player_num);
4573 }
4574
4575 int SoundHacked=0;
4576 digi_sound ReversedSound;
4577
4578 void multi_send_got_orb (char pnum)
4579 {
4580   multibuf[0]=MULTI_GOT_ORB;
4581   multibuf[1]=pnum;
4582   
4583   digi_play_sample (SOUND_YOU_GOT_ORB,F1_0*2);
4584
4585   multi_send_data (multibuf,2,1);
4586   multi_send_flags (Player_num);
4587 }
4588
4589 void multi_do_got_flag (char *buf)
4590 {
4591         char pnum=buf[1];
4592
4593    if (pnum==Player_num)
4594                 digi_start_sound_queued (SOUND_HUD_YOU_GOT_FLAG,F1_0*2);
4595         else if (get_team(pnum)==TEAM_RED)
4596                 digi_start_sound_queued (SOUND_HUD_RED_GOT_FLAG,F1_0*2);
4597         else 
4598                 digi_start_sound_queued (SOUND_HUD_BLUE_GOT_FLAG,F1_0*2);
4599    Players[(int)pnum].flags|=PLAYER_FLAGS_FLAG;
4600         HUD_init_message ("%s picked up a flag!",Players[(int)pnum].callsign);
4601 }
4602 void multi_do_got_orb (char *buf)
4603 {
4604         char pnum=buf[1];
4605
4606    Assert (Game_mode & GM_HOARD);
4607
4608    if (Game_mode & GM_TEAM)
4609          {  
4610           if (get_team(pnum)==get_team(Player_num))
4611                 digi_play_sample (SOUND_FRIEND_GOT_ORB,F1_0*2);
4612           else 
4613                 digi_play_sample (SOUND_OPPONENT_GOT_ORB,F1_0*2);
4614     }
4615    else 
4616          digi_play_sample (SOUND_OPPONENT_GOT_ORB,F1_0*2);
4617
4618    Players[(int)pnum].flags|=PLAYER_FLAGS_FLAG;
4619         HUD_init_message ("%s picked up an orb!",Players[(int)pnum].callsign);
4620 }
4621
4622
4623 void DropOrb ()
4624 {
4625         int objnum,seed;
4626
4627         if (!(Game_mode & GM_HOARD))
4628          Int3(); // How did we get here? Get Leighton!
4629
4630         if (!Players[Player_num].secondary_ammo[PROXIMITY_INDEX])
4631         {
4632         HUD_init_message("No orbs to drop!");
4633                 return;
4634         }
4635
4636         seed = d_rand();
4637
4638    objnum = spit_powerup(ConsoleObject,POW_HOARD_ORB,seed);
4639          
4640    if (objnum<0)
4641                 return;
4642
4643         HUD_init_message("Orb dropped!");
4644         digi_play_sample (SOUND_DROP_WEAPON,F1_0);
4645
4646         if ((Game_mode & GM_HOARD) && objnum>-1)
4647                 multi_send_drop_flag(objnum,seed); 
4648         
4649         Players[Player_num].secondary_ammo[PROXIMITY_INDEX]--;
4650
4651    // If empty, tell everyone to stop drawing the box around me
4652         if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]==0)
4653          multi_send_flags (Player_num);
4654 }
4655
4656 void DropFlag ()
4657 {
4658         int objnum,seed;
4659
4660         if (!(Game_mode & GM_CAPTURE) && !(Game_mode & GM_HOARD))
4661                 return;
4662         if (Game_mode & GM_HOARD)
4663         {
4664                 DropOrb();
4665                 return;
4666    }    
4667
4668         if (!(Players[Player_num].flags & PLAYER_FLAGS_FLAG))
4669         {
4670         HUD_init_message("No flag to drop!");
4671                 return;
4672         }
4673
4674
4675         HUD_init_message("Flag dropped!");
4676         digi_play_sample (SOUND_DROP_WEAPON,F1_0);
4677
4678         seed = d_rand();
4679
4680    if (get_team (Player_num)==TEAM_RED)
4681          objnum = spit_powerup(ConsoleObject,POW_FLAG_BLUE,seed);
4682         else
4683          objnum = spit_powerup(ConsoleObject,POW_FLAG_RED,seed);
4684          
4685    if (objnum<0)
4686                 return;
4687
4688         if ((Game_mode & GM_CAPTURE) && objnum>-1)
4689                 multi_send_drop_flag(objnum,seed);
4690
4691         Players[Player_num].flags &=~(PLAYER_FLAGS_FLAG);
4692 }
4693
4694
4695 void multi_send_drop_flag (int objnum,int seed)
4696 {
4697         object *objp;
4698         int count=0;
4699
4700         objp = &Objects[objnum];
4701
4702         multibuf[count++]=(char)MULTI_DROP_FLAG;
4703         multibuf[count++]=(char)objp->id;
4704         
4705         *(short *) (multibuf+count)=INTEL_SHORT(Player_num); count += 2;
4706         *(short *) (multibuf+count)=INTEL_SHORT(objnum); count += 2;
4707         *(short *) (multibuf+count)=INTEL_SHORT(objp->ctype.powerup_info.count); count += 2;
4708         *(int *) (multibuf+count)=INTEL_INT(seed);
4709    
4710         map_objnum_local_to_local(objnum);
4711   
4712    if (!(Game_mode & GM_HOARD))
4713                 if (Game_mode & GM_NETWORK)
4714                         PowerupsInMine[objp->id]++;
4715
4716   multi_send_data(multibuf, 12, 2);
4717 }
4718
4719 void multi_do_drop_flag (char *buf)
4720 {
4721         int pnum,ammo,objnum,remote_objnum,seed;
4722         object *objp;
4723         int powerup_id;
4724
4725         powerup_id=buf[1];
4726         pnum=INTEL_SHORT( *(short *)(buf+2) );
4727         remote_objnum=INTEL_SHORT( *(short *)(buf+4) );
4728         ammo=INTEL_SHORT( *(short *)(buf+6) );
4729         seed=INTEL_INT( *(int *)(buf+8) );
4730
4731         objp = &Objects[Players[pnum].objnum];
4732
4733         objnum = spit_powerup(objp, powerup_id, seed);
4734
4735         map_objnum_local_to_remote(objnum, remote_objnum, pnum);                
4736
4737         if (objnum!=-1)
4738                 Objects[objnum].ctype.powerup_info.count = ammo;
4739    
4740         if (!(Game_mode & GM_HOARD))
4741         {
4742                 if (Game_mode & GM_NETWORK)
4743                         PowerupsInMine[powerup_id]++;
4744            Players[pnum].flags &= ~(PLAYER_FLAGS_FLAG);
4745         }
4746         mprintf ((0,"Dropped flag %d!\n"));
4747   
4748 }
4749
4750 void multi_bad_restore ()
4751 {
4752         Function_mode = FMODE_MENU;
4753         nm_messagebox(NULL, 1, TXT_OK, 
4754 "A multi-save game was restored\nthat you are missing or does not\nmatch that of the others.\nYou must rejoin if you wish to\ncontinue.");
4755         Function_mode = FMODE_GAME;
4756         multi_quit_game = 1;
4757         multi_leave_menu = 1;
4758         multi_reset_stuff();
4759 }
4760
4761 extern int robot_controlled[MAX_ROBOTS_CONTROLLED];
4762 extern int robot_agitation[MAX_ROBOTS_CONTROLLED];
4763 extern fix robot_controlled_time[MAX_ROBOTS_CONTROLLED];
4764 extern fix robot_last_send_time[MAX_ROBOTS_CONTROLLED];
4765 extern fix robot_last_message_time[MAX_ROBOTS_CONTROLLED];
4766 extern int robot_send_pending[MAX_ROBOTS_CONTROLLED];
4767 extern int robot_fired[MAX_ROBOTS_CONTROLLED];
4768 extern byte robot_fire_buf[MAX_ROBOTS_CONTROLLED][18+3];
4769
4770
4771 void multi_send_robot_controls (char pnum)
4772  {
4773   int count=2;
4774   
4775   mprintf ((0,"Sending ROBOT_CONTROLS!!!\n"));
4776  
4777   multibuf[0]=MULTI_ROBOT_CONTROLS;
4778   multibuf[1]=pnum;
4779   memcpy (&(multibuf[count]),&robot_controlled,MAX_ROBOTS_CONTROLLED*4);
4780   count+=(MAX_ROBOTS_CONTROLLED*4);
4781   memcpy (&(multibuf[count]),&robot_agitation,MAX_ROBOTS_CONTROLLED*4);
4782   count+=(MAX_ROBOTS_CONTROLLED*4);
4783   memcpy (&(multibuf[count]),&robot_controlled_time,MAX_ROBOTS_CONTROLLED*4);
4784   count+=(MAX_ROBOTS_CONTROLLED*4);
4785   memcpy (&(multibuf[count]),&robot_last_send_time,MAX_ROBOTS_CONTROLLED*4);
4786   count+=(MAX_ROBOTS_CONTROLLED*4);
4787   memcpy (&(multibuf[count]),&robot_last_message_time,MAX_ROBOTS_CONTROLLED*4);
4788   count+=(MAX_ROBOTS_CONTROLLED*4);
4789   memcpy (&(multibuf[count]),&robot_send_pending,MAX_ROBOTS_CONTROLLED*4);
4790   count+=(MAX_ROBOTS_CONTROLLED*4);
4791   memcpy (&(multibuf[count]),&robot_fired,MAX_ROBOTS_CONTROLLED*4);
4792   count+=(MAX_ROBOTS_CONTROLLED*4);
4793
4794   network_send_naked_packet (multibuf,142,pnum);
4795  }
4796 void multi_do_robot_controls(char *buf)
4797  {
4798   int count=2;
4799
4800   mprintf ((0,"Recieved ROBOT_CONTROLS!!!\n"));
4801  
4802   if (buf[1]!=Player_num)
4803         {
4804     Int3(); // Get Jason!  Recieved a coop_sync that wasn't ours!
4805     return;
4806    }
4807
4808   memcpy (&robot_controlled,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4809   count+=(MAX_ROBOTS_CONTROLLED*4);
4810   memcpy (&robot_agitation,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4811   count+=(MAX_ROBOTS_CONTROLLED*4);
4812   memcpy (&robot_controlled_time,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4813   count+=(MAX_ROBOTS_CONTROLLED*4);
4814   memcpy (&robot_last_send_time,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4815   count+=(MAX_ROBOTS_CONTROLLED*4);
4816   memcpy (&robot_last_message_time,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4817   count+=(MAX_ROBOTS_CONTROLLED*4);
4818   memcpy (&robot_send_pending,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4819   count+=(MAX_ROBOTS_CONTROLLED*4);
4820   memcpy (&robot_fired,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4821   count+=(MAX_ROBOTS_CONTROLLED*4);
4822  }
4823
4824 #define POWERUPADJUSTS 5
4825 int PowerupAdjustMapping[]={11,19,39,41,44};
4826
4827 int multi_powerup_is_4pack (int id)
4828  {
4829   int i;
4830  
4831   for (i=0;i<POWERUPADJUSTS;i++)
4832    if (id==PowerupAdjustMapping[i])
4833     return (1);
4834   return (0);
4835  } 
4836
4837 int multi_powerup_is_allowed(int id)
4838  {
4839         if (id == POW_INVULNERABILITY && !Netgame.DoInvulnerability)
4840          return (0);
4841         if (id == POW_CLOAK && !Netgame.DoCloak) 
4842          return (0);
4843         if (id == POW_AFTERBURNER && !Netgame.DoAfterburner)
4844          return (0);
4845         if (id == POW_FUSION_WEAPON &&  !Netgame.DoFusions)
4846          return (0);
4847         if (id == POW_PHOENIX_WEAPON && !Netgame.DoPhoenix)
4848          return (0);
4849         if (id == POW_HELIX_WEAPON && !Netgame.DoHelix)
4850          return (0);
4851         if (id == POW_MEGA_WEAPON && !Netgame.DoMegas)
4852          return (0);
4853         if (id == POW_SMARTBOMB_WEAPON && !Netgame.DoSmarts)
4854          return (0);
4855         if (id == POW_GAUSS_WEAPON && !Netgame.DoGauss)
4856          return (0);
4857         if (id == POW_VULCAN_WEAPON && !Netgame.DoVulcan)
4858          return (0);
4859         if (id == POW_PLASMA_WEAPON && !Netgame.DoPlasma)
4860          return (0);
4861         if (id == POW_OMEGA_WEAPON && !Netgame.DoOmega)
4862          return (0);
4863         if (id == POW_SUPER_LASER && !Netgame.DoSuperLaser)
4864          return (0);
4865         if (id == POW_PROXIMITY_WEAPON && !Netgame.DoProximity)
4866          return (0);
4867         if (id==POW_VULCAN_AMMO && (!Netgame.DoVulcan && !Netgame.DoGauss))
4868          return (0);
4869         if (id == POW_SPREADFIRE_WEAPON && !Netgame.DoSpread)
4870          return (0);
4871         if (id == POW_SMART_MINE && !Netgame.DoSmartMine)
4872          return (0);
4873         if (id == POW_SMISSILE1_1 &&  !Netgame.DoFlash)
4874          return (0);
4875         if (id == POW_SMISSILE1_4 &&  !Netgame.DoFlash)
4876          return (0);
4877         if (id == POW_GUIDED_MISSILE_1 &&  !Netgame.DoGuided)
4878          return (0);
4879         if (id == POW_GUIDED_MISSILE_4 &&  !Netgame.DoGuided)
4880          return (0);
4881         if (id == POW_EARTHSHAKER_MISSILE &&  !Netgame.DoEarthShaker)
4882          return (0);
4883         if (id == POW_MERCURY_MISSILE_1 &&  !Netgame.DoMercury)
4884          return (0);
4885         if (id == POW_MERCURY_MISSILE_4 &&  !Netgame.DoMercury)
4886          return (0);
4887         if (id == POW_CONVERTER &&  !Netgame.DoConverter)
4888          return (0);
4889         if (id == POW_AMMO_RACK &&  !Netgame.DoAmmoRack)
4890          return (0);
4891         if (id == POW_HEADLIGHT &&  !Netgame.DoHeadlight)
4892          return (0);
4893         if (id == POW_LASER &&  !Netgame.DoLaserUpgrade)
4894          return (0);
4895         if (id == POW_HOMING_AMMO_1 &&  !Netgame.DoHoming)
4896          return (0);
4897         if (id == POW_HOMING_AMMO_4 &&  !Netgame.DoHoming)
4898          return (0);
4899         if (id == POW_QUAD_FIRE &&  !Netgame.DoQuadLasers)
4900          return (0);
4901    if (id == POW_FLAG_BLUE && !(Game_mode & GM_CAPTURE))
4902          return (0);
4903    if (id == POW_FLAG_RED && !(Game_mode & GM_CAPTURE))
4904          return (0);
4905
4906   return (1);
4907  }
4908
4909 void multi_send_finish_game ()
4910  {
4911   multibuf[0]=MULTI_FINISH_GAME;
4912   multibuf[1]=Player_num;
4913
4914   multi_send_data (multibuf,2,1);
4915  }
4916
4917
4918 extern void do_final_boss_hacks();
4919 void multi_do_finish_game (char *buf)
4920  {
4921   if (buf[0]!=MULTI_FINISH_GAME)
4922         return;
4923  
4924   if (Current_level_num!=Last_level)
4925         return;
4926
4927   do_final_boss_hacks();
4928  }
4929
4930 void multi_send_trigger_specific (char pnum,char trig)
4931  {
4932         multibuf[0] = MULTI_START_TRIGGER;
4933    multibuf[1] = trig;
4934    
4935         network_send_naked_packet(multibuf, 2, pnum);
4936  }
4937 void multi_do_start_trigger (char *buf)
4938  {  
4939   Triggers[(int)buf[1]].flags |=TF_DISABLED;
4940  }
4941
4942 extern char *RankStrings[];
4943
4944 void multi_add_lifetime_kills ()
4945  {  
4946   // This function adds a kill to lifetime stats of this player, and possibly
4947   // gives a promotion.  If so, it will tell everyone else
4948         
4949   int oldrank;
4950
4951   if (!Game_mode & GM_NETWORK)
4952         return;
4953  
4954   oldrank=GetMyNetRanking();
4955   
4956   Netlife_kills++;
4957
4958   if (oldrank!=GetMyNetRanking())
4959    {
4960          multi_send_ranking();
4961     if (!FindArg("-norankings"))
4962           {
4963                  HUD_init_message ("You have been promoted to %s!",RankStrings[GetMyNetRanking()]);
4964          digi_play_sample (SOUND_BUDDY_MET_GOAL,F1_0*2);
4965             NetPlayers.players[Player_num].rank=GetMyNetRanking();
4966           }
4967         }
4968   write_player_file();  
4969  } 
4970
4971 void multi_add_lifetime_killed ()
4972  {  
4973   // This function adds a "killed" to lifetime stats of this player, and possibly
4974   // gives a demotion.  If so, it will tell everyone else
4975
4976   int oldrank;
4977
4978   if (!Game_mode & GM_NETWORK)
4979         return;
4980  
4981   oldrank=GetMyNetRanking();
4982   
4983   Netlife_killed++;
4984
4985   if (oldrank!=GetMyNetRanking())
4986    {
4987           multi_send_ranking();
4988           NetPlayers.players[Player_num].rank=GetMyNetRanking();
4989           
4990      if (!FindArg("-norankings"))       
4991                   HUD_init_message ("You have been demoted to %s!",RankStrings[GetMyNetRanking()]);
4992   
4993         }       
4994   write_player_file();  
4995
4996  } 
4997    
4998 void multi_send_ranking ()
4999  {
5000   multibuf[0]=(char)MULTI_RANK;
5001   multibuf[1]=(char)Player_num;
5002   multibuf[2]=(char)GetMyNetRanking();
5003   
5004   multi_send_data (multibuf,3,1);
5005  }
5006
5007 void multi_do_ranking (char *buf)
5008  {
5009   char rankstr[20];
5010   char pnum=buf[1];
5011   char rank=buf[2];
5012
5013   if (NetPlayers.players[(int)pnum].rank<rank)
5014          strcpy (rankstr,"promoted");
5015   else if (NetPlayers.players[(int)pnum].rank>rank)
5016          strcpy (rankstr,"demoted");
5017   else
5018          return;
5019
5020   NetPlayers.players[(int)pnum].rank=rank;
5021
5022   if (!FindArg("-norankings"))
5023           HUD_init_message ("%s has been %s to %s!",Players[(int)pnum].callsign,rankstr,RankStrings[(int)rank]);
5024  }
5025 void multi_send_modem_ping ()
5026 {
5027   multibuf[0]=MULTI_MODEM_PING;
5028   multi_send_data (multibuf,1,1);
5029 }
5030 void multi_send_modem_ping_return ()
5031  {
5032   multibuf[0]=MULTI_MODEM_PING_RETURN;
5033   multi_send_data (multibuf,1,1);
5034  }
5035
5036 void  multi_do_modem_ping_return ()
5037  {
5038   if (PingLaunchTime==0)
5039         {
5040          mprintf ((0,"Got invalid PING RETURN from opponent!\n"));
5041     return;
5042    }
5043   
5044   PingReturnTime=timer_get_fixed_seconds();
5045
5046   HUD_init_message ("Ping time for opponent is %d ms!",f2i(fixmul(PingReturnTime-PingLaunchTime,i2f(1000))));
5047   PingLaunchTime=0;
5048  }
5049
5050
5051 void multi_quick_sound_hack (int num) 
5052  {
5053    int length,i;
5054         num = digi_xlat_sound(num);
5055    length=GameSounds[num].length;
5056    ReversedSound.data=(ubyte *)d_malloc (length);
5057    ReversedSound.length=length;
5058       
5059    for (i=0;i<length;i++)
5060          ReversedSound.data[i]=GameSounds[num].data[length-i-1];
5061
5062    SoundHacked=1;
5063  }
5064
5065 void multi_send_play_by_play (int num,int spnum,int dpnum)
5066  {
5067   if (!(Game_mode & GM_HOARD))
5068         return;
5069  
5070   return;
5071   multibuf[0]=MULTI_PLAY_BY_PLAY;
5072   multibuf[1]=(char)num; 
5073   multibuf[2]=(char)spnum;
5074   multibuf[3]=(char)dpnum;
5075   multi_send_data (multibuf,4,1);
5076   multi_do_play_by_play (multibuf);
5077  }
5078 void multi_do_play_by_play (char *buf)
5079 {
5080         int whichplay=buf[1];
5081         int spnum=buf[2];
5082         int dpnum=buf[3];
5083
5084         if (!(Game_mode & GM_HOARD))
5085         {
5086            Int3(); // Get Leighton, something bad has happened.
5087                 return;
5088    }
5089
5090         switch (whichplay)
5091         {
5092                 case 0: // Smacked!
5093        HUD_init_message ("Ouch! %s has been smacked by %s!",Players[dpnum].callsign,Players[spnum].callsign);
5094                  break;
5095                 case 1: // Spanked!
5096        HUD_init_message ("Haha! %s has been spanked by %s!",Players[dpnum].callsign,Players[spnum].callsign);
5097                  break;
5098                 default:
5099                         Int3(); 
5100         }
5101 }
5102   
5103 ///
5104 /// CODE TO LOAD HOARD DATA
5105 ///
5106
5107
5108 void init_bitmap(grs_bitmap *bm,int w,int h,int flags,ubyte *data)
5109 {
5110         bm->bm_x = bm->bm_y = 0;
5111         bm->bm_w = bm->bm_rowsize = w;
5112         bm->bm_h = h;
5113         bm->bm_type = BM_LINEAR;
5114         bm->bm_flags = flags;
5115         bm->bm_data = data;
5116         bm->bm_handle = 0;
5117         bm->avg_color = 0;
5118 }
5119
5120 grs_bitmap Orb_icons[2];
5121
5122 int Hoard_goal_eclip;
5123
5124 void init_hoard_data()
5125 {
5126         static int first_time=1;
5127         static int orb_vclip;
5128         int n_orb_frames,n_goal_frames;
5129         int orb_w,orb_h;
5130         int icon_w,icon_h;
5131         ubyte palette[256*3];
5132         CFILE *ifile;
5133         int i,save_pos;
5134         extern int Num_bitmap_files,Num_effects,Num_sound_files;
5135
5136         ifile = cfopen("hoard.ham","rb");
5137         if (ifile == NULL)
5138                 Error("can't open <hoard.dat>");
5139
5140         n_orb_frames = cfile_read_short(ifile);
5141         orb_w = cfile_read_short(ifile);
5142         orb_h = cfile_read_short(ifile);
5143         save_pos = cftell(ifile);
5144         cfseek(ifile,sizeof(palette)+n_orb_frames*orb_w*orb_h,SEEK_CUR);
5145         n_goal_frames = cfile_read_short(ifile);
5146         cfseek(ifile,save_pos,SEEK_SET);
5147
5148         if (first_time) {
5149                 ubyte *bitmap_data;
5150                 int bitmap_num=Num_bitmap_files;
5151
5152                 //Allocate memory for bitmaps
5153                 MALLOC( bitmap_data, ubyte, n_orb_frames*orb_w*orb_h + n_goal_frames*64*64 );
5154
5155                 //Create orb vclip
5156                 orb_vclip = Num_vclips++;
5157                 Assert(Num_vclips <= VCLIP_MAXNUM);
5158                 Vclip[orb_vclip].play_time = F1_0/2;
5159                 Vclip[orb_vclip].num_frames = n_orb_frames;
5160                 Vclip[orb_vclip].frame_time = Vclip[orb_vclip].play_time / Vclip[orb_vclip].num_frames;
5161                 Vclip[orb_vclip].flags = 0;
5162                 Vclip[orb_vclip].sound_num = -1;
5163                 Vclip[orb_vclip].light_value = F1_0;
5164                 for (i=0;i<n_orb_frames;i++) {
5165                         Vclip[orb_vclip].frames[i].index = bitmap_num;
5166                         init_bitmap(&GameBitmaps[bitmap_num],orb_w,orb_h,BM_FLAG_TRANSPARENT,bitmap_data);
5167                         bitmap_data += orb_w*orb_h;
5168                         bitmap_num++;
5169                         Assert(bitmap_num < MAX_BITMAP_FILES);
5170                 }
5171
5172                 //Create obj powerup
5173                 Powerup_info[POW_HOARD_ORB].vclip_num = orb_vclip;
5174                 Powerup_info[POW_HOARD_ORB].hit_sound = -1;     //Powerup_info[POW_SHIELD_BOOST].hit_sound;
5175                 Powerup_info[POW_HOARD_ORB].size = Powerup_info[POW_SHIELD_BOOST].size;
5176                 Powerup_info[POW_HOARD_ORB].light = Powerup_info[POW_SHIELD_BOOST].light;
5177
5178                 //Create orb goal wall effect
5179                 Hoard_goal_eclip = Num_effects++;
5180                 Assert(Num_effects < MAX_EFFECTS);
5181                 Effects[Hoard_goal_eclip] = Effects[94];                //copy from blue goal
5182                 Effects[Hoard_goal_eclip].changing_wall_texture = NumTextures;
5183                 Effects[Hoard_goal_eclip].vc.num_frames=n_goal_frames;
5184         
5185                 TmapInfo[NumTextures] = TmapInfo[find_goal_texture(TMI_GOAL_BLUE)];
5186                 TmapInfo[NumTextures].eclip_num = Hoard_goal_eclip;
5187                 TmapInfo[NumTextures].flags = TMI_GOAL_HOARD;
5188                 NumTextures++;
5189                 Assert(NumTextures < MAX_TEXTURES);
5190                 for (i=0;i<n_goal_frames;i++) {
5191                         Effects[Hoard_goal_eclip].vc.frames[i].index = bitmap_num;
5192                         init_bitmap(&GameBitmaps[bitmap_num],64,64,0,bitmap_data);
5193                         bitmap_data += 64*64;
5194                         bitmap_num++;
5195                         Assert(bitmap_num < MAX_BITMAP_FILES);
5196                 }
5197
5198         }
5199
5200         //Load and remap bitmap data for orb
5201         cfread(palette,3,256,ifile);
5202         for (i=0;i<n_orb_frames;i++) {
5203                 grs_bitmap *bm = &GameBitmaps[Vclip[orb_vclip].frames[i].index];
5204                 cfread(bm->bm_data,1,orb_w*orb_h,ifile);
5205                 gr_remap_bitmap_good( bm, palette, 255, -1 );
5206         }
5207
5208         //Load and remap bitmap data for goal texture
5209         cfile_read_short(ifile);                //skip frame count
5210         cfread(palette,3,256,ifile);
5211         for (i=0;i<n_goal_frames;i++) {
5212                 grs_bitmap *bm = &GameBitmaps[Effects[Hoard_goal_eclip].vc.frames[i].index];
5213                 cfread(bm->bm_data,1,64*64,ifile);
5214                 gr_remap_bitmap_good( bm, palette, 255, -1 );
5215         }
5216
5217         //Load and remap bitmap data for HUD icons
5218         for (i=0;i<2;i++) {
5219                 icon_w = cfile_read_short(ifile);
5220                 icon_h = cfile_read_short(ifile);
5221                 if (first_time) {
5222                         ubyte *bitmap_data;
5223                         MALLOC( bitmap_data, ubyte, icon_w*icon_h );
5224                         init_bitmap(&Orb_icons[i],icon_w,icon_h,BM_FLAG_TRANSPARENT,bitmap_data);
5225                 }
5226                 cfread(palette,3,256,ifile);
5227                 cfread(Orb_icons[i].bm_data,1,icon_w*icon_h,ifile);
5228                 gr_remap_bitmap_good( &Orb_icons[i], palette, 255, -1 );
5229         }
5230
5231         if (first_time) {
5232
5233                 //Load sounds for orb game
5234
5235                 for (i=0;i<4;i++) {
5236                         int len;
5237
5238                         len = cfile_read_int(ifile);    //get 11k len
5239
5240                         if (digi_sample_rate == SAMPLE_RATE_22K) {
5241                                 cfseek(ifile,len,SEEK_CUR);             //skip over 11k sample
5242                                 len = cfile_read_int(ifile);            //get 22k len
5243                         }
5244
5245                         GameSounds[Num_sound_files+i].length = len;
5246                         GameSounds[Num_sound_files+i].data = d_malloc(len);
5247                         cfread(GameSounds[Num_sound_files+i].data,1,len,ifile);
5248
5249                         if (digi_sample_rate == SAMPLE_RATE_11K) {
5250                                 len = cfile_read_int(ifile);            //get 22k len
5251                                 cfseek(ifile,len,SEEK_CUR);             //skip over 22k sample
5252                         }
5253
5254                         Sounds[SOUND_YOU_GOT_ORB+i] = Num_sound_files+i;
5255                         AltSounds[SOUND_YOU_GOT_ORB+i] = Sounds[SOUND_YOU_GOT_ORB+i];
5256                 }
5257         }
5258
5259         cfclose(ifile);
5260
5261         first_time = 0;
5262 }
5263
5264 void
5265 multi_process_data(char *buf, int len)
5266 {
5267         // Take an entire message (that has already been checked for validity,
5268         // if necessary) and act on it.  
5269
5270         int type;
5271         len = len;
5272
5273         type = buf[0];
5274         
5275         if (type > MULTI_MAX_TYPE)
5276         {
5277                 mprintf((1, "multi_process_data: invalid type %d.\n", type));
5278                 Int3();
5279                 return;
5280         }
5281
5282
5283    #ifdef NETPROFILING
5284            TTRecv[type]++;  
5285            fprintf (RecieveLogFile,"Packet type: %d Len:%d TT=%d\n",type,len,TTRecv[type]);
5286            fflush (RecieveLogFile);
5287         #endif
5288   
5289         switch(type) 
5290         {
5291                 case MULTI_POSITION:
5292                         if (!Endlevel_sequence) multi_do_position(buf); break;
5293                 case MULTI_REAPPEAR:
5294                         if (!Endlevel_sequence) multi_do_reappear(buf); break;
5295                 case MULTI_FIRE:
5296                         if (!Endlevel_sequence) multi_do_fire(buf); break;
5297                 case MULTI_KILL:
5298                         multi_do_kill(buf); break;
5299                 case MULTI_REMOVE_OBJECT:
5300                         if (!Endlevel_sequence) multi_do_remobj(buf); break;
5301                 case MULTI_PLAYER_DROP:
5302                 case MULTI_PLAYER_EXPLODE:
5303                         if (!Endlevel_sequence) multi_do_player_explode(buf); break;
5304                 case MULTI_MESSAGE:
5305                         if (!Endlevel_sequence) multi_do_message(buf); break;
5306                 case MULTI_QUIT:
5307                         if (!Endlevel_sequence) multi_do_quit(buf); break;
5308                 case MULTI_BEGIN_SYNC:
5309                    break;
5310                 case MULTI_CONTROLCEN:
5311                         if (!Endlevel_sequence) multi_do_controlcen_destroy(buf); break;
5312                 case MULTI_POWERUP_UPDATE:
5313                         if (!Endlevel_sequence) multi_do_powerup_update(buf); break;
5314                 case MULTI_SOUND_FUNCTION:
5315                         multi_do_sound_function(buf); break;
5316       case MULTI_MARKER:
5317          if (!Endlevel_sequence) multi_do_drop_marker (buf); break;
5318       case MULTI_DROP_WEAPON:
5319          if (!Endlevel_sequence) multi_do_drop_weapon(buf); break;
5320       case MULTI_DROP_FLAG:
5321          if (!Endlevel_sequence) multi_do_drop_flag(buf); break;
5322       case MULTI_GUIDED:
5323          if (!Endlevel_sequence) multi_do_guided (buf); break;
5324       case MULTI_STOLEN_ITEMS:
5325          if (!Endlevel_sequence) multi_do_stolen_items(buf); break;
5326       case MULTI_WALL_STATUS:
5327          if (!Endlevel_sequence) multi_do_wall_status(buf); break;
5328       case MULTI_HEARTBEAT:
5329          if (!Endlevel_sequence) multi_do_heartbeat (buf); break;
5330       case MULTI_SEISMIC:
5331                 if (!Endlevel_sequence) multi_do_seismic (buf); break;
5332            case MULTI_LIGHT:
5333                 if (!Endlevel_sequence) multi_do_light (buf); break;
5334       case MULTI_KILLGOALS:
5335                 
5336                  if (!Endlevel_sequence) multi_do_kill_goal_counts (buf); break;
5337                 case MULTI_ENDLEVEL_START:
5338                         if (!Endlevel_sequence) multi_do_escape(buf); break;
5339                 case MULTI_END_SYNC:
5340                         break;
5341                 case MULTI_CLOAK:
5342                         if (!Endlevel_sequence) multi_do_cloak(buf); break;
5343                 case MULTI_DECLOAK:
5344                         if (!Endlevel_sequence) multi_do_decloak(buf); break;
5345                 case MULTI_DOOR_OPEN:
5346                         if (!Endlevel_sequence) multi_do_door_open(buf); break;
5347                 case MULTI_CREATE_EXPLOSION:
5348                         if (!Endlevel_sequence) multi_do_create_explosion(buf); break;
5349                 case MULTI_CONTROLCEN_FIRE:
5350                         if (!Endlevel_sequence) multi_do_controlcen_fire(buf); break;
5351                 case MULTI_CREATE_POWERUP:
5352                         if (!Endlevel_sequence) multi_do_create_powerup(buf); break;
5353                 case MULTI_PLAY_SOUND:
5354                         if (!Endlevel_sequence) multi_do_play_sound(buf); break;
5355                 case MULTI_CAPTURE_BONUS:
5356                         if (!Endlevel_sequence) multi_do_capture_bonus(buf); break;
5357                 case MULTI_ORB_BONUS:
5358                         if (!Endlevel_sequence) multi_do_orb_bonus(buf); break;
5359                 case MULTI_GOT_FLAG:
5360                         if (!Endlevel_sequence) multi_do_got_flag(buf); break;
5361                 case MULTI_GOT_ORB:
5362                         if (!Endlevel_sequence) multi_do_got_orb(buf); break;
5363                 case MULTI_PLAY_BY_PLAY:
5364                         if (!Endlevel_sequence) multi_do_play_by_play(buf); break;
5365                 case MULTI_RANK:
5366                         if (!Endlevel_sequence) multi_do_ranking (buf); break;
5367                 case MULTI_MODEM_PING:
5368                         if (!Endlevel_sequence) multi_send_modem_ping_return(); break;
5369                 case MULTI_MODEM_PING_RETURN:
5370                         if (!Endlevel_sequence) multi_do_modem_ping_return(); break;
5371 #ifndef SHAREWARE
5372                 case MULTI_FINISH_GAME:
5373                         multi_do_finish_game(buf);      break;  // do this one regardless of endsequence        
5374                 case MULTI_ROBOT_CONTROLS:
5375                         if (!Endlevel_sequence) multi_do_robot_controls(buf); break;
5376                 case MULTI_ROBOT_CLAIM:
5377                         if (!Endlevel_sequence) multi_do_claim_robot(buf); break;
5378                 case MULTI_ROBOT_POSITION:
5379                         if (!Endlevel_sequence) multi_do_robot_position(buf); break;
5380                 case MULTI_ROBOT_EXPLODE:
5381                         if (!Endlevel_sequence) multi_do_robot_explode(buf); break;
5382                 case MULTI_ROBOT_RELEASE:
5383                         if (!Endlevel_sequence) multi_do_release_robot(buf); break;
5384                 case MULTI_ROBOT_FIRE:
5385                         if (!Endlevel_sequence) multi_do_robot_fire(buf); break;
5386 #endif
5387                 case MULTI_SCORE:
5388                         if (!Endlevel_sequence) multi_do_score(buf); break;
5389                 case MULTI_CREATE_ROBOT:
5390                         if (!Endlevel_sequence) multi_do_create_robot(buf); break;
5391                 case MULTI_TRIGGER:
5392                         if (!Endlevel_sequence) multi_do_trigger(buf); break;
5393                 case MULTI_START_TRIGGER:
5394                         if (!Endlevel_sequence) multi_do_start_trigger(buf); break;
5395                 case MULTI_FLAGS:
5396                         if (!Endlevel_sequence) multi_do_flags(buf); break;
5397                 case MULTI_DROP_BLOB:
5398                         if (!Endlevel_sequence) multi_do_drop_blob(buf); break;
5399            case MULTI_ACTIVE_DOOR:
5400                         if (!Endlevel_sequence) multi_do_active_door(buf); break;
5401                 case MULTI_BOSS_ACTIONS:
5402                         if (!Endlevel_sequence) multi_do_boss_actions(buf); break;
5403                 case MULTI_CREATE_ROBOT_POWERUPS:
5404                         if (!Endlevel_sequence) multi_do_create_robot_powerups(buf); break;
5405                 case MULTI_HOSTAGE_DOOR:
5406                         if (!Endlevel_sequence) multi_do_hostage_door_status(buf); break;
5407                 case MULTI_SAVE_GAME:
5408                         if (!Endlevel_sequence) multi_do_save_game(buf); break;
5409                 case MULTI_RESTORE_GAME:
5410                         if (!Endlevel_sequence) multi_do_restore_game(buf); break;
5411                 case MULTI_REQ_PLAYER:
5412                         if (!Endlevel_sequence) multi_do_req_player(buf); break;
5413                 case MULTI_SEND_PLAYER:
5414                         if (!Endlevel_sequence) multi_do_send_player(buf); break;
5415
5416                 default:
5417                         mprintf((1, "Invalid type in multi_process_input().\n"));
5418                         Int3();
5419         }
5420 }