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