2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
16 * Multiplayer code shared by serial and network play.
43 void multi_reset_player_object(object *objp);
44 void multi_reset_object_texture(object *objp);
45 void multi_add_lifetime_killed(void);
46 void multi_add_lifetime_kills(void);
47 void multi_send_play_by_play(int num,int spnum,int dpnum);
48 void multi_send_heartbeat(void);
49 void multi_send_modem_ping(void);
50 void multi_cap_objects(void);
51 void multi_adjust_remote_cap(int pnum);
52 void multi_save_game(ubyte slot, uint id, char *desc);
53 void multi_restore_game(ubyte slot, uint id);
54 void multi_set_robot_ai(void);
55 void multi_send_powerup_update(void);
56 void bash_to_shield(int i,char *s);
57 void init_hoard_data(void);
58 void multi_apply_goal_textures(void);
59 int find_goal_texture(ubyte t);
60 void multi_bad_restore(void);
61 void multi_do_capture_bonus(char *buf);
62 void multi_do_orb_bonus(char *buf);
63 void multi_send_drop_flag(int objnum,int seed);
64 void multi_send_ranking(void);
65 void multi_do_play_by_play(char *buf);
68 // Local macros and prototypes
73 #define vm_angvec_zero(v) (v)->p=(v)->b=(v)->h=0
75 void drop_player_eggs(object *player); // from collide.c
76 void GameLoop(int, int); // From game.c
82 extern vms_vector MarkerPoint[];
84 int control_invul_time = 0;
85 int who_killed_controlcen = -1; // -1 = noone
87 //do we draw the kill list on the HUD?
88 int Show_kill_list = 1;
89 int Show_reticle_name = 1;
90 fix Show_kill_list_timer = 0;
92 char Multi_is_guided=0;
93 char PKilledFlags[MAX_NUM_NET_PLAYERS];
95 int multi_sending_message = 0;
96 int multi_defining_message = 0;
97 int multi_message_index = 0;
99 char multibuf[MAX_MULTI_MESSAGE_LEN+4]; // This is where multiplayer message are built
101 short remote_to_local[MAX_NUM_NET_PLAYERS][MAX_OBJECTS]; // Remote object number for each local object
102 short local_to_remote[MAX_OBJECTS];
103 sbyte object_owner[MAX_OBJECTS]; // Who created each object in my universe, -1 = loaded at start
105 int Net_create_objnums[MAX_NET_CREATE_OBJECTS]; // For tracking object creation that will be sent to remote
106 int Net_create_loc = 0; // pointer into previous array
107 int Network_laser_fired = 0; // How many times we shot
108 int Network_laser_gun; // Which gun number we shot
109 int Network_laser_flags; // Special flags for the shot
110 int Network_laser_level; // What level
111 short Network_laser_track; // Who is it tracking?
112 char Network_message[MAX_MESSAGE_LEN];
113 cvar_t Network_message_macro[4] = {
114 { "TauntMacro1", "", CVAR_ARCHIVE },
115 { "TauntMacro2", "", CVAR_ARCHIVE },
116 { "TauntMacro3", "", CVAR_ARCHIVE },
117 { "TauntMacro4", "", CVAR_ARCHIVE },
119 int Network_message_reciever=-1;
120 int sorted_kills[MAX_NUM_NET_PLAYERS];
121 short kill_matrix[MAX_NUM_NET_PLAYERS][MAX_NUM_NET_PLAYERS];
122 int multi_goto_secret = 0;
124 int multi_in_menu = 0;
125 int multi_leave_menu = 0;
126 int multi_quit_game = 0;
128 netgame_info Netgame;
129 AllNetPlayers_info NetPlayers;
131 bitmap_index multi_player_textures[MAX_NUM_NET_PLAYERS][N_PLAYER_SHIP_TEXTURES];
133 typedef struct netplayer_stats {
135 ubyte Player_num; // Who am i?
136 uint flags; // Powerup flags, see below...
137 fix energy; // Amount of energy remaining.
138 fix shields; // shields remaining (protection)
139 ubyte lives; // Lives remaining, 0 = game over.
140 ubyte laser_level; // Current level of the laser.
141 ubyte primary_weapon_flags; // bit set indicates the player has this weapon.
142 ubyte secondary_weapon_flags; // bit set indicates the player has this weapon.
143 ushort primary_ammo[MAX_PRIMARY_WEAPONS]; // How much ammo of each type.
144 ushort secondary_ammo[MAX_SECONDARY_WEAPONS]; // How much ammo of each type.
145 int last_score; // Score at beginning of current level.
146 int score; // Current score.
147 fix cloak_time; // Time cloaked
148 fix invulnerable_time; // Time invulnerable
149 fix homing_object_dist; // Distance of nearest homing object.
151 short net_killed_total; // Number of times killed total
152 short net_kills_total; // Number of net kills total
153 short num_kills_level; // Number of kills this level
154 short num_kills_total; // Number of kills total
155 short num_robots_level; // Number of initial robots this level
156 short num_robots_total; // Number of robots total
157 ushort hostages_rescued_total; // Total number of hostages rescued.
158 ushort hostages_total; // Total number of hostages.
159 ubyte hostages_on_board; // Number of hostages on ship.
163 static const int message_length[MULTI_MAX_TYPE+1] = {
169 97+9, // PLAYER_EXPLODE
170 37, // MESSAGE (MAX_MESSAGE_LENGTH = 40)
180 2, // CREATE_EXPLOSION
181 16, // CONTROLCEN_FIRE
183 19, // CREATE_POWERUP
187 28, // ROBOT_POSITION (shortpos_length (23) + 5 = 28)
195 27, // ROBOT_POWERUPS
197 2+24, // SAVE_GAME (ubyte slot, uint id, char name[20])
198 2+4, // RESTORE_GAME (ubyte slot, uint id)
199 1+1, // MULTI_REQ_PLAYER
200 sizeof(netplayer_stats), // MULTI_SEND_PLAYER
202 12, // MULTI_DROP_WEAPON
204 3+sizeof(shortpos), // MULTI_GUIDED, IF SHORTPOS CHANGES, CHANGE MAC VALUE BELOW
206 26, // MULTI_GUIDED IF SIZE OF SHORTPOS CHANGES, CHANGE THIS VALUE AS WELL!!!!!!
208 11, // MULTI_STOLEN_ITEMS
209 6, // MULTI_WALL_STATUS
210 5, // MULTI_HEARTBEAT
211 9, // MULTI_KILLGOALS
214 2, // MULTI_START_TRIGGER
216 2, // MULTI_DROP_BLOB
217 MAX_POWERUP_TYPES+1, // MULTI_POWERUP_UPDATE
218 sizeof(active_door)+3, // MULTI_ACTIVE_DOOR
219 4, // MULTI_SOUND_FUNCTION
220 2, // MULTI_CAPTURE_BONUS
222 12, // MULTI_DROP_FLAG
223 142, // MULTI_ROBOT_CONTROLS
224 2, // MULTI_FINISH_GAME
226 1, // MULTI_MODEM_PING
227 1, // MULTI_MODEM_PING_RETURN
228 3, // MULTI_ORB_BONUS
230 12, // MULTI_DROP_ORB
231 4, // MULTI_PLAY_BY_PLAY
234 void extract_netplayer_stats( netplayer_stats *ps, player * pd );
235 void use_netplayer_stats( player * ps, netplayer_stats *pd );
236 char PowerupsInMine[MAX_POWERUP_TYPES],MaxPowerupsAllowed[MAX_POWERUP_TYPES];
237 extern fix ThisLevelTime;
240 // Functions that replace what used to be macros
243 int objnum_remote_to_local(int remote_objnum, int owner)
245 // Map a remote object number from owner to a local object number
249 if ((owner >= N_players) || (owner < -1)) {
251 return(remote_objnum);
255 return(remote_objnum);
257 if ((remote_objnum < 0) || (remote_objnum >= MAX_OBJECTS))
260 result = remote_to_local[owner][remote_objnum];
264 mprintf((1, "Remote object owner %d number %d mapped to -1!\n", owner, remote_objnum));
269 if (object_owner[result] != owner)
271 mprintf((1, "Remote object owner %d number %d doesn't match owner %d.\n", owner, remote_objnum, object_owner[result]));
274 //Assert(object_owner[result] == owner);
279 int objnum_local_to_remote(int local_objnum, sbyte *owner)
281 // Map a local object number to a remote + owner
285 if ((local_objnum < 0) || (local_objnum > Highest_object_index)) {
290 *owner = object_owner[local_objnum];
293 return(local_objnum);
295 if ((*owner >= N_players) || (*owner < -1)) {
301 result = local_to_remote[local_objnum];
303 //mprintf((0, "Local object %d mapped to owner %d objnum %d.\n", local_objnum,
308 Int3(); // See Rob, object has no remote number!
315 map_objnum_local_to_remote(int local_objnum, int remote_objnum, int owner)
317 // Add a mapping from a network remote object number to a local one
319 Assert(local_objnum > -1);
320 Assert(local_objnum < MAX_OBJECTS);
321 Assert(remote_objnum > -1);
322 Assert(remote_objnum < MAX_OBJECTS);
324 Assert(owner != Player_num);
326 object_owner[local_objnum] = owner;
328 remote_to_local[owner][remote_objnum] = local_objnum;
329 local_to_remote[local_objnum] = remote_objnum;
335 map_objnum_local_to_local(int local_objnum)
337 // Add a mapping for our locally created objects
339 Assert(local_objnum > -1);
340 Assert(local_objnum < MAX_OBJECTS);
342 object_owner[local_objnum] = Player_num;
343 remote_to_local[Player_num][local_objnum] = local_objnum;
344 local_to_remote[local_objnum] = local_objnum;
349 void reset_network_objects()
351 memset(local_to_remote, -1, MAX_OBJECTS*sizeof(short));
352 memset(remote_to_local, -1, MAX_NUM_NET_PLAYERS*MAX_OBJECTS*sizeof(short));
353 memset(object_owner, -1, MAX_OBJECTS);
357 // Part 1 : functions whose main purpose in life is to divert the flow
358 // of execution to either network or serial specific code based
359 // on the curretn Game_mode value.
363 multi_endlevel_score(void)
368 // Show a score list to end of net players
370 // Save connect state and change to new connect state
372 if (Game_mode & GM_NETWORK)
374 old_connect = Players[Player_num].connected;
375 if (Players[Player_num].connected!=3)
376 Players[Player_num].connected = CONNECT_END_MENU;
377 Network_status = NETSTAT_ENDLEVEL;
382 // Do the actual screen we wish to show
384 Function_mode = FMODE_MENU;
386 kmatrix_view(Game_mode & GM_NETWORK);
388 Function_mode = FMODE_GAME;
390 // Restore connect state
392 if (Game_mode & GM_NETWORK)
394 Players[Player_num].connected = old_connect;
398 if (Game_mode & GM_MULTI_COOP)
401 for (i = 0; i < MaxNumNetPlayers; i++)
403 Players[i].flags &= ~(PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_GOLD_KEY);
405 for (i = 0; i < MaxNumNetPlayers; i++)
406 Players[i].flags &= ~(PLAYER_FLAGS_FLAG); // Clear capture flag
410 for (i=0;i<MAX_PLAYERS;i++)
411 Players[i].KillGoalCount=0;
413 for (i=0;i<MAX_POWERUP_TYPES;i++)
415 MaxPowerupsAllowed[i]=0;
424 if ((Game_mode & GM_CAPTURE) && ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)))
427 if (Netgame.team_vector & (1 << pnum))
433 extern void game_disable_cheats(void);
440 // Reset variables for a new net game
442 memset(kill_matrix, 0, MAX_NUM_NET_PLAYERS*MAX_NUM_NET_PLAYERS*2); // Clear kill matrix
444 for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
447 Players[i].net_killed_total = 0;
448 Players[i].net_kills_total = 0;
449 Players[i].flags = 0;
450 Players[i].KillGoalCount=0;
454 for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
456 robot_controlled[i] = -1;
457 robot_agitation[i] = 0;
462 team_kills[0] = team_kills[1] = 0;
463 Endlevel_sequence = 0;
465 multi_leave_menu = 0;
468 game_disable_cheats();
470 Dead_player_camera = 0;
474 multi_make_player_ghost(int playernum)
478 //Assert(playernum != Player_num);
479 //Assert(playernum < MAX_NUM_NET_PLAYERS);
481 if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS) || (playernum < 0))
483 Int3(); // Non-terminal, see Rob
487 // if (Objects[Players[playernum].objnum].type != OBJ_PLAYER)
488 // mprintf((1, "Warning: Player %d is not currently a player.\n", playernum));
490 obj = &Objects[Players[playernum].objnum];
492 obj->type = OBJ_GHOST;
493 obj->render_type = RT_NONE;
494 obj->movement_type = MT_NONE;
495 multi_reset_player_object(obj);
497 if (Game_mode & GM_MULTI_ROBOTS)
498 multi_strip_robots(playernum);
502 multi_make_ghost_player(int playernum)
506 // Assert(playernum != Player_num);
507 // Assert(playernum < MAX_NUM_NET_PLAYERS);
509 if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS))
511 Int3(); // Non-terminal, see rob
515 // if(Objects[Players[playernum].objnum].type != OBJ_GHOST)
516 // mprintf((1, "Warning: Player %d is not currently a ghost.\n", playernum));
518 obj = &Objects[Players[playernum].objnum];
520 obj->type = OBJ_PLAYER;
521 obj->movement_type = MT_PHYSICS;
522 multi_reset_player_object(obj);
525 int multi_get_kill_list(int *plist)
527 // Returns the number of active net players and their
528 // sorted order of kills
532 for (i = 0; i < N_players; i++)
533 //if (Players[sorted_kills[i]].connected)
534 plist[n++] = sorted_kills[i];
537 Int3(); // SEE ROB OR MATT
539 //memcpy(plist, sorted_kills, N_players*sizeof(int));
545 multi_sort_kill_list(void)
547 // Sort the kills list each time a new kill is added
549 int kills[MAX_NUM_NET_PLAYERS];
553 for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
556 if (Game_mode & GM_MULTI_COOP)
557 kills[i] = Players[i].score;
560 if (Show_kill_list==2)
562 if (Players[i].net_killed_total+Players[i].net_kills_total==0)
563 kills[i]=-1; // always draw the ones without any ratio last
565 kills[i]=(int)((float)((float)Players[i].net_kills_total/((float)Players[i].net_killed_total+(float)Players[i].net_kills_total))*100.0);
568 kills[i] = Players[i].net_kills_total;
574 for (i = 0; i < N_players-1; i++)
576 if (kills[sorted_kills[i]] < kills[sorted_kills[i+1]])
578 changed = sorted_kills[i];
579 sorted_kills[i] = sorted_kills[i+1];
580 sorted_kills[i+1] = changed;
585 mprintf((0, "Sorted kills %d %d.\n", sorted_kills[0], sorted_kills[1]));
588 extern object *obj_find_first_of_type (int);
589 char Multi_killed_yourself=0;
591 void multi_compute_kill(int killer, int killed)
593 // Figure out the results of a network kills and add it to the
594 // appropriate player's tally.
596 int killed_pnum, killed_type;
597 int killer_pnum, killer_type,killer_id;
599 char killed_name[(CALLSIGN_LEN*2)+4];
600 char killer_name[(CALLSIGN_LEN*2)+4];
602 kmatrix_kills_changed = 1;
603 Multi_killed_yourself=0;
605 // Both object numbers are localized already!
607 mprintf((0, "compute_kill passed: object %d killed object %d.\n", killer, killed));
609 if ((killed < 0) || (killed > Highest_object_index) || (killer < 0) || (killer > Highest_object_index))
611 Int3(); // See Rob, illegal value passed to compute_kill;
615 killed_type = Objects[killed].type;
616 killer_type = Objects[killer].type;
617 killer_id = Objects[killer].id;
619 if ((killed_type != OBJ_PLAYER) && (killed_type != OBJ_GHOST))
621 Int3(); // compute_kill passed non-player object!
625 killed_pnum = Objects[killed].id;
627 PKilledFlags[killed_pnum]=1;
629 Assert ((killed_pnum >= 0) && (killed_pnum < N_players));
631 if (Game_mode & GM_TEAM)
632 sprintf(killed_name, "%s (%s)", Players[killed_pnum].callsign, Netgame.team_name[get_team(killed_pnum)]);
634 sprintf(killed_name, "%s", Players[killed_pnum].callsign);
636 if (Newdemo_state == ND_STATE_RECORDING)
637 newdemo_record_multi_death(killed_pnum);
639 digi_play_sample( SOUND_HUD_KILL, F3_0 );
641 if (Control_center_destroyed)
642 Players[killed_pnum].connected=3;
644 if (killer_type == OBJ_CNTRLCEN)
646 Players[killed_pnum].net_killed_total++;
647 Players[killed_pnum].net_kills_total--;
649 if (Newdemo_state == ND_STATE_RECORDING)
650 newdemo_record_multi_kill(killed_pnum, -1);
652 if (killed_pnum == Player_num)
654 HUD_init_message("%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_NONPLAY);
655 multi_add_lifetime_killed ();
658 HUD_init_message("%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_NONPLAY );
663 else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST))
665 if (killer_id==PMINE_ID && killer_type!=OBJ_ROBOT)
667 if (killed_pnum == Player_num)
668 HUD_init_message("You were killed by a mine!");
670 HUD_init_message("%s was killed by a mine!",killed_name);
674 if (killed_pnum == Player_num)
676 HUD_init_message("%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_ROBOT);
677 multi_add_lifetime_killed();
680 HUD_init_message("%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_ROBOT );
682 Players[killed_pnum].net_killed_total++;
686 else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST) && (killer_id!=PMINE_ID))
688 Int3(); // Illegal killer type?
691 if (killer_id==PMINE_ID)
693 if (killed_pnum==Player_num)
694 HUD_init_message("You were killed by a mine!");
696 HUD_init_message("%s was killed by a mine!",killed_name);
698 Players[killed_pnum].net_killed_total++;
704 killer_pnum = Objects[killer].id;
706 if (Game_mode & GM_TEAM)
707 sprintf(killer_name, "%s (%s)", Players[killer_pnum].callsign, Netgame.team_name[get_team(killer_pnum)]);
709 sprintf(killer_name, "%s", Players[killer_pnum].callsign);
711 // Beyond this point, it was definitely a player-player kill situation
713 if ((killer_pnum < 0) || (killer_pnum >= N_players))
714 Int3(); // See rob, tracking down bug with kill HUD messages
715 if ((killed_pnum < 0) || (killed_pnum >= N_players))
716 Int3(); // See rob, tracking down bug with kill HUD messages
718 if (killer_pnum == killed_pnum)
720 if (!(Game_mode & GM_HOARD))
722 if (Game_mode & GM_TEAM)
723 team_kills[get_team(killed_pnum)] -= 1;
725 Players[killed_pnum].net_killed_total += 1;
726 Players[killed_pnum].net_kills_total -= 1;
728 if (Newdemo_state == ND_STATE_RECORDING)
729 newdemo_record_multi_kill(killed_pnum, -1);
731 kill_matrix[killed_pnum][killed_pnum] += 1; // # of suicides
733 if (killer_pnum == Player_num)
735 HUD_init_message("%s %s %s!", TXT_YOU, TXT_KILLED, TXT_YOURSELF );
736 Multi_killed_yourself=1;
737 multi_add_lifetime_killed();
740 HUD_init_message("%s %s", killed_name, TXT_SUICIDE);
745 if (!(Game_mode & GM_HOARD))
747 if (Game_mode & GM_TEAM)
749 if (get_team(killed_pnum) == get_team(killer_pnum))
750 team_kills[get_team(killed_pnum)] -= 1;
752 team_kills[get_team(killer_pnum)] += 1;
755 Players[killer_pnum].net_kills_total += 1;
756 Players[killer_pnum].KillGoalCount+=1;
758 if (Newdemo_state == ND_STATE_RECORDING)
759 newdemo_record_multi_kill(killer_pnum, 1);
763 if (Game_mode & GM_TEAM)
765 if (killed_pnum==Player_num && get_team(killed_pnum) == get_team(killer_pnum))
766 Multi_killed_yourself=1;
770 kill_matrix[killer_pnum][killed_pnum] += 1;
771 Players[killed_pnum].net_killed_total += 1;
772 if (killer_pnum == Player_num) {
773 HUD_init_message("%s %s %s!", TXT_YOU, TXT_KILLED, killed_name);
774 multi_add_lifetime_kills();
775 if ((Game_mode & GM_MULTI_COOP) && (Players[Player_num].score >= 1000))
776 add_points_to_score(-1000);
778 else if (killed_pnum == Player_num)
780 HUD_init_message("%s %s %s!", killer_name, TXT_KILLED, TXT_YOU);
781 multi_add_lifetime_killed();
782 if (Game_mode & GM_HOARD)
784 if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]>3)
785 multi_send_play_by_play (1,killer_pnum,Player_num);
786 else if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]>0)
787 multi_send_play_by_play (0,killer_pnum,Player_num);
791 HUD_init_message("%s %s %s!", killer_name, TXT_KILLED, killed_name);
794 TheGoal=Netgame.KillGoal*5;
796 if (Netgame.KillGoal>0)
798 if (Players[killer_pnum].KillGoalCount>=TheGoal)
800 if (killer_pnum==Player_num)
802 HUD_init_message("You reached the kill goal!");
803 Players[Player_num].shields=i2f(200);
806 HUD_init_message ("%s has reached the kill goal!",Players[killer_pnum].callsign);
808 HUD_init_message ("The control center has been destroyed!");
809 net_destroy_controlcen (obj_find_first_of_type (OBJ_CNTRLCEN));
813 multi_sort_kill_list();
814 multi_show_player_list();
815 Players[killed_pnum].flags&=(~(PLAYER_FLAGS_HEADLIGHT_ON)); // clear the killed guys flags/headlights
821 static int lasttime=0;
824 if (!(Game_mode & GM_MULTI))
830 if ((Game_mode & GM_NETWORK) && Netgame.PlayTimeAllowed && lasttime!=f2i (ThisLevelTime))
832 for (i=0;i<N_players;i++)
833 if (Players[i].connected)
837 multi_send_heartbeat();
838 lasttime=f2i(ThisLevelTime);
844 multi_send_message(); // Send any waiting messages
847 multi_leave_menu = 0;
850 if (Game_mode & GM_MULTI_ROBOTS)
852 multi_check_robot_timeout();
856 if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
862 network_do_frame(0, 1);
865 if (multi_quit_game && !multi_in_menu)
868 longjmp(LeaveGame, 0);
873 multi_send_data(char *buf, int len, int repeat)
875 Assert(len == message_length[(int)buf[0]]);
876 Assert(buf[0] <= MULTI_MAX_TYPE);
877 // Assert(buf[0] >= 0);
879 if (Game_mode & GM_NETWORK)
882 if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
883 com_send_data(buf, len, repeat);
884 else if (Game_mode & GM_NETWORK)
885 network_send_data((unsigned char *)buf, len, repeat);
889 multi_leave_game(void)
892 // if (Function_mode != FMODE_GAME)
895 if (!(Game_mode & GM_MULTI))
898 if (Game_mode & GM_NETWORK)
900 mprintf((0, "Sending explosion message.\n"));
905 drop_player_eggs(ConsoleObject);
906 multi_send_position(Players[Player_num].objnum);
907 multi_send_player_explode(MULTI_PLAYER_DROP);
910 mprintf((1, "Sending leave game.\n"));
911 multi_send_quit(MULTI_QUIT);
913 if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
915 if (Game_mode & GM_NETWORK)
916 network_leave_game();
918 Game_mode |= GM_GAME_OVER;
920 if (Function_mode!=FMODE_EXIT)
921 Function_mode = FMODE_MENU;
925 // change_playernum_to(0);
926 // Viewer = ConsoleObject = &Objects[0];
931 multi_show_player_list()
933 if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_COOP))
939 Show_kill_list_timer = F1_0*5; // 5 second timer
944 multi_endlevel(int *secret)
948 if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
949 com_endlevel(secret); // an opportunity to re-sync or whatever
950 else if (Game_mode & GM_NETWORK)
951 result = network_endlevel(secret);
957 // Part 2 : functions that act on network/serial messages and change the
958 // the state of the game in some way.
962 //extern PORT *com_port;
966 multi_menu_poll(void)
969 int was_fuelcen_alive;
972 was_fuelcen_alive = Control_center_destroyed;
974 // Special polling function for in-game menus for multiplayer and serial
976 if (! ((Game_mode & GM_MULTI) && (Function_mode == FMODE_GAME)) )
979 if (multi_leave_menu)
982 old_shields = Players[Player_num].shields;
983 player_was_dead = Player_is_dead;
985 multi_in_menu++; // Track level of menu nesting
991 timer_delay(f0_1); // delay 100 milliseconds
993 if (Endlevel_sequence || (Control_center_destroyed && !was_fuelcen_alive) || (Player_is_dead != player_was_dead) || (Players[Player_num].shields < old_shields))
995 multi_leave_menu = 1;
998 if ((Control_center_destroyed) && (Countdown_seconds_left < 10))
1000 multi_leave_menu = 1;
1005 if ((Game_mode & GM_MODEM) && (!GetCd(com_port)))
1007 multi_leave_menu = 1;
1016 multi_define_macro(int key)
1018 if (!(Game_mode & GM_MULTI))
1021 key &= (~KEY_SHIFTED);
1026 multi_defining_message = 1; break;
1028 multi_defining_message = 2; break;
1030 multi_defining_message = 3; break;
1032 multi_defining_message = 4; break;
1037 if (multi_defining_message) {
1038 multi_message_index = 0;
1039 Network_message[multi_message_index] = 0;
1044 char feedback_result[200];
1047 multi_message_feedback(void)
1053 if (!( ((colon = strrchr(Network_message, ':')) == NULL) || (colon-Network_message < 1) || (colon-Network_message > CALLSIGN_LEN) ))
1055 sprintf(feedback_result, "%s ", TXT_MESSAGE_SENT_TO);
1056 if ((Game_mode & GM_TEAM) && (atoi(Network_message) > 0) && (atoi(Network_message) < 3))
1058 sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[atoi(Network_message)-1]);
1061 if (Game_mode & GM_TEAM)
1063 for (i = 0; i < N_players; i++)
1065 if (!strnicmp(Netgame.team_name[i], Network_message, colon-Network_message))
1068 strcat(feedback_result, ", ");
1071 strcat(feedback_result, "\n");
1072 sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[i]);
1076 for (i = 0; i < N_players; i++)
1078 if ((!strnicmp(Players[i].callsign, Network_message, colon-Network_message)) && (i != Player_num) && (Players[i].connected))
1081 strcat(feedback_result, ", ");
1084 strcat(feedback_result, "\n");
1085 sprintf(feedback_result+strlen(feedback_result), "%s", Players[i].callsign);
1089 strcat(feedback_result, TXT_NOBODY);
1091 strcat(feedback_result, ".");
1093 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1095 Assert(strlen(feedback_result) < 200);
1097 HUD_init_message(feedback_result);
1098 //sprintf (temp,"%s",colon);
1099 //sprintf (Network_message,"%s",temp);
1105 multi_send_macro(int key)
1107 if (! (Game_mode & GM_MULTI) )
1124 if (!Network_message_macro[key].string[0]) {
1125 HUD_init_message(TXT_NO_MACRO);
1129 strncpy(Network_message, Network_message_macro[key].string, MAX_MESSAGE_LEN);
1130 Network_message[MAX_MESSAGE_LEN - 1] = 0;
1131 Network_message_reciever = 100;
1133 HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
1134 multi_message_feedback();
1139 multi_send_message_start()
1141 if (Game_mode&GM_MULTI) {
1142 multi_sending_message = 1;
1143 multi_message_index = 0;
1144 Network_message[multi_message_index] = 0;
1148 extern fix StartingShields;
1149 fix PingLaunchTime,PingReturnTime;
1151 extern void network_send_ping (ubyte);
1152 extern int network_who_is_master(void);
1153 extern char NameReturning;
1154 extern int force_cockpit_redraw;
1156 void network_dump_appletalk_player(ubyte node, ushort net, ubyte socket, int why);
1158 void multi_send_message_end()
1163 Network_message_reciever = 100;
1165 if (!strnicmp (Network_message,"!Names",6))
1167 NameReturning=1-NameReturning;
1168 HUD_init_message ("Name returning is now %s.",NameReturning?"active":"disabled");
1170 else if (!strnicmp (Network_message,"Handicap:",9))
1172 mytempbuf=&Network_message[9];
1173 mprintf ((0,"Networkhandi=%s\n",mytempbuf));
1174 StartingShields = atoi(mytempbuf);
1175 if (StartingShields<10)
1177 if (StartingShields>100)
1179 sprintf (Network_message,"%s has tried to cheat!",Players[Player_num].callsign);
1180 StartingShields=100;
1183 sprintf (Network_message,"%s handicap is now %d",Players[Player_num].callsign,StartingShields);
1185 HUD_init_message ("Telling others of your handicap of %d!",StartingShields);
1186 StartingShields=i2f(StartingShields);
1188 else if (!strnicmp (Network_message,"NoBombs",7))
1189 Netgame.DoSmartMine=0;
1190 else if (!strnicmp (Network_message,"Ping:",5))
1192 if (Game_mode & GM_NETWORK)
1195 if (strlen(Network_message) > 5)
1196 while (Network_message[name_index] == ' ')
1199 if (strlen(Network_message)<=name_index)
1201 HUD_init_message ("You must specify a name to ping");
1205 for (i = 0; i < N_players; i++)
1206 if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (i != Player_num) && (Players[i].connected))
1208 PingLaunchTime=timer_get_fixed_seconds();
1209 network_send_ping (i);
1210 HUD_init_message("Pinging %s...",Players[i].callsign);
1211 multi_message_index = 0;
1212 multi_sending_message = 0;
1216 else // Modem/Serial ping
1218 PingLaunchTime=timer_get_fixed_seconds();
1219 multi_send_modem_ping ();
1220 HUD_init_message("Pinging opponent...");
1221 multi_message_index = 0;
1222 multi_sending_message = 0;
1226 else if (!strnicmp (Network_message,"move:",5))
1228 mprintf ((0,"moving?\n"));
1230 if ((Game_mode & GM_NETWORK) && (Game_mode & GM_TEAM))
1233 if (strlen(Network_message) > 5)
1234 while (Network_message[name_index] == ' ')
1237 if (!network_i_am_master())
1239 HUD_init_message ("Only %s can move players!",Players[network_who_is_master()].callsign);
1243 if (strlen(Network_message)<=name_index)
1245 HUD_init_message ("You must specify a name to move");
1249 for (i = 0; i < N_players; i++)
1250 if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (Players[i].connected))
1252 if ((Game_mode & GM_CAPTURE) && (Players[i].flags & PLAYER_FLAGS_FLAG))
1254 HUD_init_message ("Can't move player because s/he has a flag!");
1258 if (Netgame.team_vector & (1<<i))
1259 Netgame.team_vector&=(~(1<<i));
1261 Netgame.team_vector|=(1<<i);
1263 for (t=0;t<N_players;t++)
1264 if (Players[t].connected)
1265 multi_reset_object_texture (&Objects[Players[t].objnum]);
1267 network_send_netgame_update ();
1268 sprintf (Network_message,"%s has changed teams!",Players[i].callsign);
1271 HUD_init_message ("You have changed teams!");
1275 HUD_init_message ("Moving %s to other team.",Players[i].callsign);
1281 else if (!strnicmp (Network_message,"kick:",5) && (Game_mode & GM_NETWORK))
1284 if (strlen(Network_message) > 5)
1285 while (Network_message[name_index] == ' ')
1288 if (!network_i_am_master())
1290 HUD_init_message ("Only %s can kick others out!",Players[network_who_is_master()].callsign);
1291 multi_message_index = 0;
1292 multi_sending_message = 0;
1295 if (strlen(Network_message)<=name_index)
1297 HUD_init_message ("You must specify a name to kick");
1298 multi_message_index = 0;
1299 multi_sending_message = 0;
1303 if (Network_message[name_index] == '#' && isdigit(Network_message[name_index+1])) {
1304 int players[MAX_NUM_NET_PLAYERS];
1305 int listpos = Network_message[name_index+1] - '0';
1307 mprintf ((0,"Trying to kick %d , show_kill_list=%d\n",listpos,Show_kill_list));
1309 if (Show_kill_list==1 || Show_kill_list==2) {
1310 if (listpos == 0 || listpos >= N_players) {
1311 HUD_init_message ("Invalid player number for kick.");
1312 multi_message_index = 0;
1313 multi_sending_message = 0;
1316 multi_get_kill_list(players);
1317 i = players[listpos];
1318 if ((i != Player_num) && (Players[i].connected))
1321 else HUD_init_message ("You cannot use # kicking with in team display.");
1324 multi_message_index = 0;
1325 multi_sending_message = 0;
1330 for (i = 0; i < N_players; i++)
1331 if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (i != Player_num) && (Players[i].connected)) {
1333 if (Network_game_type == IPX_GAME)
1334 network_dump_player(NetPlayers.players[i].network.ipx.server,NetPlayers.players[i].network.ipx.node, 7);
1337 network_dump_appletalk_player(NetPlayers.players[i].network.appletalk.node,NetPlayers.players[i].network.appletalk.net, NetPlayers.players[i].network.appletalk.socket, 7);
1339 HUD_init_message("Dumping %s...",Players[i].callsign);
1340 multi_message_index = 0;
1341 multi_sending_message = 0;
1347 HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
1349 multi_send_message();
1350 multi_message_feedback();
1352 multi_message_index = 0;
1353 multi_sending_message = 0;
1356 void multi_define_macro_end()
1358 Assert( multi_defining_message > 0 );
1360 cvar_set_cvar( &Network_message_macro[multi_defining_message - 1], Network_message );
1363 multi_message_index = 0;
1364 multi_defining_message = 0;
1367 void multi_message_input_sub( int key )
1372 multi_sending_message = 0;
1373 multi_defining_message = 0;
1374 game_flush_inputs();
1379 if (multi_message_index > 0)
1380 multi_message_index--;
1381 Network_message[multi_message_index] = 0;
1384 if ( multi_sending_message )
1385 multi_send_message_end();
1386 else if ( multi_defining_message )
1387 multi_define_macro_end();
1388 game_flush_inputs();
1392 int ascii = key_to_ascii(key);
1393 if ((ascii < 255 )) {
1394 if (multi_message_index < MAX_MESSAGE_LEN-2 ) {
1395 Network_message[multi_message_index++] = ascii;
1396 Network_message[multi_message_index] = 0;
1397 } else if ( multi_sending_message ) {
1399 char * ptext, * pcolon;
1401 Network_message[multi_message_index++] = ascii;
1402 Network_message[multi_message_index] = 0;
1403 for (i=multi_message_index-1; i>=0; i-- ) {
1404 if ( Network_message[i]==32 ) {
1405 ptext = &Network_message[i+1];
1406 Network_message[i] = 0;
1410 multi_send_message_end();
1412 multi_sending_message = 1;
1413 pcolon = strchr( Network_message, ':' );
1415 strcpy( pcolon+1, ptext );
1417 strcpy( Network_message, ptext );
1418 multi_message_index = (int)strlen( Network_message );
1427 multi_send_message_dialog(void)
1432 if (!(Game_mode&GM_MULTI))
1435 Network_message[0] = 0; // Get rid of old contents
1437 m[0].type=NM_TYPE_INPUT; m[0].text = Network_message; m[0].text_len = MAX_MESSAGE_LEN-1;
1438 choice = newmenu_do( NULL, TXT_SEND_MESSAGE, 1, m, NULL );
1440 if ((choice > -1) && (strlen(Network_message) > 0)) {
1441 Network_message_reciever = 100;
1442 HUD_init_message("%s '%s'", TXT_SENDING, Network_message);
1443 multi_message_feedback();
1450 multi_do_death(int objnum)
1452 // Do any miscellaneous stuff for a new network player after death
1454 if (!(Game_mode & GM_MULTI_COOP))
1456 mprintf((0, "Setting all keys for player %d.\n", Player_num));
1457 Players[Player_num].flags |= (PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_GOLD_KEY);
1462 multi_do_fire(char *buf)
1469 // Act out the actual shooting
1473 weapon = (int)buf[2];
1478 Network_laser_track = GET_INTEL_SHORT(buf + 6);
1480 Assert (pnum < N_players);
1482 if (Objects[Players[(int)pnum].objnum].type == OBJ_GHOST)
1483 multi_make_ghost_player(pnum);
1485 // mprintf((0,"multi_do_fire, weapon = %d\n",weapon));
1487 if (weapon == FLARE_ADJUST)
1488 Laser_player_fire( &Objects[Players[(int)pnum].objnum], FLARE_ID, 6, 1, 0 );
1489 else if (weapon >= MISSILE_ADJUST) {
1490 int weapon_id,weapon_gun;
1492 weapon_id = Secondary_weapon_to_weapon_info[weapon-MISSILE_ADJUST];
1493 weapon_gun = Secondary_weapon_to_gun_num[weapon-MISSILE_ADJUST] + (flags & 1);
1494 mprintf((0,"missile id = %d, gun = %d\n",weapon_id,weapon_gun));
1496 if (weapon-MISSILE_ADJUST==GUIDED_INDEX)
1498 mprintf ((0,"Missile is guided!!!\n"));
1502 Laser_player_fire( &Objects[Players[(int)pnum].objnum], weapon_id, weapon_gun, 1, 0 );
1505 fix save_charge = Fusion_charge;
1507 if (weapon == FUSION_INDEX) {
1508 Fusion_charge = flags << 12;
1509 mprintf((0, "Fusion charge X%f.\n", f2fl(Fusion_charge)));
1511 if (weapon == LASER_ID) {
1512 if (flags & LASER_QUAD)
1513 Players[(int)pnum].flags |= PLAYER_FLAGS_QUAD_LASERS;
1515 Players[(int)pnum].flags &= ~PLAYER_FLAGS_QUAD_LASERS;
1518 do_laser_firing(Players[(int)pnum].objnum, weapon, (int)buf[3], flags, (int)buf[5]);
1520 if (weapon == FUSION_INDEX)
1521 Fusion_charge = save_charge;
1526 multi_do_message(char *buf)
1529 char *tilde,mesbuf[100];
1534 if ((tilde=strchr (buf+loc,'$'))) // do that stupid name stuff
1535 { // why'd I put this in? Probably for the
1536 tloc = (int)(tilde - (buf + loc)); // same reason you can name your guidebot
1537 mprintf ((0,"Tloc=%d\n",tloc));
1540 strncpy (mesbuf,buf+loc,tloc);
1541 strcpy (mesbuf+tloc,Players[Player_num].callsign);
1542 strcpy (mesbuf+strlen(Players[Player_num].callsign)+tloc,buf+loc+tloc+1);
1543 strcpy (buf+loc,mesbuf);
1546 if (((colon = strrchr(buf+loc, ':')) == NULL) || (colon-(buf+loc) < 1) || (colon-(buf+loc) > CALLSIGN_LEN))
1549 mesbuf[1] = BM_XRGB(28, 0, 0);
1550 strcpy(&mesbuf[2], Players[(int)buf[1]].callsign);
1551 t = (int)strlen(mesbuf);
1554 mesbuf[t+2] = BM_XRGB(0, 31, 0);
1557 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1558 HUD_init_message("%s %s", mesbuf, buf+2);
1562 if ( (!strnicmp(Players[Player_num].callsign, buf+loc, colon-(buf+loc))) ||
1563 ((Game_mode & GM_TEAM) && ( (get_team(Player_num) == atoi(buf+loc)-1) || !strnicmp(Netgame.team_name[get_team(Player_num)], buf+loc, colon-(buf+loc)))) )
1566 mesbuf[1] = BM_XRGB(0, 32, 32);
1567 strcpy(&mesbuf[2], Players[(int)buf[1]].callsign);
1568 t = (int)strlen(mesbuf);
1571 mesbuf[t+2] = BM_XRGB(0, 31, 0);
1574 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1575 HUD_init_message("%s %s", mesbuf, colon+1);
1581 multi_do_position(char *buf)
1583 #ifdef WORDS_BIGENDIAN
1587 // This routine does only player positions, mode game only
1588 // mprintf((0, "Got position packet.\n"));
1590 int pnum = (Player_num+1)%2;
1592 Assert(&Objects[Players[pnum].objnum] != ConsoleObject);
1594 if (Game_mode & GM_NETWORK)
1596 Int3(); // Get Jason, what the hell are we doing here?
1601 #ifndef WORDS_BIGENDIAN
1602 extract_shortpos(&Objects[Players[pnum].objnum], (shortpos *)(buf+1),0);
1604 memcpy((ubyte *)(sp.bytemat), (ubyte *)(buf + 1), 9);
1605 memcpy((ubyte *)&(sp.xo), (ubyte *)(buf + 10), 14);
1606 extract_shortpos(&Objects[Players[pnum].objnum], &sp, 1);
1609 if (Objects[Players[pnum].objnum].movement_type == MT_PHYSICS)
1610 set_thrust_from_velocity(&Objects[Players[pnum].objnum]);
1614 multi_do_reappear(char *buf)
1618 objnum = GET_INTEL_SHORT(buf + 1);
1620 Assert(objnum >= 0);
1621 // Assert(Players[Objects[objnum].id]].objnum == objnum);
1623 // mprintf((0, "Switching rendering back on for object %d.\n", objnum));
1625 multi_make_ghost_player(Objects[objnum].id);
1626 create_player_appearance_effect(&Objects[objnum]);
1627 PKilledFlags[Objects[objnum].id]=0;
1631 multi_do_player_explode(char *buf)
1633 // Only call this for players, not robots. pnum is player number, not
1640 char remote_created;
1645 if ((pnum < 0) || (pnum >= N_players))
1649 Assert(pnum < N_players);
1653 // If we are in the process of sending objects to a new player, reset that process
1654 if (Network_send_objects)
1656 mprintf((0, "Resetting object sync due to player explosion.\n"));
1657 Network_send_objnum = -1;
1661 // Stuff the Players structure to prepare for the explosion
1664 Players[pnum].primary_weapon_flags = GET_INTEL_SHORT(buf + count); count += 2;
1665 Players[pnum].secondary_weapon_flags = GET_INTEL_SHORT(buf + count); count += 2;
1666 Players[pnum].laser_level = buf[count]; count++;
1667 Players[pnum].secondary_ammo[HOMING_INDEX] = buf[count]; count++;
1668 Players[pnum].secondary_ammo[CONCUSSION_INDEX] = buf[count];count++;
1669 Players[pnum].secondary_ammo[SMART_INDEX] = buf[count]; count++;
1670 Players[pnum].secondary_ammo[MEGA_INDEX] = buf[count]; count++;
1671 Players[pnum].secondary_ammo[PROXIMITY_INDEX] = buf[count]; count++;
1673 Players[pnum].secondary_ammo[SMISSILE1_INDEX] = buf[count]; count++;
1674 Players[pnum].secondary_ammo[GUIDED_INDEX] = buf[count]; count++;
1675 Players[pnum].secondary_ammo[SMART_MINE_INDEX]= buf[count]; count++;
1676 Players[pnum].secondary_ammo[SMISSILE4_INDEX] = buf[count]; count++;
1677 Players[pnum].secondary_ammo[SMISSILE5_INDEX] = buf[count]; count++;
1679 Players[pnum].primary_ammo[VULCAN_INDEX] = GET_INTEL_SHORT(buf + count); count += 2;
1680 Players[pnum].primary_ammo[GAUSS_INDEX] = GET_INTEL_SHORT(buf + count); count += 2;
1681 Players[pnum].flags = GET_INTEL_INT(buf + count); count += 4;
1683 multi_adjust_remote_cap (pnum);
1685 objp = &Objects[Players[pnum].objnum];
1687 // objp->phys_info.velocity = *(vms_vector *)(buf+16); // 12 bytes
1688 // objp->pos = *(vms_vector *)(buf+28); // 12 bytes
1690 remote_created = buf[count++]; // How many did the other guy create?
1694 drop_player_eggs(objp);
1696 // Create mapping from remote to local numbering system
1698 mprintf((0, "I Created %d powerups, remote created %d.\n", Net_create_loc, remote_created));
1700 // We now handle this situation gracefully, Int3 not required
1701 // if (Net_create_loc != remote_created)
1702 // Int3(); // Probably out of object array space, see Rob
1704 for (i = 0; i < remote_created; i++)
1708 s = GET_INTEL_SHORT(buf + count);
1710 if ((i < Net_create_loc) && (s > 0))
1711 map_objnum_local_to_remote((short)Net_create_objnums[i], s, pnum);
1714 mprintf((0, "WARNING: Remote created object has non-valid number %d (player %d)", s, pnum));
1718 mprintf((0, "WARNING: Could not create all powerups created by player %d.\n", pnum));
1723 for (i = remote_created; i < Net_create_loc; i++) {
1724 mprintf((0, "WARNING: I Created more powerups than player %d, deleting.\n", pnum));
1725 Objects[Net_create_objnums[i]].flags |= OF_SHOULD_BE_DEAD;
1728 if (buf[0] == MULTI_PLAYER_EXPLODE)
1730 explode_badass_player(objp);
1732 objp->flags &= ~OF_SHOULD_BE_DEAD; //don't really kill player
1733 multi_make_player_ghost(pnum);
1737 create_player_appearance_effect(objp);
1740 Players[pnum].flags &= ~(PLAYER_FLAGS_CLOAKED | PLAYER_FLAGS_INVULNERABLE | PLAYER_FLAGS_FLAG);
1741 Players[pnum].cloak_time = 0;
1745 multi_do_kill(char *buf)
1751 pnum = (int)(buf[count]);
1752 if ((pnum < 0) || (pnum >= N_players))
1754 Int3(); // Invalid player number killed
1757 killed = Players[pnum].objnum;
1760 killer = GET_INTEL_SHORT(buf + count);
1762 killer = objnum_remote_to_local(killer, (sbyte)buf[count+2]);
1765 if ((Objects[killed].type != OBJ_PLAYER) && (Objects[killed].type != OBJ_GHOST))
1768 mprintf( (1, "SOFT INT3: MULTI.C Non-player object %d of type %d killed! (JOHN)\n", killed, Objects[killed].type ));
1773 multi_compute_kill(killer, killed);
1778 // Changed by MK on 10/20/94 to send NULL as object to net_destroy_controlcen if it got -1
1779 // which means not a controlcen object, but contained in another object
1780 void multi_do_controlcen_destroy(char *buf)
1785 objnum = GET_INTEL_SHORT(buf + 1);
1788 if (Control_center_destroyed != 1)
1790 if ((who < N_players) && (who != Player_num)) {
1791 HUD_init_message("%s %s", Players[who].callsign, TXT_HAS_DEST_CONTROL);
1793 else if (who == Player_num)
1794 HUD_init_message(TXT_YOU_DEST_CONTROL);
1796 HUD_init_message(TXT_CONTROL_DESTROYED);
1799 net_destroy_controlcen(&Objects[objnum]);
1801 net_destroy_controlcen(NULL);
1806 multi_do_escape(char *buf)
1810 objnum = Players[(int)buf[1]].objnum;
1812 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1813 digi_kill_sound_linked_to_object (objnum);
1817 HUD_init_message("%s %s", Players[(int)buf[1]].callsign, TXT_HAS_ESCAPED);
1818 if (Game_mode & GM_NETWORK)
1819 Players[(int)buf[1]].connected = CONNECT_ESCAPE_TUNNEL;
1820 if (!multi_goto_secret)
1821 multi_goto_secret = 2;
1823 else if (buf[2] == 1)
1825 HUD_init_message("%s %s", Players[(int)buf[1]].callsign, TXT_HAS_FOUND_SECRET);
1826 if (Game_mode & GM_NETWORK)
1827 Players[(int)buf[1]].connected = CONNECT_FOUND_SECRET;
1828 if (!multi_goto_secret)
1829 multi_goto_secret = 1;
1831 create_player_appearance_effect(&Objects[objnum]);
1832 multi_make_player_ghost(buf[1]);
1836 multi_do_remobj(char *buf)
1838 short objnum; // which object to remove
1840 sbyte obj_owner; // which remote list is it entered in
1842 objnum = GET_INTEL_SHORT(buf + 1);
1845 Assert(objnum >= 0);
1850 local_objnum = objnum_remote_to_local(objnum, obj_owner); // translate to local objnum
1852 // mprintf((0, "multi_do_remobj: %d owner %d = %d.\n", objnum, obj_owner, local_objnum));
1854 if (local_objnum < 0)
1856 mprintf((1, "multi_do_remobj: Could not remove referenced object.\n"));
1860 if ((Objects[local_objnum].type != OBJ_POWERUP) && (Objects[local_objnum].type != OBJ_HOSTAGE))
1862 mprintf((1, "multi_get_remobj: tried to remove invalid type %d.\n", Objects[local_objnum].type));
1866 if (Network_send_objects && network_objnum_is_past(local_objnum))
1868 mprintf((0, "Resetting object sync due to object removal.\n"));
1869 Network_send_objnum = -1;
1871 if (Objects[local_objnum].type==OBJ_POWERUP)
1872 if (Game_mode & GM_NETWORK)
1874 if (PowerupsInMine[Objects[local_objnum].id]>0)
1875 PowerupsInMine[Objects[local_objnum].id]--;
1877 if (multi_powerup_is_4pack (Objects[local_objnum].id))
1879 mprintf ((0,"Hey babe! Doing that wacky 4 pack stuff."));
1881 if (PowerupsInMine[Objects[local_objnum].id-1]-4<0)
1882 PowerupsInMine[Objects[local_objnum].id-1]=0;
1884 PowerupsInMine[Objects[local_objnum].id-1]-=4;
1887 mprintf ((0,"Decrementing powerups! %d\n",PowerupsInMine[Objects[local_objnum].id]));
1890 Objects[local_objnum].flags |= OF_SHOULD_BE_DEAD; // quick and painless
1895 multi_do_quit(char *buf)
1898 if (Game_mode & GM_NETWORK)
1902 digi_play_sample( SOUND_HUD_MESSAGE, F1_0 );
1904 HUD_init_message( "%s %s", Players[(int)buf[1]].callsign, TXT_HAS_LEFT_THE_GAME);
1906 network_disconnect_player(buf[1]);
1911 for (i = 0; i < N_players; i++)
1912 if (Players[i].connected) n++;
1915 nm_messagebox(NULL, 1, TXT_OK, TXT_YOU_ARE_ONLY);
1919 if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM))
1921 Function_mode = FMODE_MENU;
1922 multi_quit_game = 1;
1923 multi_leave_menu = 1;
1924 nm_messagebox(NULL, 1, TXT_OK, TXT_OPPONENT_LEFT);
1925 Function_mode = FMODE_GAME;
1926 multi_reset_stuff();
1932 multi_do_cloak(char *buf)
1936 pnum = (int)(buf[1]);
1938 Assert(pnum < N_players);
1940 mprintf((0, "Cloaking player %d\n", pnum));
1942 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
1943 Players[pnum].cloak_time = GameTime;
1944 ai_do_cloak_stuff();
1947 if (Game_mode & GM_MULTI_ROBOTS)
1948 multi_strip_robots(pnum);
1951 if (Newdemo_state == ND_STATE_RECORDING)
1952 newdemo_record_multi_cloak(pnum);
1956 multi_do_decloak(char *buf)
1960 pnum = (int)(buf[1]);
1962 if (Newdemo_state == ND_STATE_RECORDING)
1963 newdemo_record_multi_decloak(pnum);
1968 multi_do_door_open(char *buf)
1976 segnum = GET_INTEL_SHORT(buf + 1);
1980 // mprintf((0, "Opening door on side %d of segment # %d.\n", side, segnum));
1982 if ((segnum < 0) || (segnum > Highest_segment_index) || (side < 0) || (side > 5))
1988 seg = &Segments[segnum];
1990 if (seg->sides[side].wall_num == -1) { //Opening door on illegal wall
1995 w = &Walls[seg->sides[side].wall_num];
1997 if (w->type == WALL_BLASTABLE)
1999 if (!(w->flags & WALL_BLASTED))
2001 mprintf((0, "Blasting wall by remote command.\n"));
2002 wall_destroy(seg, side);
2006 else if (w->state != WALL_DOOR_OPENING)
2008 wall_open_door(seg, side);
2015 // mprintf((0, "Door already opening!\n"));
2019 multi_do_create_explosion(char *buf)
2024 pnum = buf[count++];
2026 // mprintf((0, "Creating small fireball.\n"));
2027 create_small_fireball_on_object(&Objects[Players[pnum].objnum], F1_0, 1);
2031 multi_do_controlcen_fire(char *buf)
2033 vms_vector to_target;
2038 memcpy(&to_target, buf+count, 12); count += 12;
2039 #ifdef WORDS_BIGENDIAN // swap the vector to_target
2040 to_target.x = (fix)INTEL_INT((int)to_target.x);
2041 to_target.y = (fix)INTEL_INT((int)to_target.y);
2042 to_target.z = (fix)INTEL_INT((int)to_target.z);
2044 gun_num = buf[count]; count += 1;
2045 objnum = GET_INTEL_SHORT(buf + count); count += 2;
2047 Laser_create_new_easy(&to_target, &Gun_pos[(int)gun_num], objnum, CONTROLCEN_WEAPON_NUM, 1);
2051 multi_do_create_powerup(char *buf)
2061 if (Endlevel_sequence || Control_center_destroyed)
2064 pnum = buf[count++];
2065 powerup_type = buf[count++];
2066 segnum = GET_INTEL_SHORT(buf + count); count += 2;
2067 objnum = GET_INTEL_SHORT(buf + count); count += 2;
2069 if ((segnum < 0) || (segnum > Highest_segment_index)) {
2074 memcpy(&new_pos, buf+count, sizeof(vms_vector)); count+=sizeof(vms_vector);
2075 #ifdef WORDS_BIGENDIAN
2076 new_pos.x = (fix)SWAPINT((int)new_pos.x);
2077 new_pos.y = (fix)SWAPINT((int)new_pos.y);
2078 new_pos.z = (fix)SWAPINT((int)new_pos.z);
2082 my_objnum = call_object_create_egg(&Objects[Players[(int)pnum].objnum], 1, OBJ_POWERUP, powerup_type);
2084 if (my_objnum < 0) {
2085 mprintf((0, "Could not create new powerup!\n"));
2089 if (Network_send_objects && network_objnum_is_past(my_objnum))
2091 mprintf((0, "Resetting object sync due to powerup creation.\n"));
2092 Network_send_objnum = -1;
2095 Objects[my_objnum].pos = new_pos;
2097 vm_vec_zero(&Objects[my_objnum].mtype.phys_info.velocity);
2099 obj_relink(my_objnum, segnum);
2101 map_objnum_local_to_remote(my_objnum, objnum, pnum);
2103 object_create_explosion(segnum, &new_pos, i2f(5), VCLIP_POWERUP_DISAPPEARANCE);
2104 mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));
2106 if (Game_mode & GM_NETWORK)
2107 PowerupsInMine[(int)powerup_type]++;
2111 multi_do_play_sound(char *buf)
2113 int pnum = (int)(buf[1]);
2114 int sound_num = (int)(buf[2]);
2115 fix volume = (int)(buf[3]) << 12;
2117 if (!Players[pnum].connected)
2120 Assert(Players[pnum].objnum >= 0);
2121 Assert(Players[pnum].objnum <= Highest_object_index);
2123 digi_link_sound_to_object( sound_num, Players[pnum].objnum, 0, volume);
2127 multi_do_score(char *buf)
2129 int pnum = (int)(buf[1]);
2131 if ((pnum < 0) || (pnum >= N_players))
2133 Int3(); // Non-terminal, see rob
2137 if (Newdemo_state == ND_STATE_RECORDING) {
2139 score = GET_INTEL_INT(buf + 2);
2140 newdemo_record_multi_score(pnum, score);
2143 Players[pnum].score = GET_INTEL_INT(buf + 2);
2145 multi_sort_kill_list();
2149 multi_do_trigger(char *buf)
2151 int pnum = (int)(buf[1]);
2152 int trigger = (int)(buf[2]);
2154 mprintf ((0,"MULTI doing trigger!\n"));
2156 if ((pnum < 0) || (pnum >= N_players) || (pnum == Player_num))
2158 Int3(); // Got trigger from illegal playernum
2161 if ((trigger < 0) || (trigger >= Num_triggers))
2163 Int3(); // Illegal trigger number in multiplayer
2166 check_trigger_sub(trigger, pnum,0);
2169 void multi_do_drop_marker (char *buf)
2172 int pnum=(int)(buf[1]);
2173 int mesnum=(int)(buf[2]);
2174 vms_vector position;
2176 if (pnum==Player_num) // my marker? don't set it down cuz it might screw up the orientation
2179 position.x = GET_INTEL_INT(buf + 3);
2180 position.y = GET_INTEL_INT(buf + 7);
2181 position.z = GET_INTEL_INT(buf + 11);
2184 MarkerMessage[(pnum*2)+mesnum][i]=buf[15+i];
2186 MarkerPoint[(pnum*2)+mesnum]=position;
2188 if (MarkerObject[(pnum*2)+mesnum] !=-1 && Objects[MarkerObject[(pnum*2)+mesnum]].type!=OBJ_NONE && MarkerObject[(pnum*2)+mesnum] !=0)
2189 obj_delete(MarkerObject[(pnum*2)+mesnum]);
2191 MarkerObject[(pnum*2)+mesnum] = drop_marker_object(&position,Objects[Players[Player_num].objnum].segnum,&Objects[Players[Player_num].objnum].orient,(pnum*2)+mesnum);
2192 strcpy (MarkerOwner[(pnum*2)+mesnum],Players[pnum].callsign);
2193 mprintf ((0,"Dropped player %d message: %s\n",pnum,MarkerMessage[(pnum*2)+mesnum]));
2197 void multi_do_hostage_door_status(char *buf)
2199 // Update hit point status of a door
2205 wallnum = GET_INTEL_SHORT(buf + count); count += 2;
2206 hps = GET_INTEL_INT(buf + count); count += 4;
2208 if ((wallnum < 0) || (wallnum > Num_walls) || (hps < 0) || (Walls[wallnum].type != WALL_BLASTABLE))
2210 Int3(); // Non-terminal, see Rob
2214 // mprintf((0, "Damaging wall number %d to %f points.\n", wallnum, f2fl(hps)));
2216 if (hps < Walls[wallnum].hps)
2217 wall_damage(&Segments[Walls[wallnum].segnum], Walls[wallnum].sidenum, Walls[wallnum].hps - hps);
2220 void multi_do_save_game(char *buf)
2227 slot = *(ubyte *)(buf+count); count += 1;
2228 id = GET_INTEL_INT(buf + count); count += 4;
2229 memcpy( desc, &buf[count], 20 ); count += 20;
2231 multi_save_game( slot, id, desc );
2234 void multi_do_restore_game(char *buf)
2240 slot = *(ubyte *)(buf+count); count += 1;
2241 id = GET_INTEL_INT(buf + count); count += 4;
2243 multi_restore_game( slot, id );
2247 void multi_do_req_player(char *buf)
2251 // Send my netplayer_stats to everyone!
2252 player_n = *(ubyte *)(buf+1);
2253 if ( (player_n == Player_num) || (player_n == 255) ) {
2254 extract_netplayer_stats( &ps, &Players[Player_num] );
2255 ps.Player_num = Player_num;
2256 ps.message_type = MULTI_SEND_PLAYER; // SET
2257 multi_send_data((char*)&ps, sizeof(netplayer_stats), 0);
2261 void multi_do_send_player(char *buf)
2263 // Got a player packet from someone!!!
2264 netplayer_stats * p;
2265 p = (netplayer_stats *)buf;
2267 Assert( p->Player_num <= N_players );
2269 mprintf(( 0, "Got netplayer_stats for player %d (I'm %d)\n", p->Player_num, Player_num ));
2270 mprintf(( 0, "Their shields are: %d\n", f2i(p->shields) ));
2272 use_netplayer_stats( &Players[p->Player_num], p );
2276 multi_reset_stuff(void)
2278 // A generic, emergency function to solve problems that crop up
2279 // when a player exits quick-out from the game because of a
2280 // serial connection loss. Fixes several weird bugs!
2284 Players[Player_num].homing_object_dist = -F1_0; // Turn off homing sound.
2286 Dead_player_camera = 0;
2287 Endlevel_sequence = 0;
2292 multi_reset_player_object(object *objp)
2296 //Init physics for a non-console player
2298 Assert(OBJECT_NUMBER(objp) >= 0);
2299 Assert(OBJECT_NUMBER(objp) <= Highest_object_index);
2300 Assert((objp->type == OBJ_PLAYER) || (objp->type == OBJ_GHOST));
2302 vm_vec_zero(&objp->mtype.phys_info.velocity);
2303 vm_vec_zero(&objp->mtype.phys_info.thrust);
2304 vm_vec_zero(&objp->mtype.phys_info.rotvel);
2305 vm_vec_zero(&objp->mtype.phys_info.rotthrust);
2306 objp->mtype.phys_info.brakes = objp->mtype.phys_info.turnroll = 0;
2307 objp->mtype.phys_info.mass = Player_ship->mass;
2308 objp->mtype.phys_info.drag = Player_ship->drag;
2309 // objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE | PF_USES_THRUST);
2310 objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE);
2314 objp->render_type = RT_POLYOBJ;
2315 objp->rtype.pobj_info.model_num = Player_ship->model_num; //what model is this?
2316 objp->rtype.pobj_info.subobj_flags = 0; //zero the flags
2317 for (i=0;i<MAX_SUBMODELS;i++)
2318 vm_angvec_zero(&objp->rtype.pobj_info.anim_angles[i]);
2320 //reset textures for this, if not player 0
2322 multi_reset_object_texture (objp);
2328 if (objp->type == OBJ_GHOST)
2329 objp->render_type = RT_NONE;
2333 void multi_reset_object_texture (object *objp)
2337 if (Game_mode & GM_TEAM)
2338 id = get_team(objp->id);
2343 objp->rtype.pobj_info.alt_textures=0;
2345 Assert(N_PLAYER_SHIP_TEXTURES == Polygon_models[objp->rtype.pobj_info.model_num].n_textures);
2347 for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
2348 multi_player_textures[id-1][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[objp->rtype.pobj_info.model_num].first_texture+i]];
2350 multi_player_textures[id-1][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2]];
2351 multi_player_textures[id-1][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2+1]];
2353 objp->rtype.pobj_info.alt_textures = id;
2361 extern int TTRecv[];
2362 extern FILE *RecieveLogFile;
2366 multi_process_bigdata(char *buf, int len)
2368 // Takes a bunch of messages, check them for validity,
2369 // and pass them to multi_process_data.
2371 int type, sub_len, bytes_processed = 0;
2373 while( bytes_processed < len ) {
2374 type = buf[bytes_processed];
2376 if ( (type<0) || (type>MULTI_MAX_TYPE)) {
2377 mprintf( (1, "multi_process_bigdata: Invalid packet type %d!\n", type ));
2380 sub_len = message_length[type];
2382 Assert(sub_len > 0);
2384 if ( (bytes_processed+sub_len) > len ) {
2385 mprintf( (1, "multi_process_bigdata: packet type %d too short (%d>%d)!\n", type, (bytes_processed+sub_len), len ));
2390 multi_process_data(&buf[bytes_processed], sub_len);
2391 bytes_processed += sub_len;
2396 // Part 2 : Functions that send communication messages to inform the other
2397 // players of something we did.
2401 multi_send_fire(void)
2403 if (!Network_laser_fired)
2406 multibuf[0] = (char)MULTI_FIRE;
2407 multibuf[1] = (char)Player_num;
2408 multibuf[2] = (char)Network_laser_gun;
2409 multibuf[3] = (char)Network_laser_level;
2410 multibuf[4] = (char)Network_laser_flags;
2411 multibuf[5] = (char)Network_laser_fired;
2413 PUT_INTEL_SHORT(multibuf+6, Network_laser_track);
2415 multi_send_data(multibuf, 8, 0);
2417 Network_laser_fired = 0;
2421 multi_send_destroy_controlcen(int objnum, int player)
2423 if (player == Player_num)
2424 HUD_init_message(TXT_YOU_DEST_CONTROL);
2425 else if ((player > 0) && (player < N_players))
2426 HUD_init_message("%s %s", Players[player].callsign, TXT_HAS_DEST_CONTROL);
2428 HUD_init_message(TXT_CONTROL_DESTROYED);
2430 multibuf[0] = (char)MULTI_CONTROLCEN;
2431 PUT_INTEL_SHORT(multibuf+1, objnum);
2432 multibuf[3] = player;
2433 multi_send_data(multibuf, 4, 2);
2436 void multi_send_drop_marker (int player,vms_vector position,char messagenum,char text[])
2440 if (player<N_players)
2442 mprintf ((0,"Sending MARKER drop!\n"));
2443 multibuf[0]=(char)MULTI_MARKER;
2444 multibuf[1]=(char)player;
2445 multibuf[2]=messagenum;
2446 PUT_INTEL_INT(multibuf+3, position.x);
2447 PUT_INTEL_INT(multibuf+7, position.y);
2448 PUT_INTEL_INT(multibuf+11, position.z);
2450 multibuf[15+i]=text[i];
2452 multi_send_data(multibuf, 55, 1);
2456 multi_send_endlevel_start(int secret)
2458 multibuf[0] = (char)MULTI_ENDLEVEL_START;
2459 multibuf[1] = Player_num;
2460 multibuf[2] = (char)secret;
2462 if ((secret) && !multi_goto_secret)
2463 multi_goto_secret = 1;
2464 else if (!multi_goto_secret)
2465 multi_goto_secret = 2;
2467 multi_send_data(multibuf, 3, 1);
2468 if (Game_mode & GM_NETWORK)
2470 Players[Player_num].connected = 5;
2471 network_send_endlevel_packet();
2476 multi_send_player_explode(char type)
2481 Assert( (type == MULTI_PLAYER_DROP) || (type == MULTI_PLAYER_EXPLODE) );
2483 multi_send_position(Players[Player_num].objnum);
2485 if (Network_send_objects)
2487 mprintf((0, "Resetting object sync due to player explosion.\n"));
2488 Network_send_objnum = -1;
2491 multibuf[count++] = type;
2492 multibuf[count++] = Player_num;
2494 PUT_INTEL_SHORT(multibuf+count, Players[Player_num].primary_weapon_flags);
2496 PUT_INTEL_SHORT(multibuf+count, Players[Player_num].secondary_weapon_flags);
2498 multibuf[count++] = (char)Players[Player_num].laser_level;
2500 multibuf[count++] = (char)Players[Player_num].secondary_ammo[HOMING_INDEX];
2501 multibuf[count++] = (char)Players[Player_num].secondary_ammo[CONCUSSION_INDEX];
2502 multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_INDEX];
2503 multibuf[count++] = (char)Players[Player_num].secondary_ammo[MEGA_INDEX];
2504 multibuf[count++] = (char)Players[Player_num].secondary_ammo[PROXIMITY_INDEX];
2506 multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE1_INDEX];
2507 multibuf[count++] = (char)Players[Player_num].secondary_ammo[GUIDED_INDEX];
2508 multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_MINE_INDEX];
2509 multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE4_INDEX];
2510 multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMISSILE5_INDEX];
2512 PUT_INTEL_SHORT(multibuf+count, Players[Player_num].primary_ammo[VULCAN_INDEX] );
2514 PUT_INTEL_SHORT(multibuf+count, Players[Player_num].primary_ammo[GAUSS_INDEX] );
2516 PUT_INTEL_INT(multibuf+count, Players[Player_num].flags );
2519 multibuf[count++] = Net_create_loc;
2521 Assert(Net_create_loc <= MAX_NET_CREATE_OBJECTS);
2523 memset(multibuf+count, -1, MAX_NET_CREATE_OBJECTS*sizeof(short));
2525 mprintf((0, "Created %d explosion objects.\n", Net_create_loc));
2527 for (i = 0; i < Net_create_loc; i++)
2529 if (Net_create_objnums[i] <= 0) {
2530 Int3(); // Illegal value in created egg object numbers
2535 PUT_INTEL_SHORT(multibuf+count, Net_create_objnums[i]); count += 2;
2537 // We created these objs so our local number = the network number
2538 map_objnum_local_to_local((short)Net_create_objnums[i]);
2543 // mprintf((1, "explode message size = %d, max = %d.\n", count, message_length[MULTI_PLAYER_EXPLODE]));
2545 if (count > message_length[MULTI_PLAYER_EXPLODE])
2550 multi_send_data(multibuf, message_length[MULTI_PLAYER_EXPLODE], 2);
2551 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
2552 multi_send_decloak();
2553 if (Game_mode & GM_MULTI_ROBOTS)
2554 multi_strip_robots(Player_num);
2557 extern ubyte Secondary_weapon_to_powerup[];
2558 extern ubyte Primary_weapon_to_powerup[];
2560 // put a lid on how many objects will be spewed by an exploding player
2561 // to prevent rampant powerups in netgames
2563 void multi_cap_objects ()
2568 if (!(Game_mode & GM_NETWORK))
2571 for (index=0;index<MAX_PRIMARY_WEAPONS;index++)
2573 type=Primary_weapon_to_powerup[index];
2574 if (PowerupsInMine[(int)type]>=MaxPowerupsAllowed[(int)type])
2575 if(Players[Player_num].primary_weapon_flags & (1 << index))
2577 mprintf ((0,"PIM=%d MPA=%d\n",PowerupsInMine[(int)type],MaxPowerupsAllowed[(int)type]));
2578 mprintf ((0,"Killing a primary cuz there's too many! (%d)\n",(int)type));
2579 Players[Player_num].primary_weapon_flags&=(~(1 << index));
2584 // Don't do the adjustment stuff for Hoard mode
2585 if (!(Game_mode & GM_HOARD))
2586 Players[Player_num].secondary_ammo[2]/=4;
2588 Players[Player_num].secondary_ammo[7]/=4;
2590 for (index=0;index<MAX_SECONDARY_WEAPONS;index++)
2592 if ((Game_mode & GM_HOARD) && index==PROXIMITY_INDEX)
2595 type=Secondary_weapon_to_powerup[index];
2597 if ((Players[Player_num].secondary_ammo[index]+PowerupsInMine[(int)type])>MaxPowerupsAllowed[(int)type])
2599 if (MaxPowerupsAllowed[(int)type]-PowerupsInMine[(int)type]<0)
2600 Players[Player_num].secondary_ammo[index]=0;
2602 Players[Player_num].secondary_ammo[index]=(MaxPowerupsAllowed[(int)type]-PowerupsInMine[(int)type]);
2604 mprintf ((0,"Hey! I killed secondary type %d because PIM=%d MPA=%d\n",(int)type,PowerupsInMine[(int)type],MaxPowerupsAllowed[(int)type]));
2608 if (!(Game_mode & GM_HOARD))
2609 Players[Player_num].secondary_ammo[2]*=4;
2610 Players[Player_num].secondary_ammo[7]*=4;
2612 if (Players[Player_num].laser_level > MAX_LASER_LEVEL)
2613 if (PowerupsInMine[POW_SUPER_LASER]+1 > MaxPowerupsAllowed[POW_SUPER_LASER])
2614 Players[Player_num].laser_level=0;
2616 if (Players[Player_num].flags & PLAYER_FLAGS_QUAD_LASERS)
2617 if (PowerupsInMine[POW_QUAD_FIRE]+1 > MaxPowerupsAllowed[POW_QUAD_FIRE])
2618 Players[Player_num].flags&=(~PLAYER_FLAGS_QUAD_LASERS);
2620 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)
2621 if (PowerupsInMine[POW_CLOAK]+1 > MaxPowerupsAllowed[POW_CLOAK])
2622 Players[Player_num].flags&=(~PLAYER_FLAGS_CLOAKED);
2624 if (Players[Player_num].flags & PLAYER_FLAGS_MAP_ALL)
2625 if (PowerupsInMine[POW_FULL_MAP]+1 > MaxPowerupsAllowed[POW_FULL_MAP])
2626 Players[Player_num].flags&=(~PLAYER_FLAGS_MAP_ALL);
2628 if (Players[Player_num].flags & PLAYER_FLAGS_AFTERBURNER)
2629 if (PowerupsInMine[POW_AFTERBURNER]+1 > MaxPowerupsAllowed[POW_AFTERBURNER])
2630 Players[Player_num].flags&=(~PLAYER_FLAGS_AFTERBURNER);
2632 if (Players[Player_num].flags & PLAYER_FLAGS_AMMO_RACK)
2633 if (PowerupsInMine[POW_AMMO_RACK]+1 > MaxPowerupsAllowed[POW_AMMO_RACK])
2634 Players[Player_num].flags&=(~PLAYER_FLAGS_AMMO_RACK);
2636 if (Players[Player_num].flags & PLAYER_FLAGS_CONVERTER)
2637 if (PowerupsInMine[POW_CONVERTER]+1 > MaxPowerupsAllowed[POW_CONVERTER])
2638 Players[Player_num].flags&=(~PLAYER_FLAGS_CONVERTER);
2640 if (Players[Player_num].flags & PLAYER_FLAGS_HEADLIGHT)
2641 if (PowerupsInMine[POW_HEADLIGHT]+1 > MaxPowerupsAllowed[POW_HEADLIGHT])
2642 Players[Player_num].flags&=(~PLAYER_FLAGS_HEADLIGHT);
2644 if (Game_mode & GM_CAPTURE)
2646 if (Players[Player_num].flags & PLAYER_FLAGS_FLAG)
2648 if (get_team(Player_num)==TEAM_RED)
2649 flagtype=POW_FLAG_BLUE;
2651 flagtype=POW_FLAG_RED;
2653 if (PowerupsInMine[(int)flagtype]+1 > MaxPowerupsAllowed[(int)flagtype])
2654 Players[Player_num].flags&=(~PLAYER_FLAGS_FLAG);
2660 // adds players inventory to multi cap
2662 void multi_adjust_cap_for_player (int pnum)
2668 if (!(Game_mode & GM_NETWORK))
2671 for (index=0;index<MAX_PRIMARY_WEAPONS;index++)
2673 type=Primary_weapon_to_powerup[index];
2674 if (Players[pnum].primary_weapon_flags & (1 << index))
2675 MaxPowerupsAllowed[(int)type]++;
2678 for (index=0;index<MAX_SECONDARY_WEAPONS;index++)
2680 type=Secondary_weapon_to_powerup[index];
2681 MaxPowerupsAllowed[(int)type]+=Players[pnum].secondary_ammo[index];
2684 if (Players[pnum].laser_level > MAX_LASER_LEVEL)
2685 MaxPowerupsAllowed[POW_SUPER_LASER]++;
2687 if (Players[pnum].flags & PLAYER_FLAGS_QUAD_LASERS)
2688 MaxPowerupsAllowed[POW_QUAD_FIRE]++;
2690 if (Players[pnum].flags & PLAYER_FLAGS_CLOAKED)
2691 MaxPowerupsAllowed[POW_CLOAK]++;
2693 if (Players[pnum].flags & PLAYER_FLAGS_MAP_ALL)
2694 MaxPowerupsAllowed[POW_FULL_MAP]++;
2696 if (Players[pnum].flags & PLAYER_FLAGS_AFTERBURNER)
2697 MaxPowerupsAllowed[POW_AFTERBURNER]++;
2699 if (Players[pnum].flags & PLAYER_FLAGS_AMMO_RACK)
2700 MaxPowerupsAllowed[POW_AMMO_RACK]++;
2702 if (Players[pnum].flags & PLAYER_FLAGS_CONVERTER)
2703 MaxPowerupsAllowed[POW_CONVERTER]++;
2705 if (Players[pnum].flags & PLAYER_FLAGS_HEADLIGHT)
2706 MaxPowerupsAllowed[POW_HEADLIGHT]++;
2709 void multi_adjust_remote_cap (int pnum)
2715 if (!(Game_mode & GM_NETWORK))
2718 for (index=0;index<MAX_PRIMARY_WEAPONS;index++)
2720 type=Primary_weapon_to_powerup[index];
2721 if (Players[pnum].primary_weapon_flags & (1 << index))
2722 PowerupsInMine[(int)type]++;
2725 for (index=0;index<MAX_SECONDARY_WEAPONS;index++)
2727 type=Secondary_weapon_to_powerup[index];
2729 if ((Game_mode & GM_HOARD) && index==2)
2732 if (index==2 || index==7) // PROX or SMARTMINES? Those bastards...
2733 PowerupsInMine[(int)type]+=(Players[pnum].secondary_ammo[index]/4);
2735 PowerupsInMine[(int)type]+=Players[pnum].secondary_ammo[index];
2739 if (Players[pnum].laser_level > MAX_LASER_LEVEL)
2740 PowerupsInMine[POW_SUPER_LASER]++;
2742 if (Players[pnum].flags & PLAYER_FLAGS_QUAD_LASERS)
2743 PowerupsInMine[POW_QUAD_FIRE]++;
2745 if (Players[pnum].flags & PLAYER_FLAGS_CLOAKED)
2746 PowerupsInMine[POW_CLOAK]++;
2748 if (Players[pnum].flags & PLAYER_FLAGS_MAP_ALL)
2749 PowerupsInMine[POW_FULL_MAP]++;
2751 if (Players[pnum].flags & PLAYER_FLAGS_AFTERBURNER)
2752 PowerupsInMine[POW_AFTERBURNER]++;
2754 if (Players[pnum].flags & PLAYER_FLAGS_AMMO_RACK)
2755 PowerupsInMine[POW_AMMO_RACK]++;
2757 if (Players[pnum].flags & PLAYER_FLAGS_CONVERTER)
2758 PowerupsInMine[POW_CONVERTER]++;
2760 if (Players[pnum].flags & PLAYER_FLAGS_HEADLIGHT)
2761 PowerupsInMine[POW_HEADLIGHT]++;
2766 multi_send_message(void)
2769 if (Network_message_reciever != -1)
2771 multibuf[loc] = (char)MULTI_MESSAGE; loc += 1;
2772 multibuf[loc] = (char)Player_num; loc += 1;
2773 strncpy(multibuf+loc, Network_message, MAX_MESSAGE_LEN); loc += MAX_MESSAGE_LEN;
2774 multibuf[loc-1] = '\0';
2775 multi_send_data(multibuf, loc, 0);
2776 Network_message_reciever = -1;
2781 multi_send_reappear()
2783 multibuf[0] = (char)MULTI_REAPPEAR;
2784 PUT_INTEL_SHORT(multibuf+1, Players[Player_num].objnum);
2786 multi_send_data(multibuf, 3, 2);
2787 PKilledFlags[Player_num]=0;
2791 multi_send_position(int objnum)
2793 #ifdef WORDS_BIGENDIAN
2798 if (Game_mode & GM_NETWORK) {
2802 multibuf[count++] = (char)MULTI_POSITION;
2803 #ifndef WORDS_BIGENDIAN
2804 create_shortpos((shortpos *)(multibuf+count), &Objects[objnum], 0);
2805 count += sizeof(shortpos);
2807 create_shortpos(&sp, &Objects[objnum], 1);
2808 memcpy(&(multibuf[count]), (ubyte *)(sp.bytemat), 9);
2810 memcpy(&(multibuf[count]), (ubyte *)&(sp.xo), 14);
2814 multi_send_data(multibuf, count, 0);
2818 multi_send_kill(int objnum)
2820 // I died, tell the world.
2825 Assert(Objects[objnum].id == Player_num);
2826 killer_objnum = Players[Player_num].killer_objnum;
2828 multi_compute_kill(killer_objnum, objnum);
2830 multibuf[0] = (char)MULTI_KILL; count += 1;
2831 multibuf[1] = Player_num; count += 1;
2832 if (killer_objnum > -1) {
2833 short s; // do it with variable since INTEL_SHORT won't work on return val from function.
2835 s = (short)objnum_local_to_remote(killer_objnum, (sbyte *)&multibuf[count+2]);
2836 PUT_INTEL_SHORT(multibuf+count, s);
2840 PUT_INTEL_SHORT(multibuf+count, -1);
2841 multibuf[count+2] = (char)-1;
2844 multi_send_data(multibuf, count, 1);
2847 if (Game_mode & GM_MULTI_ROBOTS)
2848 multi_strip_robots(Player_num);
2853 multi_send_remobj(int objnum)
2855 // Tell the other guy to remove an object from his list
2858 short remote_objnum;
2860 if (Objects[objnum].type==OBJ_POWERUP && (Game_mode & GM_NETWORK))
2862 if (PowerupsInMine[Objects[objnum].id] > 0)
2864 PowerupsInMine[Objects[objnum].id]--;
2865 if (multi_powerup_is_4pack (Objects[objnum].id))
2867 mprintf ((0,"Hey babe! Doing that wacky 4 pack stuff."));
2869 if (PowerupsInMine[Objects[objnum].id-1]-4<0)
2870 PowerupsInMine[Objects[objnum].id-1]=0;
2872 PowerupsInMine[Objects[objnum].id-1]-=4;
2878 multibuf[0] = (char)MULTI_REMOVE_OBJECT;
2880 remote_objnum = objnum_local_to_remote((short)objnum, &obj_owner);
2882 PUT_INTEL_SHORT(multibuf+1, remote_objnum); // Map to network objnums
2884 multibuf[3] = obj_owner;
2886 // mprintf((0, "multi_send_remobj: %d = %d owner %d.\n", objnum, remote_objnum, obj_owner));
2888 multi_send_data(multibuf, 4, 0);
2890 if (Network_send_objects && network_objnum_is_past(objnum))
2892 mprintf((0, "Resetting object sync due to object removal.\n"));
2893 Network_send_objnum = -1;
2898 multi_send_quit(int why)
2900 // I am quitting the game, tell the other guy the bad news.
2902 Assert (why == MULTI_QUIT);
2904 multibuf[0] = (char)why;
2905 multibuf[1] = Player_num;
2906 multi_send_data(multibuf, 2, 1);
2911 multi_send_cloak(void)
2913 // Broadcast a change in our pflags (made to support cloaking)
2915 multibuf[0] = MULTI_CLOAK;
2916 multibuf[1] = (char)Player_num;
2918 multi_send_data(multibuf, 2, 1);
2921 if (Game_mode & GM_MULTI_ROBOTS)
2922 multi_strip_robots(Player_num);
2927 multi_send_decloak(void)
2929 // Broadcast a change in our pflags (made to support cloaking)
2931 multibuf[0] = MULTI_DECLOAK;
2932 multibuf[1] = (char)Player_num;
2934 multi_send_data(multibuf, 2, 1);
2938 multi_send_door_open(int segnum, int side,ubyte flag)
2940 // When we open a door make sure everyone else opens that door
2942 multibuf[0] = MULTI_DOOR_OPEN;
2943 PUT_INTEL_SHORT(multibuf+1, segnum );
2944 multibuf[3] = (sbyte)side;
2947 multi_send_data(multibuf, 5, 2);
2950 extern void network_send_naked_packet (char *,short,int);
2953 multi_send_door_open_specific(int pnum,int segnum, int side,ubyte flag)
2955 // For sending doors only to a specific person (usually when they're joining)
2957 Assert (Game_mode & GM_NETWORK);
2958 // Assert (pnum>-1 && pnum<N_players);
2960 multibuf[0] = MULTI_DOOR_OPEN;
2961 PUT_INTEL_SHORT(multibuf+1, segnum);
2962 multibuf[3] = (sbyte)side;
2965 network_send_naked_packet(multibuf, 5, pnum);
2969 // Part 3 : Functions that change or prepare the game for multiplayer use.
2970 // Not including functions needed to syncronize or start the
2971 // particular type of multiplayer game. Includes preparing the
2972 // mines, player structures, etc.
2975 multi_send_create_explosion(int pnum)
2977 // Send all data needed to create a remote explosion
2981 multibuf[count] = MULTI_CREATE_EXPLOSION; count += 1;
2982 multibuf[count] = (sbyte)pnum; count += 1;
2986 multi_send_data(multibuf, count, 0);
2990 multi_send_controlcen_fire(vms_vector *to_goal, int best_gun_num, int objnum)
2992 #ifdef WORDS_BIGENDIAN
2993 vms_vector swapped_vec;
2997 multibuf[count] = MULTI_CONTROLCEN_FIRE; count += 1;
2998 #ifndef WORDS_BIGENDIAN
2999 memcpy(multibuf+count, to_goal, 12); count += 12;
3001 swapped_vec.x = (fix)INTEL_INT( (int)to_goal->x );
3002 swapped_vec.y = (fix)INTEL_INT( (int)to_goal->y );
3003 swapped_vec.z = (fix)INTEL_INT( (int)to_goal->z );
3004 memcpy(multibuf+count, &swapped_vec, 12); count += 12;
3006 multibuf[count] = (char)best_gun_num; count += 1;
3007 PUT_INTEL_SHORT(multibuf+count, objnum ); count += 2;
3010 multi_send_data(multibuf, count, 0);
3014 multi_send_create_powerup(int powerup_type, int segnum, int objnum, vms_vector *pos)
3016 // Create a powerup on a remote machine, used for remote
3017 // placement of used powerups like missiles and cloaking
3020 #ifdef WORDS_BIGENDIAN
3021 vms_vector swapped_vec;
3025 if (Game_mode & GM_NETWORK)
3026 PowerupsInMine[powerup_type]++;
3028 multibuf[count] = MULTI_CREATE_POWERUP; count += 1;
3029 multibuf[count] = Player_num; count += 1;
3030 multibuf[count] = powerup_type; count += 1;
3031 PUT_INTEL_SHORT(multibuf+count, segnum ); count += 2;
3032 PUT_INTEL_SHORT(multibuf+count, objnum ); count += 2;
3033 #ifndef WORDS_BIGENDIAN
3034 memcpy(multibuf+count, pos, sizeof(vms_vector)); count += sizeof(vms_vector);
3036 swapped_vec.x = (fix)INTEL_INT( (int)pos->x );
3037 swapped_vec.y = (fix)INTEL_INT( (int)pos->y );
3038 swapped_vec.z = (fix)INTEL_INT( (int)pos->z );
3039 memcpy(multibuf+count, &swapped_vec, 12); count += 12;
3043 multi_send_data(multibuf, count, 2);
3045 if (Network_send_objects && network_objnum_is_past(objnum))
3047 mprintf((0, "Resetting object sync due to powerup creation.\n"));
3048 Network_send_objnum = -1;
3051 mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum));
3052 map_objnum_local_to_local(objnum);
3056 multi_send_play_sound(int sound_num, fix volume)
3059 multibuf[count] = MULTI_PLAY_SOUND; count += 1;
3060 multibuf[count] = Player_num; count += 1;
3061 multibuf[count] = (char)sound_num; count += 1;
3062 multibuf[count] = (char)(volume >> 12); count += 1;
3065 multi_send_data(multibuf, count, 0);
3069 multi_send_audio_taunt(int taunt_num)
3071 return; // Taken out, awaiting sounds..
3074 int audio_taunts[4] = {
3075 SOUND_CONTROL_CENTER_WARNING_SIREN,
3076 SOUND_HOSTAGE_RESCUED,
3077 SOUND_REFUEL_STATION_GIVING_FUEL,
3082 Assert(taunt_num >= 0);
3083 Assert(taunt_num < 4);
3085 digi_play_sample( audio_taunts[taunt_num], F1_0 );
3086 multi_send_play_sound(audio_taunts[taunt_num], F1_0);
3091 multi_send_score(void)
3093 // Send my current score to all other players so it will remain
3097 if (Game_mode & GM_MULTI_COOP) {
3098 multi_sort_kill_list();
3099 multibuf[count] = MULTI_SCORE; count += 1;
3100 multibuf[count] = Player_num; count += 1;
3101 PUT_INTEL_INT(multibuf+count, Players[Player_num].score); count += 4;
3102 multi_send_data(multibuf, count, 0);
3108 multi_send_save_game(ubyte slot, uint id, char * desc)
3112 multibuf[count] = MULTI_SAVE_GAME; count += 1;
3113 multibuf[count] = slot; count += 1; // Save slot=0
3114 PUT_INTEL_INT(multibuf+count, id ); count += 4; // Save id
3115 memcpy( &multibuf[count], desc, 20 ); count += 20;
3117 multi_send_data(multibuf, count, 2);
3121 multi_send_restore_game(ubyte slot, uint id)
3125 multibuf[count] = MULTI_RESTORE_GAME; count += 1;
3126 multibuf[count] = slot; count += 1; // Save slot=0
3127 PUT_INTEL_INT(multibuf+count, id); count += 4; // Save id
3129 multi_send_data(multibuf, count, 2);
3133 multi_send_netplayer_stats_request(ubyte player_num)
3137 multibuf[count] = MULTI_REQ_PLAYER; count += 1;
3138 multibuf[count] = player_num; count += 1;
3140 multi_send_data(multibuf, count, 0 );
3144 multi_send_trigger(int triggernum)
3146 // Send an even to trigger something in the mine
3150 multibuf[count] = MULTI_TRIGGER; count += 1;
3151 multibuf[count] = Player_num; count += 1;
3152 multibuf[count] = (ubyte)triggernum; count += 1;
3154 mprintf ((0,"Sending trigger %d\n",triggernum));
3156 multi_send_data(multibuf, count, 1);
3157 //multi_send_data(multibuf, count, 1); // twice?
3161 multi_send_hostage_door_status(int wallnum)
3163 // Tell the other player what the hit point status of a hostage door
3168 Assert(Walls[wallnum].type == WALL_BLASTABLE);
3170 multibuf[count] = MULTI_HOSTAGE_DOOR; count += 1;
3171 PUT_INTEL_SHORT(multibuf+count, wallnum ); count += 2;
3172 PUT_INTEL_INT(multibuf+count, Walls[wallnum].hps ); count += 4;
3174 // mprintf((0, "Door %d damaged by %f points.\n", wallnum, f2fl(Walls[wallnum].hps)));
3176 multi_send_data(multibuf, count, 0);
3179 extern int ConsistencyCount;
3180 extern int Drop_afterburner_blob_flag;
3184 void multi_prep_level(void)
3186 // Do any special stuff to the level required for serial games
3187 // before we begin playing in it.
3189 // Player_num MUST be set before calling this procedure.
3191 // This function must be called before checksuming the Object array,
3192 // since the resulting checksum with depend on the value of Player_num
3193 // at the time this is called.
3196 int cloak_count, inv_count;
3198 Assert(Game_mode & GM_MULTI);
3200 Assert(NumNetPlayerPositions > 0);
3204 Drop_afterburner_blob_flag=0;
3207 for (i=0;i<MAX_NUM_NET_PLAYERS;i++)
3210 for (i = 0; i < NumNetPlayerPositions; i++)
3212 if (i != Player_num)
3213 Objects[Players[i].objnum].control_type = CT_REMOTE;
3214 Objects[Players[i].objnum].movement_type = MT_PHYSICS;
3215 multi_reset_player_object(&Objects[Players[i].objnum]);
3216 LastPacketTime[i] = 0;
3220 for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
3222 robot_controlled[i] = -1;
3223 robot_agitation[i] = 0;
3228 Viewer = ConsoleObject = &Objects[Players[Player_num].objnum];
3230 if (!(Game_mode & GM_MULTI_COOP))
3232 multi_delete_extra_objects(); // Removes monsters from level
3235 if (Game_mode & GM_MULTI_ROBOTS)
3237 multi_set_robot_ai(); // Set all Robot AI to types we can cope with
3240 if (Game_mode & GM_NETWORK)
3242 multi_adjust_cap_for_player(Player_num);
3243 multi_send_powerup_update();
3244 ng=1; // ng means network game
3250 for (i=0; i<=Highest_object_index; i++)
3254 if ((Objects[i].type == OBJ_HOSTAGE) && !(Game_mode & GM_MULTI_COOP))
3256 objnum = obj_create(OBJ_POWERUP, POW_SHIELD_BOOST, Objects[i].segnum, &Objects[i].pos, &vmd_identity_matrix, Powerup_info[POW_SHIELD_BOOST].size, CT_POWERUP, MT_PHYSICS, RT_POWERUP);
3260 Objects[objnum].rtype.vclip_info.vclip_num = Powerup_info[POW_SHIELD_BOOST].vclip_num;
3261 Objects[objnum].rtype.vclip_info.frametime = Vclip[Objects[objnum].rtype.vclip_info.vclip_num].frame_time;
3262 Objects[objnum].rtype.vclip_info.framenum = 0;
3263 Objects[objnum].mtype.phys_info.drag = 512; //1024;
3264 Objects[objnum].mtype.phys_info.mass = F1_0;
3265 vm_vec_zero(&Objects[objnum].mtype.phys_info.velocity);
3270 if (Objects[i].type == OBJ_POWERUP)
3272 if (Objects[i].id == POW_EXTRA_LIFE)
3274 if (ng && !Netgame.DoInvulnerability)
3276 Objects[i].id = POW_SHIELD_BOOST;
3277 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3278 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3282 Objects[i].id = POW_INVULNERABILITY;
3283 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3284 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3289 if (!(Game_mode & GM_MULTI_COOP))
3290 if ((Objects[i].id >= POW_KEY_BLUE) && (Objects[i].id <= POW_KEY_GOLD))
3292 Objects[i].id = POW_SHIELD_BOOST;
3293 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3294 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3297 if (Objects[i].id == POW_INVULNERABILITY) {
3298 if (inv_count >= 3 || (ng && !Netgame.DoInvulnerability)) {
3299 mprintf((0, "Bashing Invulnerability object #%i to shield.\n", i));
3300 Objects[i].id = POW_SHIELD_BOOST;
3301 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3302 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3307 if (Objects[i].id == POW_CLOAK) {
3308 if (cloak_count >= 3 || (ng && !Netgame.DoCloak)) {
3309 mprintf((0, "Bashing Cloak object #%i to shield.\n", i));
3310 Objects[i].id = POW_SHIELD_BOOST;
3311 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3312 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3317 if (Objects[i].id == POW_AFTERBURNER && ng && !Netgame.DoAfterburner)
3318 bash_to_shield (i,"afterburner");
3319 if (Objects[i].id == POW_FUSION_WEAPON && ng && !Netgame.DoFusions)
3320 bash_to_shield (i,"fusion");
3321 if (Objects[i].id == POW_PHOENIX_WEAPON && ng && !Netgame.DoPhoenix)
3322 bash_to_shield (i,"phoenix");
3324 if (Objects[i].id == POW_HELIX_WEAPON && ng && !Netgame.DoHelix)
3325 bash_to_shield (i,"helix");
3327 if (Objects[i].id == POW_MEGA_WEAPON && ng && !Netgame.DoMegas)
3328 bash_to_shield (i,"mega");
3330 if (Objects[i].id == POW_SMARTBOMB_WEAPON && ng && !Netgame.DoSmarts)
3331 bash_to_shield (i,"smartmissile");
3333 if (Objects[i].id == POW_GAUSS_WEAPON && ng && !Netgame.DoGauss)
3334 bash_to_shield (i,"gauss");
3336 if (Objects[i].id == POW_VULCAN_WEAPON && ng && !Netgame.DoVulcan)
3337 bash_to_shield (i,"vulcan");
3339 if (Objects[i].id == POW_PLASMA_WEAPON && ng && !Netgame.DoPlasma)
3340 bash_to_shield (i,"plasma");
3342 if (Objects[i].id == POW_OMEGA_WEAPON && ng && !Netgame.DoOmega)
3343 bash_to_shield (i,"omega");
3345 if (Objects[i].id == POW_SUPER_LASER && ng && !Netgame.DoSuperLaser)
3346 bash_to_shield (i,"superlaser");
3348 if (Objects[i].id == POW_PROXIMITY_WEAPON && ng && !Netgame.DoProximity)
3349 bash_to_shield (i,"proximity");
3351 // Special: Make all proximity bombs into shields if in
3352 // hoard mode because we use the proximity slot in the
3353 // player struct to signify how many orbs the player has.
3355 if (Objects[i].id == POW_PROXIMITY_WEAPON && ng && (Game_mode & GM_HOARD))
3356 bash_to_shield (i,"proximity");
3358 if (Objects[i].id==POW_VULCAN_AMMO && ng && (!Netgame.DoVulcan && !Netgame.DoGauss))
3359 bash_to_shield(i,"vulcan ammo");
3361 if (Objects[i].id == POW_SPREADFIRE_WEAPON && ng && !Netgame.DoSpread)
3362 bash_to_shield (i,"spread");
3363 if (Objects[i].id == POW_SMART_MINE && ng && !Netgame.DoSmartMine)
3364 bash_to_shield (i,"smartmine");
3365 if (Objects[i].id == POW_SMISSILE1_1 && ng && !Netgame.DoFlash)
3366 bash_to_shield (i,"flash");
3367 if (Objects[i].id == POW_SMISSILE1_4 && ng && !Netgame.DoFlash)
3368 bash_to_shield (i,"flash");
3369 if (Objects[i].id == POW_GUIDED_MISSILE_1 && ng && !Netgame.DoGuided)
3370 bash_to_shield (i,"guided");
3371 if (Objects[i].id == POW_GUIDED_MISSILE_4 && ng && !Netgame.DoGuided)
3372 bash_to_shield (i,"guided");
3373 if (Objects[i].id == POW_EARTHSHAKER_MISSILE && ng && !Netgame.DoEarthShaker)
3374 bash_to_shield (i,"earth");
3375 if (Objects[i].id == POW_MERCURY_MISSILE_1 && ng && !Netgame.DoMercury)
3376 bash_to_shield (i,"Mercury");
3377 if (Objects[i].id == POW_MERCURY_MISSILE_4 && ng && !Netgame.DoMercury)
3378 bash_to_shield (i,"Mercury");
3379 if (Objects[i].id == POW_CONVERTER && ng && !Netgame.DoConverter)
3380 bash_to_shield (i,"Converter");
3381 if (Objects[i].id == POW_AMMO_RACK && ng && !Netgame.DoAmmoRack)
3382 bash_to_shield (i,"Ammo rack");
3383 if (Objects[i].id == POW_HEADLIGHT && ng && !Netgame.DoHeadlight)
3384 bash_to_shield (i,"Headlight");
3385 if (Objects[i].id == POW_LASER && ng && !Netgame.DoLaserUpgrade)
3386 bash_to_shield (i,"Laser powerup");
3387 if (Objects[i].id == POW_HOMING_AMMO_1 && ng && !Netgame.DoHoming)
3388 bash_to_shield (i,"Homing");
3389 if (Objects[i].id == POW_HOMING_AMMO_4 && ng && !Netgame.DoHoming)
3390 bash_to_shield (i,"Homing");
3391 if (Objects[i].id == POW_QUAD_FIRE && ng && !Netgame.DoQuadLasers)
3392 bash_to_shield (i,"Quad Lasers");
3393 if (Objects[i].id == POW_FLAG_BLUE && !(Game_mode & GM_CAPTURE))
3394 bash_to_shield (i,"Blue flag");
3395 if (Objects[i].id == POW_FLAG_RED && !(Game_mode & GM_CAPTURE))
3396 bash_to_shield (i,"Red flag");
3400 if (Game_mode & GM_HOARD)
3403 if ((Game_mode & GM_CAPTURE) || (Game_mode & GM_HOARD))
3404 multi_apply_goal_textures();
3406 multi_sort_kill_list();
3408 multi_show_player_list();
3410 ConsoleObject->control_type = CT_FLYING;
3412 reset_player_object();
3416 int Goal_blue_segnum,Goal_red_segnum;
3418 void multi_apply_goal_textures()
3424 for (i=0; i <= Highest_segment_index; i++)
3427 seg2 = &Segment2s[i];
3429 if (seg2->special==SEGMENT_IS_GOAL_BLUE)
3432 Goal_blue_segnum = i;
3434 if (Game_mode & GM_HOARD)
3435 tex=find_goal_texture (TMI_GOAL_HOARD);
3437 tex=find_goal_texture (TMI_GOAL_BLUE);
3440 for (j = 0; j < 6; j++) {
3442 seg->sides[j].tmap_num=tex;
3444 seg->sides[j].uvls[v].l = i2f(100); //max out
3447 seg2->static_light = i2f(100); //make static light bright
3451 if (seg2->special==SEGMENT_IS_GOAL_RED)
3453 Goal_red_segnum = i;
3455 // Make both textures the same if Hoard mode
3457 if (Game_mode & GM_HOARD)
3458 tex=find_goal_texture (TMI_GOAL_HOARD);
3460 tex=find_goal_texture (TMI_GOAL_RED);
3463 for (j = 0; j < 6; j++) {
3465 seg->sides[j].tmap_num=tex;
3467 seg->sides[j].uvls[v].l = i2f(1000); //max out
3470 seg2->static_light = i2f(100); //make static light bright
3474 int find_goal_texture (ubyte t)
3478 for (i=0;i<NumTextures;i++)
3479 if (TmapInfo[i].flags & t)
3482 Int3(); // Hey, there is no goal texture for this PIG!!!!
3483 // Edit bitmaps.tbl and designate two textures to be RED and BLUE
3489 /* DPH: Moved to gameseq.c
3490 void bash_to_shield (int i,char *s)
3492 int type=Objects[i].id;
3494 mprintf((0, "Bashing %s object #%i to shield.\n",s, i));
3496 PowerupsInMine[type]=MaxPowerupsAllowed[type]=0;
3498 Objects[i].id = POW_SHIELD_BOOST;
3499 Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num;
3500 Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time;
3504 void multi_set_robot_ai(void)
3506 // Go through the objects array looking for robots and setting
3507 // them to certain supported types of NET AI behavior.
3511 // for (i = 0; i <= Highest_object_index; i++)
3513 // if (Objects[i].type == OBJ_ROBOT) {
3514 // Objects[i].ai_info.REMOTE_OWNER = -1;
3515 // if (Objects[i].ai_info.behavior == AIB_STATION)
3516 // Objects[i].ai_info.behavior = AIB_NORMAL;
3521 int multi_delete_extra_objects()
3527 // Go through the object list and remove any objects not used in
3528 // 'Anarchy!' games.
3530 // This function also prints the total number of available multiplayer
3531 // positions in this level, even though this should always be 8 or more!
3534 for (i=0;i<=Highest_object_index;i++) {
3535 if ((objp->type==OBJ_PLAYER) || (objp->type==OBJ_GHOST))
3537 else if ((objp->type==OBJ_ROBOT) && (Game_mode & GM_MULTI_ROBOTS))
3539 else if ( (objp->type!=OBJ_NONE) && (objp->type!=OBJ_PLAYER) && (objp->type!=OBJ_POWERUP) && (objp->type!=OBJ_CNTRLCEN) && (objp->type!=OBJ_HOSTAGE) && !(objp->type==OBJ_WEAPON && objp->id==PMINE_ID) ) {
3540 // Before deleting object, if it's a robot, drop it's special powerup, if any
3541 if (objp->type == OBJ_ROBOT)
3542 if (objp->contains_count && (objp->contains_type == OBJ_POWERUP))
3543 object_create_egg(objp);
3552 void change_playernum_to( int new_Player_num )
3554 if (Player_num > -1)
3555 memcpy( Players[new_Player_num].callsign, Players[Player_num].callsign, CALLSIGN_LEN+1 );
3556 Player_num = new_Player_num;
3559 int multi_all_players_alive()
3562 for (i=0;i<N_players;i++)
3564 if (PKilledFlags[i] && Players[i].connected)
3570 void multi_initiate_save_game()
3577 if ((Endlevel_sequence) || (Control_center_destroyed))
3580 if (!multi_all_players_alive())
3582 HUD_init_message ("Can't save...all players must be alive!");
3586 //multi_send_netplayer_stats_request(255);
3591 slot = state_get_save_file(filename, desc, 1, 0);
3600 // Make a unique game id
3601 game_id = timer_get_fixed_seconds();
3602 game_id ^= N_players<<4;
3603 for (i=0; i<N_players; i++ )
3606 memcpy(&call2i, Players[i].callsign, sizeof(fix));
3609 if ( game_id == 0 ) game_id = 1; // 0 is invalid
3611 mprintf(( 1, "Game_id = %8x\n", game_id));
3612 multi_send_save_game(slot, game_id, desc );
3614 multi_save_game(slot,game_id, desc );
3617 extern int state_get_game_id(char *);
3619 void multi_initiate_restore_game()
3624 if ((Endlevel_sequence) || (Control_center_destroyed))
3627 if (!multi_all_players_alive())
3629 HUD_init_message ("Can't restore...all players must be alive!");
3634 slot = state_get_restore_file(filename,1);
3639 state_game_id=state_get_game_id (filename);
3645 multi_send_restore_game(slot,state_game_id);
3647 multi_restore_game(slot,state_game_id);
3650 void multi_save_game(ubyte slot, uint id, char *desc)
3654 if ((Endlevel_sequence) || (Control_center_destroyed))
3657 sprintf( filename, PLAYER_DIR "%s.mg%d", Players[Player_num].callsign, slot );
3658 mprintf(( 0, "Save game %x on slot %d\n", id, slot ));
3659 HUD_init_message( "Saving game #%d, '%s'", slot, desc );
3662 state_save_all_sub(filename, desc, 0 );
3665 void multi_restore_game(ubyte slot, uint id)
3668 player saved_player;
3672 if ((Endlevel_sequence) || (Control_center_destroyed))
3675 mprintf(( 0, "Restore game %x from slot %d\n", id, slot ));
3676 saved_player = Players[Player_num];
3677 sprintf( filename, PLAYER_DIR "%s.mg%d", Players[Player_num].callsign, slot );
3679 for (i=0;i<N_players;i++)
3680 multi_strip_robots(i);
3682 thisid=state_get_game_id (filename);
3685 multi_bad_restore ();
3689 pnum=state_restore_all_sub( filename, 1, 0 );
3691 mprintf ((0,"StateId=%d ThisID=%d\n",state_game_id,id));
3694 if (state_game_id != id ) {
3695 // Game doesn't match!!!
3696 nm_messagebox( "Error", 1, "Ok", "Cannot restore saved game" );
3697 Game_mode |= GM_GAME_OVER;
3698 Function_mode = FMODE_MENU;
3699 longjmp(LeaveGame, 0);
3702 change_playernum_to(pnum-1);
3703 memcpy( Players[Player_num].callsign, saved_player.callsign, CALLSIGN_LEN+1 );
3704 memcpy( Players[Player_num].net_address, saved_player.net_address, 6 );
3705 Players[Player_num].connected = saved_player.connected;
3706 Players[Player_num].n_packets_got = saved_player.n_packets_got;
3707 Players[Player_num].n_packets_sent = saved_player.n_packets_sent;
3708 Viewer = ConsoleObject = &Objects[pnum-1];
3714 void extract_netplayer_stats( netplayer_stats *ps, player * pd )
3718 ps->flags = INTEL_INT(pd->flags); // Powerup flags, see below...
3719 ps->energy = (fix)INTEL_INT(pd->energy); // Amount of energy remaining.
3720 ps->shields = (fix)INTEL_INT(pd->shields); // shields remaining (protection)
3721 ps->lives = pd->lives; // Lives remaining, 0 = game over.
3722 ps->laser_level = pd->laser_level; // Current level of the laser.
3723 ps->primary_weapon_flags=pd->primary_weapon_flags; // bit set indicates the player has this weapon.
3724 ps->secondary_weapon_flags=pd->secondary_weapon_flags; // bit set indicates the player has this weapon.
3725 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
3726 ps->primary_ammo[i] = INTEL_SHORT(pd->primary_ammo[i]);
3727 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
3728 ps->secondary_ammo[i] = INTEL_SHORT(pd->secondary_ammo[i]);
3730 //memcpy( ps->primary_ammo, pd->primary_ammo, MAX_PRIMARY_WEAPONS*sizeof(short) ); // How much ammo of each type.
3731 //memcpy( ps->secondary_ammo, pd->secondary_ammo, MAX_SECONDARY_WEAPONS*sizeof(short) ); // How much ammo of each type.
3733 ps->last_score=INTEL_INT(pd->last_score); // Score at beginning of current level.
3734 ps->score=INTEL_INT(pd->score); // Current score.
3735 ps->cloak_time=(fix)INTEL_INT(pd->cloak_time); // Time cloaked
3736 ps->homing_object_dist=(fix)INTEL_INT(pd->homing_object_dist); // Distance of nearest homing object.
3737 ps->invulnerable_time=(fix)INTEL_INT(pd->invulnerable_time); // Time invulnerable
3738 ps->KillGoalCount=INTEL_SHORT(pd->KillGoalCount);
3739 ps->net_killed_total=INTEL_SHORT(pd->net_killed_total); // Number of times killed total
3740 ps->net_kills_total=INTEL_SHORT(pd->net_kills_total); // Number of net kills total
3741 ps->num_kills_level=INTEL_SHORT(pd->num_kills_level); // Number of kills this level
3742 ps->num_kills_total=INTEL_SHORT(pd->num_kills_total); // Number of kills total
3743 ps->num_robots_level=INTEL_SHORT(pd->num_robots_level); // Number of initial robots this level
3744 ps->num_robots_total=INTEL_SHORT(pd->num_robots_total); // Number of robots total
3745 ps->hostages_rescued_total=INTEL_SHORT(pd->hostages_rescued_total); // Total number of hostages rescued.
3746 ps->hostages_total=INTEL_SHORT(pd->hostages_total); // Total number of hostages.
3747 ps->hostages_on_board=pd->hostages_on_board; // Number of hostages on ship.
3750 void use_netplayer_stats( player * ps, netplayer_stats *pd )
3754 ps->flags = INTEL_INT(pd->flags); // Powerup flags, see below...
3755 ps->energy = (fix)INTEL_INT((int)pd->energy); // Amount of energy remaining.
3756 ps->shields = (fix)INTEL_INT((int)pd->shields); // shields remaining (protection)
3757 ps->lives = pd->lives; // Lives remaining, 0 = game over.
3758 ps->laser_level = pd->laser_level; // Current level of the laser.
3759 ps->primary_weapon_flags=pd->primary_weapon_flags; // bit set indicates the player has this weapon.
3760 ps->secondary_weapon_flags=pd->secondary_weapon_flags; // bit set indicates the player has this weapon.
3761 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
3762 ps->primary_ammo[i] = INTEL_SHORT(pd->primary_ammo[i]);
3763 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
3764 ps->secondary_ammo[i] = INTEL_SHORT(pd->secondary_ammo[i]);
3765 //memcpy( ps->primary_ammo, pd->primary_ammo, MAX_PRIMARY_WEAPONS*sizeof(short) ); // How much ammo of each type.
3766 //memcpy( ps->secondary_ammo, pd->secondary_ammo, MAX_SECONDARY_WEAPONS*sizeof(short) ); // How much ammo of each type.
3767 ps->last_score = INTEL_INT(pd->last_score); // Score at beginning of current level.
3768 ps->score = INTEL_INT(pd->score); // Current score.
3769 ps->cloak_time = (fix)INTEL_INT((int)pd->cloak_time); // Time cloaked
3770 ps->homing_object_dist = (fix)INTEL_INT((int)pd->homing_object_dist); // Distance of nearest homing object.
3771 ps->invulnerable_time = (fix)INTEL_INT((int)pd->invulnerable_time); // Time invulnerable
3772 ps->KillGoalCount=INTEL_SHORT(pd->KillGoalCount);
3773 ps->net_killed_total = INTEL_SHORT(pd->net_killed_total); // Number of times killed total
3774 ps->net_kills_total = INTEL_SHORT(pd->net_kills_total); // Number of net kills total
3775 ps->num_kills_level = INTEL_SHORT(pd->num_kills_level); // Number of kills this level
3776 ps->num_kills_total = INTEL_SHORT(pd->num_kills_total); // Number of kills total
3777 ps->num_robots_level = INTEL_SHORT(pd->num_robots_level); // Number of initial robots this level
3778 ps->num_robots_total = INTEL_SHORT(pd->num_robots_total); // Number of robots total
3779 ps->hostages_rescued_total = INTEL_SHORT(pd->hostages_rescued_total); // Total number of hostages rescued.
3780 ps->hostages_total = INTEL_SHORT(pd->hostages_total); // Total number of hostages.
3781 ps->hostages_on_board=pd->hostages_on_board; // Number of hostages on ship.
3784 void multi_send_drop_weapon (int objnum,int seed)
3790 objp = &Objects[objnum];
3792 ammo_count = objp->ctype.powerup_info.count;
3794 if (objp->id == POW_OMEGA_WEAPON && ammo_count == F1_0)
3795 ammo_count = F1_0 - 1; //make fit in short
3797 Assert(ammo_count < F1_0); //make sure fits in short
3799 multibuf[count++]=(char)MULTI_DROP_WEAPON;
3800 multibuf[count++]=(char)objp->id;
3802 PUT_INTEL_SHORT(multibuf+count, Player_num); count += 2;
3803 PUT_INTEL_SHORT(multibuf+count, objnum); count += 2;
3804 PUT_INTEL_SHORT(multibuf+count, ammo_count); count += 2;
3805 PUT_INTEL_INT(multibuf+count, seed);
3807 map_objnum_local_to_local(objnum);
3809 if (Game_mode & GM_NETWORK)
3810 PowerupsInMine[objp->id]++;
3812 multi_send_data(multibuf, 12, 2);
3815 void multi_do_drop_weapon (char *buf)
3817 int pnum,ammo,objnum,remote_objnum,seed;
3821 powerup_id=(int)(buf[1]);
3822 pnum = GET_INTEL_SHORT(buf + 2);
3823 remote_objnum = GET_INTEL_SHORT(buf + 4);
3824 ammo = GET_INTEL_SHORT(buf + 6);
3825 seed = GET_INTEL_INT(buf + 8);
3827 objp = &Objects[Players[pnum].objnum];
3829 objnum = spit_powerup(objp, powerup_id, seed);
3831 map_objnum_local_to_remote(objnum, remote_objnum, pnum);
3834 Objects[objnum].ctype.powerup_info.count = ammo;
3836 if (Game_mode & GM_NETWORK)
3837 PowerupsInMine[powerup_id]++;
3839 mprintf ((0,"Dropped weapon %d!\n"));
3843 void multi_send_guided_info (object *miss,char done)
3845 #ifdef WORDS_BIGENDIAN
3850 mprintf ((0,"Sending guided info!\n"));
3852 multibuf[count++]=(char)MULTI_GUIDED;
3853 multibuf[count++]=(char)Player_num;
3854 multibuf[count++]=done;
3856 #ifndef WORDS_BIGENDIAN
3857 create_shortpos((shortpos *)(multibuf+count), miss,0);
3858 count+=sizeof(shortpos);
3860 create_shortpos(&sp, miss, 1);
3861 memcpy(&(multibuf[count]), (ubyte *)(sp.bytemat), 9);
3863 memcpy(&(multibuf[count]), (ubyte *)&(sp.xo), 14);
3867 multi_send_data(multibuf, count, 0);
3870 void multi_do_guided (char *buf)
3875 #ifdef WORDS_BIGENDIAN
3879 if (Guided_missile[(int)pnum]==NULL)
3883 mprintf ((0,"Guided missile for %s is NULL!\n",Players[(int)pnum].callsign));
3890 mprintf ((0,"Got guided info for %d (%s)\n",pnum,Players[(int)pnum].callsign));
3896 release_guided_missile(pnum);
3901 if (OBJECT_NUMBER(Guided_missile[(int)pnum]) < 0 || OBJECT_NUMBER(Guided_missile[(int)pnum]) > Highest_object_index)
3903 Int3(); // Get Jason immediately!
3907 #ifndef WORDS_BIGENDIAN
3908 extract_shortpos(Guided_missile[(int)pnum], (shortpos *)(buf+count),0);
3910 memcpy((ubyte *)(sp.bytemat), (ubyte *)(buf + count), 9);
3911 memcpy((ubyte *)&(sp.xo), (ubyte *)(buf + count + 9), 14);
3912 extract_shortpos(Guided_missile[(int)pnum], &sp, 1);
3915 count+=sizeof (shortpos);
3917 update_object_seg(Guided_missile[(int)pnum]);
3920 void multi_send_stolen_items ()
3923 multibuf[0]=MULTI_STOLEN_ITEMS;
3925 for (i=0;i<MAX_STOLEN_ITEMS;i++)
3927 multibuf[i+1]=Stolen_items[i];
3928 mprintf ((0,"[%d]=%d ",i,Stolen_items[i]));
3929 count++; // So I like to break my stuff into smaller chunks, so what?
3932 multi_send_data(multibuf, count, 1);
3935 void multi_do_stolen_items (char *buf)
3939 mprintf ((0,"Recieved a stolen item packet...\n"));
3941 for (i=0;i<MAX_STOLEN_ITEMS;i++)
3943 Stolen_items[i]=buf[i+1];
3944 mprintf ((0,"[%d]=%d ",i,Stolen_items[i]));
3949 extern void network_send_important_packet (char *,int);
3951 void multi_send_wall_status (int wallnum,ubyte type,ubyte flags,ubyte state)
3954 multibuf[count]=MULTI_WALL_STATUS; count++;
3955 PUT_INTEL_SHORT(multibuf+count, wallnum); count+=2;
3956 multibuf[count]=type; count++;
3957 multibuf[count]=flags; count++;
3958 multibuf[count]=state; count++;
3961 if (Game_mode & GM_NETWORK)
3963 network_send_important_packet (multibuf,count);
3964 network_send_important_packet (multibuf,count);
3969 multi_send_data(multibuf, count, 1); // twice, just to be sure
3970 multi_send_data(multibuf, count, 1);
3973 void multi_send_wall_status_specific (int pnum,int wallnum,ubyte type,ubyte flags,ubyte state)
3975 // Send wall states a specific rejoining player
3979 Assert (Game_mode & GM_NETWORK);
3980 //Assert (pnum>-1 && pnum<N_players);
3982 multibuf[count]=MULTI_WALL_STATUS; count++;
3983 PUT_INTEL_SHORT(multibuf+count, wallnum); count+=2;
3984 multibuf[count]=type; count++;
3985 multibuf[count]=flags; count++;
3986 multibuf[count]=state; count++;
3988 network_send_naked_packet(multibuf, count,pnum); // twice, just to be sure
3989 network_send_naked_packet(multibuf, count,pnum);
3992 void multi_do_wall_status (char *buf)
3995 ubyte flag,type,state;
3997 wallnum = GET_INTEL_SHORT(buf + 1);
4002 Assert (wallnum>=0);
4003 Walls[wallnum].type=type;
4004 Walls[wallnum].flags=flag;
4005 //Assert(state <= 4);
4006 Walls[wallnum].state=state;
4008 if (Walls[wallnum].type==WALL_OPEN)
4010 digi_kill_sound_linked_to_segment(Walls[wallnum].segnum,Walls[wallnum].sidenum,SOUND_FORCEFIELD_HUM);
4011 //digi_kill_sound_linked_to_segment(SEGMENT_NUMBER(csegp), cside, SOUND_FORCEFIELD_HUM);
4015 //mprintf ((0,"Got a walls packet.\n"));
4018 void multi_send_jason_cheat (int num)
4023 void multi_send_kill_goal_counts()
4026 multibuf[0]=MULTI_KILLGOALS;
4028 for (i=0;i<MAX_PLAYERS;i++)
4030 *(char *)(multibuf+count)=(char)Players[i].KillGoalCount;
4034 mprintf ((0,"MULTI: Sending KillGoalCounts...\n"));
4035 multi_send_data(multibuf, count, 1);
4038 void multi_do_kill_goal_counts(char *buf)
4042 for (i=0;i<MAX_PLAYERS;i++)
4044 Players[i].KillGoalCount=*(char *)(buf+count);
4045 mprintf ((0,"KGC: %s has %d kills!\n",Players[i].callsign,Players[i].KillGoalCount));
4051 void multi_send_heartbeat ()
4053 if (!Netgame.PlayTimeAllowed)
4056 multibuf[0]=MULTI_HEARTBEAT;
4057 PUT_INTEL_INT(multibuf+1, ThisLevelTime);
4058 multi_send_data(multibuf, 5, 0);
4061 void multi_do_heartbeat (char *buf)
4065 num = GET_INTEL_INT(buf + 1);
4070 void multi_check_for_killgoal_winner ()
4072 int i,best=0,bestnum=0;
4075 if (Control_center_destroyed)
4078 for (i=0;i<N_players;i++)
4080 if (Players[i].KillGoalCount>best)
4082 best=Players[i].KillGoalCount;
4087 if (bestnum==Player_num)
4089 HUD_init_message("You have the best score at %d kills!",best);
4090 //Players[Player_num].shields=i2f(200);
4094 HUD_init_message ("%s has the best score with %d kills!",Players[bestnum].callsign,best);
4096 HUD_init_message ("The control center has been destroyed!");
4098 objp=obj_find_first_of_type (OBJ_CNTRLCEN);
4099 net_destroy_controlcen (objp);
4102 void multi_send_seismic (fix start,fix end)
4106 multibuf[0]=MULTI_SEISMIC;
4107 PUT_INTEL_INT(multibuf+count, start); count+=(sizeof(fix));
4108 PUT_INTEL_INT(multibuf+count, end); count+=(sizeof(fix));
4110 multi_send_data(multibuf, count, 1);
4113 extern fix Seismic_disturbance_start_time;
4114 extern fix Seismic_disturbance_end_time;
4116 void multi_do_seismic (char *buf)
4118 Seismic_disturbance_start_time = GET_INTEL_INT(buf + 1);
4119 Seismic_disturbance_end_time = GET_INTEL_INT(buf + 5);
4120 digi_play_sample (SOUND_SEISMIC_DISTURBANCE_START, F1_0);
4123 void multi_send_light (int segnum,ubyte val)
4126 multibuf[0]=MULTI_LIGHT;
4127 PUT_INTEL_INT(multibuf+count, segnum); count+=(sizeof(int));
4128 *(char *)(multibuf+count)=val; count++;
4131 //mprintf ((0,"Sending %d!\n",Segments[segnum].sides[i].tmap_num2));
4132 PUT_INTEL_SHORT(multibuf+count, Segments[segnum].sides[i].tmap_num2); count+=2;
4134 multi_send_data(multibuf, count, 1);
4136 void multi_send_light_specific (int pnum,int segnum,ubyte val)
4140 Assert (Game_mode & GM_NETWORK);
4141 // Assert (pnum>-1 && pnum<N_players);
4143 multibuf[0]=MULTI_LIGHT;
4144 PUT_INTEL_INT(multibuf+count, segnum); count+=(sizeof(int));
4145 *(char *)(multibuf+count)=val; count++;
4149 //mprintf ((0,"Sending %d!\n",Segments[segnum].sides[i].tmap_num2));
4150 PUT_INTEL_SHORT(multibuf+count, Segments[segnum].sides[i].tmap_num2); count+=2;
4152 network_send_naked_packet(multibuf, count, pnum);
4155 void multi_do_light (char *buf)
4158 ubyte sides=*(char *)(buf+5);
4160 seg = GET_INTEL_INT(buf + 1);
4163 if ((sides & (1<<i)))
4165 subtract_light (seg,i);
4166 Segments[seg].sides[i].tmap_num2 = GET_INTEL_SHORT(buf + (6 + (2 * i)));
4167 //mprintf ((0,"Got %d!\n",Segments[seg].sides[i].tmap_num2));
4172 //@@void multi_send_start_trigger(int triggernum)
4174 //@@ // Send an even to trigger something in the mine
4178 //@@ multibuf[count] = MULTI_START_TRIGGER; count += 1;
4179 //@@ multibuf[count] = Player_num; count += 1;
4180 //@@ multibuf[count] = (ubyte)triggernum; count += 1;
4182 //@@ //mprintf ((0,"Sending start trigger %d\n",triggernum));
4183 //@@ multi_send_data(multibuf, count, 2);
4185 //@@void multi_do_start_trigger(char *buf)
4187 //@@ int pnum = buf[1];
4188 //@@ int trigger = buf[2];
4190 //@@ //mprintf ((0,"MULTI doing start trigger!\n"));
4192 //@@ if ((pnum < 0) || (pnum >= N_players) || (pnum == Player_num))
4194 //@@ Int3(); // Got trigger from illegal playernum
4197 //@@ if ((trigger < 0) || (trigger >= Num_triggers))
4199 //@@ Int3(); // Illegal trigger number in multiplayer
4203 //@@ if (!(Triggers[trigger].flags & TF_SPRUNG))
4204 //@@ check_trigger_sub(trigger, pnum,0);
4208 void multi_do_flags (char *buf)
4213 flags = GET_INTEL_INT(buf + 2);
4214 if (pnum!=Player_num)
4215 Players[(int)pnum].flags=flags;
4218 void multi_send_flags (char pnum)
4220 multibuf[0]=MULTI_FLAGS;
4222 PUT_INTEL_INT(multibuf+2, Players[(int)pnum].flags);
4224 multi_send_data(multibuf, 6, 1);
4227 void multi_send_drop_blobs (char pnum)
4229 multibuf[0]=MULTI_DROP_BLOB;
4232 multi_send_data(multibuf, 2, 0);
4235 void multi_do_drop_blob (char *buf)
4238 drop_afterburner_blobs (&Objects[Players[(int)pnum].objnum], 2, i2f(5)/2, -1);
4241 void multi_send_powerup_update ()
4246 multibuf[0]=MULTI_POWERUP_UPDATE;
4247 for (i=0;i<MAX_POWERUP_TYPES;i++)
4248 multibuf[i+1]=MaxPowerupsAllowed[i];
4250 multi_send_data(multibuf, MAX_POWERUP_TYPES+1, 1);
4252 void multi_do_powerup_update (char *buf)
4256 for (i=0;i<MAX_POWERUP_TYPES;i++)
4257 if (buf[i+1]>MaxPowerupsAllowed[i])
4258 MaxPowerupsAllowed[i]=buf[i+1];
4261 extern active_door ActiveDoors[];
4262 extern int Num_open_doors; // Number of open doors
4265 #if 0 // never used...
4266 void multi_send_active_door (int i)
4270 multibuf[0]=MULTI_ACTIVE_DOOR;
4272 multibuf[2]=Num_open_doors;
4274 #ifndef WORDS_BIGENDIAN
4275 memcpy ((char *)(&multibuf[3]),&ActiveDoors[(int)i],sizeof(struct active_door));
4276 count += sizeof(active_door);
4278 *(int *)(multibuf + count) = INTEL_INT(ActiveDoors[i].n_parts); count += 4;
4279 *(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].front_wallnum[0]); count += 2;
4280 *(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].front_wallnum[1]); count += 2;
4281 *(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].back_wallnum[0]); count += 2;
4282 *(short *)(multibuf + count) = INTEL_SHORT(ActiveDoors[i].back_wallnum[1]); count += 2;
4283 *(int *)(multibuf + count) = INTEL_INT(ActiveDoors[i].time); count += 4;
4285 //multi_send_data (multibuf,sizeof(struct active_door)+3,1);
4286 multi_send_data (multibuf,count,1);
4288 #endif // 0 (never used)
4290 void multi_do_active_door (char *buf)
4292 char i = multibuf[1];
4293 Num_open_doors = buf[2];
4295 memcpy(&ActiveDoors[(int)i], buf+3, sizeof(struct active_door));
4296 #ifdef WORDS_BIGENDIAN
4298 active_door *ad = &ActiveDoors[(int)i];
4299 ad->n_parts = INTEL_INT(ad->n_parts);
4300 ad->front_wallnum[0] = INTEL_SHORT(ad->front_wallnum[0]);
4301 ad->front_wallnum[1] = INTEL_SHORT(ad->front_wallnum[1]);
4302 ad->back_wallnum[0] = INTEL_SHORT(ad->back_wallnum[0]);
4303 ad->back_wallnum[1] = INTEL_SHORT(ad->back_wallnum[1]);
4304 ad->time = INTEL_INT(ad->time);
4306 #endif //WORDS_BIGENDIAN
4309 void multi_send_sound_function (char whichfunc, char sound)
4313 multibuf[0]=MULTI_SOUND_FUNCTION; count++;
4314 multibuf[1]=Player_num; count++;
4315 multibuf[2]=whichfunc; count++;
4316 #ifndef WORDS_BIGENDIAN
4317 *(uint *)(multibuf+count)=sound; count++;
4319 multibuf[3] = sound; count++; // this would probably work on the PC as well. Jason?
4321 multi_send_data (multibuf,4,0);
4324 #define AFTERBURNER_LOOP_START 20098
4325 #define AFTERBURNER_LOOP_END 25776
4327 void multi_do_sound_function (char *buf)
4331 char pnum,whichfunc;
4334 if (Players[Player_num].connected!=1)
4342 digi_kill_sound_linked_to_object (Players[(int)pnum].objnum);
4343 else if (whichfunc==3)
4344 digi_link_sound_to_object3( sound, Players[(int)pnum].objnum, 1,F1_0, i2f(256), AFTERBURNER_LOOP_START, AFTERBURNER_LOOP_END);
4347 void multi_send_capture_bonus (char pnum)
4349 Assert (Game_mode & GM_CAPTURE);
4351 multibuf[0]=MULTI_CAPTURE_BONUS;
4354 multi_send_data (multibuf,2,1);
4355 multi_do_capture_bonus (multibuf);
4357 void multi_send_orb_bonus (char pnum)
4359 Assert (Game_mode & GM_HOARD);
4361 multibuf[0]=MULTI_ORB_BONUS;
4363 multibuf[2]=Players[Player_num].secondary_ammo[PROXIMITY_INDEX];
4365 multi_send_data (multibuf,3,1);
4366 multi_do_orb_bonus (multibuf);
4368 void multi_do_capture_bonus(char *buf)
4370 // Figure out the results of a network kills and add it to the
4371 // appropriate player's tally.
4376 kmatrix_kills_changed = 1;
4378 if (pnum==Player_num)
4379 HUD_init_message("You have Scored!");
4381 HUD_init_message("%s has Scored!",Players[(int)pnum].callsign);
4383 if (pnum==Player_num)
4384 digi_play_sample (SOUND_HUD_YOU_GOT_GOAL,F1_0*2);
4385 else if (get_team(pnum)==TEAM_RED)
4386 digi_play_sample (SOUND_HUD_RED_GOT_GOAL,F1_0*2);
4388 digi_play_sample (SOUND_HUD_BLUE_GOT_GOAL,F1_0*2);
4390 Players[(int)pnum].flags &= ~(PLAYER_FLAGS_FLAG); // Clear capture flag
4392 team_kills[get_team(pnum)] += 5;
4393 Players[(int)pnum].net_kills_total += 5;
4394 Players[(int)pnum].KillGoalCount+=5;
4396 if (Netgame.KillGoal>0)
4398 TheGoal=Netgame.KillGoal*5;
4400 if (Players[(int)pnum].KillGoalCount>=TheGoal)
4402 if (pnum==Player_num)
4404 HUD_init_message("You reached the kill goal!");
4405 Players[Player_num].shields=i2f(200);
4408 HUD_init_message ("%s has reached the kill goal!",Players[(int)pnum].callsign);
4410 HUD_init_message ("The control center has been destroyed!");
4411 net_destroy_controlcen (obj_find_first_of_type (OBJ_CNTRLCEN));
4415 multi_sort_kill_list();
4416 multi_show_player_list();
4419 int GetOrbBonus (char num)
4423 bonus=num*(num+1)/2;
4427 void multi_do_orb_bonus(char *buf)
4429 // Figure out the results of a network kills and add it to the
4430 // appropriate player's tally.
4434 int bonus=GetOrbBonus (buf[2]);
4436 kmatrix_kills_changed = 1;
4438 if (pnum==Player_num)
4439 HUD_init_message("You have scored %d points!",bonus);
4441 HUD_init_message("%s has scored with %d orbs!",Players[(int)pnum].callsign,buf[2]);
4443 if (pnum==Player_num)
4444 digi_start_sound_queued (SOUND_HUD_YOU_GOT_GOAL,F1_0*2);
4445 else if (Game_mode & GM_TEAM)
4447 if (get_team(pnum)==TEAM_RED)
4448 digi_play_sample (SOUND_HUD_RED_GOT_GOAL,F1_0*2);
4450 digi_play_sample (SOUND_HUD_BLUE_GOT_GOAL,F1_0*2);
4453 digi_play_sample (SOUND_OPPONENT_HAS_SCORED,F1_0*2);
4455 if (bonus>PhallicLimit)
4457 if (pnum==Player_num)
4458 HUD_init_message ("You have the record with %d points!",bonus);
4460 HUD_init_message ("%s has the record with %d points!",Players[(int)pnum].callsign,bonus);
4461 digi_play_sample (SOUND_BUDDY_MET_GOAL,F1_0*2);
4466 Players[(int)pnum].flags &= ~(PLAYER_FLAGS_FLAG); // Clear orb flag
4468 team_kills[get_team(pnum)] += bonus;
4469 Players[(int)pnum].net_kills_total += bonus;
4470 Players[(int)pnum].KillGoalCount+=bonus;
4472 team_kills[get_team(pnum)]%=1000;
4473 Players[(int)pnum].net_kills_total%=1000;
4474 Players[(int)pnum].KillGoalCount%=1000;
4476 if (Netgame.KillGoal>0)
4478 TheGoal=Netgame.KillGoal*5;
4480 if (Players[(int)pnum].KillGoalCount>=TheGoal)
4482 if (pnum==Player_num)
4484 HUD_init_message("You reached the kill goal!");
4485 Players[Player_num].shields=i2f(200);
4488 HUD_init_message ("%s has reached the kill goal!",Players[(int)pnum].callsign);
4490 HUD_init_message ("The control center has been destroyed!");
4491 net_destroy_controlcen (obj_find_first_of_type (OBJ_CNTRLCEN));
4494 multi_sort_kill_list();
4495 multi_show_player_list();
4498 void multi_send_got_flag (char pnum)
4500 multibuf[0]=MULTI_GOT_FLAG;
4503 digi_start_sound_queued (SOUND_HUD_YOU_GOT_FLAG,F1_0*2);
4505 multi_send_data (multibuf,2,1);
4506 multi_send_flags (Player_num);
4510 digi_sound ReversedSound;
4512 void multi_send_got_orb (char pnum)
4514 multibuf[0]=MULTI_GOT_ORB;
4517 digi_play_sample (SOUND_YOU_GOT_ORB,F1_0*2);
4519 multi_send_data (multibuf,2,1);
4520 multi_send_flags (Player_num);
4523 void multi_do_got_flag (char *buf)
4527 if (pnum==Player_num)
4528 digi_start_sound_queued (SOUND_HUD_YOU_GOT_FLAG,F1_0*2);
4529 else if (get_team(pnum)==TEAM_RED)
4530 digi_start_sound_queued (SOUND_HUD_RED_GOT_FLAG,F1_0*2);
4532 digi_start_sound_queued (SOUND_HUD_BLUE_GOT_FLAG,F1_0*2);
4533 Players[(int)pnum].flags|=PLAYER_FLAGS_FLAG;
4534 HUD_init_message ("%s picked up a flag!",Players[(int)pnum].callsign);
4536 void multi_do_got_orb (char *buf)
4540 Assert (Game_mode & GM_HOARD);
4542 if (Game_mode & GM_TEAM)
4544 if (get_team(pnum)==get_team(Player_num))
4545 digi_play_sample (SOUND_FRIEND_GOT_ORB,F1_0*2);
4547 digi_play_sample (SOUND_OPPONENT_GOT_ORB,F1_0*2);
4550 digi_play_sample (SOUND_OPPONENT_GOT_ORB,F1_0*2);
4552 Players[(int)pnum].flags|=PLAYER_FLAGS_FLAG;
4553 HUD_init_message ("%s picked up an orb!",Players[(int)pnum].callsign);
4561 if (!(Game_mode & GM_HOARD))
4562 Int3(); // How did we get here? Get Leighton!
4564 if (!Players[Player_num].secondary_ammo[PROXIMITY_INDEX])
4566 HUD_init_message("No orbs to drop!");
4572 objnum = spit_powerup(ConsoleObject,POW_HOARD_ORB,seed);
4577 HUD_init_message("Orb dropped!");
4578 digi_play_sample (SOUND_DROP_WEAPON,F1_0);
4580 if ((Game_mode & GM_HOARD) && objnum>-1)
4581 multi_send_drop_flag(objnum,seed);
4583 Players[Player_num].secondary_ammo[PROXIMITY_INDEX]--;
4585 // If empty, tell everyone to stop drawing the box around me
4586 if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]==0)
4587 multi_send_flags (Player_num);
4594 if (!(Game_mode & GM_CAPTURE) && !(Game_mode & GM_HOARD))
4596 if (Game_mode & GM_HOARD)
4602 if (!(Players[Player_num].flags & PLAYER_FLAGS_FLAG))
4604 HUD_init_message("No flag to drop!");
4609 HUD_init_message("Flag dropped!");
4610 digi_play_sample (SOUND_DROP_WEAPON,F1_0);
4614 if (get_team (Player_num)==TEAM_RED)
4615 objnum = spit_powerup(ConsoleObject,POW_FLAG_BLUE,seed);
4617 objnum = spit_powerup(ConsoleObject,POW_FLAG_RED,seed);
4622 if ((Game_mode & GM_CAPTURE) && objnum>-1)
4623 multi_send_drop_flag(objnum,seed);
4625 Players[Player_num].flags &=~(PLAYER_FLAGS_FLAG);
4629 void multi_send_drop_flag (int objnum,int seed)
4634 objp = &Objects[objnum];
4636 multibuf[count++]=(char)MULTI_DROP_FLAG;
4637 multibuf[count++]=(char)objp->id;
4639 PUT_INTEL_SHORT(multibuf+count, Player_num); count += 2;
4640 PUT_INTEL_SHORT(multibuf+count, objnum); count += 2;
4641 PUT_INTEL_SHORT(multibuf+count, objp->ctype.powerup_info.count); count += 2;
4642 PUT_INTEL_INT(multibuf+count, seed);
4644 map_objnum_local_to_local(objnum);
4646 if (!(Game_mode & GM_HOARD))
4647 if (Game_mode & GM_NETWORK)
4648 PowerupsInMine[objp->id]++;
4650 multi_send_data(multibuf, 12, 2);
4653 void multi_do_drop_flag (char *buf)
4655 int pnum,ammo,objnum,remote_objnum,seed;
4660 pnum = GET_INTEL_SHORT(buf + 2);
4661 remote_objnum = GET_INTEL_SHORT(buf + 4);
4662 ammo = GET_INTEL_SHORT(buf + 6);
4663 seed = GET_INTEL_INT(buf + 8);
4665 objp = &Objects[Players[pnum].objnum];
4667 objnum = spit_powerup(objp, powerup_id, seed);
4669 map_objnum_local_to_remote(objnum, remote_objnum, pnum);
4672 Objects[objnum].ctype.powerup_info.count = ammo;
4674 if (!(Game_mode & GM_HOARD))
4676 if (Game_mode & GM_NETWORK)
4677 PowerupsInMine[powerup_id]++;
4678 Players[pnum].flags &= ~(PLAYER_FLAGS_FLAG);
4680 mprintf ((0,"Dropped flag %d!\n"));
4684 void multi_bad_restore ()
4686 Function_mode = FMODE_MENU;
4687 nm_messagebox(NULL, 1, TXT_OK,
4688 "A multi-save game was restored\nthat you are missing or does not\nmatch that of the others.\nYou must rejoin if you wish to\ncontinue.");
4689 Function_mode = FMODE_GAME;
4690 multi_quit_game = 1;
4691 multi_leave_menu = 1;
4692 multi_reset_stuff();
4695 extern int robot_controlled[MAX_ROBOTS_CONTROLLED];
4696 extern int robot_agitation[MAX_ROBOTS_CONTROLLED];
4697 extern fix robot_controlled_time[MAX_ROBOTS_CONTROLLED];
4698 extern fix robot_last_send_time[MAX_ROBOTS_CONTROLLED];
4699 extern fix robot_last_message_time[MAX_ROBOTS_CONTROLLED];
4700 extern int robot_send_pending[MAX_ROBOTS_CONTROLLED];
4701 extern int robot_fired[MAX_ROBOTS_CONTROLLED];
4702 extern sbyte robot_fire_buf[MAX_ROBOTS_CONTROLLED][18+3];
4705 void multi_do_robot_controls(char *buf)
4709 mprintf ((0,"Recieved ROBOT_CONTROLS!!!\n"));
4711 if (buf[1]!=Player_num)
4713 Int3(); // Get Jason! Recieved a coop_sync that wasn't ours!
4717 memcpy (&robot_controlled,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4718 count+=(MAX_ROBOTS_CONTROLLED*4);
4719 memcpy (&robot_agitation,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4720 count+=(MAX_ROBOTS_CONTROLLED*4);
4721 memcpy (&robot_controlled_time,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4722 count+=(MAX_ROBOTS_CONTROLLED*4);
4723 memcpy (&robot_last_send_time,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4724 count+=(MAX_ROBOTS_CONTROLLED*4);
4725 memcpy (&robot_last_message_time,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4726 count+=(MAX_ROBOTS_CONTROLLED*4);
4727 memcpy (&robot_send_pending,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4728 count+=(MAX_ROBOTS_CONTROLLED*4);
4729 memcpy (&robot_fired,&(buf[count]),MAX_ROBOTS_CONTROLLED*4);
4730 count+=(MAX_ROBOTS_CONTROLLED*4);
4733 #define POWERUPADJUSTS 5
4734 int PowerupAdjustMapping[]={11,19,39,41,44};
4736 int multi_powerup_is_4pack (int id)
4740 for (i=0;i<POWERUPADJUSTS;i++)
4741 if (id==PowerupAdjustMapping[i])
4746 int multi_powerup_is_allowed(int id)
4748 if (id == POW_INVULNERABILITY && !Netgame.DoInvulnerability)
4750 if (id == POW_CLOAK && !Netgame.DoCloak)
4752 if (id == POW_AFTERBURNER && !Netgame.DoAfterburner)
4754 if (id == POW_FUSION_WEAPON && !Netgame.DoFusions)
4756 if (id == POW_PHOENIX_WEAPON && !Netgame.DoPhoenix)
4758 if (id == POW_HELIX_WEAPON && !Netgame.DoHelix)
4760 if (id == POW_MEGA_WEAPON && !Netgame.DoMegas)
4762 if (id == POW_SMARTBOMB_WEAPON && !Netgame.DoSmarts)
4764 if (id == POW_GAUSS_WEAPON && !Netgame.DoGauss)
4766 if (id == POW_VULCAN_WEAPON && !Netgame.DoVulcan)
4768 if (id == POW_PLASMA_WEAPON && !Netgame.DoPlasma)
4770 if (id == POW_OMEGA_WEAPON && !Netgame.DoOmega)
4772 if (id == POW_SUPER_LASER && !Netgame.DoSuperLaser)
4774 if (id == POW_PROXIMITY_WEAPON && !Netgame.DoProximity)
4776 if (id==POW_VULCAN_AMMO && (!Netgame.DoVulcan && !Netgame.DoGauss))
4778 if (id == POW_SPREADFIRE_WEAPON && !Netgame.DoSpread)
4780 if (id == POW_SMART_MINE && !Netgame.DoSmartMine)
4782 if (id == POW_SMISSILE1_1 && !Netgame.DoFlash)
4784 if (id == POW_SMISSILE1_4 && !Netgame.DoFlash)
4786 if (id == POW_GUIDED_MISSILE_1 && !Netgame.DoGuided)
4788 if (id == POW_GUIDED_MISSILE_4 && !Netgame.DoGuided)
4790 if (id == POW_EARTHSHAKER_MISSILE && !Netgame.DoEarthShaker)
4792 if (id == POW_MERCURY_MISSILE_1 && !Netgame.DoMercury)
4794 if (id == POW_MERCURY_MISSILE_4 && !Netgame.DoMercury)
4796 if (id == POW_CONVERTER && !Netgame.DoConverter)
4798 if (id == POW_AMMO_RACK && !Netgame.DoAmmoRack)
4800 if (id == POW_HEADLIGHT && !Netgame.DoHeadlight)
4802 if (id == POW_LASER && !Netgame.DoLaserUpgrade)
4804 if (id == POW_HOMING_AMMO_1 && !Netgame.DoHoming)
4806 if (id == POW_HOMING_AMMO_4 && !Netgame.DoHoming)
4808 if (id == POW_QUAD_FIRE && !Netgame.DoQuadLasers)
4810 if (id == POW_FLAG_BLUE && !(Game_mode & GM_CAPTURE))
4812 if (id == POW_FLAG_RED && !(Game_mode & GM_CAPTURE))
4818 void multi_send_finish_game ()
4820 multibuf[0]=MULTI_FINISH_GAME;
4821 multibuf[1]=Player_num;
4823 multi_send_data (multibuf,2,1);
4827 extern void do_final_boss_hacks(void);
4828 void multi_do_finish_game (char *buf)
4830 if (buf[0]!=MULTI_FINISH_GAME)
4833 if (Current_level_num!=Last_level)
4836 do_final_boss_hacks();
4839 void multi_send_trigger_specific (char pnum,char trig)
4841 multibuf[0] = MULTI_START_TRIGGER;
4844 network_send_naked_packet(multibuf, 2, pnum);
4846 void multi_do_start_trigger (char *buf)
4848 Triggers[(int)buf[1]].flags |=TF_DISABLED;
4851 extern char *RankStrings[];
4853 void multi_add_lifetime_kills ()
4855 // This function adds a kill to lifetime stats of this player, and possibly
4856 // gives a promotion. If so, it will tell everyone else
4860 if ( !(Game_mode & GM_NETWORK) )
4863 oldrank=GetMyNetRanking();
4867 if (oldrank!=GetMyNetRanking())
4869 multi_send_ranking();
4870 if (!FindArg("-norankings"))
4872 HUD_init_message ("You have been promoted to %s!",RankStrings[GetMyNetRanking()]);
4873 digi_play_sample (SOUND_BUDDY_MET_GOAL,F1_0*2);
4874 NetPlayers.players[Player_num].rank=GetMyNetRanking();
4880 void multi_add_lifetime_killed ()
4882 // This function adds a "killed" to lifetime stats of this player, and possibly
4883 // gives a demotion. If so, it will tell everyone else
4887 if ( !(Game_mode & GM_NETWORK) )
4890 oldrank=GetMyNetRanking();
4894 if (oldrank!=GetMyNetRanking())
4896 multi_send_ranking();
4897 NetPlayers.players[Player_num].rank=GetMyNetRanking();
4899 if (!FindArg("-norankings"))
4900 HUD_init_message ("You have been demoted to %s!",RankStrings[GetMyNetRanking()]);
4907 void multi_send_ranking ()
4909 multibuf[0]=(char)MULTI_RANK;
4910 multibuf[1]=(char)Player_num;
4911 multibuf[2]=(char)GetMyNetRanking();
4913 multi_send_data (multibuf,3,1);
4916 void multi_do_ranking (char *buf)
4922 if (NetPlayers.players[(int)pnum].rank<rank)
4923 strcpy (rankstr,"promoted");
4924 else if (NetPlayers.players[(int)pnum].rank>rank)
4925 strcpy (rankstr,"demoted");
4929 NetPlayers.players[(int)pnum].rank=rank;
4931 if (!FindArg("-norankings"))
4932 HUD_init_message ("%s has been %s to %s!",Players[(int)pnum].callsign,rankstr,RankStrings[(int)rank]);
4934 void multi_send_modem_ping ()
4936 multibuf[0]=MULTI_MODEM_PING;
4937 multi_send_data (multibuf,1,1);
4939 void multi_send_modem_ping_return ()
4941 multibuf[0]=MULTI_MODEM_PING_RETURN;
4942 multi_send_data (multibuf,1,1);
4945 void multi_do_modem_ping_return ()
4947 if (PingLaunchTime==0)
4949 mprintf ((0,"Got invalid PING RETURN from opponent!\n"));
4953 PingReturnTime=timer_get_fixed_seconds();
4955 HUD_init_message ("Ping time for opponent is %d ms!",f2i(fixmul(PingReturnTime-PingLaunchTime,i2f(1000))));
4960 void multi_quick_sound_hack (int num)
4963 num = digi_xlat_sound(num);
4964 length=GameSounds[num].length;
4965 ReversedSound.data=(ubyte *)d_malloc (length);
4966 ReversedSound.length=length;
4968 for (i=0;i<length;i++)
4969 ReversedSound.data[i]=GameSounds[num].data[length-i-1];
4974 void multi_send_play_by_play (int num,int spnum,int dpnum)
4976 if (!(Game_mode & GM_HOARD))
4980 multibuf[0]=MULTI_PLAY_BY_PLAY;
4981 multibuf[1]=(char)num;
4982 multibuf[2]=(char)spnum;
4983 multibuf[3]=(char)dpnum;
4984 multi_send_data (multibuf,4,1);
4985 multi_do_play_by_play (multibuf);
4988 void multi_do_play_by_play (char *buf)
4990 int whichplay=buf[1];
4994 if (!(Game_mode & GM_HOARD))
4996 Int3(); // Get Leighton, something bad has happened.
5003 HUD_init_message ("Ouch! %s has been smacked by %s!",Players[dpnum].callsign,Players[spnum].callsign);
5006 HUD_init_message ("Haha! %s has been spanked by %s!",Players[dpnum].callsign,Players[spnum].callsign);
5014 /// CODE TO LOAD HOARD DATA
5018 void init_bitmap(grs_bitmap *bm,int w,int h,int flags,ubyte *data)
5020 bm->bm_x = bm->bm_y = 0;
5021 bm->bm_w = bm->bm_rowsize = w;
5023 bm->bm_type = BM_LINEAR;
5024 bm->bm_flags = flags;
5030 grs_bitmap Orb_icons[2];
5032 int Hoard_goal_eclip;
5034 void init_hoard_data()
5036 static int first_time=1;
5037 static int orb_vclip;
5038 int n_orb_frames,n_goal_frames;
5041 ubyte palette[256*3];
5044 extern int Num_bitmap_files,Num_effects,Num_sound_files;
5046 ifile = cfopen("hoard.ham","rb");
5048 Error("can't open <hoard.ham>");
5050 n_orb_frames = cfile_read_short(ifile);
5051 orb_w = cfile_read_short(ifile);
5052 orb_h = cfile_read_short(ifile);
5053 save_pos = cftell(ifile);
5054 cfseek(ifile,sizeof(palette)+n_orb_frames*orb_w*orb_h,SEEK_CUR);
5055 n_goal_frames = cfile_read_short(ifile);
5056 cfseek(ifile,save_pos,SEEK_SET);
5060 int bitmap_num=Num_bitmap_files;
5062 //Allocate memory for bitmaps
5063 MALLOC( bitmap_data, ubyte, n_orb_frames*orb_w*orb_h + n_goal_frames*64*64 );
5066 orb_vclip = Num_vclips++;
5067 Assert(Num_vclips <= VCLIP_MAXNUM);
5068 Vclip[orb_vclip].play_time = F1_0/2;
5069 Vclip[orb_vclip].num_frames = n_orb_frames;
5070 Vclip[orb_vclip].frame_time = Vclip[orb_vclip].play_time / Vclip[orb_vclip].num_frames;
5071 Vclip[orb_vclip].flags = 0;
5072 Vclip[orb_vclip].sound_num = -1;
5073 Vclip[orb_vclip].light_value = F1_0;
5074 for (i=0;i<n_orb_frames;i++) {
5075 Vclip[orb_vclip].frames[i].index = bitmap_num;
5076 init_bitmap(&GameBitmaps[bitmap_num],orb_w,orb_h,BM_FLAG_TRANSPARENT,bitmap_data);
5077 bitmap_data += orb_w*orb_h;
5079 Assert(bitmap_num < MAX_BITMAP_FILES);
5082 //Create obj powerup
5083 Powerup_info[POW_HOARD_ORB].vclip_num = orb_vclip;
5084 Powerup_info[POW_HOARD_ORB].hit_sound = -1; //Powerup_info[POW_SHIELD_BOOST].hit_sound;
5085 Powerup_info[POW_HOARD_ORB].size = Powerup_info[POW_SHIELD_BOOST].size;
5086 Powerup_info[POW_HOARD_ORB].light = Powerup_info[POW_SHIELD_BOOST].light;
5088 //Create orb goal wall effect
5089 Hoard_goal_eclip = Num_effects++;
5090 Assert(Num_effects < MAX_EFFECTS);
5091 Effects[Hoard_goal_eclip] = Effects[94]; //copy from blue goal
5092 Effects[Hoard_goal_eclip].changing_wall_texture = NumTextures;
5093 Effects[Hoard_goal_eclip].vc.num_frames=n_goal_frames;
5095 TmapInfo[NumTextures] = TmapInfo[find_goal_texture(TMI_GOAL_BLUE)];
5096 TmapInfo[NumTextures].eclip_num = Hoard_goal_eclip;
5097 TmapInfo[NumTextures].flags = TMI_GOAL_HOARD;
5099 Assert(NumTextures < MAX_TEXTURES);
5100 for (i=0;i<n_goal_frames;i++) {
5101 Effects[Hoard_goal_eclip].vc.frames[i].index = bitmap_num;
5102 init_bitmap(&GameBitmaps[bitmap_num],64,64,0,bitmap_data);
5103 bitmap_data += 64*64;
5105 Assert(bitmap_num < MAX_BITMAP_FILES);
5110 //Load and remap bitmap data for orb
5111 cfread(palette,3,256,ifile);
5112 for (i=0;i<n_orb_frames;i++) {
5113 grs_bitmap *bm = &GameBitmaps[Vclip[orb_vclip].frames[i].index];
5114 cfread(bm->bm_data,1,orb_w*orb_h,ifile);
5115 gr_remap_bitmap_good( bm, palette, 255, -1 );
5118 //Load and remap bitmap data for goal texture
5119 cfile_read_short(ifile); //skip frame count
5120 cfread(palette,3,256,ifile);
5121 for (i=0;i<n_goal_frames;i++) {
5122 grs_bitmap *bm = &GameBitmaps[Effects[Hoard_goal_eclip].vc.frames[i].index];
5123 cfread(bm->bm_data,1,64*64,ifile);
5124 gr_remap_bitmap_good( bm, palette, 255, -1 );
5127 //Load and remap bitmap data for HUD icons
5129 icon_w = cfile_read_short(ifile);
5130 icon_h = cfile_read_short(ifile);
5133 MALLOC( bitmap_data, ubyte, icon_w*icon_h );
5134 init_bitmap(&Orb_icons[i],icon_w,icon_h,BM_FLAG_TRANSPARENT,bitmap_data);
5136 cfread(palette,3,256,ifile);
5137 cfread(Orb_icons[i].bm_data,1,icon_w*icon_h,ifile);
5138 gr_remap_bitmap_good( &Orb_icons[i], palette, 255, -1 );
5143 //Load sounds for orb game
5148 len = cfile_read_int(ifile); //get 11k len
5150 if (digi_sample_rate == SAMPLE_RATE_22K) {
5151 cfseek(ifile,len,SEEK_CUR); //skip over 11k sample
5152 len = cfile_read_int(ifile); //get 22k len
5155 GameSounds[Num_sound_files+i].length = len;
5156 GameSounds[Num_sound_files+i].data = d_malloc(len);
5157 cfread(GameSounds[Num_sound_files+i].data,1,len,ifile);
5159 if (digi_sample_rate == SAMPLE_RATE_11K) {
5160 len = cfile_read_int(ifile); //get 22k len
5161 cfseek(ifile,len,SEEK_CUR); //skip over 22k sample
5164 Sounds[SOUND_YOU_GOT_ORB+i] = Num_sound_files+i;
5165 AltSounds[SOUND_YOU_GOT_ORB+i] = Sounds[SOUND_YOU_GOT_ORB+i];
5175 void save_hoard_data(void)
5177 #define MAX_BITMAPS_PER_BRUSH 30
5178 grs_bitmap * bm[MAX_BITMAPS_PER_BRUSH];
5181 ubyte palette[256*3];
5184 char *sounds[] = {"selforb.raw","selforb.r22", //SOUND_YOU_GOT_ORB
5185 "teamorb.raw","teamorb.r22", //SOUND_FRIEND_GOT_ORB
5186 "enemyorb.raw","enemyorb.r22", //SOUND_OPPONENT_GOT_ORB
5187 "OPSCORE1.raw","OPSCORE1.r22"}; //SOUND_OPPONENT_HAS_SCORED
5189 ofile = PHYSFSX_openWriteBuffered("hoard.ham");
5191 iff_error = iff_read_animbrush("orb.abm",bm,MAX_BITMAPS_PER_BRUSH,&nframes,palette);
5192 Assert(iff_error == IFF_NO_ERROR);
5193 PHYSFS_writeULE16(ofile, nframes);
5194 PHYSFS_writeULE16(ofile, bm[0]->bm_w);
5195 PHYSFS_writeULE16(ofile, bm[0]->bm_h);
5196 PHYSFS_write(ofile, palette, 3, 256);
5197 for (i=0;i<nframes;i++)
5198 PHYSFS_write(ofile, bm[i]->bm_data, bm[i]->bm_w*bm[i]->bm_h, 1);
5200 iff_error = iff_read_animbrush("orbgoal.abm",bm,MAX_BITMAPS_PER_BRUSH,&nframes,palette);
5201 Assert(iff_error == IFF_NO_ERROR);
5202 Assert(bm[0]->bm_w == 64 && bm[0]->bm_h == 64);
5203 PHYSFS_writeULE16(ofile, nframes);
5204 PHYSFS_write(ofile, palette, 3, 256);
5205 for (i=0;i<nframes;i++)
5206 PHYSFS_write(ofile, bm[i]->bm_data, bm[i]->bm_w*bm[i]->bm_h, 1);
5210 iff_error = iff_read_bitmap(i?"orbb.bbm":"orb.bbm",&icon,BM_LINEAR,palette);
5211 Assert(iff_error == IFF_NO_ERROR);
5212 PHYSFS_writeULE16(ofile, icon.bm_w);
5213 PHYSFS_writeULE16(ofile, icon.bm_h);
5214 PHYSFS_write(ofile, palette, 3, 256);
5215 PHYSFS_write(ofile, icon.bm_data, icon.bm_w*icon.bm_h, 1);
5218 for (i=0;i<sizeof(sounds)/sizeof(*sounds);i++) {
5223 ifile = PHYSFS_openRead(sounds[i]);
5224 Assert(ifile != NULL);
5225 size = (int)PHYSFS_fileLength(ifile);
5226 buf = d_malloc(size);
5227 PHYSFS_read(ifile, buf, size, 1);
5228 PHYSFS_writeULE32(ofile, size);
5229 PHYSFS_write(ofile, buf, size, 1);
5231 PHYSFS_close(ifile);
5234 PHYSFS_close(ofile);
5239 multi_process_data(char *buf, int len)
5241 // Take an entire message (that has already been checked for validity,
5242 // if necessary) and act on it.
5248 if (type > MULTI_MAX_TYPE)
5250 mprintf((1, "multi_process_data: invalid type %d.\n", type));
5258 fprintf (RecieveLogFile,"Packet type: %d Len:%d TT=%d\n",type,len,TTRecv[type]);
5259 fflush (RecieveLogFile);
5264 case MULTI_POSITION:
5265 if (!Endlevel_sequence) multi_do_position(buf); break;
5266 case MULTI_REAPPEAR:
5267 if (!Endlevel_sequence) multi_do_reappear(buf); break;
5269 if (!Endlevel_sequence) multi_do_fire(buf); break;
5271 multi_do_kill(buf); break;
5272 case MULTI_REMOVE_OBJECT:
5273 if (!Endlevel_sequence) multi_do_remobj(buf); break;
5274 case MULTI_PLAYER_DROP:
5275 case MULTI_PLAYER_EXPLODE:
5276 if (!Endlevel_sequence) multi_do_player_explode(buf); break;
5278 if (!Endlevel_sequence) multi_do_message(buf); break;
5280 if (!Endlevel_sequence) multi_do_quit(buf); break;
5281 case MULTI_BEGIN_SYNC:
5283 case MULTI_CONTROLCEN:
5284 if (!Endlevel_sequence) multi_do_controlcen_destroy(buf); break;
5285 case MULTI_POWERUP_UPDATE:
5286 if (!Endlevel_sequence) multi_do_powerup_update(buf); break;
5287 case MULTI_SOUND_FUNCTION:
5288 multi_do_sound_function(buf); break;
5290 if (!Endlevel_sequence) multi_do_drop_marker (buf); break;
5291 case MULTI_DROP_WEAPON:
5292 if (!Endlevel_sequence) multi_do_drop_weapon(buf); break;
5293 case MULTI_DROP_FLAG:
5294 if (!Endlevel_sequence) multi_do_drop_flag(buf); break;
5296 if (!Endlevel_sequence) multi_do_guided (buf); break;
5297 case MULTI_STOLEN_ITEMS:
5298 if (!Endlevel_sequence) multi_do_stolen_items(buf); break;
5299 case MULTI_WALL_STATUS:
5300 if (!Endlevel_sequence) multi_do_wall_status(buf); break;
5301 case MULTI_HEARTBEAT:
5302 if (!Endlevel_sequence) multi_do_heartbeat (buf); break;
5304 if (!Endlevel_sequence) multi_do_seismic (buf); break;
5306 if (!Endlevel_sequence) multi_do_light (buf); break;
5307 case MULTI_KILLGOALS:
5309 if (!Endlevel_sequence) multi_do_kill_goal_counts (buf); break;
5310 case MULTI_ENDLEVEL_START:
5311 if (!Endlevel_sequence) multi_do_escape(buf); break;
5312 case MULTI_END_SYNC:
5315 if (!Endlevel_sequence) multi_do_cloak(buf); break;
5317 if (!Endlevel_sequence) multi_do_decloak(buf); break;
5318 case MULTI_DOOR_OPEN:
5319 if (!Endlevel_sequence) multi_do_door_open(buf); break;
5320 case MULTI_CREATE_EXPLOSION:
5321 if (!Endlevel_sequence) multi_do_create_explosion(buf); break;
5322 case MULTI_CONTROLCEN_FIRE:
5323 if (!Endlevel_sequence) multi_do_controlcen_fire(buf); break;
5324 case MULTI_CREATE_POWERUP:
5325 if (!Endlevel_sequence) multi_do_create_powerup(buf); break;
5326 case MULTI_PLAY_SOUND:
5327 if (!Endlevel_sequence) multi_do_play_sound(buf); break;
5328 case MULTI_CAPTURE_BONUS:
5329 if (!Endlevel_sequence) multi_do_capture_bonus(buf); break;
5330 case MULTI_ORB_BONUS:
5331 if (!Endlevel_sequence) multi_do_orb_bonus(buf); break;
5332 case MULTI_GOT_FLAG:
5333 if (!Endlevel_sequence) multi_do_got_flag(buf); break;
5335 if (!Endlevel_sequence) multi_do_got_orb(buf); break;
5336 case MULTI_PLAY_BY_PLAY:
5337 if (!Endlevel_sequence) multi_do_play_by_play(buf); break;
5339 if (!Endlevel_sequence) multi_do_ranking (buf); break;
5340 case MULTI_MODEM_PING:
5341 if (!Endlevel_sequence) multi_send_modem_ping_return(); break;
5342 case MULTI_MODEM_PING_RETURN:
5343 if (!Endlevel_sequence) multi_do_modem_ping_return(); break;
5345 case MULTI_FINISH_GAME:
5346 multi_do_finish_game(buf); break; // do this one regardless of endsequence
5347 case MULTI_ROBOT_CONTROLS:
5348 if (!Endlevel_sequence) multi_do_robot_controls(buf); break;
5349 case MULTI_ROBOT_CLAIM:
5350 if (!Endlevel_sequence) multi_do_claim_robot(buf); break;
5351 case MULTI_ROBOT_POSITION:
5352 if (!Endlevel_sequence) multi_do_robot_position(buf); break;
5353 case MULTI_ROBOT_EXPLODE:
5354 if (!Endlevel_sequence) multi_do_robot_explode(buf); break;
5355 case MULTI_ROBOT_RELEASE:
5356 if (!Endlevel_sequence) multi_do_release_robot(buf); break;
5357 case MULTI_ROBOT_FIRE:
5358 if (!Endlevel_sequence) multi_do_robot_fire(buf); break;
5361 if (!Endlevel_sequence) multi_do_score(buf); break;
5362 case MULTI_CREATE_ROBOT:
5363 if (!Endlevel_sequence) multi_do_create_robot(buf); break;
5365 if (!Endlevel_sequence) multi_do_trigger(buf); break;
5366 case MULTI_START_TRIGGER:
5367 if (!Endlevel_sequence) multi_do_start_trigger(buf); break;
5369 if (!Endlevel_sequence) multi_do_flags(buf); break;
5370 case MULTI_DROP_BLOB:
5371 if (!Endlevel_sequence) multi_do_drop_blob(buf); break;
5372 case MULTI_ACTIVE_DOOR:
5373 if (!Endlevel_sequence) multi_do_active_door(buf); break;
5374 case MULTI_BOSS_ACTIONS:
5375 if (!Endlevel_sequence) multi_do_boss_actions(buf); break;
5376 case MULTI_CREATE_ROBOT_POWERUPS:
5377 if (!Endlevel_sequence) multi_do_create_robot_powerups(buf); break;
5378 case MULTI_HOSTAGE_DOOR:
5379 if (!Endlevel_sequence) multi_do_hostage_door_status(buf); break;
5380 case MULTI_SAVE_GAME:
5381 if (!Endlevel_sequence) multi_do_save_game(buf); break;
5382 case MULTI_RESTORE_GAME:
5383 if (!Endlevel_sequence) multi_do_restore_game(buf); break;
5384 case MULTI_REQ_PLAYER:
5385 if (!Endlevel_sequence) multi_do_req_player(buf); break;
5386 case MULTI_SEND_PLAYER:
5387 if (!Endlevel_sequence) multi_do_send_player(buf); break;
5390 mprintf((1, "Invalid type in multi_process_input().\n"));