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