]> icculus.org git repositories - btb/d2x.git/blob - main/multi.c
remove rcs tags
[btb/d2x.git] / main / multi.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 /*
15  *
16  * Multiplayer code shared by serial and network play.
17  *
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <conf.h>
22 #endif
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <ctype.h>
29
30 #include "u_mem.h"
31 #include "strutil.h"
32 #include "game.h"
33 #include "modem.h"
34 #include "network.h"
35 #include "multi.h"
36 #include "object.h"
37 #include "laser.h"
38 #include "fuelcen.h"
39 #include "scores.h"
40 #include "gauges.h"
41 #include "collide.h"
42 #include "error.h"
43 #include "fireball.h"
44 #include "newmenu.h"
45 #include "mono.h"
46 #include "wall.h"
47 #include "cntrlcen.h"
48 #include "powerup.h"
49 #include "polyobj.h"
50 #include "bm.h"
51 #include "endlevel.h"
52 #include "key.h"
53 #include "playsave.h"
54 #include "timer.h"
55 #include "digi.h"
56 #include "sounds.h"
57 #include "kconfig.h"
58 #include "newdemo.h"
59 #include "text.h"
60 #include "kmatrix.h"
61 #include "multibot.h"
62 #include "gameseq.h"
63 #include "physics.h"
64 #include "config.h"
65 #include "state.h"
66 #include "ai.h"
67 #include "switch.h"
68 #include "textures.h"
69 #include "byteswap.h"
70 #include "sounds.h"
71 #include "args.h"
72 #include "cfile.h"
73 #include "effects.h"
74 #include "iff.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         if (!(Game_mode & GM_MULTI_COOP))
1485         {
1486                 mprintf((0, "Setting all keys for player %d.\n", Player_num));
1487                 Players[Player_num].flags |= (PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_GOLD_KEY);
1488         }
1489 }
1490
1491 void
1492 multi_do_fire(char *buf)
1493 {
1494         ubyte weapon;
1495         char pnum;
1496         sbyte flags;
1497         //static dum=0;
1498
1499         // Act out the actual shooting
1500         pnum = buf[1];
1501
1502 #ifndef MACINTOSH
1503         weapon = (int)buf[2];
1504 #else
1505         weapon = buf[2];
1506 #endif
1507         flags = buf[4];
1508         Network_laser_track = GET_INTEL_SHORT(buf + 6);
1509
1510         Assert (pnum < N_players);
1511
1512         if (Objects[Players[(int)pnum].objnum].type == OBJ_GHOST)
1513                 multi_make_ghost_player(pnum);
1514
1515         //  mprintf((0,"multi_do_fire, weapon = %d\n",weapon));
1516
1517         if (weapon == FLARE_ADJUST)
1518                 Laser_player_fire( &Objects[Players[(int)pnum].objnum], FLARE_ID, 6, 1, 0 );
1519         else if (weapon >= MISSILE_ADJUST) {
1520                 int weapon_id,weapon_gun;
1521
1522                 weapon_id = Secondary_weapon_to_weapon_info[weapon-MISSILE_ADJUST];
1523                 weapon_gun = Secondary_weapon_to_gun_num[weapon-MISSILE_ADJUST] + (flags & 1);
1524                 mprintf((0,"missile id = %d, gun = %d\n",weapon_id,weapon_gun));
1525
1526                 if (weapon-MISSILE_ADJUST==GUIDED_INDEX)
1527                 {
1528                         mprintf ((0,"Missile is guided!!!\n"));
1529                         Multi_is_guided=1;
1530                 }
1531
1532                 Laser_player_fire( &Objects[Players[(int)pnum].objnum], weapon_id, weapon_gun, 1, 0 );
1533         }
1534         else {
1535                 fix save_charge = Fusion_charge;
1536
1537                 if (weapon == FUSION_INDEX) {
1538                         Fusion_charge = flags << 12;
1539                         mprintf((0, "Fusion charge X%f.\n", f2fl(Fusion_charge)));
1540                 }
1541                 if (weapon == LASER_ID) {
1542                         if (flags & LASER_QUAD)
1543                                 Players[(int)pnum].flags |= PLAYER_FLAGS_QUAD_LASERS;
1544                         else
1545                                 Players[(int)pnum].flags &= ~PLAYER_FLAGS_QUAD_LASERS;
1546                 }
1547
1548                 do_laser_firing(Players[(int)pnum].objnum, weapon, (int)buf[3], flags, (int)buf[5]);
1549
1550                 if (weapon == FUSION_INDEX)
1551                         Fusion_charge = save_charge;
1552         }
1553 }
1554
1555 void
1556 multi_do_message(char *buf)
1557 {
1558         char *colon;
1559         char *tilde,mesbuf[100];
1560         int tloc,t;
1561
1562         int loc = 2;
1563
1564         if ((tilde=strchr (buf+loc,'$')))  // do that stupid name stuff
1565         {                                                                                       // why'd I put this in?  Probably for the
1566                 tloc=tilde-(buf+loc);                           // same reason you can name your guidebot
1567                 mprintf ((0,"Tloc=%d\n",tloc));
1568
1569                 if (tloc>0)
1570                         strncpy (mesbuf,buf+loc,tloc);
1571                 strcpy (mesbuf+tloc,Players[Player_num].callsign);
1572                 strcpy (mesbuf+strlen(Players[Player_num].callsign)+tloc,buf+loc+tloc+1);
1573                 strcpy (buf+loc,mesbuf);
1574         }
1575
1576         if (((colon = strrchr(buf+loc, ':')) == NULL) || (colon-(buf+loc) < 1) || (colon-(buf+loc) > CALLSIGN_LEN))
1577         {
1578                 mesbuf[0] = 1;
1579                 mesbuf[1] = BM_XRGB(28, 0, 0);
1580                 strcpy(&mesbuf[2], Players[(int)buf[1]].callsign);
1581                 t = strlen(mesbuf);
1582                 mesbuf[t] = ':';
1583                 mesbuf[t+1] = 1;
1584                 mesbuf[t+2] = BM_XRGB(0, 31, 0);
1585                 mesbuf[t+3] = 0;
1586
1587                 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1588                 HUD_init_message("%s %s", mesbuf, buf+2);
1589         }
1590         else
1591         {
1592                 if ( (!strnicmp(Players[Player_num].callsign, buf+loc, colon-(buf+loc))) ||
1593                          ((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)))) )
1594                 {
1595                         mesbuf[0] = 1;
1596                         mesbuf[1] = BM_XRGB(0, 32, 32);
1597                         strcpy(&mesbuf[2], Players[(int)buf[1]].callsign);
1598                         t = strlen(mesbuf);
1599                         mesbuf[t] = ':';
1600                         mesbuf[t+1] = 1;
1601                         mesbuf[t+2] = BM_XRGB(0, 31, 0);
1602                         mesbuf[t+3] = 0;
1603
1604                         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1605                         HUD_init_message("%s %s", mesbuf, colon+1);
1606                 }
1607         }
1608 }
1609
1610 void
1611 multi_do_position(char *buf)
1612 {
1613 #ifdef WORDS_BIGENDIAN
1614         shortpos sp;
1615 #endif
1616
1617         // This routine does only player positions, mode game only
1618         //      mprintf((0, "Got position packet.\n"));
1619
1620         int pnum = (Player_num+1)%2;
1621
1622         Assert(&Objects[Players[pnum].objnum] != ConsoleObject);
1623
1624         if (Game_mode & GM_NETWORK)
1625         {
1626                 Int3(); // Get Jason, what the hell are we doing here?
1627                 return;
1628     }
1629
1630
1631 #ifndef WORDS_BIGENDIAN
1632         extract_shortpos(&Objects[Players[pnum].objnum], (shortpos *)(buf+1),0);
1633 #else
1634         memcpy((ubyte *)(sp.bytemat), (ubyte *)(buf + 1), 9);
1635         memcpy((ubyte *)&(sp.xo), (ubyte *)(buf + 10), 14);
1636         extract_shortpos(&Objects[Players[pnum].objnum], &sp, 1);
1637 #endif
1638
1639         if (Objects[Players[pnum].objnum].movement_type == MT_PHYSICS)
1640                 set_thrust_from_velocity(&Objects[Players[pnum].objnum]);
1641 }
1642
1643 void
1644 multi_do_reappear(char *buf)
1645 {
1646         short objnum;
1647
1648         objnum = GET_INTEL_SHORT(buf + 1);
1649
1650         Assert(objnum >= 0);
1651         //      Assert(Players[Objects[objnum].id]].objnum == objnum);
1652
1653         // mprintf((0, "Switching rendering back on for object %d.\n", objnum));
1654
1655         multi_make_ghost_player(Objects[objnum].id);
1656         create_player_appearance_effect(&Objects[objnum]);
1657         PKilledFlags[Objects[objnum].id]=0;
1658 }
1659
1660 void
1661 multi_do_player_explode(char *buf)
1662 {
1663         // Only call this for players, not robots.  pnum is player number, not
1664         // Object number.
1665
1666         object *objp;
1667         int count;
1668         int pnum;
1669         int i;
1670         char remote_created;
1671
1672         pnum = buf[1];
1673
1674 #ifdef NDEBUG
1675         if ((pnum < 0) || (pnum >= N_players))
1676                 return;
1677 #else
1678         Assert(pnum >= 0);
1679         Assert(pnum < N_players);
1680 #endif
1681
1682 #ifdef NETWORK
1683         // If we are in the process of sending objects to a new player, reset that process
1684         if (Network_send_objects)
1685         {
1686                 mprintf((0, "Resetting object sync due to player explosion.\n"));
1687                 Network_send_objnum = -1;
1688         }
1689 #endif
1690
1691         // Stuff the Players structure to prepare for the explosion
1692
1693         count = 2;
1694         Players[pnum].primary_weapon_flags = GET_INTEL_SHORT(buf + count); count += 2;
1695         Players[pnum].secondary_weapon_flags = GET_INTEL_SHORT(buf + count); count += 2;
1696         Players[pnum].laser_level = buf[count];                                                 count++;
1697         Players[pnum].secondary_ammo[HOMING_INDEX] = buf[count];                count++;
1698         Players[pnum].secondary_ammo[CONCUSSION_INDEX] = buf[count];count++;
1699         Players[pnum].secondary_ammo[SMART_INDEX] = buf[count];         count++;
1700         Players[pnum].secondary_ammo[MEGA_INDEX] = buf[count];          count++;
1701         Players[pnum].secondary_ammo[PROXIMITY_INDEX] = buf[count]; count++;
1702
1703         Players[pnum].secondary_ammo[SMISSILE1_INDEX] = buf[count]; count++;
1704         Players[pnum].secondary_ammo[GUIDED_INDEX]    = buf[count]; count++;
1705         Players[pnum].secondary_ammo[SMART_MINE_INDEX]= buf[count]; count++;
1706         Players[pnum].secondary_ammo[SMISSILE4_INDEX] = buf[count]; count++;
1707         Players[pnum].secondary_ammo[SMISSILE5_INDEX] = buf[count]; count++;
1708
1709         Players[pnum].primary_ammo[VULCAN_INDEX] = GET_INTEL_SHORT(buf + count); count += 2;
1710         Players[pnum].primary_ammo[GAUSS_INDEX] = GET_INTEL_SHORT(buf + count); count += 2;
1711         Players[pnum].flags = GET_INTEL_INT(buf + count);               count += 4;
1712
1713         multi_adjust_remote_cap (pnum);
1714
1715         objp = &Objects[Players[pnum].objnum];
1716
1717         //      objp->phys_info.velocity = *(vms_vector *)(buf+16); // 12 bytes
1718         //      objp->pos = *(vms_vector *)(buf+28);                // 12 bytes
1719
1720         remote_created = buf[count++]; // How many did the other guy create?
1721
1722         Net_create_loc = 0;
1723
1724         drop_player_eggs(objp);
1725
1726         // Create mapping from remote to local numbering system
1727
1728         mprintf((0, "I Created %d powerups, remote created %d.\n", Net_create_loc, remote_created));
1729
1730         // We now handle this situation gracefully, Int3 not required
1731         //      if (Net_create_loc != remote_created)
1732         //              Int3(); // Probably out of object array space, see Rob
1733
1734         for (i = 0; i < remote_created; i++)
1735         {
1736                 short s;
1737
1738                 s = GET_INTEL_SHORT(buf + count);
1739
1740                 if ((i < Net_create_loc) && (s > 0))
1741                         map_objnum_local_to_remote((short)Net_create_objnums[i], s, pnum);
1742                 else if (s <= 0)
1743                 {
1744                         mprintf((0, "WARNING: Remote created object has non-valid number %d (player %d)", s, pnum));
1745                 }
1746                 else
1747                 {
1748                         mprintf((0, "WARNING: Could not create all powerups created by player %d.\n", pnum));
1749                 }
1750                 //              Assert(s > 0);
1751                 count += 2;
1752         }
1753         for (i = remote_created; i < Net_create_loc; i++) {
1754                 mprintf((0, "WARNING: I Created more powerups than player %d, deleting.\n", pnum));
1755                 Objects[Net_create_objnums[i]].flags |= OF_SHOULD_BE_DEAD;
1756         }
1757
1758         if (buf[0] == MULTI_PLAYER_EXPLODE)
1759         {
1760                 explode_badass_player(objp);
1761
1762                 objp->flags &= ~OF_SHOULD_BE_DEAD;              //don't really kill player
1763                 multi_make_player_ghost(pnum);
1764         }
1765         else
1766         {
1767                 create_player_appearance_effect(objp);
1768         }
1769
1770         Players[pnum].flags &= ~(PLAYER_FLAGS_CLOAKED | PLAYER_FLAGS_INVULNERABLE | PLAYER_FLAGS_FLAG);
1771         Players[pnum].cloak_time = 0;
1772 }
1773
1774 void
1775 multi_do_kill(char *buf)
1776 {
1777         int killer, killed;
1778         int count = 1;
1779         int pnum;
1780
1781         pnum = (int)(buf[count]);
1782         if ((pnum < 0) || (pnum >= N_players))
1783         {
1784                 Int3(); // Invalid player number killed
1785                 return;
1786         }
1787         killed = Players[pnum].objnum;
1788         count += 1;
1789
1790         killer = GET_INTEL_SHORT(buf + count);
1791         if (killer > 0)
1792                 killer = objnum_remote_to_local(killer, (sbyte)buf[count+2]);
1793
1794 #ifdef SHAREWARE
1795         if ((Objects[killed].type != OBJ_PLAYER) && (Objects[killed].type != OBJ_GHOST))
1796         {
1797                 Int3();
1798                 mprintf( (1, "SOFT INT3: MULTI.C Non-player object %d of type %d killed! (JOHN)\n", killed, Objects[killed].type ));
1799                 return;
1800         }
1801 #endif
1802
1803         multi_compute_kill(killer, killed);
1804
1805 }
1806
1807
1808 //      Changed by MK on 10/20/94 to send NULL as object to net_destroy_controlcen if it got -1
1809 // which means not a controlcen object, but contained in another object
1810 void multi_do_controlcen_destroy(char *buf)
1811 {
1812         sbyte who;
1813         short objnum;
1814
1815         objnum = GET_INTEL_SHORT(buf + 1);
1816         who = buf[3];
1817
1818         if (Control_center_destroyed != 1)
1819         {
1820                 if ((who < N_players) && (who != Player_num)) {
1821                         HUD_init_message("%s %s", Players[who].callsign, TXT_HAS_DEST_CONTROL);
1822                 }
1823                 else if (who == Player_num)
1824                         HUD_init_message(TXT_YOU_DEST_CONTROL);
1825                 else
1826                         HUD_init_message(TXT_CONTROL_DESTROYED);
1827
1828                 if (objnum != -1)
1829                         net_destroy_controlcen(&Objects[objnum]);
1830                 else
1831                         net_destroy_controlcen(NULL);
1832         }
1833 }
1834
1835 void
1836 multi_do_escape(char *buf)
1837 {
1838         int objnum;
1839
1840         objnum = Players[(int)buf[1]].objnum;
1841
1842         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1843         digi_kill_sound_linked_to_object (objnum);
1844
1845         if (buf[2] == 0)
1846         {
1847                 HUD_init_message("%s %s", Players[(int)buf[1]].callsign, TXT_HAS_ESCAPED);
1848                 if (Game_mode & GM_NETWORK)
1849                         Players[(int)buf[1]].connected = CONNECT_ESCAPE_TUNNEL;
1850                 if (!multi_goto_secret)
1851                         multi_goto_secret = 2;
1852         }
1853         else if (buf[2] == 1)
1854         {
1855                 HUD_init_message("%s %s", Players[(int)buf[1]].callsign, TXT_HAS_FOUND_SECRET);
1856                 if (Game_mode & GM_NETWORK)
1857                         Players[(int)buf[1]].connected = CONNECT_FOUND_SECRET;
1858                 if (!multi_goto_secret)
1859                         multi_goto_secret = 1;
1860         }
1861         create_player_appearance_effect(&Objects[objnum]);
1862         multi_make_player_ghost(buf[1]);
1863 }
1864
1865 void
1866 multi_do_remobj(char *buf)
1867 {
1868         short objnum; // which object to remove
1869         short local_objnum;
1870         sbyte obj_owner; // which remote list is it entered in
1871
1872         objnum = GET_INTEL_SHORT(buf + 1);
1873         obj_owner = buf[3];
1874
1875         Assert(objnum >= 0);
1876
1877         if (objnum < 1)
1878                 return;
1879
1880         local_objnum = objnum_remote_to_local(objnum, obj_owner); // translate to local objnum
1881
1882         //      mprintf((0, "multi_do_remobj: %d owner %d = %d.\n", objnum, obj_owner, local_objnum));
1883
1884         if (local_objnum < 0)
1885         {
1886                 mprintf((1, "multi_do_remobj: Could not remove referenced object.\n"));
1887                 return;
1888         }
1889
1890         if ((Objects[local_objnum].type != OBJ_POWERUP) && (Objects[local_objnum].type != OBJ_HOSTAGE))
1891         {
1892                 mprintf((1, "multi_get_remobj: tried to remove invalid type %d.\n", Objects[local_objnum].type));
1893                 return;
1894         }
1895
1896         if (Network_send_objects && network_objnum_is_past(local_objnum))
1897         {
1898                 mprintf((0, "Resetting object sync due to object removal.\n"));
1899                 Network_send_objnum = -1;
1900         }
1901         if (Objects[local_objnum].type==OBJ_POWERUP)
1902                 if (Game_mode & GM_NETWORK)
1903                 {
1904                         if (PowerupsInMine[Objects[local_objnum].id]>0)
1905                                 PowerupsInMine[Objects[local_objnum].id]--;
1906
1907                         if (multi_powerup_is_4pack (Objects[local_objnum].id))
1908                         {
1909                                 mprintf ((0,"Hey babe! Doing that wacky 4 pack stuff."));
1910
1911                                 if (PowerupsInMine[Objects[local_objnum].id-1]-4<0)
1912                                         PowerupsInMine[Objects[local_objnum].id-1]=0;
1913                                 else
1914                                         PowerupsInMine[Objects[local_objnum].id-1]-=4;
1915                         }
1916
1917                         mprintf ((0,"Decrementing powerups! %d\n",PowerupsInMine[Objects[local_objnum].id]));
1918                 }
1919
1920         Objects[local_objnum].flags |= OF_SHOULD_BE_DEAD; // quick and painless
1921
1922 }
1923
1924 void
1925 multi_do_quit(char *buf)
1926 {
1927
1928         if (Game_mode & GM_NETWORK)
1929         {
1930                 int i, n = 0;
1931
1932                 digi_play_sample( SOUND_HUD_MESSAGE, F1_0 );
1933
1934                 HUD_init_message( "%s %s", Players[(int)buf[1]].callsign, TXT_HAS_LEFT_THE_GAME);
1935
1936                 network_disconnect_player(buf[1]);
1937
1938                 if (multi_in_menu)
1939                         return;
1940
1941                 for (i = 0; i < N_players; i++)
1942                         if (Players[i].connected) n++;
1943                 if (n == 1)
1944                 {
1945                         nm_messagebox(NULL, 1, TXT_OK, TXT_YOU_ARE_ONLY);
1946                 }
1947         }
1948
1949         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
1950         {
1951                 Function_mode = FMODE_MENU;
1952                 multi_quit_game = 1;
1953                 multi_leave_menu = 1;
1954                 nm_messagebox(NULL, 1, TXT_OK, TXT_OPPONENT_LEFT);
1955                 Function_mode = FMODE_GAME;
1956                 multi_reset_stuff();
1957         }
1958         return;
1959 }
1960
1961 void
1962 multi_do_cloak(char *buf)
1963 {
1964         int pnum;
1965
1966         pnum = (int)(buf[1]);
1967
1968         Assert(pnum < N_players);
1969
1970         mprintf((0, "Cloaking player %d\n", pnum));
1971
1972         Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
1973         Players[pnum].cloak_time = GameTime;
1974         ai_do_cloak_stuff();
1975
1976 #ifndef SHAREWARE
1977         if (Game_mode & GM_MULTI_ROBOTS)
1978                 multi_strip_robots(pnum);
1979 #endif
1980
1981         if (Newdemo_state == ND_STATE_RECORDING)
1982                 newdemo_record_multi_cloak(pnum);
1983 }
1984
1985 void
1986 multi_do_decloak(char *buf)
1987 {
1988         int pnum;
1989
1990         pnum = (int)(buf[1]);
1991
1992         if (Newdemo_state == ND_STATE_RECORDING)
1993                 newdemo_record_multi_decloak(pnum);
1994
1995 }
1996
1997 void
1998 multi_do_door_open(char *buf)
1999 {
2000         int segnum;
2001         sbyte side;
2002         segment *seg;
2003         wall *w;
2004         ubyte flag;
2005
2006         segnum = GET_INTEL_SHORT(buf + 1);
2007         side = buf[3];
2008         flag= buf[4];
2009
2010         //      mprintf((0, "Opening door on side %d of segment # %d.\n", side, segnum));
2011
2012         if ((segnum < 0) || (segnum > Highest_segment_index) || (side < 0) || (side > 5))
2013         {
2014                 Int3();
2015                 return;
2016         }
2017
2018         seg = &Segments[segnum];
2019
2020         if (seg->sides[side].wall_num == -1) {  //Opening door on illegal wall
2021                 Int3();
2022                 return;
2023         }
2024
2025         w = &Walls[seg->sides[side].wall_num];
2026
2027         if (w->type == WALL_BLASTABLE)
2028         {
2029                 if (!(w->flags & WALL_BLASTED))
2030                 {
2031                         mprintf((0, "Blasting wall by remote command.\n"));
2032                         wall_destroy(seg, side);
2033                 }
2034                 return;
2035         }
2036         else if (w->state != WALL_DOOR_OPENING)
2037         {
2038                 wall_open_door(seg, side);
2039                 w->flags=flag;
2040         }
2041         else
2042                 w->flags=flag;
2043
2044         //      else
2045         //              mprintf((0, "Door already opening!\n"));
2046 }
2047
2048 void
2049 multi_do_create_explosion(char *buf)
2050 {
2051         int pnum;
2052         int count = 1;
2053
2054         pnum = buf[count++];
2055
2056         //      mprintf((0, "Creating small fireball.\n"));
2057         create_small_fireball_on_object(&Objects[Players[pnum].objnum], F1_0, 1);
2058 }
2059
2060 void
2061 multi_do_controlcen_fire(char *buf)
2062 {
2063         vms_vector to_target;
2064         char gun_num;
2065         short objnum;
2066         int count = 1;
2067
2068         memcpy(&to_target, buf+count, 12);          count += 12;
2069 #ifdef WORDS_BIGENDIAN  // swap the vector to_target
2070         to_target.x = (fix)INTEL_INT((int)to_target.x);
2071         to_target.y = (fix)INTEL_INT((int)to_target.y);
2072         to_target.z = (fix)INTEL_INT((int)to_target.z);
2073 #endif
2074         gun_num = buf[count];                       count += 1;
2075         objnum = GET_INTEL_SHORT(buf + count);      count += 2;
2076
2077         Laser_create_new_easy(&to_target, &Gun_pos[(int)gun_num], objnum, CONTROLCEN_WEAPON_NUM, 1);
2078 }
2079
2080 void
2081 multi_do_create_powerup(char *buf)
2082 {
2083         short segnum;
2084         short objnum;
2085         int my_objnum;
2086         char pnum;
2087         int count = 1;
2088         vms_vector new_pos;
2089         char powerup_type;
2090
2091         if (Endlevel_sequence || Control_center_destroyed)
2092                 return;
2093
2094         pnum = buf[count++];
2095         powerup_type = buf[count++];
2096         segnum = GET_INTEL_SHORT(buf + count); count += 2;
2097         objnum = GET_INTEL_SHORT(buf + count); count += 2;
2098
2099         if ((segnum < 0) || (segnum > Highest_segment_index)) {
2100                 Int3();
2101                 return;
2102         }
2103
2104         memcpy(&new_pos, buf+count, sizeof(vms_vector)); count+=sizeof(vms_vector);
2105 #ifdef WORDS_BIGENDIAN
2106         new_pos.x = (fix)SWAPINT((int)new_pos.x);
2107         new_pos.y = (fix)SWAPINT((int)new_pos.y);
2108         new_pos.z = (fix)SWAPINT((int)new_pos.z);
2109 #endif
2110
2111         Net_create_loc = 0;
2112         my_objnum = call_object_create_egg(&Objects[Players[(int)pnum].objnum], 1, OBJ_POWERUP, powerup_type);
2113
2114         if (my_objnum < 0) {
2115                 mprintf((0, "Could not create new powerup!\n"));
2116                 return;
2117         }
2118
2119         if (Network_send_objects && network_objnum_is_past(my_objnum))
2120         {
2121                 mprintf((0, "Resetting object sync due to powerup creation.\n"));
2122                 Network_send_objnum = -1;
2123         }
2124
2125         Objects[my_objnum].pos = new_pos;
2126
2127         vm_vec_zero(&Objects[my_objnum].mtype.phys_info.velocity);
2128
2129         obj_relink(my_objnum, segnum);
2130
2131         map_objnum_local_to_remote(my_objnum, objnum, pnum);
2132
2133         object_create_explosion(segnum, &new_pos, i2f(5), VCLIP_POWERUP_DISAPPEARANCE);
2134         mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));
2135
2136         if (Game_mode & GM_NETWORK)
2137                 PowerupsInMine[(int)powerup_type]++;
2138 }
2139
2140 void
2141 multi_do_play_sound(char *buf)
2142 {
2143         int pnum = (int)(buf[1]);
2144         int sound_num = (int)(buf[2]);
2145         fix volume = (int)(buf[3]) << 12;
2146
2147         if (!Players[pnum].connected)
2148                 return;
2149
2150         Assert(Players[pnum].objnum >= 0);
2151         Assert(Players[pnum].objnum <= Highest_object_index);
2152
2153         digi_link_sound_to_object( sound_num, Players[pnum].objnum, 0, volume);
2154 }
2155
2156 void
2157 multi_do_score(char *buf)
2158 {
2159         int pnum = (int)(buf[1]);
2160
2161         if ((pnum < 0) || (pnum >= N_players))
2162         {
2163                 Int3(); // Non-terminal, see rob
2164                 return;
2165         }
2166
2167         if (Newdemo_state == ND_STATE_RECORDING) {
2168                 int score;
2169                 score = GET_INTEL_INT(buf + 2);
2170                 newdemo_record_multi_score(pnum, score);
2171         }
2172
2173         Players[pnum].score = GET_INTEL_INT(buf + 2);
2174
2175         multi_sort_kill_list();
2176 }
2177
2178 void
2179 multi_do_trigger(char *buf)
2180 {
2181         int pnum = (int)(buf[1]);
2182         int trigger = (int)(buf[2]);
2183
2184         mprintf ((0,"MULTI doing trigger!\n"));
2185
2186         if ((pnum < 0) || (pnum >= N_players) || (pnum == Player_num))
2187         {
2188                 Int3(); // Got trigger from illegal playernum
2189                 return;
2190         }
2191         if ((trigger < 0) || (trigger >= Num_triggers))
2192         {
2193                 Int3(); // Illegal trigger number in multiplayer
2194                 return;
2195         }
2196         check_trigger_sub(trigger, pnum,0);
2197 }
2198
2199 void multi_do_drop_marker (char *buf)
2200 {
2201         int i;
2202         int pnum=(int)(buf[1]);
2203         int mesnum=(int)(buf[2]);
2204         vms_vector position;
2205
2206         if (pnum==Player_num)  // my marker? don't set it down cuz it might screw up the orientation
2207                 return;
2208
2209         position.x = GET_INTEL_INT(buf + 3);
2210         position.y = GET_INTEL_INT(buf + 7);
2211         position.z = GET_INTEL_INT(buf + 11);
2212
2213         for (i=0;i<40;i++)
2214                 MarkerMessage[(pnum*2)+mesnum][i]=buf[15+i];
2215
2216         MarkerPoint[(pnum*2)+mesnum]=position;
2217
2218         if (MarkerObject[(pnum*2)+mesnum] !=-1 && Objects[MarkerObject[(pnum*2)+mesnum]].type!=OBJ_NONE && MarkerObject[(pnum*2)+mesnum] !=0)
2219                 obj_delete(MarkerObject[(pnum*2)+mesnum]);
2220
2221         MarkerObject[(pnum*2)+mesnum] = drop_marker_object(&position,Objects[Players[Player_num].objnum].segnum,&Objects[Players[Player_num].objnum].orient,(pnum*2)+mesnum);
2222         strcpy (MarkerOwner[(pnum*2)+mesnum],Players[pnum].callsign);
2223         mprintf ((0,"Dropped player %d message: %s\n",pnum,MarkerMessage[(pnum*2)+mesnum]));
2224 }
2225
2226
2227 void multi_do_hostage_door_status(char *buf)
2228 {
2229         // Update hit point status of a door
2230
2231         int count = 1;
2232         int wallnum;
2233         fix hps;
2234
2235         wallnum = GET_INTEL_SHORT(buf + count);     count += 2;
2236         hps = GET_INTEL_INT(buf + count);           count += 4;
2237
2238         if ((wallnum < 0) || (wallnum > Num_walls) || (hps < 0) || (Walls[wallnum].type != WALL_BLASTABLE))
2239         {
2240                 Int3(); // Non-terminal, see Rob
2241                 return;
2242         }
2243
2244         //      mprintf((0, "Damaging wall number %d to %f points.\n", wallnum, f2fl(hps)));
2245
2246         if (hps < Walls[wallnum].hps)
2247                 wall_damage(&Segments[Walls[wallnum].segnum], Walls[wallnum].sidenum, Walls[wallnum].hps - hps);
2248 }
2249
2250 void multi_do_save_game(char *buf)
2251 {
2252         int count = 1;
2253         ubyte slot;
2254         uint id;
2255         char desc[25];
2256
2257         slot = *(ubyte *)(buf+count);           count += 1;
2258         id = GET_INTEL_INT(buf + count);        count += 4;
2259         memcpy( desc, &buf[count], 20 );        count += 20;
2260
2261         multi_save_game( slot, id, desc );
2262 }
2263
2264 void multi_do_restore_game(char *buf)
2265 {
2266         int count = 1;
2267         ubyte slot;
2268         uint id;
2269
2270         slot = *(ubyte *)(buf+count);           count += 1;
2271         id = GET_INTEL_INT(buf + count);        count += 4;
2272
2273         multi_restore_game( slot, id );
2274 }
2275
2276
2277 void multi_do_req_player(char *buf)
2278 {
2279         netplayer_stats ps;
2280         ubyte player_n;
2281         // Send my netplayer_stats to everyone!
2282         player_n = *(ubyte *)(buf+1);
2283         if ( (player_n == Player_num) || (player_n == 255)  )   {
2284                 extract_netplayer_stats( &ps, &Players[Player_num] );
2285                 ps.Player_num = Player_num;
2286                 ps.message_type = MULTI_SEND_PLAYER;            // SET
2287                 multi_send_data((char*)&ps, sizeof(netplayer_stats), 0);
2288         }
2289 }
2290
2291 void multi_do_send_player(char *buf)
2292 {
2293         // Got a player packet from someone!!!
2294         netplayer_stats * p;
2295         p = (netplayer_stats *)buf;
2296
2297         Assert( p->Player_num <= N_players );
2298
2299         mprintf(( 0, "Got netplayer_stats for player %d (I'm %d)\n", p->Player_num, Player_num ));
2300         mprintf(( 0, "Their shields are: %d\n", f2i(p->shields) ));
2301
2302         use_netplayer_stats( &Players[p->Player_num], p );
2303 }
2304
2305 void
2306 multi_reset_stuff(void)
2307 {
2308         // A generic, emergency function to solve problems that crop up
2309         // when a player exits quick-out from the game because of a
2310         // serial connection loss.  Fixes several weird bugs!
2311
2312         dead_player_end();
2313
2314         Players[Player_num].homing_object_dist = -F1_0; // Turn off homing sound.
2315
2316         Dead_player_camera = 0;
2317         Endlevel_sequence = 0;
2318         reset_rear_view();
2319 }
2320
2321 void
2322 multi_reset_player_object(object *objp)
2323 {
2324         int i;
2325
2326         //Init physics for a non-console player
2327
2328         Assert(OBJECT_NUMBER(objp) >= 0);
2329         Assert(OBJECT_NUMBER(objp) <= Highest_object_index);
2330         Assert((objp->type == OBJ_PLAYER) || (objp->type == OBJ_GHOST));
2331
2332         vm_vec_zero(&objp->mtype.phys_info.velocity);
2333         vm_vec_zero(&objp->mtype.phys_info.thrust);
2334         vm_vec_zero(&objp->mtype.phys_info.rotvel);
2335         vm_vec_zero(&objp->mtype.phys_info.rotthrust);
2336         objp->mtype.phys_info.brakes = objp->mtype.phys_info.turnroll = 0;
2337         objp->mtype.phys_info.mass = Player_ship->mass;
2338         objp->mtype.phys_info.drag = Player_ship->drag;
2339         //      objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE | PF_USES_THRUST);
2340         objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE);
2341
2342         //Init render info
2343
2344         objp->render_type = RT_POLYOBJ;
2345         objp->rtype.pobj_info.model_num = Player_ship->model_num;               //what model is this?
2346         objp->rtype.pobj_info.subobj_flags = 0;         //zero the flags
2347         for (i=0;i<MAX_SUBMODELS;i++)
2348                 vm_angvec_zero(&objp->rtype.pobj_info.anim_angles[i]);
2349
2350         //reset textures for this, if not player 0
2351
2352         multi_reset_object_texture (objp);
2353
2354         // Clear misc
2355
2356         objp->flags = 0;
2357
2358         if (objp->type == OBJ_GHOST)
2359                 objp->render_type = RT_NONE;
2360
2361 }
2362
2363 void multi_reset_object_texture (object *objp)
2364 {
2365         int id,i;
2366
2367         if (Game_mode & GM_TEAM)
2368                 id = get_team(objp->id);
2369         else
2370                 id = objp->id;
2371
2372         if (id == 0)
2373                 objp->rtype.pobj_info.alt_textures=0;
2374         else {
2375                 Assert(N_PLAYER_SHIP_TEXTURES == Polygon_models[objp->rtype.pobj_info.model_num].n_textures);
2376
2377                 for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
2378                         multi_player_textures[id-1][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[objp->rtype.pobj_info.model_num].first_texture+i]];
2379
2380                 multi_player_textures[id-1][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2]];
2381                 multi_player_textures[id-1][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2+1]];
2382
2383                 objp->rtype.pobj_info.alt_textures = id;
2384         }
2385 }
2386
2387
2388
2389
2390 #ifdef NETPROFILING
2391 extern int TTRecv[];
2392 extern FILE *RecieveLogFile;
2393 #endif
2394
2395 void
2396 multi_process_bigdata(char *buf, int len)
2397 {
2398         // Takes a bunch of messages, check them for validity,
2399         // and pass them to multi_process_data.
2400
2401         int type, sub_len, bytes_processed = 0;
2402
2403         while( bytes_processed < len )  {
2404                 type = buf[bytes_processed];
2405
2406                 if ( (type<0) || (type>MULTI_MAX_TYPE)) {
2407                         mprintf( (1, "multi_process_bigdata: Invalid packet type %d!\n", type ));
2408                         return;
2409                 }
2410                 sub_len = message_length[type];
2411
2412                 Assert(sub_len > 0);
2413
2414                 if ( (bytes_processed+sub_len) > len )  {
2415                         mprintf( (1, "multi_process_bigdata: packet type %d too short (%d>%d)!\n", type, (bytes_processed+sub_len), len ));
2416                         Int3();
2417                         return;
2418                 }
2419
2420                 multi_process_data(&buf[bytes_processed], sub_len);
2421                 bytes_processed += sub_len;
2422         }
2423 }
2424
2425 //
2426 // Part 2 : Functions that send communication messages to inform the other
2427 //          players of something we did.
2428 //
2429
2430 void
2431 multi_send_fire(void)
2432 {
2433         if (!Network_laser_fired)
2434                 return;
2435
2436         multibuf[0] = (char)MULTI_FIRE;
2437         multibuf[1] = (char)Player_num;
2438         multibuf[2] = (char)Network_laser_gun;
2439         multibuf[3] = (char)Network_laser_level;
2440         multibuf[4] = (char)Network_laser_flags;
2441         multibuf[5] = (char)Network_laser_fired;
2442
2443         PUT_INTEL_SHORT(multibuf+6, Network_laser_track);
2444
2445         multi_send_data(multibuf, 8, 0);
2446
2447         Network_laser_fired = 0;
2448 }
2449
2450 void
2451 multi_send_destroy_controlcen(int objnum, int player)
2452 {
2453         if (player == Player_num)
2454                 HUD_init_message(TXT_YOU_DEST_CONTROL);
2455         else if ((player > 0) && (player < N_players))
2456                 HUD_init_message("%s %s", Players[player].callsign, TXT_HAS_DEST_CONTROL);
2457         else
2458                 HUD_init_message(TXT_CONTROL_DESTROYED);
2459
2460         multibuf[0] = (char)MULTI_CONTROLCEN;
2461         PUT_INTEL_SHORT(multibuf+1, objnum);
2462         multibuf[3] = player;
2463         multi_send_data(multibuf, 4, 2);
2464 }
2465
2466 void multi_send_drop_marker (int player,vms_vector position,char messagenum,char text[])
2467 {
2468         int i;
2469
2470         if (player<N_players)
2471         {
2472                 mprintf ((0,"Sending MARKER drop!\n"));
2473                 multibuf[0]=(char)MULTI_MARKER;
2474                 multibuf[1]=(char)player;
2475                 multibuf[2]=messagenum;
2476                 PUT_INTEL_INT(multibuf+3, position.x);
2477                 PUT_INTEL_INT(multibuf+7, position.y);
2478                 PUT_INTEL_INT(multibuf+11, position.z);
2479                 for (i=0;i<40;i++)
2480                         multibuf[15+i]=text[i];
2481         }
2482         multi_send_data(multibuf, 55, 1);
2483 }
2484
2485 void
2486 multi_send_endlevel_start(int secret)
2487 {
2488         multibuf[0] = (char)MULTI_ENDLEVEL_START;
2489         multibuf[1] = Player_num;
2490         multibuf[2] = (char)secret;
2491
2492         if ((secret) && !multi_goto_secret)
2493                 multi_goto_secret = 1;
2494         else if (!multi_goto_secret)
2495                 multi_goto_secret = 2;
2496
2497         multi_send_data(multibuf, 3, 1);
2498         if (Game_mode & GM_NETWORK)
2499         {
2500                 Players[Player_num].connected = 5;
2501                 network_send_endlevel_packet();
2502         }
2503 }
2504
2505 void
2506 multi_send_player_explode(char type)
2507 {
2508         int count = 0;
2509         int i;
2510
2511         Assert( (type == MULTI_PLAYER_DROP) || (type == MULTI_PLAYER_EXPLODE) );
2512
2513         multi_send_position(Players[Player_num].objnum);
2514
2515         if (Network_send_objects)
2516         {
2517                 mprintf((0, "Resetting object sync due to player explosion.\n"));
2518                 Network_send_objnum = -1;
2519         }
2520
2521         multibuf[count++] = type;
2522         multibuf[count++] = Player_num;
2523
2524         PUT_INTEL_SHORT(multibuf+count, Players[Player_num].primary_weapon_flags);
2525         count += 2;
2526         PUT_INTEL_SHORT(multibuf+count, Players[Player_num].secondary_weapon_flags);
2527         count += 2;
2528         multibuf[count++] = (char)Players[Player_num].laser_level;
2529
2530         multibuf[count++] = (char)Players[Player_num].secondary_ammo[HOMING_INDEX];
2531         multibuf[count++] = (char)Players[Player_num].secondary_ammo[CONCUSSION_INDEX];
2532         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_INDEX];
2533         multibuf[count++] = (char)Players[Player_num].secondary_ammo[MEGA_INDEX];
2534         multibuf[count++] = (char)Players[Player_num].secondary_ammo[PROXIMITY_INDEX];
2535
2536         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE1_INDEX];
2537         multibuf[count++] = (char)Players[Player_num].secondary_ammo[GUIDED_INDEX];
2538         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_MINE_INDEX];
2539         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE4_INDEX];
2540         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE5_INDEX];
2541
2542         PUT_INTEL_SHORT(multibuf+count, Players[Player_num].primary_ammo[VULCAN_INDEX] );
2543         count += 2;
2544         PUT_INTEL_SHORT(multibuf+count, Players[Player_num].primary_ammo[GAUSS_INDEX] );
2545         count += 2;
2546         PUT_INTEL_INT(multibuf+count, Players[Player_num].flags );
2547         count += 4;
2548
2549         multibuf[count++] = Net_create_loc;
2550
2551         Assert(Net_create_loc <= MAX_NET_CREATE_OBJECTS);
2552
2553         memset(multibuf+count, -1, MAX_NET_CREATE_OBJECTS*sizeof(short));
2554
2555         mprintf((0, "Created %d explosion objects.\n", Net_create_loc));
2556
2557         for (i = 0; i < Net_create_loc; i++)
2558         {
2559                 if (Net_create_objnums[i] <= 0) {
2560                         Int3(); // Illegal value in created egg object numbers
2561                         count +=2;
2562                         continue;
2563                 }
2564
2565                 PUT_INTEL_SHORT(multibuf+count, Net_create_objnums[i]); count += 2;
2566
2567                 // We created these objs so our local number = the network number
2568                 map_objnum_local_to_local((short)Net_create_objnums[i]);
2569         }
2570
2571         Net_create_loc = 0;
2572
2573         //      mprintf((1, "explode message size = %d, max = %d.\n", count, message_length[MULTI_PLAYER_EXPLODE]));
2574
2575         if (count > message_length[MULTI_PLAYER_EXPLODE])
2576         {
2577                 Int3(); // See Rob
2578         }
2579
2580         multi_send_data(multibuf, message_length[MULTI_PLAYER_EXPLODE], 2);
2581         if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
2582                 multi_send_decloak();
2583         if (Game_mode & GM_MULTI_ROBOTS)
2584                 multi_strip_robots(Player_num);
2585 }
2586
2587 extern ubyte Secondary_weapon_to_powerup[];
2588 extern ubyte Primary_weapon_to_powerup[];
2589
2590 // put a lid on how many objects will be spewed by an exploding player
2591 // to prevent rampant powerups in netgames
2592
2593 void multi_cap_objects ()
2594 {
2595         char type,flagtype;
2596         int index;
2597
2598         if (!(Game_mode & GM_NETWORK))
2599                 return;
2600
2601         for (index=0;index<MAX_PRIMARY_WEAPONS;index++)
2602         {
2603                 type=Primary_weapon_to_powerup[index];
2604                 if (PowerupsInMine[(int)type]>=MaxPowerupsAllowed[(int)type])
2605                         if(Players[Player_num].primary_weapon_flags & (1 << index))
2606                         {
2607                                 mprintf ((0,"PIM=%d MPA=%d\n",PowerupsInMine[(int)type],MaxPowerupsAllowed[(int)type]));
2608                                 mprintf ((0,"Killing a primary cuz there's too many! (%d)\n",(int)type));
2609                                 Players[Player_num].primary_weapon_flags&=(~(1 << index));
2610                         }
2611         }
2612
2613
2614         // Don't do the adjustment stuff for Hoard mode
2615         if (!(Game_mode & GM_HOARD))
2616                 Players[Player_num].secondary_ammo[2]/=4;
2617
2618         Players[Player_num].secondary_ammo[7]/=4;
2619
2620         for (index=0;index<MAX_SECONDARY_WEAPONS;index++)
2621         {
2622                 if ((Game_mode & GM_HOARD) && index==PROXIMITY_INDEX)
2623                         continue;
2624
2625                 type=Secondary_weapon_to_powerup[index];
2626
2627                 if ((Players[Player_num].secondary_ammo[index]+PowerupsInMine[(int)type])>MaxPowerupsAllowed[(int)type])
2628                 {
2629                         if (MaxPowerupsAllowed[(int)type]-PowerupsInMine[(int)type]<0)
2630                                 Players[Player_num].secondary_ammo[index]=0;
2631                         else
2632                                 Players[Player_num].secondary_ammo[index]=(MaxPowerupsAllowed[(int)type]-PowerupsInMine[(int)type]);
2633
2634                         mprintf ((0,"Hey! I killed secondary type %d because PIM=%d MPA=%d\n",(int)type,PowerupsInMine[(int)type],MaxPowerupsAllowed[(int)type]));
2635                 }
2636         }
2637
2638         if (!(Game_mode & GM_HOARD))
2639                 Players[Player_num].secondary_ammo[2]*=4;
2640         Players[Player_num].secondary_ammo[7]*=4;
2641
2642         if (Players[Player_num].laser_level > MAX_LASER_LEVEL)
2643                 if (PowerupsInMine[POW_SUPER_LASER]+1 > MaxPowerupsAllowed[POW_SUPER_LASER])
2644                         Players[Player_num].laser_level=0;
2645
2646         if (Players[Player_num].flags & PLAYER_FLAGS_QUAD_LASERS)
2647                 if (PowerupsInMine[POW_QUAD_FIRE]+1 > MaxPowerupsAllowed[POW_QUAD_FIRE])
2648                         Players[Player_num].flags&=(~PLAYER_FLAGS_QUAD_LASERS);
2649
2650         if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
2651                 if (PowerupsInMine[POW_CLOAK]+1 > MaxPowerupsAllowed[POW_CLOAK])
2652                         Players[Player_num].flags&=(~PLAYER_FLAGS_CLOAKED);
2653
2654         if (Players[Player_num].flags & PLAYER_FLAGS_MAP_ALL)
2655                 if (PowerupsInMine[POW_FULL_MAP]+1 > MaxPowerupsAllowed[POW_FULL_MAP])
2656                         Players[Player_num].flags&=(~PLAYER_FLAGS_MAP_ALL);
2657
2658         if (Players[Player_num].flags & PLAYER_FLAGS_AFTERBURNER)
2659                 if (PowerupsInMine[POW_AFTERBURNER]+1 > MaxPowerupsAllowed[POW_AFTERBURNER])
2660                         Players[Player_num].flags&=(~PLAYER_FLAGS_AFTERBURNER);
2661
2662         if (Players[Player_num].flags & PLAYER_FLAGS_AMMO_RACK)
2663                 if (PowerupsInMine[POW_AMMO_RACK]+1 > MaxPowerupsAllowed[POW_AMMO_RACK])
2664                         Players[Player_num].flags&=(~PLAYER_FLAGS_AMMO_RACK);
2665
2666         if (Players[Player_num].flags & PLAYER_FLAGS_CONVERTER)
2667                 if (PowerupsInMine[POW_CONVERTER]+1 > MaxPowerupsAllowed[POW_CONVERTER])
2668                         Players[Player_num].flags&=(~PLAYER_FLAGS_CONVERTER);
2669
2670         if (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT)
2671                 if (PowerupsInMine[POW_HEADLIGHT]+1 > MaxPowerupsAllowed[POW_HEADLIGHT])
2672                         Players[Player_num].flags&=(~PLAYER_FLAGS_HEADLIGHT);
2673
2674         if (Game_mode & GM_CAPTURE)
2675         {
2676                 if (Players[Player_num].flags & PLAYER_FLAGS_FLAG)
2677                 {
2678                         if (get_team(Player_num)==TEAM_RED)
2679                                 flagtype=POW_FLAG_BLUE;
2680                         else
2681                                 flagtype=POW_FLAG_RED;
2682
2683                         if (PowerupsInMine[(int)flagtype]+1 > MaxPowerupsAllowed[(int)flagtype])
2684                                 Players[Player_num].flags&=(~PLAYER_FLAGS_FLAG);
2685                 }
2686         }
2687
2688 }
2689
2690 // adds players inventory to multi cap
2691
2692 void multi_adjust_cap_for_player (int pnum)
2693 {
2694         char type;
2695
2696         int index;
2697
2698         if (!(Game_mode & GM_NETWORK))
2699                 return;
2700
2701         for (index=0;index<MAX_PRIMARY_WEAPONS;index++)
2702         {
2703                 type=Primary_weapon_to_powerup[index];
2704                 if (Players[pnum].primary_weapon_flags & (1 << index))
2705                     MaxPowerupsAllowed[(int)type]++;
2706         }
2707
2708         for (index=0;index<MAX_SECONDARY_WEAPONS;index++)
2709         {
2710                 type=Secondary_weapon_to_powerup[index];
2711                 MaxPowerupsAllowed[(int)type]+=Players[pnum].secondary_ammo[index];
2712         }
2713
2714         if (Players[pnum].laser_level > MAX_LASER_LEVEL)
2715                 MaxPowerupsAllowed[POW_SUPER_LASER]++;
2716
2717         if (Players[pnum].flags & PLAYER_FLAGS_QUAD_LASERS)
2718                 MaxPowerupsAllowed[POW_QUAD_FIRE]++;
2719
2720         if (Players[pnum].flags & PLAYER_FLAGS_CLOAKED)
2721                 MaxPowerupsAllowed[POW_CLOAK]++;
2722
2723         if (Players[pnum].flags & PLAYER_FLAGS_MAP_ALL)
2724                 MaxPowerupsAllowed[POW_FULL_MAP]++;
2725
2726         if (Players[pnum].flags & PLAYER_FLAGS_AFTERBURNER)
2727                 MaxPowerupsAllowed[POW_AFTERBURNER]++;
2728
2729         if (Players[pnum].flags & PLAYER_FLAGS_AMMO_RACK)
2730                 MaxPowerupsAllowed[POW_AMMO_RACK]++;
2731
2732         if (Players[pnum].flags & PLAYER_FLAGS_CONVERTER)
2733                 MaxPowerupsAllowed[POW_CONVERTER]++;
2734
2735         if (Players[pnum].flags & PLAYER_FLAGS_HEADLIGHT)
2736                 MaxPowerupsAllowed[POW_HEADLIGHT]++;
2737 }
2738
2739 void multi_adjust_remote_cap (int pnum)
2740 {
2741         char type;
2742
2743         int index;
2744
2745         if (!(Game_mode & GM_NETWORK))
2746                 return;
2747
2748         for (index=0;index<MAX_PRIMARY_WEAPONS;index++)
2749         {
2750                 type=Primary_weapon_to_powerup[index];
2751                 if (Players[pnum].primary_weapon_flags & (1 << index))
2752                     PowerupsInMine[(int)type]++;
2753         }
2754
2755         for (index=0;index<MAX_SECONDARY_WEAPONS;index++)
2756         {
2757                 type=Secondary_weapon_to_powerup[index];
2758
2759                 if ((Game_mode & GM_HOARD) && index==2)
2760                         continue;
2761
2762                 if (index==2 || index==7) // PROX or SMARTMINES? Those bastards...
2763                         PowerupsInMine[(int)type]+=(Players[pnum].secondary_ammo[index]/4);
2764                 else
2765                         PowerupsInMine[(int)type]+=Players[pnum].secondary_ammo[index];
2766
2767         }
2768
2769         if (Players[pnum].laser_level > MAX_LASER_LEVEL)
2770                 PowerupsInMine[POW_SUPER_LASER]++;
2771
2772         if (Players[pnum].flags & PLAYER_FLAGS_QUAD_LASERS)
2773                 PowerupsInMine[POW_QUAD_FIRE]++;
2774
2775         if (Players[pnum].flags & PLAYER_FLAGS_CLOAKED)
2776                 PowerupsInMine[POW_CLOAK]++;
2777
2778         if (Players[pnum].flags & PLAYER_FLAGS_MAP_ALL)
2779                 PowerupsInMine[POW_FULL_MAP]++;
2780
2781         if (Players[pnum].flags & PLAYER_FLAGS_AFTERBURNER)
2782                 PowerupsInMine[POW_AFTERBURNER]++;
2783
2784         if (Players[pnum].flags & PLAYER_FLAGS_AMMO_RACK)
2785                 PowerupsInMine[POW_AMMO_RACK]++;
2786
2787         if (Players[pnum].flags & PLAYER_FLAGS_CONVERTER)
2788                 PowerupsInMine[POW_CONVERTER]++;
2789
2790         if (Players[pnum].flags & PLAYER_FLAGS_HEADLIGHT)
2791                 PowerupsInMine[POW_HEADLIGHT]++;
2792
2793 }
2794
2795 void
2796 multi_send_message(void)
2797 {
2798         int loc = 0;
2799         if (Network_message_reciever != -1)
2800         {
2801                 multibuf[loc] = (char)MULTI_MESSAGE;            loc += 1;
2802                 multibuf[loc] = (char)Player_num;                       loc += 1;
2803                 strncpy(multibuf+loc, Network_message, MAX_MESSAGE_LEN); loc += MAX_MESSAGE_LEN;
2804                 multibuf[loc-1] = '\0';
2805                 multi_send_data(multibuf, loc, 0);
2806                 Network_message_reciever = -1;
2807         }
2808 }
2809
2810 void
2811 multi_send_reappear()
2812 {
2813         multibuf[0] = (char)MULTI_REAPPEAR;
2814         PUT_INTEL_SHORT(multibuf+1, Players[Player_num].objnum);
2815
2816         multi_send_data(multibuf, 3, 2);
2817         PKilledFlags[Player_num]=0;
2818 }
2819
2820 void
2821 multi_send_position(int objnum)
2822 {
2823 #ifdef WORDS_BIGENDIAN
2824         shortpos sp;
2825 #endif
2826         int count=0;
2827
2828         if (Game_mode & GM_NETWORK) {
2829                 return;
2830         }
2831
2832         multibuf[count++] = (char)MULTI_POSITION;
2833 #ifndef WORDS_BIGENDIAN
2834         create_shortpos((shortpos *)(multibuf+count), &Objects[objnum], 0);
2835         count += sizeof(shortpos);
2836 #else
2837         create_shortpos(&sp, &Objects[objnum], 1);
2838         memcpy(&(multibuf[count]), (ubyte *)(sp.bytemat), 9);
2839         count += 9;
2840         memcpy(&(multibuf[count]), (ubyte *)&(sp.xo), 14);
2841         count += 14;
2842 #endif
2843
2844         multi_send_data(multibuf, count, 0);
2845 }
2846
2847 void
2848 multi_send_kill(int objnum)
2849 {
2850         // I died, tell the world.
2851
2852         int killer_objnum;
2853         int count = 0;
2854
2855         Assert(Objects[objnum].id == Player_num);
2856         killer_objnum = Players[Player_num].killer_objnum;
2857
2858         multi_compute_kill(killer_objnum, objnum);
2859
2860         multibuf[0] = (char)MULTI_KILL;     count += 1;
2861         multibuf[1] = Player_num;           count += 1;
2862         if (killer_objnum > -1) {
2863                 short s;                // do it with variable since INTEL_SHORT won't work on return val from function.
2864
2865                 s = (short)objnum_local_to_remote(killer_objnum, (sbyte *)&multibuf[count+2]);
2866                 PUT_INTEL_SHORT(multibuf+count, s);
2867         }
2868         else
2869         {
2870                 PUT_INTEL_SHORT(multibuf+count, -1);
2871                 multibuf[count+2] = (char)-1;
2872         }
2873         count += 3;
2874         multi_send_data(multibuf, count, 1);
2875
2876 #ifndef SHAREWARE
2877         if (Game_mode & GM_MULTI_ROBOTS)
2878                 multi_strip_robots(Player_num);
2879 #endif
2880 }
2881
2882 void
2883 multi_send_remobj(int objnum)
2884 {
2885         // Tell the other guy to remove an object from his list
2886
2887         sbyte obj_owner;
2888         short remote_objnum;
2889
2890         if (Objects[objnum].type==OBJ_POWERUP && (Game_mode & GM_NETWORK))
2891     {
2892                 if (PowerupsInMine[Objects[objnum].id] > 0)
2893                 {
2894                         PowerupsInMine[Objects[objnum].id]--;
2895                         if (multi_powerup_is_4pack (Objects[objnum].id))
2896                         {
2897                                 mprintf ((0,"Hey babe! Doing that wacky 4 pack stuff."));
2898
2899                                 if (PowerupsInMine[Objects[objnum].id-1]-4<0)
2900                                         PowerupsInMine[Objects[objnum].id-1]=0;
2901                                 else
2902                                         PowerupsInMine[Objects[objnum].id-1]-=4;
2903                         }
2904                 }
2905
2906         }
2907
2908         multibuf[0] = (char)MULTI_REMOVE_OBJECT;
2909
2910         remote_objnum = objnum_local_to_remote((short)objnum, &obj_owner);
2911
2912         PUT_INTEL_SHORT(multibuf+1, remote_objnum); // Map to network objnums
2913
2914         multibuf[3] = obj_owner;
2915
2916         //      mprintf((0, "multi_send_remobj: %d = %d owner %d.\n", objnum, remote_objnum, obj_owner));
2917
2918         multi_send_data(multibuf, 4, 0);
2919
2920         if (Network_send_objects && network_objnum_is_past(objnum))
2921         {
2922                 mprintf((0, "Resetting object sync due to object removal.\n"));
2923                 Network_send_objnum = -1;
2924         }
2925 }
2926
2927 void
2928 multi_send_quit(int why)
2929 {
2930         // I am quitting the game, tell the other guy the bad news.
2931
2932         Assert (why == MULTI_QUIT);
2933
2934         multibuf[0] = (char)why;
2935         multibuf[1] = Player_num;
2936         multi_send_data(multibuf, 2, 1);
2937
2938 }
2939
2940 void
2941 multi_send_cloak(void)
2942 {
2943         // Broadcast a change in our pflags (made to support cloaking)
2944
2945         multibuf[0] = MULTI_CLOAK;
2946         multibuf[1] = (char)Player_num;
2947
2948         multi_send_data(multibuf, 2, 1);
2949
2950 #ifndef SHAREWARE
2951         if (Game_mode & GM_MULTI_ROBOTS)
2952                 multi_strip_robots(Player_num);
2953 #endif
2954 }
2955
2956 void
2957 multi_send_decloak(void)
2958 {
2959         // Broadcast a change in our pflags (made to support cloaking)
2960
2961         multibuf[0] = MULTI_DECLOAK;
2962         multibuf[1] = (char)Player_num;
2963
2964         multi_send_data(multibuf, 2, 1);
2965 }
2966
2967 void
2968 multi_send_door_open(int segnum, int side,ubyte flag)
2969 {
2970         // When we open a door make sure everyone else opens that door
2971
2972         multibuf[0] = MULTI_DOOR_OPEN;
2973         PUT_INTEL_SHORT(multibuf+1, segnum );
2974         multibuf[3] = (sbyte)side;
2975         multibuf[4] = flag;
2976
2977         multi_send_data(multibuf, 5, 2);
2978 }
2979
2980 extern void network_send_naked_packet (char *,short,int);
2981
2982 void
2983 multi_send_door_open_specific(int pnum,int segnum, int side,ubyte flag)
2984 {
2985         // For sending doors only to a specific person (usually when they're joining)
2986
2987         Assert (Game_mode & GM_NETWORK);
2988         //   Assert (pnum>-1 && pnum<N_players);
2989
2990         multibuf[0] = MULTI_DOOR_OPEN;
2991         PUT_INTEL_SHORT(multibuf+1, segnum);
2992         multibuf[3] = (sbyte)side;
2993         multibuf[4] = flag;
2994
2995         network_send_naked_packet(multibuf, 5, pnum);
2996 }
2997
2998 //
2999 // Part 3 : Functions that change or prepare the game for multiplayer use.
3000 //          Not including functions needed to syncronize or start the
3001 //          particular type of multiplayer game.  Includes preparing the
3002 //                      mines, player structures, etc.
3003
3004 void
3005 multi_send_create_explosion(int pnum)
3006 {
3007         // Send all data needed to create a remote explosion
3008
3009         int count = 0;
3010
3011         multibuf[count] = MULTI_CREATE_EXPLOSION;       count += 1;
3012         multibuf[count] = (sbyte)pnum;                  count += 1;
3013         //                                                                                                      -----------
3014         //                                                                                                      Total size = 2
3015
3016         multi_send_data(multibuf, count, 0);
3017 }
3018
3019 void
3020 multi_send_controlcen_fire(vms_vector *to_goal, int best_gun_num, int objnum)
3021 {
3022 #ifdef WORDS_BIGENDIAN
3023         vms_vector swapped_vec;
3024 #endif
3025         int count = 0;
3026
3027         multibuf[count] = MULTI_CONTROLCEN_FIRE;                count +=  1;
3028 #ifndef WORDS_BIGENDIAN
3029         memcpy(multibuf+count, to_goal, 12);                    count += 12;
3030 #else
3031         swapped_vec.x = (fix)INTEL_INT( (int)to_goal->x );
3032         swapped_vec.y = (fix)INTEL_INT( (int)to_goal->y );
3033         swapped_vec.z = (fix)INTEL_INT( (int)to_goal->z );
3034         memcpy(multibuf+count, &swapped_vec, 12);                               count += 12;
3035 #endif
3036         multibuf[count] = (char)best_gun_num;                   count +=  1;
3037         PUT_INTEL_SHORT(multibuf+count, objnum );     count +=  2;
3038         //                                                                                                                      ------------
3039         //                                                                                                                      Total  = 16
3040         multi_send_data(multibuf, count, 0);
3041 }
3042
3043 void
3044 multi_send_create_powerup(int powerup_type, int segnum, int objnum, vms_vector *pos)
3045 {
3046         // Create a powerup on a remote machine, used for remote
3047         // placement of used powerups like missiles and cloaking
3048         // powerups.
3049
3050 #ifdef WORDS_BIGENDIAN
3051         vms_vector swapped_vec;
3052 #endif
3053         int count = 0;
3054
3055         if (Game_mode & GM_NETWORK)
3056                 PowerupsInMine[powerup_type]++;
3057
3058         multibuf[count] = MULTI_CREATE_POWERUP;         count += 1;
3059         multibuf[count] = Player_num;                                      count += 1;
3060         multibuf[count] = powerup_type;                                 count += 1;
3061         PUT_INTEL_SHORT(multibuf+count, segnum );     count += 2;
3062         PUT_INTEL_SHORT(multibuf+count, objnum );     count += 2;
3063 #ifndef WORDS_BIGENDIAN
3064         memcpy(multibuf+count, pos, sizeof(vms_vector));  count += sizeof(vms_vector);
3065 #else
3066         swapped_vec.x = (fix)INTEL_INT( (int)pos->x );
3067         swapped_vec.y = (fix)INTEL_INT( (int)pos->y );
3068         swapped_vec.z = (fix)INTEL_INT( (int)pos->z );
3069         memcpy(multibuf+count, &swapped_vec, 12);                               count += 12;
3070 #endif
3071         //                                                                                                            -----------
3072         //                                                                                                            Total =  19
3073         multi_send_data(multibuf, count, 2);
3074
3075         if (Network_send_objects && network_objnum_is_past(objnum))
3076         {
3077                 mprintf((0, "Resetting object sync due to powerup creation.\n"));
3078                 Network_send_objnum = -1;
3079         }
3080
3081         mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));
3082         map_objnum_local_to_local(objnum);
3083 }
3084
3085 void
3086 multi_send_play_sound(int sound_num, fix volume)
3087 {
3088         int count = 0;
3089         multibuf[count] = MULTI_PLAY_SOUND;                     count += 1;
3090         multibuf[count] = Player_num;                                   count += 1;
3091         multibuf[count] = (char)sound_num;                      count += 1;
3092         multibuf[count] = (char)(volume >> 12); count += 1;
3093         //                                                                                                         -----------
3094         //                                                                                                         Total = 4
3095         multi_send_data(multibuf, count, 0);
3096 }
3097
3098 void
3099 multi_send_audio_taunt(int taunt_num)
3100 {
3101         return; // Taken out, awaiting sounds..
3102
3103 #if 0
3104         int audio_taunts[4] = {
3105                 SOUND_CONTROL_CENTER_WARNING_SIREN,
3106                 SOUND_HOSTAGE_RESCUED,
3107                 SOUND_REFUEL_STATION_GIVING_FUEL,
3108                 SOUND_BAD_SELECTION
3109         };
3110
3111
3112         Assert(taunt_num >= 0);
3113         Assert(taunt_num < 4);
3114
3115         digi_play_sample( audio_taunts[taunt_num], F1_0 );
3116         multi_send_play_sound(audio_taunts[taunt_num], F1_0);
3117 #endif
3118 }
3119
3120 void
3121 multi_send_score(void)
3122 {
3123         // Send my current score to all other players so it will remain
3124         // synced.
3125         int count = 0;
3126
3127         if (Game_mode & GM_MULTI_COOP) {
3128                 multi_sort_kill_list();
3129                 multibuf[count] = MULTI_SCORE;                  count += 1;
3130                 multibuf[count] = Player_num;                           count += 1;
3131                 PUT_INTEL_INT(multibuf+count, Players[Player_num].score);  count += 4;
3132                 multi_send_data(multibuf, count, 0);
3133         }
3134 }
3135
3136
3137 void
3138 multi_send_save_game(ubyte slot, uint id, char * desc)
3139 {
3140         int count = 0;
3141
3142         multibuf[count] = MULTI_SAVE_GAME;              count += 1;
3143         multibuf[count] = slot;                         count += 1;    // Save slot=0
3144         PUT_INTEL_INT(multibuf+count, id );           count += 4;             // Save id
3145         memcpy( &multibuf[count], desc, 20 ); count += 20;
3146
3147         multi_send_data(multibuf, count, 2);
3148 }
3149
3150 void
3151 multi_send_restore_game(ubyte slot, uint id)
3152 {
3153         int count = 0;
3154
3155         multibuf[count] = MULTI_RESTORE_GAME;   count += 1;
3156         multibuf[count] = slot;                                                 count += 1;             // Save slot=0
3157         PUT_INTEL_INT(multibuf+count, id);         count += 4;             // Save id
3158
3159         multi_send_data(multibuf, count, 2);
3160 }
3161
3162 void
3163 multi_send_netplayer_stats_request(ubyte player_num)
3164 {
3165         int count = 0;
3166
3167         multibuf[count] = MULTI_REQ_PLAYER;     count += 1;
3168         multibuf[count] = player_num;                   count += 1;
3169
3170         multi_send_data(multibuf, count, 0 );
3171 }
3172
3173 void
3174 multi_send_trigger(int triggernum)
3175 {
3176         // Send an even to trigger something in the mine
3177
3178         int count = 0;
3179
3180         multibuf[count] = MULTI_TRIGGER;                                count += 1;
3181         multibuf[count] = Player_num;                                   count += 1;
3182         multibuf[count] = (ubyte)triggernum;            count += 1;
3183
3184         mprintf ((0,"Sending trigger %d\n",triggernum));
3185
3186         multi_send_data(multibuf, count, 1);
3187         //multi_send_data(multibuf, count, 1); // twice?
3188 }
3189
3190 void
3191 multi_send_hostage_door_status(int wallnum)
3192 {
3193         // Tell the other player what the hit point status of a hostage door
3194         // should be
3195
3196         int count = 0;
3197
3198         Assert(Walls[wallnum].type == WALL_BLASTABLE);
3199
3200         multibuf[count] = MULTI_HOSTAGE_DOOR;           count += 1;
3201         PUT_INTEL_SHORT(multibuf+count, wallnum );           count += 2;
3202         PUT_INTEL_INT(multibuf+count, Walls[wallnum].hps );  count += 4;
3203
3204         //      mprintf((0, "Door %d damaged by %f points.\n", wallnum, f2fl(Walls[wallnum].hps)));
3205
3206         multi_send_data(multibuf, count, 0);
3207 }
3208
3209 extern int ConsistencyCount;
3210 extern int Drop_afterburner_blob_flag;
3211 int PhallicLimit=0;
3212 int PhallicMan=-1;
3213
3214 void multi_prep_level(void)
3215 {
3216         // Do any special stuff to the level required for serial games
3217         // before we begin playing in it.
3218
3219         // Player_num MUST be set before calling this procedure.
3220
3221         // This function must be called before checksuming the Object array,
3222         // since the resulting checksum with depend on the value of Player_num
3223         // at the time this is called.
3224
3225         int i,ng=0;
3226         int     cloak_count, inv_count;
3227
3228         Assert(Game_mode & GM_MULTI);
3229
3230         Assert(NumNetPlayerPositions > 0);
3231
3232         PhallicLimit=0;
3233         PhallicMan=-1;
3234         Drop_afterburner_blob_flag=0;
3235         ConsistencyCount=0;
3236
3237         for (i=0;i<MAX_NUM_NET_PLAYERS;i++)
3238                 PKilledFlags[i]=0;
3239
3240         for (i = 0; i < NumNetPlayerPositions; i++)
3241         {
3242                 if (i != Player_num)
3243                         Objects[Players[i].objnum].control_type = CT_REMOTE;
3244                 Objects[Players[i].objnum].movement_type = MT_PHYSICS;
3245                 multi_reset_player_object(&Objects[Players[i].objnum]);
3246                 LastPacketTime[i] = 0;
3247         }
3248
3249 #ifndef SHAREWARE
3250         for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
3251         {
3252                 robot_controlled[i] = -1;
3253                 robot_agitation[i] = 0;
3254                 robot_fired[i] = 0;
3255         }
3256 #endif
3257
3258         Viewer = ConsoleObject = &Objects[Players[Player_num].objnum];
3259
3260         if (!(Game_mode & GM_MULTI_COOP))
3261         {
3262                 multi_delete_extra_objects(); // Removes monsters from level
3263         }
3264
3265         if (Game_mode & GM_MULTI_ROBOTS)
3266         {
3267                 multi_set_robot_ai(); // Set all Robot AI to types we can cope with
3268         }
3269
3270         if (Game_mode & GM_NETWORK)
3271         {
3272                 multi_adjust_cap_for_player(Player_num);
3273                 multi_send_powerup_update();
3274                 ng=1;  // ng means network game
3275         }
3276         ng=1;
3277
3278         inv_count = 0;
3279         cloak_count = 0;
3280         for (i=0; i<=Highest_object_index; i++)
3281         {
3282                 int objnum;
3283
3284                 if ((Objects[i].type == OBJ_HOSTAGE) && !(Game_mode & GM_MULTI_COOP))
3285                 {
3286                         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);
3287                         obj_delete(i);
3288                         if (objnum != -1)
3289                         {
3290                                 Objects[objnum].rtype.vclip_info.vclip_num = Powerup_info[POW_SHIELD_BOOST].vclip_num;
3291                                 Objects[objnum].rtype.vclip_info.frametime = Vclip[Objects[objnum].rtype.vclip_info.vclip_num].frame_time;
3292                                 Objects[objnum].rtype.vclip_info.framenum = 0;
3293                                 Objects[objnum].mtype.phys_info.drag = 512;     //1024;
3294                                 Objects[objnum].mtype.phys_info.mass = F1_0;
3295                                 vm_vec_zero(&Objects[objnum].mtype.phys_info.velocity);
3296                         }
3297                         continue;
3298                 }
3299
3300                 if (Objects[i].type == OBJ_POWERUP)
3301                 {
3302                         if (Objects[i].id == POW_EXTRA_LIFE)
3303                         {
3304                                 if (ng && !Netgame.DoInvulnerability)
3305                                 {
3306                                         Objects[i].id = POW_SHIELD_BOOST;
3307                                         Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3308                                         Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3309                                 }
3310                                 else
3311                                 {
3312                                         Objects[i].id = POW_INVULNERABILITY;
3313                                         Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3314                                         Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3315                                 }
3316
3317                         }
3318
3319                         if (!(Game_mode & GM_MULTI_COOP))
3320                                 if ((Objects[i].id >= POW_KEY_BLUE) && (Objects[i].id <= POW_KEY_GOLD))
3321                                 {
3322                                         Objects[i].id = POW_SHIELD_BOOST;
3323                                         Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3324                                         Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3325                                 }
3326
3327                         if (Objects[i].id == POW_INVULNERABILITY) {
3328                                 if (inv_count >= 3 || (ng && !Netgame.DoInvulnerability)) {
3329                                         mprintf((0, "Bashing Invulnerability object #%i to shield.\n", i));
3330                                         Objects[i].id = POW_SHIELD_BOOST;
3331                                         Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3332                                         Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3333                                 } else
3334                                         inv_count++;
3335                         }
3336
3337                         if (Objects[i].id == POW_CLOAK) {
3338                                 if (cloak_count >= 3 || (ng && !Netgame.DoCloak)) {
3339                                         mprintf((0, "Bashing Cloak object #%i to shield.\n", i));
3340                                         Objects[i].id = POW_SHIELD_BOOST;
3341                                         Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3342                                         Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3343                                 } else
3344                                         cloak_count++;
3345                         }
3346
3347                         if (Objects[i].id == POW_AFTERBURNER && ng && !Netgame.DoAfterburner)
3348                                 bash_to_shield (i,"afterburner");
3349                         if (Objects[i].id == POW_FUSION_WEAPON && ng &&  !Netgame.DoFusions)
3350                                 bash_to_shield (i,"fusion");
3351                         if (Objects[i].id == POW_PHOENIX_WEAPON && ng && !Netgame.DoPhoenix)
3352                                 bash_to_shield (i,"phoenix");
3353
3354                         if (Objects[i].id == POW_HELIX_WEAPON && ng && !Netgame.DoHelix)
3355                                 bash_to_shield (i,"helix");
3356
3357                         if (Objects[i].id == POW_MEGA_WEAPON && ng && !Netgame.DoMegas)
3358                                 bash_to_shield (i,"mega");
3359
3360                         if (Objects[i].id == POW_SMARTBOMB_WEAPON && ng && !Netgame.DoSmarts)
3361                                 bash_to_shield (i,"smartmissile");
3362
3363                         if (Objects[i].id == POW_GAUSS_WEAPON && ng && !Netgame.DoGauss)
3364                                 bash_to_shield (i,"gauss");
3365
3366                         if (Objects[i].id == POW_VULCAN_WEAPON && ng && !Netgame.DoVulcan)
3367                                 bash_to_shield (i,"vulcan");
3368
3369                         if (Objects[i].id == POW_PLASMA_WEAPON && ng && !Netgame.DoPlasma)
3370                                 bash_to_shield (i,"plasma");
3371
3372                         if (Objects[i].id == POW_OMEGA_WEAPON && ng && !Netgame.DoOmega)
3373                                 bash_to_shield (i,"omega");
3374
3375                         if (Objects[i].id == POW_SUPER_LASER && ng && !Netgame.DoSuperLaser)
3376                                 bash_to_shield (i,"superlaser");
3377
3378                         if (Objects[i].id == POW_PROXIMITY_WEAPON && ng && !Netgame.DoProximity)
3379                                 bash_to_shield (i,"proximity");
3380
3381                         // Special: Make all proximity bombs into shields if in
3382                         // hoard mode because we use the proximity slot in the
3383                         // player struct to signify how many orbs the player has.
3384
3385                         if (Objects[i].id == POW_PROXIMITY_WEAPON && ng && (Game_mode & GM_HOARD))
3386                                 bash_to_shield (i,"proximity");
3387
3388                         if (Objects[i].id==POW_VULCAN_AMMO && ng && (!Netgame.DoVulcan && !Netgame.DoGauss))
3389                                 bash_to_shield(i,"vulcan ammo");
3390
3391                         if (Objects[i].id == POW_SPREADFIRE_WEAPON && ng && !Netgame.DoSpread)
3392                                 bash_to_shield (i,"spread");
3393                         if (Objects[i].id == POW_SMART_MINE && ng && !Netgame.DoSmartMine)
3394                                 bash_to_shield (i,"smartmine");
3395                         if (Objects[i].id == POW_SMISSILE1_1 && ng &&  !Netgame.DoFlash)
3396                                 bash_to_shield (i,"flash");
3397                         if (Objects[i].id == POW_SMISSILE1_4 && ng &&  !Netgame.DoFlash)
3398                                 bash_to_shield (i,"flash");
3399                         if (Objects[i].id == POW_GUIDED_MISSILE_1 && ng &&  !Netgame.DoGuided)
3400                                 bash_to_shield (i,"guided");
3401                         if (Objects[i].id == POW_GUIDED_MISSILE_4 && ng &&  !Netgame.DoGuided)
3402                                 bash_to_shield (i,"guided");
3403                         if (Objects[i].id == POW_EARTHSHAKER_MISSILE && ng &&  !Netgame.DoEarthShaker)
3404                                 bash_to_shield (i,"earth");
3405                         if (Objects[i].id == POW_MERCURY_MISSILE_1 && ng &&  !Netgame.DoMercury)
3406                                 bash_to_shield (i,"Mercury");
3407                         if (Objects[i].id == POW_MERCURY_MISSILE_4 && ng &&  !Netgame.DoMercury)
3408                                 bash_to_shield (i,"Mercury");
3409                         if (Objects[i].id == POW_CONVERTER && ng &&  !Netgame.DoConverter)
3410                                 bash_to_shield (i,"Converter");
3411                         if (Objects[i].id == POW_AMMO_RACK && ng &&  !Netgame.DoAmmoRack)
3412                                 bash_to_shield (i,"Ammo rack");
3413                         if (Objects[i].id == POW_HEADLIGHT && ng &&  !Netgame.DoHeadlight)
3414                                 bash_to_shield (i,"Headlight");
3415                         if (Objects[i].id == POW_LASER && ng &&  !Netgame.DoLaserUpgrade)
3416                                 bash_to_shield (i,"Laser powerup");
3417                         if (Objects[i].id == POW_HOMING_AMMO_1 && ng &&  !Netgame.DoHoming)
3418                                 bash_to_shield (i,"Homing");
3419                         if (Objects[i].id == POW_HOMING_AMMO_4 && ng &&  !Netgame.DoHoming)
3420                                 bash_to_shield (i,"Homing");
3421                         if (Objects[i].id == POW_QUAD_FIRE && ng &&  !Netgame.DoQuadLasers)
3422                                 bash_to_shield (i,"Quad Lasers");
3423                         if (Objects[i].id == POW_FLAG_BLUE && !(Game_mode & GM_CAPTURE))
3424                                 bash_to_shield (i,"Blue flag");
3425                         if (Objects[i].id == POW_FLAG_RED && !(Game_mode & GM_CAPTURE))
3426                                 bash_to_shield (i,"Red flag");
3427                 }
3428         }
3429
3430         if (Game_mode & GM_HOARD)
3431                 init_hoard_data();
3432
3433         if ((Game_mode & GM_CAPTURE) || (Game_mode & GM_HOARD))
3434                 multi_apply_goal_textures();
3435
3436         multi_sort_kill_list();
3437
3438         multi_show_player_list();
3439
3440         ConsoleObject->control_type = CT_FLYING;
3441
3442         reset_player_object();
3443
3444 }
3445
3446 int Goal_blue_segnum,Goal_red_segnum;
3447
3448 void multi_apply_goal_textures()
3449 {
3450         int             i,j,tex;
3451         segment *seg;
3452         segment2        *seg2;
3453
3454         for (i=0; i <= Highest_segment_index; i++)
3455         {
3456                 seg = &Segments[i];
3457                 seg2 = &Segment2s[i];
3458
3459                 if (seg2->special==SEGMENT_IS_GOAL_BLUE)
3460                 {
3461
3462                         Goal_blue_segnum = i;
3463
3464                         if (Game_mode & GM_HOARD)
3465                                 tex=find_goal_texture (TMI_GOAL_HOARD);
3466                         else
3467                                 tex=find_goal_texture (TMI_GOAL_BLUE);
3468
3469                         if (tex>-1)
3470                                 for (j = 0; j < 6; j++) {
3471                                         int v;
3472                                         seg->sides[j].tmap_num=tex;
3473                                         for (v=0;v<4;v++)
3474                                                 seg->sides[j].uvls[v].l = i2f(100);             //max out
3475                                 }
3476
3477                         seg2->static_light = i2f(100);  //make static light bright
3478
3479                 }
3480
3481                 if (seg2->special==SEGMENT_IS_GOAL_RED)
3482                 {
3483                         Goal_red_segnum = i;
3484
3485                         // Make both textures the same if Hoard mode
3486
3487                         if (Game_mode & GM_HOARD)
3488                                 tex=find_goal_texture (TMI_GOAL_HOARD);
3489                         else
3490                                 tex=find_goal_texture (TMI_GOAL_RED);
3491
3492                         if (tex>-1)
3493                                 for (j = 0; j < 6; j++) {
3494                                         int v;
3495                                         seg->sides[j].tmap_num=tex;
3496                                         for (v=0;v<4;v++)
3497                                                 seg->sides[j].uvls[v].l = i2f(1000);            //max out
3498                                 }
3499
3500                         seg2->static_light = i2f(100);  //make static light bright
3501                 }
3502         }
3503 }
3504 int find_goal_texture (ubyte t)
3505 {
3506         int i;
3507
3508         for (i=0;i<NumTextures;i++)
3509                 if (TmapInfo[i].flags & t)
3510                         return i;
3511
3512         Int3(); // Hey, there is no goal texture for this PIG!!!!
3513         // Edit bitmaps.tbl and designate two textures to be RED and BLUE
3514         // goal textures
3515         return (-1);
3516 }
3517
3518
3519 /* DPH: Moved to gameseq.c
3520    void bash_to_shield (int i,char *s)
3521    {
3522    int type=Objects[i].id;
3523
3524    mprintf((0, "Bashing %s object #%i to shield.\n",s, i));
3525
3526    PowerupsInMine[type]=MaxPowerupsAllowed[type]=0;
3527
3528    Objects[i].id = POW_SHIELD_BOOST;
3529    Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3530    Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3531    }
3532 */
3533
3534 void multi_set_robot_ai(void)
3535 {
3536         // Go through the objects array looking for robots and setting
3537         // them to certain supported types of NET AI behavior.
3538
3539         //      int i;
3540         //
3541         //      for (i = 0; i <= Highest_object_index; i++)
3542         //      {
3543         //              if (Objects[i].type == OBJ_ROBOT) {
3544         //                      Objects[i].ai_info.REMOTE_OWNER = -1;
3545         //                      if (Objects[i].ai_info.behavior == AIB_STATION)
3546         //                              Objects[i].ai_info.behavior = AIB_NORMAL;
3547         //              }
3548         //      }
3549 }
3550
3551 int multi_delete_extra_objects()
3552 {
3553         int i;
3554         int nnp=0;
3555         object *objp;
3556
3557         // Go through the object list and remove any objects not used in
3558         // 'Anarchy!' games.
3559
3560         // This function also prints the total number of available multiplayer
3561         // positions in this level, even though this should always be 8 or more!
3562
3563         objp = Objects;
3564         for (i=0;i<=Highest_object_index;i++) {
3565                 if ((objp->type==OBJ_PLAYER) || (objp->type==OBJ_GHOST))
3566                         nnp++;
3567                 else if ((objp->type==OBJ_ROBOT) && (Game_mode & GM_MULTI_ROBOTS))
3568                         ;
3569                 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) ) {
3570                         // Before deleting object, if it's a robot, drop it's special powerup, if any
3571                         if (objp->type == OBJ_ROBOT)
3572                                 if (objp->contains_count && (objp->contains_type == OBJ_POWERUP))
3573                                         object_create_egg(objp);
3574                         obj_delete(i);
3575                 }
3576                 objp++;
3577         }
3578
3579         return nnp;
3580 }
3581
3582 void change_playernum_to( int new_Player_num )
3583 {
3584         if (Player_num > -1)
3585                 memcpy( Players[new_Player_num].callsign, Players[Player_num].callsign, CALLSIGN_LEN+1 );
3586         Player_num = new_Player_num;
3587 }
3588
3589 int multi_all_players_alive()
3590 {
3591         int i;
3592         for (i=0;i<N_players;i++)
3593         {
3594                 if (PKilledFlags[i] && Players[i].connected)
3595                         return (0);
3596         }
3597         return (1);
3598 }
3599
3600 void multi_initiate_save_game()
3601 {
3602         uint game_id;
3603         int i, slot;
3604         char filename[128];
3605         char desc[24];
3606
3607         if ((Endlevel_sequence) || (Control_center_destroyed))
3608                 return;
3609
3610         if (!multi_all_players_alive())
3611         {
3612                 HUD_init_message ("Can't save...all players must be alive!");
3613                 return;
3614         }
3615
3616         //multi_send_netplayer_stats_request(255);
3617         //return;
3618
3619         //stop_time();
3620
3621         slot = state_get_save_file(filename, desc, 1, 0);
3622         if (!slot)      {
3623                 //start_time();
3624                 return;
3625         }
3626         slot--;
3627
3628         //start_time();
3629
3630         // Make a unique game id
3631         game_id = timer_get_fixed_seconds();
3632         game_id ^= N_players<<4;
3633         for (i=0; i<N_players; i++ )
3634                 game_id ^= *(uint *)Players[i].callsign;
3635         if ( game_id == 0 ) game_id = 1;                // 0 is invalid
3636
3637         mprintf(( 1, "Game_id = %8x\n", game_id));
3638         multi_send_save_game(slot, game_id, desc );
3639         multi_do_frame();
3640         multi_save_game(slot,game_id, desc );
3641 }
3642
3643 extern int state_get_game_id(char *);
3644
3645 void multi_initiate_restore_game()
3646 {
3647         int slot;
3648         char filename[128];
3649
3650         if ((Endlevel_sequence) || (Control_center_destroyed))
3651                 return;
3652
3653         if (!multi_all_players_alive())
3654         {
3655                 HUD_init_message ("Can't restore...all players must be alive!");
3656                 return;
3657         }
3658
3659         //stop_time();
3660         slot = state_get_restore_file(filename,1);
3661         if (!slot)      {
3662                 //start_time();
3663                 return;
3664         }
3665         state_game_id=state_get_game_id (filename);
3666         if (!state_game_id)
3667                 return;
3668
3669         slot--;
3670         //start_time();
3671         multi_send_restore_game(slot,state_game_id);
3672         multi_do_frame();
3673         multi_restore_game(slot,state_game_id);
3674 }
3675
3676 void multi_save_game(ubyte slot, uint id, char *desc)
3677 {
3678         char filename[128];
3679
3680         if ((Endlevel_sequence) || (Control_center_destroyed))
3681                 return;
3682
3683         sprintf( filename, PLAYER_DIR "%s.mg%d", Players[Player_num].callsign, slot );
3684         mprintf(( 0, "Save game %x on slot %d\n", id, slot ));
3685         HUD_init_message( "Saving game #%d, '%s'", slot, desc );
3686         stop_time();
3687         state_game_id = id;
3688         state_save_all_sub(filename, desc, 0 );
3689 }
3690
3691 void multi_restore_game(ubyte slot, uint id)
3692 {
3693         char filename[128];
3694         player saved_player;
3695         int pnum,i;
3696         int thisid;
3697
3698         if ((Endlevel_sequence) || (Control_center_destroyed))
3699                 return;
3700
3701         mprintf(( 0, "Restore game %x from slot %d\n", id, slot ));
3702         saved_player = Players[Player_num];
3703         sprintf( filename, PLAYER_DIR "%s.mg%d", Players[Player_num].callsign, slot );
3704
3705         for (i=0;i<N_players;i++)
3706                 multi_strip_robots(i);
3707
3708         thisid=state_get_game_id (filename);
3709         if (thisid!=id)
3710         {
3711                 multi_bad_restore ();
3712                 return;
3713         }
3714
3715         pnum=state_restore_all_sub( filename, 1, 0 );
3716
3717         mprintf ((0,"StateId=%d ThisID=%d\n",state_game_id,id));
3718
3719 #if 0
3720         if (state_game_id != id )       {
3721                 // Game doesn't match!!!
3722                 nm_messagebox( "Error", 1, "Ok", "Cannot restore saved game" );
3723                 Game_mode |= GM_GAME_OVER;
3724                 Function_mode = FMODE_MENU;
3725                 longjmp(LeaveGame, 0);
3726         }
3727
3728         change_playernum_to(pnum-1);
3729         memcpy( Players[Player_num].callsign, saved_player.callsign, CALLSIGN_LEN+1 );
3730         memcpy( Players[Player_num].net_address, saved_player.net_address, 6 );
3731         Players[Player_num].connected = saved_player.connected;
3732         Players[Player_num].n_packets_got  = saved_player.n_packets_got;
3733         Players[Player_num].n_packets_sent = saved_player.n_packets_sent;
3734         Viewer = ConsoleObject = &Objects[pnum-1];
3735 #endif
3736
3737 }
3738
3739
3740 void extract_netplayer_stats( netplayer_stats *ps, player * pd )
3741 {
3742         int i;
3743
3744         ps->flags = INTEL_INT(pd->flags);                                   // Powerup flags, see below...
3745         ps->energy = (fix)INTEL_INT(pd->energy);                            // Amount of energy remaining.
3746         ps->shields = (fix)INTEL_INT(pd->shields);                          // shields remaining (protection)
3747         ps->lives = pd->lives;                                              // Lives remaining, 0 = game over.
3748         ps->laser_level = pd->laser_level;                                  // Current level of the laser.
3749         ps->primary_weapon_flags=pd->primary_weapon_flags;                  // bit set indicates the player has this weapon.
3750         ps->secondary_weapon_flags=pd->secondary_weapon_flags;              // bit set indicates the player has this weapon.
3751         for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
3752                 ps->primary_ammo[i] = INTEL_SHORT(pd->primary_ammo[i]);
3753         for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
3754                 ps->secondary_ammo[i] = INTEL_SHORT(pd->secondary_ammo[i]);
3755
3756         //memcpy( ps->primary_ammo, pd->primary_ammo, MAX_PRIMARY_WEAPONS*sizeof(short) );        // How much ammo of each type.
3757         //memcpy( ps->secondary_ammo, pd->secondary_ammo, MAX_SECONDARY_WEAPONS*sizeof(short) ); // How much ammo of each type.
3758
3759         ps->last_score=INTEL_INT(pd->last_score);                           // Score at beginning of current level.
3760         ps->score=INTEL_INT(pd->score);                                     // Current score.
3761         ps->cloak_time=(fix)INTEL_INT(pd->cloak_time);                      // Time cloaked
3762         ps->homing_object_dist=(fix)INTEL_INT(pd->homing_object_dist);      // Distance of nearest homing object.
3763         ps->invulnerable_time=(fix)INTEL_INT(pd->invulnerable_time);        // Time invulnerable
3764         ps->KillGoalCount=INTEL_SHORT(pd->KillGoalCount);
3765         ps->net_killed_total=INTEL_SHORT(pd->net_killed_total);             // Number of times killed total
3766         ps->net_kills_total=INTEL_SHORT(pd->net_kills_total);               // Number of net kills total
3767         ps->num_kills_level=INTEL_SHORT(pd->num_kills_level);               // Number of kills this level
3768         ps->num_kills_total=INTEL_SHORT(pd->num_kills_total);               // Number of kills total
3769         ps->num_robots_level=INTEL_SHORT(pd->num_robots_level);             // Number of initial robots this level
3770         ps->num_robots_total=INTEL_SHORT(pd->num_robots_total);             // Number of robots total
3771         ps->hostages_rescued_total=INTEL_SHORT(pd->hostages_rescued_total); // Total number of hostages rescued.
3772         ps->hostages_total=INTEL_SHORT(pd->hostages_total);                 // Total number of hostages.
3773         ps->hostages_on_board=pd->hostages_on_board;                        // Number of hostages on ship.
3774 }
3775
3776 void use_netplayer_stats( player * ps, netplayer_stats *pd )
3777 {
3778         int i;
3779
3780         ps->flags = INTEL_INT(pd->flags);                       // Powerup flags, see below...
3781         ps->energy = (fix)INTEL_INT((int)pd->energy);           // Amount of energy remaining.
3782         ps->shields = (fix)INTEL_INT((int)pd->shields);         // shields remaining (protection)
3783         ps->lives = pd->lives;                                  // Lives remaining, 0 = game over.
3784         ps->laser_level = pd->laser_level;                      // Current level of the laser.
3785         ps->primary_weapon_flags=pd->primary_weapon_flags;      // bit set indicates the player has this weapon.
3786         ps->secondary_weapon_flags=pd->secondary_weapon_flags;  // bit set indicates the player has this weapon.
3787         for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
3788                 ps->primary_ammo[i] = INTEL_SHORT(pd->primary_ammo[i]);
3789         for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
3790                 ps->secondary_ammo[i] = INTEL_SHORT(pd->secondary_ammo[i]);
3791         //memcpy( ps->primary_ammo, pd->primary_ammo, MAX_PRIMARY_WEAPONS*sizeof(short) );  // How much ammo of each type.
3792         //memcpy( ps->secondary_ammo, pd->secondary_ammo, MAX_SECONDARY_WEAPONS*sizeof(short) ); // How much ammo of each type.
3793         ps->last_score = INTEL_INT(pd->last_score);             // Score at beginning of current level.
3794         ps->score = INTEL_INT(pd->score);                       // Current score.
3795         ps->cloak_time = (fix)INTEL_INT((int)pd->cloak_time);   // Time cloaked
3796         ps->homing_object_dist = (fix)INTEL_INT((int)pd->homing_object_dist); // Distance of nearest homing object.
3797         ps->invulnerable_time = (fix)INTEL_INT((int)pd->invulnerable_time); // Time invulnerable
3798         ps->KillGoalCount=INTEL_SHORT(pd->KillGoalCount);
3799         ps->net_killed_total = INTEL_SHORT(pd->net_killed_total); // Number of times killed total
3800         ps->net_kills_total = INTEL_SHORT(pd->net_kills_total); // Number of net kills total
3801         ps->num_kills_level = INTEL_SHORT(pd->num_kills_level); // Number of kills this level
3802         ps->num_kills_total = INTEL_SHORT(pd->num_kills_total); // Number of kills total
3803         ps->num_robots_level = INTEL_SHORT(pd->num_robots_level); // Number of initial robots this level
3804         ps->num_robots_total = INTEL_SHORT(pd->num_robots_total); // Number of robots total
3805         ps->hostages_rescued_total = INTEL_SHORT(pd->hostages_rescued_total); // Total number of hostages rescued.
3806         ps->hostages_total = INTEL_SHORT(pd->hostages_total);   // Total number of hostages.
3807         ps->hostages_on_board=pd->hostages_on_board;            // Number of hostages on ship.
3808 }
3809
3810 void multi_send_drop_weapon (int objnum,int seed)
3811 {
3812         object *objp;
3813         int count=0;
3814         int ammo_count;
3815
3816         objp = &Objects[objnum];
3817
3818         ammo_count = objp->ctype.powerup_info.count;
3819
3820         if (objp->id == POW_OMEGA_WEAPON && ammo_count == F1_0)
3821                 ammo_count = F1_0 - 1; //make fit in short
3822
3823         Assert(ammo_count < F1_0); //make sure fits in short
3824
3825         multibuf[count++]=(char)MULTI_DROP_WEAPON;
3826         multibuf[count++]=(char)objp->id;
3827
3828         PUT_INTEL_SHORT(multibuf+count, Player_num); count += 2;
3829         PUT_INTEL_SHORT(multibuf+count, objnum); count += 2;
3830         PUT_INTEL_SHORT(multibuf+count, ammo_count); count += 2;
3831         PUT_INTEL_INT(multibuf+count, seed);
3832
3833         map_objnum_local_to_local(objnum);
3834
3835         if (Game_mode & GM_NETWORK)
3836                 PowerupsInMine[objp->id]++;
3837
3838         multi_send_data(multibuf, 12, 2);
3839 }
3840
3841 void multi_do_drop_weapon (char *buf)
3842 {
3843         int pnum,ammo,objnum,remote_objnum,seed;
3844         object *objp;
3845         int powerup_id;
3846
3847         powerup_id=(int)(buf[1]);
3848         pnum = GET_INTEL_SHORT(buf + 2);
3849         remote_objnum = GET_INTEL_SHORT(buf + 4);
3850         ammo = GET_INTEL_SHORT(buf + 6);
3851         seed = GET_INTEL_INT(buf + 8);
3852
3853         objp = &Objects[Players[pnum].objnum];
3854
3855         objnum = spit_powerup(objp, powerup_id, seed);
3856
3857         map_objnum_local_to_remote(objnum, remote_objnum, pnum);
3858
3859         if (objnum!=-1)
3860                 Objects[objnum].ctype.powerup_info.count = ammo;
3861
3862         if (Game_mode & GM_NETWORK)
3863                 PowerupsInMine[powerup_id]++;
3864
3865         mprintf ((0,"Dropped weapon %d!\n"));
3866
3867 }
3868
3869 void multi_send_guided_info (object *miss,char done)
3870 {
3871 #ifdef WORDS_BIGENDIAN
3872         shortpos sp;
3873 #endif
3874         int count=0;
3875
3876         mprintf ((0,"Sending guided info!\n"));
3877
3878         multibuf[count++]=(char)MULTI_GUIDED;
3879         multibuf[count++]=(char)Player_num;
3880         multibuf[count++]=done;
3881
3882 #ifndef WORDS_BIGENDIAN
3883         create_shortpos((shortpos *)(multibuf+count), miss,0);
3884         count+=sizeof(shortpos);
3885 #else
3886         create_shortpos(&sp, miss, 1);
3887         memcpy(&(multibuf[count]), (ubyte *)(sp.bytemat), 9);
3888         count += 9;
3889         memcpy(&(multibuf[count]), (ubyte *)&(sp.xo), 14);
3890         count += 14;
3891 #endif
3892
3893         multi_send_data(multibuf, count, 0);
3894 }
3895
3896 void multi_do_guided (char *buf)
3897 {
3898         char pnum=buf[1];
3899         int count=3;
3900         static int fun=200;
3901 #ifdef WORDS_BIGENDIAN
3902         shortpos sp;
3903 #endif
3904
3905         if (Guided_missile[(int)pnum]==NULL)
3906         {
3907                 if (++fun>=50)
3908                 {
3909                         mprintf ((0,"Guided missile for %s is NULL!\n",Players[(int)pnum].callsign));
3910                         fun=0;
3911                 }
3912                 return;
3913         }
3914         else if (++fun>=50)
3915         {
3916                 mprintf ((0,"Got guided info for %d (%s)\n",pnum,Players[(int)pnum].callsign));
3917                 fun=0;
3918         }
3919
3920         if (buf[2])
3921         {
3922                 release_guided_missile(pnum);
3923                 return;
3924         }
3925
3926
3927         if (OBJECT_NUMBER(Guided_missile[(int)pnum]) < 0 || OBJECT_NUMBER(Guided_missile[(int)pnum]) > Highest_object_index)
3928         {
3929                 Int3();  // Get Jason immediately!
3930                 return;
3931         }
3932
3933 #ifndef WORDS_BIGENDIAN
3934         extract_shortpos(Guided_missile[(int)pnum], (shortpos *)(buf+count),0);
3935 #else
3936         memcpy((ubyte *)(sp.bytemat), (ubyte *)(buf + count), 9);
3937         memcpy((ubyte *)&(sp.xo), (ubyte *)(buf + count + 9), 14);
3938         extract_shortpos(Guided_missile[(int)pnum], &sp, 1);
3939 #endif
3940
3941         count+=sizeof (shortpos);
3942
3943         update_object_seg(Guided_missile[(int)pnum]);
3944 }
3945
3946 void multi_send_stolen_items ()
3947 {
3948         int i,count=1;
3949         multibuf[0]=MULTI_STOLEN_ITEMS;
3950
3951         for (i=0;i<MAX_STOLEN_ITEMS;i++)
3952         {
3953                 multibuf[i+1]=Stolen_items[i];
3954                 mprintf ((0,"[%d]=%d ",i,Stolen_items[i]));
3955                 count++;      // So I like to break my stuff into smaller chunks, so what?
3956         }
3957         mprintf ((0,"\n"));
3958         multi_send_data(multibuf, count, 1);
3959 }
3960
3961 void multi_do_stolen_items (char *buf)
3962 {
3963         int i;
3964
3965         mprintf ((0,"Recieved a stolen item packet...\n"));
3966
3967         for (i=0;i<MAX_STOLEN_ITEMS;i++)
3968         {
3969                 Stolen_items[i]=buf[i+1];
3970                 mprintf ((0,"[%d]=%d ",i,Stolen_items[i]));
3971         }
3972         mprintf ((0,"\n"));
3973 }
3974
3975 extern void network_send_important_packet (char *,int);
3976
3977 void multi_send_wall_status (int wallnum,ubyte type,ubyte flags,ubyte state)
3978 {
3979         int count=0;
3980         multibuf[count]=MULTI_WALL_STATUS;        count++;
3981         PUT_INTEL_SHORT(multibuf+count, wallnum);   count+=2;
3982         multibuf[count]=type;                 count++;
3983         multibuf[count]=flags;                count++;
3984         multibuf[count]=state;                count++;
3985
3986 #if 0
3987         if (Game_mode & GM_NETWORK)
3988         {
3989                 network_send_important_packet (multibuf,count);
3990                 network_send_important_packet (multibuf,count);
3991         }
3992         else
3993 #endif
3994         {
3995                 multi_send_data(multibuf, count, 1); // twice, just to be sure
3996                 multi_send_data(multibuf, count, 1);
3997         }
3998 }
3999 void multi_send_wall_status_specific (int pnum,int wallnum,ubyte type,ubyte flags,ubyte state)
4000 {
4001         // Send wall states a specific rejoining player
4002
4003         int count=0;
4004
4005         Assert (Game_mode & GM_NETWORK);
4006         //Assert (pnum>-1 && pnum<N_players);
4007
4008         multibuf[count]=MULTI_WALL_STATUS;        count++;
4009         PUT_INTEL_SHORT(multibuf+count, wallnum);  count+=2;
4010         multibuf[count]=type;                 count++;
4011         multibuf[count]=flags;                count++;
4012         multibuf[count]=state;                count++;
4013
4014         network_send_naked_packet(multibuf, count,pnum); // twice, just to be sure
4015         network_send_naked_packet(multibuf, count,pnum);
4016 }
4017
4018 void multi_do_wall_status (char *buf)
4019 {
4020         short wallnum;
4021         ubyte flag,type,state;
4022
4023         wallnum = GET_INTEL_SHORT(buf + 1);
4024         type=buf[3];
4025         flag=buf[4];
4026         state=buf[5];
4027
4028         Assert (wallnum>=0);
4029         Walls[wallnum].type=type;
4030         Walls[wallnum].flags=flag;
4031         //Assert(state <= 4);
4032         Walls[wallnum].state=state;
4033
4034         if (Walls[wallnum].type==WALL_OPEN)
4035         {
4036                 digi_kill_sound_linked_to_segment(Walls[wallnum].segnum,Walls[wallnum].sidenum,SOUND_FORCEFIELD_HUM);
4037                 //digi_kill_sound_linked_to_segment(SEGMENT_NUMBER(csegp), cside, SOUND_FORCEFIELD_HUM);
4038         }
4039
4040
4041         //mprintf ((0,"Got a walls packet.\n"));
4042 }
4043
4044 void multi_send_jason_cheat (int num)
4045 {
4046         return;
4047 }
4048
4049 void multi_send_kill_goal_counts()
4050 {
4051         int i,count=1;
4052         multibuf[0]=MULTI_KILLGOALS;
4053
4054         for (i=0;i<MAX_PLAYERS;i++)
4055         {
4056                 *(char *)(multibuf+count)=(char)Players[i].KillGoalCount;
4057                 count++;
4058         }
4059
4060         mprintf ((0,"MULTI: Sending KillGoalCounts...\n"));
4061         multi_send_data(multibuf, count, 1);
4062 }
4063
4064 void multi_do_kill_goal_counts(char *buf)
4065 {
4066         int i,count=1;
4067
4068         for (i=0;i<MAX_PLAYERS;i++)
4069         {
4070                 Players[i].KillGoalCount=*(char *)(buf+count);
4071                 mprintf ((0,"KGC: %s has %d kills!\n",Players[i].callsign,Players[i].KillGoalCount));
4072                 count++;
4073         }
4074
4075 }
4076
4077 void multi_send_heartbeat ()
4078 {
4079         if (!Netgame.PlayTimeAllowed)
4080                 return;
4081
4082         multibuf[0]=MULTI_HEARTBEAT;
4083         PUT_INTEL_INT(multibuf+1, ThisLevelTime);
4084         multi_send_data(multibuf, 5, 0);
4085 }
4086
4087 void multi_do_heartbeat (char *buf)
4088 {
4089         fix num;
4090
4091         num = GET_INTEL_INT(buf + 1);
4092
4093         ThisLevelTime=num;
4094 }
4095
4096 void multi_check_for_killgoal_winner ()
4097 {
4098         int i,best=0,bestnum=0;
4099         object *objp;
4100
4101         if (Control_center_destroyed)
4102                 return;
4103
4104         for (i=0;i<N_players;i++)
4105         {
4106                 if (Players[i].KillGoalCount>best)
4107                 {
4108                         best=Players[i].KillGoalCount;
4109                         bestnum=i;
4110                 }
4111         }
4112
4113         if (bestnum==Player_num)
4114         {
4115                 HUD_init_message("You have the best score at %d kills!",best);
4116                 //Players[Player_num].shields=i2f(200);
4117         }
4118         else
4119
4120                 HUD_init_message ("%s has the best score with %d kills!",Players[bestnum].callsign,best);
4121
4122         HUD_init_message ("The control center has been destroyed!");
4123
4124         objp=obj_find_first_of_type (OBJ_CNTRLCEN);
4125         net_destroy_controlcen (objp);
4126 }
4127
4128 void multi_send_seismic (fix start,fix end)
4129 {
4130         int count=1;
4131
4132         multibuf[0]=MULTI_SEISMIC;
4133         PUT_INTEL_INT(multibuf+count, start); count+=(sizeof(fix));
4134         PUT_INTEL_INT(multibuf+count, end); count+=(sizeof(fix));
4135
4136         multi_send_data(multibuf, count, 1);
4137 }
4138
4139 extern fix Seismic_disturbance_start_time;
4140 extern fix Seismic_disturbance_end_time;
4141
4142 void multi_do_seismic (char *buf)
4143 {
4144         Seismic_disturbance_start_time = GET_INTEL_INT(buf + 1);
4145         Seismic_disturbance_end_time = GET_INTEL_INT(buf + 5);
4146         digi_play_sample (SOUND_SEISMIC_DISTURBANCE_START, F1_0);
4147 }
4148
4149 void multi_send_light (int segnum,ubyte val)
4150 {
4151         int count=1,i;
4152         multibuf[0]=MULTI_LIGHT;
4153         PUT_INTEL_INT(multibuf+count, segnum); count+=(sizeof(int));
4154         *(char *)(multibuf+count)=val; count++;
4155         for (i=0;i<6;i++)
4156         {
4157                 //mprintf ((0,"Sending %d!\n",Segments[segnum].sides[i].tmap_num2));
4158                 PUT_INTEL_SHORT(multibuf+count, Segments[segnum].sides[i].tmap_num2); count+=2;
4159         }
4160         multi_send_data(multibuf, count, 1);
4161 }
4162 void multi_send_light_specific (int pnum,int segnum,ubyte val)
4163 {
4164         int count=1,i;
4165
4166         Assert (Game_mode & GM_NETWORK);
4167         //  Assert (pnum>-1 && pnum<N_players);
4168
4169         multibuf[0]=MULTI_LIGHT;
4170         PUT_INTEL_INT(multibuf+count, segnum); count+=(sizeof(int));
4171         *(char *)(multibuf+count)=val; count++;
4172
4173         for (i=0;i<6;i++)
4174         {
4175                 //mprintf ((0,"Sending %d!\n",Segments[segnum].sides[i].tmap_num2));
4176                 PUT_INTEL_SHORT(multibuf+count, Segments[segnum].sides[i].tmap_num2); count+=2;
4177         }
4178         network_send_naked_packet(multibuf, count, pnum);
4179 }
4180
4181 void multi_do_light (char *buf)
4182 {
4183         int i, seg;
4184         ubyte sides=*(char *)(buf+5);
4185
4186         seg = GET_INTEL_INT(buf + 1);
4187         for (i=0;i<6;i++)
4188         {
4189                 if ((sides & (1<<i)))
4190                 {
4191                         subtract_light (seg,i);
4192                         Segments[seg].sides[i].tmap_num2 = GET_INTEL_SHORT(buf + (6 + (2 * i)));
4193                         //mprintf ((0,"Got %d!\n",Segments[seg].sides[i].tmap_num2));
4194                 }
4195         }
4196 }
4197
4198 //@@void multi_send_start_trigger(int triggernum)
4199 //@@{
4200 //@@    // Send an even to trigger something in the mine
4201 //@@
4202 //@@    int count = 0;
4203 //@@
4204 //@@    multibuf[count] = MULTI_START_TRIGGER;          count += 1;
4205 //@@    multibuf[count] = Player_num;                   count += 1;
4206 //@@    multibuf[count] = (ubyte)triggernum;            count += 1;
4207 //@@
4208 //@@    //mprintf ((0,"Sending start trigger %d\n",triggernum));
4209 //@@    multi_send_data(multibuf, count, 2);
4210 //@@}
4211 //@@void multi_do_start_trigger(char *buf)
4212 //@@{
4213 //@@    int pnum = buf[1];
4214 //@@    int trigger = buf[2];
4215 //@@
4216 //@@    //mprintf ((0,"MULTI doing start trigger!\n"));
4217 //@@
4218 //@@    if ((pnum < 0) || (pnum >= N_players) || (pnum == Player_num))
4219 //@@    {
4220 //@@            Int3(); // Got trigger from illegal playernum
4221 //@@            return;
4222 //@@    }
4223 //@@    if ((trigger < 0) || (trigger >= Num_triggers))
4224 //@@    {
4225 //@@            Int3(); // Illegal trigger number in multiplayer
4226 //@@            return;
4227 //@@    }
4228 //@@
4229 //@@    if (!(Triggers[trigger].flags & TF_SPRUNG))
4230 //@@            check_trigger_sub(trigger, pnum,0);
4231 //@@}
4232
4233
4234 void multi_do_flags (char *buf)
4235 {
4236         char pnum=buf[1];
4237         uint flags;
4238
4239         flags = GET_INTEL_INT(buf + 2);
4240         if (pnum!=Player_num)
4241                 Players[(int)pnum].flags=flags;
4242 }
4243
4244 void multi_send_flags (char pnum)
4245 {
4246         multibuf[0]=MULTI_FLAGS;
4247         multibuf[1]=pnum;
4248         PUT_INTEL_INT(multibuf+2, Players[(int)pnum].flags);
4249  
4250         multi_send_data(multibuf, 6, 1);
4251 }
4252
4253 void multi_send_drop_blobs (char pnum)
4254 {
4255         multibuf[0]=MULTI_DROP_BLOB;
4256         multibuf[1]=pnum;
4257
4258         multi_send_data(multibuf, 2, 0);
4259 }
4260
4261 void multi_do_drop_blob (char *buf)
4262 {
4263         char pnum=buf[1];
4264         drop_afterburner_blobs (&Objects[Players[(int)pnum].objnum], 2, i2f(5)/2, -1);
4265 }
4266
4267 void multi_send_powerup_update ()
4268 {
4269         int i;
4270
4271
4272         multibuf[0]=MULTI_POWERUP_UPDATE;
4273         for (i=0;i<MAX_POWERUP_TYPES;i++)
4274                 multibuf[i+1]=MaxPowerupsAllowed[i];
4275
4276         multi_send_data(multibuf, MAX_POWERUP_TYPES+1, 1);
4277 }
4278 void multi_do_powerup_update (char *buf)
4279 {
4280         int i;
4281
4282         for (i=0;i<MAX_POWERUP_TYPES;i++)
4283                 if (buf[i+1]>MaxPowerupsAllowed[i])
4284                         MaxPowerupsAllowed[i]=buf[i+1];
4285 }
4286
4287 extern active_door ActiveDoors[];
4288 extern int Num_open_doors;          // Number of open doors
4289
4290
4291 #if 0 // never used...
4292 void multi_send_active_door (int i)
4293 {
4294         int count;
4295
4296         multibuf[0]=MULTI_ACTIVE_DOOR;
4297         multibuf[1]=i;
4298         multibuf[2]=Num_open_doors;
4299         count = 3;
4300 #ifndef WORDS_BIGENDIAN
4301         memcpy ((char *)(&multibuf[3]),&ActiveDoors[(int)i],sizeof(struct active_door));
4302         count += sizeof(active_door);
4303 #else
4304         *(int *)(multibuf + count) = INTEL_INT(ActiveDoors[i].n_parts);                 count += 4;
4305         *(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].front_wallnum[0]);    count += 2;
4306         *(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].front_wallnum[1]);    count += 2;
4307         *(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].back_wallnum[0]);     count += 2;
4308         *(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].back_wallnum[1]);     count += 2;
4309         *(int *)(multibuf + count) = INTEL_INT(ActiveDoors[i].time);                    count += 4;
4310 #endif
4311         //multi_send_data (multibuf,sizeof(struct active_door)+3,1);
4312         multi_send_data (multibuf,count,1);
4313 }
4314 #endif // 0 (never used)
4315
4316 void multi_do_active_door (char *buf)
4317 {
4318         char i = multibuf[1];
4319         Num_open_doors = buf[2];
4320
4321         memcpy(&ActiveDoors[(int)i], buf+3, sizeof(struct active_door));
4322 #ifdef WORDS_BIGENDIAN
4323         {
4324                 active_door *ad = &ActiveDoors[(int)i];
4325                 ad->n_parts = INTEL_INT(ad->n_parts);
4326                 ad->front_wallnum[0] = INTEL_SHORT(ad->front_wallnum[0]);
4327                 ad->front_wallnum[1] = INTEL_SHORT(ad->front_wallnum[1]);
4328                 ad->back_wallnum[0] = INTEL_SHORT(ad->back_wallnum[0]);
4329                 ad->back_wallnum[1] = INTEL_SHORT(ad->back_wallnum[1]);
4330                 ad->time = INTEL_INT(ad->time);
4331         }
4332 #endif //WORDS_BIGENDIAN
4333 }
4334
4335 void multi_send_sound_function (char whichfunc, char sound)
4336 {
4337         int count=0;
4338
4339         multibuf[0]=MULTI_SOUND_FUNCTION;   count++;
4340         multibuf[1]=Player_num;             count++;
4341         multibuf[2]=whichfunc;              count++;
4342 #ifndef WORDS_BIGENDIAN
4343         *(uint *)(multibuf+count)=sound;    count++;
4344 #else
4345         multibuf[3] = sound; count++;       // this would probably work on the PC as well.  Jason?
4346 #endif
4347         multi_send_data (multibuf,4,0);
4348 }
4349
4350 #define AFTERBURNER_LOOP_START  20098
4351 #define AFTERBURNER_LOOP_END    25776
4352
4353 void multi_do_sound_function (char *buf)
4354 {
4355         // for afterburner
4356
4357         char pnum,whichfunc;
4358         int sound;
4359
4360         if (Players[Player_num].connected!=1)
4361                 return;
4362
4363         pnum=buf[1];
4364         whichfunc=buf[2];
4365         sound=buf[3];
4366
4367         if (whichfunc==0)
4368                 digi_kill_sound_linked_to_object (Players[(int)pnum].objnum);
4369         else if (whichfunc==3)
4370                 digi_link_sound_to_object3( sound, Players[(int)pnum].objnum, 1,F1_0, i2f(256), AFTERBURNER_LOOP_START, AFTERBURNER_LOOP_END);
4371 }
4372
4373 void multi_send_capture_bonus (char pnum)
4374 {
4375         Assert (Game_mode & GM_CAPTURE);
4376
4377         multibuf[0]=MULTI_CAPTURE_BONUS;
4378         multibuf[1]=pnum;
4379
4380         multi_send_data (multibuf,2,1);
4381         multi_do_capture_bonus (multibuf);
4382 }
4383 void multi_send_orb_bonus (char pnum)
4384 {
4385         Assert (Game_mode & GM_HOARD);
4386
4387         multibuf[0]=MULTI_ORB_BONUS;
4388         multibuf[1]=pnum;
4389         multibuf[2]=Players[Player_num].secondary_ammo[PROXIMITY_INDEX];
4390
4391         multi_send_data (multibuf,3,1);
4392         multi_do_orb_bonus (multibuf);
4393 }
4394 void multi_do_capture_bonus(char *buf)
4395 {
4396         // Figure out the results of a network kills and add it to the
4397         // appropriate player's tally.
4398
4399         char pnum=buf[1];
4400         int TheGoal;
4401
4402         kmatrix_kills_changed = 1;
4403
4404         if (pnum==Player_num)
4405                 HUD_init_message("You have Scored!");
4406         else
4407                 HUD_init_message("%s has Scored!",Players[(int)pnum].callsign);
4408
4409         if (pnum==Player_num)
4410                 digi_play_sample (SOUND_HUD_YOU_GOT_GOAL,F1_0*2);
4411         else if (get_team(pnum)==TEAM_RED)
4412                 digi_play_sample (SOUND_HUD_RED_GOT_GOAL,F1_0*2);
4413         else
4414                 digi_play_sample (SOUND_HUD_BLUE_GOT_GOAL,F1_0*2);
4415
4416         Players[(int)pnum].flags &= ~(PLAYER_FLAGS_FLAG);  // Clear capture flag
4417
4418         team_kills[get_team(pnum)] += 5;
4419         Players[(int)pnum].net_kills_total += 5;
4420         Players[(int)pnum].KillGoalCount+=5;
4421
4422         if (Netgame.KillGoal>0)
4423         {
4424                 TheGoal=Netgame.KillGoal*5;
4425
4426                 if (Players[(int)pnum].KillGoalCount>=TheGoal)
4427                 {
4428                         if (pnum==Player_num)
4429                         {
4430                                 HUD_init_message("You reached the kill goal!");
4431                                 Players[Player_num].shields=i2f(200);
4432                         }
4433                         else
4434                                 HUD_init_message ("%s has reached the kill goal!",Players[(int)pnum].callsign);
4435
4436                         HUD_init_message ("The control center has been destroyed!");
4437                         net_destroy_controlcen (obj_find_first_of_type (OBJ_CNTRLCEN));
4438                 }
4439         }
4440
4441         multi_sort_kill_list();
4442         multi_show_player_list();
4443 }
4444
4445 int GetOrbBonus (char num)
4446 {
4447         int bonus;
4448
4449         bonus=num*(num+1)/2;
4450         return (bonus);
4451 }
4452
4453 void multi_do_orb_bonus(char *buf)
4454 {
4455         // Figure out the results of a network kills and add it to the
4456         // appropriate player's tally.
4457
4458         char pnum=buf[1];
4459         int TheGoal;
4460         int bonus=GetOrbBonus (buf[2]);
4461
4462         kmatrix_kills_changed = 1;
4463
4464         if (pnum==Player_num)
4465                 HUD_init_message("You have scored %d points!",bonus);
4466         else
4467                 HUD_init_message("%s has scored with %d orbs!",Players[(int)pnum].callsign,buf[2]);
4468
4469         if (pnum==Player_num)
4470                 digi_start_sound_queued (SOUND_HUD_YOU_GOT_GOAL,F1_0*2);
4471         else if (Game_mode & GM_TEAM)
4472         {
4473                 if (get_team(pnum)==TEAM_RED)
4474                         digi_play_sample (SOUND_HUD_RED_GOT_GOAL,F1_0*2);
4475                 else
4476                         digi_play_sample (SOUND_HUD_BLUE_GOT_GOAL,F1_0*2);
4477         }
4478         else
4479                 digi_play_sample (SOUND_OPPONENT_HAS_SCORED,F1_0*2);
4480
4481         if (bonus>PhallicLimit)
4482         {
4483                 if (pnum==Player_num)
4484                         HUD_init_message ("You have the record with %d points!",bonus);
4485                 else
4486                         HUD_init_message ("%s has the record with %d points!",Players[(int)pnum].callsign,bonus);
4487                 digi_play_sample (SOUND_BUDDY_MET_GOAL,F1_0*2);
4488                 PhallicMan=pnum;
4489                 PhallicLimit=bonus;
4490         }
4491
4492         Players[(int)pnum].flags &= ~(PLAYER_FLAGS_FLAG);  // Clear orb flag
4493
4494         team_kills[get_team(pnum)] += bonus;
4495         Players[(int)pnum].net_kills_total += bonus;
4496         Players[(int)pnum].KillGoalCount+=bonus;
4497
4498         team_kills[get_team(pnum)]%=1000;
4499         Players[(int)pnum].net_kills_total%=1000;
4500         Players[(int)pnum].KillGoalCount%=1000;
4501
4502         if (Netgame.KillGoal>0)
4503         {
4504                 TheGoal=Netgame.KillGoal*5;
4505
4506                 if (Players[(int)pnum].KillGoalCount>=TheGoal)
4507                 {
4508                         if (pnum==Player_num)
4509                         {
4510                                 HUD_init_message("You reached the kill goal!");
4511                                 Players[Player_num].shields=i2f(200);
4512                         }
4513                         else
4514                                 HUD_init_message ("%s has reached the kill goal!",Players[(int)pnum].callsign);
4515
4516                         HUD_init_message ("The control center has been destroyed!");
4517                         net_destroy_controlcen (obj_find_first_of_type (OBJ_CNTRLCEN));
4518                 }
4519         }
4520         multi_sort_kill_list();
4521         multi_show_player_list();
4522 }
4523
4524 void multi_send_got_flag (char pnum)
4525 {
4526         multibuf[0]=MULTI_GOT_FLAG;
4527         multibuf[1]=pnum;
4528
4529         digi_start_sound_queued (SOUND_HUD_YOU_GOT_FLAG,F1_0*2);
4530
4531         multi_send_data (multibuf,2,1);
4532         multi_send_flags (Player_num);
4533 }
4534
4535 int SoundHacked=0;
4536 digi_sound ReversedSound;
4537
4538 void multi_send_got_orb (char pnum)
4539 {
4540         multibuf[0]=MULTI_GOT_ORB;
4541         multibuf[1]=pnum;
4542
4543         digi_play_sample (SOUND_YOU_GOT_ORB,F1_0*2);
4544
4545         multi_send_data (multibuf,2,1);
4546         multi_send_flags (Player_num);
4547 }
4548
4549 void multi_do_got_flag (char *buf)
4550 {
4551         char pnum=buf[1];
4552
4553         if (pnum==Player_num)
4554                 digi_start_sound_queued (SOUND_HUD_YOU_GOT_FLAG,F1_0*2);
4555         else if (get_team(pnum)==TEAM_RED)
4556                 digi_start_sound_queued (SOUND_HUD_RED_GOT_FLAG,F1_0*2);
4557         else
4558                 digi_start_sound_queued (SOUND_HUD_BLUE_GOT_FLAG,F1_0*2);
4559         Players[(int)pnum].flags|=PLAYER_FLAGS_FLAG;
4560         HUD_init_message ("%s picked up a flag!",Players[(int)pnum].callsign);
4561 }
4562 void multi_do_got_orb (char *buf)
4563 {
4564         char pnum=buf[1];
4565
4566         Assert (Game_mode & GM_HOARD);
4567
4568         if (Game_mode & GM_TEAM)
4569         {
4570                 if (get_team(pnum)==get_team(Player_num))
4571                         digi_play_sample (SOUND_FRIEND_GOT_ORB,F1_0*2);
4572                 else
4573                         digi_play_sample (SOUND_OPPONENT_GOT_ORB,F1_0*2);
4574     }
4575         else
4576                 digi_play_sample (SOUND_OPPONENT_GOT_ORB,F1_0*2);
4577
4578         Players[(int)pnum].flags|=PLAYER_FLAGS_FLAG;
4579         HUD_init_message ("%s picked up an orb!",Players[(int)pnum].callsign);
4580 }
4581
4582
4583 void DropOrb ()
4584 {
4585         int objnum,seed;
4586
4587         if (!(Game_mode & GM_HOARD))
4588                 Int3(); // How did we get here? Get Leighton!
4589
4590         if (!Players[Player_num].secondary_ammo[PROXIMITY_INDEX])
4591         {
4592                 HUD_init_message("No orbs to drop!");
4593                 return;
4594         }
4595
4596         seed = d_rand();
4597
4598         objnum = spit_powerup(ConsoleObject,POW_HOARD_ORB,seed);
4599
4600         if (objnum<0)
4601                 return;
4602
4603         HUD_init_message("Orb dropped!");
4604         digi_play_sample (SOUND_DROP_WEAPON,F1_0);
4605
4606         if ((Game_mode & GM_HOARD) && objnum>-1)
4607                 multi_send_drop_flag(objnum,seed);
4608
4609         Players[Player_num].secondary_ammo[PROXIMITY_INDEX]--;
4610
4611         // If empty, tell everyone to stop drawing the box around me
4612         if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]==0)
4613                 multi_send_flags (Player_num);
4614 }
4615
4616 void DropFlag ()
4617 {
4618         int objnum,seed;
4619
4620         if (!(Game_mode & GM_CAPTURE) && !(Game_mode & GM_HOARD))
4621                 return;
4622         if (Game_mode & GM_HOARD)
4623         {
4624                 DropOrb();
4625                 return;
4626         }
4627
4628         if (!(Players[Player_num].flags & PLAYER_FLAGS_FLAG))
4629         {
4630                 HUD_init_message("No flag to drop!");
4631                 return;
4632         }
4633
4634
4635         HUD_init_message("Flag dropped!");
4636         digi_play_sample (SOUND_DROP_WEAPON,F1_0);
4637
4638         seed = d_rand();
4639
4640         if (get_team (Player_num)==TEAM_RED)
4641                 objnum = spit_powerup(ConsoleObject,POW_FLAG_BLUE,seed);
4642         else
4643                 objnum = spit_powerup(ConsoleObject,POW_FLAG_RED,seed);
4644
4645         if (objnum<0)
4646                 return;
4647
4648         if ((Game_mode & GM_CAPTURE) && objnum>-1)
4649                 multi_send_drop_flag(objnum,seed);
4650
4651         Players[Player_num].flags &=~(PLAYER_FLAGS_FLAG);
4652 }
4653
4654
4655 void multi_send_drop_flag (int objnum,int seed)
4656 {
4657         object *objp;
4658         int count=0;
4659
4660         objp = &Objects[objnum];
4661
4662         multibuf[count++]=(char)MULTI_DROP_FLAG;
4663         multibuf[count++]=(char)objp->id;
4664
4665         PUT_INTEL_SHORT(multibuf+count, Player_num); count += 2;
4666         PUT_INTEL_SHORT(multibuf+count, objnum); count += 2;
4667         PUT_INTEL_SHORT(multibuf+count, objp->ctype.powerup_info.count); count += 2;
4668         PUT_INTEL_INT(multibuf+count, seed);
4669
4670         map_objnum_local_to_local(objnum);
4671
4672         if (!(Game_mode & GM_HOARD))
4673                 if (Game_mode & GM_NETWORK)
4674                         PowerupsInMine[objp->id]++;
4675
4676         multi_send_data(multibuf, 12, 2);
4677 }
4678
4679 void multi_do_drop_flag (char *buf)
4680 {
4681         int pnum,ammo,objnum,remote_objnum,seed;
4682         object *objp;
4683         int powerup_id;
4684
4685         powerup_id=buf[1];
4686         pnum = GET_INTEL_SHORT(buf + 2);
4687         remote_objnum = GET_INTEL_SHORT(buf + 4);
4688         ammo = GET_INTEL_SHORT(buf + 6);
4689         seed = GET_INTEL_INT(buf + 8);
4690
4691         objp = &Objects[Players[pnum].objnum];
4692
4693         objnum = spit_powerup(objp, powerup_id, seed);
4694
4695         map_objnum_local_to_remote(objnum, remote_objnum, pnum);
4696
4697         if (objnum!=-1)
4698                 Objects[objnum].ctype.powerup_info.count = ammo;
4699
4700         if (!(Game_mode & GM_HOARD))
4701         {
4702                 if (Game_mode & GM_NETWORK)
4703                         PowerupsInMine[powerup_id]++;
4704                 Players[pnum].flags &= ~(PLAYER_FLAGS_FLAG);
4705         }
4706         mprintf ((0,"Dropped flag %d!\n"));
4707
4708 }
4709
4710 void multi_bad_restore ()
4711 {
4712         Function_mode = FMODE_MENU;
4713         nm_messagebox(NULL, 1, TXT_OK,
4714                       "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.");
4715         Function_mode = FMODE_GAME;
4716         multi_quit_game = 1;
4717         multi_leave_menu = 1;
4718         multi_reset_stuff();
4719 }
4720
4721 extern int robot_controlled[MAX_ROBOTS_CONTROLLED];
4722 extern int robot_agitation[MAX_ROBOTS_CONTROLLED];
4723 extern fix robot_controlled_time[MAX_ROBOTS_CONTROLLED];
4724 extern fix robot_last_send_time[MAX_ROBOTS_CONTROLLED];
4725 extern fix robot_last_message_time[MAX_ROBOTS_CONTROLLED];
4726 extern int robot_send_pending[MAX_ROBOTS_CONTROLLED];
4727 extern int robot_fired[MAX_ROBOTS_CONTROLLED];
4728 extern sbyte robot_fire_buf[MAX_ROBOTS_CONTROLLED][18+3];
4729
4730
4731 void multi_send_robot_controls (char pnum)
4732 {
4733         int count=2;
4734
4735         mprintf ((0,"Sending ROBOT_CONTROLS!!!\n"));
4736
4737         multibuf[0]=MULTI_ROBOT_CONTROLS;
4738         multibuf[1]=pnum;
4739         memcpy (&(multibuf[count]),&robot_controlled,MAX_ROBOTS_CONTROLLED*4);
4740         count+=(MAX_ROBOTS_CONTROLLED*4);
4741         memcpy (&(multibuf[count]),&robot_agitation,MAX_ROBOTS_CONTROLLED*4);
4742         count+=(MAX_ROBOTS_CONTROLLED*4);
4743         memcpy (&(multibuf[count]),&robot_controlled_time,MAX_ROBOTS_CONTROLLED*4);
4744         count+=(MAX_ROBOTS_CONTROLLED*4);
4745         memcpy (&(multibuf[count]),&robot_last_send_time,MAX_ROBOTS_CONTROLLED*4);
4746         count+=(MAX_ROBOTS_CONTROLLED*4);
4747         memcpy (&(multibuf[count]),&robot_last_message_time,MAX_ROBOTS_CONTROLLED*4);
4748         count+=(MAX_ROBOTS_CONTROLLED*4);
4749         memcpy (&(multibuf[count]),&robot_send_pending,MAX_ROBOTS_CONTROLLED*4);
4750         count+=(MAX_ROBOTS_CONTROLLED*4);
4751         memcpy (&(multibuf[count]),&robot_fired,MAX_ROBOTS_CONTROLLED*4);
4752         count+=(MAX_ROBOTS_CONTROLLED*4);
4753
4754         network_send_naked_packet (multibuf,142,pnum);
4755 }
4756 void multi_do_robot_controls(char *buf)
4757 {
4758         int count=2;
4759
4760         mprintf ((0,"Recieved ROBOT_CONTROLS!!!\n"));
4761
4762         if (buf[1]!=Player_num)
4763         {
4764                 Int3(); // Get Jason!  Recieved a coop_sync that wasn't ours!
4765                 return;
4766         }
4767
4768         memcpy (&robot_controlled,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4769         count+=(MAX_ROBOTS_CONTROLLED*4);
4770         memcpy (&robot_agitation,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4771         count+=(MAX_ROBOTS_CONTROLLED*4);
4772         memcpy (&robot_controlled_time,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4773         count+=(MAX_ROBOTS_CONTROLLED*4);
4774         memcpy (&robot_last_send_time,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4775         count+=(MAX_ROBOTS_CONTROLLED*4);
4776         memcpy (&robot_last_message_time,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4777         count+=(MAX_ROBOTS_CONTROLLED*4);
4778         memcpy (&robot_send_pending,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4779         count+=(MAX_ROBOTS_CONTROLLED*4);
4780         memcpy (&robot_fired,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4781         count+=(MAX_ROBOTS_CONTROLLED*4);
4782 }
4783
4784 #define POWERUPADJUSTS 5
4785 int PowerupAdjustMapping[]={11,19,39,41,44};
4786
4787 int multi_powerup_is_4pack (int id)
4788 {
4789         int i;
4790
4791         for (i=0;i<POWERUPADJUSTS;i++)
4792                 if (id==PowerupAdjustMapping[i])
4793                         return (1);
4794         return (0);
4795 }
4796
4797 int multi_powerup_is_allowed(int id)
4798 {
4799         if (id == POW_INVULNERABILITY && !Netgame.DoInvulnerability)
4800                 return (0);
4801         if (id == POW_CLOAK && !Netgame.DoCloak)
4802                 return (0);
4803         if (id == POW_AFTERBURNER && !Netgame.DoAfterburner)
4804                 return (0);
4805         if (id == POW_FUSION_WEAPON &&  !Netgame.DoFusions)
4806                 return (0);
4807         if (id == POW_PHOENIX_WEAPON && !Netgame.DoPhoenix)
4808                 return (0);
4809         if (id == POW_HELIX_WEAPON && !Netgame.DoHelix)
4810                 return (0);
4811         if (id == POW_MEGA_WEAPON && !Netgame.DoMegas)
4812                 return (0);
4813         if (id == POW_SMARTBOMB_WEAPON && !Netgame.DoSmarts)
4814                 return (0);
4815         if (id == POW_GAUSS_WEAPON && !Netgame.DoGauss)
4816                 return (0);
4817         if (id == POW_VULCAN_WEAPON && !Netgame.DoVulcan)
4818                 return (0);
4819         if (id == POW_PLASMA_WEAPON && !Netgame.DoPlasma)
4820                 return (0);
4821         if (id == POW_OMEGA_WEAPON && !Netgame.DoOmega)
4822                 return (0);
4823         if (id == POW_SUPER_LASER && !Netgame.DoSuperLaser)
4824                 return (0);
4825         if (id == POW_PROXIMITY_WEAPON && !Netgame.DoProximity)
4826                 return (0);
4827         if (id==POW_VULCAN_AMMO && (!Netgame.DoVulcan && !Netgame.DoGauss))
4828                 return (0);
4829         if (id == POW_SPREADFIRE_WEAPON && !Netgame.DoSpread)
4830                 return (0);
4831         if (id == POW_SMART_MINE && !Netgame.DoSmartMine)
4832                 return (0);
4833         if (id == POW_SMISSILE1_1 &&  !Netgame.DoFlash)
4834                 return (0);
4835         if (id == POW_SMISSILE1_4 &&  !Netgame.DoFlash)
4836                 return (0);
4837         if (id == POW_GUIDED_MISSILE_1 &&  !Netgame.DoGuided)
4838                 return (0);
4839         if (id == POW_GUIDED_MISSILE_4 &&  !Netgame.DoGuided)
4840                 return (0);
4841         if (id == POW_EARTHSHAKER_MISSILE &&  !Netgame.DoEarthShaker)
4842                 return (0);
4843         if (id == POW_MERCURY_MISSILE_1 &&  !Netgame.DoMercury)
4844                 return (0);
4845         if (id == POW_MERCURY_MISSILE_4 &&  !Netgame.DoMercury)
4846                 return (0);
4847         if (id == POW_CONVERTER &&  !Netgame.DoConverter)
4848                 return (0);
4849         if (id == POW_AMMO_RACK &&  !Netgame.DoAmmoRack)
4850                 return (0);
4851         if (id == POW_HEADLIGHT &&  !Netgame.DoHeadlight)
4852                 return (0);
4853         if (id == POW_LASER &&  !Netgame.DoLaserUpgrade)
4854                 return (0);
4855         if (id == POW_HOMING_AMMO_1 &&  !Netgame.DoHoming)
4856                 return (0);
4857         if (id == POW_HOMING_AMMO_4 &&  !Netgame.DoHoming)
4858                 return (0);
4859         if (id == POW_QUAD_FIRE &&  !Netgame.DoQuadLasers)
4860                 return (0);
4861         if (id == POW_FLAG_BLUE && !(Game_mode & GM_CAPTURE))
4862                 return (0);
4863         if (id == POW_FLAG_RED && !(Game_mode & GM_CAPTURE))
4864                 return (0);
4865
4866         return (1);
4867 }
4868
4869 void multi_send_finish_game ()
4870 {
4871         multibuf[0]=MULTI_FINISH_GAME;
4872         multibuf[1]=Player_num;
4873
4874         multi_send_data (multibuf,2,1);
4875 }
4876
4877
4878 extern void do_final_boss_hacks();
4879 void multi_do_finish_game (char *buf)
4880 {
4881         if (buf[0]!=MULTI_FINISH_GAME)
4882                 return;
4883
4884         if (Current_level_num!=Last_level)
4885                 return;
4886
4887         do_final_boss_hacks();
4888 }
4889
4890 void multi_send_trigger_specific (char pnum,char trig)
4891 {
4892         multibuf[0] = MULTI_START_TRIGGER;
4893         multibuf[1] = trig;
4894
4895         network_send_naked_packet(multibuf, 2, pnum);
4896 }
4897 void multi_do_start_trigger (char *buf)
4898 {
4899         Triggers[(int)buf[1]].flags |=TF_DISABLED;
4900 }
4901
4902 extern char *RankStrings[];
4903
4904 void multi_add_lifetime_kills ()
4905 {
4906         // This function adds a kill to lifetime stats of this player, and possibly
4907         // gives a promotion.  If so, it will tell everyone else
4908
4909         int oldrank;
4910
4911         if (!Game_mode & GM_NETWORK)
4912                 return;
4913
4914         oldrank=GetMyNetRanking();
4915
4916         Netlife_kills++;
4917
4918         if (oldrank!=GetMyNetRanking())
4919         {
4920                 multi_send_ranking();
4921                 if (!FindArg("-norankings"))
4922                 {
4923                         HUD_init_message ("You have been promoted to %s!",RankStrings[GetMyNetRanking()]);
4924                         digi_play_sample (SOUND_BUDDY_MET_GOAL,F1_0*2);
4925                         NetPlayers.players[Player_num].rank=GetMyNetRanking();
4926                 }
4927         }
4928         write_player_file();
4929 }
4930
4931 void multi_add_lifetime_killed ()
4932 {
4933         // This function adds a "killed" to lifetime stats of this player, and possibly
4934         // gives a demotion.  If so, it will tell everyone else
4935
4936         int oldrank;
4937
4938         if (!Game_mode & GM_NETWORK)
4939                 return;
4940
4941         oldrank=GetMyNetRanking();
4942
4943         Netlife_killed++;
4944
4945         if (oldrank!=GetMyNetRanking())
4946         {
4947                 multi_send_ranking();
4948                 NetPlayers.players[Player_num].rank=GetMyNetRanking();
4949
4950                 if (!FindArg("-norankings"))
4951                         HUD_init_message ("You have been demoted to %s!",RankStrings[GetMyNetRanking()]);
4952
4953         }
4954         write_player_file();
4955
4956 }
4957
4958 void multi_send_ranking ()
4959 {
4960         multibuf[0]=(char)MULTI_RANK;
4961         multibuf[1]=(char)Player_num;
4962         multibuf[2]=(char)GetMyNetRanking();
4963
4964         multi_send_data (multibuf,3,1);
4965 }
4966
4967 void multi_do_ranking (char *buf)
4968 {
4969         char rankstr[20];
4970         char pnum=buf[1];
4971         char rank=buf[2];
4972
4973         if (NetPlayers.players[(int)pnum].rank<rank)
4974                 strcpy (rankstr,"promoted");
4975         else if (NetPlayers.players[(int)pnum].rank>rank)
4976                 strcpy (rankstr,"demoted");
4977         else
4978                 return;
4979
4980         NetPlayers.players[(int)pnum].rank=rank;
4981
4982         if (!FindArg("-norankings"))
4983                 HUD_init_message ("%s has been %s to %s!",Players[(int)pnum].callsign,rankstr,RankStrings[(int)rank]);
4984 }
4985 void multi_send_modem_ping ()
4986 {
4987         multibuf[0]=MULTI_MODEM_PING;
4988         multi_send_data (multibuf,1,1);
4989 }
4990 void multi_send_modem_ping_return ()
4991 {
4992         multibuf[0]=MULTI_MODEM_PING_RETURN;
4993         multi_send_data (multibuf,1,1);
4994 }
4995
4996 void  multi_do_modem_ping_return ()
4997 {
4998         if (PingLaunchTime==0)
4999         {
5000                 mprintf ((0,"Got invalid PING RETURN from opponent!\n"));
5001                 return;
5002         }
5003
5004         PingReturnTime=timer_get_fixed_seconds();
5005
5006         HUD_init_message ("Ping time for opponent is %d ms!",f2i(fixmul(PingReturnTime-PingLaunchTime,i2f(1000))));
5007         PingLaunchTime=0;
5008 }
5009
5010
5011 void multi_quick_sound_hack (int num)
5012 {
5013         int length,i;
5014         num = digi_xlat_sound(num);
5015         length=GameSounds[num].length;
5016         ReversedSound.data=(ubyte *)d_malloc (length);
5017         ReversedSound.length=length;
5018
5019         for (i=0;i<length;i++)
5020                 ReversedSound.data[i]=GameSounds[num].data[length-i-1];
5021
5022         SoundHacked=1;
5023 }
5024
5025 void multi_send_play_by_play (int num,int spnum,int dpnum)
5026 {
5027         if (!(Game_mode & GM_HOARD))
5028                 return;
5029
5030         return;
5031         multibuf[0]=MULTI_PLAY_BY_PLAY;
5032         multibuf[1]=(char)num;
5033         multibuf[2]=(char)spnum;
5034         multibuf[3]=(char)dpnum;
5035         multi_send_data (multibuf,4,1);
5036         multi_do_play_by_play (multibuf);
5037 }
5038
5039 void multi_do_play_by_play (char *buf)
5040 {
5041         int whichplay=buf[1];
5042         int spnum=buf[2];
5043         int dpnum=buf[3];
5044
5045         if (!(Game_mode & GM_HOARD))
5046         {
5047                 Int3(); // Get Leighton, something bad has happened.
5048                 return;
5049         }
5050
5051         switch (whichplay)
5052         {
5053         case 0: // Smacked!
5054                 HUD_init_message ("Ouch! %s has been smacked by %s!",Players[dpnum].callsign,Players[spnum].callsign);
5055                 break;
5056         case 1: // Spanked!
5057                 HUD_init_message ("Haha! %s has been spanked by %s!",Players[dpnum].callsign,Players[spnum].callsign);
5058                 break;
5059         default:
5060                 Int3();
5061         }
5062 }
5063
5064 ///
5065 /// CODE TO LOAD HOARD DATA
5066 ///
5067
5068
5069 void init_bitmap(grs_bitmap *bm,int w,int h,int flags,ubyte *data)
5070 {
5071         bm->bm_x = bm->bm_y = 0;
5072         bm->bm_w = bm->bm_rowsize = w;
5073         bm->bm_h = h;
5074         bm->bm_type = BM_LINEAR;
5075         bm->bm_flags = flags;
5076         bm->bm_data = data;
5077         bm->bm_handle = 0;
5078         bm->avg_color = 0;
5079 }
5080
5081 grs_bitmap Orb_icons[2];
5082
5083 int Hoard_goal_eclip;
5084
5085 void init_hoard_data()
5086 {
5087         static int first_time=1;
5088         static int orb_vclip;
5089         int n_orb_frames,n_goal_frames;
5090         int orb_w,orb_h;
5091         int icon_w,icon_h;
5092         ubyte palette[256*3];
5093         CFILE *ifile;
5094         int i,save_pos;
5095         extern int Num_bitmap_files,Num_effects,Num_sound_files;
5096
5097         ifile = cfopen("hoard.ham","rb");
5098         if (ifile == NULL)
5099                 Error("can't open <hoard.ham>");
5100
5101         n_orb_frames = cfile_read_short(ifile);
5102         orb_w = cfile_read_short(ifile);
5103         orb_h = cfile_read_short(ifile);
5104         save_pos = cftell(ifile);
5105         cfseek(ifile,sizeof(palette)+n_orb_frames*orb_w*orb_h,SEEK_CUR);
5106         n_goal_frames = cfile_read_short(ifile);
5107         cfseek(ifile,save_pos,SEEK_SET);
5108
5109         if (first_time) {
5110                 ubyte *bitmap_data;
5111                 int bitmap_num=Num_bitmap_files;
5112
5113                 //Allocate memory for bitmaps
5114                 MALLOC( bitmap_data, ubyte, n_orb_frames*orb_w*orb_h + n_goal_frames*64*64 );
5115
5116                 //Create orb vclip
5117                 orb_vclip = Num_vclips++;
5118                 Assert(Num_vclips <= VCLIP_MAXNUM);
5119                 Vclip[orb_vclip].play_time = F1_0/2;
5120                 Vclip[orb_vclip].num_frames = n_orb_frames;
5121                 Vclip[orb_vclip].frame_time = Vclip[orb_vclip].play_time / Vclip[orb_vclip].num_frames;
5122                 Vclip[orb_vclip].flags = 0;
5123                 Vclip[orb_vclip].sound_num = -1;
5124                 Vclip[orb_vclip].light_value = F1_0;
5125                 for (i=0;i<n_orb_frames;i++) {
5126                         Vclip[orb_vclip].frames[i].index = bitmap_num;
5127                         init_bitmap(&GameBitmaps[bitmap_num],orb_w,orb_h,BM_FLAG_TRANSPARENT,bitmap_data);
5128                         bitmap_data += orb_w*orb_h;
5129                         bitmap_num++;
5130                         Assert(bitmap_num < MAX_BITMAP_FILES);
5131                 }
5132
5133                 //Create obj powerup
5134                 Powerup_info[POW_HOARD_ORB].vclip_num = orb_vclip;
5135                 Powerup_info[POW_HOARD_ORB].hit_sound = -1; //Powerup_info[POW_SHIELD_BOOST].hit_sound;
5136                 Powerup_info[POW_HOARD_ORB].size = Powerup_info[POW_SHIELD_BOOST].size;
5137                 Powerup_info[POW_HOARD_ORB].light = Powerup_info[POW_SHIELD_BOOST].light;
5138
5139                 //Create orb goal wall effect
5140                 Hoard_goal_eclip = Num_effects++;
5141                 Assert(Num_effects < MAX_EFFECTS);
5142                 Effects[Hoard_goal_eclip] = Effects[94];        //copy from blue goal
5143                 Effects[Hoard_goal_eclip].changing_wall_texture = NumTextures;
5144                 Effects[Hoard_goal_eclip].vc.num_frames=n_goal_frames;
5145
5146                 TmapInfo[NumTextures] = TmapInfo[find_goal_texture(TMI_GOAL_BLUE)];
5147                 TmapInfo[NumTextures].eclip_num = Hoard_goal_eclip;
5148                 TmapInfo[NumTextures].flags = TMI_GOAL_HOARD;
5149                 NumTextures++;
5150                 Assert(NumTextures < MAX_TEXTURES);
5151                 for (i=0;i<n_goal_frames;i++) {
5152                         Effects[Hoard_goal_eclip].vc.frames[i].index = bitmap_num;
5153                         init_bitmap(&GameBitmaps[bitmap_num],64,64,0,bitmap_data);
5154                         bitmap_data += 64*64;
5155                         bitmap_num++;
5156                         Assert(bitmap_num < MAX_BITMAP_FILES);
5157                 }
5158
5159         }
5160
5161         //Load and remap bitmap data for orb
5162         cfread(palette,3,256,ifile);
5163         for (i=0;i<n_orb_frames;i++) {
5164                 grs_bitmap *bm = &GameBitmaps[Vclip[orb_vclip].frames[i].index];
5165                 cfread(bm->bm_data,1,orb_w*orb_h,ifile);
5166                 gr_remap_bitmap_good( bm, palette, 255, -1 );
5167         }
5168
5169         //Load and remap bitmap data for goal texture
5170         cfile_read_short(ifile);        //skip frame count
5171         cfread(palette,3,256,ifile);
5172         for (i=0;i<n_goal_frames;i++) {
5173                 grs_bitmap *bm = &GameBitmaps[Effects[Hoard_goal_eclip].vc.frames[i].index];
5174                 cfread(bm->bm_data,1,64*64,ifile);
5175                 gr_remap_bitmap_good( bm, palette, 255, -1 );
5176         }
5177
5178         //Load and remap bitmap data for HUD icons
5179         for (i=0;i<2;i++) {
5180                 icon_w = cfile_read_short(ifile);
5181                 icon_h = cfile_read_short(ifile);
5182                 if (first_time) {
5183                         ubyte *bitmap_data;
5184                         MALLOC( bitmap_data, ubyte, icon_w*icon_h );
5185                         init_bitmap(&Orb_icons[i],icon_w,icon_h,BM_FLAG_TRANSPARENT,bitmap_data);
5186                 }
5187                 cfread(palette,3,256,ifile);
5188                 cfread(Orb_icons[i].bm_data,1,icon_w*icon_h,ifile);
5189                 gr_remap_bitmap_good( &Orb_icons[i], palette, 255, -1 );
5190         }
5191
5192         if (first_time) {
5193
5194                 //Load sounds for orb game
5195
5196                 for (i=0;i<4;i++) {
5197                         int len;
5198
5199                         len = cfile_read_int(ifile);        //get 11k len
5200
5201                         if (digi_sample_rate == SAMPLE_RATE_22K) {
5202                                 cfseek(ifile,len,SEEK_CUR);     //skip over 11k sample
5203                                 len = cfile_read_int(ifile);    //get 22k len
5204                         }
5205
5206                         GameSounds[Num_sound_files+i].length = len;
5207                         GameSounds[Num_sound_files+i].data = d_malloc(len);
5208                         cfread(GameSounds[Num_sound_files+i].data,1,len,ifile);
5209
5210                         if (digi_sample_rate == SAMPLE_RATE_11K) {
5211                                 len = cfile_read_int(ifile);    //get 22k len
5212                                 cfseek(ifile,len,SEEK_CUR);     //skip over 22k sample
5213                         }
5214
5215                         Sounds[SOUND_YOU_GOT_ORB+i] = Num_sound_files+i;
5216                         AltSounds[SOUND_YOU_GOT_ORB+i] = Sounds[SOUND_YOU_GOT_ORB+i];
5217                 }
5218         }
5219
5220         cfclose(ifile);
5221
5222         first_time = 0;
5223 }
5224
5225 #ifdef EDITOR
5226 void save_hoard_data(void)
5227 {
5228 #define MAX_BITMAPS_PER_BRUSH 30
5229         grs_bitmap * bm[MAX_BITMAPS_PER_BRUSH];
5230         grs_bitmap icon;
5231         int nframes;
5232         ubyte palette[256*3];
5233         PHYSFS_file *ofile;
5234         int iff_error,i;
5235         char *sounds[] = {"selforb.raw","selforb.r22",          //SOUND_YOU_GOT_ORB                     
5236                                                                 "teamorb.raw","teamorb.r22",            //SOUND_FRIEND_GOT_ORB                  
5237                                                                 "enemyorb.raw","enemyorb.r22",  //SOUND_OPPONENT_GOT_ORB        
5238                                                                 "OPSCORE1.raw","OPSCORE1.r22"}; //SOUND_OPPONENT_HAS_SCORED
5239
5240         ofile = PHYSFSX_openWriteBuffered("hoard.ham");
5241
5242         iff_error = iff_read_animbrush("orb.abm",bm,MAX_BITMAPS_PER_BRUSH,&nframes,palette);
5243         Assert(iff_error == IFF_NO_ERROR);
5244         PHYSFS_writeULE16(ofile, nframes);
5245         PHYSFS_writeULE16(ofile, bm[0]->bm_w);
5246         PHYSFS_writeULE16(ofile, bm[0]->bm_h);
5247         PHYSFS_write(ofile, palette, 3, 256);
5248         for (i=0;i<nframes;i++)
5249                 PHYSFS_write(ofile, bm[i]->bm_data, bm[i]->bm_w*bm[i]->bm_h, 1);
5250
5251         iff_error = iff_read_animbrush("orbgoal.abm",bm,MAX_BITMAPS_PER_BRUSH,&nframes,palette);
5252         Assert(iff_error == IFF_NO_ERROR);
5253         Assert(bm[0]->bm_w == 64 && bm[0]->bm_h == 64);
5254         PHYSFS_writeULE16(ofile, nframes);
5255         PHYSFS_write(ofile, palette, 3, 256);
5256         for (i=0;i<nframes;i++)
5257                 PHYSFS_write(ofile, bm[i]->bm_data, bm[i]->bm_w*bm[i]->bm_h, 1);
5258
5259         for (i=0;i<2;i++)
5260         {
5261                 iff_error = iff_read_bitmap(i?"orbb.bbm":"orb.bbm",&icon,BM_LINEAR,palette);
5262                 Assert(iff_error == IFF_NO_ERROR);
5263                 PHYSFS_writeULE16(ofile, icon.bm_w);
5264                 PHYSFS_writeULE16(ofile, icon.bm_h);
5265                 PHYSFS_write(ofile, palette, 3, 256);
5266                 PHYSFS_write(ofile, icon.bm_data, icon.bm_w*icon.bm_h, 1);
5267         }
5268
5269         for (i=0;i<sizeof(sounds)/sizeof(*sounds);i++) {
5270                 PHYSFS_file *ifile;
5271                 int size;
5272                 ubyte *buf;
5273
5274                 ifile = PHYSFS_openRead(sounds[i]);
5275                 Assert(ifile != NULL);
5276                 size = PHYSFS_fileLength(ifile);
5277                 buf = d_malloc(size);
5278                 PHYSFS_read(ifile, buf, size, 1);
5279                 PHYSFS_writeULE32(ofile, size);
5280                 PHYSFS_write(ofile, buf, size, 1);
5281                 d_free(buf);
5282                 PHYSFS_close(ifile);
5283         }
5284
5285         PHYSFS_close(ofile);
5286 }
5287 #endif
5288
5289 void
5290 multi_process_data(char *buf, int len)
5291 {
5292         // Take an entire message (that has already been checked for validity,
5293         // if necessary) and act on it.
5294
5295         int type;
5296
5297         type = buf[0];
5298
5299         if (type > MULTI_MAX_TYPE)
5300         {
5301                 mprintf((1, "multi_process_data: invalid type %d.\n", type));
5302                 Int3();
5303                 return;
5304         }
5305
5306
5307 #ifdef NETPROFILING
5308         TTRecv[type]++;
5309         fprintf (RecieveLogFile,"Packet type: %d Len:%d TT=%d\n",type,len,TTRecv[type]);
5310         fflush (RecieveLogFile);
5311 #endif
5312
5313         switch(type)
5314         {
5315         case MULTI_POSITION:
5316                 if (!Endlevel_sequence) multi_do_position(buf); break;
5317         case MULTI_REAPPEAR:
5318                 if (!Endlevel_sequence) multi_do_reappear(buf); break;
5319         case MULTI_FIRE:
5320                 if (!Endlevel_sequence) multi_do_fire(buf); break;
5321         case MULTI_KILL:
5322                 multi_do_kill(buf); break;
5323         case MULTI_REMOVE_OBJECT:
5324                 if (!Endlevel_sequence) multi_do_remobj(buf); break;
5325         case MULTI_PLAYER_DROP:
5326         case MULTI_PLAYER_EXPLODE:
5327                 if (!Endlevel_sequence) multi_do_player_explode(buf); break;
5328         case MULTI_MESSAGE:
5329                 if (!Endlevel_sequence) multi_do_message(buf); break;
5330         case MULTI_QUIT:
5331                 if (!Endlevel_sequence) multi_do_quit(buf); break;
5332         case MULTI_BEGIN_SYNC:
5333                 break;
5334         case MULTI_CONTROLCEN:
5335                 if (!Endlevel_sequence) multi_do_controlcen_destroy(buf); break;
5336         case MULTI_POWERUP_UPDATE:
5337                 if (!Endlevel_sequence) multi_do_powerup_update(buf); break;
5338         case MULTI_SOUND_FUNCTION:
5339                 multi_do_sound_function(buf); break;
5340         case MULTI_MARKER:
5341                 if (!Endlevel_sequence) multi_do_drop_marker (buf); break;
5342         case MULTI_DROP_WEAPON:
5343                 if (!Endlevel_sequence) multi_do_drop_weapon(buf); break;
5344         case MULTI_DROP_FLAG:
5345                 if (!Endlevel_sequence) multi_do_drop_flag(buf); break;
5346         case MULTI_GUIDED:
5347                 if (!Endlevel_sequence) multi_do_guided (buf); break;
5348         case MULTI_STOLEN_ITEMS:
5349                 if (!Endlevel_sequence) multi_do_stolen_items(buf); break;
5350         case MULTI_WALL_STATUS:
5351                 if (!Endlevel_sequence) multi_do_wall_status(buf); break;
5352         case MULTI_HEARTBEAT:
5353                 if (!Endlevel_sequence) multi_do_heartbeat (buf); break;
5354         case MULTI_SEISMIC:
5355                 if (!Endlevel_sequence) multi_do_seismic (buf); break;
5356         case MULTI_LIGHT:
5357                 if (!Endlevel_sequence) multi_do_light (buf); break;
5358         case MULTI_KILLGOALS:
5359
5360                 if (!Endlevel_sequence) multi_do_kill_goal_counts (buf); break;
5361         case MULTI_ENDLEVEL_START:
5362                 if (!Endlevel_sequence) multi_do_escape(buf); break;
5363         case MULTI_END_SYNC:
5364                 break;
5365         case MULTI_CLOAK:
5366                 if (!Endlevel_sequence) multi_do_cloak(buf); break;
5367         case MULTI_DECLOAK:
5368                 if (!Endlevel_sequence) multi_do_decloak(buf); break;
5369         case MULTI_DOOR_OPEN:
5370                 if (!Endlevel_sequence) multi_do_door_open(buf); break;
5371         case MULTI_CREATE_EXPLOSION:
5372                 if (!Endlevel_sequence) multi_do_create_explosion(buf); break;
5373         case MULTI_CONTROLCEN_FIRE:
5374                 if (!Endlevel_sequence) multi_do_controlcen_fire(buf); break;
5375         case MULTI_CREATE_POWERUP:
5376                 if (!Endlevel_sequence) multi_do_create_powerup(buf); break;
5377         case MULTI_PLAY_SOUND:
5378                 if (!Endlevel_sequence) multi_do_play_sound(buf); break;
5379         case MULTI_CAPTURE_BONUS:
5380                 if (!Endlevel_sequence) multi_do_capture_bonus(buf); break;
5381         case MULTI_ORB_BONUS:
5382                 if (!Endlevel_sequence) multi_do_orb_bonus(buf); break;
5383         case MULTI_GOT_FLAG:
5384                 if (!Endlevel_sequence) multi_do_got_flag(buf); break;
5385         case MULTI_GOT_ORB:
5386                 if (!Endlevel_sequence) multi_do_got_orb(buf); break;
5387         case MULTI_PLAY_BY_PLAY:
5388                 if (!Endlevel_sequence) multi_do_play_by_play(buf); break;
5389         case MULTI_RANK:
5390                 if (!Endlevel_sequence) multi_do_ranking (buf); break;
5391         case MULTI_MODEM_PING:
5392                 if (!Endlevel_sequence) multi_send_modem_ping_return(); break;
5393         case MULTI_MODEM_PING_RETURN:
5394                 if (!Endlevel_sequence) multi_do_modem_ping_return(); break;
5395 #ifndef SHAREWARE
5396         case MULTI_FINISH_GAME:
5397                 multi_do_finish_game(buf); break;  // do this one regardless of endsequence
5398         case MULTI_ROBOT_CONTROLS:
5399                 if (!Endlevel_sequence) multi_do_robot_controls(buf); break;
5400         case MULTI_ROBOT_CLAIM:
5401                 if (!Endlevel_sequence) multi_do_claim_robot(buf); break;
5402         case MULTI_ROBOT_POSITION:
5403                 if (!Endlevel_sequence) multi_do_robot_position(buf); break;
5404         case MULTI_ROBOT_EXPLODE:
5405                 if (!Endlevel_sequence) multi_do_robot_explode(buf); break;
5406         case MULTI_ROBOT_RELEASE:
5407                 if (!Endlevel_sequence) multi_do_release_robot(buf); break;
5408         case MULTI_ROBOT_FIRE:
5409                 if (!Endlevel_sequence) multi_do_robot_fire(buf); break;
5410 #endif
5411         case MULTI_SCORE:
5412                 if (!Endlevel_sequence) multi_do_score(buf); break;
5413         case MULTI_CREATE_ROBOT:
5414                 if (!Endlevel_sequence) multi_do_create_robot(buf); break;
5415         case MULTI_TRIGGER:
5416                 if (!Endlevel_sequence) multi_do_trigger(buf); break;
5417         case MULTI_START_TRIGGER:
5418                 if (!Endlevel_sequence) multi_do_start_trigger(buf); break;
5419         case MULTI_FLAGS:
5420                 if (!Endlevel_sequence) multi_do_flags(buf); break;
5421         case MULTI_DROP_BLOB:
5422                 if (!Endlevel_sequence) multi_do_drop_blob(buf); break;
5423         case MULTI_ACTIVE_DOOR:
5424                 if (!Endlevel_sequence) multi_do_active_door(buf); break;
5425         case MULTI_BOSS_ACTIONS:
5426                 if (!Endlevel_sequence) multi_do_boss_actions(buf); break;
5427         case MULTI_CREATE_ROBOT_POWERUPS:
5428                 if (!Endlevel_sequence) multi_do_create_robot_powerups(buf); break;
5429         case MULTI_HOSTAGE_DOOR:
5430                 if (!Endlevel_sequence) multi_do_hostage_door_status(buf); break;
5431         case MULTI_SAVE_GAME:
5432                 if (!Endlevel_sequence) multi_do_save_game(buf); break;
5433         case MULTI_RESTORE_GAME:
5434                 if (!Endlevel_sequence) multi_do_restore_game(buf); break;
5435         case MULTI_REQ_PLAYER:
5436                 if (!Endlevel_sequence) multi_do_req_player(buf); break;
5437         case MULTI_SEND_PLAYER:
5438                 if (!Endlevel_sequence) multi_do_send_player(buf); break;
5439
5440         default:
5441                 mprintf((1, "Invalid type in multi_process_input().\n"));
5442                 Int3();
5443         }
5444 }