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