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