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