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