Fix crash if Num_walls=0
[btb/d2x.git] / main / multi.c
1 /* $Id: multi.c,v 1.18 2004-12-17 13:17:46 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 sbyte object_owner[MAX_OBJECTS];   // Who created each object in my universe, -1 = loaded at start
140
141 int   Net_create_objnums[MAX_NET_CREATE_OBJECTS]; // For tracking object creation that will be sent to remote
142 int   Net_create_loc = 0;       // pointer into previous array
143 int   Network_laser_fired = 0;  // How many times we shot
144 int   Network_laser_gun;        // Which gun number we shot
145 int   Network_laser_flags;      // Special flags for the shot
146 int   Network_laser_level;      // What level
147 short Network_laser_track;      // Who is it tracking?
148 char  Network_message[MAX_MESSAGE_LEN];
149 char  Network_message_macro[4][MAX_MESSAGE_LEN];
150 int   Network_message_reciever=-1;
151 int   sorted_kills[MAX_NUM_NET_PLAYERS];
152 short kill_matrix[MAX_NUM_NET_PLAYERS][MAX_NUM_NET_PLAYERS];
153 int   multi_goto_secret = 0;
154 short team_kills[2];
155 int   multi_in_menu = 0;
156 int   multi_leave_menu = 0;
157 int   multi_quit_game = 0;
158
159 netgame_info Netgame;
160 AllNetPlayers_info NetPlayers;
161
162 bitmap_index multi_player_textures[MAX_NUM_NET_PLAYERS][N_PLAYER_SHIP_TEXTURES];
163
164 typedef struct netplayer_stats {
165         ubyte  message_type;
166         ubyte  Player_num;              // Who am i?
167         uint   flags;                   // Powerup flags, see below...
168         fix    energy;                  // Amount of energy remaining.
169         fix    shields;                 // shields remaining (protection)
170         ubyte  lives;                   // Lives remaining, 0 = game over.
171         ubyte  laser_level;             // Current level of the laser.
172         ubyte  primary_weapon_flags;    // bit set indicates the player has this weapon.
173         ubyte  secondary_weapon_flags;  // bit set indicates the player has this weapon.
174         ushort primary_ammo[MAX_PRIMARY_WEAPONS];     // How much ammo of each type.
175         ushort secondary_ammo[MAX_SECONDARY_WEAPONS]; // How much ammo of each type.
176         int    last_score;              // Score at beginning of current level.
177         int    score;                   // Current score.
178         fix    cloak_time;              // Time cloaked
179         fix    invulnerable_time;       // Time invulnerable
180         fix    homing_object_dist;      // Distance of nearest homing object.
181         short  KillGoalCount;
182         short  net_killed_total;        // Number of times killed total
183         short  net_kills_total;         // Number of net kills total
184         short  num_kills_level;         // Number of kills this level
185         short  num_kills_total;         // Number of kills total
186         short  num_robots_level;        // Number of initial robots this level
187         short  num_robots_total;        // Number of robots total
188         ushort hostages_rescued_total;  // Total number of hostages rescued.
189         ushort hostages_total;          // Total number of hostages.
190         ubyte  hostages_on_board;       // Number of hostages on ship.
191         ubyte  unused[16];
192 } netplayer_stats;
193
194 int message_length[MULTI_MAX_TYPE+1] = {
195         24, // POSITION
196         3,  // REAPPEAR
197         8,  // FIRE
198         5,  // KILL
199         4,  // REMOVE_OBJECT
200         97+9, // PLAYER_EXPLODE
201         37, // MESSAGE (MAX_MESSAGE_LENGTH = 40)
202         2,  // QUIT
203         4,  // PLAY_SOUND
204         41, // BEGIN_SYNC
205         4,  // CONTROLCEN
206         5,  // CLAIM ROBOT
207         4,  // END_SYNC
208         2,  // CLOAK
209         3,  // ENDLEVEL_START
210         5,  // DOOR_OPEN
211         2,  // CREATE_EXPLOSION
212         16, // CONTROLCEN_FIRE
213         97+9, // PLAYER_DROP
214         19, // CREATE_POWERUP
215         9,  // MISSILE_TRACK
216         2,  // DE-CLOAK
217         2,  // MENU_CHOICE
218         28, // ROBOT_POSITION  (shortpos_length (23) + 5 = 28)
219         9,  // ROBOT_EXPLODE
220         5,  // ROBOT_RELEASE
221         18, // ROBOT_FIRE
222         6,  // SCORE
223         6,  // CREATE_ROBOT
224         3,  // TRIGGER
225         10, // BOSS_ACTIONS
226         27, // ROBOT_POWERUPS
227         7,  // HOSTAGE_DOOR
228         2+24, // SAVE_GAME      (ubyte slot, uint id, char name[20])
229         2+4,  // RESTORE_GAME   (ubyte slot, uint id)
230         1+1,  // MULTI_REQ_PLAYER
231         sizeof(netplayer_stats), // MULTI_SEND_PLAYER
232         55, // MULTI_MARKER
233         12, // MULTI_DROP_WEAPON
234 #ifndef MACINTOSH
235         3+sizeof(shortpos), // MULTI_GUIDED, IF SHORTPOS CHANGES, CHANGE MAC VALUE BELOW
236 #else
237         26, // MULTI_GUIDED IF SIZE OF SHORTPOS CHANGES, CHANGE THIS VALUE AS WELL!!!!!!
238 #endif
239         11, // MULTI_STOLEN_ITEMS
240         6,  // MULTI_WALL_STATUS
241         5,  // MULTI_HEARTBEAT
242         9,  // MULTI_KILLGOALS
243         9,  // MULTI_SEISMIC
244         18, // MULTI_LIGHT
245         2,  // MULTI_START_TRIGGER
246         6,  // MULTI_FLAGS
247         2,  // MULTI_DROP_BLOB
248         MAX_POWERUP_TYPES+1, // MULTI_POWERUP_UPDATE
249         sizeof(active_door)+3, // MULTI_ACTIVE_DOOR
250         4,  // MULTI_SOUND_FUNCTION
251         2,  // MULTI_CAPTURE_BONUS
252         2,  // MULTI_GOT_FLAG
253         12, // MULTI_DROP_FLAG
254         142, // MULTI_ROBOT_CONTROLS
255         2,  // MULTI_FINISH_GAME
256         3,  // MULTI_RANK
257         1,  // MULTI_MODEM_PING
258         1,  // MULTI_MODEM_PING_RETURN
259         3,  // MULTI_ORB_BONUS
260         2,  // MULTI_GOT_ORB
261         12, // MULTI_DROP_ORB
262         4,  // MULTI_PLAY_BY_PLAY
263 };
264
265 void extract_netplayer_stats( netplayer_stats *ps, player * pd );
266 void use_netplayer_stats( player * ps, netplayer_stats *pd );
267 char PowerupsInMine[MAX_POWERUP_TYPES],MaxPowerupsAllowed[MAX_POWERUP_TYPES];
268 extern fix ThisLevelTime;
269
270 //
271 //  Functions that replace what used to be macros
272 //
273
274 int objnum_remote_to_local(int remote_objnum, int owner)
275 {
276         // Map a remote object number from owner to a local object number
277
278         int result;
279
280         if ((owner >= N_players) || (owner < -1)) {
281                 Int3(); // Illegal!
282                 return(remote_objnum);
283         }
284
285         if (owner == -1)
286                 return(remote_objnum);
287
288         if ((remote_objnum < 0) || (remote_objnum >= MAX_OBJECTS))
289                 return(-1);
290
291         result = remote_to_local[owner][remote_objnum];
292
293         if (result < 0)
294         {
295                 mprintf((1, "Remote object owner %d number %d mapped to -1!\n", owner, remote_objnum));
296                 return(-1);
297         }
298
299 #ifndef NDEBUG
300         if (object_owner[result] != owner)
301         {
302                 mprintf((1, "Remote object owner %d number %d doesn't match owner %d.\n", owner, remote_objnum, object_owner[result]));
303         }
304 #endif
305         //Assert(object_owner[result] == owner);
306
307         return(result);
308 }
309
310 int objnum_local_to_remote(int local_objnum, sbyte *owner)
311 {
312         // Map a local object number to a remote + owner
313
314         int result;
315
316         if ((local_objnum < 0) || (local_objnum > Highest_object_index)) {
317                 *owner = -1;
318                 return(-1);
319         }
320
321         *owner = object_owner[local_objnum];
322
323         if (*owner == -1)
324                 return(local_objnum);
325
326         if ((*owner >= N_players) || (*owner < -1)) {
327                 Int3(); // Illegal!
328                 *owner = -1;
329                 return local_objnum;
330         }
331
332         result = local_to_remote[local_objnum];
333
334         //mprintf((0, "Local object %d mapped to owner %d objnum %d.\n", local_objnum,
335         //      *owner, result));
336
337         if (result < 0)
338         {
339                 Int3(); // See Rob, object has no remote number!
340         }
341
342         return(result);
343 }
344
345 void
346 map_objnum_local_to_remote(int local_objnum, int remote_objnum, int owner)
347 {
348         // Add a mapping from a network remote object number to a local one
349
350         Assert(local_objnum > -1);
351         Assert(local_objnum < MAX_OBJECTS);
352         Assert(remote_objnum > -1);
353         Assert(remote_objnum < MAX_OBJECTS);
354         Assert(owner > -1);
355         Assert(owner != Player_num);
356
357         object_owner[local_objnum] = owner;
358
359         remote_to_local[owner][remote_objnum] = local_objnum;
360         local_to_remote[local_objnum] = remote_objnum;
361
362         return;
363 }
364
365 void
366 map_objnum_local_to_local(int local_objnum)
367 {
368         // Add a mapping for our locally created objects
369
370         Assert(local_objnum > -1);
371         Assert(local_objnum < MAX_OBJECTS);
372
373         object_owner[local_objnum] = Player_num;
374         remote_to_local[Player_num][local_objnum] = local_objnum;
375         local_to_remote[local_objnum] = local_objnum;
376
377         return;
378 }
379
380 void reset_network_objects()
381 {
382         memset(local_to_remote, -1, MAX_OBJECTS*sizeof(short));
383         memset(remote_to_local, -1, MAX_NUM_NET_PLAYERS*MAX_OBJECTS*sizeof(short));
384         memset(object_owner, -1, MAX_OBJECTS);
385 }
386
387 //
388 // Part 1 : functions whose main purpose in life is to divert the flow
389 //          of execution to either network or serial specific code based
390 //          on the curretn Game_mode value.
391 //
392
393 void
394 multi_endlevel_score(void)
395 {
396         int old_connect=0;
397         int i;
398
399         // Show a score list to end of net players
400
401         // Save connect state and change to new connect state
402 #ifdef NETWORK
403         if (Game_mode & GM_NETWORK)
404         {
405                 old_connect = Players[Player_num].connected;
406                 if (Players[Player_num].connected!=3)
407                         Players[Player_num].connected = CONNECT_END_MENU;
408                 Network_status = NETSTAT_ENDLEVEL;
409
410         }
411 #endif
412
413         // Do the actual screen we wish to show
414
415         Function_mode = FMODE_MENU;
416
417         kmatrix_view(Game_mode & GM_NETWORK);
418
419         Function_mode = FMODE_GAME;
420
421         // Restore connect state
422
423         if (Game_mode & GM_NETWORK)
424         {
425                 Players[Player_num].connected = old_connect;
426         }
427
428 #ifndef SHAREWARE
429         if (Game_mode & GM_MULTI_COOP)
430         {
431                 int i;
432                 for (i = 0; i < MaxNumNetPlayers; i++)
433                         // Reset keys
434                         Players[i].flags &= ~(PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_GOLD_KEY);
435         }
436         for (i = 0; i < MaxNumNetPlayers; i++)
437                 Players[i].flags &= ~(PLAYER_FLAGS_FLAG);  // Clear capture flag
438
439 #endif
440
441         for (i=0;i<MAX_PLAYERS;i++)
442                 Players[i].KillGoalCount=0;
443
444         for (i=0;i<MAX_POWERUP_TYPES;i++)
445         {
446                 MaxPowerupsAllowed[i]=0;
447                 PowerupsInMine[i]=0;
448         }
449
450 }
451
452 int
453 get_team(int pnum)
454 {
455         if ((Game_mode & GM_CAPTURE) && ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)))
456                 return pnum;
457
458         if (Netgame.team_vector & (1 << pnum))
459                 return 1;
460         else
461                 return 0;
462 }
463
464 extern void game_disable_cheats();
465
466 void
467 multi_new_game(void)
468 {
469         int i;
470
471         // Reset variables for a new net game
472
473         memset(kill_matrix, 0, MAX_NUM_NET_PLAYERS*MAX_NUM_NET_PLAYERS*2); // Clear kill matrix
474
475         for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
476         {
477                 sorted_kills[i] = i;
478                 Players[i].net_killed_total = 0;
479                 Players[i].net_kills_total = 0;
480                 Players[i].flags = 0;
481                 Players[i].KillGoalCount=0;
482         }
483
484 #ifndef SHAREWARE
485         for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
486         {
487                 robot_controlled[i] = -1;
488                 robot_agitation[i] = 0;
489                 robot_fired[i] = 0;
490         }
491 #endif
492
493         team_kills[0] = team_kills[1] = 0;
494         Endlevel_sequence = 0;
495         Player_is_dead = 0;
496         multi_leave_menu = 0;
497         multi_quit_game = 0;
498         Show_kill_list = 1;
499         game_disable_cheats();
500         Player_exploded = 0;
501         Dead_player_camera = 0;
502 }
503
504 void
505 multi_make_player_ghost(int playernum)
506 {
507         object *obj;
508
509         //Assert(playernum != Player_num);
510         //Assert(playernum < MAX_NUM_NET_PLAYERS);
511
512         if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS) || (playernum < 0))
513         {
514                 Int3(); // Non-terminal, see Rob
515                 return;
516         }
517
518 //      if (Objects[Players[playernum].objnum].type != OBJ_PLAYER)
519 //              mprintf((1, "Warning: Player %d is not currently a player.\n", playernum));
520
521         obj = &Objects[Players[playernum].objnum];
522
523         obj->type = OBJ_GHOST;
524         obj->render_type = RT_NONE;
525         obj->movement_type = MT_NONE;
526         multi_reset_player_object(obj);
527
528         if (Game_mode & GM_MULTI_ROBOTS)
529                 multi_strip_robots(playernum);
530 }
531
532 void
533 multi_make_ghost_player(int playernum)
534 {
535         object *obj;
536
537 // Assert(playernum != Player_num);
538 // Assert(playernum < MAX_NUM_NET_PLAYERS);
539
540         if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS))
541         {
542                 Int3(); // Non-terminal, see rob
543                 return;
544         }
545
546 //      if(Objects[Players[playernum].objnum].type != OBJ_GHOST)
547 //              mprintf((1, "Warning: Player %d is not currently a ghost.\n", playernum));
548
549         obj = &Objects[Players[playernum].objnum];
550
551         obj->type = OBJ_PLAYER;
552         obj->movement_type = MT_PHYSICS;
553         multi_reset_player_object(obj);
554 }
555
556 int multi_get_kill_list(int *plist)
557 {
558         // Returns the number of active net players and their
559         // sorted order of kills
560         int i;
561         int n = 0;
562
563         for (i = 0; i < N_players; i++)
564                 //if (Players[sorted_kills[i]].connected)
565                 plist[n++] = sorted_kills[i];
566
567         if (n == 0)
568                 Int3(); // SEE ROB OR MATT
569
570         //memcpy(plist, sorted_kills, N_players*sizeof(int));
571
572         return(n);
573 }
574
575 void
576 multi_sort_kill_list(void)
577 {
578         // Sort the kills list each time a new kill is added
579
580         int kills[MAX_NUM_NET_PLAYERS];
581         int i;
582         int changed = 1;
583
584         for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
585         {
586 #ifndef SHAREWARE
587                 if (Game_mode & GM_MULTI_COOP)
588                         kills[i] = Players[i].score;
589                 else
590 #endif
591                 if (Show_kill_list==2)
592                 {
593                         if (Players[i].net_killed_total+Players[i].net_kills_total==0)
594                                 kills[i]=-1;  // always draw the ones without any ratio last
595                         else
596                                 kills[i]=(int)((float)((float)Players[i].net_kills_total/((float)Players[i].net_killed_total+(float)Players[i].net_kills_total))*100.0);
597                 }
598                 else
599                         kills[i] = Players[i].net_kills_total;
600         }
601
602         while (changed)
603         {
604                 changed = 0;
605                 for (i = 0; i < N_players-1; i++)
606                 {
607                         if (kills[sorted_kills[i]] < kills[sorted_kills[i+1]])
608                         {
609                                 changed = sorted_kills[i];
610                                 sorted_kills[i] = sorted_kills[i+1];
611                                 sorted_kills[i+1] = changed;
612                                 changed = 1;
613                         }
614                 }
615         }
616         mprintf((0, "Sorted kills %d %d.\n", sorted_kills[0], sorted_kills[1]));
617 }
618
619 extern object *obj_find_first_of_type (int);
620 char Multi_killed_yourself=0;
621
622 void multi_compute_kill(int killer, int killed)
623 {
624         // Figure out the results of a network kills and add it to the
625         // appropriate player's tally.
626
627         int killed_pnum, killed_type;
628         int killer_pnum, killer_type,killer_id;
629         int TheGoal;
630         char killed_name[(CALLSIGN_LEN*2)+4];
631         char killer_name[(CALLSIGN_LEN*2)+4];
632
633         kmatrix_kills_changed = 1;
634         Multi_killed_yourself=0;
635
636         // Both object numbers are localized already!
637
638         mprintf((0, "compute_kill passed: object %d killed object %d.\n", killer, killed));
639
640         if ((killed < 0) || (killed > Highest_object_index) || (killer < 0) || (killer > Highest_object_index))
641         {
642                 Int3(); // See Rob, illegal value passed to compute_kill;
643                 return;
644         }
645
646         killed_type = Objects[killed].type;
647         killer_type = Objects[killer].type;
648         killer_id = Objects[killer].id;
649
650         if ((killed_type != OBJ_PLAYER) && (killed_type != OBJ_GHOST))
651         {
652                 Int3(); // compute_kill passed non-player object!
653                 return;
654         }
655
656         killed_pnum = Objects[killed].id;
657
658         PKilledFlags[killed_pnum]=1;
659
660         Assert ((killed_pnum >= 0) && (killed_pnum < N_players));
661
662         if (Game_mode & GM_TEAM)
663                 sprintf(killed_name, "%s (%s)", Players[killed_pnum].callsign, Netgame.team_name[get_team(killed_pnum)]);
664         else
665                 sprintf(killed_name, "%s", Players[killed_pnum].callsign);
666
667         if (Newdemo_state == ND_STATE_RECORDING)
668                 newdemo_record_multi_death(killed_pnum);
669
670         digi_play_sample( SOUND_HUD_KILL, F3_0 );
671
672         if (Control_center_destroyed)
673                 Players[killed_pnum].connected=3;
674
675         if (killer_type == OBJ_CNTRLCEN)
676         {
677                 Players[killed_pnum].net_killed_total++;
678                 Players[killed_pnum].net_kills_total--;
679
680                 if (Newdemo_state == ND_STATE_RECORDING)
681                         newdemo_record_multi_kill(killed_pnum, -1);
682
683                 if (killed_pnum == Player_num)
684                 {
685                         HUD_init_message("%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_NONPLAY);
686                         multi_add_lifetime_killed ();
687                 }
688                 else
689                         HUD_init_message("%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_NONPLAY );
690                 return;
691         }
692
693 #ifndef SHAREWARE
694         else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST))
695         {
696                 if (killer_id==PMINE_ID && killer_type!=OBJ_ROBOT)
697                 {
698                         if (killed_pnum == Player_num)
699                                 HUD_init_message("You were killed by a mine!");
700                         else
701                                 HUD_init_message("%s was killed by a mine!",killed_name);
702                 }
703                 else
704                 {
705                         if (killed_pnum == Player_num)
706                         {
707                                 HUD_init_message("%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_ROBOT);
708                                 multi_add_lifetime_killed();
709                         }
710                         else
711                                 HUD_init_message("%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_ROBOT );
712                 }
713                 Players[killed_pnum].net_killed_total++;
714                 return;
715         }
716 #else
717         else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST) && (killer_id!=PMINE_ID))
718         {
719                 Int3(); // Illegal killer type?
720                 return;
721         }
722         if (killer_id==PMINE_ID)
723         {
724                 if (killed_pnum==Player_num)
725                         HUD_init_message("You were killed by a mine!");
726                 else
727                         HUD_init_message("%s was killed by a mine!",killed_name);
728
729                 Players[killed_pnum].net_killed_total++;
730
731                 return;
732         }
733 #endif
734
735         killer_pnum = Objects[killer].id;
736
737         if (Game_mode & GM_TEAM)
738                 sprintf(killer_name, "%s (%s)", Players[killer_pnum].callsign, Netgame.team_name[get_team(killer_pnum)]);
739         else
740                 sprintf(killer_name, "%s", Players[killer_pnum].callsign);
741
742         // Beyond this point, it was definitely a player-player kill situation
743
744         if ((killer_pnum < 0) || (killer_pnum >= N_players))
745                 Int3(); // See rob, tracking down bug with kill HUD messages
746         if ((killed_pnum < 0) || (killed_pnum >= N_players))
747                 Int3(); // See rob, tracking down bug with kill HUD messages
748
749         if (killer_pnum == killed_pnum)
750         {
751                 if (!(Game_mode & GM_HOARD))
752                 {
753                         if (Game_mode & GM_TEAM)
754                                 team_kills[get_team(killed_pnum)] -= 1;
755
756                         Players[killed_pnum].net_killed_total += 1;
757                         Players[killed_pnum].net_kills_total -= 1;
758
759                         if (Newdemo_state == ND_STATE_RECORDING)
760                                 newdemo_record_multi_kill(killed_pnum, -1);
761                 }
762                 kill_matrix[killed_pnum][killed_pnum] += 1; // # of suicides
763
764                 if (killer_pnum == Player_num)
765                 {
766                         HUD_init_message("%s %s %s!", TXT_YOU, TXT_KILLED, TXT_YOURSELF );
767                         Multi_killed_yourself=1;
768                         multi_add_lifetime_killed();
769                 }
770                 else
771                         HUD_init_message("%s %s", killed_name, TXT_SUICIDE);
772         }
773
774         else
775         {
776                 if (!(Game_mode & GM_HOARD))
777                 {
778                         if (Game_mode & GM_TEAM)
779                         {
780                                 if (get_team(killed_pnum) == get_team(killer_pnum))
781                                         team_kills[get_team(killed_pnum)] -= 1;
782                                 else
783                                         team_kills[get_team(killer_pnum)] += 1;
784                         }
785
786                         Players[killer_pnum].net_kills_total += 1;
787                         Players[killer_pnum].KillGoalCount+=1;
788
789                         if (Newdemo_state == ND_STATE_RECORDING)
790                                 newdemo_record_multi_kill(killer_pnum, 1);
791                 }
792                 else
793                 {
794                         if (Game_mode & GM_TEAM)
795                         {
796                                 if (killed_pnum==Player_num && get_team(killed_pnum) == get_team(killer_pnum))
797                                         Multi_killed_yourself=1;
798                         }
799                 }
800
801                 kill_matrix[killer_pnum][killed_pnum] += 1;
802                 Players[killed_pnum].net_killed_total += 1;
803                 if (killer_pnum == Player_num) {
804                         HUD_init_message("%s %s %s!", TXT_YOU, TXT_KILLED, killed_name);
805                         multi_add_lifetime_kills();
806                         if ((Game_mode & GM_MULTI_COOP) && (Players[Player_num].score >= 1000))
807                                 add_points_to_score(-1000);
808                 }
809                 else if (killed_pnum == Player_num)
810                 {
811                         HUD_init_message("%s %s %s!", killer_name, TXT_KILLED, TXT_YOU);
812                         multi_add_lifetime_killed();
813                         if (Game_mode & GM_HOARD)
814                         {
815                                 if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]>3)
816                                         multi_send_play_by_play (1,killer_pnum,Player_num);
817                                 else if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]>0)
818                                         multi_send_play_by_play (0,killer_pnum,Player_num);
819                         }
820                 }
821                 else
822                         HUD_init_message("%s %s %s!", killer_name, TXT_KILLED, killed_name);
823         }
824
825         TheGoal=Netgame.KillGoal*5;
826
827         if (Netgame.KillGoal>0)
828         {
829                 if (Players[killer_pnum].KillGoalCount>=TheGoal)
830                 {
831                         if (killer_pnum==Player_num)
832                         {
833                                 HUD_init_message("You reached the kill goal!");
834                                 Players[Player_num].shields=i2f(200);
835                         }
836                         else
837                                 HUD_init_message ("%s has reached the kill goal!",Players[killer_pnum].callsign);
838
839                         HUD_init_message ("The control center has been destroyed!");
840                         net_destroy_controlcen (obj_find_first_of_type (OBJ_CNTRLCEN));
841                 }
842         }
843
844         multi_sort_kill_list();
845         multi_show_player_list();
846         Players[killed_pnum].flags&=(~(PLAYER_FLAGS_HEADLIGHT_ON));  // clear the killed guys flags/headlights
847 }
848
849 void
850 multi_do_frame(void)
851 {
852         static int lasttime=0;
853         int i;
854
855         if (!(Game_mode & GM_MULTI))
856         {
857                 Int3();
858                 return;
859         }
860
861         if ((Game_mode & GM_NETWORK) && Netgame.PlayTimeAllowed && lasttime!=f2i (ThisLevelTime))
862         {
863                 for (i=0;i<N_players;i++)
864                         if (Players[i].connected)
865                         {
866                                 if (i==Player_num)
867                                 {
868                                         multi_send_heartbeat();
869                                         lasttime=f2i(ThisLevelTime);
870                                 }
871                                 break;
872                         }
873         }
874
875         multi_send_message(); // Send any waiting messages
876
877         if (!multi_in_menu)
878                 multi_leave_menu = 0;
879
880 #ifndef SHAREWARE
881         if (Game_mode & GM_MULTI_ROBOTS)
882         {
883                 multi_check_robot_timeout();
884         }
885 #endif
886
887         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
888         {
889                 com_do_frame();
890         }
891         else
892         {
893                 network_do_frame(0, 1);
894         }
895
896         if (multi_quit_game && !multi_in_menu)
897         {
898                 multi_quit_game = 0;
899                 longjmp(LeaveGame, 0);
900         }
901 }
902
903 void
904 multi_send_data(char *buf, int len, int repeat)
905 {
906         Assert(len == message_length[(int)buf[0]]);
907         Assert(buf[0] <= MULTI_MAX_TYPE);
908         //      Assert(buf[0] >= 0);
909
910         if (Game_mode & GM_NETWORK)
911                 Assert(buf[0] > 0);
912
913         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
914                 com_send_data(buf, len, repeat);
915         else if (Game_mode & GM_NETWORK)
916                 network_send_data((unsigned char *)buf, len, repeat);
917 }
918
919 void
920 multi_leave_game(void)
921 {
922
923         //      if (Function_mode != FMODE_GAME)
924         //              return;
925
926         if (!(Game_mode & GM_MULTI))
927                 return;
928
929         if (Game_mode & GM_NETWORK)
930         {
931                 mprintf((0, "Sending explosion message.\n"));
932
933                 Net_create_loc = 0;
934                 AdjustMineSpawn();
935                 multi_cap_objects();
936                 drop_player_eggs(ConsoleObject);
937                 multi_send_position(Players[Player_num].objnum);
938                 multi_send_player_explode(MULTI_PLAYER_DROP);
939         }
940
941         mprintf((1, "Sending leave game.\n"));
942         multi_send_quit(MULTI_QUIT);
943
944         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
945                 serial_leave_game();
946         if (Game_mode & GM_NETWORK)
947                 network_leave_game();
948
949         Game_mode |= GM_GAME_OVER;
950
951         if (Function_mode!=FMODE_EXIT)
952                 Function_mode = FMODE_MENU;
953
954         //      N_players = 0;
955
956         //      change_playernum_to(0);
957         //      Viewer = ConsoleObject = &Objects[0];
958
959 }
960
961 void
962 multi_show_player_list()
963 {
964         if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_COOP))
965                 return;
966
967         if (Show_kill_list)
968                 return;
969
970         Show_kill_list_timer = F1_0*5; // 5 second timer
971         Show_kill_list = 1;
972 }
973
974 int
975 multi_endlevel(int *secret)
976 {
977         int result = 0;
978
979         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
980                 com_endlevel(secret);          // an opportunity to re-sync or whatever
981         else if (Game_mode & GM_NETWORK)
982                 result = network_endlevel(secret);
983
984         return(result);
985 }
986
987 //
988 // Part 2 : functions that act on network/serial messages and change the
989 //          the state of the game in some way.
990 //
991
992 #ifndef MACINTOSH
993 //extern PORT *com_port;
994 #endif
995
996 int
997 multi_menu_poll(void)
998 {
999         fix old_shields;
1000         int was_fuelcen_alive;
1001         int player_was_dead;
1002
1003         was_fuelcen_alive = Control_center_destroyed;
1004
1005         // Special polling function for in-game menus for multiplayer and serial
1006
1007         if (! ((Game_mode & GM_MULTI) && (Function_mode == FMODE_GAME)) )
1008                 return(0);
1009
1010         if (multi_leave_menu)
1011                 return(-1);
1012
1013         old_shields = Players[Player_num].shields;
1014         player_was_dead = Player_is_dead;
1015
1016         multi_in_menu++; // Track level of menu nesting
1017
1018         GameLoop( 0, 0 );
1019
1020         multi_in_menu--;
1021
1022         timer_delay(f0_1);   // delay 100 milliseconds
1023
1024         if (Endlevel_sequence || (Control_center_destroyed && !was_fuelcen_alive) || (Player_is_dead != player_was_dead) || (Players[Player_num].shields < old_shields))
1025         {
1026                 multi_leave_menu = 1;
1027                 return(-1);
1028         }
1029         if ((Control_center_destroyed) && (Countdown_seconds_left < 10))
1030         {
1031                 multi_leave_menu = 1;
1032                 return(-1);
1033         }
1034
1035 #if 0
1036         if ((Game_mode & GM_MODEM) && (!GetCd(com_port)))
1037         {
1038                 multi_leave_menu = 1;
1039                 return(-1);
1040         }
1041 #endif
1042
1043         return(0);
1044 }
1045
1046 void
1047 multi_define_macro(int key)
1048 {
1049         if (!(Game_mode & GM_MULTI))
1050                 return;
1051
1052         key &= (~KEY_SHIFTED);
1053
1054         switch(key)
1055         {
1056         case KEY_F9:
1057                 multi_defining_message = 1; break;
1058         case KEY_F10:
1059                 multi_defining_message = 2; break;
1060         case KEY_F11:
1061                 multi_defining_message = 3; break;
1062         case KEY_F12:
1063                 multi_defining_message = 4; break;
1064         default:
1065                 Int3();
1066         }
1067
1068         if (multi_defining_message)     {
1069                 multi_message_index = 0;
1070                 Network_message[multi_message_index] = 0;
1071         }
1072
1073 }
1074
1075 char feedback_result[200];
1076
1077 void
1078 multi_message_feedback(void)
1079 {
1080         char *colon;
1081         int found = 0;
1082         int i;
1083
1084         if (!( ((colon = strrchr(Network_message, ':')) == NULL) || (colon-Network_message < 1) || (colon-Network_message > CALLSIGN_LEN) ))
1085         {
1086                 sprintf(feedback_result, "%s ", TXT_MESSAGE_SENT_TO);
1087                 if ((Game_mode & GM_TEAM) && (atoi(Network_message) > 0) && (atoi(Network_message) < 3))
1088                 {
1089                         sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[atoi(Network_message)-1]);
1090                         found = 1;
1091                 }
1092                 if (Game_mode & GM_TEAM)
1093                 {
1094                         for (i = 0; i < N_players; i++)
1095                         {
1096                                 if (!strnicmp(Netgame.team_name[i], Network_message, colon-Network_message))
1097                                 {
1098                                         if (found)
1099                                                 strcat(feedback_result, ", ");
1100                                         found++;
1101                                         if (!(found % 4))
1102                                                 strcat(feedback_result, "\n");
1103                                         sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[i]);
1104                                 }
1105                         }
1106                 }
1107                 for (i = 0; i < N_players; i++)
1108                 {
1109                         if ((!strnicmp(Players[i].callsign, Network_message, colon-Network_message)) && (i != Player_num) && (Players[i].connected))
1110                         {
1111                                 if (found)
1112                                         strcat(feedback_result, ", ");
1113                                 found++;
1114                                 if (!(found % 4))
1115                                         strcat(feedback_result, "\n");
1116                                 sprintf(feedback_result+strlen(feedback_result), "%s", Players[i].callsign);
1117                         }
1118                 }
1119                 if (!found)
1120                         strcat(feedback_result, TXT_NOBODY);
1121                 else
1122                         strcat(feedback_result, ".");
1123
1124                 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1125
1126                 Assert(strlen(feedback_result) < 200);
1127
1128                 HUD_init_message(feedback_result);
1129                 //sprintf (temp,"%s",colon);
1130                 //sprintf (Network_message,"%s",temp);
1131
1132         }
1133 }
1134
1135 void
1136 multi_send_macro(int key)
1137 {
1138         if (! (Game_mode & GM_MULTI) )
1139                 return;
1140
1141         switch(key)
1142         {
1143         case KEY_F9:
1144                 key = 0; break;
1145         case KEY_F10:
1146                 key = 1; break;
1147         case KEY_F11:
1148                 key = 2; break;
1149         case KEY_F12:
1150                 key = 3; break;
1151         default:
1152                 Int3();
1153         }
1154
1155         if (!Network_message_macro[key][0])
1156         {
1157                 HUD_init_message(TXT_NO_MACRO);
1158                 return;
1159         }
1160
1161         strcpy(Network_message, Network_message_macro[key]);
1162         Network_message_reciever = 100;
1163
1164         HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
1165         multi_message_feedback();
1166 }
1167
1168
1169 void
1170 multi_send_message_start()
1171 {
1172         if (Game_mode&GM_MULTI) {
1173                 multi_sending_message = 1;
1174                 multi_message_index = 0;
1175                 Network_message[multi_message_index] = 0;
1176         }
1177 }
1178
1179 extern fix StartingShields;
1180 fix PingLaunchTime,PingReturnTime;
1181
1182 extern void network_send_ping (ubyte);
1183 extern int network_who_is_master();
1184 extern char NameReturning;
1185 extern int force_cockpit_redraw;
1186
1187 void network_dump_appletalk_player(ubyte node, ushort net, ubyte socket, int why);
1188
1189 void multi_send_message_end()
1190 {
1191         char *mytempbuf;
1192         int i,t;
1193
1194         Network_message_reciever = 100;
1195
1196         if (!strnicmp (Network_message,"!Names",6))
1197         {
1198                 NameReturning=1-NameReturning;
1199                 HUD_init_message ("Name returning is now %s.",NameReturning?"active":"disabled");
1200         }
1201         else if (!strnicmp (Network_message,"Handicap:",9))
1202         {
1203                 mytempbuf=&Network_message[9];
1204                 mprintf ((0,"Networkhandi=%s\n",mytempbuf));
1205                 StartingShields=atol (mytempbuf);
1206                 if (StartingShields<10)
1207                         StartingShields=10;
1208                 if (StartingShields>100)
1209                 {
1210                         sprintf (Network_message,"%s has tried to cheat!",Players[Player_num].callsign);
1211                         StartingShields=100;
1212                 }
1213                 else
1214                         sprintf (Network_message,"%s handicap is now %d",Players[Player_num].callsign,StartingShields);
1215
1216                 HUD_init_message ("Telling others of your handicap of %d!",StartingShields);
1217                 StartingShields=i2f(StartingShields);
1218         }
1219         else if (!strnicmp (Network_message,"NoBombs",7))
1220                 Netgame.DoSmartMine=0;
1221         else if (!strnicmp (Network_message,"Ping:",5))
1222         {
1223                 if (Game_mode & GM_NETWORK)
1224                 {
1225                         int name_index=5;
1226                         if (strlen(Network_message) > 5)
1227                                 while (Network_message[name_index] == ' ')
1228                                         name_index++;
1229
1230                         if (strlen(Network_message)<=name_index)
1231                         {
1232                                 HUD_init_message ("You must specify a name to ping");
1233                                 return;
1234                         }
1235
1236                         for (i = 0; i < N_players; i++)
1237                                 if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (i != Player_num) && (Players[i].connected))
1238                                 {
1239                                         PingLaunchTime=timer_get_fixed_seconds();
1240                                         network_send_ping (i);
1241                                         HUD_init_message("Pinging %s...",Players[i].callsign);
1242                                         multi_message_index = 0;
1243                                         multi_sending_message = 0;
1244                                         return;
1245                                 }
1246                 }
1247                 else // Modem/Serial ping
1248                 {
1249                         PingLaunchTime=timer_get_fixed_seconds();
1250                         multi_send_modem_ping ();
1251                         HUD_init_message("Pinging opponent...");
1252                     multi_message_index = 0;
1253                         multi_sending_message = 0;
1254                         return;
1255                 }
1256         }
1257         else if (!strnicmp (Network_message,"move:",5))
1258         {
1259                 mprintf ((0,"moving?\n"));
1260
1261                 if ((Game_mode & GM_NETWORK) && (Game_mode & GM_TEAM))
1262                 {
1263                         int name_index=5;
1264                         if (strlen(Network_message) > 5)
1265                                 while (Network_message[name_index] == ' ')
1266                                         name_index++;
1267
1268                         if (!network_i_am_master())
1269                         {
1270                                 HUD_init_message ("Only %s can move players!",Players[network_who_is_master()].callsign);
1271                                 return;
1272                         }
1273
1274                         if (strlen(Network_message)<=name_index)
1275                         {
1276                                 HUD_init_message ("You must specify a name to move");
1277                                 return;
1278                         }
1279
1280                         for (i = 0; i < N_players; i++)
1281                                 if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (Players[i].connected))
1282                                 {
1283                                         if ((Game_mode & GM_CAPTURE) && (Players[i].flags & PLAYER_FLAGS_FLAG))
1284                                         {
1285                                                 HUD_init_message ("Can't move player because s/he has a flag!");
1286                                                 return;
1287                                         }
1288
1289                                         if (Netgame.team_vector & (1<<i))
1290                                                 Netgame.team_vector&=(~(1<<i));
1291                                         else
1292                                                 Netgame.team_vector|=(1<<i);
1293
1294                                         for (t=0;t<N_players;t++)
1295                                                 if (Players[t].connected)
1296                                                         multi_reset_object_texture (&Objects[Players[t].objnum]);
1297
1298                                         network_send_netgame_update ();
1299                                         sprintf (Network_message,"%s has changed teams!",Players[i].callsign);
1300                                         if (i==Player_num)
1301                                         {
1302                                                 HUD_init_message ("You have changed teams!");
1303                                                 reset_cockpit();
1304                                         }
1305                                         else
1306                                                 HUD_init_message ("Moving %s to other team.",Players[i].callsign);
1307                                         break;
1308                                 }
1309                 }
1310         }
1311
1312         else if (!strnicmp (Network_message,"kick:",5) && (Game_mode & GM_NETWORK))
1313         {
1314                 int name_index=5;
1315                 if (strlen(Network_message) > 5)
1316                         while (Network_message[name_index] == ' ')
1317                                 name_index++;
1318
1319                 if (!network_i_am_master())
1320                 {
1321                         HUD_init_message ("Only %s can kick others out!",Players[network_who_is_master()].callsign);
1322                         multi_message_index = 0;
1323                         multi_sending_message = 0;
1324                         return;
1325                 }
1326                 if (strlen(Network_message)<=name_index)
1327                 {
1328                         HUD_init_message ("You must specify a name to kick");
1329                         multi_message_index = 0;
1330                         multi_sending_message = 0;
1331                         return;
1332                 }
1333
1334                 if (Network_message[name_index] == '#' && isdigit(Network_message[name_index+1])) {
1335                         int players[MAX_NUM_NET_PLAYERS];
1336                         int listpos = Network_message[name_index+1] - '0';
1337
1338                         mprintf ((0,"Trying to kick %d , show_kill_list=%d\n",listpos,Show_kill_list));
1339
1340                         if (Show_kill_list==1 || Show_kill_list==2) {
1341                                 if (listpos == 0 || listpos >= N_players) {
1342                                         HUD_init_message ("Invalid player number for kick.");
1343                                         multi_message_index = 0;
1344                                         multi_sending_message = 0;
1345                                         return;
1346                                 }
1347                                 multi_get_kill_list(players);
1348                                 i = players[listpos];
1349                                 if ((i != Player_num) && (Players[i].connected))
1350                                         goto kick_player;
1351                         }
1352                         else HUD_init_message ("You cannot use # kicking with in team display.");
1353
1354
1355                     multi_message_index = 0;
1356                     multi_sending_message = 0;
1357                         return;
1358                 }
1359
1360
1361                 for (i = 0; i < N_players; i++)
1362                 if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (i != Player_num) && (Players[i].connected)) {
1363                         kick_player:;
1364                                 if (Network_game_type == IPX_GAME)
1365                                         network_dump_player(NetPlayers.players[i].network.ipx.server,NetPlayers.players[i].network.ipx.node, 7);
1366                                 else
1367                                         network_dump_appletalk_player(NetPlayers.players[i].network.appletalk.node,NetPlayers.players[i].network.appletalk.net, NetPlayers.players[i].network.appletalk.socket, 7);
1368
1369                                 HUD_init_message("Dumping %s...",Players[i].callsign);
1370                                 multi_message_index = 0;
1371                                 multi_sending_message = 0;
1372                                 return;
1373                         }
1374         }
1375
1376         else
1377                 HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
1378
1379         multi_send_message();
1380         multi_message_feedback();
1381
1382         multi_message_index = 0;
1383         multi_sending_message = 0;
1384 }
1385
1386 void multi_define_macro_end()
1387 {
1388         Assert( multi_defining_message > 0 );
1389
1390         strcpy( Network_message_macro[multi_defining_message-1], Network_message );
1391         write_player_file();
1392
1393         multi_message_index = 0;
1394         multi_defining_message = 0;
1395 }
1396
1397 void multi_message_input_sub( int key )
1398 {
1399         switch( key )   {
1400         case KEY_F8:
1401         case KEY_ESC:
1402                 multi_sending_message = 0;
1403                 multi_defining_message = 0;
1404                 game_flush_inputs();
1405                 break;
1406         case KEY_LEFT:
1407         case KEY_BACKSP:
1408         case KEY_PAD4:
1409                 if (multi_message_index > 0)
1410                         multi_message_index--;
1411                 Network_message[multi_message_index] = 0;
1412                 break;
1413         case KEY_ENTER:
1414                 if ( multi_sending_message )
1415                         multi_send_message_end();
1416                 else if ( multi_defining_message )
1417                         multi_define_macro_end();
1418                 game_flush_inputs();
1419                 break;
1420         default:
1421                 if ( key > 0 )  {
1422                         int ascii = key_to_ascii(key);
1423                         if ((ascii < 255 ))     {
1424                                 if (multi_message_index < MAX_MESSAGE_LEN-2 )   {
1425                                         Network_message[multi_message_index++] = ascii;
1426                                         Network_message[multi_message_index] = 0;
1427                                 } else if ( multi_sending_message )     {
1428                                         int i;
1429                                         char * ptext, * pcolon;
1430                                         ptext = NULL;
1431                                         Network_message[multi_message_index++] = ascii;
1432                                         Network_message[multi_message_index] = 0;
1433                                         for (i=multi_message_index-1; i>=0; i-- )       {
1434                                                 if ( Network_message[i]==32 )   {
1435                                                         ptext = &Network_message[i+1];
1436                                                         Network_message[i] = 0;
1437                                                         break;
1438                                                 }
1439                                         }
1440                                         multi_send_message_end();
1441                                         if ( ptext )    {
1442                                                 multi_sending_message = 1;
1443                                                 pcolon = strchr( Network_message, ':' );
1444                                                 if ( pcolon )
1445                                                         strcpy( pcolon+1, ptext );
1446                                                 else
1447                                                         strcpy( Network_message, ptext );
1448                                                 multi_message_index = strlen( Network_message );
1449                                         }
1450                                 }
1451                         }
1452                 }
1453         }
1454 }
1455
1456 void
1457 multi_send_message_dialog(void)
1458 {
1459         newmenu_item m[1];
1460         int choice;
1461
1462         if (!(Game_mode&GM_MULTI))
1463                 return;
1464
1465         Network_message[0] = 0;             // Get rid of old contents
1466
1467         m[0].type=NM_TYPE_INPUT; m[0].text = Network_message; m[0].text_len = MAX_MESSAGE_LEN-1;
1468         choice = newmenu_do( NULL, TXT_SEND_MESSAGE, 1, m, NULL );
1469
1470         if ((choice > -1) && (strlen(Network_message) > 0)) {
1471                 Network_message_reciever = 100;
1472                 HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
1473                 multi_message_feedback();
1474         }
1475 }
1476
1477
1478
1479 void
1480 multi_do_death(int objnum)
1481 {
1482         // Do any miscellaneous stuff for a new network player after death
1483
1484         objnum = objnum;
1485
1486         if (!(Game_mode & GM_MULTI_COOP))
1487         {
1488                 mprintf((0, "Setting all keys for player %d.\n", Player_num));
1489                 Players[Player_num].flags |= (PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_GOLD_KEY);
1490         }
1491 }
1492
1493 void
1494 multi_do_fire(char *buf)
1495 {
1496         ubyte weapon;
1497         char pnum;
1498         sbyte flags;
1499         //static dum=0;
1500
1501         // Act out the actual shooting
1502         pnum = buf[1];
1503
1504 #ifndef MACINTOSH
1505         weapon = (int)buf[2];
1506 #else
1507         weapon = buf[2];
1508 #endif
1509         flags = buf[4];
1510         Network_laser_track = GET_INTEL_SHORT(buf + 6);
1511
1512         Assert (pnum < N_players);
1513
1514         if (Objects[Players[(int)pnum].objnum].type == OBJ_GHOST)
1515                 multi_make_ghost_player(pnum);
1516
1517         //  mprintf((0,"multi_do_fire, weapon = %d\n",weapon));
1518
1519         if (weapon == FLARE_ADJUST)
1520                 Laser_player_fire( Objects+Players[(int)pnum].objnum, FLARE_ID, 6, 1, 0);
1521         else if (weapon >= MISSILE_ADJUST) {
1522                 int weapon_id,weapon_gun;
1523
1524                 weapon_id = Secondary_weapon_to_weapon_info[weapon-MISSILE_ADJUST];
1525                 weapon_gun = Secondary_weapon_to_gun_num[weapon-MISSILE_ADJUST] + (flags & 1);
1526                 mprintf((0,"missile id = %d, gun = %d\n",weapon_id,weapon_gun));
1527
1528                 if (weapon-MISSILE_ADJUST==GUIDED_INDEX)
1529                 {
1530                         mprintf ((0,"Missile is guided!!!\n"));
1531                         Multi_is_guided=1;
1532                 }
1533
1534                 Laser_player_fire( Objects+Players[(int)pnum].objnum, weapon_id, weapon_gun, 1, 0 );
1535         }
1536         else {
1537                 fix save_charge = Fusion_charge;
1538
1539                 if (weapon == FUSION_INDEX) {
1540                         Fusion_charge = flags << 12;
1541                         mprintf((0, "Fusion charge X%f.\n", f2fl(Fusion_charge)));
1542                 }
1543                 if (weapon == LASER_ID) {
1544                         if (flags & LASER_QUAD)
1545                                 Players[(int)pnum].flags |= PLAYER_FLAGS_QUAD_LASERS;
1546                         else
1547                                 Players[(int)pnum].flags &= ~PLAYER_FLAGS_QUAD_LASERS;
1548                 }
1549
1550                 do_laser_firing(Players[(int)pnum].objnum, weapon, (int)buf[3], flags, (int)buf[5]);
1551
1552                 if (weapon == FUSION_INDEX)
1553                         Fusion_charge = save_charge;
1554         }
1555 }
1556
1557 void
1558 multi_do_message(char *buf)
1559 {
1560         char *colon;
1561         char *tilde,mesbuf[100];
1562         int tloc,t;
1563
1564         int loc = 2;
1565
1566         if ((tilde=strchr (buf+loc,'$')))  // do that stupid name stuff
1567         {                                                                                       // why'd I put this in?  Probably for the
1568                 tloc=tilde-(buf+loc);                           // same reason you can name your guidebot
1569                 mprintf ((0,"Tloc=%d\n",tloc));
1570
1571                 if (tloc>0)
1572                         strncpy (mesbuf,buf+loc,tloc);
1573                 strcpy (mesbuf+tloc,Players[Player_num].callsign);
1574                 strcpy (mesbuf+strlen(Players[Player_num].callsign)+tloc,buf+loc+tloc+1);
1575                 strcpy (buf+loc,mesbuf);
1576         }
1577
1578         if (((colon = strrchr(buf+loc, ':')) == NULL) || (colon-(buf+loc) < 1) || (colon-(buf+loc) > CALLSIGN_LEN))
1579         {
1580                 mesbuf[0] = 1;
1581                 mesbuf[1] = BM_XRGB(28, 0, 0);
1582                 strcpy(&mesbuf[2], Players[(int)buf[1]].callsign);
1583                 t = strlen(mesbuf);
1584                 mesbuf[t] = ':';
1585                 mesbuf[t+1] = 1;
1586                 mesbuf[t+2] = BM_XRGB(0, 31, 0);
1587                 mesbuf[t+3] = 0;
1588
1589                 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1590                 HUD_init_message("%s %s", mesbuf, buf+2);
1591         }
1592         else
1593         {
1594                 if ( (!strnicmp(Players[Player_num].callsign, buf+loc, colon-(buf+loc))) ||
1595                          ((Game_mode & GM_TEAM) && ( (get_team(Player_num) == atoi(buf+loc)-1) || !strnicmp(Netgame.team_name[get_team(Player_num)], buf+loc, colon-(buf+loc)))) )
1596                 {
1597                         mesbuf[0] = 1;
1598                         mesbuf[1] = BM_XRGB(0, 32, 32);
1599                         strcpy(&mesbuf[2], Players[(int)buf[1]].callsign);
1600                         t = strlen(mesbuf);
1601                         mesbuf[t] = ':';
1602                         mesbuf[t+1] = 1;
1603                         mesbuf[t+2] = BM_XRGB(0, 31, 0);
1604                         mesbuf[t+3] = 0;
1605
1606                         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1607                         HUD_init_message("%s %s", mesbuf, colon+1);
1608                 }
1609         }
1610 }
1611
1612 void
1613 multi_do_position(char *buf)
1614 {
1615 #ifdef WORDS_BIGENDIAN
1616         shortpos sp;
1617 #endif
1618
1619         // This routine does only player positions, mode game only
1620         //      mprintf((0, "Got position packet.\n"));
1621
1622         int pnum = (Player_num+1)%2;
1623
1624         Assert(&Objects[Players[pnum].objnum] != ConsoleObject);
1625
1626         if (Game_mode & GM_NETWORK)
1627         {
1628                 Int3(); // Get Jason, what the hell are we doing here?
1629                 return;
1630     }
1631
1632
1633 #ifndef WORDS_BIGENDIAN
1634         extract_shortpos(&Objects[Players[pnum].objnum], (shortpos *)(buf+1),0);
1635 #else
1636         memcpy((ubyte *)(sp.bytemat), (ubyte *)(buf + 1), 9);
1637         memcpy((ubyte *)&(sp.xo), (ubyte *)(buf + 10), 14);
1638         extract_shortpos(&Objects[Players[pnum].objnum], &sp, 1);
1639 #endif
1640
1641         if (Objects[Players[pnum].objnum].movement_type == MT_PHYSICS)
1642                 set_thrust_from_velocity(&Objects[Players[pnum].objnum]);
1643 }
1644
1645 void
1646 multi_do_reappear(char *buf)
1647 {
1648         short objnum;
1649
1650         objnum = GET_INTEL_SHORT(buf + 1);
1651
1652         Assert(objnum >= 0);
1653         //      Assert(Players[Objects[objnum].id]].objnum == objnum);
1654
1655         // mprintf((0, "Switching rendering back on for object %d.\n", objnum));
1656
1657         multi_make_ghost_player(Objects[objnum].id);
1658         create_player_appearance_effect(&Objects[objnum]);
1659         PKilledFlags[Objects[objnum].id]=0;
1660 }
1661
1662 void
1663 multi_do_player_explode(char *buf)
1664 {
1665         // Only call this for players, not robots.  pnum is player number, not
1666         // Object number.
1667
1668         object *objp;
1669         int count;
1670         int pnum;
1671         int i;
1672         char remote_created;
1673
1674         pnum = buf[1];
1675
1676 #ifdef NDEBUG
1677         if ((pnum < 0) || (pnum >= N_players))
1678                 return;
1679 #else
1680         Assert(pnum >= 0);
1681         Assert(pnum < N_players);
1682 #endif
1683
1684 #ifdef NETWORK
1685         // If we are in the process of sending objects to a new player, reset that process
1686         if (Network_send_objects)
1687         {
1688                 mprintf((0, "Resetting object sync due to player explosion.\n"));
1689                 Network_send_objnum = -1;
1690         }
1691 #endif
1692
1693         // Stuff the Players structure to prepare for the explosion
1694
1695         count = 2;
1696         Players[pnum].primary_weapon_flags = GET_INTEL_SHORT(buf + count); count += 2;
1697         Players[pnum].secondary_weapon_flags = GET_INTEL_SHORT(buf + count); count += 2;
1698         Players[pnum].laser_level = buf[count];                                                 count++;
1699         Players[pnum].secondary_ammo[HOMING_INDEX] = buf[count];                count++;
1700         Players[pnum].secondary_ammo[CONCUSSION_INDEX] = buf[count];count++;
1701         Players[pnum].secondary_ammo[SMART_INDEX] = buf[count];         count++;
1702         Players[pnum].secondary_ammo[MEGA_INDEX] = buf[count];          count++;
1703         Players[pnum].secondary_ammo[PROXIMITY_INDEX] = buf[count]; count++;
1704
1705         Players[pnum].secondary_ammo[SMISSILE1_INDEX] = buf[count]; count++;
1706         Players[pnum].secondary_ammo[GUIDED_INDEX]    = buf[count]; count++;
1707         Players[pnum].secondary_ammo[SMART_MINE_INDEX]= buf[count]; count++;
1708         Players[pnum].secondary_ammo[SMISSILE4_INDEX] = buf[count]; count++;
1709         Players[pnum].secondary_ammo[SMISSILE5_INDEX] = buf[count]; count++;
1710
1711         Players[pnum].primary_ammo[VULCAN_INDEX] = GET_INTEL_SHORT(buf + count); count += 2;
1712         Players[pnum].primary_ammo[GAUSS_INDEX] = GET_INTEL_SHORT(buf + count); count += 2;
1713         Players[pnum].flags = GET_INTEL_INT(buf + count);               count += 4;
1714
1715         multi_adjust_remote_cap (pnum);
1716
1717         objp = Objects+Players[pnum].objnum;
1718
1719         //      objp->phys_info.velocity = *(vms_vector *)(buf+16); // 12 bytes
1720         //      objp->pos = *(vms_vector *)(buf+28);                // 12 bytes
1721
1722         remote_created = buf[count++]; // How many did the other guy create?
1723
1724         Net_create_loc = 0;
1725
1726         drop_player_eggs(objp);
1727
1728         // Create mapping from remote to local numbering system
1729
1730         mprintf((0, "I Created %d powerups, remote created %d.\n", Net_create_loc, remote_created));
1731
1732         // We now handle this situation gracefully, Int3 not required
1733         //      if (Net_create_loc != remote_created)
1734         //              Int3(); // Probably out of object array space, see Rob
1735
1736         for (i = 0; i < remote_created; i++)
1737         {
1738                 short s;
1739
1740                 s = GET_INTEL_SHORT(buf + count);
1741
1742                 if ((i < Net_create_loc) && (s > 0))
1743                         map_objnum_local_to_remote((short)Net_create_objnums[i], s, pnum);
1744                 else if (s <= 0)
1745                 {
1746                         mprintf((0, "WARNING: Remote created object has non-valid number %d (player %d)", s, pnum));
1747                 }
1748                 else
1749                 {
1750                         mprintf((0, "WARNING: Could not create all powerups created by player %d.\n", pnum));
1751                 }
1752                 //              Assert(s > 0);
1753                 count += 2;
1754         }
1755         for (i = remote_created; i < Net_create_loc; i++) {
1756                 mprintf((0, "WARNING: I Created more powerups than player %d, deleting.\n", pnum));
1757                 Objects[Net_create_objnums[i]].flags |= OF_SHOULD_BE_DEAD;
1758         }
1759
1760         if (buf[0] == MULTI_PLAYER_EXPLODE)
1761         {
1762                 explode_badass_player(objp);
1763
1764                 objp->flags &= ~OF_SHOULD_BE_DEAD;              //don't really kill player
1765                 multi_make_player_ghost(pnum);
1766         }
1767         else
1768         {
1769                 create_player_appearance_effect(objp);
1770         }
1771
1772         Players[pnum].flags &= ~(PLAYER_FLAGS_CLOAKED | PLAYER_FLAGS_INVULNERABLE | PLAYER_FLAGS_FLAG);
1773         Players[pnum].cloak_time = 0;
1774 }
1775
1776 void
1777 multi_do_kill(char *buf)
1778 {
1779         int killer, killed;
1780         int count = 1;
1781         int pnum;
1782
1783         pnum = (int)(buf[count]);
1784         if ((pnum < 0) || (pnum >= N_players))
1785         {
1786                 Int3(); // Invalid player number killed
1787                 return;
1788         }
1789         killed = Players[pnum].objnum;
1790         count += 1;
1791
1792         killer = GET_INTEL_SHORT(buf + count);
1793         if (killer > 0)
1794                 killer = objnum_remote_to_local(killer, (sbyte)buf[count+2]);
1795
1796 #ifdef SHAREWARE
1797         if ((Objects[killed].type != OBJ_PLAYER) && (Objects[killed].type != OBJ_GHOST))
1798         {
1799                 Int3();
1800                 mprintf( (1, "SOFT INT3: MULTI.C Non-player object %d of type %d killed! (JOHN)\n", killed, Objects[killed].type ));
1801                 return;
1802         }
1803 #endif
1804
1805         multi_compute_kill(killer, killed);
1806
1807 }
1808
1809
1810 //      Changed by MK on 10/20/94 to send NULL as object to net_destroy_controlcen if it got -1
1811 // which means not a controlcen object, but contained in another object
1812 void multi_do_controlcen_destroy(char *buf)
1813 {
1814         sbyte who;
1815         short objnum;
1816
1817         objnum = GET_INTEL_SHORT(buf + 1);
1818         who = buf[3];
1819
1820         if (Control_center_destroyed != 1)
1821         {
1822                 if ((who < N_players) && (who != Player_num)) {
1823                         HUD_init_message("%s %s", Players[who].callsign, TXT_HAS_DEST_CONTROL);
1824                 }
1825                 else if (who == Player_num)
1826                         HUD_init_message(TXT_YOU_DEST_CONTROL);
1827                 else
1828                         HUD_init_message(TXT_CONTROL_DESTROYED);
1829
1830                 if (objnum != -1)
1831                         net_destroy_controlcen(Objects+objnum);
1832                 else
1833                         net_destroy_controlcen(NULL);
1834         }
1835 }
1836
1837 void
1838 multi_do_escape(char *buf)
1839 {
1840         int objnum;
1841
1842         objnum = Players[(int)buf[1]].objnum;
1843
1844         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1845         digi_kill_sound_linked_to_object (objnum);
1846
1847         if (buf[2] == 0)
1848         {
1849                 HUD_init_message("%s %s", Players[(int)buf[1]].callsign, TXT_HAS_ESCAPED);
1850                 if (Game_mode & GM_NETWORK)
1851                         Players[(int)buf[1]].connected = CONNECT_ESCAPE_TUNNEL;
1852                 if (!multi_goto_secret)
1853                         multi_goto_secret = 2;
1854         }
1855         else if (buf[2] == 1)
1856         {
1857                 HUD_init_message("%s %s", Players[(int)buf[1]].callsign, TXT_HAS_FOUND_SECRET);
1858                 if (Game_mode & GM_NETWORK)
1859                         Players[(int)buf[1]].connected = CONNECT_FOUND_SECRET;
1860                 if (!multi_goto_secret)
1861                         multi_goto_secret = 1;
1862         }
1863         create_player_appearance_effect(&Objects[objnum]);
1864         multi_make_player_ghost(buf[1]);
1865 }
1866
1867 void
1868 multi_do_remobj(char *buf)
1869 {
1870         short objnum; // which object to remove
1871         short local_objnum;
1872         sbyte obj_owner; // which remote list is it entered in
1873
1874         objnum = GET_INTEL_SHORT(buf + 1);
1875         obj_owner = buf[3];
1876
1877         Assert(objnum >= 0);
1878
1879         if (objnum < 1)
1880                 return;
1881
1882         local_objnum = objnum_remote_to_local(objnum, obj_owner); // translate to local objnum
1883
1884         //      mprintf((0, "multi_do_remobj: %d owner %d = %d.\n", objnum, obj_owner, local_objnum));
1885
1886         if (local_objnum < 0)
1887         {
1888                 mprintf((1, "multi_do_remobj: Could not remove referenced object.\n"));
1889                 return;
1890         }
1891
1892         if ((Objects[local_objnum].type != OBJ_POWERUP) && (Objects[local_objnum].type != OBJ_HOSTAGE))
1893         {
1894                 mprintf((1, "multi_get_remobj: tried to remove invalid type %d.\n", Objects[local_objnum].type));
1895                 return;
1896         }
1897
1898         if (Network_send_objects && network_objnum_is_past(local_objnum))
1899         {
1900                 mprintf((0, "Resetting object sync due to object removal.\n"));
1901                 Network_send_objnum = -1;
1902         }
1903         if (Objects[local_objnum].type==OBJ_POWERUP)
1904                 if (Game_mode & GM_NETWORK)
1905                 {
1906                         if (PowerupsInMine[Objects[local_objnum].id]>0)
1907                                 PowerupsInMine[Objects[local_objnum].id]--;
1908
1909                         if (multi_powerup_is_4pack (Objects[local_objnum].id))
1910                         {
1911                                 mprintf ((0,"Hey babe! Doing that wacky 4 pack stuff."));
1912
1913                                 if (PowerupsInMine[Objects[local_objnum].id-1]-4<0)
1914                                         PowerupsInMine[Objects[local_objnum].id-1]=0;
1915                                 else
1916                                         PowerupsInMine[Objects[local_objnum].id-1]-=4;
1917                         }
1918
1919                         mprintf ((0,"Decrementing powerups! %d\n",PowerupsInMine[Objects[local_objnum].id]));
1920                 }
1921
1922         Objects[local_objnum].flags |= OF_SHOULD_BE_DEAD; // quick and painless
1923
1924 }
1925
1926 void
1927 multi_do_quit(char *buf)
1928 {
1929
1930         if (Game_mode & GM_NETWORK)
1931         {
1932                 int i, n = 0;
1933
1934                 digi_play_sample( SOUND_HUD_MESSAGE, F1_0 );
1935
1936                 HUD_init_message( "%s %s", Players[(int)buf[1]].callsign, TXT_HAS_LEFT_THE_GAME);
1937
1938                 network_disconnect_player(buf[1]);
1939
1940                 if (multi_in_menu)
1941                         return;
1942
1943                 for (i = 0; i < N_players; i++)
1944                         if (Players[i].connected) n++;
1945                 if (n == 1)
1946                 {
1947                         nm_messagebox(NULL, 1, TXT_OK, TXT_YOU_ARE_ONLY);
1948                 }
1949         }
1950
1951         if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
1952         {
1953                 Function_mode = FMODE_MENU;
1954                 multi_quit_game = 1;
1955                 multi_leave_menu = 1;
1956                 nm_messagebox(NULL, 1, TXT_OK, TXT_OPPONENT_LEFT);
1957                 Function_mode = FMODE_GAME;
1958                 multi_reset_stuff();
1959         }
1960         return;
1961 }
1962
1963 void
1964 multi_do_cloak(char *buf)
1965 {
1966         int pnum;
1967
1968         pnum = (int)(buf[1]);
1969
1970         Assert(pnum < N_players);
1971
1972         mprintf((0, "Cloaking player %d\n", pnum));
1973
1974         Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
1975         Players[pnum].cloak_time = GameTime;
1976         ai_do_cloak_stuff();
1977
1978 #ifndef SHAREWARE
1979         if (Game_mode & GM_MULTI_ROBOTS)
1980                 multi_strip_robots(pnum);
1981 #endif
1982
1983         if (Newdemo_state == ND_STATE_RECORDING)
1984                 newdemo_record_multi_cloak(pnum);
1985 }
1986
1987 void
1988 multi_do_decloak(char *buf)
1989 {
1990         int pnum;
1991
1992         pnum = (int)(buf[1]);
1993
1994         if (Newdemo_state == ND_STATE_RECORDING)
1995                 newdemo_record_multi_decloak(pnum);
1996
1997 }
1998
1999 void
2000 multi_do_door_open(char *buf)
2001 {
2002         int segnum;
2003         sbyte side;
2004         segment *seg;
2005         wall *w;
2006         ubyte flag;
2007
2008         segnum = GET_INTEL_SHORT(buf + 1);
2009         side = buf[3];
2010         flag= buf[4];
2011
2012         //      mprintf((0, "Opening door on side %d of segment # %d.\n", side, segnum));
2013
2014         if ((segnum < 0) || (segnum > Highest_segment_index) || (side < 0) || (side > 5))
2015         {
2016                 Int3();
2017                 return;
2018         }
2019
2020         seg = &Segments[segnum];
2021
2022         if (seg->sides[side].wall_num == -1) {  //Opening door on illegal wall
2023                 Int3();
2024                 return;
2025         }
2026
2027         w = &Walls[seg->sides[side].wall_num];
2028
2029         if (w->type == WALL_BLASTABLE)
2030         {
2031                 if (!(w->flags & WALL_BLASTED))
2032                 {
2033                         mprintf((0, "Blasting wall by remote command.\n"));
2034                         wall_destroy(seg, side);
2035                 }
2036                 return;
2037         }
2038         else if (w->state != WALL_DOOR_OPENING)
2039         {
2040                 wall_open_door(seg, side);
2041                 w->flags=flag;
2042         }
2043         else
2044                 w->flags=flag;
2045
2046         //      else
2047         //              mprintf((0, "Door already opening!\n"));
2048 }
2049
2050 void
2051 multi_do_create_explosion(char *buf)
2052 {
2053         int pnum;
2054         int count = 1;
2055
2056         pnum = buf[count++];
2057
2058         //      mprintf((0, "Creating small fireball.\n"));
2059         create_small_fireball_on_object(&Objects[Players[pnum].objnum], F1_0, 1);
2060 }
2061
2062 void
2063 multi_do_controlcen_fire(char *buf)
2064 {
2065         vms_vector to_target;
2066         char gun_num;
2067         short objnum;
2068         int count = 1;
2069
2070         memcpy(&to_target, buf+count, 12);          count += 12;
2071 #ifdef WORDS_BIGENDIAN  // swap the vector to_target
2072         to_target.x = (fix)INTEL_INT((int)to_target.x);
2073         to_target.y = (fix)INTEL_INT((int)to_target.y);
2074         to_target.z = (fix)INTEL_INT((int)to_target.z);
2075 #endif
2076         gun_num = buf[count];                       count += 1;
2077         objnum = GET_INTEL_SHORT(buf + count);      count += 2;
2078
2079         Laser_create_new_easy(&to_target, &Gun_pos[(int)gun_num], objnum, CONTROLCEN_WEAPON_NUM, 1);
2080 }
2081
2082 void
2083 multi_do_create_powerup(char *buf)
2084 {
2085         short segnum;
2086         short objnum;
2087         int my_objnum;
2088         char pnum;
2089         int count = 1;
2090         vms_vector new_pos;
2091         char powerup_type;
2092
2093         if (Endlevel_sequence || Control_center_destroyed)
2094                 return;
2095
2096         pnum = buf[count++];
2097         powerup_type = buf[count++];
2098         segnum = GET_INTEL_SHORT(buf + count); count += 2;
2099         objnum = GET_INTEL_SHORT(buf + count); count += 2;
2100
2101         if ((segnum < 0) || (segnum > Highest_segment_index)) {
2102                 Int3();
2103                 return;
2104         }
2105
2106         memcpy(&new_pos, buf+count, sizeof(vms_vector)); count+=sizeof(vms_vector);
2107 #ifdef WORDS_BIGENDIAN
2108         new_pos.x = (fix)SWAPINT((int)new_pos.x);
2109         new_pos.y = (fix)SWAPINT((int)new_pos.y);
2110         new_pos.z = (fix)SWAPINT((int)new_pos.z);
2111 #endif
2112
2113         Net_create_loc = 0;
2114         my_objnum = call_object_create_egg(&Objects[Players[(int)pnum].objnum], 1, OBJ_POWERUP, powerup_type);
2115
2116         if (my_objnum < 0) {
2117                 mprintf((0, "Could not create new powerup!\n"));
2118                 return;
2119         }
2120
2121         if (Network_send_objects && network_objnum_is_past(my_objnum))
2122         {
2123                 mprintf((0, "Resetting object sync due to powerup creation.\n"));
2124                 Network_send_objnum = -1;
2125         }
2126
2127         Objects[my_objnum].pos = new_pos;
2128
2129         vm_vec_zero(&Objects[my_objnum].mtype.phys_info.velocity);
2130
2131         obj_relink(my_objnum, segnum);
2132
2133         map_objnum_local_to_remote(my_objnum, objnum, pnum);
2134
2135         object_create_explosion(segnum, &new_pos, i2f(5), VCLIP_POWERUP_DISAPPEARANCE);
2136         mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));
2137
2138         if (Game_mode & GM_NETWORK)
2139                 PowerupsInMine[(int)powerup_type]++;
2140 }
2141
2142 void
2143 multi_do_play_sound(char *buf)
2144 {
2145         int pnum = (int)(buf[1]);
2146         int sound_num = (int)(buf[2]);
2147         fix volume = (int)(buf[3]) << 12;
2148
2149         if (!Players[pnum].connected)
2150                 return;
2151
2152         Assert(Players[pnum].objnum >= 0);
2153         Assert(Players[pnum].objnum <= Highest_object_index);
2154
2155         digi_link_sound_to_object( sound_num, Players[pnum].objnum, 0, volume);
2156 }
2157
2158 void
2159 multi_do_score(char *buf)
2160 {
2161         int pnum = (int)(buf[1]);
2162
2163         if ((pnum < 0) || (pnum >= N_players))
2164         {
2165                 Int3(); // Non-terminal, see rob
2166                 return;
2167         }
2168
2169         if (Newdemo_state == ND_STATE_RECORDING) {
2170                 int score;
2171                 score = GET_INTEL_INT(buf + 2);
2172                 newdemo_record_multi_score(pnum, score);
2173         }
2174
2175         Players[pnum].score = GET_INTEL_INT(buf + 2);
2176
2177         multi_sort_kill_list();
2178 }
2179
2180 void
2181 multi_do_trigger(char *buf)
2182 {
2183         int pnum = (int)(buf[1]);
2184         int trigger = (int)(buf[2]);
2185
2186         mprintf ((0,"MULTI doing trigger!\n"));
2187
2188         if ((pnum < 0) || (pnum >= N_players) || (pnum == Player_num))
2189         {
2190                 Int3(); // Got trigger from illegal playernum
2191                 return;
2192         }
2193         if ((trigger < 0) || (trigger >= Num_triggers))
2194         {
2195                 Int3(); // Illegal trigger number in multiplayer
2196                 return;
2197         }
2198         check_trigger_sub(trigger, pnum,0);
2199 }
2200
2201 void multi_do_drop_marker (char *buf)
2202 {
2203         int i;
2204         int pnum=(int)(buf[1]);
2205         int mesnum=(int)(buf[2]);
2206         vms_vector position;
2207
2208         if (pnum==Player_num)  // my marker? don't set it down cuz it might screw up the orientation
2209                 return;
2210
2211         position.x = GET_INTEL_INT(buf + 3);
2212         position.y = GET_INTEL_INT(buf + 7);
2213         position.z = GET_INTEL_INT(buf + 11);
2214
2215         for (i=0;i<40;i++)
2216                 MarkerMessage[(pnum*2)+mesnum][i]=buf[15+i];
2217
2218         MarkerPoint[(pnum*2)+mesnum]=position;
2219
2220         if (MarkerObject[(pnum*2)+mesnum] !=-1 && Objects[MarkerObject[(pnum*2)+mesnum]].type!=OBJ_NONE && MarkerObject[(pnum*2)+mesnum] !=0)
2221                 obj_delete(MarkerObject[(pnum*2)+mesnum]);
2222
2223         MarkerObject[(pnum*2)+mesnum] = drop_marker_object(&position,Objects[Players[Player_num].objnum].segnum,&Objects[Players[Player_num].objnum].orient,(pnum*2)+mesnum);
2224         strcpy (MarkerOwner[(pnum*2)+mesnum],Players[pnum].callsign);
2225         mprintf ((0,"Dropped player %d message: %s\n",pnum,MarkerMessage[(pnum*2)+mesnum]));
2226 }
2227
2228
2229 void multi_do_hostage_door_status(char *buf)
2230 {
2231         // Update hit point status of a door
2232
2233         int count = 1;
2234         int wallnum;
2235         fix hps;
2236
2237         wallnum = GET_INTEL_SHORT(buf + count);     count += 2;
2238         hps = GET_INTEL_INT(buf + count);           count += 4;
2239
2240         if ((wallnum < 0) || (wallnum > Num_walls) || (hps < 0) || (Walls[wallnum].type != WALL_BLASTABLE))
2241         {
2242                 Int3(); // Non-terminal, see Rob
2243                 return;
2244         }
2245
2246         //      mprintf((0, "Damaging wall number %d to %f points.\n", wallnum, f2fl(hps)));
2247
2248         if (hps < Walls[wallnum].hps)
2249                 wall_damage(&Segments[Walls[wallnum].segnum], Walls[wallnum].sidenum, Walls[wallnum].hps - hps);
2250 }
2251
2252 void multi_do_save_game(char *buf)
2253 {
2254         int count = 1;
2255         ubyte slot;
2256         uint id;
2257         char desc[25];
2258
2259         slot = *(ubyte *)(buf+count);           count += 1;
2260         id = GET_INTEL_INT(buf + count);        count += 4;
2261         memcpy( desc, &buf[count], 20 );        count += 20;
2262
2263         multi_save_game( slot, id, desc );
2264 }
2265
2266 void multi_do_restore_game(char *buf)
2267 {
2268         int count = 1;
2269         ubyte slot;
2270         uint id;
2271
2272         slot = *(ubyte *)(buf+count);           count += 1;
2273         id = GET_INTEL_INT(buf + count);        count += 4;
2274
2275         multi_restore_game( slot, id );
2276 }
2277
2278
2279 void multi_do_req_player(char *buf)
2280 {
2281         netplayer_stats ps;
2282         ubyte player_n;
2283         // Send my netplayer_stats to everyone!
2284         player_n = *(ubyte *)(buf+1);
2285         if ( (player_n == Player_num) || (player_n == 255)  )   {
2286                 extract_netplayer_stats( &ps, &Players[Player_num] );
2287                 ps.Player_num = Player_num;
2288                 ps.message_type = MULTI_SEND_PLAYER;            // SET
2289                 multi_send_data((char*)&ps, sizeof(netplayer_stats), 0);
2290         }
2291 }
2292
2293 void multi_do_send_player(char *buf)
2294 {
2295         // Got a player packet from someone!!!
2296         netplayer_stats * p;
2297         p = (netplayer_stats *)buf;
2298
2299         Assert( p->Player_num <= N_players );
2300
2301         mprintf(( 0, "Got netplayer_stats for player %d (I'm %d)\n", p->Player_num, Player_num ));
2302         mprintf(( 0, "Their shields are: %d\n", f2i(p->shields) ));
2303
2304         use_netplayer_stats( &Players[p->Player_num], p );
2305 }
2306
2307 void
2308 multi_reset_stuff(void)
2309 {
2310         // A generic, emergency function to solve problems that crop up
2311         // when a player exits quick-out from the game because of a
2312         // serial connection loss.  Fixes several weird bugs!
2313
2314         dead_player_end();
2315
2316         Players[Player_num].homing_object_dist = -F1_0; // Turn off homing sound.
2317
2318         Dead_player_camera = 0;
2319         Endlevel_sequence = 0;
2320         reset_rear_view();
2321 }
2322
2323 void
2324 multi_reset_player_object(object *objp)
2325 {
2326         int i;
2327
2328         //Init physics for a non-console player
2329
2330         Assert(objp >= Objects);
2331         Assert(objp <= Objects+Highest_object_index);
2332         Assert((objp->type == OBJ_PLAYER) || (objp->type == OBJ_GHOST));
2333
2334         vm_vec_zero(&objp->mtype.phys_info.velocity);
2335         vm_vec_zero(&objp->mtype.phys_info.thrust);
2336         vm_vec_zero(&objp->mtype.phys_info.rotvel);
2337         vm_vec_zero(&objp->mtype.phys_info.rotthrust);
2338         objp->mtype.phys_info.brakes = objp->mtype.phys_info.turnroll = 0;
2339         objp->mtype.phys_info.mass = Player_ship->mass;
2340         objp->mtype.phys_info.drag = Player_ship->drag;
2341         //      objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE | PF_USES_THRUST);
2342         objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE);
2343
2344         //Init render info
2345
2346         objp->render_type = RT_POLYOBJ;
2347         objp->rtype.pobj_info.model_num = Player_ship->model_num;               //what model is this?
2348         objp->rtype.pobj_info.subobj_flags = 0;         //zero the flags
2349         for (i=0;i<MAX_SUBMODELS;i++)
2350                 vm_angvec_zero(&objp->rtype.pobj_info.anim_angles[i]);
2351
2352         //reset textures for this, if not player 0
2353
2354         multi_reset_object_texture (objp);
2355
2356         // Clear misc
2357
2358         objp->flags = 0;
2359
2360         if (objp->type == OBJ_GHOST)
2361                 objp->render_type = RT_NONE;
2362
2363 }
2364
2365 void multi_reset_object_texture (object *objp)
2366 {
2367         int id,i;
2368
2369         if (Game_mode & GM_TEAM)
2370                 id = get_team(objp->id);
2371         else
2372                 id = objp->id;
2373
2374         if (id == 0)
2375                 objp->rtype.pobj_info.alt_textures=0;
2376         else {
2377                 Assert(N_PLAYER_SHIP_TEXTURES == Polygon_models[objp->rtype.pobj_info.model_num].n_textures);
2378
2379                 for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
2380                         multi_player_textures[id-1][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[objp->rtype.pobj_info.model_num].first_texture+i]];
2381
2382                 multi_player_textures[id-1][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2]];
2383                 multi_player_textures[id-1][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2+1]];
2384
2385                 objp->rtype.pobj_info.alt_textures = id;
2386         }
2387 }
2388
2389
2390
2391
2392 #ifdef NETPROFILING
2393 extern int TTRecv[];
2394 extern FILE *RecieveLogFile;
2395 #endif
2396
2397 void
2398 multi_process_bigdata(char *buf, int len)
2399 {
2400         // Takes a bunch of messages, check them for validity,
2401         // and pass them to multi_process_data.
2402
2403         int type, sub_len, bytes_processed = 0;
2404
2405         while( bytes_processed < len )  {
2406                 type = buf[bytes_processed];
2407
2408                 if ( (type<0) || (type>MULTI_MAX_TYPE)) {
2409                         mprintf( (1, "multi_process_bigdata: Invalid packet type %d!\n", type ));
2410                         return;
2411                 }
2412                 sub_len = message_length[type];
2413
2414                 Assert(sub_len > 0);
2415
2416                 if ( (bytes_processed+sub_len) > len )  {
2417                         mprintf( (1, "multi_process_bigdata: packet type %d too short (%d>%d)!\n", type, (bytes_processed+sub_len), len ));
2418                         Int3();
2419                         return;
2420                 }
2421
2422                 multi_process_data(&buf[bytes_processed], sub_len);
2423                 bytes_processed += sub_len;
2424         }
2425 }
2426
2427 //
2428 // Part 2 : Functions that send communication messages to inform the other
2429 //          players of something we did.
2430 //
2431
2432 void
2433 multi_send_fire(void)
2434 {
2435         if (!Network_laser_fired)
2436                 return;
2437
2438         multibuf[0] = (char)MULTI_FIRE;
2439         multibuf[1] = (char)Player_num;
2440         multibuf[2] = (char)Network_laser_gun;
2441         multibuf[3] = (char)Network_laser_level;
2442         multibuf[4] = (char)Network_laser_flags;
2443         multibuf[5] = (char)Network_laser_fired;
2444
2445         PUT_INTEL_SHORT(multibuf+6, Network_laser_track);
2446
2447         multi_send_data(multibuf, 8, 0);
2448
2449         Network_laser_fired = 0;
2450 }
2451
2452 void
2453 multi_send_destroy_controlcen(int objnum, int player)
2454 {
2455         if (player == Player_num)
2456                 HUD_init_message(TXT_YOU_DEST_CONTROL);
2457         else if ((player > 0) && (player < N_players))
2458                 HUD_init_message("%s %s", Players[player].callsign, TXT_HAS_DEST_CONTROL);
2459         else
2460                 HUD_init_message(TXT_CONTROL_DESTROYED);
2461
2462         multibuf[0] = (char)MULTI_CONTROLCEN;
2463         PUT_INTEL_SHORT(multibuf+1, objnum);
2464         multibuf[3] = player;
2465         multi_send_data(multibuf, 4, 2);
2466 }
2467
2468 void multi_send_drop_marker (int player,vms_vector position,char messagenum,char text[])
2469 {
2470         int i;
2471
2472         if (player<N_players)
2473         {
2474                 mprintf ((0,"Sending MARKER drop!\n"));
2475                 multibuf[0]=(char)MULTI_MARKER;
2476                 multibuf[1]=(char)player;
2477                 multibuf[2]=messagenum;
2478                 PUT_INTEL_INT(multibuf+3, position.x);
2479                 PUT_INTEL_INT(multibuf+7, position.y);
2480                 PUT_INTEL_INT(multibuf+11, position.z);
2481                 for (i=0;i<40;i++)
2482                         multibuf[15+i]=text[i];
2483         }
2484         multi_send_data(multibuf, 55, 1);
2485 }
2486
2487 void
2488 multi_send_endlevel_start(int secret)
2489 {
2490         multibuf[0] = (char)MULTI_ENDLEVEL_START;
2491         multibuf[1] = Player_num;
2492         multibuf[2] = (char)secret;
2493
2494         if ((secret) && !multi_goto_secret)
2495                 multi_goto_secret = 1;
2496         else if (!multi_goto_secret)
2497                 multi_goto_secret = 2;
2498
2499         multi_send_data(multibuf, 3, 1);
2500         if (Game_mode & GM_NETWORK)
2501         {
2502                 Players[Player_num].connected = 5;
2503                 network_send_endlevel_packet();
2504         }
2505 }
2506
2507 void
2508 multi_send_player_explode(char type)
2509 {
2510         int count = 0;
2511         int i;
2512
2513         Assert( (type == MULTI_PLAYER_DROP) || (type == MULTI_PLAYER_EXPLODE) );
2514
2515         multi_send_position(Players[Player_num].objnum);
2516
2517         if (Network_send_objects)
2518         {
2519                 mprintf((0, "Resetting object sync due to player explosion.\n"));
2520                 Network_send_objnum = -1;
2521         }
2522
2523         multibuf[count++] = type;
2524         multibuf[count++] = Player_num;
2525
2526         PUT_INTEL_SHORT(multibuf+count, Players[Player_num].primary_weapon_flags);
2527         count += 2;
2528         PUT_INTEL_SHORT(multibuf+count, Players[Player_num].secondary_weapon_flags);
2529         count += 2;
2530         multibuf[count++] = (char)Players[Player_num].laser_level;
2531
2532         multibuf[count++] = (char)Players[Player_num].secondary_ammo[HOMING_INDEX];
2533         multibuf[count++] = (char)Players[Player_num].secondary_ammo[CONCUSSION_INDEX];
2534         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_INDEX];
2535         multibuf[count++] = (char)Players[Player_num].secondary_ammo[MEGA_INDEX];
2536         multibuf[count++] = (char)Players[Player_num].secondary_ammo[PROXIMITY_INDEX];
2537
2538         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE1_INDEX];
2539         multibuf[count++] = (char)Players[Player_num].secondary_ammo[GUIDED_INDEX];
2540         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_MINE_INDEX];
2541         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE4_INDEX];
2542         multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE5_INDEX];
2543
2544         PUT_INTEL_SHORT(multibuf+count, Players[Player_num].primary_ammo[VULCAN_INDEX] );
2545         count += 2;
2546         PUT_INTEL_SHORT(multibuf+count, Players[Player_num].primary_ammo[GAUSS_INDEX] );
2547         count += 2;
2548         PUT_INTEL_INT(multibuf+count, Players[Player_num].flags );
2549         count += 4;
2550
2551         multibuf[count++] = Net_create_loc;
2552
2553         Assert(Net_create_loc <= MAX_NET_CREATE_OBJECTS);
2554
2555         memset(multibuf+count, -1, MAX_NET_CREATE_OBJECTS*sizeof(short));
2556
2557         mprintf((0, "Created %d explosion objects.\n", Net_create_loc));
2558
2559         for (i = 0; i < Net_create_loc; i++)
2560         {
2561                 if (Net_create_objnums[i] <= 0) {
2562                         Int3(); // Illegal value in created egg object numbers
2563                         count +=2;
2564                         continue;
2565                 }
2566
2567                 PUT_INTEL_SHORT(multibuf+count, Net_create_objnums[i]); count += 2;
2568
2569                 // We created these objs so our local number = the network number
2570                 map_objnum_local_to_local((short)Net_create_objnums[i]);
2571         }
2572
2573         Net_create_loc = 0;
2574
2575         //      mprintf((1, "explode message size = %d, max = %d.\n", count, message_length[MULTI_PLAYER_EXPLODE]));
2576
2577         if (count > message_length[MULTI_PLAYER_EXPLODE])
2578         {
2579                 Int3(); // See Rob
2580         }
2581
2582         multi_send_data(multibuf, message_length[MULTI_PLAYER_EXPLODE], 2);
2583         if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
2584                 multi_send_decloak();
2585         if (Game_mode & GM_MULTI_ROBOTS)
2586                 multi_strip_robots(Player_num);
2587 }
2588
2589 extern ubyte Secondary_weapon_to_powerup[];
2590 extern ubyte Primary_weapon_to_powerup[];
2591
2592 // put a lid on how many objects will be spewed by an exploding player
2593 // to prevent rampant powerups in netgames
2594
2595 void multi_cap_objects ()
2596 {
2597         char type,flagtype;
2598         int index;
2599
2600         if (!(Game_mode & GM_NETWORK))
2601                 return;
2602
2603         for (index=0;index<MAX_PRIMARY_WEAPONS;index++)
2604         {
2605                 type=Primary_weapon_to_powerup[index];
2606                 if (PowerupsInMine[(int)type]>=MaxPowerupsAllowed[(int)type])
2607                         if(Players[Player_num].primary_weapon_flags & (1 << index))
2608                         {
2609                                 mprintf ((0,"PIM=%d MPA=%d\n",PowerupsInMine[(int)type],MaxPowerupsAllowed[(int)type]));
2610                                 mprintf ((0,"Killing a primary cuz there's too many! (%d)\n",(int)type));
2611                                 Players[Player_num].primary_weapon_flags&=(~(1 << index));
2612                         }
2613         }
2614
2615
2616         // Don't do the adjustment stuff for Hoard mode
2617         if (!(Game_mode & GM_HOARD))
2618                 Players[Player_num].secondary_ammo[2]/=4;
2619
2620         Players[Player_num].secondary_ammo[7]/=4;
2621
2622         for (index=0;index<MAX_SECONDARY_WEAPONS;index++)
2623         {
2624                 if ((Game_mode & GM_HOARD) && index==PROXIMITY_INDEX)
2625                         continue;
2626
2627                 type=Secondary_weapon_to_powerup[index];
2628
2629                 if ((Players[Player_num].secondary_ammo[index]+PowerupsInMine[(int)type])>MaxPowerupsAllowed[(int)type])
2630                 {
2631                         if (MaxPowerupsAllowed[(int)type]-PowerupsInMine[(int)type]<0)
2632                                 Players[Player_num].secondary_ammo[index]=0;
2633                         else
2634                                 Players[Player_num].secondary_ammo[index]=(MaxPowerupsAllowed[(int)type]-PowerupsInMine[(int)type]);
2635
2636                         mprintf ((0,"Hey! I killed secondary type %d because PIM=%d MPA=%d\n",(int)type,PowerupsInMine[(int)type],MaxPowerupsAllowed[(int)type]));
2637                 }
2638         }
2639
2640         if (!(Game_mode & GM_HOARD))
2641                 Players[Player_num].secondary_ammo[2]*=4;
2642         Players[Player_num].secondary_ammo[7]*=4;
2643
2644         if (Players[Player_num].laser_level > MAX_LASER_LEVEL)
2645                 if (PowerupsInMine[POW_SUPER_LASER]+1 > MaxPowerupsAllowed[POW_SUPER_LASER])
2646                         Players[Player_num].laser_level=0;
2647
2648         if (Players[Player_num].flags & PLAYER_FLAGS_QUAD_LASERS)
2649                 if (PowerupsInMine[POW_QUAD_FIRE]+1 > MaxPowerupsAllowed[POW_QUAD_FIRE])
2650                         Players[Player_num].flags&=(~PLAYER_FLAGS_QUAD_LASERS);
2651
2652         if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
2653                 if (PowerupsInMine[POW_CLOAK]+1 > MaxPowerupsAllowed[POW_CLOAK])
2654                         Players[Player_num].flags&=(~PLAYER_FLAGS_CLOAKED);
2655
2656         if (Players[Player_num].flags & PLAYER_FLAGS_MAP_ALL)
2657                 if (PowerupsInMine[POW_FULL_MAP]+1 > MaxPowerupsAllowed[POW_FULL_MAP])
2658                         Players[Player_num].flags&=(~PLAYER_FLAGS_MAP_ALL);
2659
2660         if (Players[Player_num].flags & PLAYER_FLAGS_AFTERBURNER)
2661                 if (PowerupsInMine[POW_AFTERBURNER]+1 > MaxPowerupsAllowed[POW_AFTERBURNER])
2662                         Players[Player_num].flags&=(~PLAYER_FLAGS_AFTERBURNER);
2663
2664         if (Players[Player_num].flags & PLAYER_FLAGS_AMMO_RACK)
2665                 if (PowerupsInMine[POW_AMMO_RACK]+1 > MaxPowerupsAllowed[POW_AMMO_RACK])
2666                         Players[Player_num].flags&=(~PLAYER_FLAGS_AMMO_RACK);
2667
2668         if (Players[Player_num].flags & PLAYER_FLAGS_CONVERTER)
2669                 if (PowerupsInMine[POW_CONVERTER]+1 > MaxPowerupsAllowed[POW_CONVERTER])
2670                         Players[Player_num].flags&=(~PLAYER_FLAGS_CONVERTER);
2671
2672         if (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT)
2673                 if (PowerupsInMine[POW_HEADLIGHT]+1 > MaxPowerupsAllowed[POW_HEADLIGHT])
2674                         Players[Player_num].flags&=(~PLAYER_FLAGS_HEADLIGHT);
2675
2676         if (Game_mode & GM_CAPTURE)
2677         {
2678                 if (Players[Player_num].flags & PLAYER_FLAGS_FLAG)
2679                 {
2680                         if (get_team(Player_num)==TEAM_RED)
2681                                 flagtype=POW_FLAG_BLUE;
2682                         else
2683                                 flagtype=POW_FLAG_RED;
2684
2685                         if (PowerupsInMine[(int)flagtype]+1 > MaxPowerupsAllowed[(int)flagtype])
2686                                 Players[Player_num].flags&=(~PLAYER_FLAGS_FLAG);
2687                 }
2688         }
2689
2690 }
2691
2692 // adds players inventory to multi cap
2693
2694 void multi_adjust_cap_for_player (int pnum)
2695 {
2696         char type;
2697
2698         int index;
2699
2700         if (!(Game_mode & GM_NETWORK))
2701                 return;
2702
2703         for (index=0;index<MAX_PRIMARY_WEAPONS;index++)
2704         {
2705                 type=Primary_weapon_to_powerup[index];
2706                 if (Players[pnum].primary_weapon_flags & (1 << index))
2707                     MaxPowerupsAllowed[(int)type]++;
2708         }
2709
2710         for (index=0;index<MAX_SECONDARY_WEAPONS;index++)
2711         {
2712                 type=Secondary_weapon_to_powerup[index];
2713                 MaxPowerupsAllowed[(int)type]+=Players[pnum].secondary_ammo[index];
2714         }
2715
2716         if (Players[pnum].laser_level > MAX_LASER_LEVEL)
2717                 MaxPowerupsAllowed[POW_SUPER_LASER]++;
2718
2719         if (Players[pnum].flags & PLAYER_FLAGS_QUAD_LASERS)
2720                 MaxPowerupsAllowed[POW_QUAD_FIRE]++;
2721
2722         if (Players[pnum].flags & PLAYER_FLAGS_CLOAKED)
2723                 MaxPowerupsAllowed[POW_CLOAK]++;
2724
2725         if (Players[pnum].flags & PLAYER_FLAGS_MAP_ALL)
2726                 MaxPowerupsAllowed[POW_FULL_MAP]++;
2727
2728         if (Players[pnum].flags & PLAYER_FLAGS_AFTERBURNER)
2729                 MaxPowerupsAllowed[POW_AFTERBURNER]++;
2730
2731         if (Players[pnum].flags & PLAYER_FLAGS_AMMO_RACK)
2732                 MaxPowerupsAllowed[POW_AMMO_RACK]++;
2733
2734         if (Players[pnum].flags & PLAYER_FLAGS_CONVERTER)
2735                 MaxPowerupsAllowed[POW_CONVERTER]++;
2736
2737         if (Players[pnum].flags & PLAYER_FLAGS_HEADLIGHT)
2738                 MaxPowerupsAllowed[POW_HEADLIGHT]++;
2739 }
2740
2741 void multi_adjust_remote_cap (int pnum)
2742 {
2743         char type;
2744
2745         int index;
2746
2747         if (!(Game_mode & GM_NETWORK))
2748                 return;
2749
2750         for (index=0;index<MAX_PRIMARY_WEAPONS;index++)
2751         {
2752                 type=Primary_weapon_to_powerup[index];
2753                 if (Players[pnum].primary_weapon_flags & (1 << index))
2754                     PowerupsInMine[(int)type]++;
2755         }
2756
2757         for (index=0;index<MAX_SECONDARY_WEAPONS;index++)
2758         {
2759                 type=Secondary_weapon_to_powerup[index];
2760
2761                 if ((Game_mode & GM_HOARD) && index==2)
2762                         continue;
2763
2764                 if (index==2 || index==7) // PROX or SMARTMINES? Those bastards...
2765                         PowerupsInMine[(int)type]+=(Players[pnum].secondary_ammo[index]/4);
2766                 else
2767                         PowerupsInMine[(int)type]+=Players[pnum].secondary_ammo[index];
2768
2769         }
2770
2771         if (Players[pnum].laser_level > MAX_LASER_LEVEL)
2772                 PowerupsInMine[POW_SUPER_LASER]++;
2773
2774         if (Players[pnum].flags & PLAYER_FLAGS_QUAD_LASERS)
2775                 PowerupsInMine[POW_QUAD_FIRE]++;
2776
2777         if (Players[pnum].flags & PLAYER_FLAGS_CLOAKED)
2778                 PowerupsInMine[POW_CLOAK]++;
2779
2780         if (Players[pnum].flags & PLAYER_FLAGS_MAP_ALL)
2781                 PowerupsInMine[POW_FULL_MAP]++;
2782
2783         if (Players[pnum].flags & PLAYER_FLAGS_AFTERBURNER)
2784                 PowerupsInMine[POW_AFTERBURNER]++;
2785
2786         if (Players[pnum].flags & PLAYER_FLAGS_AMMO_RACK)
2787                 PowerupsInMine[POW_AMMO_RACK]++;
2788
2789         if (Players[pnum].flags & PLAYER_FLAGS_CONVERTER)
2790                 PowerupsInMine[POW_CONVERTER]++;
2791
2792         if (Players[pnum].flags & PLAYER_FLAGS_HEADLIGHT)
2793                 PowerupsInMine[POW_HEADLIGHT]++;
2794
2795 }
2796
2797 void
2798 multi_send_message(void)
2799 {
2800         int loc = 0;
2801         if (Network_message_reciever != -1)
2802         {
2803                 multibuf[loc] = (char)MULTI_MESSAGE;            loc += 1;
2804                 multibuf[loc] = (char)Player_num;                       loc += 1;
2805                 strncpy(multibuf+loc, Network_message, MAX_MESSAGE_LEN); loc += MAX_MESSAGE_LEN;
2806                 multibuf[loc-1] = '\0';
2807                 multi_send_data(multibuf, loc, 0);
2808                 Network_message_reciever = -1;
2809         }
2810 }
2811
2812 void
2813 multi_send_reappear()
2814 {
2815         multibuf[0] = (char)MULTI_REAPPEAR;
2816         PUT_INTEL_SHORT(multibuf+1, Players[Player_num].objnum);
2817
2818         multi_send_data(multibuf, 3, 2);
2819         PKilledFlags[Player_num]=0;
2820 }
2821
2822 void
2823 multi_send_position(int objnum)
2824 {
2825 #ifdef WORDS_BIGENDIAN
2826         shortpos sp;
2827 #endif
2828         int count=0;
2829
2830         if (Game_mode & GM_NETWORK) {
2831                 return;
2832         }
2833
2834         multibuf[count++] = (char)MULTI_POSITION;
2835 #ifndef WORDS_BIGENDIAN
2836         create_shortpos((shortpos *)(multibuf+count), Objects+objnum,0);
2837         count += sizeof(shortpos);
2838 #else
2839         create_shortpos(&sp, Objects+objnum, 1);
2840         memcpy(&(multibuf[count]), (ubyte *)(sp.bytemat), 9);
2841         count += 9;
2842         memcpy(&(multibuf[count]), (ubyte *)&(sp.xo), 14);
2843         count += 14;
2844 #endif
2845
2846         multi_send_data(multibuf, count, 0);
2847 }
2848
2849 void
2850 multi_send_kill(int objnum)
2851 {
2852         // I died, tell the world.
2853
2854         int killer_objnum;
2855         int count = 0;
2856
2857         Assert(Objects[objnum].id == Player_num);
2858         killer_objnum = Players[Player_num].killer_objnum;
2859
2860         multi_compute_kill(killer_objnum, objnum);
2861
2862         multibuf[0] = (char)MULTI_KILL;     count += 1;
2863         multibuf[1] = Player_num;           count += 1;
2864         if (killer_objnum > -1) {
2865                 short s;                // do it with variable since INTEL_SHORT won't work on return val from function.
2866
2867                 s = (short)objnum_local_to_remote(killer_objnum, (sbyte *)&multibuf[count+2]);
2868                 PUT_INTEL_SHORT(multibuf+count, s);
2869         }
2870         else
2871         {
2872                 PUT_INTEL_SHORT(multibuf+count, -1);
2873                 multibuf[count+2] = (char)-1;
2874         }
2875         count += 3;
2876         multi_send_data(multibuf, count, 1);
2877
2878 #ifndef SHAREWARE
2879         if (Game_mode & GM_MULTI_ROBOTS)
2880                 multi_strip_robots(Player_num);
2881 #endif
2882 }
2883
2884 void
2885 multi_send_remobj(int objnum)
2886 {
2887         // Tell the other guy to remove an object from his list
2888
2889         sbyte obj_owner;
2890         short remote_objnum;
2891
2892         if (Objects[objnum].type==OBJ_POWERUP && (Game_mode & GM_NETWORK))
2893     {
2894                 if (PowerupsInMine[Objects[objnum].id] > 0)
2895                 {
2896                         PowerupsInMine[Objects[objnum].id]--;
2897                         if (multi_powerup_is_4pack (Objects[objnum].id))
2898                         {
2899                                 mprintf ((0,"Hey babe! Doing that wacky 4 pack stuff."));
2900
2901                                 if (PowerupsInMine[Objects[objnum].id-1]-4<0)
2902                                         PowerupsInMine[Objects[objnum].id-1]=0;
2903                                 else
2904                                         PowerupsInMine[Objects[objnum].id-1]-=4;
2905                         }
2906                 }
2907
2908         }
2909
2910         multibuf[0] = (char)MULTI_REMOVE_OBJECT;
2911
2912         remote_objnum = objnum_local_to_remote((short)objnum, &obj_owner);
2913
2914         PUT_INTEL_SHORT(multibuf+1, remote_objnum); // Map to network objnums
2915
2916         multibuf[3] = obj_owner;
2917
2918         //      mprintf((0, "multi_send_remobj: %d = %d owner %d.\n", objnum, remote_objnum, obj_owner));
2919
2920         multi_send_data(multibuf, 4, 0);
2921
2922         if (Network_send_objects && network_objnum_is_past(objnum))
2923         {
2924                 mprintf((0, "Resetting object sync due to object removal.\n"));
2925                 Network_send_objnum = -1;
2926         }
2927 }
2928
2929 void
2930 multi_send_quit(int why)
2931 {
2932         // I am quitting the game, tell the other guy the bad news.
2933
2934         Assert (why == MULTI_QUIT);
2935
2936         multibuf[0] = (char)why;
2937         multibuf[1] = Player_num;
2938         multi_send_data(multibuf, 2, 1);
2939
2940 }
2941
2942 void
2943 multi_send_cloak(void)
2944 {
2945         // Broadcast a change in our pflags (made to support cloaking)
2946
2947         multibuf[0] = MULTI_CLOAK;
2948         multibuf[1] = (char)Player_num;
2949
2950         multi_send_data(multibuf, 2, 1);
2951
2952 #ifndef SHAREWARE
2953         if (Game_mode & GM_MULTI_ROBOTS)
2954                 multi_strip_robots(Player_num);
2955 #endif
2956 }
2957
2958 void
2959 multi_send_decloak(void)
2960 {
2961         // Broadcast a change in our pflags (made to support cloaking)
2962
2963         multibuf[0] = MULTI_DECLOAK;
2964         multibuf[1] = (char)Player_num;
2965
2966         multi_send_data(multibuf, 2, 1);
2967 }
2968
2969 void
2970 multi_send_door_open(int segnum, int side,ubyte flag)
2971 {
2972         // When we open a door make sure everyone else opens that door
2973
2974         multibuf[0] = MULTI_DOOR_OPEN;
2975         PUT_INTEL_SHORT(multibuf+1, segnum );
2976         multibuf[3] = (sbyte)side;
2977         multibuf[4] = flag;
2978
2979         multi_send_data(multibuf, 5, 2);
2980 }
2981
2982 extern void network_send_naked_packet (char *,short,int);
2983
2984 void
2985 multi_send_door_open_specific(int pnum,int segnum, int side,ubyte flag)
2986 {
2987         // For sending doors only to a specific person (usually when they're joining)
2988
2989         Assert (Game_mode & GM_NETWORK);
2990         //   Assert (pnum>-1 && pnum<N_players);
2991
2992         multibuf[0] = MULTI_DOOR_OPEN;
2993         PUT_INTEL_SHORT(multibuf+1, segnum);
2994         multibuf[3] = (sbyte)side;
2995         multibuf[4] = flag;
2996
2997         network_send_naked_packet(multibuf, 5, pnum);
2998 }
2999
3000 //
3001 // Part 3 : Functions that change or prepare the game for multiplayer use.
3002 //          Not including functions needed to syncronize or start the
3003 //          particular type of multiplayer game.  Includes preparing the
3004 //                      mines, player structures, etc.
3005
3006 void
3007 multi_send_create_explosion(int pnum)
3008 {
3009         // Send all data needed to create a remote explosion
3010
3011         int count = 0;
3012
3013         multibuf[count] = MULTI_CREATE_EXPLOSION;       count += 1;
3014         multibuf[count] = (sbyte)pnum;                  count += 1;
3015         //                                                                                                      -----------
3016         //                                                                                                      Total size = 2
3017
3018         multi_send_data(multibuf, count, 0);
3019 }
3020
3021 void
3022 multi_send_controlcen_fire(vms_vector *to_goal, int best_gun_num, int objnum)
3023 {
3024 #ifdef WORDS_BIGENDIAN
3025         vms_vector swapped_vec;
3026 #endif
3027         int count = 0;
3028
3029         multibuf[count] = MULTI_CONTROLCEN_FIRE;                count +=  1;
3030 #ifndef WORDS_BIGENDIAN
3031         memcpy(multibuf+count, to_goal, 12);                    count += 12;
3032 #else
3033         swapped_vec.x = (fix)INTEL_INT( (int)to_goal->x );
3034         swapped_vec.y = (fix)INTEL_INT( (int)to_goal->y );
3035         swapped_vec.z = (fix)INTEL_INT( (int)to_goal->z );
3036         memcpy(multibuf+count, &swapped_vec, 12);                               count += 12;
3037 #endif
3038         multibuf[count] = (char)best_gun_num;                   count +=  1;
3039         PUT_INTEL_SHORT(multibuf+count, objnum );     count +=  2;
3040         //                                                                                                                      ------------
3041         //                                                                                                                      Total  = 16
3042         multi_send_data(multibuf, count, 0);
3043 }
3044
3045 void
3046 multi_send_create_powerup(int powerup_type, int segnum, int objnum, vms_vector *pos)
3047 {
3048         // Create a powerup on a remote machine, used for remote
3049         // placement of used powerups like missiles and cloaking
3050         // powerups.
3051
3052 #ifdef WORDS_BIGENDIAN
3053         vms_vector swapped_vec;
3054 #endif
3055         int count = 0;
3056
3057         if (Game_mode & GM_NETWORK)
3058                 PowerupsInMine[powerup_type]++;
3059
3060         multibuf[count] = MULTI_CREATE_POWERUP;         count += 1;
3061         multibuf[count] = Player_num;                                      count += 1;
3062         multibuf[count] = powerup_type;                                 count += 1;
3063         PUT_INTEL_SHORT(multibuf+count, segnum );     count += 2;
3064         PUT_INTEL_SHORT(multibuf+count, objnum );     count += 2;
3065 #ifndef WORDS_BIGENDIAN
3066         memcpy(multibuf+count, pos, sizeof(vms_vector));  count += sizeof(vms_vector);
3067 #else
3068         swapped_vec.x = (fix)INTEL_INT( (int)pos->x );
3069         swapped_vec.y = (fix)INTEL_INT( (int)pos->y );
3070         swapped_vec.z = (fix)INTEL_INT( (int)pos->z );
3071         memcpy(multibuf+count, &swapped_vec, 12);                               count += 12;
3072 #endif
3073         //                                                                                                            -----------
3074         //                                                                                                            Total =  19
3075         multi_send_data(multibuf, count, 2);
3076
3077         if (Network_send_objects && network_objnum_is_past(objnum))
3078         {
3079                 mprintf((0, "Resetting object sync due to powerup creation.\n"));
3080                 Network_send_objnum = -1;
3081         }
3082
3083         mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));
3084         map_objnum_local_to_local(objnum);
3085 }
3086
3087 void
3088 multi_send_play_sound(int sound_num, fix volume)
3089 {
3090         int count = 0;
3091         multibuf[count] = MULTI_PLAY_SOUND;                     count += 1;
3092         multibuf[count] = Player_num;                                   count += 1;
3093         multibuf[count] = (char)sound_num;                      count += 1;
3094         multibuf[count] = (char)(volume >> 12); count += 1;
3095         //                                                                                                         -----------
3096         //                                                                                                         Total = 4
3097         multi_send_data(multibuf, count, 0);
3098 }
3099
3100 void
3101 multi_send_audio_taunt(int taunt_num)
3102 {
3103         return; // Taken out, awaiting sounds..
3104
3105 #if 0
3106         int audio_taunts[4] = {
3107                 SOUND_CONTROL_CENTER_WARNING_SIREN,
3108                 SOUND_HOSTAGE_RESCUED,
3109                 SOUND_REFUEL_STATION_GIVING_FUEL,
3110                 SOUND_BAD_SELECTION
3111         };
3112
3113
3114         Assert(taunt_num >= 0);
3115         Assert(taunt_num < 4);
3116
3117         digi_play_sample( audio_taunts[taunt_num], F1_0 );
3118         multi_send_play_sound(audio_taunts[taunt_num], F1_0);
3119 #endif
3120 }
3121
3122 void
3123 multi_send_score(void)
3124 {
3125         // Send my current score to all other players so it will remain
3126         // synced.
3127         int count = 0;
3128
3129         if (Game_mode & GM_MULTI_COOP) {
3130                 multi_sort_kill_list();
3131                 multibuf[count] = MULTI_SCORE;                  count += 1;
3132                 multibuf[count] = Player_num;                           count += 1;
3133                 PUT_INTEL_INT(multibuf+count, Players[Player_num].score);  count += 4;
3134                 multi_send_data(multibuf, count, 0);
3135         }
3136 }
3137
3138
3139 void
3140 multi_send_save_game(ubyte slot, uint id, char * desc)
3141 {
3142         int count = 0;
3143
3144         multibuf[count] = MULTI_SAVE_GAME;              count += 1;
3145         multibuf[count] = slot;                         count += 1;    // Save slot=0
3146         PUT_INTEL_INT(multibuf+count, id );           count += 4;             // Save id
3147         memcpy( &multibuf[count], desc, 20 ); count += 20;
3148
3149         multi_send_data(multibuf, count, 2);
3150 }
3151
3152 void
3153 multi_send_restore_game(ubyte slot, uint id)
3154 {
3155         int count = 0;
3156
3157         multibuf[count] = MULTI_RESTORE_GAME;   count += 1;
3158         multibuf[count] = slot;                                                 count += 1;             // Save slot=0
3159         PUT_INTEL_INT(multibuf+count, id);         count += 4;             // Save id
3160
3161         multi_send_data(multibuf, count, 2);
3162 }
3163
3164 void
3165 multi_send_netplayer_stats_request(ubyte player_num)
3166 {
3167         int count = 0;
3168
3169         multibuf[count] = MULTI_REQ_PLAYER;     count += 1;
3170         multibuf[count] = player_num;                   count += 1;
3171
3172         multi_send_data(multibuf, count, 0 );
3173 }
3174
3175 void
3176 multi_send_trigger(int triggernum)
3177 {
3178         // Send an even to trigger something in the mine
3179
3180         int count = 0;
3181
3182         multibuf[count] = MULTI_TRIGGER;                                count += 1;
3183         multibuf[count] = Player_num;                                   count += 1;
3184         multibuf[count] = (ubyte)triggernum;            count += 1;
3185
3186         mprintf ((0,"Sending trigger %d\n",triggernum));
3187
3188         multi_send_data(multibuf, count, 1);
3189         //multi_send_data(multibuf, count, 1); // twice?
3190 }
3191
3192 void
3193 multi_send_hostage_door_status(int wallnum)
3194 {
3195         // Tell the other player what the hit point status of a hostage door
3196         // should be
3197
3198         int count = 0;
3199
3200         Assert(Walls[wallnum].type == WALL_BLASTABLE);
3201
3202         multibuf[count] = MULTI_HOSTAGE_DOOR;           count += 1;
3203         PUT_INTEL_SHORT(multibuf+count, wallnum );           count += 2;
3204         PUT_INTEL_INT(multibuf+count, Walls[wallnum].hps );  count += 4;
3205
3206         //      mprintf((0, "Door %d damaged by %f points.\n", wallnum, f2fl(Walls[wallnum].hps)));
3207
3208         multi_send_data(multibuf, count, 0);
3209 }
3210
3211 extern int ConsistencyCount;
3212 extern int Drop_afterburner_blob_flag;
3213 int PhallicLimit=0;
3214 int PhallicMan=-1;
3215
3216 void multi_prep_level(void)
3217 {
3218         // Do any special stuff to the level required for serial games
3219         // before we begin playing in it.
3220
3221         // Player_num MUST be set before calling this procedure.
3222
3223         // This function must be called before checksuming the Object array,
3224         // since the resulting checksum with depend on the value of Player_num
3225         // at the time this is called.
3226
3227         int i,ng=0;
3228         int     cloak_count, inv_count;
3229
3230         Assert(Game_mode & GM_MULTI);
3231
3232         Assert(NumNetPlayerPositions > 0);
3233
3234         PhallicLimit=0;
3235         PhallicMan=-1;
3236         Drop_afterburner_blob_flag=0;
3237         ConsistencyCount=0;
3238
3239         for (i=0;i<MAX_NUM_NET_PLAYERS;i++)
3240                 PKilledFlags[i]=0;
3241
3242         for (i = 0; i < NumNetPlayerPositions; i++)
3243         {
3244                 if (i != Player_num)
3245                         Objects[Players[i].objnum].control_type = CT_REMOTE;
3246                 Objects[Players[i].objnum].movement_type = MT_PHYSICS;
3247                 multi_reset_player_object(&Objects[Players[i].objnum]);
3248                 LastPacketTime[i] = 0;
3249         }
3250
3251 #ifndef SHAREWARE
3252         for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
3253         {
3254                 robot_controlled[i] = -1;
3255                 robot_agitation[i] = 0;
3256                 robot_fired[i] = 0;
3257         }
3258 #endif
3259
3260         Viewer = ConsoleObject = &Objects[Players[Player_num].objnum];
3261
3262         if (!(Game_mode & GM_MULTI_COOP))
3263         {
3264                 multi_delete_extra_objects(); // Removes monsters from level
3265         }
3266
3267         if (Game_mode & GM_MULTI_ROBOTS)
3268         {
3269                 multi_set_robot_ai(); // Set all Robot AI to types we can cope with
3270         }
3271
3272         if (Game_mode & GM_NETWORK)
3273         {
3274                 multi_adjust_cap_for_player(Player_num);
3275                 multi_send_powerup_update();
3276                 ng=1;  // ng means network game
3277         }
3278         ng=1;
3279
3280         inv_count = 0;
3281         cloak_count = 0;
3282         for (i=0; i<=Highest_object_index; i++)
3283         {
3284                 int objnum;
3285
3286                 if ((Objects[i].type == OBJ_HOSTAGE) && !(Game_mode & GM_MULTI_COOP))
3287                 {
3288                         objnum = obj_create(OBJ_POWERUP, POW_SHIELD_BOOST, Objects[i].segnum, &Objects[i].pos, &vmd_identity_matrix, Powerup_info[POW_SHIELD_BOOST].size, CT_POWERUP, MT_PHYSICS, RT_POWERUP);
3289                         obj_delete(i);
3290                         if (objnum != -1)
3291                         {
3292                                 Objects[objnum].rtype.vclip_info.vclip_num = Powerup_info[POW_SHIELD_BOOST].vclip_num;
3293                                 Objects[objnum].rtype.vclip_info.frametime = Vclip[Objects[objnum].rtype.vclip_info.vclip_num].frame_time;
3294                                 Objects[objnum].rtype.vclip_info.framenum = 0;
3295                                 Objects[objnum].mtype.phys_info.drag = 512;     //1024;
3296                                 Objects[objnum].mtype.phys_info.mass = F1_0;
3297                                 vm_vec_zero(&Objects[objnum].mtype.phys_info.velocity);
3298                         }
3299                         continue;
3300                 }
3301
3302                 if (Objects[i].type == OBJ_POWERUP)
3303                 {
3304                         if (Objects[i].id == POW_EXTRA_LIFE)
3305                         {
3306                                 if (ng && !Netgame.DoInvulnerability)
3307                                 {
3308                                         Objects[i].id = POW_SHIELD_BOOST;
3309                                         Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3310                                         Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3311                                 }
3312                                 else
3313                                 {
3314                                         Objects[i].id = POW_INVULNERABILITY;
3315                                         Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3316                                         Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3317                                 }
3318
3319                         }
3320
3321                         if (!(Game_mode & GM_MULTI_COOP))
3322                                 if ((Objects[i].id >= POW_KEY_BLUE) && (Objects[i].id <= POW_KEY_GOLD))
3323                                 {
3324                                         Objects[i].id = POW_SHIELD_BOOST;
3325                                         Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3326                                         Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3327                                 }
3328
3329                         if (Objects[i].id == POW_INVULNERABILITY) {
3330                                 if (inv_count >= 3 || (ng && !Netgame.DoInvulnerability)) {
3331                                         mprintf((0, "Bashing Invulnerability object #%i to shield.\n", i));
3332                                         Objects[i].id = POW_SHIELD_BOOST;
3333                                         Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3334                                         Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3335                                 } else
3336                                         inv_count++;
3337                         }
3338
3339                         if (Objects[i].id == POW_CLOAK) {
3340                                 if (cloak_count >= 3 || (ng && !Netgame.DoCloak)) {
3341                                         mprintf((0, "Bashing Cloak object #%i to shield.\n", i));
3342                                         Objects[i].id = POW_SHIELD_BOOST;
3343                                         Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3344                                         Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3345                                 } else
3346                                         cloak_count++;
3347                         }
3348
3349                         if (Objects[i].id == POW_AFTERBURNER && ng && !Netgame.DoAfterburner)
3350                                 bash_to_shield (i,"afterburner");
3351                         if (Objects[i].id == POW_FUSION_WEAPON && ng &&  !Netgame.DoFusions)
3352                                 bash_to_shield (i,"fusion");
3353                         if (Objects[i].id == POW_PHOENIX_WEAPON && ng && !Netgame.DoPhoenix)
3354                                 bash_to_shield (i,"phoenix");
3355
3356                         if (Objects[i].id == POW_HELIX_WEAPON && ng && !Netgame.DoHelix)
3357                                 bash_to_shield (i,"helix");
3358
3359                         if (Objects[i].id == POW_MEGA_WEAPON && ng && !Netgame.DoMegas)
3360                                 bash_to_shield (i,"mega");
3361
3362                         if (Objects[i].id == POW_SMARTBOMB_WEAPON && ng && !Netgame.DoSmarts)
3363                                 bash_to_shield (i,"smartmissile");
3364
3365                         if (Objects[i].id == POW_GAUSS_WEAPON && ng && !Netgame.DoGauss)
3366                                 bash_to_shield (i,"gauss");
3367
3368                         if (Objects[i].id == POW_VULCAN_WEAPON && ng && !Netgame.DoVulcan)
3369                                 bash_to_shield (i,"vulcan");
3370
3371                         if (Objects[i].id == POW_PLASMA_WEAPON && ng && !Netgame.DoPlasma)
3372                                 bash_to_shield (i,"plasma");
3373
3374                         if (Objects[i].id == POW_OMEGA_WEAPON && ng && !Netgame.DoOmega)
3375                                 bash_to_shield (i,"omega");
3376
3377                         if (Objects[i].id == POW_SUPER_LASER && ng && !Netgame.DoSuperLaser)
3378                                 bash_to_shield (i,"superlaser");
3379
3380                         if (Objects[i].id == POW_PROXIMITY_WEAPON && ng && !Netgame.DoProximity)
3381                                 bash_to_shield (i,"proximity");
3382
3383                         // Special: Make all proximity bombs into shields if in
3384                         // hoard mode because we use the proximity slot in the
3385                         // player struct to signify how many orbs the player has.
3386
3387                         if (Objects[i].id == POW_PROXIMITY_WEAPON && ng && (Game_mode & GM_HOARD))
3388                                 bash_to_shield (i,"proximity");
3389
3390                         if (Objects[i].id==POW_VULCAN_AMMO && ng && (!Netgame.DoVulcan && !Netgame.DoGauss))
3391                                 bash_to_shield(i,"vulcan ammo");
3392
3393                         if (Objects[i].id == POW_SPREADFIRE_WEAPON && ng && !Netgame.DoSpread)
3394                                 bash_to_shield (i,"spread");
3395                         if (Objects[i].id == POW_SMART_MINE && ng && !Netgame.DoSmartMine)
3396                                 bash_to_shield (i,"smartmine");
3397                         if (Objects[i].id == POW_SMISSILE1_1 && ng &&  !Netgame.DoFlash)
3398                                 bash_to_shield (i,"flash");
3399                         if (Objects[i].id == POW_SMISSILE1_4 && ng &&  !Netgame.DoFlash)
3400                                 bash_to_shield (i,"flash");
3401                         if (Objects[i].id == POW_GUIDED_MISSILE_1 && ng &&  !Netgame.DoGuided)
3402                                 bash_to_shield (i,"guided");
3403                         if (Objects[i].id == POW_GUIDED_MISSILE_4 && ng &&  !Netgame.DoGuided)
3404                                 bash_to_shield (i,"guided");
3405                         if (Objects[i].id == POW_EARTHSHAKER_MISSILE && ng &&  !Netgame.DoEarthShaker)
3406                                 bash_to_shield (i,"earth");
3407                         if (Objects[i].id == POW_MERCURY_MISSILE_1 && ng &&  !Netgame.DoMercury)
3408                                 bash_to_shield (i,"Mercury");
3409                         if (Objects[i].id == POW_MERCURY_MISSILE_4 && ng &&  !Netgame.DoMercury)
3410                                 bash_to_shield (i,"Mercury");
3411                         if (Objects[i].id == POW_CONVERTER && ng &&  !Netgame.DoConverter)
3412                                 bash_to_shield (i,"Converter");
3413                         if (Objects[i].id == POW_AMMO_RACK && ng &&  !Netgame.DoAmmoRack)
3414                                 bash_to_shield (i,"Ammo rack");
3415                         if (Objects[i].id == POW_HEADLIGHT && ng &&  !Netgame.DoHeadlight)
3416                                 bash_to_shield (i,"Headlight");
3417                         if (Objects[i].id == POW_LASER && ng &&  !Netgame.DoLaserUpgrade)
3418                                 bash_to_shield (i,"Laser powerup");
3419                         if (Objects[i].id == POW_HOMING_AMMO_1 && ng &&  !Netgame.DoHoming)
3420                                 bash_to_shield (i,"Homing");
3421                         if (Objects[i].id == POW_HOMING_AMMO_4 && ng &&  !Netgame.DoHoming)
3422                                 bash_to_shield (i,"Homing");
3423                         if (Objects[i].id == POW_QUAD_FIRE && ng &&  !Netgame.DoQuadLasers)
3424                                 bash_to_shield (i,"Quad Lasers");
3425                         if (Objects[i].id == POW_FLAG_BLUE && !(Game_mode & GM_CAPTURE))
3426                                 bash_to_shield (i,"Blue flag");
3427                         if (Objects[i].id == POW_FLAG_RED && !(Game_mode & GM_CAPTURE))
3428                                 bash_to_shield (i,"Red flag");
3429                 }
3430         }
3431
3432         if (Game_mode & GM_HOARD)
3433                 init_hoard_data();
3434
3435         if ((Game_mode & GM_CAPTURE) || (Game_mode & GM_HOARD))
3436                 multi_apply_goal_textures();
3437
3438         multi_sort_kill_list();
3439
3440         multi_show_player_list();
3441
3442         ConsoleObject->control_type = CT_FLYING;
3443
3444         reset_player_object();
3445
3446 }
3447
3448 int Goal_blue_segnum,Goal_red_segnum;
3449
3450 void multi_apply_goal_textures()
3451 {
3452         int             i,j,tex;
3453         segment *seg;
3454         segment2        *seg2;
3455
3456         for (i=0; i <= Highest_segment_index; i++)
3457         {
3458                 seg = &Segments[i];
3459                 seg2 = &Segment2s[i];
3460
3461                 if (seg2->special==SEGMENT_IS_GOAL_BLUE)
3462                 {
3463
3464                         Goal_blue_segnum = i;
3465
3466                         if (Game_mode & GM_HOARD)
3467                                 tex=find_goal_texture (TMI_GOAL_HOARD);
3468                         else
3469                                 tex=find_goal_texture (TMI_GOAL_BLUE);
3470
3471                         if (tex>-1)
3472                                 for (j = 0; j < 6; j++) {
3473                                         int v;
3474                                         seg->sides[j].tmap_num=tex;
3475                                         for (v=0;v<4;v++)
3476                                                 seg->sides[j].uvls[v].l = i2f(100);             //max out
3477                                 }
3478
3479                         seg2->static_light = i2f(100);  //make static light bright
3480
3481                 }
3482
3483                 if (seg2->special==SEGMENT_IS_GOAL_RED)
3484                 {
3485                         Goal_red_segnum = i;
3486
3487                         // Make both textures the same if Hoard mode
3488
3489                         if (Game_mode & GM_HOARD)
3490                                 tex=find_goal_texture (TMI_GOAL_HOARD);
3491                         else
3492                                 tex=find_goal_texture (TMI_GOAL_RED);
3493
3494                         if (tex>-1)
3495                                 for (j = 0; j < 6; j++) {
3496                                         int v;
3497                                         seg->sides[j].tmap_num=tex;
3498                                         for (v=0;v<4;v++)
3499                                                 seg->sides[j].uvls[v].l = i2f(1000);            //max out
3500                                 }
3501
3502                         seg2->static_light = i2f(100);  //make static light bright
3503                 }
3504         }
3505 }
3506 int find_goal_texture (ubyte t)
3507 {
3508         int i;
3509
3510         for (i=0;i<NumTextures;i++)
3511                 if (TmapInfo[i].flags & t)
3512                         return i;
3513
3514         Int3(); // Hey, there is no goal texture for this PIG!!!!
3515         // Edit bitmaps.tbl and designate two textures to be RED and BLUE
3516         // goal textures
3517         return (-1);
3518 }
3519
3520
3521 /* DPH: Moved to gameseq.c
3522    void bash_to_shield (int i,char *s)
3523    {
3524    int type=Objects[i].id;
3525
3526    mprintf((0, "Bashing %s object #%i to shield.\n",s, i));
3527
3528    PowerupsInMine[type]=MaxPowerupsAllowed[type]=0;
3529
3530    Objects[i].id = POW_SHIELD_BOOST;
3531    Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3532    Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3533    }
3534 */
3535
3536 void multi_set_robot_ai(void)
3537 {
3538         // Go through the objects array looking for robots and setting
3539         // them to certain supported types of NET AI behavior.
3540
3541         //      int i;
3542         //
3543         //      for (i = 0; i <= Highest_object_index; i++)
3544         //      {
3545         //              if (Objects[i].type == OBJ_ROBOT) {
3546         //                      Objects[i].ai_info.REMOTE_OWNER = -1;
3547         //                      if (Objects[i].ai_info.behavior == AIB_STATION)
3548         //                              Objects[i].ai_info.behavior = AIB_NORMAL;
3549         //              }
3550         //      }
3551 }
3552