attempt to support d1 for mac mission
[btb/d2x.git] / main / multi.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 /*
15  * $Source: /cvs/cvsroot/d2x/main/multi.c,v $
16  * $Revision: 1.7 $
17  * $Author: btb $
18  * $Date: 2002-12-31 23:19:42 $
19  *
20  * FIXME: put description here
21  *
22  * $Log: not supported by cvs2svn $
23  * Revision 1.6  2002/08/30 01:01:18  btb
24  * more networking fixes
25  *
26  * Revision 1.5  2001/10/23 21:53:19  bradleyb
27  * No longer #ifdef'ing out the whole file.  RCS header added
28  *
29  *
30  */
31
32 #ifdef HAVE_CONFIG_H
33 #include <conf.h>
34 #endif
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40 #include <ctype.h>
41
42 #include "u_mem.h"
43 #include "strutil.h"
44 #include "game.h"
45 #include "modem.h"
46 #include "network.h"
47 #include "multi.h"
48 #include "object.h"
49 #include "laser.h"
50 #include "fuelcen.h"
51 #include "scores.h"
52 #include "gauges.h"
53 #include "collide.h"
54 #include "error.h"
55 #include "fireball.h"
56 #include "newmenu.h"
57 #include "mono.h"
58 #include "wall.h"
59 #include "cntrlcen.h"
60 #include "powerup.h"
61 #include "polyobj.h"
62 #include "bm.h"
63 #include "endlevel.h"
64 #include "key.h"
65 #include "playsave.h"
66 #include "timer.h"
67 #include "digi.h"
68 #include "sounds.h"
69 #include "kconfig.h"
70 #include "newdemo.h"
71 #include "text.h"
72 #include "kmatrix.h"
73 #include "multibot.h"
74 #include "gameseq.h"
75 #include "physics.h"
76 #include "config.h"
77 #include "state.h"
78 #include "ai.h"
79 #include "switch.h"
80 #include "textures.h"
81 #include "byteswap.h"
82 #include "sounds.h"
83 #include "args.h"
84 #include "d_delay.h"
85 #include "cfile.h"
86 #include "effects.h"
87
88 void multi_reset_player_object(object *objp);
89 void multi_reset_object_texture (object *objp);
90 void multi_add_lifetime_killed ();
91 void multi_add_lifetime_kills ();
92 void multi_send_play_by_play (int num,int spnum,int dpnum);
93 void multi_send_heartbeat ();
94 void multi_send_modem_ping ();
95 void multi_cap_objects ();
96 void multi_adjust_remote_cap (int pnum);
97 void multi_save_game(ubyte slot, uint id, char *desc);
98 void multi_restore_game(ubyte slot, uint id);
99 void multi_set_robot_ai(void);
100 void multi_send_powerup_update ();
101 void bash_to_shield (int i,char *s);
102 void init_hoard_data();
103 void multi_apply_goal_textures();
104 int find_goal_texture (ubyte t);
105 void multi_bad_restore ();
106 void multi_do_capture_bonus(char *buf);
107 void multi_do_orb_bonus(char *buf);
108 void multi_send_drop_flag (int objnum,int seed);
109 void multi_send_ranking ();
110 void multi_do_play_by_play (char *buf);
111
112 //
113 // Local macros and prototypes
114 //
115
116 // LOCALIZE ME!!
117
118 #define vm_angvec_zero(v) (v)->p=(v)->b=(v)->h=0
119
120 void drop_player_eggs(object *player); // from collide.c
121 void GameLoop(int, int); // From game.c
122
123 //
124 // Global variables
125 //
126
127 extern vms_vector MarkerPoint[];
128 extern char MarkerMessage[16][40];
129 extern char MarkerOwner[16][40];
130 extern int MarkerObject[];
131
132 int control_invul_time = 0;
133 int who_killed_controlcen = -1;  // -1 = noone
134
135 //do we draw the kill list on the HUD?
136 int Show_kill_list = 1;
137 int Show_reticle_name = 1;
138 fix Show_kill_list_timer = 0;
139
140 char Multi_is_guided=0;
141 char PKilledFlags[MAX_NUM_NET_PLAYERS];
142
143 int multi_sending_message = 0;
144 int multi_defining_message = 0;
145 int multi_message_index = 0;
146
147 char multibuf[MAX_MULTI_MESSAGE_LEN+4];                // This is where multiplayer message are built
148
149 short remote_to_local[MAX_NUM_NET_PLAYERS][MAX_OBJECTS];  // Remote object number for each local object
150 short local_to_remote[MAX_OBJECTS]; 
151 byte  object_owner[MAX_OBJECTS];   // Who created each object in my universe, -1 = loaded at start
152
153 int     Net_create_objnums[MAX_NET_CREATE_OBJECTS]; // For tracking object creation that will be sent to remote
154 int     Net_create_loc = 0;  // pointer into previous array
155 int     Network_laser_fired = 0; // How many times we shot
156 int     Network_laser_gun; // Which gun number we shot
157 int   Network_laser_flags; // Special flags for the shot
158 int   Network_laser_level; // What level
159 short   Network_laser_track; // Who is it tracking?
160 char    Network_message[MAX_MESSAGE_LEN];
161 char  Network_message_macro[4][MAX_MESSAGE_LEN];
162 int     Network_message_reciever=-1;
163 int     sorted_kills[MAX_NUM_NET_PLAYERS];
164 short kill_matrix[MAX_NUM_NET_PLAYERS][MAX_NUM_NET_PLAYERS];
165 int     multi_goto_secret = 0;
166 short   team_kills[2];
167 int     multi_in_menu = 0;
168 int     multi_leave_menu = 0;
169 int     multi_quit_game = 0;
170
171 netgame_info Netgame;
172 AllNetPlayers_info NetPlayers;
173
174 bitmap_index multi_player_textures[MAX_NUM_NET_PLAYERS][N_PLAYER_SHIP_TEXTURES];
175
176 typedef struct netplayer_stats {
177         ubyte           message_type;
178         ubyte           Player_num;                                             // Who am i?
179         uint            flags;                                                  // Powerup flags, see below...
180         fix             energy;                                                 // Amount of energy remaining.
181         fix             shields;                                                        // shields remaining (protection) 
182         ubyte           lives;                                                  // Lives remaining, 0 = game over.
183         ubyte           laser_level;                                    //      Current level of the laser.
184         ubyte           primary_weapon_flags;                                   //      bit set indicates the player has this weapon.
185         ubyte           secondary_weapon_flags;                                 //      bit set indicates the player has this weapon.
186         ushort  primary_ammo[MAX_PRIMARY_WEAPONS];      // How much ammo of each type.
187         ushort  secondary_ammo[MAX_SECONDARY_WEAPONS]; // How much ammo of each type.
188         int             last_score;                                     // Score at beginning of current level.
189         int             score;                                                  // Current score.
190         fix             cloak_time;                                             // Time cloaked
191         fix             invulnerable_time;                      // Time invulnerable
192         fix             homing_object_dist;                     //      Distance of nearest homing object.
193         short           KillGoalCount;
194         short           net_killed_total;                               // Number of times killed total
195         short           net_kills_total;                                // Number of net kills total
196         short           num_kills_level;                                // Number of kills this level
197         short           num_kills_total;                                // Number of kills total
198         short           num_robots_level;                       // Number of initial robots this level
199         short           num_robots_total;                       // Number of robots total
200         ushort  hostages_rescued_total;         // Total number of hostages rescued.
201         ushort  hostages_total;                         // Total number of hostages.
202         ubyte           hostages_on_board;                      //      Number of hostages on ship.
203         ubyte           unused[16];
204 } netplayer_stats;                                                      
205
206 int message_length[MULTI_MAX_TYPE+1] = {
207         24, // POSITION
208         3,  // REAPPEAR
209         8,  // FIRE
210         5,  // KILL
211         4,  // REMOVE_OBJECT
212         97+9, // PLAYER_EXPLODE
213         37, // MESSAGE (MAX_MESSAGE_LENGTH = 40)
214         2,  // QUIT
215         4,  // PLAY_SOUND
216         41, // BEGIN_SYNC
217         4,  // CONTROLCEN
218         5,  // CLAIM ROBOT
219         4,  // END_SYNC
220    2,  // CLOAK
221         3,  // ENDLEVEL_START
222    5,  // DOOR_OPEN
223         2,  // CREATE_EXPLOSION
224         16, // CONTROLCEN_FIRE
225         97+9, // PLAYER_DROP
226         19, // CREATE_POWERUP
227         9,  // MISSILE_TRACK
228         2,  // DE-CLOAK
229         2,       // MENU_CHOICE
230         28, // ROBOT_POSITION  (shortpos_length (23) + 5 = 28)
231         9,  // ROBOT_EXPLODE
232         5,       // ROBOT_RELEASE
233         18, // ROBOT_FIRE
234         6,  // SCORE
235         6,  // CREATE_ROBOT
236         3,  // TRIGGER
237         10, // BOSS_ACTIONS     
238         27, // ROBOT_POWERUPS
239         7,  // HOSTAGE_DOOR
240         2+24,   //SAVE_GAME             (ubyte slot, uint id, char name[20])
241         2+4,   //RESTORE_GAME   (ubyte slot, uint id)
242         1+1,    // MULTI_REQ_PLAYER
243         sizeof(netplayer_stats),                        // MULTI_SEND_PLAYER
244         55, // MULTI_MARKER
245         12, // MULTI_DROP_WEAPON
246 #ifndef MACINTOSH
247         3+sizeof(shortpos), // MULTI_GUIDED, IF SHORTPOS CHANGES, CHANGE MAC VALUE BELOW
248 #else
249         26,     //MULTI_GUIDED IF SIZE OF SHORTPOS CHANGES, CHANGE THIS VALUE AS WELL!!!!!!
250 #endif
251         11, // MULTI_STOLEN_ITEMS
252         6, // MULTI_WALL_STATUS
253         5, // MULTI_HEARTBEAT
254         9, // MULTI_KILLGOALS
255    9, // MULTI_SEISMIC
256         18, // MULTI_LIGHT
257    2, // MULTI_START_TRIGGER     
258         6, // MULTI_FLAGS
259         2, // MULTI_DROP_BLOB
260         MAX_POWERUP_TYPES+1, // MULTI_POWERUP_UPDATE
261         sizeof(active_door)+3, // MULTI_ACTIVE_DOOR
262         4, // MULTI_SOUND_FUNCTION
263         2, // MULTI_CAPTURE_BONUS
264    2, // MULTI_GOT_FLAG
265         12, // MULTI_DROP_FLAG
266    142, // MULTI_ROBOT_CONTROLS
267    2, // MULTI_FINISH_GAME
268    3, // MULTI_RANK
269    1, // MULTI_MODEM_PING
270    1, // MULTI_MODEM_PING_RETURN
271         3, // MULTI_ORB_BONUS
272    2, // MULTI_GOT_ORB
273         12, // MULTI_DROP_ORB
274         4,  // MULTI_PLAY_BY_PLAY
275 };
276
277 void extract_netplayer_stats( netplayer_stats *ps, player * pd );
278 void use_netplayer_stats( player * ps, netplayer_stats *pd );
279 char PowerupsInMine[MAX_POWERUP_TYPES],MaxPowerupsAllowed[MAX_POWERUP_TYPES];
280 extern fix ThisLevelTime;
281
282 //
283 //  Functions that replace what used to be macros
284 //              
285
286 int objnum_remote_to_local(int remote_objnum, int owner)
287 {
288         // Map a remote object number from owner to a local object number
289
290         int result;     
291
292         if ((owner >= N_players) || (owner < -1)) {
293                 Int3(); // Illegal!
294                 return(remote_objnum);
295         }
296
297         if (owner == -1)
298                 return(remote_objnum);
299         
300         if ((remote_objnum < 0) || (remote_objnum >= MAX_OBJECTS))
301                 return(-1);
302
303         result = remote_to_local[owner][remote_objnum];
304
305         if (result < 0)
306         {
307                 mprintf((1, "Remote object owner %d number %d mapped to -1!\n", owner, remote_objnum));
308                 return(-1);
309         }
310
311 #ifndef NDEBUG
312         if (object_owner[result] != owner)
313         {
314                 mprintf((1, "Remote object owner %d number %d doesn't match owner %d.\n", owner, remote_objnum, object_owner[result]));
315         }
316 #endif  
317 //      Assert(object_owner[result] == owner);
318
319         return(result);
320 }
321
322 int objnum_local_to_remote(int local_objnum, byte *owner)
323 {
324         // Map a local object number to a remote + owner
325
326         int result;
327
328         if ((local_objnum < 0) || (local_objnum > Highest_object_index)) {
329                 *owner = -1;
330                 return(-1);
331         }
332
333         *owner = object_owner[local_objnum];
334
335         if (*owner == -1)
336                 return(local_objnum);
337
338         if ((*owner >= N_players) || (*owner < -1)) {
339                 Int3(); // Illegal!
340                 *owner = -1;
341                 return local_objnum;
342         }
343         
344         result = local_to_remote[local_objnum];
345         
346 //      mprintf((0, "Local object %d mapped to owner %d objnum %d.\n", local_objnum,
347 //              *owner, result));
348
349         if (result < 0)
350         {
351                 Int3(); // See Rob, object has no remote number!
352         }
353
354         return(result);
355 }
356
357 void
358 map_objnum_local_to_remote(int local_objnum, int remote_objnum, int owner)
359 {
360         // Add a mapping from a network remote object number to a local one
361
362         Assert(local_objnum > -1);
363         Assert(remote_objnum > -1);
364         Assert(owner > -1);
365         Assert(owner != Player_num);
366         Assert(local_objnum < MAX_OBJECTS);
367         Assert(remote_objnum < MAX_OBJECTS);
368
369         object_owner[local_objnum] = owner;
370
371         remote_to_local[owner][remote_objnum] = local_objnum;
372         local_to_remote[local_objnum] = remote_objnum;
373
374         return;
375 }
376
377 void
378 map_objnum_local_to_local(int local_objnum)
379 {
380         // Add a mapping for our locally created objects
381
382         Assert(local_objnum > -1);
383         Assert(local_objnum < MAX_OBJECTS);
384
385         object_owner[local_objnum] = Player_num;
386         remote_to_local[Player_num][local_objnum] = local_objnum;
387         local_to_remote[local_objnum] = local_objnum;
388
389         return;
390 }
391
392 //
393 // Part 1 : functions whose main purpose in life is to divert the flow
394 //          of execution to either network or serial specific code based
395 //          on the curretn Game_mode value.
396 //
397
398 void
399 multi_endlevel_score(void)
400 {
401         int old_connect=0;
402         int i;
403
404         // Show a score list to end of net players
405
406         // Save connect state and change to new connect state
407 #ifdef NETWORK
408         if (Game_mode & GM_NETWORK)
409         {
410                 old_connect = Players[Player_num].connected;
411                 if (Players[Player_num].connected!=3)
412                  Players[Player_num].connected = CONNECT_END_MENU;
413                 Network_status = NETSTAT_ENDLEVEL;
414                 
415         }
416 #endif
417
418         // Do the actual screen we wish to show
419
420         Function_mode = FMODE_MENU;
421
422         kmatrix_view(Game_mode & GM_NETWORK);
423
424         Function_mode = FMODE_GAME;
425
426         // Restore connect state
427
428         if (Game_mode & GM_NETWORK)
429         {
430           Players[Player_num].connected = old_connect;
431         }
432
433 #ifndef SHAREWARE
434         if (Game_mode & GM_MULTI_COOP)
435         {
436            int i;
437                 for (i = 0; i < MaxNumNetPlayers; i++)
438                 // Reset keys
439                         Players[i].flags &= ~(PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_GOLD_KEY);
440         }
441         for (i = 0; i < MaxNumNetPlayers; i++)
442            Players[i].flags &= ~(PLAYER_FLAGS_FLAG);  // Clear capture flag
443         
444 #endif
445
446   for (i=0;i<MAX_PLAYERS;i++)
447     Players[i].KillGoalCount=0;
448
449   for (i=0;i<MAX_POWERUP_TYPES;i++)
450         {
451                 MaxPowerupsAllowed[i]=0;
452                 PowerupsInMine[i]=0;
453         }  
454
455 }
456
457 int
458 get_team(int pnum)
459 {
460         if ((Game_mode & GM_CAPTURE) && ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)))
461                 return pnum;
462
463         if (Netgame.team_vector & (1 << pnum))
464                 return 1;
465         else
466                 return 0;
467 }
468
469 int
470 multi_choose_mission(int *anarchy_only)
471 {
472         int i, n_missions;
473         int default_mission;
474    char *m[MAX_MISSIONS];
475         int new_mission_num = 0;
476
477         *anarchy_only = 0;
478
479         n_missions = build_mission_list(1);
480
481         if (n_missions > 1) {
482
483                 default_mission = 0;
484                 for (i=0;i<n_missions;i++) {
485                         m[i] = Mission_list[i].mission_name;
486                         if ( !stricmp( m[i], config_last_mission ) )    
487                                 default_mission = i;
488                 }
489
490       ExtGameStatus=GAMESTAT_START_MULTIPLAYER_MISSION;
491                 new_mission_num = newmenu_listbox1(TXT_MULTI_MISSION, n_missions, m, 1, default_mission, NULL );
492
493                 if (new_mission_num == -1)
494                         return -1;      //abort!
495
496                 strcpy(config_last_mission, m[new_mission_num]  );
497                 
498                 if (!load_mission(new_mission_num)) {
499                         nm_messagebox( NULL, 1, TXT_OK, TXT_MISSION_ERROR); 
500                         return -1;
501                 }
502
503            *anarchy_only = Mission_list[new_mission_num].anarchy_only_flag;
504         }
505         return(new_mission_num);
506 }
507
508 extern void game_disable_cheats();
509         
510 void
511 multi_new_game(void)
512 {
513         int i;
514         
515         // Reset variables for a new net game
516
517         memset(kill_matrix, 0, MAX_NUM_NET_PLAYERS*MAX_NUM_NET_PLAYERS*2); // Clear kill matrix
518
519         for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
520         {
521                 sorted_kills[i] = i;
522                 Players[i].net_killed_total = 0;
523                 Players[i].net_kills_total = 0;
524                 Players[i].flags = 0;
525                 Players[i].KillGoalCount=0;
526         }
527
528 #ifndef SHAREWARE
529         for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
530         {
531                 robot_controlled[i] = -1;       
532                 robot_agitation[i] = 0;
533                 robot_fired[i] = 0;
534         }
535 #endif
536
537         team_kills[0] = team_kills[1] = 0;
538         Endlevel_sequence = 0;
539         Player_is_dead = 0;
540         multi_leave_menu = 0;
541         multi_quit_game = 0;
542         Show_kill_list = 1;
543         game_disable_cheats();
544         Player_exploded = 0;
545         Dead_player_camera = 0;
546 }
547         
548 void
549 multi_make_player_ghost(int playernum)
550 {
551         object *obj;
552
553 //      Assert(playernum != Player_num);
554 //      Assert(playernum < MAX_NUM_NET_PLAYERS);
555
556         if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS) || (playernum < 0))
557         {
558                 Int3(); // Non-terminal, see Rob
559                 return;
560         }
561
562 //      if (Objects[Players[playernum].objnum].type != OBJ_PLAYER)
563 //              mprintf((1, "Warning: Player %d is not currently a player.\n", playernum));
564
565         obj = &Objects[Players[playernum].objnum];
566
567         obj->type = OBJ_GHOST;
568         obj->render_type = RT_NONE;
569         obj->movement_type = MT_NONE;
570         multi_reset_player_object(obj);
571
572         if (Game_mode & GM_MULTI_ROBOTS)
573                 multi_strip_robots(playernum);
574 }
575
576 void
577 multi_make_ghost_player(int playernum)
578 {
579         object *obj;
580
581 // Assert(playernum != Player_num);
582 // Assert(playernum < MAX_NUM_NET_PLAYERS);
583
584         if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS))
585         {
586                 Int3(); // Non-terminal, see rob
587                 return;
588         }
589
590 //      if(Objects[Players[playernum].objnum].type != OBJ_GHOST)
591 //              mprintf((1, "Warning: Player %d is not currently a ghost.\n", playernum));
592
593         obj = &Objects[Players[playernum].objnum];
594
595         obj->type = OBJ_PLAYER;
596         obj->movement_type = MT_PHYSICS;
597         multi_reset_player_object(obj);
598 }
599
600 int multi_get_kill_list(int *plist)
601 {
602         // Returns the number of active net players and their
603         // sorted order of kills
604         int i;
605         int n = 0;
606
607         for (i = 0; i < N_players; i++)
608 //              if (Players[sorted_kills[i]].connected)
609                         plist[n++] = sorted_kills[i];
610
611         if (n == 0)
612                 Int3(); // SEE ROB OR MATT
613
614 //      memcpy(plist, sorted_kills, N_players*sizeof(int));     
615
616         return(n);
617 }
618         
619 void
620 multi_sort_kill_list(void)
621 {
622         // Sort the kills list each time a new kill is added
623
624         int kills[MAX_NUM_NET_PLAYERS];
625         int i;
626         int changed = 1;
627         
628         for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
629         {
630 #ifndef SHAREWARE
631                 if (Game_mode & GM_MULTI_COOP)
632                         kills[i] = Players[i].score;
633                 else
634 #endif
635                 if (Show_kill_list==2)
636                  {
637                   if (Players[i].net_killed_total+Players[i].net_kills_total==0)
638                         kills[i]=-1;  // always draw the ones without any ratio last
639                   else
640                    kills[i]=(int)((float)((float)Players[i].net_kills_total/((float)Players[i].net_killed_total+(float)Players[i].net_kills_total))*100.0);             
641                  }      
642                 else                    
643                         kills[i] = Players[i].net_kills_total;
644         }
645
646         while (changed)
647         {
648                 changed = 0;
649                 for (i = 0; i < N_players-1; i++)
650                 {
651                         if (kills[sorted_kills[i]] < kills[sorted_kills[i+1]])
652                         {
653                                 changed = sorted_kills[i];
654                                 sorted_kills[i] = sorted_kills[i+1];
655                                 sorted_kills[i+1] = changed;
656                                 changed = 1;
657                         }
658                 }
659         }
660       mprintf((0, "Sorted kills %d %d.\n", sorted_kills[0], sorted_kills[1]));
661 }
662
663 extern object *obj_find_first_of_type (int);
664 char Multi_killed_yourself=0;
665
666 void multi_compute_kill(int killer, int killed)
667 {
668         // Figure out the results of a network kills and add it to the
669         // appropriate player's tally.
670
671         int killed_pnum, killed_type;
672         int killer_pnum, killer_type,killer_id;
673         int TheGoal;
674         char killed_name[(CALLSIGN_LEN*2)+4];
675         char killer_name[(CALLSIGN_LEN*2)+4];
676
677         kmatrix_kills_changed = 1;
678    Multi_killed_yourself=0;
679   
680         // Both object numbers are localized already!
681
682         mprintf((0, "compute_kill passed: object %d killed object %d.\n", killer, killed));
683
684         if ((killed < 0) || (killed > Highest_object_index) || (killer < 0) || (killer > Highest_object_index))
685         {
686                 Int3(); // See Rob, illegal value passed to compute_kill;
687                 return;
688         }
689
690         killed_type = Objects[killed].type;
691         killer_type = Objects[killer].type;
692    killer_id = Objects[killer].id;
693
694         if ((killed_type != OBJ_PLAYER) && (killed_type != OBJ_GHOST)) 
695         {
696                 Int3(); // compute_kill passed non-player object!
697                 return;
698         }
699
700         killed_pnum = Objects[killed].id;
701
702    PKilledFlags[killed_pnum]=1;
703
704         Assert ((killed_pnum >= 0) && (killed_pnum < N_players));
705
706         if (Game_mode & GM_TEAM)
707                 sprintf(killed_name, "%s (%s)", Players[killed_pnum].callsign, Netgame.team_name[get_team(killed_pnum)]);
708         else
709                 sprintf(killed_name, "%s", Players[killed_pnum].callsign);
710
711         if (Newdemo_state == ND_STATE_RECORDING)
712                 newdemo_record_multi_death(killed_pnum);
713
714         digi_play_sample( SOUND_HUD_KILL, F3_0 );
715
716    if (Control_center_destroyed)
717          Players[killed_pnum].connected=3;
718
719         if (killer_type == OBJ_CNTRLCEN)
720         {
721                 Players[killed_pnum].net_killed_total++;
722                 Players[killed_pnum].net_kills_total--;
723
724                 if (Newdemo_state == ND_STATE_RECORDING)
725                         newdemo_record_multi_kill(killed_pnum, -1);
726
727                 if (killed_pnum == Player_num)
728                  {
729                         HUD_init_message("%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_NONPLAY);
730                         multi_add_lifetime_killed ();
731                  }
732                 else
733                         HUD_init_message("%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_NONPLAY );
734                 return;         
735         }
736
737 #ifndef SHAREWARE
738         else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST))
739         {
740                 if (killer_id==PMINE_ID && killer_type!=OBJ_ROBOT)
741             {
742                   if (killed_pnum == Player_num) 
743                                 HUD_init_message("You were killed by a mine!");
744                         else
745                                 HUD_init_message("%s was killed by a mine!",killed_name);
746                  }
747                 else
748             {
749                   if (killed_pnum == Player_num) 
750                          {
751                                 HUD_init_message("%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_ROBOT);  
752                                 multi_add_lifetime_killed();
753                          }
754              else
755                                 HUD_init_message("%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_ROBOT );
756        }
757                 Players[killed_pnum].net_killed_total++;
758                 return;         
759         }
760 #else
761         else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST) && (killer_id!=PMINE_ID))
762         {
763                 Int3(); // Illegal killer type?
764                 return;
765         }
766   if (killer_id==PMINE_ID)
767    {
768     if (killed_pnum==Player_num)
769            HUD_init_message("You were killed by a mine!");
770     else
771                 HUD_init_message("%s was killed by a mine!",killed_name);
772          
773     Players[killed_pnum].net_killed_total++;
774         
775     return;
776    }
777 #endif
778
779         killer_pnum = Objects[killer].id;
780
781         if (Game_mode & GM_TEAM)
782                 sprintf(killer_name, "%s (%s)", Players[killer_pnum].callsign, Netgame.team_name[get_team(killer_pnum)]);
783         else
784                 sprintf(killer_name, "%s", Players[killer_pnum].callsign);
785
786         // Beyond this point, it was definitely a player-player kill situation
787
788         if ((killer_pnum < 0) || (killer_pnum >= N_players))
789                 Int3(); // See rob, tracking down bug with kill HUD messages
790         if ((killed_pnum < 0) || (killed_pnum >= N_players))
791                 Int3(); // See rob, tracking down bug with kill HUD messages
792
793         if (killer_pnum == killed_pnum)
794         {
795       if (!(Game_mode & GM_HOARD))
796                 {
797                         if (Game_mode & GM_TEAM)
798                                 team_kills[get_team(killed_pnum)] -= 1;
799
800                         Players[killed_pnum].net_killed_total += 1;
801                         Players[killed_pnum].net_kills_total -= 1;
802
803                         if (Newdemo_state == ND_STATE_RECORDING)
804                                 newdemo_record_multi_kill(killed_pnum, -1);
805                 }
806                 kill_matrix[killed_pnum][killed_pnum] += 1; // # of suicides
807
808                 if (killer_pnum == Player_num)
809                  {
810                         HUD_init_message("%s %s %s!", TXT_YOU, TXT_KILLED, TXT_YOURSELF );      
811                    Multi_killed_yourself=1;
812                         multi_add_lifetime_killed();
813                  }
814                 else
815                         HUD_init_message("%s %s", killed_name, TXT_SUICIDE);
816         }
817
818         else
819         {
820                 if (!(Game_mode & GM_HOARD))
821                 {
822                         if (Game_mode & GM_TEAM)
823                         {
824                                 if (get_team(killed_pnum) == get_team(killer_pnum))
825                                         team_kills[get_team(killed_pnum)] -= 1;
826                                 else
827                                         team_kills[get_team(killer_pnum)] += 1;
828                         }
829
830                         Players[killer_pnum].net_kills_total += 1;
831                         Players[killer_pnum].KillGoalCount+=1;
832
833                         if (Newdemo_state == ND_STATE_RECORDING)
834                                 newdemo_record_multi_kill(killer_pnum, 1);
835                 }
836                 else
837                 {
838                         if (Game_mode & GM_TEAM)
839                         {
840                                 if (killed_pnum==Player_num && get_team(killed_pnum) == get_team(killer_pnum))
841                                         Multi_killed_yourself=1;
842                         }
843                 }
844         
845                 kill_matrix[killer_pnum][killed_pnum] += 1;
846                 Players[killed_pnum].net_killed_total += 1;
847                 if (killer_pnum == Player_num) {
848                         HUD_init_message("%s %s %s!", TXT_YOU, TXT_KILLED, killed_name);
849                         multi_add_lifetime_kills();
850                         if ((Game_mode & GM_MULTI_COOP) && (Players[Player_num].score >= 1000))
851                                 add_points_to_score(-1000);
852                 }
853                 else if (killed_pnum == Player_num)
854                   {
855                         HUD_init_message("%s %s %s!", killer_name, TXT_KILLED, TXT_YOU);
856                         multi_add_lifetime_killed();
857                         if (Game_mode & GM_HOARD) 
858                          {
859                                 if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]>3)
860                                         multi_send_play_by_play (1,killer_pnum,Player_num);
861                                 else if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]>0)
862                                         multi_send_play_by_play (0,killer_pnum,Player_num);
863                          }
864                   }
865                 else
866                         HUD_init_message("%s %s %s!", killer_name, TXT_KILLED, killed_name);
867         }
868
869   TheGoal=Netgame.KillGoal*5;
870
871   if (Netgame.KillGoal>0)
872         {
873          if (Players[killer_pnum].KillGoalCount>=TheGoal)
874           {
875            if (killer_pnum==Player_num)
876             {
877              HUD_init_message("You reached the kill goal!");
878              Players[Player_num].shields=i2f(200);
879             }  
880            else
881             HUD_init_message ("%s has reached the kill goal!",Players[killer_pnum].callsign);
882
883            HUD_init_message ("The control center has been destroyed!");
884       net_destroy_controlcen (obj_find_first_of_type (OBJ_CNTRLCEN));
885           }
886         }
887
888         multi_sort_kill_list();
889         multi_show_player_list();
890    Players[killed_pnum].flags&=(~(PLAYER_FLAGS_HEADLIGHT_ON));  // clear the killed guys flags/headlights
891 }
892
893 void
894 multi_do_frame(void)
895 {
896      static int lasttime=0;
897      int i;
898
899         if (!(Game_mode & GM_MULTI))
900         {
901                 Int3();
902                 return;
903         }
904
905   if ((Game_mode & GM_NETWORK) && Netgame.PlayTimeAllowed && lasttime!=f2i (ThisLevelTime))
906         {
907          for (i=0;i<N_players;i++)
908           if (Players[i].connected)
909            {
910             if (i==Player_num)
911              {
912               multi_send_heartbeat();
913               lasttime=f2i(ThisLevelTime);
914              }
915             break;
916            }
917         }
918
919         multi_send_message(); // Send any waiting messages
920
921         if (!multi_in_menu)
922                 multi_leave_menu = 0;
923
924 #ifndef SHAREWARE
925         if (Game_mode & GM_MULTI_ROBOTS)
926         {
927                 multi_check_robot_timeout();
928         }
929 #endif  
930
931         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
932         {
933                 com_do_frame();
934         }
935         else
936         {
937                 network_do_frame(0, 1);
938         }
939
940         if (multi_quit_game && !multi_in_menu)
941         {
942                 multi_quit_game = 0;
943                 longjmp(LeaveGame, 0);
944         }
945 }
946                 
947 void
948 multi_send_data(char *buf, int len, int repeat)
949 {
950         Assert(len == message_length[(int)buf[0]]);
951         Assert(buf[0] <= MULTI_MAX_TYPE);
952 //      Assert(buf[0] >= 0);
953
954         if (Game_mode & GM_NETWORK)
955                 Assert(buf[0] > 0);
956
957         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
958                 com_send_data(buf, len, repeat);
959         else if (Game_mode & GM_NETWORK)
960                 network_send_data(buf, len, repeat);
961 }
962
963 void
964 multi_leave_game(void)
965 {
966
967 //      if (Function_mode != FMODE_GAME)
968 //              return;
969
970         if (!(Game_mode & GM_MULTI))
971                 return;
972
973         if (Game_mode & GM_NETWORK)
974         {
975                 mprintf((0, "Sending explosion message.\n"));
976
977                 Net_create_loc = 0;
978                 AdjustMineSpawn();
979                 multi_cap_objects();
980                 drop_player_eggs(ConsoleObject);
981                 multi_send_position(Players[Player_num].objnum);
982                 multi_send_player_explode(MULTI_PLAYER_DROP);
983         }
984
985         mprintf((1, "Sending leave game.\n"));
986         multi_send_quit(MULTI_QUIT);
987
988         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
989                 serial_leave_game();
990         if (Game_mode & GM_NETWORK)
991                 network_leave_game();
992
993         Game_mode |= GM_GAME_OVER;
994
995    if (Function_mode!=FMODE_EXIT)
996                 Function_mode = FMODE_MENU;
997
998 //      N_players = 0;
999
1000 //      change_playernum_to(0);
1001 //      Viewer = ConsoleObject = &Objects[0];
1002
1003 }
1004                 
1005 void 
1006 multi_show_player_list()
1007 {
1008         if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_COOP))
1009                 return;
1010
1011         if (Show_kill_list)
1012                 return;
1013
1014         Show_kill_list_timer = F1_0*5; // 5 second timer
1015         Show_kill_list = 1;
1016 }
1017
1018 int 
1019 multi_endlevel(int *secret)
1020 {
1021         int result = 0;
1022
1023         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
1024                 com_endlevel(secret);          // an opportunity to re-sync or whatever
1025         else if (Game_mode & GM_NETWORK)
1026                 result = network_endlevel(secret);
1027         
1028         return(result);         
1029 }
1030
1031 //
1032 // Part 2 : functions that act on network/serial messages and change the
1033 //          the state of the game in some way.
1034 //
1035
1036 #ifndef MACINTOSH
1037 //extern PORT *com_port;
1038 #endif
1039
1040 int 
1041 multi_menu_poll(void)
1042 {
1043         fix old_shields;
1044         int was_fuelcen_alive;
1045         int player_was_dead;
1046
1047         was_fuelcen_alive = Control_center_destroyed;
1048
1049         // Special polling function for in-game menus for multiplayer and serial
1050
1051         if (! ((Game_mode & GM_MULTI) && (Function_mode == FMODE_GAME)) )
1052                 return(0);
1053
1054         if (multi_leave_menu)
1055                 return(-1);
1056
1057         old_shields = Players[Player_num].shields;
1058         player_was_dead = Player_is_dead;
1059
1060         multi_in_menu++; // Track level of menu nesting
1061
1062         GameLoop( 0, 0 );                       
1063
1064         multi_in_menu--;
1065
1066 //      timer_delay(f1_0/10);   changed by allender for portability
1067         d_delay(100);                           // delay 100 milliseconds
1068                 
1069         if (Endlevel_sequence || (Control_center_destroyed && !was_fuelcen_alive) || (Player_is_dead != player_was_dead) || (Players[Player_num].shields < old_shields))
1070         {
1071                 multi_leave_menu = 1;
1072                 return(-1);
1073         }
1074         if ((Control_center_destroyed) && (Countdown_seconds_left < 10))
1075         {
1076                 multi_leave_menu = 1;
1077                 return(-1);
1078         }
1079
1080 #if 0
1081         if ((Game_mode & GM_MODEM) && (!GetCd(com_port)))
1082         {
1083                 multi_leave_menu = 1;
1084                 return(-1);
1085         }
1086 #endif
1087
1088         return(0);
1089 }
1090
1091 void
1092 multi_define_macro(int key)
1093 {
1094         if (!(Game_mode & GM_MULTI))
1095                 return;
1096
1097         key &= (~KEY_SHIFTED);
1098
1099         switch(key) 
1100         {
1101                 case KEY_F9:
1102                         multi_defining_message = 1; break;
1103                 case KEY_F10:
1104                         multi_defining_message = 2; break;
1105                 case KEY_F11:
1106                         multi_defining_message = 3; break;
1107                 case KEY_F12:
1108                         multi_defining_message = 4; break;
1109                 default:
1110                         Int3();
1111         }
1112
1113         if (multi_defining_message)     {
1114                 multi_message_index = 0;
1115                 Network_message[multi_message_index] = 0;
1116         }
1117
1118 }
1119
1120 char feedback_result[200];
1121
1122 void
1123 multi_message_feedback(void)
1124 {
1125         char *colon;
1126         int found = 0;
1127         int i;
1128
1129         if (!( ((colon = strrchr(Network_message, ':')) == NULL) || (colon-Network_message < 1) || (colon-Network_message > CALLSIGN_LEN) ))
1130         {
1131                 sprintf(feedback_result, "%s ", TXT_MESSAGE_SENT_TO);
1132                 if ((Game_mode & GM_TEAM) && (atoi(Network_message) > 0) && (atoi(Network_message) < 3))
1133                 {
1134                         sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[atoi(Network_message)-1]);
1135                         found = 1;
1136                 }
1137                 if (Game_mode & GM_TEAM)
1138                 {
1139                         for (i = 0; i < N_players; i++)
1140                         {
1141                                 if (!strnicmp(Netgame.team_name[i], Network_message, colon-Network_message))
1142                                 {
1143                                         if (found)
1144                                                 strcat(feedback_result, ", ");
1145                                         found++;
1146                                         if (!(found % 4))
1147                                                 strcat(feedback_result, "\n");
1148                                         sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[i]);
1149                                 }
1150                         }
1151                 }
1152                 for (i = 0; i < N_players; i++)
1153                 {
1154                         if ((!strnicmp(Players[i].callsign, Network_message, colon-Network_message)) && (i != Player_num) && (Players[i].connected))
1155                         {
1156                                 if (found)
1157                                         strcat(feedback_result, ", ");
1158                                 found++;
1159                                 if (!(found % 4))
1160                                         strcat(feedback_result, "\n");
1161                                 sprintf(feedback_result+strlen(feedback_result), "%s", Players[i].callsign);
1162                         }
1163                 }
1164                 if (!found)
1165                         strcat(feedback_result, TXT_NOBODY);
1166                 else
1167                         strcat(feedback_result, ".");
1168
1169                 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1170
1171                 Assert(strlen(feedback_result) < 200);
1172
1173                 HUD_init_message(feedback_result);
1174 //      sprintf (temp,"%s",colon);
1175 //         sprintf (Network_message,"%s",temp);
1176  
1177         }
1178 }
1179
1180 void
1181 multi_send_macro(int key)
1182 {
1183         if (! (Game_mode & GM_MULTI) )
1184                 return;
1185
1186         switch(key) 
1187         {
1188                 case KEY_F9:
1189                         key = 0; break;
1190                 case KEY_F10:
1191                         key = 1; break;
1192                 case KEY_F11:
1193                         key = 2; break;
1194                 case KEY_F12:
1195                         key = 3; break;
1196                 default:
1197                         Int3();
1198         }
1199
1200         if (!Network_message_macro[key][0])
1201         {
1202                 HUD_init_message(TXT_NO_MACRO);
1203                 return;
1204         }
1205
1206         strcpy(Network_message, Network_message_macro[key]);
1207         Network_message_reciever = 100;
1208
1209         HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
1210         multi_message_feedback();
1211 }
1212
1213
1214 void 
1215 multi_send_message_start()
1216 {
1217         if (Game_mode&GM_MULTI) {
1218                 multi_sending_message = 1;
1219                 multi_message_index = 0;
1220                 Network_message[multi_message_index] = 0;
1221         }
1222 }
1223
1224 extern fix StartingShields;
1225 fix PingLaunchTime,PingReturnTime;
1226
1227 extern void network_send_ping (ubyte);
1228 extern int network_who_is_master();
1229 extern char NameReturning;
1230 extern int force_cockpit_redraw;
1231
1232 void network_dump_appletalk_player(ubyte node, ushort net, ubyte socket, int why);
1233
1234 void multi_send_message_end()
1235 {
1236         char *mytempbuf;
1237    int i,t;
1238         
1239         Network_message_reciever = 100;
1240
1241         if (!strnicmp (Network_message,"!Names",6))
1242                 {
1243                  NameReturning=1-NameReturning;
1244                  HUD_init_message ("Name returning is now %s.",NameReturning?"active":"disabled");
1245                 }
1246         else if (!strnicmp (Network_message,"Handicap:",9))
1247                 {
1248                  mytempbuf=&Network_message[9];          
1249                  mprintf ((0,"Networkhandi=%s\n",mytempbuf));
1250                  StartingShields=atol (mytempbuf);
1251                  if (StartingShields<10)
1252                         StartingShields=10;
1253                  if (StartingShields>100)
1254                         {
1255                                 sprintf (Network_message,"%s has tried to cheat!",Players[Player_num].callsign);
1256                                 StartingShields=100;
1257                         }
1258                  else
1259                         sprintf (Network_message,"%s handicap is now %d",Players[Player_num].callsign,StartingShields);
1260
1261                  HUD_init_message ("Telling others of your handicap of %d!",StartingShields);
1262                  StartingShields=i2f(StartingShields);
1263                 }
1264         else if (!strnicmp (Network_message,"NoBombs",7))
1265          Netgame.DoSmartMine=0;
1266         else if (!strnicmp (Network_message,"Ping:",5))
1267          {
1268           if (Game_mode & GM_NETWORK)
1269                 {
1270                         int name_index=5;
1271                         if (strlen(Network_message) > 5)
1272                                 while (Network_message[name_index] == ' ')
1273                                         name_index++;
1274
1275                         if (strlen(Network_message)<=name_index)
1276                          {
1277                                 HUD_init_message ("You must specify a name to ping");
1278                                 return;
1279                          }
1280
1281                         for (i = 0; i < N_players; i++)
1282                 if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (i != Player_num) && (Players[i].connected))
1283             {
1284                                  PingLaunchTime=timer_get_fixed_seconds();
1285                                  network_send_ping (i);
1286                                  HUD_init_message("Pinging %s...",Players[i].callsign);
1287                             multi_message_index = 0;
1288                                  multi_sending_message = 0;
1289                                  return;
1290                            }
1291                 }
1292           else // Modem/Serial ping
1293            {
1294                          PingLaunchTime=timer_get_fixed_seconds();
1295                          multi_send_modem_ping ();
1296                          HUD_init_message("Pinging opponent...");
1297                     multi_message_index = 0;
1298                          multi_sending_message = 0;
1299                          return;
1300            }
1301          }                                                                                               
1302         else if (!strnicmp (Network_message,"move:",5))
1303          { 
1304                 mprintf ((0,"moving?\n"));
1305                 
1306           if ((Game_mode & GM_NETWORK) && (Game_mode & GM_TEAM))
1307                 {
1308                         int name_index=5;
1309                         if (strlen(Network_message) > 5)
1310                                 while (Network_message[name_index] == ' ')
1311                                         name_index++;
1312
1313                   if (!network_i_am_master())
1314                         {       
1315                          HUD_init_message ("Only %s can move players!",Players[network_who_is_master()].callsign);
1316                          return;
1317                         }
1318         
1319                         if (strlen(Network_message)<=name_index)
1320                          {
1321                                 HUD_init_message ("You must specify a name to move");
1322                                 return;
1323                          }
1324
1325                         for (i = 0; i < N_players; i++)
1326                 if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (Players[i].connected))
1327             {
1328                                  if ((Game_mode & GM_CAPTURE) && (Players[i].flags & PLAYER_FLAGS_FLAG))
1329                                   {
1330                                         HUD_init_message ("Can't move player because s/he has a flag!");
1331                                         return;
1332                                   }
1333                 
1334                                  if (Netgame.team_vector & (1<<i))
1335                                    Netgame.team_vector&=(~(1<<i));
1336                                  else
1337                                    Netgame.team_vector|=(1<<i);
1338         
1339                                  for (t=0;t<N_players;t++)
1340                                  if (Players[t].connected)
1341                                 multi_reset_object_texture (&Objects[Players[t].objnum]);
1342
1343                                  network_send_netgame_update ();        
1344                                  sprintf (Network_message,"%s has changed teams!",Players[i].callsign);
1345                                  if (i==Player_num)
1346                                         {
1347                                     HUD_init_message ("You have changed teams!");
1348                                          reset_cockpit();       
1349                                         }
1350                                  else
1351                                          HUD_init_message ("Moving %s to other team.",Players[i].callsign);
1352                                  break;
1353                            }
1354                   }    
1355          }                                                                                               
1356
1357         else if (!strnicmp (Network_message,"kick:",5) && (Game_mode & GM_NETWORK))
1358          {
1359                 int name_index=5;
1360                 if (strlen(Network_message) > 5)
1361                         while (Network_message[name_index] == ' ')
1362                                 name_index++;
1363
1364           if (!network_i_am_master())
1365                 {       
1366                  HUD_init_message ("Only %s can kick others out!",Players[network_who_is_master()].callsign);
1367             multi_message_index = 0;
1368             multi_sending_message = 0;
1369                  return;
1370                 }
1371                 if (strlen(Network_message)<=name_index)
1372                  {
1373                         HUD_init_message ("You must specify a name to kick");
1374                         multi_message_index = 0;
1375                         multi_sending_message = 0;
1376                         return;
1377                  }
1378
1379                 if (Network_message[name_index] == '#' && isdigit(Network_message[name_index+1])) {
1380                         int players[MAX_NUM_NET_PLAYERS];
1381                         int listpos = Network_message[name_index+1] - '0';
1382
1383                         mprintf ((0,"Trying to kick %d , show_kill_list=%d\n",listpos,Show_kill_list));
1384
1385                         if (Show_kill_list==1 || Show_kill_list==2) {
1386                                 if (listpos == 0 || listpos >= N_players) {
1387                                         HUD_init_message ("Invalid player number for kick.");
1388                                    multi_message_index = 0;
1389                                    multi_sending_message = 0;
1390                                         return;
1391                                 }
1392                                 multi_get_kill_list(players);
1393                                 i = players[listpos];
1394                                 if ((i != Player_num) && (Players[i].connected))
1395                                         goto kick_player;
1396                         }
1397                         else HUD_init_message ("You cannot use # kicking with in team display.");
1398                                 
1399
1400                     multi_message_index = 0;
1401                     multi_sending_message = 0;
1402                          return;
1403                 }
1404
1405
1406                 for (i = 0; i < N_players; i++)
1407                 if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (i != Player_num) && (Players[i].connected)) {
1408 kick_player:;
1409                         if (Network_game_type == IPX_GAME)
1410                                         network_dump_player(NetPlayers.players[i].network.ipx.server,NetPlayers.players[i].network.ipx.node, 7);
1411                                 else
1412                                         network_dump_appletalk_player(NetPlayers.players[i].network.appletalk.node,NetPlayers.players[i].network.appletalk.net, NetPlayers.players[i].network.appletalk.socket, 7);
1413                                 
1414                                 HUD_init_message("Dumping %s...",Players[i].callsign);
1415                                 multi_message_index = 0;
1416                                 multi_sending_message = 0;
1417                                 return;
1418                         }
1419          }                                                                                               
1420
1421    else
1422                 HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
1423
1424         multi_send_message();
1425         multi_message_feedback();               
1426
1427         multi_message_index = 0;
1428         multi_sending_message = 0;
1429 }
1430
1431 void multi_define_macro_end()
1432 {
1433         Assert( multi_defining_message > 0 );
1434
1435         strcpy( Network_message_macro[multi_defining_message-1], Network_message );
1436         write_player_file();
1437
1438         multi_message_index = 0;
1439         multi_defining_message = 0;
1440 }
1441
1442 void multi_message_input_sub( int key )
1443 {
1444         switch( key )   {
1445         case KEY_F8:
1446         case KEY_ESC:
1447                 multi_sending_message = 0;
1448                 multi_defining_message = 0;
1449                 game_flush_inputs();
1450                 break;
1451         case KEY_LEFT:
1452         case KEY_BACKSP:
1453         case KEY_PAD4:
1454                 if (multi_message_index > 0)
1455                         multi_message_index--;
1456                 Network_message[multi_message_index] = 0;
1457                 break;
1458         case KEY_ENTER:
1459                 if ( multi_sending_message )    
1460                         multi_send_message_end();
1461                 else if ( multi_defining_message )
1462                         multi_define_macro_end();
1463                 game_flush_inputs();
1464                 break;
1465         default:
1466                 if ( key > 0 )  {
1467                         int ascii = key_to_ascii(key);
1468                         if ((ascii < 255 ))     {
1469                                 if (multi_message_index < MAX_MESSAGE_LEN-2 )   {
1470                                         Network_message[multi_message_index++] = ascii;
1471                                         Network_message[multi_message_index] = 0;
1472                                 } else if ( multi_sending_message )     {               
1473                                         int i;
1474                                         char * ptext, * pcolon;
1475                                         ptext = NULL;
1476                                         Network_message[multi_message_index++] = ascii;
1477                                         Network_message[multi_message_index] = 0;
1478                                         for (i=multi_message_index-1; i>=0; i-- )       {
1479                                                 if ( Network_message[i]==32 )   {
1480                                                         ptext = &Network_message[i+1];
1481                                                         Network_message[i] = 0;
1482                                                         break;
1483                                                 }
1484                                         }
1485                                         multi_send_message_end();
1486                                         if ( ptext )    {
1487                                                 multi_sending_message = 1;
1488                                                 pcolon = strchr( Network_message, ':' );
1489                                                 if ( pcolon )
1490                                                         strcpy( pcolon+1, ptext );
1491                                                 else
1492                                                         strcpy( Network_message, ptext );
1493                                                 multi_message_index = strlen( Network_message );
1494                                         } 
1495                                 }
1496                         }
1497                 }
1498         }
1499 }
1500
1501 void 
1502 multi_send_message_dialog(void)
1503 {
1504         newmenu_item m[1];
1505         int choice;
1506
1507         if (!(Game_mode&GM_MULTI))
1508                 return;
1509
1510         Network_message[0] = 0;             // Get rid of old contents
1511
1512         m[0].type=NM_TYPE_INPUT; m[0].text = Network_message; m[0].text_len = MAX_MESSAGE_LEN-1;
1513         choice = newmenu_do( NULL, TXT_SEND_MESSAGE, 1, m, NULL );
1514
1515         if ((choice > -1) && (strlen(Network_message) > 0)) {
1516                 Network_message_reciever = 100;
1517                 HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
1518                 multi_message_feedback();               
1519         }
1520 }
1521
1522
1523
1524 void
1525 multi_do_death(int objnum)
1526 {
1527         // Do any miscellaneous stuff for a new network player after death
1528
1529         objnum = objnum;
1530
1531         if (!(Game_mode & GM_MULTI_COOP)) 
1532         {
1533                 mprintf((0, "Setting all keys for player %d.\n", Player_num));
1534                 Players[Player_num].flags |= (PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_GOLD_KEY);
1535         }
1536 }
1537
1538 void
1539 multi_do_fire(char *buf)
1540 {
1541         ubyte weapon;
1542         char pnum;
1543         byte flags;
1544    //static dum=0;
1545         
1546         // Act out the actual shooting
1547         pnum = buf[1];
1548
1549 #ifndef MACINTOSH
1550         weapon = (int)buf[2];   
1551 #else
1552         weapon = buf[2];
1553 #endif
1554         flags = buf[4];
1555         Network_laser_track = INTEL_SHORT(*(short *)(buf+6));
1556         
1557         Assert (pnum < N_players);
1558
1559         if (Objects[Players[(int)pnum].objnum].type == OBJ_GHOST)
1560                 multi_make_ghost_player(pnum);
1561
1562    //  mprintf((0,"multi_do_fire, weapon = %d\n",weapon));
1563
1564         if (weapon == FLARE_ADJUST)
1565                 Laser_player_fire( Objects+Players[(int)pnum].objnum, FLARE_ID, 6, 1, 0);
1566         else if (weapon >= MISSILE_ADJUST) {
1567                 int weapon_id,weapon_gun;
1568
1569                 weapon_id = Secondary_weapon_to_weapon_info[weapon-MISSILE_ADJUST];
1570                 weapon_gun = Secondary_weapon_to_gun_num[weapon-MISSILE_ADJUST] + (flags & 1);
1571                 mprintf((0,"missile id = %d, gun = %d\n",weapon_id,weapon_gun));
1572
1573                 if (weapon-MISSILE_ADJUST==GUIDED_INDEX)
1574                  {
1575                   mprintf ((0,"Missile is guided!!!\n"));
1576                   Multi_is_guided=1;
1577                  }
1578
1579                 Laser_player_fire( Objects+Players[(int)pnum].objnum, weapon_id, weapon_gun, 1, 0 );
1580         }
1581         else {
1582                 fix save_charge = Fusion_charge;
1583
1584                 if (weapon == FUSION_INDEX) {
1585                         Fusion_charge = flags << 12;
1586                         mprintf((0, "Fusion charge X%f.\n", f2fl(Fusion_charge)));
1587                 }
1588                 if (weapon == LASER_ID) {
1589                         if (flags & LASER_QUAD)
1590                                 Players[(int)pnum].flags |= PLAYER_FLAGS_QUAD_LASERS;
1591                         else
1592                                 Players[(int)pnum].flags &= ~PLAYER_FLAGS_QUAD_LASERS;
1593                 }
1594         
1595                 do_laser_firing(Players[(int)pnum].objnum, weapon, (int)buf[3], flags, (int)buf[5]);
1596         
1597                 if (weapon == FUSION_INDEX)
1598                         Fusion_charge = save_charge;
1599         }
1600 }
1601
1602 void 
1603 multi_do_message(char *buf)
1604 {
1605         char *colon;
1606    char *tilde,mesbuf[100];
1607    int tloc,t;
1608
1609         int loc = 2;
1610         
1611    if ((tilde=strchr (buf+loc,'$')))  // do that stupid name stuff
1612          {                                                                                      // why'd I put this in?  Probably for the
1613           tloc=tilde-(buf+loc);                         // same reason you can name your guidebot
1614           mprintf ((0,"Tloc=%d\n",tloc));
1615    
1616           if (tloc>0)
1617           strncpy (mesbuf,buf+loc,tloc);
1618           strcpy (mesbuf+tloc,Players[Player_num].callsign);
1619      strcpy (mesbuf+strlen(Players[Player_num].callsign)+tloc,buf+loc+tloc+1);
1620      strcpy (buf+loc,mesbuf);
1621          }      
1622  
1623         if (((colon = strrchr(buf+loc, ':')) == NULL) || (colon-(buf+loc) < 1) || (colon-(buf+loc) > CALLSIGN_LEN))
1624         {
1625            mesbuf[0] = 1;
1626                 mesbuf[1] = BM_XRGB(28, 0, 0);
1627                 strcpy(&mesbuf[2], Players[(int)buf[1]].callsign);
1628                 t = strlen(mesbuf);
1629                 mesbuf[t] = ':';
1630                 mesbuf[t+1] = 1;
1631                 mesbuf[t+2] = BM_XRGB(0, 31, 0);
1632                 mesbuf[t+3] = 0;
1633
1634                 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1635                 HUD_init_message("%s %s", mesbuf, buf+2);
1636         }
1637         else
1638         {
1639                 if ( (!strnicmp(Players[Player_num].callsign, buf+loc, colon-(buf+loc))) ||
1640                           ((Game_mode & GM_TEAM) && ( (get_team(Player_num) == atoi(buf+loc)-1) || !strnicmp(Netgame.team_name[get_team(Player_num)], buf+loc, colon-(buf+loc)))) )
1641                 {
1642                    mesbuf[0] = 1;
1643                         mesbuf[1] = BM_XRGB(0, 32, 32);
1644                         strcpy(&mesbuf[2], Players[(int)buf[1]].callsign);
1645                         t = strlen(mesbuf);
1646                         mesbuf[t] = ':';
1647                         mesbuf[t+1] = 1;
1648                         mesbuf[t+2] = BM_XRGB(0, 31, 0);
1649                         mesbuf[t+3] = 0;
1650
1651                         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);      
1652                 HUD_init_message("%s %s", mesbuf, colon+1);
1653                 }
1654         }
1655 }
1656
1657 void
1658 multi_do_position(char *buf)
1659 {               
1660 #ifdef MACINTOSH
1661         shortpos sp;
1662 #endif
1663
1664         // This routine does only player positions, mode game only
1665         //      mprintf((0, "Got position packet.\n"));
1666
1667         int pnum = (Player_num+1)%2;
1668
1669         Assert(&Objects[Players[pnum].objnum] != ConsoleObject);
1670
1671         if (Game_mode & GM_NETWORK)
1672          {
1673           Int3(); // Get Jason, what the hell are we doing here?
1674           return;
1675     }
1676          
1677
1678 #ifndef MACINTOSH
1679         extract_shortpos(&Objects[Players[pnum].objnum], (shortpos *)(buf+1),0);
1680 #else
1681         memcpy((ubyte *)(sp.bytemat), (ubyte *)(buf + 1), 9);
1682         memcpy((ubyte *)&(sp.xo), (ubyte *)(buf + 10), 14);
1683         extract_shortpos(&Objects[Players[pnum].objnum], &sp, 1);
1684 #endif
1685
1686         if (Objects[Players[pnum].objnum].movement_type == MT_PHYSICS)
1687                 set_thrust_from_velocity(&Objects[Players[pnum].objnum]);
1688 }
1689
1690 void
1691 multi_do_reappear(char *buf)
1692 {
1693         short objnum;
1694
1695         objnum = INTEL_SHORT(*(short *)(buf+1));
1696
1697         Assert(objnum >= 0);
1698 //      Assert(Players[Objects[objnum].id]].objnum == objnum);
1699         
1700 // mprintf((0, "Switching rendering back on for object %d.\n", objnum));
1701
1702         multi_make_ghost_player(Objects[objnum].id);
1703         create_player_appearance_effect(&Objects[objnum]);
1704         PKilledFlags[Objects[objnum].id]=0;
1705 }
1706         
1707 void
1708 multi_do_player_explode(char *buf)
1709 {
1710         // Only call this for players, not robots.  pnum is player number, not
1711         // Object number.
1712
1713         object *objp;
1714         int count;
1715         int pnum;
1716         int i;
1717         char remote_created;
1718
1719         pnum = buf[1];
1720
1721 #ifdef NDEBUG
1722         if ((pnum < 0) || (pnum >= N_players))
1723                 return;
1724 #else
1725         Assert(pnum >= 0);
1726         Assert(pnum < N_players);
1727 #endif
1728
1729 #ifdef NETWORK
1730         // If we are in the process of sending objects to a new player, reset that process
1731         if (Network_send_objects)
1732         {
1733                 mprintf((0, "Resetting object sync due to player explosion.\n"));
1734                 Network_send_objnum = -1;
1735         }
1736 #endif
1737
1738         // Stuff the Players structure to prepare for the explosion
1739         
1740         count = 2;
1741         Players[pnum].primary_weapon_flags = INTEL_SHORT(*(ushort *)(buf+count)); count += 2;
1742         Players[pnum].secondary_weapon_flags = INTEL_SHORT(*(ushort *)(buf+count)); count += 2;
1743         Players[pnum].laser_level = buf[count];                                                 count++;
1744         Players[pnum].secondary_ammo[HOMING_INDEX] = buf[count];                count++;
1745         Players[pnum].secondary_ammo[CONCUSSION_INDEX] = buf[count];count++;
1746         Players[pnum].secondary_ammo[SMART_INDEX] = buf[count];         count++;
1747         Players[pnum].secondary_ammo[MEGA_INDEX] = buf[count];          count++;
1748         Players[pnum].secondary_ammo[PROXIMITY_INDEX] = buf[count]; count++;
1749
1750         Players[pnum].secondary_ammo[SMISSILE1_INDEX] = buf[count]; count++;
1751         Players[pnum].secondary_ammo[GUIDED_INDEX]    = buf[count]; count++;
1752         Players[pnum].secondary_ammo[SMART_MINE_INDEX]= buf[count]; count++;
1753         Players[pnum].secondary_ammo[SMISSILE4_INDEX] = buf[count]; count++;
1754         Players[pnum].secondary_ammo[SMISSILE5_INDEX] = buf[count]; count++;
1755
1756         Players[pnum].primary_ammo[VULCAN_INDEX] = INTEL_SHORT(*(ushort *)(buf+count)); count += 2;
1757         Players[pnum].primary_ammo[GAUSS_INDEX] = INTEL_SHORT(*(ushort *)(buf+count)); count += 2;
1758         Players[pnum].flags = INTEL_INT(*(uint *)(buf+count));                     count += 4;
1759     
1760         multi_adjust_remote_cap (pnum);
1761
1762         objp = Objects+Players[pnum].objnum;
1763
1764 //      objp->phys_info.velocity = *(vms_vector *)(buf+16); // 12 bytes
1765 //      objp->pos = *(vms_vector *)(buf+28);                // 12 bytes
1766
1767         remote_created = buf[count++]; // How many did the other guy create?
1768
1769         Net_create_loc = 0;
1770
1771         drop_player_eggs(objp);
1772  
1773         // Create mapping from remote to local numbering system
1774
1775         mprintf((0, "I Created %d powerups, remote created %d.\n", Net_create_loc, remote_created));
1776
1777         // We now handle this situation gracefully, Int3 not required
1778         //      if (Net_create_loc != remote_created)
1779         //              Int3(); // Probably out of object array space, see Rob
1780
1781         for (i = 0; i < remote_created; i++)
1782         {
1783                 short s;
1784                 
1785                 s = INTEL_SHORT(*(short *)(buf+count));
1786                 
1787                 if ((i < Net_create_loc) && (s > 0))
1788                         map_objnum_local_to_remote((short)Net_create_objnums[i], s, pnum);          
1789                 else if (*(short *)(buf+count) <= 0)
1790                 {
1791                         mprintf((0, "WARNING: Remote created object has non-valid number %d (player %d)", s, pnum));
1792                 }
1793                 else 
1794                 {
1795                         mprintf((0, "WARNING: Could not create all powerups created by player %d.\n", pnum));
1796                 }
1797 //              Assert(s > 0);
1798                 count += 2;
1799         }
1800         for (i = remote_created; i < Net_create_loc; i++) {
1801                 mprintf((0, "WARNING: I Created more powerups than player %d, deleting.\n", pnum));
1802                 Objects[Net_create_objnums[i]].flags |= OF_SHOULD_BE_DEAD;
1803         }
1804
1805         if (buf[0] == MULTI_PLAYER_EXPLODE)
1806         {
1807                 explode_badass_player(objp);
1808                 
1809                 objp->flags &= ~OF_SHOULD_BE_DEAD;              //don't really kill player
1810                 multi_make_player_ghost(pnum);
1811         }
1812         else
1813         {
1814                 create_player_appearance_effect(objp);
1815         }
1816
1817         Players[pnum].flags &= ~(PLAYER_FLAGS_CLOAKED | PLAYER_FLAGS_INVULNERABLE | PLAYER_FLAGS_FLAG);
1818         Players[pnum].cloak_time = 0;
1819 }
1820
1821 void
1822 multi_do_kill(char *buf)
1823 {
1824         int killer, killed;
1825         int count = 1;
1826         int pnum;
1827         
1828         pnum = (int)(buf[count]);
1829         if ((pnum < 0) || (pnum >= N_players))
1830         {
1831                 Int3(); // Invalid player number killed
1832                 return;
1833         }
1834         killed = Players[pnum].objnum;                  
1835         count += 1;
1836
1837         killer = INTEL_SHORT(*(short *)(buf+count)); 
1838         if (killer > 0)
1839                 killer = objnum_remote_to_local(killer, (byte)buf[count+2]);
1840
1841 #ifdef SHAREWARE
1842         if ((Objects[killed].type != OBJ_PLAYER) && (Objects[killed].type != OBJ_GHOST))
1843         {
1844                 Int3();
1845                 mprintf( (1, "SOFT INT3: MULTI.C Non-player object %d of type %d killed! (JOHN)\n", killed, Objects[killed].type ));
1846                 return;
1847         }
1848 #endif          
1849
1850         multi_compute_kill(killer, killed);
1851
1852 }
1853
1854
1855 //      Changed by MK on 10/20/94 to send NULL as object to net_destroy_controlcen if it got -1
1856 // which means not a controlcen object, but contained in another object
1857 void multi_do_controlcen_destroy(char *buf)
1858 {
1859         byte who;
1860         short objnum;
1861
1862         objnum = INTEL_SHORT(*(short *)(buf+1));
1863         who = buf[3];
1864
1865         if (Control_center_destroyed != 1) 
1866         {
1867                 if ((who < N_players) && (who != Player_num)) {
1868                         HUD_init_message("%s %s", Players[who].callsign, TXT_HAS_DEST_CONTROL);
1869                 }
1870                 else if (who == Player_num)
1871                         HUD_init_message(TXT_YOU_DEST_CONTROL);
1872                 else 
1873                         HUD_init_message(TXT_CONTROL_DESTROYED);
1874
1875                 if (objnum != -1)
1876                         net_destroy_controlcen(Objects+objnum);
1877                 else
1878                         net_destroy_controlcen(NULL);
1879         }
1880 }
1881
1882 void 
1883 multi_do_escape(char *buf)
1884 {
1885         int objnum;
1886
1887         objnum = Players[(int)buf[1]].objnum;
1888
1889         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1890         digi_kill_sound_linked_to_object (objnum);
1891         
1892         if (buf[2] == 0)
1893         {
1894                 HUD_init_message("%s %s", Players[(int)buf[1]].callsign, TXT_HAS_ESCAPED);
1895                 if (Game_mode & GM_NETWORK)
1896                         Players[(int)buf[1]].connected = CONNECT_ESCAPE_TUNNEL;
1897                 if (!multi_goto_secret)
1898                         multi_goto_secret = 2;
1899         }
1900         else if (buf[2] == 1) 
1901         {
1902                 HUD_init_message("%s %s", Players[(int)buf[1]].callsign, TXT_HAS_FOUND_SECRET);
1903                 if (Game_mode & GM_NETWORK)
1904                         Players[(int)buf[1]].connected = CONNECT_FOUND_SECRET;
1905                 if (!multi_goto_secret)
1906                         multi_goto_secret = 1;
1907         }
1908         create_player_appearance_effect(&Objects[objnum]);
1909         multi_make_player_ghost(buf[1]);
1910 }
1911
1912 void
1913 multi_do_remobj(char *buf)
1914 {
1915         short objnum; // which object to remove
1916         short local_objnum;
1917         byte obj_owner; // which remote list is it entered in
1918
1919         objnum = INTEL_SHORT(*(short *)(buf+1));
1920         obj_owner = buf[3];
1921
1922         Assert(objnum >= 0);
1923
1924         if (objnum < 1)
1925                 return;
1926
1927         local_objnum = objnum_remote_to_local(objnum, obj_owner); // translate to local objnum
1928
1929 //      mprintf((0, "multi_do_remobj: %d owner %d = %d.\n", objnum, obj_owner, local_objnum));
1930
1931         if (local_objnum < 0)
1932         {
1933                 mprintf((1, "multi_do_remobj: Could not remove referenced object.\n"));
1934                 return;
1935         }
1936
1937         if ((Objects[local_objnum].type != OBJ_POWERUP) && (Objects[local_objnum].type != OBJ_HOSTAGE))
1938         {
1939                 mprintf((1, "multi_get_remobj: tried to remove invalid type %d.\n", Objects[local_objnum].type));
1940                 return;
1941         }
1942         
1943         if (Network_send_objects && network_objnum_is_past(local_objnum))
1944         {
1945                 mprintf((0, "Resetting object sync due to object removal.\n"));
1946                 Network_send_objnum = -1;
1947         }
1948         if (Objects[local_objnum].type==OBJ_POWERUP)
1949          if (Game_mode & GM_NETWORK)    
1950                 {
1951                  if (PowerupsInMine[Objects[local_objnum].id]>0)
1952                         PowerupsInMine[Objects[local_objnum].id]--;
1953
1954                   if (multi_powerup_is_4pack (Objects[local_objnum].id))
1955                          {
1956                                 mprintf ((0,"Hey babe! Doing that wacky 4 pack stuff."));
1957                 
1958                                 if (PowerupsInMine[Objects[local_objnum].id-1]-4<0)
1959                                         PowerupsInMine[Objects[local_objnum].id-1]=0;
1960                                 else
1961                                         PowerupsInMine[Objects[local_objnum].id-1]-=4;
1962                          }
1963                 
1964                  mprintf ((0,"Decrementing powerups! %d\n",PowerupsInMine[Objects[local_objnum].id]));
1965                 }
1966
1967         Objects[local_objnum].flags |= OF_SHOULD_BE_DEAD; // quick and painless
1968         
1969 }
1970
1971 void
1972 multi_do_quit(char *buf)
1973 {
1974         
1975         if (Game_mode & GM_NETWORK)
1976         {
1977                 int i, n = 0;
1978
1979                 digi_play_sample( SOUND_HUD_MESSAGE, F1_0 );
1980
1981                 HUD_init_message( "%s %s", Players[(int)buf[1]].callsign, TXT_HAS_LEFT_THE_GAME);
1982                 
1983                 network_disconnect_player(buf[1]);
1984
1985                 if (multi_in_menu)
1986                         return;
1987
1988                 for (i = 0; i < N_players; i++)
1989                         if (Players[i].connected) n++;
1990                 if (n == 1)
1991                 {
1992                         nm_messagebox(NULL, 1, TXT_OK, TXT_YOU_ARE_ONLY);
1993                 }
1994         }
1995
1996         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
1997         {
1998                 Function_mode = FMODE_MENU;
1999                 multi_quit_game = 1;
2000                 multi_leave_menu = 1;
2001                 nm_messagebox(NULL, 1, TXT_OK, TXT_OPPONENT_LEFT);
2002                 Function_mode = FMODE_GAME;
2003                 multi_reset_stuff();
2004         }
2005         return;
2006 }
2007
2008 void
2009 multi_do_cloak(char *buf)
2010 {
2011         int pnum;
2012
2013         pnum = (int)(buf[1]);
2014
2015         Assert(pnum < N_players);
2016         
2017         mprintf((0, "Cloaking player %d\n", pnum));
2018
2019         Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2020         Players[pnum].cloak_time = GameTime;
2021         ai_do_cloak_stuff();
2022
2023 #ifndef SHAREWARE
2024         if (Game_mode & GM_MULTI_ROBOTS)
2025                 multi_strip_robots(pnum);
2026 #endif
2027
2028         if (Newdemo_state == ND_STATE_RECORDING)
2029                 newdemo_record_multi_cloak(pnum);
2030 }
2031         
2032 void
2033 multi_do_decloak(char *buf)
2034 {
2035         int pnum;
2036
2037         pnum = (int)(buf[1]);
2038
2039         if (Newdemo_state == ND_STATE_RECORDING)
2040                 newdemo_record_multi_decloak(pnum);
2041
2042 }
2043         
2044 void
2045 multi_do_door_open(char *buf)
2046 {
2047         int segnum;
2048         byte side;
2049         segment *seg;
2050         wall *w;
2051         ubyte flag;
2052
2053         segnum = INTEL_SHORT(*(short *)(buf+1));
2054         side = buf[3];
2055         flag= buf[4];
2056
2057 //      mprintf((0, "Opening door on side %d of segment # %d.\n", side, segnum));
2058
2059         if ((segnum < 0) || (segnum > Highest_segment_index) || (side < 0) || (side > 5))
2060         {
2061                 Int3();
2062                 return;
2063         }
2064
2065         seg = &Segments[segnum];
2066
2067         if (seg->sides[side].wall_num == -1) {  //Opening door on illegal wall
2068                 Int3();
2069                 return;
2070         }
2071
2072         w = &Walls[seg->sides[side].wall_num];
2073
2074         if (w->type == WALL_BLASTABLE)
2075         {
2076                 if (!(w->flags & WALL_BLASTED))
2077                 {
2078                         mprintf((0, "Blasting wall by remote command.\n"));
2079                         wall_destroy(seg, side);
2080                 }
2081                 return;
2082         }
2083         else if (w->state != WALL_DOOR_OPENING)
2084         {
2085                 wall_open_door(seg, side);
2086       w->flags=flag;
2087         }
2088         else
2089     w->flags=flag;
2090
2091 //      else
2092 //              mprintf((0, "Door already opening!\n"));
2093 }
2094
2095 void
2096 multi_do_create_explosion(char *buf)
2097 {
2098         int pnum;
2099         int count = 1;
2100
2101         pnum = buf[count++];
2102
2103 //      mprintf((0, "Creating small fireball.\n"));
2104         create_small_fireball_on_object(&Objects[Players[pnum].objnum], F1_0, 1);
2105 }
2106         
2107 void
2108 multi_do_controlcen_fire(char *buf)
2109 {
2110         vms_vector to_target;
2111         char gun_num;
2112         short objnum;
2113         int count = 1;
2114
2115         memcpy(&to_target, buf+count, 12);      count += 12;
2116         #ifdef MACINTOSH        // swap the vector to_target
2117         to_target.x = (fix)INTEL_INT((int)to_target.x);
2118         to_target.y = (fix)INTEL_INT((int)to_target.y);
2119         to_target.z = (fix)INTEL_INT((int)to_target.z);
2120         #endif
2121         gun_num = buf[count];                                   count += 1;
2122         objnum = INTEL_SHORT(*(short *)(buf+count));         count += 2;
2123
2124         Laser_create_new_easy(&to_target, &Gun_pos[(int)gun_num], objnum, CONTROLCEN_WEAPON_NUM, 1);
2125 }
2126
2127 void
2128 multi_do_create_powerup(char *buf)
2129 {
2130         short segnum;
2131         short objnum;
2132         int my_objnum;
2133         char pnum;
2134         int count = 1;
2135         vms_vector new_pos;
2136         char powerup_type;
2137
2138         if (Endlevel_sequence || Control_center_destroyed)
2139                 return;
2140
2141         pnum = buf[count++];
2142         powerup_type = buf[count++];
2143         segnum = INTEL_SHORT(*(short *)(buf+count)); count+=2;
2144         objnum = INTEL_SHORT(*(short *)(buf+count)); count+=2;
2145
2146         if ((segnum < 0) || (segnum > Highest_segment_index)) {
2147                 Int3();
2148                 return;
2149         }
2150         
2151         new_pos = *(vms_vector *)(buf+count); count+=sizeof(vms_vector);
2152 #ifdef MACINTOSH
2153         new_pos.x = (fix)SWAPINT((int)new_pos.x);
2154         new_pos.y = (fix)SWAPINT((int)new_pos.y);
2155         new_pos.z = (fix)SWAPINT((int)new_pos.z);
2156 #endif
2157
2158         Net_create_loc = 0;
2159         my_objnum = call_object_create_egg(&Objects[Players[(int)pnum].objnum], 1, OBJ_POWERUP, powerup_type);
2160
2161         if (my_objnum < 0) {
2162                 mprintf((0, "Could not create new powerup!\n"));
2163                 return;
2164         }
2165
2166         if (Network_send_objects && network_objnum_is_past(my_objnum))
2167         {
2168                 mprintf((0, "Resetting object sync due to powerup creation.\n"));
2169                 Network_send_objnum = -1;
2170         }
2171
2172         Objects[my_objnum].pos = new_pos;
2173
2174         vm_vec_zero(&Objects[my_objnum].mtype.phys_info.velocity);
2175         
2176         obj_relink(my_objnum, segnum);
2177
2178         map_objnum_local_to_remote(my_objnum, objnum, pnum);
2179
2180         object_create_explosion(segnum, &new_pos, i2f(5), VCLIP_POWERUP_DISAPPEARANCE);
2181         mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));
2182
2183    if (Game_mode & GM_NETWORK)
2184                 PowerupsInMine[(int)powerup_type]++;
2185 }               
2186
2187 void
2188 multi_do_play_sound(char *buf)
2189 {
2190         int pnum = (int)(buf[1]);
2191         int sound_num = (int)(buf[2]);
2192         fix volume = (int)(buf[3]) << 12;
2193
2194         if (!Players[pnum].connected)
2195                 return;
2196
2197         Assert(Players[pnum].objnum >= 0);
2198         Assert(Players[pnum].objnum <= Highest_object_index);
2199
2200         digi_link_sound_to_object( sound_num, Players[pnum].objnum, 0, volume); 
2201 }
2202
2203 void
2204 multi_do_score(char *buf)
2205 {
2206         int pnum = (int)(buf[1]);
2207         
2208         if ((pnum < 0) || (pnum >= N_players))
2209         {
2210                 Int3(); // Non-terminal, see rob
2211                 return;
2212         }
2213
2214         if (Newdemo_state == ND_STATE_RECORDING)
2215                 newdemo_record_multi_score(pnum, INTEL_INT(*(int *)(buf+2)) );
2216
2217         Players[pnum].score = INTEL_INT(*(int *)(buf+2));
2218
2219         multi_sort_kill_list();
2220 }
2221
2222 void
2223 multi_do_trigger(char *buf)
2224 {
2225         int pnum = (int)(buf[1]);
2226         int trigger = (int)(buf[2]);
2227    
2228    mprintf ((0,"MULTI doing trigger!\n"));
2229
2230         if ((pnum < 0) || (pnum >= N_players) || (pnum == Player_num))
2231         {
2232                 Int3(); // Got trigger from illegal playernum
2233                 return;
2234         }
2235         if ((trigger < 0) || (trigger >= Num_triggers))
2236         {
2237                 Int3(); // Illegal trigger number in multiplayer
2238                 return;
2239         }
2240         check_trigger_sub(trigger, pnum,0);
2241 }
2242
2243 void multi_do_drop_marker (char *buf)
2244  {
2245   int i;
2246   int pnum=(int)(buf[1]);
2247   int mesnum=(int)(buf[2]);
2248   vms_vector position;
2249
2250   if (pnum==Player_num)  // my marker? don't set it down cuz it might screw up the orientation
2251    return;
2252
2253   position.x=(fix)INTEL_INT(*(int *)(buf+3));
2254   position.y=(fix)INTEL_INT(*(int *)(buf+7));
2255   position.z=(fix)INTEL_INT(*(int *)(buf+11));
2256
2257   for (i=0;i<40;i++)
2258    MarkerMessage[(pnum*2)+mesnum][i]=buf[15+i];
2259
2260   MarkerPoint[(pnum*2)+mesnum]=position;
2261
2262   if (MarkerObject[(pnum*2)+mesnum] !=-1 && Objects[MarkerObject[(pnum*2)+mesnum]].type!=OBJ_NONE && MarkerObject[(pnum*2)+mesnum] !=0)
2263                 obj_delete(MarkerObject[(pnum*2)+mesnum]);
2264
2265   MarkerObject[(pnum*2)+mesnum] = drop_marker_object(&position,Objects[Players[Player_num].objnum].segnum,&Objects[Players[Player_num].objnum].orient,(pnum*2)+mesnum);
2266   strcpy (MarkerOwner[(pnum*2)+mesnum],Players[pnum].callsign);
2267   mprintf ((0,"Dropped player %d message: %s\n",pnum,MarkerMessage[(pnum*2)+mesnum]));
2268  }
2269
2270
2271 void multi_do_hostage_door_status(char *buf)
2272 {
2273         // Update hit point status of a door
2274
2275         int count = 1;
2276         int wallnum; 
2277         fix hps;
2278
2279         wallnum = INTEL_SHORT(*(short *)(buf+count));                count += 2;
2280         hps = (fix)INTEL_INT(*(int *)(buf+count));              count += 4;
2281
2282         if ((wallnum < 0) || (wallnum > Num_walls) || (hps < 0) || (Walls[wallnum].type != WALL_BLASTABLE))
2283         {
2284                 Int3(); // Non-terminal, see Rob
2285                 return;
2286         }
2287
2288 //      mprintf((0, "Damaging wall number %d to %f points.\n", wallnum, f2fl(hps)));
2289
2290         if (hps < Walls[wallnum].hps)
2291                 wall_damage(&Segments[Walls[wallnum].segnum], Walls[wallnum].sidenum, Walls[wallnum].hps - hps);
2292 }
2293
2294 void multi_do_save_game(char *buf)
2295 {
2296         int count = 1;
2297         ubyte slot;
2298         uint id;
2299         char desc[25];
2300
2301         slot = *(ubyte *)(buf+count);           count += 1;
2302         id = INTEL_INT(*(uint *)(buf+count));              count += 4;
2303         memcpy( desc, &buf[count], 20 );        count += 20;
2304
2305         multi_save_game( slot, id, desc );
2306 }
2307
2308 void multi_do_restore_game(char *buf)
2309 {
2310         int count = 1;
2311         ubyte slot;
2312         uint id;
2313
2314         slot = *(ubyte *)(buf+count);           count += 1;
2315         id = INTEL_INT(*(uint *)(buf+count));              count += 4;
2316
2317         multi_restore_game( slot, id );
2318 }
2319
2320  
2321 void multi_do_req_player(char *buf)
2322 {
2323         netplayer_stats ps;
2324         ubyte player_n;
2325         // Send my netplayer_stats to everyone!
2326         player_n = *(ubyte *)(buf+1);
2327         if ( (player_n == Player_num) || (player_n == 255)  )   {
2328                 extract_netplayer_stats( &ps, &Players[Player_num] );
2329                 ps.Player_num = Player_num;
2330                 ps.message_type = MULTI_SEND_PLAYER;            // SET
2331                 multi_send_data((ubyte*)&ps, sizeof(netplayer_stats), 0);
2332         }
2333 }
2334
2335 void multi_do_send_player(char *buf)
2336 {
2337         // Got a player packet from someone!!!
2338         netplayer_stats * p;
2339         p = (netplayer_stats *)buf;     
2340
2341         Assert( p->Player_num <= N_players );
2342
2343         mprintf(( 0, "Got netplayer_stats for player %d (I'm %d)\n", p->Player_num, Player_num ));
2344         mprintf(( 0, "Their shields are: %d\n", f2i(p->shields) ));
2345
2346         use_netplayer_stats( &Players[p->Player_num], p );
2347 }
2348
2349 void
2350 multi_reset_stuff(void)
2351 {
2352         // A generic, emergency function to solve problems that crop up
2353         // when a player exits quick-out from the game because of a 
2354         // serial connection loss.  Fixes several weird bugs!
2355
2356         dead_player_end();
2357
2358         Players[Player_num].homing_object_dist = -F1_0; // Turn off homing sound.
2359
2360         Dead_player_camera = 0;
2361         Endlevel_sequence = 0;
2362         reset_rear_view();
2363 }
2364
2365 void
2366 multi_reset_player_object(object *objp)
2367 {
2368         int i;
2369
2370         //Init physics for a non-console player
2371
2372         Assert(objp >= Objects);
2373         Assert(objp <= Objects+Highest_object_index);
2374         Assert((objp->type == OBJ_PLAYER) || (objp->type == OBJ_GHOST));
2375
2376         vm_vec_zero(&objp->mtype.phys_info.velocity);
2377         vm_vec_zero(&objp->mtype.phys_info.thrust);
2378         vm_vec_zero(&objp->mtype.phys_info.rotvel);
2379         vm_vec_zero(&objp->mtype.phys_info.rotthrust);
2380         objp->mtype.phys_info.brakes = objp->mtype.phys_info.turnroll = 0;
2381         objp->mtype.phys_info.mass = Player_ship->mass;
2382         objp->mtype.phys_info.drag = Player_ship->drag;
2383 //      objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE | PF_USES_THRUST);
2384         objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE);
2385
2386         //Init render info
2387
2388         objp->render_type = RT_POLYOBJ;
2389         objp->rtype.pobj_info.model_num = Player_ship->model_num;               //what model is this?
2390         objp->rtype.pobj_info.subobj_flags = 0;         //zero the flags
2391         for (i=0;i<MAX_SUBMODELS;i++)
2392                 vm_angvec_zero(&objp->rtype.pobj_info.anim_angles[i]);
2393
2394         //reset textures for this, if not player 0
2395
2396         multi_reset_object_texture (objp);      
2397         
2398         // Clear misc
2399
2400         objp->flags = 0;
2401         
2402         if (objp->type == OBJ_GHOST)
2403                 objp->render_type = RT_NONE;
2404
2405 }
2406
2407 void multi_reset_object_texture (object *objp)
2408  {
2409         int id,i;
2410
2411         if (Game_mode & GM_TEAM)
2412                 id = get_team(objp->id);
2413         else
2414                 id = objp->id;
2415
2416         if (id == 0)
2417                 objp->rtype.pobj_info.alt_textures=0;
2418         else {
2419                 Assert(N_PLAYER_SHIP_TEXTURES == Polygon_models[objp->rtype.pobj_info.model_num].n_textures);
2420
2421                 for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
2422                         multi_player_textures[id-1][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[objp->rtype.pobj_info.model_num].first_texture+i]];
2423
2424                 multi_player_textures[id-1][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2]];
2425                 multi_player_textures[id-1][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2+1]];
2426
2427                 objp->rtype.pobj_info.alt_textures = id;
2428    }
2429  }
2430
2431
2432
2433
2434 extern int TTRecv[];
2435 extern FILE *RecieveLogFile;
2436
2437 void
2438 multi_process_bigdata(char *buf, int len)
2439 {
2440         // Takes a bunch of messages, check them for validity,
2441         // and pass them to multi_process_data. 
2442
2443         int type, sub_len, bytes_processed = 0;
2444
2445         while( bytes_processed < len )  {
2446                 type = buf[bytes_processed];
2447
2448                 if ( (type<0) || (type>MULTI_MAX_TYPE)) {
2449                         mprintf( (1, "multi_process_bigdata: Invalid packet type %d!\n", type ));
2450                         return;
2451                 }
2452                 sub_len = message_length[type];
2453
2454                 Assert(sub_len > 0);
2455
2456                 if ( (bytes_processed+sub_len) > len )  {
2457                         mprintf( (1, "multi_process_bigdata: packet type %d too short (%d>%d)!\n", type, (bytes_processed+sub_len), len ));
2458                         Int3();
2459                         return;
2460                 }
2461
2462                 multi_process_data(&buf[bytes_processed], sub_len);
2463                 bytes_processed += sub_len;
2464         }
2465 }
2466
2467 //
2468 // Part 2 : Functions that send communication messages to inform the other
2469 //          players of something we did.
2470 //
2471
2472 void
2473 multi_send_fire(void)
2474 {
2475         if (!Network_laser_fired)
2476                 return;
2477
2478         multibuf[0] = (char)MULTI_FIRE;
2479         multibuf[1] = (char)Player_num;
2480         multibuf[2] = (char)Network_laser_gun;
2481         multibuf[3] = (char)Network_laser_level;
2482         multibuf[4] = (char)Network_laser_flags;
2483         multibuf[5] = (char)Network_laser_fired;
2484        
2485         *(short *)(multibuf+6) = INTEL_SHORT(Network_laser_track);
2486
2487         multi_send_data(multibuf, 8, 0);
2488         
2489         Network_laser_fired = 0;
2490 }
2491
2492 void
2493 multi_send_destroy_controlcen(int objnum, int player)
2494 {
2495         if (player == Player_num)
2496                 HUD_init_message(TXT_YOU_DEST_CONTROL);
2497         else if ((player > 0) && (player < N_players))
2498                 HUD_init_message("%s %s", Players[player].callsign, TXT_HAS_DEST_CONTROL);
2499         else
2500                 HUD_init_message(TXT_CONTROL_DESTROYED);
2501
2502         multibuf[0] = (char)MULTI_CONTROLCEN;
2503         *(ushort *)(multibuf+1) = INTEL_SHORT(objnum);
2504         multibuf[3] = player;
2505    multi_send_data(multibuf, 4, 2);
2506 }
2507
2508 void multi_send_drop_marker (int player,vms_vector position,char messagenum,char text[])
2509  {
2510   int i;
2511
2512    if (player<N_players)
2513      {
2514        mprintf ((0,"Sending MARKER drop!\n"));
2515        multibuf[0]=(char)MULTI_MARKER;
2516        multibuf[1]=(char)player;
2517        multibuf[2]=messagenum;
2518        *(fix *)(multibuf+3)=INTEL_INT(position.x);
2519        *(fix *)(multibuf+7)=INTEL_INT(position.y);
2520        *(fix *)(multibuf+11)=INTEL_INT(position.z);
2521        for (i=0;i<40;i++)
2522              multibuf[15+i]=text[i];
2523      }
2524    multi_send_data(multibuf, 55, 1);
2525  }
2526                            
2527 void 
2528 multi_send_endlevel_start(int secret)
2529 {
2530         multibuf[0] = (char)MULTI_ENDLEVEL_START;
2531         multibuf[1] = Player_num;
2532         multibuf[2] = (char)secret;
2533         
2534         if ((secret) && !multi_goto_secret)
2535                 multi_goto_secret = 1;
2536         else if (!multi_goto_secret)
2537                 multi_goto_secret = 2;
2538
2539         multi_send_data(multibuf, 3, 1);
2540         if (Game_mode & GM_NETWORK)
2541         {
2542                 Players[Player_num].connected = 5;
2543                 network_send_endlevel_packet();
2544         }
2545 }
2546
2547 void
2548 multi_send_player_explode(char type)
2549 {
2550         int count = 0;
2551         int i;
2552
2553         Assert( (type == MULTI_PLAYER_DROP) || (type == MULTI_PLAYER_EXPLODE) );
2554
2555         multi_send_position(Players[Player_num].objnum);
2556
2557         if (Network_send_objects)
2558         {
2559                 mprintf((0, "Resetting object sync due to player explosion.\n"));
2560                 Network_send_objnum = -1;
2561         }
2562         
2563         multibuf[count++] = type;
2564         multibuf[count++] = Player_num;
2565   
2566         *(ushort *)(multibuf+count) = INTEL_SHORT((ushort)Players[Player_num].primary_weapon_flags);
2567         count += 2;
2568         *(ushort *)(multibuf+count) = INTEL_SHORT((ushort)Players[Player_num].secondary_weapon_flags);
2569         count += 2;
2570         multibuf[count++] = (char)Players[Player_num].laser_level;
2571         
2572         multibuf[count++] = (char)Players[Player_num].secondary_ammo[HOMING_INDEX];
2573         multibuf[count++] = (char)Players[Player_num].secondary_ammo[CONCUSSION_INDEX];
2574         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_INDEX];
2575         multibuf[count++] = (char)Players[Player_num].secondary_ammo[MEGA_INDEX];
2576         multibuf[count++] = (char)Players[Player_num].secondary_ammo[PROXIMITY_INDEX];
2577
2578         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE1_INDEX];
2579         multibuf[count++] = (char)Players[Player_num].secondary_ammo[GUIDED_INDEX];
2580         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_MINE_INDEX];
2581         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE4_INDEX];
2582         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE5_INDEX];
2583
2584         *(ushort *)(multibuf+count) = INTEL_SHORT( (ushort)Players[Player_num].primary_ammo[VULCAN_INDEX] );
2585         count += 2;
2586         *(ushort *)(multibuf+count) = INTEL_SHORT( (ushort)Players[Player_num].primary_ammo[GAUSS_INDEX] );
2587         count += 2;
2588         *(uint *)(multibuf+count) = INTEL_INT( (uint)Players[Player_num].flags );
2589         count += 4;
2590         
2591         multibuf[count++] = Net_create_loc;
2592
2593         Assert(Net_create_loc <= MAX_NET_CREATE_OBJECTS);
2594
2595         memset(multibuf+count, -1, MAX_NET_CREATE_OBJECTS*sizeof(short));
2596         
2597         mprintf((0, "Created %d explosion objects.\n", Net_create_loc));
2598
2599         for (i = 0; i < Net_create_loc; i++)
2600         {
2601                 if (Net_create_objnums[i] <= 0) {
2602                         Int3(); // Illegal value in created egg object numbers
2603                         count +=2;
2604                         continue;
2605                 }
2606
2607                 *(short *)(multibuf+count) = INTEL_SHORT( (short)Net_create_objnums[i] ); count += 2;
2608
2609                 // We created these objs so our local number = the network number
2610                 map_objnum_local_to_local((short)Net_create_objnums[i]);
2611         }
2612
2613         Net_create_loc = 0;
2614
2615 //      mprintf((1, "explode message size = %d, max = %d.\n", count, message_length[MULTI_PLAYER_EXPLODE]));
2616
2617         if (count > message_length[MULTI_PLAYER_EXPLODE])
2618         {
2619                 Int3(); // See Rob
2620         }
2621
2622         multi_send_data(multibuf, message_length[MULTI_PLAYER_EXPLODE], 2);
2623         if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
2624                 multi_send_decloak();
2625         if (Game_mode & GM_MULTI_ROBOTS)
2626                 multi_strip_robots(Player_num);
2627 }
2628
2629 extern ubyte Secondary_weapon_to_powerup[];
2630 extern ubyte Primary_weapon_to_powerup[];
2631
2632 // put a lid on how many objects will be spewed by an exploding player
2633 // to prevent rampant powerups in netgames
2634
2635 void multi_cap_objects ()
2636  {
2637   char type,flagtype;
2638   int index;
2639
2640   if (!(Game_mode & GM_NETWORK))
2641         return;
2642
2643   for (index=0;index<MAX_PRIMARY_WEAPONS;index++)       
2644         {
2645                 type=Primary_weapon_to_powerup[index];
2646         if (PowerupsInMine[(int)type]>=MaxPowerupsAllowed[(int)type])
2647                  if(Players[Player_num].primary_weapon_flags & (1 << index))
2648                         {
2649                          mprintf ((0,"PIM=%d MPA=%d\n",PowerupsInMine[(int)type],MaxPowerupsAllowed[(int)type]));
2650                          mprintf ((0,"Killing a primary cuz there's too many! (%d)\n",(int)type));
2651                     Players[Player_num].primary_weapon_flags&=(~(1 << index));
2652                         }
2653         }
2654   
2655
2656   // Don't do the adjustment stuff for Hoard mode
2657   if (!(Game_mode & GM_HOARD))            
2658           Players[Player_num].secondary_ammo[2]/=4;
2659
2660   Players[Player_num].secondary_ammo[7]/=4;
2661         
2662   for (index=0;index<MAX_SECONDARY_WEAPONS;index++)     
2663         {
2664                 if ((Game_mode & GM_HOARD) && index==PROXIMITY_INDEX)
2665                         continue;
2666
2667                 type=Secondary_weapon_to_powerup[index];
2668                 
2669         if ((Players[Player_num].secondary_ammo[index]+PowerupsInMine[(int)type])>MaxPowerupsAllowed[(int)type])
2670                  {
2671         if (MaxPowerupsAllowed[(int)type]-PowerupsInMine[(int)type]<0)
2672                          Players[Player_num].secondary_ammo[index]=0;
2673                  else   
2674                  Players[Player_num].secondary_ammo[index]=(MaxPowerupsAllowed[(int)type]-PowerupsInMine[(int)type]);
2675                   
2676                         mprintf ((0,"Hey! I killed secondary type %d because PIM=%d MPA=%d\n",(int)type,PowerupsInMine[(int)type],MaxPowerupsAllowed[(int)type]));
2677                  }      
2678         }
2679
2680   if (!(Game_mode & GM_HOARD))            
2681           Players[Player_num].secondary_ammo[2]*=4;
2682   Players[Player_num].secondary_ammo[7]*=4;
2683     
2684                 if (Players[Player_num].laser_level > MAX_LASER_LEVEL)
2685                  if (PowerupsInMine[POW_SUPER_LASER]+1 > MaxPowerupsAllowed[POW_SUPER_LASER])
2686                         Players[Player_num].laser_level=0;
2687
2688                 if (Players[Player_num].flags & PLAYER_FLAGS_QUAD_LASERS)
2689                  if (PowerupsInMine[POW_QUAD_FIRE]+1 > MaxPowerupsAllowed[POW_QUAD_FIRE])
2690                         Players[Player_num].flags&=(~PLAYER_FLAGS_QUAD_LASERS);
2691
2692                 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
2693                  if (PowerupsInMine[POW_CLOAK]+1 > MaxPowerupsAllowed[POW_CLOAK])
2694                         Players[Player_num].flags&=(~PLAYER_FLAGS_CLOAKED);
2695
2696                 if (Players[Player_num].flags & PLAYER_FLAGS_MAP_ALL)
2697                  if (PowerupsInMine[POW_FULL_MAP]+1 > MaxPowerupsAllowed[POW_FULL_MAP])
2698                         Players[Player_num].flags&=(~PLAYER_FLAGS_MAP_ALL);
2699
2700                 if (Players[Player_num].flags & PLAYER_FLAGS_AFTERBURNER)
2701                  if (PowerupsInMine[POW_AFTERBURNER]+1 > MaxPowerupsAllowed[POW_AFTERBURNER])
2702                         Players[Player_num].flags&=(~PLAYER_FLAGS_AFTERBURNER);
2703
2704                 if (Players[Player_num].flags & PLAYER_FLAGS_AMMO_RACK)
2705                  if (PowerupsInMine[POW_AMMO_RACK]+1 > MaxPowerupsAllowed[POW_AMMO_RACK])
2706                         Players[Player_num].flags&=(~PLAYER_FLAGS_AMMO_RACK);
2707
2708                 if (Players[Player_num].flags & PLAYER_FLAGS_CONVERTER)
2709                  if (PowerupsInMine[POW_CONVERTER]+1 > MaxPowerupsAllowed[POW_CONVERTER])
2710                         Players[Player_num].flags&=(~PLAYER_FLAGS_CONVERTER);
2711
2712                 if (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT)
2713                  if (PowerupsInMine[POW_HEADLIGHT]+1 > MaxPowerupsAllowed[POW_HEADLIGHT])
2714                         Players[Player_num].flags&=(~PLAYER_FLAGS_HEADLIGHT);
2715                 
2716                 if (Game_mode & GM_CAPTURE)
2717                 {
2718                         if (Players[Player_num].flags & PLAYER_FLAGS_FLAG)
2719                          {
2720                           if (get_team(Player_num)==TEAM_RED)
2721                                 flagtype=POW_FLAG_BLUE;
2722                           else
2723                                 flagtype=POW_FLAG_RED;
2724                         
2725                           if (PowerupsInMine[(int)flagtype]+1 > MaxPowerupsAllowed[(int)flagtype])
2726                              Players[Player_num].flags&=(~PLAYER_FLAGS_FLAG);
2727                          }
2728                 }
2729
2730  }
2731
2732 // adds players inventory to multi cap
2733
2734 void multi_adjust_cap_for_player (int pnum)
2735  {
2736   char type;
2737   
2738   int index;
2739
2740   if (!(Game_mode & GM_NETWORK))
2741         return;
2742
2743   for (index=0;index<MAX_PRIMARY_WEAPONS;index++)       
2744         {
2745                 type=Primary_weapon_to_powerup[index];
2746              if (Players[pnum].primary_weapon_flags & (1 << index))
2747                     MaxPowerupsAllowed[(int)type]++;
2748         }
2749                         
2750   for (index=0;index<MAX_SECONDARY_WEAPONS;index++)     
2751         {
2752                 type=Secondary_weapon_to_powerup[index];
2753                 MaxPowerupsAllowed[(int)type]+=Players[pnum].secondary_ammo[index];
2754         }
2755
2756   if (Players[pnum].laser_level > MAX_LASER_LEVEL)
2757          MaxPowerupsAllowed[POW_SUPER_LASER]++;
2758
2759   if (Players[pnum].flags & PLAYER_FLAGS_QUAD_LASERS)
2760     MaxPowerupsAllowed[POW_QUAD_FIRE]++;
2761
2762   if (Players[pnum].flags & PLAYER_FLAGS_CLOAKED)
2763          MaxPowerupsAllowed[POW_CLOAK]++;
2764
2765   if (Players[pnum].flags & PLAYER_FLAGS_MAP_ALL)
2766          MaxPowerupsAllowed[POW_FULL_MAP]++;
2767
2768   if (Players[pnum].flags & PLAYER_FLAGS_AFTERBURNER)
2769         MaxPowerupsAllowed[POW_AFTERBURNER]++;
2770
2771   if (Players[pnum].flags & PLAYER_FLAGS_AMMO_RACK)
2772           MaxPowerupsAllowed[POW_AMMO_RACK]++;
2773
2774   if (Players[pnum].flags & PLAYER_FLAGS_CONVERTER)
2775          MaxPowerupsAllowed[POW_CONVERTER]++;
2776
2777   if (Players[pnum].flags & PLAYER_FLAGS_HEADLIGHT)
2778          MaxPowerupsAllowed[POW_HEADLIGHT]++;
2779  }
2780
2781 void multi_adjust_remote_cap (int pnum)
2782  {
2783   char type;
2784   
2785   int index;
2786
2787   if (!(Game_mode & GM_NETWORK))
2788         return;
2789
2790   for (index=0;index<MAX_PRIMARY_WEAPONS;index++)       
2791         {
2792                 type=Primary_weapon_to_powerup[index];
2793              if (Players[pnum].primary_weapon_flags & (1 << index))
2794                     PowerupsInMine[(int)type]++;
2795         }
2796                         
2797   for (index=0;index<MAX_SECONDARY_WEAPONS;index++)     
2798         {
2799                 type=Secondary_weapon_to_powerup[index];
2800
2801                 if ((Game_mode & GM_HOARD) && index==2)
2802                         continue;
2803    
2804       if (index==2 || index==7) // PROX or SMARTMINES? Those bastards...
2805                         PowerupsInMine[(int)type]+=(Players[pnum].secondary_ammo[index]/4);
2806                 else
2807                    PowerupsInMine[(int)type]+=Players[pnum].secondary_ammo[index];
2808  
2809         }
2810
2811   if (Players[pnum].laser_level > MAX_LASER_LEVEL)
2812          PowerupsInMine[POW_SUPER_LASER]++;
2813
2814   if (Players[pnum].flags & PLAYER_FLAGS_QUAD_LASERS)
2815     PowerupsInMine[POW_QUAD_FIRE]++;
2816
2817   if (Players[pnum].flags & PLAYER_FLAGS_CLOAKED)
2818          PowerupsInMine[POW_CLOAK]++;
2819
2820   if (Players[pnum].flags & PLAYER_FLAGS_MAP_ALL)
2821          PowerupsInMine[POW_FULL_MAP]++;
2822
2823   if (Players[pnum].flags & PLAYER_FLAGS_AFTERBURNER)
2824         PowerupsInMine[POW_AFTERBURNER]++;
2825
2826   if (Players[pnum].flags & PLAYER_FLAGS_AMMO_RACK)
2827           PowerupsInMine[POW_AMMO_RACK]++;
2828
2829   if (Players[pnum].flags & PLAYER_FLAGS_CONVERTER)
2830          PowerupsInMine[POW_CONVERTER]++;
2831
2832   if (Players[pnum].flags & PLAYER_FLAGS_HEADLIGHT)
2833          PowerupsInMine[POW_HEADLIGHT]++;
2834
2835  }
2836
2837 void
2838 multi_send_message(void)
2839 {
2840         int loc = 0;
2841         if (Network_message_reciever != -1)
2842         {
2843                 multibuf[loc] = (char)MULTI_MESSAGE;            loc += 1;
2844                 multibuf[loc] = (char)Player_num;                       loc += 1;
2845                 strncpy(multibuf+loc, Network_message, MAX_MESSAGE_LEN); loc += MAX_MESSAGE_LEN;
2846                 multibuf[loc-1] = '\0';
2847                 multi_send_data(multibuf, loc, 0);
2848                 Network_message_reciever = -1;
2849         }
2850 }
2851
2852 void
2853 multi_send_reappear()
2854 {
2855         multibuf[0] = (char)MULTI_REAPPEAR;
2856         *(short *)(multibuf+1) = INTEL_SHORT(Players[Player_num].objnum);
2857            
2858         multi_send_data(multibuf, 3, 2);
2859         PKilledFlags[Player_num]=0;
2860 }
2861
2862 void
2863 multi_send_position(int objnum)
2864 {
2865 #ifdef MACINTOSH
2866         shortpos sp;
2867 #endif
2868         int count=0;
2869
2870         if (Game_mode & GM_NETWORK) {
2871                 return;
2872         }
2873                      
2874         multibuf[count++] = (char)MULTI_POSITION;
2875 #ifndef MACINTOSH
2876         create_shortpos((shortpos *)(multibuf+count), Objects+objnum,0);
2877         count += sizeof(shortpos);
2878 #else
2879         create_shortpos(&sp, Objects+objnum, 1);
2880         memcpy(&(multibuf[count]), (ubyte *)(sp.bytemat), 9);
2881         count += 9;
2882         memcpy(&(multibuf[count]), (ubyte *)&(sp.xo), 14);
2883         count += 14;
2884 #endif
2885
2886         multi_send_data(multibuf, count, 0);
2887 }
2888
2889 void
2890 multi_send_kill(int objnum)
2891 {
2892         // I died, tell the world.
2893
2894         int killer_objnum;
2895         int count = 0;
2896
2897         Assert(Objects[objnum].id == Player_num);
2898         killer_objnum = Players[Player_num].killer_objnum;
2899
2900         multi_compute_kill(killer_objnum, objnum);
2901          
2902         multibuf[0] = (char)MULTI_KILL;     count += 1;
2903         multibuf[1] = Player_num;           count += 1;
2904    if (killer_objnum > -1) {
2905                 short s;                // do it with variable since INTEL_SHORT won't work on return val from function.
2906                 
2907                 s = (short)objnum_local_to_remote(killer_objnum, (byte *)&multibuf[count+2]);
2908                 *(short *)(multibuf+count) = INTEL_SHORT(s);
2909         } 
2910         else
2911         {
2912                 *(short *)(multibuf+count) = INTEL_SHORT((short)-1);
2913                 multibuf[count+2] = (char)-1;
2914         }
2915         count += 3;
2916         multi_send_data(multibuf, count, 1);
2917
2918 #ifndef SHAREWARE
2919         if (Game_mode & GM_MULTI_ROBOTS)
2920                 multi_strip_robots(Player_num);
2921 #endif
2922 }
2923
2924 void
2925 multi_send_remobj(int objnum)
2926 {
2927         // Tell the other guy to remove an object from his list
2928
2929         byte obj_owner;
2930         short remote_objnum;
2931
2932    if (Objects[objnum].type==OBJ_POWERUP && (Game_mode & GM_NETWORK))
2933     {
2934                 if (PowerupsInMine[Objects[objnum].id] > 0)
2935                  {
2936               PowerupsInMine[Objects[objnum].id]--;
2937                   if (multi_powerup_is_4pack (Objects[objnum].id))
2938                          {
2939                                 mprintf ((0,"Hey babe! Doing that wacky 4 pack stuff."));
2940                 
2941                                 if (PowerupsInMine[Objects[objnum].id-1]-4<0)
2942                                         PowerupsInMine[Objects[objnum].id-1]=0;
2943                                 else
2944                                         PowerupsInMine[Objects[objnum].id-1]-=4;
2945                          }
2946                  }
2947                         
2948          }
2949                         
2950         multibuf[0] = (char)MULTI_REMOVE_OBJECT;
2951
2952         remote_objnum = objnum_local_to_remote((short)objnum, &obj_owner);
2953
2954         *(short *)(multibuf+1) = INTEL_SHORT(remote_objnum); // Map to network objnums
2955
2956         multibuf[3] = obj_owner;        
2957
2958 //      mprintf((0, "multi_send_remobj: %d = %d owner %d.\n", objnum, remote_objnum, obj_owner));
2959
2960         multi_send_data(multibuf, 4, 0);
2961
2962         if (Network_send_objects && network_objnum_is_past(objnum))
2963         {
2964                 mprintf((0, "Resetting object sync due to object removal.\n"));
2965                 Network_send_objnum = -1;
2966         }
2967 }
2968         
2969 void
2970 multi_send_quit(int why)
2971 {
2972         // I am quitting the game, tell the other guy the bad news.
2973
2974         Assert (why == MULTI_QUIT);
2975
2976         multibuf[0] = (char)why;
2977         multibuf[1] = Player_num;
2978         multi_send_data(multibuf, 2, 1);
2979
2980 }
2981
2982 void
2983 multi_send_cloak(void)
2984 {
2985         // Broadcast a change in our pflags (made to support cloaking)
2986
2987         multibuf[0] = MULTI_CLOAK;
2988         multibuf[1] = (char)Player_num;
2989
2990         multi_send_data(multibuf, 2, 1);
2991
2992 #ifndef SHAREWARE
2993         if (Game_mode & GM_MULTI_ROBOTS)
2994                 multi_strip_robots(Player_num);
2995 #endif
2996 }
2997
2998 void
2999 multi_send_decloak(void)
3000 {
3001         // Broadcast a change in our pflags (made to support cloaking)
3002
3003         multibuf[0] = MULTI_DECLOAK;
3004         multibuf[1] = (char)Player_num;
3005
3006         multi_send_data(multibuf, 2, 1);
3007 }
3008
3009 void
3010 multi_send_door_open(int segnum, int side,ubyte flag)
3011 {
3012         // When we open a door make sure everyone else opens that door
3013
3014         multibuf[0] = MULTI_DOOR_OPEN;
3015         *(short *)(multibuf+1) = INTEL_SHORT( (short)segnum );
3016         multibuf[3] = (byte)side;
3017    multibuf[4] = flag;
3018    
3019         multi_send_data(multibuf, 5, 2);
3020 }
3021
3022 extern void network_send_naked_packet (char *,short,int);
3023
3024 void
3025 multi_send_door_open_specific(int pnum,int segnum, int side,ubyte flag)
3026 {
3027         // For sending doors only to a specific person (usually when they're joining)
3028
3029         Assert (Game_mode & GM_NETWORK);
3030 //   Assert (pnum>-1 && pnum<N_players);
3031  
3032         multibuf[0] = MULTI_DOOR_OPEN;
3033         *(short *)(multibuf+1) = INTEL_SHORT( (short)segnum );
3034         multibuf[3] = (byte)side;
3035    multibuf[4] = flag;
3036    
3037         network_send_naked_packet(multibuf, 5, pnum);
3038 }
3039
3040 //
3041 // Part 3 : Functions that change or prepare the game for multiplayer use.
3042 //          Not including functions needed to syncronize or start the 
3043 //          particular type of multiplayer game.  Includes preparing the
3044 //                      mines, player structures, etc.
3045
3046 void
3047 multi_send_create_explosion(int pnum)
3048 {
3049         // Send all data needed to create a remote explosion
3050
3051         int count = 0;
3052
3053         multibuf[count] = MULTI_CREATE_EXPLOSION;       count += 1;
3054         multibuf[count] = (byte)pnum;                                   count += 1;
3055         //                                                                                                      -----------
3056         //                                                                                                      Total size = 2
3057
3058         multi_send_data(multibuf, count, 0);
3059 }
3060         
3061 void
3062 multi_send_controlcen_fire(vms_vector *to_goal, int best_gun_num, int objnum)
3063 {
3064 #ifdef MACINTOSH
3065         vms_vector swapped_vec;
3066 #endif
3067         int count = 0;
3068
3069         multibuf[count] = MULTI_CONTROLCEN_FIRE;                count +=  1;
3070         #ifndef MACINTOSH
3071         memcpy(multibuf+count, to_goal, 12);                    count += 12;
3072         #else
3073         swapped_vec.x = (fix)INTEL_INT( (int)to_goal->x );
3074         swapped_vec.y = (fix)INTEL_INT( (int)to_goal->y );
3075         swapped_vec.z = (fix)INTEL_INT( (int)to_goal->z );
3076         memcpy(multibuf+count, &swapped_vec, 12);                               count += 12;
3077         #endif
3078         multibuf[count] = (char)best_gun_num;                   count +=  1;
3079         *(short *)(multibuf+count) = INTEL_SHORT( (short)objnum );     count +=  2;
3080         //                                                                                                                      ------------
3081         //                                                                                                                      Total  = 16
3082         multi_send_data(multibuf, count, 0);
3083 }
3084
3085 void
3086 multi_send_create_powerup(int powerup_type, int segnum, int objnum, vms_vector *pos)
3087 {
3088         // Create a powerup on a remote machine, used for remote
3089         // placement of used powerups like missiles and cloaking
3090         // powerups.
3091
3092 #ifdef MACINTOSH
3093         vms_vector swapped_vec;
3094 #endif
3095         int count = 0;
3096
3097    if (Game_mode & GM_NETWORK)
3098            PowerupsInMine[powerup_type]++;
3099
3100         multibuf[count] = MULTI_CREATE_POWERUP;         count += 1;
3101         multibuf[count] = Player_num;                                      count += 1;
3102         multibuf[count] = powerup_type;                                 count += 1;
3103         *(short *)(multibuf+count) = INTEL_SHORT( (short)segnum );     count += 2;
3104         *(short *)(multibuf+count) = INTEL_SHORT( (short)objnum );     count += 2;
3105 #ifndef MACINTOSH
3106         *(vms_vector *)(multibuf+count) = *pos;         count += sizeof(vms_vector);
3107 #else
3108         swapped_vec.x = (fix)INTEL_INT( (int)pos->x );
3109         swapped_vec.y = (fix)INTEL_INT( (int)pos->y );
3110         swapped_vec.z = (fix)INTEL_INT( (int)pos->z );
3111         memcpy(multibuf+count, &swapped_vec, 12);                               count += 12;
3112 #endif
3113         //                                                                                                            -----------
3114         //                                                                                                            Total =  19
3115         multi_send_data(multibuf, count, 2);
3116
3117         if (Network_send_objects && network_objnum_is_past(objnum))
3118         {
3119                 mprintf((0, "Resetting object sync due to powerup creation.\n"));
3120                 Network_send_objnum = -1;
3121         }
3122
3123         mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));
3124         map_objnum_local_to_local(objnum);
3125 }       
3126
3127 void
3128 multi_send_play_sound(int sound_num, fix volume)
3129 {
3130         int count = 0;
3131         multibuf[count] = MULTI_PLAY_SOUND;                     count += 1;
3132         multibuf[count] = Player_num;                                   count += 1;
3133         multibuf[count] = (char)sound_num;                      count += 1;
3134         multibuf[count] = (char)(volume >> 12); count += 1;
3135         //                                                                                                         -----------
3136         //                                                                                                         Total = 4
3137         multi_send_data(multibuf, count, 0);
3138 }
3139
3140 void
3141 multi_send_audio_taunt(int taunt_num)
3142 {
3143         return; // Taken out, awaiting sounds..
3144
3145 #if 0
3146         int audio_taunts[4] = {
3147                 SOUND_CONTROL_CENTER_WARNING_SIREN,
3148                 SOUND_HOSTAGE_RESCUED,
3149                 SOUND_REFUEL_STATION_GIVING_FUEL,
3150                 SOUND_BAD_SELECTION
3151         };
3152
3153
3154         Assert(taunt_num >= 0);
3155         Assert(taunt_num < 4);
3156
3157         digi_play_sample( audio_taunts[taunt_num], F1_0 );
3158         multi_send_play_sound(audio_taunts[taunt_num], F1_0);
3159 #endif
3160 }
3161
3162 void
3163 multi_send_score(void)
3164 {
3165         // Send my current score to all other players so it will remain
3166         // synced.
3167         int count = 0;
3168
3169         if (Game_mode & GM_MULTI_COOP) {
3170                 multi_sort_kill_list();
3171                 multibuf[count] = MULTI_SCORE;                  count += 1;
3172                 multibuf[count] = Player_num;                           count += 1;
3173                 *(int *)(multibuf+count) = INTEL_INT( Players[Player_num].score );  count += 4;
3174                 multi_send_data(multibuf, count, 0);
3175         }
3176 }       
3177
3178
3179 void
3180 multi_send_save_game(ubyte slot, uint id, char * desc)
3181 {
3182         int count = 0;
3183         
3184         multibuf[count] = MULTI_SAVE_GAME;              count += 1;
3185         multibuf[count] = slot;                         count += 1;    // Save slot=0
3186         *(uint *)(multibuf+count) = INTEL_INT( id );         count += 4;             // Save id
3187         memcpy( &multibuf[count], desc, 20 ); count += 20;
3188
3189         multi_send_data(multibuf, count, 2);
3190 }
3191
3192 void
3193 multi_send_restore_game(ubyte slot, uint id)
3194 {
3195         int count = 0;
3196         
3197         multibuf[count] = MULTI_RESTORE_GAME;   count += 1;
3198         multibuf[count] = slot;                                                 count += 1;             // Save slot=0
3199         *(uint *)(multibuf+count) = INTEL_INT( id );         count += 4;             // Save id
3200
3201         multi_send_data(multibuf, count, 2);
3202 }
3203
3204 void
3205 multi_send_netplayer_stats_request(ubyte player_num)
3206 {
3207         int count = 0;
3208         
3209         multibuf[count] = MULTI_REQ_PLAYER;     count += 1;
3210         multibuf[count] = player_num;                   count += 1;
3211
3212         multi_send_data(multibuf, count, 0 );
3213 }
3214
3215 void
3216 multi_send_trigger(int triggernum)
3217 {
3218         // Send an even to trigger something in the mine
3219         
3220         int count = 0;
3221         
3222         multibuf[count] = MULTI_TRIGGER;                                count += 1;
3223         multibuf[count] = Player_num;                                   count += 1;
3224         multibuf[count] = (ubyte)triggernum;            count += 1;
3225    
3226    mprintf ((0,"Sending trigger %d\n",triggernum));
3227
3228         multi_send_data(multibuf, count, 1); 
3229 //      multi_send_data(multibuf, count, 1); // twice? 
3230 }
3231
3232 void
3233 multi_send_hostage_door_status(int wallnum)
3234 {
3235         // Tell the other player what the hit point status of a hostage door
3236         // should be
3237
3238         int count = 0;
3239         
3240         Assert(Walls[wallnum].type == WALL_BLASTABLE);
3241
3242         multibuf[count] = MULTI_HOSTAGE_DOOR;           count += 1;
3243         *(short *)(multibuf+count) = INTEL_SHORT( (short)wallnum );           count += 2;
3244         *(fix *)(multibuf+count) = (fix)INTEL_INT( (int)Walls[wallnum].hps );  count += 4;
3245
3246 //      mprintf((0, "Door %d damaged by %f points.\n", wallnum, f2fl(Walls[wallnum].hps)));
3247
3248         multi_send_data(multibuf, count, 0);
3249 }
3250
3251 extern int ConsistencyCount;
3252 extern int Drop_afterburner_blob_flag;
3253 int PhallicLimit=0;
3254 int PhallicMan=-1;
3255
3256 void multi_prep_level(void)
3257 {
3258         // Do any special stuff to the level required for serial games
3259         // before we begin playing in it.
3260
3261         // Player_num MUST be set before calling this procedure.  
3262
3263         // This function must be called before checksuming the Object array,
3264         // since the resulting checksum with depend on the value of Player_num
3265         // at the time this is called.
3266
3267         int i,ng=0;
3268         int     cloak_count, inv_count;
3269
3270         Assert(Game_mode & GM_MULTI);
3271
3272         Assert(NumNetPlayerPositions > 0);
3273         
3274    PhallicLimit=0;
3275    PhallicMan=-1;
3276    Drop_afterburner_blob_flag=0;
3277    ConsistencyCount=0;
3278   
3279    for (i=0;i<MAX_NUM_NET_PLAYERS;i++)
3280          PKilledFlags[i]=0;
3281   
3282         for (i = 0; i < NumNetPlayerPositions; i++)
3283         {
3284                 if (i != Player_num)
3285                         Objects[Players[i].objnum].control_type = CT_REMOTE;
3286                 Objects[Players[i].objnum].movement_type = MT_PHYSICS;
3287                 multi_reset_player_object(&Objects[Players[i].objnum]);
3288                 LastPacketTime[i] = 0;
3289         }
3290
3291 #ifndef SHAREWARE
3292         for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
3293         {
3294                 robot_controlled[i] = -1;       
3295                 robot_agitation[i] = 0;
3296                 robot_fired[i] = 0;
3297         }
3298 #endif
3299
3300         Viewer = ConsoleObject = &Objects[Players[Player_num].objnum];
3301
3302         if (!(Game_mode & GM_MULTI_COOP))       
3303         {
3304                 multi_delete_extra_objects(); // Removes monsters from level
3305         }
3306
3307         if (Game_mode & GM_MULTI_ROBOTS)
3308         {
3309                 multi_set_robot_ai(); // Set all Robot AI to types we can cope with
3310         }
3311  
3312    if (Game_mode & GM_NETWORK)
3313          {
3314           multi_adjust_cap_for_player(Player_num);      
3315           multi_send_powerup_update();
3316           ng=1;  // ng means network game
3317          }
3318    ng=1;        
3319
3320         inv_count = 0;
3321         cloak_count = 0;
3322         for (i=0; i<=Highest_object_index; i++)
3323         {
3324                 int objnum;
3325
3326                 if ((Objects[i].type == OBJ_HOSTAGE) && !(Game_mode & GM_MULTI_COOP))
3327                 {
3328                         objnum = obj_create(OBJ_POWERUP, POW_SHIELD_BOOST, Objects[i].segnum, &Objects[i].pos, &vmd_identity_matrix, Powerup_info[POW_SHIELD_BOOST].size, CT_POWERUP, MT_PHYSICS, RT_POWERUP);
3329                         obj_delete(i);                  
3330                         if (objnum != -1)
3331                         {
3332                                 Objects[objnum].rtype.vclip_info.vclip_num = Powerup_info[POW_SHIELD_BOOST].vclip_num;
3333                                 Objects[objnum].rtype.vclip_info.frametime = Vclip[Objects[objnum].rtype.vclip_info.vclip_num].frame_time;
3334                                 Objects[objnum].rtype.vclip_info.framenum = 0;
3335                                 Objects[objnum].mtype.phys_info.drag = 512;     //1024;
3336                                 Objects[objnum].mtype.phys_info.mass = F1_0;
3337                                 vm_vec_zero(&Objects[objnum].mtype.phys_info.velocity);
3338                         }
3339                         continue;
3340                 }
3341
3342                 if (Objects[i].type == OBJ_POWERUP)
3343                 {
3344                         if (Objects[i].id == POW_EXTRA_LIFE) 
3345                         {
3346                    if (ng && !Netgame.DoInvulnerability)
3347                       {
3348                                  Objects[i].id = POW_SHIELD_BOOST;
3349                                  Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3350                                  Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3351                       }
3352                    else
3353                       {
3354                                  Objects[i].id = POW_INVULNERABILITY;
3355                                  Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3356                                  Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3357                       } 
3358
3359               }
3360
3361                         if (!(Game_mode & GM_MULTI_COOP))
3362                                 if ((Objects[i].id >= POW_KEY_BLUE) && (Objects[i].id <= POW_KEY_GOLD))
3363                                         {
3364                                                 Objects[i].id = POW_SHIELD_BOOST;
3365                                                 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3366                                                 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3367                                         }
3368
3369                         if (Objects[i].id == POW_INVULNERABILITY) {
3370                            if (inv_count >= 3 || (ng && !Netgame.DoInvulnerability)) {
3371                                         mprintf((0, "Bashing Invulnerability object #%i to shield.\n", i));
3372                                         Objects[i].id = POW_SHIELD_BOOST;
3373                                         Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3374                                         Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3375                                 } else
3376                                         inv_count++;
3377                         }
3378
3379                         if (Objects[i].id == POW_CLOAK) {
3380            if (cloak_count >= 3 || (ng && !Netgame.DoCloak)) {
3381                                         mprintf((0, "Bashing Cloak object #%i to shield.\n", i));
3382                                         Objects[i].id = POW_SHIELD_BOOST;
3383                                         Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3384                                         Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3385                                 } else
3386                                         cloak_count++;
3387                         }
3388
3389         if (Objects[i].id == POW_AFTERBURNER && ng && !Netgame.DoAfterburner)
3390            bash_to_shield (i,"afterburner");
3391         if (Objects[i].id == POW_FUSION_WEAPON && ng &&  !Netgame.DoFusions)
3392            bash_to_shield (i,"fusion");
3393         if (Objects[i].id == POW_PHOENIX_WEAPON && ng && !Netgame.DoPhoenix)
3394            bash_to_shield (i,"phoenix");
3395
3396         if (Objects[i].id == POW_HELIX_WEAPON && ng && !Netgame.DoHelix)
3397            bash_to_shield (i,"helix");
3398
3399         if (Objects[i].id == POW_MEGA_WEAPON && ng && !Netgame.DoMegas)
3400            bash_to_shield (i,"mega");
3401
3402         if (Objects[i].id == POW_SMARTBOMB_WEAPON && ng && !Netgame.DoSmarts)
3403            bash_to_shield (i,"smartmissile");
3404
3405         if (Objects[i].id == POW_GAUSS_WEAPON && ng && !Netgame.DoGauss)
3406            bash_to_shield (i,"gauss");
3407
3408         if (Objects[i].id == POW_VULCAN_WEAPON && ng && !Netgame.DoVulcan)
3409            bash_to_shield (i,"vulcan");
3410                         
3411         if (Objects[i].id == POW_PLASMA_WEAPON && ng && !Netgame.DoPlasma)
3412            bash_to_shield (i,"plasma");
3413                  
3414         if (Objects[i].id == POW_OMEGA_WEAPON && ng && !Netgame.DoOmega)
3415            bash_to_shield (i,"omega");
3416
3417         if (Objects[i].id == POW_SUPER_LASER && ng && !Netgame.DoSuperLaser)
3418            bash_to_shield (i,"superlaser");
3419
3420         if (Objects[i].id == POW_PROXIMITY_WEAPON && ng && !Netgame.DoProximity)
3421            bash_to_shield (i,"proximity");
3422
3423 //    Special: Make all proximity bombs into shields if in hoard mode because
3424 //              we use the proximity slot in the player struct to signify how many orbs
3425 //              the player has.
3426
3427         if (Objects[i].id == POW_PROXIMITY_WEAPON && ng && (Game_mode & GM_HOARD))
3428            bash_to_shield (i,"proximity");
3429
3430         if (Objects[i].id==POW_VULCAN_AMMO && ng && (!Netgame.DoVulcan && !Netgame.DoGauss))
3431                 bash_to_shield(i,"vulcan ammo");
3432
3433         if (Objects[i].id == POW_SPREADFIRE_WEAPON && ng && !Netgame.DoSpread)
3434            bash_to_shield (i,"spread");
3435         if (Objects[i].id == POW_SMART_MINE && ng && !Netgame.DoSmartMine)
3436            bash_to_shield (i,"smartmine");
3437         if (Objects[i].id == POW_SMISSILE1_1 && ng &&  !Netgame.DoFlash)
3438            bash_to_shield (i,"flash");
3439         if (Objects[i].id == POW_SMISSILE1_4 && ng &&  !Netgame.DoFlash)
3440            bash_to_shield (i,"flash");
3441         if (Objects[i].id == POW_GUIDED_MISSILE_1 && ng &&  !Netgame.DoGuided)
3442            bash_to_shield (i,"guided");
3443         if (Objects[i].id == POW_GUIDED_MISSILE_4 && ng &&  !Netgame.DoGuided)
3444            bash_to_shield (i,"guided");
3445         if (Objects[i].id == POW_EARTHSHAKER_MISSILE && ng &&  !Netgame.DoEarthShaker)
3446            bash_to_shield (i,"earth");
3447         if (Objects[i].id == POW_MERCURY_MISSILE_1 && ng &&  !Netgame.DoMercury)
3448            bash_to_shield (i,"Mercury");
3449         if (Objects[i].id == POW_MERCURY_MISSILE_4 && ng &&  !Netgame.DoMercury)
3450            bash_to_shield (i,"Mercury");
3451         if (Objects[i].id == POW_CONVERTER && ng &&  !Netgame.DoConverter)
3452            bash_to_shield (i,"Converter");
3453         if (Objects[i].id == POW_AMMO_RACK && ng &&  !Netgame.DoAmmoRack)
3454            bash_to_shield (i,"Ammo rack");
3455         if (Objects[i].id == POW_HEADLIGHT && ng &&  !Netgame.DoHeadlight)
3456            bash_to_shield (i,"Headlight");
3457         if (Objects[i].id == POW_LASER && ng &&  !Netgame.DoLaserUpgrade)
3458            bash_to_shield (i,"Laser powerup");
3459         if (Objects[i].id == POW_HOMING_AMMO_1 && ng &&  !Netgame.DoHoming)
3460            bash_to_shield (i,"Homing");
3461         if (Objects[i].id == POW_HOMING_AMMO_4 && ng &&  !Netgame.DoHoming)
3462            bash_to_shield (i,"Homing");
3463         if (Objects[i].id == POW_QUAD_FIRE && ng &&  !Netgame.DoQuadLasers)
3464            bash_to_shield (i,"Quad Lasers");
3465    if (Objects[i].id == POW_FLAG_BLUE && !(Game_mode & GM_CAPTURE))
3466            bash_to_shield (i,"Blue flag");
3467    if (Objects[i].id == POW_FLAG_RED && !(Game_mode & GM_CAPTURE))
3468            bash_to_shield (i,"Red flag");
3469      }
3470    }
3471         
3472         if (Game_mode & GM_HOARD)
3473                 init_hoard_data();
3474
3475    if ((Game_mode & GM_CAPTURE) || (Game_mode & GM_HOARD))
3476          multi_apply_goal_textures();
3477   
3478         multi_sort_kill_list();
3479
3480         multi_show_player_list();
3481
3482         ConsoleObject->control_type = CT_FLYING;
3483
3484         reset_player_object();
3485
3486 }
3487
3488 int Goal_blue_segnum,Goal_red_segnum;
3489
3490 void multi_apply_goal_textures()
3491 {
3492         int             i,j,tex;
3493         segment *seg;
3494         segment2        *seg2;
3495  
3496         for (i=0; i <= Highest_segment_index; i++)
3497          {
3498                 seg = &Segments[i];
3499                 seg2 = &Segment2s[i];
3500  
3501       if (seg2->special==SEGMENT_IS_GOAL_BLUE)
3502                  {
3503                         
3504                         Goal_blue_segnum = i;
3505
3506                         if (Game_mode & GM_HOARD)
3507                                 tex=find_goal_texture (TMI_GOAL_HOARD);
3508                         else
3509                                 tex=find_goal_texture (TMI_GOAL_BLUE);
3510                         
3511                         if (tex>-1)
3512                                 for (j = 0; j < 6; j++) {
3513                                         int v;
3514                                         seg->sides[j].tmap_num=tex;
3515                                         for (v=0;v<4;v++)
3516                                                 seg->sides[j].uvls[v].l = i2f(100);             //max out
3517                                 }
3518
3519                         seg2->static_light = i2f(100);  //make static light bright
3520
3521                  }      
3522                         
3523       if (seg2->special==SEGMENT_IS_GOAL_RED)
3524                  {
3525                         Goal_red_segnum = i;
3526
3527                         // Make both textures the same if Hoard mode
3528
3529                         if (Game_mode & GM_HOARD)
3530                                 tex=find_goal_texture (TMI_GOAL_HOARD);
3531                         else
3532                                 tex=find_goal_texture (TMI_GOAL_RED);
3533                 
3534                         if (tex>-1)
3535                                 for (j = 0; j < 6; j++) {
3536                                         int v;
3537                                         seg->sides[j].tmap_num=tex;
3538                                         for (v=0;v<4;v++)
3539                                                 seg->sides[j].uvls[v].l = i2f(1000);            //max out
3540                                 }
3541
3542                         seg2->static_light = i2f(100);  //make static light bright
3543                  }
3544          }
3545  }
3546 int find_goal_texture (ubyte t)
3547  {
3548   int i;
3549  
3550   for (i=0;i<NumTextures;i++)
3551         if (TmapInfo[i].flags & t)
3552                 return i;
3553
3554   Int3(); // Hey, there is no goal texture for this PIG!!!!
3555                          // Edit bitmaps.tbl and designate two textures to be RED and BLUE
3556                          // goal textures
3557   return (-1);