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