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