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