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