1 /* $Id: multi.c,v 1.13 2003-10-04 02:58:23 btb Exp $ */
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.
17 * Multiplayer code shared by serial and network play.
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);
101 // Local macros and prototypes
106 #define vm_angvec_zero(v) (v)->p=(v)->b=(v)->h=0
108 void drop_player_eggs(object *player); // from collide.c
109 void GameLoop(int, int); // From game.c
115 extern vms_vector MarkerPoint[];
116 extern char MarkerMessage[16][40];
117 extern char MarkerOwner[16][40];
118 extern int MarkerObject[];
120 int control_invul_time = 0;
121 int who_killed_controlcen = -1; // -1 = noone
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;
128 char Multi_is_guided=0;
129 char PKilledFlags[MAX_NUM_NET_PLAYERS];
131 int multi_sending_message = 0;
132 int multi_defining_message = 0;
133 int multi_message_index = 0;
135 char multibuf[MAX_MULTI_MESSAGE_LEN+4]; // This is where multiplayer message are built
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
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;
155 int multi_in_menu = 0;
156 int multi_leave_menu = 0;
157 int multi_quit_game = 0;
159 netgame_info Netgame;
160 AllNetPlayers_info NetPlayers;
162 bitmap_index multi_player_textures[MAX_NUM_NET_PLAYERS][N_PLAYER_SHIP_TEXTURES];
164 typedef struct netplayer_stats {
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.
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.
194 int message_length[MULTI_MAX_TYPE+1] = {
200 97+9, // PLAYER_EXPLODE
201 37, // MESSAGE (MAX_MESSAGE_LENGTH = 40)
211 2, // CREATE_EXPLOSION
212 16, // CONTROLCEN_FIRE
214 19, // CREATE_POWERUP
218 28, // ROBOT_POSITION (shortpos_length (23) + 5 = 28)
226 27, // ROBOT_POWERUPS
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
233 12, // MULTI_DROP_WEAPON
235 3+sizeof(shortpos), // MULTI_GUIDED, IF SHORTPOS CHANGES, CHANGE MAC VALUE BELOW
237 26, // MULTI_GUIDED IF SIZE OF SHORTPOS CHANGES, CHANGE THIS VALUE AS WELL!!!!!!
239 11, // MULTI_STOLEN_ITEMS
240 6, // MULTI_WALL_STATUS
241 5, // MULTI_HEARTBEAT
242 9, // MULTI_KILLGOALS
245 2, // MULTI_START_TRIGGER
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
253 12, // MULTI_DROP_FLAG
254 142, // MULTI_ROBOT_CONTROLS
255 2, // MULTI_FINISH_GAME
257 1, // MULTI_MODEM_PING
258 1, // MULTI_MODEM_PING_RETURN
259 3, // MULTI_ORB_BONUS
261 12, // MULTI_DROP_ORB
262 4, // MULTI_PLAY_BY_PLAY
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;
271 // Functions that replace what used to be macros
274 int objnum_remote_to_local(int remote_objnum, int owner)
276 // Map a remote object number from owner to a local object number
280 if ((owner >= N_players) || (owner < -1)) {
282 return(remote_objnum);
286 return(remote_objnum);
288 if ((remote_objnum < 0) || (remote_objnum >= MAX_OBJECTS))
291 result = remote_to_local[owner][remote_objnum];
295 mprintf((1, "Remote object owner %d number %d mapped to -1!\n", owner, remote_objnum));
300 if (object_owner[result] != owner)
302 mprintf((1, "Remote object owner %d number %d doesn't match owner %d.\n", owner, remote_objnum, object_owner[result]));
305 //Assert(object_owner[result] == owner);
310 int objnum_local_to_remote(int local_objnum, sbyte *owner)
312 // Map a local object number to a remote + owner
316 if ((local_objnum < 0) || (local_objnum > Highest_object_index)) {
321 *owner = object_owner[local_objnum];
324 return(local_objnum);
326 if ((*owner >= N_players) || (*owner < -1)) {
332 result = local_to_remote[local_objnum];
334 //mprintf((0, "Local object %d mapped to owner %d objnum %d.\n", local_objnum,
339 Int3(); // See Rob, object has no remote number!
346 map_objnum_local_to_remote(int local_objnum, int remote_objnum, int owner)
348 // Add a mapping from a network remote object number to a local one
350 Assert(local_objnum > -1);
351 Assert(remote_objnum > -1);
353 Assert(owner != Player_num);
354 Assert(local_objnum < MAX_OBJECTS);
355 Assert(remote_objnum < MAX_OBJECTS);
357 object_owner[local_objnum] = owner;
359 remote_to_local[owner][remote_objnum] = local_objnum;
360 local_to_remote[local_objnum] = remote_objnum;
366 map_objnum_local_to_local(int local_objnum)
368 // Add a mapping for our locally created objects
370 Assert(local_objnum > -1);
371 Assert(local_objnum < MAX_OBJECTS);
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;
381 // Part 1 : functions whose main purpose in life is to divert the flow
382 // of execution to either network or serial specific code based
383 // on the curretn Game_mode value.
387 multi_endlevel_score(void)
392 // Show a score list to end of net players
394 // Save connect state and change to new connect state
396 if (Game_mode & GM_NETWORK)
398 old_connect = Players[Player_num].connected;
399 if (Players[Player_num].connected!=3)
400 Players[Player_num].connected = CONNECT_END_MENU;
401 Network_status = NETSTAT_ENDLEVEL;
406 // Do the actual screen we wish to show
408 Function_mode = FMODE_MENU;
410 kmatrix_view(Game_mode & GM_NETWORK);
412 Function_mode = FMODE_GAME;
414 // Restore connect state
416 if (Game_mode & GM_NETWORK)
418 Players[Player_num].connected = old_connect;
422 if (Game_mode & GM_MULTI_COOP)
425 for (i = 0; i < MaxNumNetPlayers; i++)
427 Players[i].flags &= ~(PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_GOLD_KEY);
429 for (i = 0; i < MaxNumNetPlayers; i++)
430 Players[i].flags &= ~(PLAYER_FLAGS_FLAG); // Clear capture flag
434 for (i=0;i<MAX_PLAYERS;i++)
435 Players[i].KillGoalCount=0;
437 for (i=0;i<MAX_POWERUP_TYPES;i++)
439 MaxPowerupsAllowed[i]=0;
448 if ((Game_mode & GM_CAPTURE) && ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)))
451 if (Netgame.team_vector & (1 << pnum))
458 multi_choose_mission(int *anarchy_only)
462 char *m[MAX_MISSIONS];
463 int new_mission_num = 0;
467 n_missions = build_mission_list(1);
469 if (n_missions > 1) {
472 for (i=0;i<n_missions;i++) {
473 m[i] = Mission_list[i].mission_name;
474 if ( !stricmp( m[i], config_last_mission ) )
478 ExtGameStatus=GAMESTAT_START_MULTIPLAYER_MISSION;
479 new_mission_num = newmenu_listbox1(TXT_MULTI_MISSION, n_missions, m, 1, default_mission, NULL );
481 if (new_mission_num == -1)
484 strcpy(config_last_mission, m[new_mission_num] );
486 if (!load_mission(new_mission_num)) {
487 nm_messagebox( NULL, 1, TXT_OK, TXT_MISSION_ERROR);
491 *anarchy_only = Mission_list[new_mission_num].anarchy_only_flag;
493 return(new_mission_num);
496 extern void game_disable_cheats();
503 // Reset variables for a new net game
505 memset(kill_matrix, 0, MAX_NUM_NET_PLAYERS*MAX_NUM_NET_PLAYERS*2); // Clear kill matrix
507 for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
510 Players[i].net_killed_total = 0;
511 Players[i].net_kills_total = 0;
512 Players[i].flags = 0;
513 Players[i].KillGoalCount=0;
517 for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
519 robot_controlled[i] = -1;
520 robot_agitation[i] = 0;
525 team_kills[0] = team_kills[1] = 0;
526 Endlevel_sequence = 0;
528 multi_leave_menu = 0;
531 game_disable_cheats();
533 Dead_player_camera = 0;
537 multi_make_player_ghost(int playernum)
541 //Assert(playernum != Player_num);
542 //Assert(playernum < MAX_NUM_NET_PLAYERS);
544 if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS) || (playernum < 0))
546 Int3(); // Non-terminal, see Rob
550 // if (Objects[Players[playernum].objnum].type != OBJ_PLAYER)
551 // mprintf((1, "Warning: Player %d is not currently a player.\n", playernum));
553 obj = &Objects[Players[playernum].objnum];
555 obj->type = OBJ_GHOST;
556 obj->render_type = RT_NONE;
557 obj->movement_type = MT_NONE;
558 multi_reset_player_object(obj);
560 if (Game_mode & GM_MULTI_ROBOTS)
561 multi_strip_robots(playernum);
565 multi_make_ghost_player(int playernum)
569 // Assert(playernum != Player_num);
570 // Assert(playernum < MAX_NUM_NET_PLAYERS);
572 if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS))
574 Int3(); // Non-terminal, see rob
578 // if(Objects[Players[playernum].objnum].type != OBJ_GHOST)
579 // mprintf((1, "Warning: Player %d is not currently a ghost.\n", playernum));
581 obj = &Objects[Players[playernum].objnum];
583 obj->type = OBJ_PLAYER;
584 obj->movement_type = MT_PHYSICS;
585 multi_reset_player_object(obj);
588 int multi_get_kill_list(int *plist)
590 // Returns the number of active net players and their
591 // sorted order of kills
595 for (i = 0; i < N_players; i++)
596 //if (Players[sorted_kills[i]].connected)
597 plist[n++] = sorted_kills[i];
600 Int3(); // SEE ROB OR MATT
602 //memcpy(plist, sorted_kills, N_players*sizeof(int));
608 multi_sort_kill_list(void)
610 // Sort the kills list each time a new kill is added
612 int kills[MAX_NUM_NET_PLAYERS];
616 for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
619 if (Game_mode & GM_MULTI_COOP)
620 kills[i] = Players[i].score;
623 if (Show_kill_list==2)
625 if (Players[i].net_killed_total+Players[i].net_kills_total==0)
626 kills[i]=-1; // always draw the ones without any ratio last
628 kills[i]=(int)((float)((float)Players[i].net_kills_total/((float)Players[i].net_killed_total+(float)Players[i].net_kills_total))*100.0);
631 kills[i] = Players[i].net_kills_total;
637 for (i = 0; i < N_players-1; i++)
639 if (kills[sorted_kills[i]] < kills[sorted_kills[i+1]])
641 changed = sorted_kills[i];
642 sorted_kills[i] = sorted_kills[i+1];
643 sorted_kills[i+1] = changed;
648 mprintf((0, "Sorted kills %d %d.\n", sorted_kills[0], sorted_kills[1]));
651 extern object *obj_find_first_of_type (int);
652 char Multi_killed_yourself=0;
654 void multi_compute_kill(int killer, int killed)
656 // Figure out the results of a network kills and add it to the
657 // appropriate player's tally.
659 int killed_pnum, killed_type;
660 int killer_pnum, killer_type,killer_id;
662 char killed_name[(CALLSIGN_LEN*2)+4];
663 char killer_name[(CALLSIGN_LEN*2)+4];
665 kmatrix_kills_changed = 1;
666 Multi_killed_yourself=0;
668 // Both object numbers are localized already!
670 mprintf((0, "compute_kill passed: object %d killed object %d.\n", killer, killed));
672 if ((killed < 0) || (killed > Highest_object_index) || (killer < 0) || (killer > Highest_object_index))
674 Int3(); // See Rob, illegal value passed to compute_kill;
678 killed_type = Objects[killed].type;
679 killer_type = Objects[killer].type;
680 killer_id = Objects[killer].id;
682 if ((killed_type != OBJ_PLAYER) && (killed_type != OBJ_GHOST))
684 Int3(); // compute_kill passed non-player object!
688 killed_pnum = Objects[killed].id;
690 PKilledFlags[killed_pnum]=1;
692 Assert ((killed_pnum >= 0) && (killed_pnum < N_players));
694 if (Game_mode & GM_TEAM)
695 sprintf(killed_name, "%s (%s)", Players[killed_pnum].callsign, Netgame.team_name[get_team(killed_pnum)]);
697 sprintf(killed_name, "%s", Players[killed_pnum].callsign);
699 if (Newdemo_state == ND_STATE_RECORDING)
700 newdemo_record_multi_death(killed_pnum);
702 digi_play_sample( SOUND_HUD_KILL, F3_0 );
704 if (Control_center_destroyed)
705 Players[killed_pnum].connected=3;
707 if (killer_type == OBJ_CNTRLCEN)
709 Players[killed_pnum].net_killed_total++;
710 Players[killed_pnum].net_kills_total--;
712 if (Newdemo_state == ND_STATE_RECORDING)
713 newdemo_record_multi_kill(killed_pnum, -1);
715 if (killed_pnum == Player_num)
717 HUD_init_message("%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_NONPLAY);
718 multi_add_lifetime_killed ();
721 HUD_init_message("%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_NONPLAY );
726 else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST))
728 if (killer_id==PMINE_ID && killer_type!=OBJ_ROBOT)
730 if (killed_pnum == Player_num)
731 HUD_init_message("You were killed by a mine!");
733 HUD_init_message("%s was killed by a mine!",killed_name);
737 if (killed_pnum == Player_num)
739 HUD_init_message("%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_ROBOT);
740 multi_add_lifetime_killed();
743 HUD_init_message("%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_ROBOT );
745 Players[killed_pnum].net_killed_total++;
749 else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST) && (killer_id!=PMINE_ID))
751 Int3(); // Illegal killer type?
754 if (killer_id==PMINE_ID)
756 if (killed_pnum==Player_num)
757 HUD_init_message("You were killed by a mine!");
759 HUD_init_message("%s was killed by a mine!",killed_name);
761 Players[killed_pnum].net_killed_total++;
767 killer_pnum = Objects[killer].id;
769 if (Game_mode & GM_TEAM)
770 sprintf(killer_name, "%s (%s)", Players[killer_pnum].callsign, Netgame.team_name[get_team(killer_pnum)]);
772 sprintf(killer_name, "%s", Players[killer_pnum].callsign);
774 // Beyond this point, it was definitely a player-player kill situation
776 if ((killer_pnum < 0) || (killer_pnum >= N_players))
777 Int3(); // See rob, tracking down bug with kill HUD messages
778 if ((killed_pnum < 0) || (killed_pnum >= N_players))
779 Int3(); // See rob, tracking down bug with kill HUD messages
781 if (killer_pnum == killed_pnum)
783 if (!(Game_mode & GM_HOARD))
785 if (Game_mode & GM_TEAM)
786 team_kills[get_team(killed_pnum)] -= 1;
788 Players[killed_pnum].net_killed_total += 1;
789 Players[killed_pnum].net_kills_total -= 1;
791 if (Newdemo_state == ND_STATE_RECORDING)
792 newdemo_record_multi_kill(killed_pnum, -1);
794 kill_matrix[killed_pnum][killed_pnum] += 1; // # of suicides
796 if (killer_pnum == Player_num)
798 HUD_init_message("%s %s %s!", TXT_YOU, TXT_KILLED, TXT_YOURSELF );
799 Multi_killed_yourself=1;
800 multi_add_lifetime_killed();
803 HUD_init_message("%s %s", killed_name, TXT_SUICIDE);
808 if (!(Game_mode & GM_HOARD))
810 if (Game_mode & GM_TEAM)
812 if (get_team(killed_pnum) == get_team(killer_pnum))
813 team_kills[get_team(killed_pnum)] -= 1;
815 team_kills[get_team(killer_pnum)] += 1;
818 Players[killer_pnum].net_kills_total += 1;
819 Players[killer_pnum].KillGoalCount+=1;
821 if (Newdemo_state == ND_STATE_RECORDING)
822 newdemo_record_multi_kill(killer_pnum, 1);
826 if (Game_mode & GM_TEAM)
828 if (killed_pnum==Player_num && get_team(killed_pnum) == get_team(killer_pnum))
829 Multi_killed_yourself=1;
833 kill_matrix[killer_pnum][killed_pnum] += 1;
834 Players[killed_pnum].net_killed_total += 1;
835 if (killer_pnum == Player_num) {
836 HUD_init_message("%s %s %s!", TXT_YOU, TXT_KILLED, killed_name);
837 multi_add_lifetime_kills();
838 if ((Game_mode & GM_MULTI_COOP) && (Players[Player_num].score >= 1000))
839 add_points_to_score(-1000);
841 else if (killed_pnum == Player_num)
843 HUD_init_message("%s %s %s!", killer_name, TXT_KILLED, TXT_YOU);
844 multi_add_lifetime_killed();
845 if (Game_mode & GM_HOARD)
847 if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]>3)
848 multi_send_play_by_play (1,killer_pnum,Player_num);
849 else if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]>0)
850 multi_send_play_by_play (0,killer_pnum,Player_num);
854 HUD_init_message("%s %s %s!", killer_name, TXT_KILLED, killed_name);
857 TheGoal=Netgame.KillGoal*5;
859 if (Netgame.KillGoal>0)
861 if (Players[killer_pnum].KillGoalCount>=TheGoal)
863 if (killer_pnum==Player_num)
865 HUD_init_message("You reached the kill goal!");
866 Players[Player_num].shields=i2f(200);
869 HUD_init_message ("%s has reached the kill goal!",Players[killer_pnum].callsign);
871 HUD_init_message ("The control center has been destroyed!");
872 net_destroy_controlcen (obj_find_first_of_type (OBJ_CNTRLCEN));
876 multi_sort_kill_list();
877 multi_show_player_list();
878 Players[killed_pnum].flags&=(~(PLAYER_FLAGS_HEADLIGHT_ON)); // clear the killed guys flags/headlights
884 static int lasttime=0;
887 if (!(Game_mode & GM_MULTI))
893 if ((Game_mode & GM_NETWORK) && Netgame.PlayTimeAllowed && lasttime!=f2i (ThisLevelTime))
895 for (i=0;i<N_players;i++)
896 if (Players[i].connected)
900 multi_send_heartbeat();
901 lasttime=f2i(ThisLevelTime);
907 multi_send_message(); // Send any waiting messages
910 multi_leave_menu = 0;
913 if (Game_mode & GM_MULTI_ROBOTS)
915 multi_check_robot_timeout();
919 if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
925 network_do_frame(0, 1);
928 if (multi_quit_game && !multi_in_menu)
931 longjmp(LeaveGame, 0);
936 multi_send_data(char *buf, int len, int repeat)
938 Assert(len == message_length[(int)buf[0]]);
939 Assert(buf[0] <= MULTI_MAX_TYPE);
940 // Assert(buf[0] >= 0);
942 if (Game_mode & GM_NETWORK)
945 if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
946 com_send_data(buf, len, repeat);
947 else if (Game_mode & GM_NETWORK)
948 network_send_data(buf, len, repeat);
952 multi_leave_game(void)
955 // if (Function_mode != FMODE_GAME)
958 if (!(Game_mode & GM_MULTI))
961 if (Game_mode & GM_NETWORK)
963 mprintf((0, "Sending explosion message.\n"));
968 drop_player_eggs(ConsoleObject);
969 multi_send_position(Players[Player_num].objnum);
970 multi_send_player_explode(MULTI_PLAYER_DROP);
973 mprintf((1, "Sending leave game.\n"));
974 multi_send_quit(MULTI_QUIT);
976 if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
978 if (Game_mode & GM_NETWORK)
979 network_leave_game();
981 Game_mode |= GM_GAME_OVER;
983 if (Function_mode!=FMODE_EXIT)
984 Function_mode = FMODE_MENU;
988 // change_playernum_to(0);
989 // Viewer = ConsoleObject = &Objects[0];
994 multi_show_player_list()
996 if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_COOP))
1002 Show_kill_list_timer = F1_0*5; // 5 second timer
1007 multi_endlevel(int *secret)
1011 if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
1012 com_endlevel(secret); // an opportunity to re-sync or whatever
1013 else if (Game_mode & GM_NETWORK)
1014 result = network_endlevel(secret);
1020 // Part 2 : functions that act on network/serial messages and change the
1021 // the state of the game in some way.
1025 //extern PORT *com_port;
1029 multi_menu_poll(void)
1032 int was_fuelcen_alive;
1033 int player_was_dead;
1035 was_fuelcen_alive = Control_center_destroyed;
1037 // Special polling function for in-game menus for multiplayer and serial
1039 if (! ((Game_mode & GM_MULTI) && (Function_mode == FMODE_GAME)) )
1042 if (multi_leave_menu)
1045 old_shields = Players[Player_num].shields;
1046 player_was_dead = Player_is_dead;
1048 multi_in_menu++; // Track level of menu nesting
1054 timer_delay(f0_1); // delay 100 milliseconds
1056 if (Endlevel_sequence || (Control_center_destroyed && !was_fuelcen_alive) || (Player_is_dead != player_was_dead) || (Players[Player_num].shields < old_shields))
1058 multi_leave_menu = 1;
1061 if ((Control_center_destroyed) && (Countdown_seconds_left < 10))
1063 multi_leave_menu = 1;
1068 if ((Game_mode & GM_MODEM) && (!GetCd(com_port)))
1070 multi_leave_menu = 1;
1079 multi_define_macro(int key)
1081 if (!(Game_mode & GM_MULTI))
1084 key &= (~KEY_SHIFTED);
1089 multi_defining_message = 1; break;
1091 multi_defining_message = 2; break;
1093 multi_defining_message = 3; break;
1095 multi_defining_message = 4; break;
1100 if (multi_defining_message) {
1101 multi_message_index = 0;
1102 Network_message[multi_message_index] = 0;
1107 char feedback_result[200];
1110 multi_message_feedback(void)
1116 if (!( ((colon = strrchr(Network_message, ':')) == NULL) || (colon-Network_message < 1) || (colon-Network_message > CALLSIGN_LEN) ))
1118 sprintf(feedback_result, "%s ", TXT_MESSAGE_SENT_TO);
1119 if ((Game_mode & GM_TEAM) && (atoi(Network_message) > 0) && (atoi(Network_message) < 3))
1121 sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[atoi(Network_message)-1]);
1124 if (Game_mode & GM_TEAM)
1126 for (i = 0; i < N_players; i++)
1128 if (!strnicmp(Netgame.team_name[i], Network_message, colon-Network_message))
1131 strcat(feedback_result, ", ");
1134 strcat(feedback_result, "\n");
1135 sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[i]);
1139 for (i = 0; i < N_players; i++)
1141 if ((!strnicmp(Players[i].callsign, Network_message, colon-Network_message)) && (i != Player_num) && (Players[i].connected))
1144 strcat(feedback_result, ", ");
1147 strcat(feedback_result, "\n");
1148 sprintf(feedback_result+strlen(feedback_result), "%s", Players[i].callsign);
1152 strcat(feedback_result, TXT_NOBODY);
1154 strcat(feedback_result, ".");
1156 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1158 Assert(strlen(feedback_result) < 200);
1160 HUD_init_message(feedback_result);
1161 //sprintf (temp,"%s",colon);
1162 //sprintf (Network_message,"%s",temp);
1168 multi_send_macro(int key)
1170 if (! (Game_mode & GM_MULTI) )
1187 if (!Network_message_macro[key][0])
1189 HUD_init_message(TXT_NO_MACRO);
1193 strcpy(Network_message, Network_message_macro[key]);
1194 Network_message_reciever = 100;
1196 HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
1197 multi_message_feedback();
1202 multi_send_message_start()
1204 if (Game_mode&GM_MULTI) {
1205 multi_sending_message = 1;
1206 multi_message_index = 0;
1207 Network_message[multi_message_index] = 0;
1211 extern fix StartingShields;
1212 fix PingLaunchTime,PingReturnTime;
1214 extern void network_send_ping (ubyte);
1215 extern int network_who_is_master();
1216 extern char NameReturning;
1217 extern int force_cockpit_redraw;
1219 void network_dump_appletalk_player(ubyte node, ushort net, ubyte socket, int why);
1221 void multi_send_message_end()
1226 Network_message_reciever = 100;
1228 if (!strnicmp (Network_message,"!Names",6))
1230 NameReturning=1-NameReturning;
1231 HUD_init_message ("Name returning is now %s.",NameReturning?"active":"disabled");
1233 else if (!strnicmp (Network_message,"Handicap:",9))
1235 mytempbuf=&Network_message[9];
1236 mprintf ((0,"Networkhandi=%s\n",mytempbuf));
1237 StartingShields=atol (mytempbuf);
1238 if (StartingShields<10)
1240 if (StartingShields>100)
1242 sprintf (Network_message,"%s has tried to cheat!",Players[Player_num].callsign);
1243 StartingShields=100;
1246 sprintf (Network_message,"%s handicap is now %d",Players[Player_num].callsign,StartingShields);
1248 HUD_init_message ("Telling others of your handicap of %d!",StartingShields);
1249 StartingShields=i2f(StartingShields);
1251 else if (!strnicmp (Network_message,"NoBombs",7))
1252 Netgame.DoSmartMine=0;
1253 else if (!strnicmp (Network_message,"Ping:",5))
1255 if (Game_mode & GM_NETWORK)
1258 if (strlen(Network_message) > 5)
1259 while (Network_message[name_index] == ' ')
1262 if (strlen(Network_message)<=name_index)
1264 HUD_init_message ("You must specify a name to ping");
1268 for (i = 0; i < N_players; i++)
1269 if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (i != Player_num) && (Players[i].connected))
1271 PingLaunchTime=timer_get_fixed_seconds();
1272 network_send_ping (i);
1273 HUD_init_message("Pinging %s...",Players[i].callsign);
1274 multi_message_index = 0;
1275 multi_sending_message = 0;
1279 else // Modem/Serial ping
1281 PingLaunchTime=timer_get_fixed_seconds();
1282 multi_send_modem_ping ();
1283 HUD_init_message("Pinging opponent...");
1284 multi_message_index = 0;
1285 multi_sending_message = 0;
1289 else if (!strnicmp (Network_message,"move:",5))
1291 mprintf ((0,"moving?\n"));
1293 if ((Game_mode & GM_NETWORK) && (Game_mode & GM_TEAM))
1296 if (strlen(Network_message) > 5)
1297 while (Network_message[name_index] == ' ')
1300 if (!network_i_am_master())
1302 HUD_init_message ("Only %s can move players!",Players[network_who_is_master()].callsign);
1306 if (strlen(Network_message)<=name_index)
1308 HUD_init_message ("You must specify a name to move");
1312 for (i = 0; i < N_players; i++)
1313 if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (Players[i].connected))
1315 if ((Game_mode & GM_CAPTURE) && (Players[i].flags & PLAYER_FLAGS_FLAG))
1317 HUD_init_message ("Can't move player because s/he has a flag!");
1321 if (Netgame.team_vector & (1<<i))
1322 Netgame.team_vector&=(~(1<<i));
1324 Netgame.team_vector|=(1<<i);
1326 for (t=0;t<N_players;t++)
1327 if (Players[t].connected)
1328 multi_reset_object_texture (&Objects[Players[t].objnum]);
1330 network_send_netgame_update ();
1331 sprintf (Network_message,"%s has changed teams!",Players[i].callsign);
1334 HUD_init_message ("You have changed teams!");
1338 HUD_init_message ("Moving %s to other team.",Players[i].callsign);
1344 else if (!strnicmp (Network_message,"kick:",5) && (Game_mode & GM_NETWORK))
1347 if (strlen(Network_message) > 5)
1348 while (Network_message[name_index] == ' ')
1351 if (!network_i_am_master())
1353 HUD_init_message ("Only %s can kick others out!",Players[network_who_is_master()].callsign);
1354 multi_message_index = 0;
1355 multi_sending_message = 0;
1358 if (strlen(Network_message)<=name_index)
1360 HUD_init_message ("You must specify a name to kick");
1361 multi_message_index = 0;
1362 multi_sending_message = 0;
1366 if (Network_message[name_index] == '#' && isdigit(Network_message[name_index+1])) {
1367 int players[MAX_NUM_NET_PLAYERS];
1368 int listpos = Network_message[name_index+1] - '0';
1370 mprintf ((0,"Trying to kick %d , show_kill_list=%d\n",listpos,Show_kill_list));
1372 if (Show_kill_list==1 || Show_kill_list==2) {
1373 if (listpos == 0 || listpos >= N_players) {
1374 HUD_init_message ("Invalid player number for kick.");
1375 multi_message_index = 0;
1376 multi_sending_message = 0;
1379 multi_get_kill_list(players);
1380 i = players[listpos];
1381 if ((i != Player_num) && (Players[i].connected))
1384 else HUD_init_message ("You cannot use # kicking with in team display.");
1387 multi_message_index = 0;
1388 multi_sending_message = 0;
1393 for (i = 0; i < N_players; i++)
1394 if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (i != Player_num) && (Players[i].connected)) {
1396 if (Network_game_type == IPX_GAME)
1397 network_dump_player(NetPlayers.players[i].network.ipx.server,NetPlayers.players[i].network.ipx.node, 7);
1399 network_dump_appletalk_player(NetPlayers.players[i].network.appletalk.node,NetPlayers.players[i].network.appletalk.net, NetPlayers.players[i].network.appletalk.socket, 7);
1401 HUD_init_message("Dumping %s...",Players[i].callsign);
1402 multi_message_index = 0;
1403 multi_sending_message = 0;
1409 HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
1411 multi_send_message();
1412 multi_message_feedback();
1414 multi_message_index = 0;
1415 multi_sending_message = 0;
1418 void multi_define_macro_end()
1420 Assert( multi_defining_message > 0 );
1422 strcpy( Network_message_macro[multi_defining_message-1], Network_message );
1423 write_player_file();
1425 multi_message_index = 0;
1426 multi_defining_message = 0;
1429 void multi_message_input_sub( int key )
1434 multi_sending_message = 0;
1435 multi_defining_message = 0;
1436 game_flush_inputs();
1441 if (multi_message_index > 0)
1442 multi_message_index--;
1443 Network_message[multi_message_index] = 0;
1446 if ( multi_sending_message )
1447 multi_send_message_end();
1448 else if ( multi_defining_message )
1449 multi_define_macro_end();
1450 game_flush_inputs();
1454 int ascii = key_to_ascii(key);
1455 if ((ascii < 255 )) {
1456 if (multi_message_index < MAX_MESSAGE_LEN-2 ) {
1457 Network_message[multi_message_index++] = ascii;
1458 Network_message[multi_message_index] = 0;
1459 } else if ( multi_sending_message ) {
1461 char * ptext, * pcolon;
1463 Network_message[multi_message_index++] = ascii;
1464 Network_message[multi_message_index] = 0;
1465 for (i=multi_message_index-1; i>=0; i-- ) {
1466 if ( Network_message[i]==32 ) {
1467 ptext = &Network_message[i+1];
1468 Network_message[i] = 0;
1472 multi_send_message_end();
1474 multi_sending_message = 1;
1475 pcolon = strchr( Network_message, ':' );
1477 strcpy( pcolon+1, ptext );
1479 strcpy( Network_message, ptext );
1480 multi_message_index = strlen( Network_message );
1489 multi_send_message_dialog(void)
1494 if (!(Game_mode&GM_MULTI))
1497 Network_message[0] = 0; // Get rid of old contents
1499 m[0].type=NM_TYPE_INPUT; m[0].text = Network_message; m[0].text_len = MAX_MESSAGE_LEN-1;
1500 choice = newmenu_do( NULL, TXT_SEND_MESSAGE, 1, m, NULL );
1502 if ((choice > -1) && (strlen(Network_message) > 0)) {
1503 Network_message_reciever = 100;
1504 HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
1505 multi_message_feedback();
1512 multi_do_death(int objnum)
1514 // Do any miscellaneous stuff for a new network player after death
1518 if (!(Game_mode & GM_MULTI_COOP))
1520 mprintf((0, "Setting all keys for player %d.\n", Player_num));
1521 Players[Player_num].flags |= (PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_GOLD_KEY);
1526 multi_do_fire(char *buf)
1533 // Act out the actual shooting
1537 weapon = (int)buf[2];
1542 GET_INTEL_SHORT(Network_laser_track, buf+6);
1544 Assert (pnum < N_players);
1546 if (Objects[Players[(int)pnum].objnum].type == OBJ_GHOST)
1547 multi_make_ghost_player(pnum);
1549 // mprintf((0,"multi_do_fire, weapon = %d\n",weapon));
1551 if (weapon == FLARE_ADJUST)
1552 Laser_player_fire( Objects+Players[(int)pnum].objnum, FLARE_ID, 6, 1, 0);
1553 else if (weapon >= MISSILE_ADJUST) {
1554 int weapon_id,weapon_gun;
1556 weapon_id = Secondary_weapon_to_weapon_info[weapon-MISSILE_ADJUST];
1557 weapon_gun = Secondary_weapon_to_gun_num[weapon-MISSILE_ADJUST] + (flags & 1);
1558 mprintf((0,"missile id = %d, gun = %d\n",weapon_id,weapon_gun));
1560 if (weapon-MISSILE_ADJUST==GUIDED_INDEX)
1562 mprintf ((0,"Missile is guided!!!\n"));
1566 Laser_player_fire( Objects+Players[(int)pnum].objnum, weapon_id, weapon_gun, 1, 0 );
1569 fix save_charge = Fusion_charge;
1571 if (weapon == FUSION_INDEX) {
1572 Fusion_charge = flags << 12;
1573 mprintf((0, "Fusion charge X%f.\n", f2fl(Fusion_charge)));
1575 if (weapon == LASER_ID) {
1576 if (flags & LASER_QUAD)
1577 Players[(int)pnum].flags |= PLAYER_FLAGS_QUAD_LASERS;
1579 Players[(int)pnum].flags &= ~PLAYER_FLAGS_QUAD_LASERS;
1582 do_laser_firing(Players[(int)pnum].objnum, weapon, (int)buf[3], flags, (int)buf[5]);
1584 if (weapon == FUSION_INDEX)
1585 Fusion_charge = save_charge;
1590 multi_do_message(char *buf)
1593 char *tilde,mesbuf[100];
1598 if ((tilde=strchr (buf+loc,'$'))) // do that stupid name stuff
1599 { // why'd I put this in? Probably for the
1600 tloc=tilde-(buf+loc); // same reason you can name your guidebot
1601 mprintf ((0,"Tloc=%d\n",tloc));
1604 strncpy (mesbuf,buf+loc,tloc);
1605 strcpy (mesbuf+tloc,Players[Player_num].callsign);
1606 strcpy (mesbuf+strlen(Players[Player_num].callsign)+tloc,buf+loc+tloc+1);
1607 strcpy (buf+loc,mesbuf);
1610 if (((colon = strrchr(buf+loc, ':')) == NULL) || (colon-(buf+loc) < 1) || (colon-(buf+loc) > CALLSIGN_LEN))
1613 mesbuf[1] = BM_XRGB(28, 0, 0);
1614 strcpy(&mesbuf[2], Players[(int)buf[1]].callsign);
1618 mesbuf[t+2] = BM_XRGB(0, 31, 0);
1621 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1622 HUD_init_message("%s %s", mesbuf, buf+2);
1626 if ( (!strnicmp(Players[Player_num].callsign, buf+loc, colon-(buf+loc))) ||
1627 ((Game_mode & GM_TEAM) && ( (get_team(Player_num) == atoi(buf+loc)-1) || !strnicmp(Netgame.team_name[get_team(Player_num)], buf+loc, colon-(buf+loc)))) )
1630 mesbuf[1] = BM_XRGB(0, 32, 32);
1631 strcpy(&mesbuf[2], Players[(int)buf[1]].callsign);
1635 mesbuf[t+2] = BM_XRGB(0, 31, 0);
1638 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1639 HUD_init_message("%s %s", mesbuf, colon+1);
1645 multi_do_position(char *buf)
1647 #ifdef WORDS_BIGENDIAN
1651 // This routine does only player positions, mode game only
1652 // mprintf((0, "Got position packet.\n"));
1654 int pnum = (Player_num+1)%2;
1656 Assert(&Objects[Players[pnum].objnum] != ConsoleObject);
1658 if (Game_mode & GM_NETWORK)
1660 Int3(); // Get Jason, what the hell are we doing here?
1665 #ifndef WORDS_BIGENDIAN
1666 extract_shortpos(&Objects[Players[pnum].objnum], (shortpos *)(buf+1),0);
1668 memcpy((ubyte *)(sp.bytemat), (ubyte *)(buf + 1), 9);
1669 memcpy((ubyte *)&(sp.xo), (ubyte *)(buf + 10), 14);
1670 extract_shortpos(&Objects[Players[pnum].objnum], &sp, 1);
1673 if (Objects[Players[pnum].objnum].movement_type == MT_PHYSICS)
1674 set_thrust_from_velocity(&Objects[Players[pnum].objnum]);
1678 multi_do_reappear(char *buf)
1682 GET_INTEL_SHORT(objnum, buf+1);
1684 Assert(objnum >= 0);
1685 // Assert(Players[Objects[objnum].id]].objnum == objnum);
1687 // mprintf((0, "Switching rendering back on for object %d.\n", objnum));
1689 multi_make_ghost_player(Objects[objnum].id);
1690 create_player_appearance_effect(&Objects[objnum]);
1691 PKilledFlags[Objects[objnum].id]=0;
1695 multi_do_player_explode(char *buf)
1697 // Only call this for players, not robots. pnum is player number, not
1704 char remote_created;
1709 if ((pnum < 0) || (pnum >= N_players))
1713 Assert(pnum < N_players);
1717 // If we are in the process of sending objects to a new player, reset that process
1718 if (Network_send_objects)
1720 mprintf((0, "Resetting object sync due to player explosion.\n"));
1721 Network_send_objnum = -1;
1725 // Stuff the Players structure to prepare for the explosion
1728 GET_INTEL_SHORT(Players[pnum].primary_weapon_flags, buf+count); count += 2;
1729 GET_INTEL_SHORT(Players[pnum].secondary_weapon_flags, buf+count); count += 2;
1730 Players[pnum].laser_level = buf[count]; count++;
1731 Players[pnum].secondary_ammo[HOMING_INDEX] = buf[count]; count++;
1732 Players[pnum].secondary_ammo[CONCUSSION_INDEX] = buf[count];count++;
1733 Players[pnum].secondary_ammo[SMART_INDEX] = buf[count]; count++;
1734 Players[pnum].secondary_ammo[MEGA_INDEX] = buf[count]; count++;
1735 Players[pnum].secondary_ammo[PROXIMITY_INDEX] = buf[count]; count++;
1737 Players[pnum].secondary_ammo[SMISSILE1_INDEX] = buf[count]; count++;
1738 Players[pnum].secondary_ammo[GUIDED_INDEX] = buf[count]; count++;
1739 Players[pnum].secondary_ammo[SMART_MINE_INDEX]= buf[count]; count++;
1740 Players[pnum].secondary_ammo[SMISSILE4_INDEX] = buf[count]; count++;
1741 Players[pnum].secondary_ammo[SMISSILE5_INDEX] = buf[count]; count++;
1743 GET_INTEL_SHORT(Players[pnum].primary_ammo[VULCAN_INDEX], buf+count); count += 2;
1744 GET_INTEL_SHORT(Players[pnum].primary_ammo[GAUSS_INDEX], buf+count); count += 2;
1745 GET_INTEL_INT(Players[pnum].flags, buf+count); count += 4;
1747 multi_adjust_remote_cap (pnum);
1749 objp = Objects+Players[pnum].objnum;
1751 // objp->phys_info.velocity = *(vms_vector *)(buf+16); // 12 bytes
1752 // objp->pos = *(vms_vector *)(buf+28); // 12 bytes
1754 remote_created = buf[count++]; // How many did the other guy create?
1758 drop_player_eggs(objp);
1760 // Create mapping from remote to local numbering system
1762 mprintf((0, "I Created %d powerups, remote created %d.\n", Net_create_loc, remote_created));
1764 // We now handle this situation gracefully, Int3 not required
1765 // if (Net_create_loc != remote_created)
1766 // Int3(); // Probably out of object array space, see Rob
1768 for (i = 0; i < remote_created; i++)
1772 GET_INTEL_SHORT(s, buf+count);
1774 if ((i < Net_create_loc) && (s > 0))
1775 map_objnum_local_to_remote((short)Net_create_objnums[i], s, pnum);
1778 mprintf((0, "WARNING: Remote created object has non-valid number %d (player %d)", s, pnum));
1782 mprintf((0, "WARNING: Could not create all powerups created by player %d.\n", pnum));
1787 for (i = remote_created; i < Net_create_loc; i++) {
1788 mprintf((0, "WARNING: I Created more powerups than player %d, deleting.\n", pnum));
1789 Objects[Net_create_objnums[i]].flags |= OF_SHOULD_BE_DEAD;
1792 if (buf[0] == MULTI_PLAYER_EXPLODE)
1794 explode_badass_player(objp);
1796 objp->flags &= ~OF_SHOULD_BE_DEAD; //don't really kill player
1797 multi_make_player_ghost(pnum);
1801 create_player_appearance_effect(objp);
1804 Players[pnum].flags &= ~(PLAYER_FLAGS_CLOAKED | PLAYER_FLAGS_INVULNERABLE | PLAYER_FLAGS_FLAG);
1805 Players[pnum].cloak_time = 0;
1809 multi_do_kill(char *buf)
1815 pnum = (int)(buf[count]);
1816 if ((pnum < 0) || (pnum >= N_players))
1818 Int3(); // Invalid player number killed
1821 killed = Players[pnum].objnum;
1824 GET_INTEL_SHORT(killer, buf+count);
1826 killer = objnum_remote_to_local(killer, (sbyte)buf[count+2]);
1829 if ((Objects[killed].type != OBJ_PLAYER) && (Objects[killed].type != OBJ_GHOST))
1832 mprintf( (1, "SOFT INT3: MULTI.C Non-player object %d of type %d killed! (JOHN)\n", killed, Objects[killed].type ));
1837 multi_compute_kill(killer, killed);
1842 // Changed by MK on 10/20/94 to send NULL as object to net_destroy_controlcen if it got -1
1843 // which means not a controlcen object, but contained in another object
1844 void multi_do_controlcen_destroy(char *buf)
1849 GET_INTEL_SHORT(objnum, buf+1);
1852 if (Control_center_destroyed != 1)
1854 if ((who < N_players) && (who != Player_num)) {
1855 HUD_init_message("%s %s", Players[who].callsign, TXT_HAS_DEST_CONTROL);
1857 else if (who == Player_num)
1858 HUD_init_message(TXT_YOU_DEST_CONTROL);
1860 HUD_init_message(TXT_CONTROL_DESTROYED);
1863 net_destroy_controlcen(Objects+objnum);
1865 net_destroy_controlcen(NULL);
1870 multi_do_escape(char *buf)
1874 objnum = Players[(int)buf[1]].objnum;
1876 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1877 digi_kill_sound_linked_to_object (objnum);
1881 HUD_init_message("%s %s", Players[(int)buf[1]].callsign, TXT_HAS_ESCAPED);
1882 if (Game_mode & GM_NETWORK)
1883 Players[(int)buf[1]].connected = CONNECT_ESCAPE_TUNNEL;
1884 if (!multi_goto_secret)
1885 multi_goto_secret = 2;
1887 else if (buf[2] == 1)
1889 HUD_init_message("%s %s", Players[(int)buf[1]].callsign, TXT_HAS_FOUND_SECRET);
1890 if (Game_mode & GM_NETWORK)
1891 Players[(int)buf[1]].connected = CONNECT_FOUND_SECRET;
1892 if (!multi_goto_secret)
1893 multi_goto_secret = 1;
1895 create_player_appearance_effect(&Objects[objnum]);
1896 multi_make_player_ghost(buf[1]);
1900 multi_do_remobj(char *buf)
1902 short objnum; // which object to remove
1904 sbyte obj_owner; // which remote list is it entered in
1906 GET_INTEL_SHORT(objnum, buf+1);
1909 Assert(objnum >= 0);
1914 local_objnum = objnum_remote_to_local(objnum, obj_owner); // translate to local objnum
1916 // mprintf((0, "multi_do_remobj: %d owner %d = %d.\n", objnum, obj_owner, local_objnum));
1918 if (local_objnum < 0)
1920 mprintf((1, "multi_do_remobj: Could not remove referenced object.\n"));
1924 if ((Objects[local_objnum].type != OBJ_POWERUP) && (Objects[local_objnum].type != OBJ_HOSTAGE))
1926 mprintf((1, "multi_get_remobj: tried to remove invalid type %d.\n", Objects[local_objnum].type));
1930 if (Network_send_objects && network_objnum_is_past(local_objnum))
1932 mprintf((0, "Resetting object sync due to object removal.\n"));
1933 Network_send_objnum = -1;
1935 if (Objects[local_objnum].type==OBJ_POWERUP)
1936 if (Game_mode & GM_NETWORK)
1938 if (PowerupsInMine[Objects[local_objnum].id]>0)
1939 PowerupsInMine[Objects[local_objnum].id]--;
1941 if (multi_powerup_is_4pack (Objects[local_objnum].id))
1943 mprintf ((0,"Hey babe! Doing that wacky 4 pack stuff."));
1945 if (PowerupsInMine[Objects[local_objnum].id-1]-4<0)
1946 PowerupsInMine[Objects[local_objnum].id-1]=0;
1948 PowerupsInMine[Objects[local_objnum].id-1]-=4;
1951 mprintf ((0,"Decrementing powerups! %d\n",PowerupsInMine[Objects[local_objnum].id]));
1954 Objects[local_objnum].flags |= OF_SHOULD_BE_DEAD; // quick and painless
1959 multi_do_quit(char *buf)
1962 if (Game_mode & GM_NETWORK)
1966 digi_play_sample( SOUND_HUD_MESSAGE, F1_0 );
1968 HUD_init_message( "%s %s", Players[(int)buf[1]].callsign, TXT_HAS_LEFT_THE_GAME);
1970 network_disconnect_player(buf[1]);
1975 for (i = 0; i < N_players; i++)
1976 if (Players[i].connected) n++;
1979 nm_messagebox(NULL, 1, TXT_OK, TXT_YOU_ARE_ONLY);
1983 if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
1985 Function_mode = FMODE_MENU;
1986 multi_quit_game = 1;
1987 multi_leave_menu = 1;
1988 nm_messagebox(NULL, 1, TXT_OK, TXT_OPPONENT_LEFT);
1989 Function_mode = FMODE_GAME;
1990 multi_reset_stuff();
1996 multi_do_cloak(char *buf)
2000 pnum = (int)(buf[1]);
2002 Assert(pnum < N_players);
2004 mprintf((0, "Cloaking player %d\n", pnum));
2006 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2007 Players[pnum].cloak_time = GameTime;
2008 ai_do_cloak_stuff();
2011 if (Game_mode & GM_MULTI_ROBOTS)
2012 multi_strip_robots(pnum);
2015 if (Newdemo_state == ND_STATE_RECORDING)
2016 newdemo_record_multi_cloak(pnum);
2020 multi_do_decloak(char *buf)
2024 pnum = (int)(buf[1]);
2026 if (Newdemo_state == ND_STATE_RECORDING)
2027 newdemo_record_multi_decloak(pnum);
2032 multi_do_door_open(char *buf)
2040 GET_INTEL_SHORT(segnum, buf+1);
2044 // mprintf((0, "Opening door on side %d of segment # %d.\n", side, segnum));
2046 if ((segnum < 0) || (segnum > Highest_segment_index) || (side < 0) || (side > 5))
2052 seg = &Segments[segnum];
2054 if (seg->sides[side].wall_num == -1) { //Opening door on illegal wall
2059 w = &Walls[seg->sides[side].wall_num];
2061 if (w->type == WALL_BLASTABLE)
2063 if (!(w->flags & WALL_BLASTED))
2065 mprintf((0, "Blasting wall by remote command.\n"));
2066 wall_destroy(seg, side);
2070 else if (w->state != WALL_DOOR_OPENING)
2072 wall_open_door(seg, side);
2079 // mprintf((0, "Door already opening!\n"));
2083 multi_do_create_explosion(char *buf)
2088 pnum = buf[count++];
2090 // mprintf((0, "Creating small fireball.\n"));
2091 create_small_fireball_on_object(&Objects[Players[pnum].objnum], F1_0, 1);
2095 multi_do_controlcen_fire(char *buf)
2097 vms_vector to_target;
2102 memcpy(&to_target, buf+count, 12); count += 12;
2103 #ifdef WORDS_BIGENDIAN // swap the vector to_target
2104 to_target.x = (fix)INTEL_INT((int)to_target.x);
2105 to_target.y = (fix)INTEL_INT((int)to_target.y);
2106 to_target.z = (fix)INTEL_INT((int)to_target.z);
2108 gun_num = buf[count]; count += 1;
2109 GET_INTEL_SHORT(objnum, buf+count); count += 2;
2111 Laser_create_new_easy(&to_target, &Gun_pos[(int)gun_num], objnum, CONTROLCEN_WEAPON_NUM, 1);
2115 multi_do_create_powerup(char *buf)
2125 if (Endlevel_sequence || Control_center_destroyed)
2128 pnum = buf[count++];
2129 powerup_type = buf[count++];
2130 GET_INTEL_SHORT(segnum, buf+count); count+=2;
2131 GET_INTEL_SHORT(objnum, buf+count); count+=2;
2133 if ((segnum < 0) || (segnum > Highest_segment_index)) {
2138 memcpy(&new_pos, buf+count, sizeof(vms_vector)); count+=sizeof(vms_vector);
2139 #ifdef WORDS_BIGENDIAN
2140 new_pos.x = (fix)SWAPINT((int)new_pos.x);
2141 new_pos.y = (fix)SWAPINT((int)new_pos.y);
2142 new_pos.z = (fix)SWAPINT((int)new_pos.z);
2146 my_objnum = call_object_create_egg(&Objects[Players[(int)pnum].objnum], 1, OBJ_POWERUP, powerup_type);
2148 if (my_objnum < 0) {
2149 mprintf((0, "Could not create new powerup!\n"));
2153 if (Network_send_objects && network_objnum_is_past(my_objnum))
2155 mprintf((0, "Resetting object sync due to powerup creation.\n"));
2156 Network_send_objnum = -1;
2159 Objects[my_objnum].pos = new_pos;
2161 vm_vec_zero(&Objects[my_objnum].mtype.phys_info.velocity);
2163 obj_relink(my_objnum, segnum);
2165 map_objnum_local_to_remote(my_objnum, objnum, pnum);
2167 object_create_explosion(segnum, &new_pos, i2f(5), VCLIP_POWERUP_DISAPPEARANCE);
2168 mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));
2170 if (Game_mode & GM_NETWORK)
2171 PowerupsInMine[(int)powerup_type]++;
2175 multi_do_play_sound(char *buf)
2177 int pnum = (int)(buf[1]);
2178 int sound_num = (int)(buf[2]);
2179 fix volume = (int)(buf[3]) << 12;
2181 if (!Players[pnum].connected)
2184 Assert(Players[pnum].objnum >= 0);
2185 Assert(Players[pnum].objnum <= Highest_object_index);
2187 digi_link_sound_to_object( sound_num, Players[pnum].objnum, 0, volume);
2191 multi_do_score(char *buf)
2193 int pnum = (int)(buf[1]);
2195 if ((pnum < 0) || (pnum >= N_players))
2197 Int3(); // Non-terminal, see rob
2201 if (Newdemo_state == ND_STATE_RECORDING) {
2203 GET_INTEL_INT(score, buf+2);
2204 newdemo_record_multi_score(pnum, score);
2207 GET_INTEL_INT(Players[pnum].score, buf+2);
2209 multi_sort_kill_list();
2213 multi_do_trigger(char *buf)
2215 int pnum = (int)(buf[1]);
2216 int trigger = (int)(buf[2]);
2218 mprintf ((0,"MULTI doing trigger!\n"));
2220 if ((pnum < 0) || (pnum >= N_players) || (pnum == Player_num))
2222 Int3(); // Got trigger from illegal playernum
2225 if ((trigger < 0) || (trigger >= Num_triggers))
2227 Int3(); // Illegal trigger number in multiplayer
2230 check_trigger_sub(trigger, pnum,0);
2233 void multi_do_drop_marker (char *buf)
2236 int pnum=(int)(buf[1]);
2237 int mesnum=(int)(buf[2]);
2238 vms_vector position;
2240 if (pnum==Player_num) // my marker? don't set it down cuz it might screw up the orientation
2243 GET_INTEL_INT(position.x, buf+3);
2244 GET_INTEL_INT(position.y, buf+7);
2245 GET_INTEL_INT(position.z, buf+11);
2248 MarkerMessage[(pnum*2)+mesnum][i]=buf[15+i];
2250 MarkerPoint[(pnum*2)+mesnum]=position;
2252 if (MarkerObject[(pnum*2)+mesnum] !=-1 && Objects[MarkerObject[(pnum*2)+mesnum]].type!=OBJ_NONE && MarkerObject[(pnum*2)+mesnum] !=0)
2253 obj_delete(MarkerObject[(pnum*2)+mesnum]);
2255 MarkerObject[(pnum*2)+mesnum] = drop_marker_object(&position,Objects[Players[Player_num].objnum].segnum,&Objects[Players[Player_num].objnum].orient,(pnum*2)+mesnum);
2256 strcpy (MarkerOwner[(pnum*2)+mesnum],Players[pnum].callsign);
2257 mprintf ((0,"Dropped player %d message: %s\n",pnum,MarkerMessage[(pnum*2)+mesnum]));
2261 void multi_do_hostage_door_status(char *buf)
2263 // Update hit point status of a door
2269 GET_INTEL_SHORT(wallnum, buf+count); count += 2;
2270 GET_INTEL_INT(hps, buf+count); count += 4;
2272 if ((wallnum < 0) || (wallnum > Num_walls) || (hps < 0) || (Walls[wallnum].type != WALL_BLASTABLE))
2274 Int3(); // Non-terminal, see Rob
2278 // mprintf((0, "Damaging wall number %d to %f points.\n", wallnum, f2fl(hps)));
2280 if (hps < Walls[wallnum].hps)
2281 wall_damage(&Segments[Walls[wallnum].segnum], Walls[wallnum].sidenum, Walls[wallnum].hps - hps);
2284 void multi_do_save_game(char *buf)
2291 slot = *(ubyte *)(buf+count); count += 1;
2292 GET_INTEL_INT(id, buf+count); count += 4;
2293 memcpy( desc, &buf[count], 20 ); count += 20;
2295 multi_save_game( slot, id, desc );
2298 void multi_do_restore_game(char *buf)
2304 slot = *(ubyte *)(buf+count); count += 1;
2305 GET_INTEL_INT(id, buf+count); count += 4;
2307 multi_restore_game( slot, id );
2311 void multi_do_req_player(char *buf)
2315 // Send my netplayer_stats to everyone!
2316 player_n = *(ubyte *)(buf+1);
2317 if ( (player_n == Player_num) || (player_n == 255) ) {
2318 extract_netplayer_stats( &ps, &Players[Player_num] );
2319 ps.Player_num = Player_num;
2320 ps.message_type = MULTI_SEND_PLAYER; // SET
2321 multi_send_data((ubyte*)&ps, sizeof(netplayer_stats), 0);
2325 void multi_do_send_player(char *buf)
2327 // Got a player packet from someone!!!
2328 netplayer_stats * p;
2329 p = (netplayer_stats *)buf;
2331 Assert( p->Player_num <= N_players );
2333 mprintf(( 0, "Got netplayer_stats for player %d (I'm %d)\n", p->Player_num, Player_num ));
2334 mprintf(( 0, "Their shields are: %d\n", f2i(p->shields) ));
2336 use_netplayer_stats( &Players[p->Player_num], p );
2340 multi_reset_stuff(void)
2342 // A generic, emergency function to solve problems that crop up
2343 // when a player exits quick-out from the game because of a
2344 // serial connection loss. Fixes several weird bugs!
2348 Players[Player_num].homing_object_dist = -F1_0; // Turn off homing sound.
2350 Dead_player_camera = 0;
2351 Endlevel_sequence = 0;
2356 multi_reset_player_object(object *objp)
2360 //Init physics for a non-console player
2362 Assert(objp >= Objects);
2363 Assert(objp <= Objects+Highest_object_index);
2364 Assert((objp->type == OBJ_PLAYER) || (objp->type == OBJ_GHOST));
2366 vm_vec_zero(&objp->mtype.phys_info.velocity);
2367 vm_vec_zero(&objp->mtype.phys_info.thrust);
2368 vm_vec_zero(&objp->mtype.phys_info.rotvel);
2369 vm_vec_zero(&objp->mtype.phys_info.rotthrust);
2370 objp->mtype.phys_info.brakes = objp->mtype.phys_info.turnroll = 0;
2371 objp->mtype.phys_info.mass = Player_ship->mass;
2372 objp->mtype.phys_info.drag = Player_ship->drag;
2373 // objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE | PF_USES_THRUST);
2374 objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE);
2378 objp->render_type = RT_POLYOBJ;
2379 objp->rtype.pobj_info.model_num = Player_ship->model_num; //what model is this?
2380 objp->rtype.pobj_info.subobj_flags = 0; //zero the flags
2381 for (i=0;i<MAX_SUBMODELS;i++)
2382 vm_angvec_zero(&objp->rtype.pobj_info.anim_angles[i]);
2384 //reset textures for this, if not player 0
2386 multi_reset_object_texture (objp);
2392 if (objp->type == OBJ_GHOST)
2393 objp->render_type = RT_NONE;
2397 void multi_reset_object_texture (object *objp)
2401 if (Game_mode & GM_TEAM)
2402 id = get_team(objp->id);
2407 objp->rtype.pobj_info.alt_textures=0;
2409 Assert(N_PLAYER_SHIP_TEXTURES == Polygon_models[objp->rtype.pobj_info.model_num].n_textures);
2411 for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
2412 multi_player_textures[id-1][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[objp->rtype.pobj_info.model_num].first_texture+i]];
2414 multi_player_textures[id-1][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2]];
2415 multi_player_textures[id-1][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2+1]];
2417 objp->rtype.pobj_info.alt_textures = id;
2425 extern int TTRecv[];
2426 extern FILE *RecieveLogFile;
2430 multi_process_bigdata(char *buf, int len)
2432 // Takes a bunch of messages, check them for validity,
2433 // and pass them to multi_process_data.
2435 int type, sub_len, bytes_processed = 0;
2437 while( bytes_processed < len ) {
2438 type = buf[bytes_processed];
2440 if ( (type<0) || (type>MULTI_MAX_TYPE)) {
2441 mprintf( (1, "multi_process_bigdata: Invalid packet type %d!\n", type ));
2444 sub_len = message_length[type];
2446 Assert(sub_len > 0);
2448 if ( (bytes_processed+sub_len) > len ) {
2449 mprintf( (1, "multi_process_bigdata: packet type %d too short (%d>%d)!\n", type, (bytes_processed+sub_len), len ));
2454 multi_process_data(&buf[bytes_processed], sub_len);
2455 bytes_processed += sub_len;
2460 // Part 2 : Functions that send communication messages to inform the other
2461 // players of something we did.
2465 multi_send_fire(void)
2467 if (!Network_laser_fired)
2470 multibuf[0] = (char)MULTI_FIRE;
2471 multibuf[1] = (char)Player_num;
2472 multibuf[2] = (char)Network_laser_gun;
2473 multibuf[3] = (char)Network_laser_level;
2474 multibuf[4] = (char)Network_laser_flags;
2475 multibuf[5] = (char)Network_laser_fired;
2477 PUT_INTEL_SHORT(multibuf+6, Network_laser_track);
2479 multi_send_data(multibuf, 8, 0);
2481 Network_laser_fired = 0;
2485 multi_send_destroy_controlcen(int objnum, int player)
2487 if (player == Player_num)
2488 HUD_init_message(TXT_YOU_DEST_CONTROL);
2489 else if ((player > 0) && (player < N_players))
2490 HUD_init_message("%s %s", Players[player].callsign, TXT_HAS_DEST_CONTROL);
2492 HUD_init_message(TXT_CONTROL_DESTROYED);
2494 multibuf[0] = (char)MULTI_CONTROLCEN;
2495 PUT_INTEL_SHORT(multibuf+1, objnum);
2496 multibuf[3] = player;
2497 multi_send_data(multibuf, 4, 2);
2500 void multi_send_drop_marker (int player,vms_vector position,char messagenum,char text[])
2504 if (player<N_players)
2506 mprintf ((0,"Sending MARKER drop!\n"));
2507 multibuf[0]=(char)MULTI_MARKER;
2508 multibuf[1]=(char)player;
2509 multibuf[2]=messagenum;
2510 PUT_INTEL_INT(multibuf+3, position.x);
2511 PUT_INTEL_INT(multibuf+7, position.y);
2512 PUT_INTEL_INT(multibuf+11, position.z);
2514 multibuf[15+i]=text[i];
2516 multi_send_data(multibuf, 55, 1);
2520 multi_send_endlevel_start(int secret)
2522 multibuf[0] = (char)MULTI_ENDLEVEL_START;
2523 multibuf[1] = Player_num;
2524 multibuf[2] = (char)secret;
2526 if ((secret) && !multi_goto_secret)
2527 multi_goto_secret = 1;
2528 else if (!multi_goto_secret)
2529 multi_goto_secret = 2;
2531 multi_send_data(multibuf, 3, 1);
2532 if (Game_mode & GM_NETWORK)
2534 Players[Player_num].connected = 5;
2535 network_send_endlevel_packet();
2540 multi_send_player_explode(char type)
2545 Assert( (type == MULTI_PLAYER_DROP) || (type == MULTI_PLAYER_EXPLODE) );
2547 multi_send_position(Players[Player_num].objnum);
2549 if (Network_send_objects)
2551 mprintf((0, "Resetting object sync due to player explosion.\n"));
2552 Network_send_objnum = -1;
2555 multibuf[count++] = type;
2556 multibuf[count++] = Player_num;
2558 PUT_INTEL_SHORT(multibuf+count, Players[Player_num].primary_weapon_flags);
2560 PUT_INTEL_SHORT(multibuf+count, Players[Player_num].secondary_weapon_flags);
2562 multibuf[count++] = (char)Players[Player_num].laser_level;
2564 multibuf[count++] = (char)Players[Player_num].secondary_ammo[HOMING_INDEX];
2565 multibuf[count++] = (char)Players[Player_num].secondary_ammo[CONCUSSION_INDEX];
2566 multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_INDEX];
2567 multibuf[count++] = (char)Players[Player_num].secondary_ammo[MEGA_INDEX];
2568 multibuf[count++] = (char)Players[Player_num].secondary_ammo[PROXIMITY_INDEX];
2570 multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE1_INDEX];
2571 multibuf[count++] = (char)Players[Player_num].secondary_ammo[GUIDED_INDEX];
2572 multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_MINE_INDEX];
2573 multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE4_INDEX];
2574 multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE5_INDEX];
2576 PUT_INTEL_SHORT(multibuf+count, Players[Player_num].primary_ammo[VULCAN_INDEX] );
2578 PUT_INTEL_SHORT(multibuf+count, Players[Player_num].primary_ammo[GAUSS_INDEX] );
2580 PUT_INTEL_INT(multibuf+count, Players[Player_num].flags );
2583 multibuf[count++] = Net_create_loc;
2585 Assert(Net_create_loc <= MAX_NET_CREATE_OBJECTS);
2587 memset(multibuf+count, -1, MAX_NET_CREATE_OBJECTS*sizeof(short));
2589 mprintf((0, "Created %d explosion objects.\n", Net_create_loc));
2591 for (i = 0; i < Net_create_loc; i++)
2593 if (Net_create_objnums[i] <= 0) {
2594 Int3(); // Illegal value in created egg object numbers
2599 PUT_INTEL_SHORT(multibuf+count, Net_create_objnums[i]); count += 2;
2601 // We created these objs so our local number = the network number
2602 map_objnum_local_to_local((short)Net_create_objnums[i]);
2607 // mprintf((1, "explode message size = %d, max = %d.\n", count, message_length[MULTI_PLAYER_EXPLODE]));
2609 if (count > message_length[MULTI_PLAYER_EXPLODE])
2614 multi_send_data(multibuf, message_length[MULTI_PLAYER_EXPLODE], 2);
2615 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
2616 multi_send_decloak();
2617 if (Game_mode & GM_MULTI_ROBOTS)
2618 multi_strip_robots(Player_num);
2621 extern ubyte Secondary_weapon_to_powerup[];
2622 extern ubyte Primary_weapon_to_powerup[];
2624 // put a lid on how many objects will be spewed by an exploding player
2625 // to prevent rampant powerups in netgames
2627 void multi_cap_objects ()
2632 if (!(Game_mode & GM_NETWORK))
2635 for (index=0;index<MAX_PRIMARY_WEAPONS;index++)
2637 type=Primary_weapon_to_powerup[index];
2638 if (PowerupsInMine[(int)type]>=MaxPowerupsAllowed[(int)type])
2639 if(Players[Player_num].primary_weapon_flags & (1 << index))
2641 mprintf ((0,"PIM=%d MPA=%d\n",PowerupsInMine[(int)type],MaxPowerupsAllowed[(int)type]));
2642 mprintf ((0,"Killing a primary cuz there's too many! (%d)\n",(int)type));
2643 Players[Player_num].primary_weapon_flags&=(~(1 << index));
2648 // Don't do the adjustment stuff for Hoard mode
2649 if (!(Game_mode & GM_HOARD))
2650 Players[Player_num].secondary_ammo[2]/=4;
2652 Players[Player_num].secondary_ammo[7]/=4;
2654 for (index=0;index<MAX_SECONDARY_WEAPONS;index++)
2656 if ((Game_mode & GM_HOARD) && index==PROXIMITY_INDEX)
2659 type=Secondary_weapon_to_powerup[index];
2661 if ((Players[Player_num].secondary_ammo[index]+PowerupsInMine[(int)type])>MaxPowerupsAllowed[(int)type])
2663 if (MaxPowerupsAllowed[(int)type]-PowerupsInMine[(int)type]<0)
2664 Players[Player_num].secondary_ammo[index]=0;
2666 Players[Player_num].secondary_ammo[index]=(MaxPowerupsAllowed[(int)type]-PowerupsInMine[(int)type]);
2668 mprintf ((0,"Hey! I killed secondary type %d because PIM=%d MPA=%d\n",(int)type,PowerupsInMine[(int)type],MaxPowerupsAllowed[(int)type]));
2672 if (!(Game_mode & GM_HOARD))
2673 Players[Player_num].secondary_ammo[2]*=4;
2674 Players[Player_num].secondary_ammo[7]*=4;
2676 if (Players[Player_num].laser_level > MAX_LASER_LEVEL)
2677 if (PowerupsInMine[POW_SUPER_LASER]+1 > MaxPowerupsAllowed[POW_SUPER_LASER])
2678 Players[Player_num].laser_level=0;
2680 if (Players[Player_num].flags & PLAYER_FLAGS_QUAD_LASERS)
2681 if (PowerupsInMine[POW_QUAD_FIRE]+1 > MaxPowerupsAllowed[POW_QUAD_FIRE])
2682 Players[Player_num].flags&=(~PLAYER_FLAGS_QUAD_LASERS);
2684 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
2685 if (PowerupsInMine[POW_CLOAK]+1 > MaxPowerupsAllowed[POW_CLOAK])
2686 Players[Player_num].flags&=(~PLAYER_FLAGS_CLOAKED);
2688 if (Players[Player_num].flags & PLAYER_FLAGS_MAP_ALL)
2689 if (PowerupsInMine[POW_FULL_MAP]+1 > MaxPowerupsAllowed[POW_FULL_MAP])
2690 Players[Player_num].flags&=(~PLAYER_FLAGS_MAP_ALL);
2692 if (Players[Player_num].flags & PLAYER_FLAGS_AFTERBURNER)
2693 if (PowerupsInMine[POW_AFTERBURNER]+1 > MaxPowerupsAllowed[POW_AFTERBURNER])
2694 Players[Player_num].flags&=(~PLAYER_FLAGS_AFTERBURNER);
2696 if (Players[Player_num].flags & PLAYER_FLAGS_AMMO_RACK)
2697 if (PowerupsInMine[POW_AMMO_RACK]+1 > MaxPowerupsAllowed[POW_AMMO_RACK])
2698 Players[Player_num].flags&=(~PLAYER_FLAGS_AMMO_RACK);
2700 if (Players[Player_num].flags & PLAYER_FLAGS_CONVERTER)
2701 if (PowerupsInMine[POW_CONVERTER]+1 > MaxPowerupsAllowed[POW_CONVERTER])
2702 Players[Player_num].flags&=(~PLAYER_FLAGS_CONVERTER);
2704 if (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT)
2705 if (PowerupsInMine[POW_HEADLIGHT]+1 > MaxPowerupsAllowed[POW_HEADLIGHT])
2706 Players[Player_num].flags&=(~PLAYER_FLAGS_HEADLIGHT);
2708 if (Game_mode & GM_CAPTURE)
2710 if (Players[Player_num].flags & PLAYER_FLAGS_FLAG)
2712 if (get_team(Player_num)==TEAM_RED)
2713 flagtype=POW_FLAG_BLUE;
2715 flagtype=POW_FLAG_RED;
2717 if (PowerupsInMine[(int)flagtype]+1 > MaxPowerupsAllowed[(int)flagtype])
2718 Players[Player_num].flags&=(~PLAYER_FLAGS_FLAG);
2724 // adds players inventory to multi cap
2726 void multi_adjust_cap_for_player (int pnum)
2732 if (!(Game_mode & GM_NETWORK))
2735 for (index=0;index<MAX_PRIMARY_WEAPONS;index++)
2737 type=Primary_weapon_to_powerup[index];
2738 if (Players[pnum].primary_weapon_flags & (1 << index))
2739 MaxPowerupsAllowed[(int)type]++;
2742 for (index=0;index<MAX_SECONDARY_WEAPONS;index++)
2744 type=Secondary_weapon_to_powerup[index];
2745 MaxPowerupsAllowed[(int)type]+=Players[pnum].secondary_ammo[index];
2748 if (Players[pnum].laser_level > MAX_LASER_LEVEL)
2749 MaxPowerupsAllowed[POW_SUPER_LASER]++;
2751 if (Players[pnum].flags & PLAYER_FLAGS_QUAD_LASERS)
2752 MaxPowerupsAllowed[POW_QUAD_FIRE]++;
2754 if (Players[pnum].flags & PLAYER_FLAGS_CLOAKED)
2755 MaxPowerupsAllowed[POW_CLOAK]++;
2757 if (Players[pnum].flags & PLAYER_FLAGS_MAP_ALL)
2758 MaxPowerupsAllowed[POW_FULL_MAP]++;
2760 if (Players[pnum].flags & PLAYER_FLAGS_AFTERBURNER)
2761 MaxPowerupsAllowed[POW_AFTERBURNER]++;
2763 if (Players[pnum].flags & PLAYER_FLAGS_AMMO_RACK)
2764 MaxPowerupsAllowed[POW_AMMO_RACK]++;
2766 if (Players[pnum].flags & PLAYER_FLAGS_CONVERTER)
2767 MaxPowerupsAllowed[POW_CONVERTER]++;
2769 if (Players[pnum].flags & PLAYER_FLAGS_HEADLIGHT)
2770 MaxPowerupsAllowed[POW_HEADLIGHT]++;
2773 void multi_adjust_remote_cap (int pnum)
2779 if (!(Game_mode & GM_NETWORK))
2782 for (index=0;index<MAX_PRIMARY_WEAPONS;index++)
2784 type=Primary_weapon_to_powerup[index];
2785 if (Players[pnum].primary_weapon_flags & (1 << index))
2786 PowerupsInMine[(int)type]++;
2789 for (index=0;index<MAX_SECONDARY_WEAPONS;index++)
2791 type=Secondary_weapon_to_powerup[index];
2793 if ((Game_mode & GM_HOARD) && index==2)
2796 if (index==2 || index==7) // PROX or SMARTMINES? Those bastards...
2797 PowerupsInMine[(int)type]+=(Players[pnum].secondary_ammo[index]/4);
2799 PowerupsInMine[(int)type]+=Players[pnum].secondary_ammo[index];
2803 if (Players[pnum].laser_level > MAX_LASER_LEVEL)
2804 PowerupsInMine[POW_SUPER_LASER]++;
2806 if (Players[pnum].flags & PLAYER_FLAGS_QUAD_LASERS)
2807 PowerupsInMine[POW_QUAD_FIRE]++;
2809 if (Players[pnum].flags & PLAYER_FLAGS_CLOAKED)
2810 PowerupsInMine[POW_CLOAK]++;
2812 if (Players[pnum].flags & PLAYER_FLAGS_MAP_ALL)
2813 PowerupsInMine[POW_FULL_MAP]++;
2815 if (Players[pnum].flags & PLAYER_FLAGS_AFTERBURNER)
2816 PowerupsInMine[POW_AFTERBURNER]++;
2818 if (Players[pnum].flags & PLAYER_FLAGS_AMMO_RACK)
2819 PowerupsInMine[POW_AMMO_RACK]++;
2821 if (Players[pnum].flags & PLAYER_FLAGS_CONVERTER)
2822 PowerupsInMine[POW_CONVERTER]++;
2824 if (Players[pnum].flags & PLAYER_FLAGS_HEADLIGHT)
2825 PowerupsInMine[POW_HEADLIGHT]++;
2830 multi_send_message(void)
2833 if (Network_message_reciever != -1)
2835 multibuf[loc] = (char)MULTI_MESSAGE; loc += 1;
2836 multibuf[loc] = (char)Player_num; loc += 1;
2837 strncpy(multibuf+loc, Network_message, MAX_MESSAGE_LEN); loc += MAX_MESSAGE_LEN;
2838 multibuf[loc-1] = '\0';
2839 multi_send_data(multibuf, loc, 0);
2840 Network_message_reciever = -1;
2845 multi_send_reappear()
2847 multibuf[0] = (char)MULTI_REAPPEAR;
2848 PUT_INTEL_SHORT(multibuf+1, Players[Player_num].objnum);
2850 multi_send_data(multibuf, 3, 2);
2851 PKilledFlags[Player_num]=0;
2855 multi_send_position(int objnum)
2857 #ifdef WORDS_BIGENDIAN
2862 if (Game_mode & GM_NETWORK) {
2866 multibuf[count++] = (char)MULTI_POSITION;
2867 #ifndef WORDS_BIGENDIAN
2868 create_shortpos((shortpos *)(multibuf+count), Objects+objnum,0);
2869 count += sizeof(shortpos);
2871 create_shortpos(&sp, Objects+objnum, 1);
2872 memcpy(&(multibuf[count]), (ubyte *)(sp.bytemat), 9);
2874 memcpy(&(multibuf[count]), (ubyte *)&(sp.xo), 14);
2878 multi_send_data(multibuf, count, 0);
2882 multi_send_kill(int objnum)
2884 // I died, tell the world.
2889 Assert(Objects[objnum].id == Player_num);
2890 killer_objnum = Players[Player_num].killer_objnum;
2892 multi_compute_kill(killer_objnum, objnum);
2894 multibuf[0] = (char)MULTI_KILL; count += 1;
2895 multibuf[1] = Player_num; count += 1;
2896 if (killer_objnum > -1) {
2897 short s; // do it with variable since INTEL_SHORT won't work on return val from function.
2899 s = (short)objnum_local_to_remote(killer_objnum, (sbyte *)&multibuf[count+2]);
2900 PUT_INTEL_SHORT(multibuf+count, s);
2904 PUT_INTEL_SHORT(multibuf+count, -1);
2905 multibuf[count+2] = (char)-1;
2908 multi_send_data(multibuf, count, 1);
2911 if (Game_mode & GM_MULTI_ROBOTS)
2912 multi_strip_robots(Player_num);
2917 multi_send_remobj(int objnum)
2919 // Tell the other guy to remove an object from his list
2922 short remote_objnum;
2924 if (Objects[objnum].type==OBJ_POWERUP && (Game_mode & GM_NETWORK))
2926 if (PowerupsInMine[Objects[objnum].id] > 0)
2928 PowerupsInMine[Objects[objnum].id]--;
2929 if (multi_powerup_is_4pack (Objects[objnum].id))
2931 mprintf ((0,"Hey babe! Doing that wacky 4 pack stuff."));
2933 if (PowerupsInMine[Objects[objnum].id-1]-4<0)
2934 PowerupsInMine[Objects[objnum].id-1]=0;
2936 PowerupsInMine[Objects[objnum].id-1]-=4;
2942 multibuf[0] = (char)MULTI_REMOVE_OBJECT;
2944 remote_objnum = objnum_local_to_remote((short)objnum, &obj_owner);
2946 PUT_INTEL_SHORT(multibuf+1, remote_objnum); // Map to network objnums
2948 multibuf[3] = obj_owner;
2950 // mprintf((0, "multi_send_remobj: %d = %d owner %d.\n", objnum, remote_objnum, obj_owner));
2952 multi_send_data(multibuf, 4, 0);
2954 if (Network_send_objects && network_objnum_is_past(objnum))
2956 mprintf((0, "Resetting object sync due to object removal.\n"));
2957 Network_send_objnum = -1;
2962 multi_send_quit(int why)
2964 // I am quitting the game, tell the other guy the bad news.
2966 Assert (why == MULTI_QUIT);
2968 multibuf[0] = (char)why;
2969 multibuf[1] = Player_num;
2970 multi_send_data(multibuf, 2, 1);
2975 multi_send_cloak(void)
2977 // Broadcast a change in our pflags (made to support cloaking)
2979 multibuf[0] = MULTI_CLOAK;
2980 multibuf[1] = (char)Player_num;
2982 multi_send_data(multibuf, 2, 1);
2985 if (Game_mode & GM_MULTI_ROBOTS)
2986 multi_strip_robots(Player_num);
2991 multi_send_decloak(void)
2993 // Broadcast a change in our pflags (made to support cloaking)
2995 multibuf[0] = MULTI_DECLOAK;
2996 multibuf[1] = (char)Player_num;
2998 multi_send_data(multibuf, 2, 1);
3002 multi_send_door_open(int segnum, int side,ubyte flag)
3004 // When we open a door make sure everyone else opens that door
3006 multibuf[0] = MULTI_DOOR_OPEN;
3007 PUT_INTEL_SHORT(multibuf+1, segnum );
3008 multibuf[3] = (sbyte)side;
3011 multi_send_data(multibuf, 5, 2);
3014 extern void network_send_naked_packet (char *,short,int);
3017 multi_send_door_open_specific(int pnum,int segnum, int side,ubyte flag)
3019 // For sending doors only to a specific person (usually when they're joining)
3021 Assert (Game_mode & GM_NETWORK);
3022 // Assert (pnum>-1 && pnum<N_players);
3024 multibuf[0] = MULTI_DOOR_OPEN;
3025 PUT_INTEL_SHORT(multibuf+1, segnum);
3026 multibuf[3] = (sbyte)side;
3029 network_send_naked_packet(multibuf, 5, pnum);
3033 // Part 3 : Functions that change or prepare the game for multiplayer use.
3034 // Not including functions needed to syncronize or start the
3035 // particular type of multiplayer game. Includes preparing the
3036 // mines, player structures, etc.
3039 multi_send_create_explosion(int pnum)
3041 // Send all data needed to create a remote explosion
3045 multibuf[count] = MULTI_CREATE_EXPLOSION; count += 1;
3046 multibuf[count] = (sbyte)pnum; count += 1;
3050 multi_send_data(multibuf, count, 0);
3054 multi_send_controlcen_fire(vms_vector *to_goal, int best_gun_num, int objnum)
3056 #ifdef WORDS_BIGENDIAN
3057 vms_vector swapped_vec;
3061 multibuf[count] = MULTI_CONTROLCEN_FIRE; count += 1;
3062 #ifndef WORDS_BIGENDIAN
3063 memcpy(multibuf+count, to_goal, 12); count += 12;
3065 swapped_vec.x = (fix)INTEL_INT( (int)to_goal->x );
3066 swapped_vec.y = (fix)INTEL_INT( (int)to_goal->y );
3067 swapped_vec.z = (fix)INTEL_INT( (int)to_goal->z );
3068 memcpy(multibuf+count, &swapped_vec, 12); count += 12;
3070 multibuf[count] = (char)best_gun_num; count += 1;
3071 PUT_INTEL_SHORT(multibuf+count, objnum ); count += 2;
3074 multi_send_data(multibuf, count, 0);
3078 multi_send_create_powerup(int powerup_type, int segnum, int objnum, vms_vector *pos)
3080 // Create a powerup on a remote machine, used for remote
3081 // placement of used powerups like missiles and cloaking
3084 #ifdef WORDS_BIGENDIAN
3085 vms_vector swapped_vec;
3089 if (Game_mode & GM_NETWORK)
3090 PowerupsInMine[powerup_type]++;
3092 multibuf[count] = MULTI_CREATE_POWERUP; count += 1;
3093 multibuf[count] = Player_num; count += 1;
3094 multibuf[count] = powerup_type; count += 1;
3095 PUT_INTEL_SHORT(multibuf+count, segnum ); count += 2;
3096 PUT_INTEL_SHORT(multibuf+count, objnum ); count += 2;
3097 #ifndef WORDS_BIGENDIAN
3098 memcpy(multibuf+count, pos, sizeof(vms_vector)); count += sizeof(vms_vector);
3100 swapped_vec.x = (fix)INTEL_INT( (int)pos->x );
3101 swapped_vec.y = (fix)INTEL_INT( (int)pos->y );
3102 swapped_vec.z = (fix)INTEL_INT( (int)pos->z );
3103 memcpy(multibuf+count, &swapped_vec, 12); count += 12;
3107 multi_send_data(multibuf, count, 2);
3109 if (Network_send_objects && network_objnum_is_past(objnum))
3111 mprintf((0, "Resetting object sync due to powerup creation.\n"));
3112 Network_send_objnum = -1;
3115 mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));
3116 map_objnum_local_to_local(objnum);
3120 multi_send_play_sound(int sound_num, fix volume)
3123 multibuf[count] = MULTI_PLAY_SOUND; count += 1;
3124 multibuf[count] = Player_num; count += 1;
3125 multibuf[count] = (char)sound_num; count += 1;
3126 multibuf[count] = (char)(volume >> 12); count += 1;
3129 multi_send_data(multibuf, count, 0);
3133 multi_send_audio_taunt(int taunt_num)
3135 return; // Taken out, awaiting sounds..
3138 int audio_taunts[4] = {
3139 SOUND_CONTROL_CENTER_WARNING_SIREN,
3140 SOUND_HOSTAGE_RESCUED,
3141 SOUND_REFUEL_STATION_GIVING_FUEL,
3146 Assert(taunt_num >= 0);
3147 Assert(taunt_num < 4);
3149 digi_play_sample( audio_taunts[taunt_num], F1_0 );
3150 multi_send_play_sound(audio_taunts[taunt_num], F1_0);
3155 multi_send_score(void)
3157 // Send my current score to all other players so it will remain
3161 if (Game_mode & GM_MULTI_COOP) {
3162 multi_sort_kill_list();
3163 multibuf[count] = MULTI_SCORE; count += 1;
3164 multibuf[count] = Player_num; count += 1;
3165 PUT_INTEL_INT(multibuf+count, Players[Player_num].score); count += 4;
3166 multi_send_data(multibuf, count, 0);
3172 multi_send_save_game(ubyte slot, uint id, char * desc)
3176 multibuf[count] = MULTI_SAVE_GAME; count += 1;
3177 multibuf[count] = slot; count += 1; // Save slot=0
3178 PUT_INTEL_INT(multibuf+count, id ); count += 4; // Save id
3179 memcpy( &multibuf[count], desc, 20 ); count += 20;
3181 multi_send_data(multibuf, count, 2);
3185 multi_send_restore_game(ubyte slot, uint id)
3189 multibuf[count] = MULTI_RESTORE_GAME; count += 1;
3190 multibuf[count] = slot; count += 1; // Save slot=0
3191 PUT_INTEL_INT(multibuf+count, id); count += 4; // Save id
3193 multi_send_data(multibuf, count, 2);
3197 multi_send_netplayer_stats_request(ubyte player_num)
3201 multibuf[count] = MULTI_REQ_PLAYER; count += 1;
3202 multibuf[count] = player_num; count += 1;
3204 multi_send_data(multibuf, count, 0 );
3208 multi_send_trigger(int triggernum)
3210 // Send an even to trigger something in the mine
3214 multibuf[count] = MULTI_TRIGGER; count += 1;
3215 multibuf[count] = Player_num; count += 1;
3216 multibuf[count] = (ubyte)triggernum; count += 1;
3218 mprintf ((0,"Sending trigger %d\n",triggernum));
3220 multi_send_data(multibuf, count, 1);
3221 //multi_send_data(multibuf, count, 1); // twice?
3225 multi_send_hostage_door_status(int wallnum)
3227 // Tell the other player what the hit point status of a hostage door
3232 Assert(Walls[wallnum].type == WALL_BLASTABLE);
3234 multibuf[count] = MULTI_HOSTAGE_DOOR; count += 1;
3235 PUT_INTEL_SHORT(multibuf+count, wallnum ); count += 2;
3236 PUT_INTEL_INT(multibuf+count, Walls[wallnum].hps ); count += 4;
3238 // mprintf((0, "Door %d damaged by %f points.\n", wallnum, f2fl(Walls[wallnum].hps)));
3240 multi_send_data(multibuf, count, 0);
3243 extern int ConsistencyCount;
3244 extern int Drop_afterburner_blob_flag;
3248 void multi_prep_level(void)
3250 // Do any special stuff to the level required for serial games
3251 // before we begin playing in it.
3253 // Player_num MUST be set before calling this procedure.
3255 // This function must be called before checksuming the Object array,
3256 // since the resulting checksum with depend on the value of Player_num
3257 // at the time this is called.
3260 int cloak_count, inv_count;
3262 Assert(Game_mode & GM_MULTI);
3264 Assert(NumNetPlayerPositions > 0);
3268 Drop_afterburner_blob_flag=0;
3271 for (i=0;i<MAX_NUM_NET_PLAYERS;i++)
3274 for (i = 0; i < NumNetPlayerPositions; i++)
3276 if (i != Player_num)
3277 Objects[Players[i].objnum].control_type = CT_REMOTE;
3278 Objects[Players[i].objnum].movement_type = MT_PHYSICS;
3279 multi_reset_player_object(&Objects[Players[i].objnum]);
3280 LastPacketTime[i] = 0;
3284 for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
3286 robot_controlled[i] = -1;
3287 robot_agitation[i] = 0;
3292 Viewer = ConsoleObject = &Objects[Players[Player_num].objnum];
3294 if (!(Game_mode & GM_MULTI_COOP))
3296 multi_delete_extra_objects(); // Removes monsters from level
3299 if (Game_mode & GM_MULTI_ROBOTS)
3301 multi_set_robot_ai(); // Set all Robot AI to types we can cope with
3304 if (Game_mode & GM_NETWORK)
3306 multi_adjust_cap_for_player(Player_num);
3307 multi_send_powerup_update();
3308 ng=1; // ng means network game
3314 for (i=0; i<=Highest_object_index; i++)
3318 if ((Objects[i].type == OBJ_HOSTAGE) && !(Game_mode & GM_MULTI_COOP))
3320 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);
3324 Objects[objnum].rtype.vclip_info.vclip_num = Powerup_info[POW_SHIELD_BOOST].vclip_num;
3325 Objects[objnum].rtype.vclip_info.frametime = Vclip[Objects[objnum].rtype.vclip_info.vclip_num].frame_time;
3326 Objects[objnum].rtype.vclip_info.framenum = 0;
3327 Objects[objnum].mtype.phys_info.drag = 512; //1024;
3328 Objects[objnum].mtype.phys_info.mass = F1_0;
3329 vm_vec_zero(&Objects[objnum].mtype.phys_info.velocity);
3334 if (Objects[i].type == OBJ_POWERUP)
3336 if (Objects[i].id == POW_EXTRA_LIFE)
3338 if (ng && !Netgame.DoInvulnerability)
3340 Objects[i].id = POW_SHIELD_BOOST;
3341 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3342 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3346 Objects[i].id = POW_INVULNERABILITY;
3347 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3348 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3353 if (!(Game_mode & GM_MULTI_COOP))
3354 if ((Objects[i].id >= POW_KEY_BLUE) && (Objects[i].id <= POW_KEY_GOLD))
3356 Objects[i].id = POW_SHIELD_BOOST;
3357 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3358 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3361 if (Objects[i].id == POW_INVULNERABILITY) {
3362 if (inv_count >= 3 || (ng && !Netgame.DoInvulnerability)) {
3363 mprintf((0, "Bashing Invulnerability object #%i to shield.\n", i));
3364 Objects[i].id = POW_SHIELD_BOOST;
3365 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3366 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3371 if (Objects[i].id == POW_CLOAK) {
3372 if (cloak_count >= 3 || (ng && !Netgame.DoCloak)) {
3373 mprintf((0, "Bashing Cloak object #%i to shield.\n", i));
3374 Objects[i].id = POW_SHIELD_BOOST;
3375 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3376 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3381 if (Objects[i].id == POW_AFTERBURNER && ng && !Netgame.DoAfterburner)
3382 bash_to_shield (i,"afterburner");
3383 if (Objects[i].id == POW_FUSION_WEAPON && ng && !Netgame.DoFusions)
3384 bash_to_shield (i,"fusion");
3385 if (Objects[i].id == POW_PHOENIX_WEAPON && ng && !Netgame.DoPhoenix)
3386 bash_to_shield (i,"phoenix");
3388 if (Objects[i].id == POW_HELIX_WEAPON && ng && !Netgame.DoHelix)
3389 bash_to_shield (i,"helix");
3391 if (Objects[i].id == POW_MEGA_WEAPON && ng && !Netgame.DoMegas)
3392 bash_to_shield (i,"mega");
3394 if (Objects[i].id == POW_SMARTBOMB_WEAPON && ng && !Netgame.DoSmarts)
3395 bash_to_shield (i,"smartmissile");
3397 if (Objects[i].id == POW_GAUSS_WEAPON && ng && !Netgame.DoGauss)
3398 bash_to_shield (i,"gauss");
3400 if (Objects[i].id == POW_VULCAN_WEAPON && ng && !Netgame.DoVulcan)
3401 bash_to_shield (i,"vulcan");
3403 if (Objects[i].id == POW_PLASMA_WEAPON && ng && !Netgame.DoPlasma)
3404 bash_to_shield (i,"plasma");
3406 if (Objects[i].id == POW_OMEGA_WEAPON && ng && !Netgame.DoOmega)
3407 bash_to_shield (i,"omega");
3409 if (Objects[i].id == POW_SUPER_LASER && ng && !Netgame.DoSuperLaser)
3410 bash_to_shield (i,"superlaser");
3412 if (Objects[i].id == POW_PROXIMITY_WEAPON && ng && !Netgame.DoProximity)
3413 bash_to_shield (i,"proximity");
3415 // Special: Make all proximity bombs into shields if in
3416 // hoard mode because we use the proximity slot in the
3417 // player struct to signify how many orbs the player has.
3419 if (Objects[i].id == POW_PROXIMITY_WEAPON && ng && (Game_mode & GM_HOARD))
3420 bash_to_shield (i,"proximity");
3422 if (Objects[i].id==POW_VULCAN_AMMO && ng && (!Netgame.DoVulcan && !Netgame.DoGauss))
3423 bash_to_shield(i,"vulcan ammo");
3425 if (Objects[i].id == POW_SPREADFIRE_WEAPON && ng && !Netgame.DoSpread)
3426 bash_to_shield (i,"spread");
3427 if (Objects[i].id == POW_SMART_MINE && ng && !Netgame.DoSmartMine)
3428 bash_to_shield (i,"smartmine");
3429 if (Objects[i].id == POW_SMISSILE1_1 && ng && !Netgame.DoFlash)
3430 bash_to_shield (i,"flash");
3431 if (Objects[i].id == POW_SMISSILE1_4 && ng && !Netgame.DoFlash)
3432 bash_to_shield (i,"flash");
3433 if (Objects[i].id == POW_GUIDED_MISSILE_1 && ng && !Netgame.DoGuided)
3434 bash_to_shield (i,"guided");
3435 if (Objects[i].id == POW_GUIDED_MISSILE_4 && ng && !Netgame.DoGuided)
3436 bash_to_shield (i,"guided");
3437 if (Objects[i].id == POW_EARTHSHAKER_MISSILE && ng && !Netgame.DoEarthShaker)
3438 bash_to_shield (i,"earth");
3439 if (Objects[i].id == POW_MERCURY_MISSILE_1 && ng && !Netgame.DoMercury)
3440 bash_to_shield (i,"Mercury");
3441 if (Objects[i].id == POW_MERCURY_MISSILE_4 && ng && !Netgame.DoMercury)
3442 bash_to_shield (i,"Mercury");
3443 if (Objects[i].id == POW_CONVERTER && ng && !Netgame.DoConverter)
3444 bash_to_shield (i,"Converter");
3445 if (Objects[i].id == POW_AMMO_RACK && ng && !Netgame.DoAmmoRack)
3446 bash_to_shield (i,"Ammo rack");
3447 if (Objects[i].id == POW_HEADLIGHT && ng && !Netgame.DoHeadlight)
3448 bash_to_shield (i,"Headlight");
3449 if (Objects[i].id == POW_LASER && ng && !Netgame.DoLaserUpgrade)
3450 bash_to_shield (i,"Laser powerup");
3451 if (Objects[i].id == POW_HOMING_AMMO_1 && ng && !Netgame.DoHoming)
3452 bash_to_shield (i,"Homing");
3453 if (Objects[i].id == POW_HOMING_AMMO_4 && ng && !Netgame.DoHoming)
3454 bash_to_shield (i,"Homing");
3455 if (Objects[i].id == POW_QUAD_FIRE && ng && !Netgame.DoQuadLasers)
3456 bash_to_shield (i,"Quad Lasers");
3457 if (Objects[i].id == POW_FLAG_BLUE && !(Game_mode & GM_CAPTURE))
3458 bash_to_shield (i,"Blue flag");
3459 if (Objects[i].id == POW_FLAG_RED && !(Game_mode & GM_CAPTURE))
3460 bash_to_shield (i,"Red flag");
3464 if (Game_mode & GM_HOARD)
3467 if ((Game_mode & GM_CAPTURE) || (Game_mode & GM_HOARD))
3468 multi_apply_goal_textures();
3470 multi_sort_kill_list();
3472 multi_show_player_list();
3474 ConsoleObject->control_type = CT_FLYING;
3476 reset_player_object();
3480 int Goal_blue_segnum,Goal_red_segnum;
3482 void multi_apply_goal_textures()
3488 for (i=0; i <= Highest_segment_index; i++)
3491 seg2 = &Segment2s[i];
3493 if (seg2->special==SEGMENT_IS_GOAL_BLUE)
3496 Goal_blue_segnum = i;
3498 if (Game_mode & GM_HOARD)
3499 tex=find_goal_texture (TMI_GOAL_HOARD);
3501 tex=find_goal_texture (TMI_GOAL_BLUE);
3504 for (j = 0; j < 6; j++) {
3506 seg->sides[j].tmap_num=tex;
3508 seg->sides[j].uvls[v].l = i2f(100); //max out
3511 seg2->static_light = i2f(100); //make static light bright
3515 if (seg2->special==SEGMENT_IS_GOAL_RED)
3517 Goal_red_segnum = i;
3519 // Make both textures the same if Hoard mode
3521 if (Game_mode & GM_HOARD)
3522 tex=find_goal_texture (TMI_GOAL_HOARD);
3524 tex=find_goal_texture (TMI_GOAL_RED);
3527 for (j = 0; j < 6; j++) {
3529 seg->sides[j].tmap_num=tex;
3531 seg->sides[j].uvls[v].l = i2f(1000); //max out
3534 seg2->static_light = i2f(100); //make static light bright
3538 int find_goal_texture (ubyte t)
3542 for (i=0;i<NumTextures;i++)
3543 if (TmapInfo[i].flags & t)
3546 Int3(); // Hey, there is no goal texture for this PIG!!!!
3547 // Edit bitmaps.tbl and designate two textures to be RED and BLUE