/* THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. */ /* * $Source: /cvsroot/dxx-rebirth/d1x-rebirth/main/multi.c,v $ * $Revision: 1.1.1.1 $ * $Author: zicodxx $ * $Date: 2006/03/17 19:43:22 $ * * Multiplayer code shared by serial and network play. * */ #ifdef NETWORK #define DOS4G #ifdef RCS static char rcsid[] = "$Id: multi.c,v 1.1.1.1 2006/03/17 19:43:22 zicodxx Exp $"; #endif #include #include #include #include #include "game.h" #include "modem.h" #include "network.h" #include "multi.h" #include "object.h" #include "laser.h" #include "fuelcen.h" #include "scores.h" #include "gauges.h" #include "collide.h" #include "error.h" #include "fireball.h" #include "newmenu.h" #include "mono.h" #include "wall.h" #include "cntrlcen.h" #include "powerup.h" #include "polyobj.h" #include "bm.h" #include "endlevel.h" #include "key.h" #include "playsave.h" #include "timer.h" #include "digi.h" #include "sounds.h" #include "newdemo.h" #include "text.h" #include "kmatrix.h" #include "multibot.h" #include "gameseq.h" #include "physics.h" #include "config.h" #include "state.h" #include "multipow.h" #include "hudmsg.h" #include "ctype.h" // for isalpha #include "serial.h" #include "command.h" #include "vers_id.h" //Begin addition by GRiM FisH #include "nncoms.h" #include "ignore.h" //End addition by GRiM FisH //added 11/01/98 Matt Mueller #include "hudlog.h" //end addition -MM //added 11/01/98 Matt Mueller #include "pingstat.h" //end addition -MM //added 03/04/99 Matt Mueller #include "byteswap.h" #include "types.h" //end addition -MM //added 04/19/99 Matt Mueller #include "multiver.h" //end addition -MM //added on 4/23/99 by Victor Rachels #include "vlcnfire.h" //end addition -VR //added on 6/7/99 by Victor Rachels #include "reconfig.h" //end addition -VR //added on 6/15/99 by Owen Evans #include "strutil.h" //end added - OE //added on 11/20/99 by Victor Rachels to add observer mode #include "observer.h" //end this section addition - VR // // Local macros and prototypes // // LOCALIZE ME!! #define vm_angvec_zero(v) (v)->p=(v)->b=(v)->h=0 void reset_player_object(void); // In object.c but not in object.h void drop_player_eggs(object *player); // from collide.c void StartLevel(void); // From gameseq.c void GameLoop(int, int); // From game.c // // Global variables // int control_invul_time = 0; int who_killed_controlcen = -1; // -1 = noone //do we draw the kill list on the HUD? int Show_kill_list = 1; int Show_reticle_name = 1; fix Show_kill_list_timer = 0; int multi_sending_message = 0; int multi_defining_message = 0; int multi_message_index = 0; unsigned char multibuf[MAX_MULTI_MESSAGE_LEN+4]; // This is where multiplayer message are built unsigned char multibuf2[MAX_MULTI_MESSAGE_LEN+4]; short remote_to_local[MAX_NUM_NET_PLAYERS][MAX_OBJECTS]; // Remote object number for each local object short local_to_remote[MAX_OBJECTS]; sbyte object_owner[MAX_OBJECTS]; // Who created each object in my universe, -1 = loaded at start int Net_create_objnums[MAX_NET_CREATE_OBJECTS]; // For tracking object creation that will be sent to remote int Net_create_loc = 0; // pointer into previous array int Network_laser_fired = 0; // How many times we shot int Network_laser_gun; // Which gun number we shot int Network_laser_flags; // Special flags for the shot int Network_laser_level; // What level short Network_laser_track; // Who is it tracking? char Network_message[MAX_MESSAGE_LEN]; char Network_message_macro[4][MAX_MESSAGE_LEN]; int Network_message_reciever=-1; int sorted_kills[MAX_NUM_NET_PLAYERS]; short kill_matrix[MAX_NUM_NET_PLAYERS][MAX_NUM_NET_PLAYERS]; int multi_goto_secret = 0; short team_kills[2]; int multi_in_menu = 0; int multi_leave_menu = 0; int multi_quit_game = 0; //added 02/26/99 Matt Mueller - reactor kill stats short reactor_kills[MAX_NUM_NET_PLAYERS]; int reactor_kills_total; //end addition -MM uint multi_allow_powerup; uint multi_got_pow_count; netgame_info Netgame; bitmap_index multi_player_textures[MAX_NUM_NET_PLAYERS][N_PLAYER_SHIP_TEXTURES]; typedef struct netplayer_stats { ubyte message_type; ubyte Player_num; // Who am i? uint flags; // Powerup flags, see below... fix energy; // Amount of energy remaining. fix shields; // shields remaining (protection) ubyte lives; // Lives remaining, 0 = game over. ubyte laser_level; // Current level of the laser. ubyte primary_weapon_flags; // bit set indicates the player has this weapon. ubyte secondary_weapon_flags; // bit set indicates the player has this weapon. ushort primary_ammo[MAX_PRIMARY_WEAPONS]; // How much ammo of each type. ushort secondary_ammo[MAX_SECONDARY_WEAPONS]; // How much ammo of each type. int last_score; // Score at beginning of current level. int score; // Current score. fix cloak_time; // Time cloaked fix invulnerable_time; // Time invulnerable fix homing_object_dist; // Distance of nearest homing object. short net_killed_total; // Number of times killed total short net_kills_total; // Number of net kills total short num_kills_level; // Number of kills this level short num_kills_total; // Number of kills total short num_robots_level; // Number of initial robots this level short num_robots_total; // Number of robots total ushort hostages_rescued_total; // Total number of hostages rescued. ushort hostages_total; // Total number of hostages. ubyte hostages_on_board; // Number of hostages on ship. ubyte unused[16]; } netplayer_stats; int message_length[MULTI_MAX_TYPE+1] = { 24, // POSITION 3, // REAPPEAR 8, // FIRE #ifdef SHAREWARE 7, // KILL #else 5, // KILL #endif 4, // REMOVE_OBJECT #ifdef SHAREWARE 56, // PLAYER_EXPLODE #else 57, // PLAYER_EXPLODE #endif #ifdef SHAREWARE 28, // MESSAGE (MAX_MESSAGE_LENGTH = 25) #else 37, // MESSAGE (MAX_MESSAGE_LENGTH = 40) #endif 2, // QUIT #ifdef SHAREWARE 10, // PLAY_SOUND 24, // BEGIN_SYNC #else 4, // PLAY_SOUND 37, // BEGIN_SYNC #endif 4, // CONTROLCEN 5, // CLAIM ROBOT #ifdef SHAREWARE 3, // END_SYNC #else 4, // END_SYNC #endif 2, // CLOAK 3, // ENDLEVEL_START #ifdef SHAREWARE 7, // DOOR_OPEN #else 4, // DOOR_OPEN #endif 2, // CREATE_EXPLOSION 16, // CONTROLCEN_FIRE #ifdef SHAREWARE 56, // PLAYER_DROP 7, // CREATE_POWERUP #else 57, // PLAYER_DROP 19, // CREATE_POWERUP #endif 9, // MISSILE_TRACK 2, // DE-CLOAK #ifndef SHAREWARE 2, // MENU_CHOICE 28, // ROBOT_POSITION (shortpos_length (23) + 5 = 28) 8, // ROBOT_EXPLODE 5, // ROBOT_RELEASE 18, // ROBOT_FIRE 6, // SCORE 6, // CREATE_ROBOT 3, // TRIGGER 10, // BOSS_ACTIONS 27, // ROBOT_POWERUPS 7, // HOSTAGE_DOOR 2+24, //SAVE_GAME (ubyte slot, uint id, char name[20]) 2+4, //RESTORE_GAME (ubyte slot, uint id) 1+1, // MULTI_REQ_PLAYER sizeof(netplayer_stats), // MULTI_SEND_PLAYER 19, // PLAYER_POWERUP_COUNT 19, // START_POWERUP_COUNT #else 2, // MENU_CHOICE #endif #ifndef SHAREWARE //====================================================== //Added 8/28/98 by Geoff Coovert for packet insurance 1+1+4+1+57+sizeof(shorterpos), // MEKH_PACKET_NEEDACK (int p_type, int plr_num, fix PacketID, int length, packet) // [57+shorterpos is the largest packet in the list, thus the biggest that can be sent] 1+1+1+4, // MEKH_PACKET_ACK (int ptype, int ptype_we're_acking, int plnum, fix id) 2+4,//PING 2+4, //PONG 8+sizeof(shorterpos),//MULTI_POS_FIRE 57+sizeof(shorterpos),//MULTI_POS_PLAYER_EXPLODE 9,//MULTI_D1X_VER_PACKET //added 4/23/99 by Victor Rachels for vulcanfire 2, //VULCAN_ON 2, //VULCAN_OFF //added 6/7/99 by Victor Rachels for ingame config 1+1+1+64, //INGAME_CONFIG (p_type, player, version, data...) #endif }; //These are just to allow us to easily modify what should be acked int mekh_insured_packets[MULTI_MAX_TYPE+1] = { 0, // POSITION 0, // REAPPEAR 0, // FIRE //1 for testing only 1, // KILL 0, // REMOVE_OBJECT 1, // PLAYER_EXPLODE 1, // MESSAGE (MAX_MESSAGE_LENGTH = 40) 0, // QUIT 0, // PLAY_SOUND 0, // BEGIN_SYNC 1, // CONTROLCEN 0, // CLAIM ROBOT 0, // END_SYNC 1, // CLOAK 1, // ENDLEVEL_START 1, // DOOR_OPEN 0, // CREATE_EXPLOSION 0, // CONTROLCEN_FIRE 0, // PLAYER_DROP 0, // CREATE_POWERUP 1, // MISSILE_TRACK #ifndef SHAREWARE 1, // DE-CLOAK 0, // MENU_CHOICE 0, // ROBOT_POSITION (shortpos_length (23) + 5 = 28) 0, // ROBOT_EXPLODE 0, // ROBOT_RELEASE 0, // ROBOT_FIRE 1, // SCORE 0, // CREATE_ROBOT 1, // TRIGGER 0, // BOSS_ACTIONS 0, // ROBOT_POWERUPS 0, // HOSTAGE_DOOR 0, // SAVE_GAME (ubyte slot, uint id, char name[20]) 0, // RESTORE_GAME (ubyte slot, uint id) 0, // MULTI_REQ_PLAYER 0, // MULTI_SEND_PLAYER 1, // PLAYER_POWERUP_COUNT 1, // START_POWERUP_COUNT #else 0, // MENU #endif #ifndef SHAREWARE // It would be a very bad idea to set these two :) 0, // MEKH_PACKET_NEEDACK 0, // MEKH_PACKET_ACK 0, //PING 0, //PONG 0,//MULTI_POS_FIRE 1,//MULTI_POS_PLAYER_EXPLODE 1,//MULTI_D1X_VER_PACKET //added 4/23/99 by Victor Rachels for vulcanfire 0,//MULTI_ALT_VULCAN_ON 0,//MULTI_ALT_VULCAN_OFF //added 6/7/99 by Victor Rachels for ingame config 1,//INGAME_CONFIG #endif }; //====================================================== void multi_reset_player_object(object *objp); void multi_set_robot_ai(void); void multi_save_game(ubyte slot, uint id, char *desc); void multi_restore_game(ubyte slot, uint id); void extract_netplayer_stats( netplayer_stats *ps, player * pd ); // // Functions that replace what used to be macros // int objnum_remote_to_local(int remote_objnum, int owner) { // Map a remote object number from owner to a local object number int result; if ((owner >= N_players) || (owner < -1)) { Int3(); // Illegal! return(remote_objnum); } if (owner == -1) return(remote_objnum); if ((remote_objnum < 0) || (remote_objnum >= MAX_OBJECTS)) return(-1); result = remote_to_local[owner][remote_objnum]; if (result < 0) { mprintf((1, "Remote object owner %d number %d mapped to -1!\n", owner, remote_objnum)); return(-1); } #ifndef NDEBUG if (object_owner[result] != owner) { mprintf((1, "Remote object owner %d number %d doesn't match owner %d.\n", owner, remote_objnum, object_owner[result])); } #endif // Assert(object_owner[result] == owner); return(result); } int objnum_local_to_remote(int local_objnum, sbyte *owner) { // Map a local object number to a remote + owner int result; if ((local_objnum < 0) || (local_objnum > Highest_object_index)) { *owner = -1; return(-1); } *owner = object_owner[local_objnum]; if (*owner == -1) return(local_objnum); if ((*owner >= N_players) || (*owner < -1)) { Int3(); // Illegal! *owner = -1; return local_objnum; } result = local_to_remote[local_objnum]; // mprintf((0, "Local object %d mapped to owner %d objnum %d.\n", local_objnum, // *owner, result)); if (result < 0) { Int3(); // See Rob, object has no remote number! } return(result); } void map_objnum_local_to_remote(int local_objnum, int remote_objnum, int owner) { // Add a mapping from a network remote object number to a local one Assert(local_objnum > -1); Assert(remote_objnum > -1); Assert(owner > -1); Assert(owner != Player_num); Assert(local_objnum < MAX_OBJECTS); Assert(remote_objnum < MAX_OBJECTS); object_owner[local_objnum] = owner; remote_to_local[owner][remote_objnum] = local_objnum; local_to_remote[local_objnum] = remote_objnum; return; } void map_objnum_local_to_local(int local_objnum) { // Add a mapping for our locally created objects Assert(local_objnum > -1); Assert(local_objnum < MAX_OBJECTS); object_owner[local_objnum] = Player_num; remote_to_local[Player_num][local_objnum] = local_objnum; local_to_remote[local_objnum] = local_objnum; return; } // // Part 1 : functions whose main purpose in life is to divert the flow // of execution to either network or serial specific code based // on the curretn Game_mode value. // void multi_endlevel_score(void) { int old_connect=0; // int i; #ifdef SHAREWARE return; // DEBUG #endif // Show a score list to end of net players // Save connect state and change to new connect state #ifdef NETWORK if (Game_mode & GM_NETWORK) { old_connect = Players[Player_num].connected; Players[Player_num].connected = CONNECT_END_MENU; } #endif // Do the actual screen we wish to show Function_mode = FMODE_MENU; #ifdef NETWORK Network_status = NETSTAT_ENDLEVEL; #endif if (Game_mode & GM_MULTI_COOP) DoEndLevelScoreGlitz(1); else kmatrix_view(1); Function_mode = FMODE_GAME; // Restore connect state if (Game_mode & GM_NETWORK) { Players[Player_num].connected = old_connect; } #ifndef SHAREWARE if (Game_mode & GM_MULTI_COOP) { int i; for (i = 0; i < MaxNumNetPlayers; i++) // Reset keys Players[i].flags &= ~(PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_GOLD_KEY); } #endif } int get_team(int pnum) { if (Netgame.team_vector & (1 << pnum)) return 1; else return 0; } #ifndef SHAREWARE int multi_choose_mission(int *anarchy_only) { int i, n_missions; int default_mission; char *m[MAX_MISSIONS]; int new_mission_num = 0; *anarchy_only = 0; n_missions = build_mission_list(1); if (n_missions > 1) { default_mission = 0; for (i=0;i= MAX_NUM_NET_PLAYERS) || (playernum < 0)) { Int3(); // Non-terminal, see Rob return; } // if (Objects[Players[playernum].objnum].type != OBJ_PLAYER) // mprintf((1, "Warning: Player %d is not currently a player.\n", playernum)); obj = &Objects[Players[playernum].objnum]; obj->type = OBJ_GHOST; obj->render_type = RT_NONE; obj->movement_type = MT_NONE; multi_reset_player_object(obj); #ifndef SHAREWARE if (Game_mode & GM_MULTI_ROBOTS) multi_strip_robots(playernum); #endif } void multi_make_ghost_player(int playernum) { object *obj; // Assert(playernum != Player_num); // Assert(playernum < MAX_NUM_NET_PLAYERS); if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS)) { Int3(); // Non-terminal, see rob return; } // if(Objects[Players[playernum].objnum].type != OBJ_GHOST) // mprintf((1, "Warning: Player %d is not currently a ghost.\n", playernum)); obj = &Objects[Players[playernum].objnum]; obj->type = OBJ_PLAYER; obj->movement_type = MT_PHYSICS; multi_reset_player_object(obj); } int multi_get_kill_list(int *plist) { // Returns the number of active net players and their // sorted order of kills int i; int n = 0; for (i = 0; i < N_players; i++) // if (Players[sorted_kills[i]].connected) plist[n++] = sorted_kills[i]; if (n == 0) Int3(); // SEE ROB OR MATT // memcpy(plist, sorted_kills, N_players*sizeof(int)); return(n); } void multi_sort_kill_list(void) { // Sort the kills list each time a new kill is added int kills[MAX_NUM_NET_PLAYERS]; int i; int changed = 1; for (i = 0; i < MAX_NUM_NET_PLAYERS; i++) { #ifndef SHAREWARE if (Game_mode & GM_MULTI_COOP) kills[i] = Players[i].score; else #endif kills[i] = Players[i].net_kills_total; } while (changed) { changed = 0; for (i = 0; i < N_players-1; i++) { if (kills[sorted_kills[i]] < kills[sorted_kills[i+1]]) { changed = sorted_kills[i]; sorted_kills[i] = sorted_kills[i+1]; sorted_kills[i+1] = changed; changed = 1; } } } // mprintf((0, "Sorted kills %d %d.\n", sorted_kills[0], sorted_kills[1])); } void multi_compute_kill(int killer, int killed) { // Figure out the results of a network kills and add it to the // appropriate player's tally. int killed_pnum, killed_type; int killer_pnum, killer_type; char killed_name[(CALLSIGN_LEN*2)+4]; char killer_name[(CALLSIGN_LEN*2)+4]; kmatrix_kills_changed = 1; // Both object numbers are localized already! mprintf((0, "compute_kill passed: object %d killed object %d.\n", killer, killed)); if ((killed < 0) || (killed > Highest_object_index) || (killer < 0) || (killer > Highest_object_index)) { Int3(); // See Rob, illegal value passed to compute_kill; return; } killed_type = Objects[killed].type; killer_type = Objects[killer].type; if ((killed_type != OBJ_PLAYER) && (killed_type != OBJ_GHOST)) { Int3(); // compute_kill passed non-player object! return; } killed_pnum = Objects[killed].id; Assert ((killed_pnum >= 0) && (killed_pnum < N_players)); if (Game_mode & GM_TEAM) sprintf(killed_name, "%s (%s)", Players[killed_pnum].callsign, Netgame.team_name[get_team(killed_pnum)]); else sprintf(killed_name, "%s", Players[killed_pnum].callsign); #ifndef SHAREWARE if (Newdemo_state == ND_STATE_RECORDING) newdemo_record_multi_death(killed_pnum); #endif digi_play_sample( SOUND_HUD_KILL, F3_0 ); if (killer_type == OBJ_CNTRLCEN) { Players[killed_pnum].net_killed_total++; Players[killed_pnum].net_kills_total--; #ifndef SHAREWARE if (Newdemo_state == ND_STATE_RECORDING) newdemo_record_multi_kill(killed_pnum, -1); #endif //edited 02/26/99 Matt Mueller - add kill stats to messages reactor_kills[killed_pnum]++; reactor_kills_total++; if (killed_pnum == Player_num) hud_message(MSGC_MULTI_KILL, "%s(%i) %s(%i).", TXT_YOU_WERE,reactor_kills[killed_pnum], TXT_KILLED_BY_NONPLAY,reactor_kills_total); else hud_message(MSGC_MULTI_KILL, "\002%c%s\004(%i) %s %s(%i).", gr_getcolor(player_rgb[killed_pnum].r,player_rgb[killed_pnum].g,player_rgb[killed_pnum].b)+1, killed_name,reactor_kills[killed_pnum],TXT_WAS, TXT_KILLED_BY_NONPLAY,reactor_kills_total ); //end edit -MM return; } #ifndef SHAREWARE else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST)) { if (killed_pnum == Player_num) hud_message(MSGC_MULTI_KILL, "%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_ROBOT); else hud_message(MSGC_MULTI_KILL, "%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_ROBOT ); Players[killed_pnum].net_killed_total++; return; } #else else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST)) { Int3(); // Illegal killer type? return; } #endif killer_pnum = Objects[killer].id; if (Game_mode & GM_TEAM) sprintf(killer_name, "%s (%s)", Players[killer_pnum].callsign, Netgame.team_name[get_team(killer_pnum)]); else sprintf(killer_name, "%s", Players[killer_pnum].callsign); // Beyond this point, it was definitely a player-player kill situation if ((killer_pnum < 0) || (killer_pnum >= N_players)) Int3(); // See rob, tracking down bug with kill HUD messages if ((killed_pnum < 0) || (killed_pnum >= N_players)) Int3(); // See rob, tracking down bug with kill HUD messages if (killer_pnum == killed_pnum) { if (Game_mode & GM_TEAM) { team_kills[get_team(killed_pnum)] -= 1; } Players[killed_pnum].net_killed_total += 1; Players[killed_pnum].net_kills_total -= 1; #ifndef SHAREWARE if (Newdemo_state == ND_STATE_RECORDING) newdemo_record_multi_kill(killed_pnum, -1); #endif kill_matrix[killed_pnum][killed_pnum] += 1; // # of suicides //edited 02/26/99 Matt Mueller - add kill stats to messages if (killer_pnum == Player_num) hud_message(MSGC_MULTI_KILL, "%s(%i) %s %s(%i)!", TXT_YOU,Players[killed_pnum].net_kills_total, TXT_KILLED, TXT_YOURSELF,-kill_matrix[killed_pnum][killed_pnum]); else hud_message(MSGC_MULTI_KILL, "\002%c%s\004(%i) %s(%i)", gr_getcolor(player_rgb[killed_pnum].r,player_rgb[killed_pnum].g,player_rgb[killed_pnum].b)+1, killed_name,Players[killed_pnum].net_kills_total,TXT_SUICIDE,-kill_matrix[killed_pnum][killed_pnum]); //end edit -MM } else { if (Game_mode & GM_TEAM) { if (get_team(killed_pnum) == get_team(killer_pnum)) team_kills[get_team(killed_pnum)] -= 1; else team_kills[get_team(killer_pnum)] += 1; } Players[killer_pnum].net_kills_total += 1; #ifndef SHAREWARE if (Newdemo_state == ND_STATE_RECORDING) newdemo_record_multi_kill(killer_pnum, 1); #endif Players[killed_pnum].net_killed_total += 1; kill_matrix[killer_pnum][killed_pnum] += 1; //edited 02/26/99 Matt Mueller - add kill stats to messages if (killer_pnum == Player_num) { hud_message(MSGC_MULTI_KILL, "%s(%i) %s \002%c%s\004(%i)!", TXT_YOU,Players[killer_pnum].net_kills_total,TXT_KILLED, gr_getcolor(player_rgb[killed_pnum].r,player_rgb[killed_pnum].g,player_rgb[killed_pnum].b)+1, killed_name,kill_matrix[killer_pnum][killed_pnum]); if ((Game_mode & GM_MULTI_COOP) && (Players[Player_num].score >= 1000)) add_points_to_score(-1000); } else if (killed_pnum == Player_num) hud_message(MSGC_MULTI_KILL, "\002%c%s\004(%i) %s %s(%i)!", gr_getcolor(player_rgb[killer_pnum].r,player_rgb[killer_pnum].g,player_rgb[killer_pnum].b)+1, killer_name,Players[killer_pnum].net_kills_total, TXT_KILLED, TXT_YOU,kill_matrix[killer_pnum][killed_pnum]); else hud_message(MSGC_MULTI_KILL, "\002%c%s\004(%i) %s \002%c%s\004(%i)!", gr_getcolor(player_rgb[killer_pnum].r,player_rgb[killer_pnum].g,player_rgb[killer_pnum].b)+1, killer_name,Players[killer_pnum].net_kills_total,TXT_KILLED, gr_getcolor(player_rgb[killed_pnum].r,player_rgb[killed_pnum].g,player_rgb[killed_pnum].b)+1, killed_name,kill_matrix[killer_pnum][killed_pnum]); //end edit -MM } multi_sort_kill_list(); multi_show_player_list(); } void multi_do_frame(void) { if (!(Game_mode & GM_MULTI)) { Int3(); return; } //====================================================== //Added 9/5 by Geoff Coovert to do ack resends mekh_resend_needack(); //All the time and player checks are done there //====================================================== //added 2/9/99 by Victor Rachels for ping constantly if(ping_stats_on) ping_stats_frame(); //end this section addition - VR multi_send_message(); // Send any waiting messages //added on 4/17/99 by Matt Mueller multi_d1x_ver_frame(); //end this section addition - MM if (!multi_in_menu) multi_leave_menu = 0; #ifndef SHAREWARE if (Game_mode & GM_MULTI_ROBOTS) { multi_check_robot_timeout(); } #endif if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)) { com_do_frame(); } else { network_do_frame(0, 1); } //added/killed on 10/2/98 by Victor Rachels to fix non-quitting //-killed- if (multi_quit_game && !multi_in_menu) //-killed- { //-killed- multi_quit_game = 0; //-killed- longjmp(LeaveGame, 1); //-killed- } //end kill - Victor Rachels } //edit 03/04/99 Matt Mueller - some debug code.. ignore if you wish. void multi_send_data_real(unsigned char *buf, int len, int repeat,char *file,char *func,int line) //end edit -MM { // mprintf ((0,"%s:%s:%i multi_send_data %i(%i) len=%i r=%i\n",file,func,line,buf[0],(int)buf[0],len,repeat)); Assert(buf[0] <= MULTI_MAX_TYPE); if (buf[0] >MULTI_MAX_TYPE) { mprintf( (0,"multi_send_data invalid type: %i > %i\n",buf[0],MULTI_MAX_TYPE)); return; } if (Game_mode & GM_NETWORK) Assert(buf[0] > 0); //====================================================== //Edit on 9/5 by Geoff Coovert - Keep from forced bloating of needack packs if ((int)buf[0] == MEKH_PACKET_NEEDACK) Assert(len == message_length[(int)buf[7]] + 7); else Assert(len == message_length[(int)buf[0]]); //kill 03/05/99 Matt Mueller - allow ack'd packets to work on modem games //--killed-- if(Game_mode & GM_NETWORK) //end kill -MM if (mekh_insured_packets[buf[0]]) { mekh_send_reg_data(buf, len, repeat); return; } //====================================================== if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)) com_send_data(buf, len, repeat); else if (Game_mode & GM_NETWORK) { //edit 03/04/99 Matt Mueller - use direct mode for most packets. (pos_fire is handled elsewhere, so checking here might be redundant..) if(buf[0]!=MULTI_FIRE && buf[0]!=MULTI_REAPPEAR) mekh_send_direct_broadcast(buf, len); else network_send_data(buf, len, repeat); //end edit -MM } } void multi_leave_game(void) { // if (Function_mode != FMODE_GAME) // return; if (!(Game_mode & GM_MULTI)) return; //added 11/01/98 by Matthew Mueller kmatrix_log(0); //end addition -MM if (Game_mode & GM_NETWORK) { mprintf((0, "Sending explosion message.\n")); Net_create_loc = 0; drop_player_eggs(ConsoleObject); multi_send_position(Players[Player_num].objnum); multi_send_player_explode(MULTI_PLAYER_DROP); } mprintf((1, "Sending leave game.\n")); multi_send_quit(MULTI_QUIT); if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)) serial_leave_game(); if (Game_mode & GM_NETWORK) network_leave_game(); Game_mode |= GM_GAME_OVER; //added on 9/20/98 by Geoff Coovert to fix Shift-ESC in multi if (Function_mode != FMODE_EXIT) //end this addition - Geoff Coovert Function_mode = FMODE_MENU; // N_players = 0; // change_playernum_to(0); // Viewer = ConsoleObject = &Objects[0]; plyr_save_stats(); } void multi_show_player_list() { if (!(Game_mode & GM_MULTI) || (Game_mode & GM_MULTI_COOP)) return; if (Show_kill_list) return; Show_kill_list_timer = F1_0*5; // 5 second timer Show_kill_list = 1; } int multi_endlevel(int *secret) { int result = 0; if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)) com_endlevel(secret); // an opportunity to re-sync or whatever else if (Game_mode & GM_NETWORK) result = network_endlevel(secret); return(result); } // // Part 2 : functions that act on network/serial messages and change the // the state of the game in some way. // int multi_menu_poll(void) { fix old_shields; int t1; int was_fuelcen_alive; was_fuelcen_alive = Fuelcen_control_center_destroyed; // Special polling function for in-game menus for multiplayer and serial if (! ((Game_mode & GM_MULTI) && (Function_mode == FMODE_GAME)) ) return(0); if (multi_leave_menu) return(-1); old_shields = Players[Player_num].shields; multi_in_menu++; // Track level of menu nesting GameLoop( 0, 0 ); multi_in_menu--; // t1 = timer_get_fixed_seconds(); // while (timer_get_fixed_seconds() < t1+F1_0/20) // ; // only allow faster framerates if exploded to prevent // possible cheating using the higher framerate -- adb //added/changed by ADB for getting rid of TICKER t1 = timer_get_fixed_seconds() + F1_0/20; while (timer_get_fixed_seconds() < t1 && (!Player_exploded || !key_checkch())) ; //end change - ADB if (Endlevel_sequence || (Fuelcen_control_center_destroyed && !was_fuelcen_alive) || (Player_is_dead && !Player_exploded) || (Players[Player_num].shields < old_shields)) { multi_leave_menu = 1; return(-1); } if ((Fuelcen_control_center_destroyed) && (Fuelcen_seconds_left < 10)) { multi_leave_menu = 1; return(-1); } #ifdef __LINUX__ if ((Game_mode & GM_MODEM) && (!com_getdcd())) { multi_leave_menu = 1; return(-1); } #endif return(0); } void multi_define_macro(int key) { if (!(Game_mode & GM_MULTI)) return; key &= (~KEY_SHIFTED); switch(key) { case KEY_F9: multi_defining_message = 1; break; case KEY_F10: multi_defining_message = 2; break; case KEY_F11: multi_defining_message = 3; break; case KEY_F12: multi_defining_message = 4; break; default: Int3(); } if (multi_defining_message) { multi_message_index = 0; Network_message[multi_message_index] = 0; } } char feedback_result[200]; void multi_message_feedback(void) { char *colon; int found = 0; int i; if (!( ((colon = strrchr(Network_message, ':')) == NULL) || (colon-Network_message < 1) || (colon-Network_message > CALLSIGN_LEN) )) { sprintf(feedback_result, "%s ", TXT_MESSAGE_SENT_TO); if ((Game_mode & GM_TEAM) && (atoi(Network_message) > 0) && (atoi(Network_message) < 3)) { sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[atoi(Network_message)-1]); found = 1; } if (Game_mode & GM_TEAM) { for (i = 0; i < N_players; i++) { if (!strncasecmp(Netgame.team_name[i], Network_message, colon-Network_message)) { if (found) strcat(feedback_result, ", "); found++; if (!(found % 4)) strcat(feedback_result, "\n"); sprintf(feedback_result+strlen(feedback_result), "%s '%s'", TXT_TEAM, Netgame.team_name[i]); } } } for (i = 0; i < N_players; i++) { if ((!strncasecmp(Players[i].callsign, Network_message, colon-Network_message)) && (i != Player_num) && (Players[i].connected)) { if (found) strcat(feedback_result, ", "); found++; if (!(found % 4)) strcat(feedback_result, "\n"); sprintf(feedback_result+strlen(feedback_result), "%s", Players[i].callsign); } } if (!found) strcat(feedback_result, TXT_NOBODY); else strcat(feedback_result, "."); digi_play_sample(SOUND_HUD_MESSAGE, F1_0); Assert(strlen(feedback_result) < 200); hud_message(MSGC_GAME_FEEDBACK, feedback_result); } } //added/moved on 11/10/98 by Victor Rachels to declare before this function void multi_send_message_end(); //end this section change - VR void multi_send_macro(int key) { if (! (Game_mode & GM_MULTI) ) return; switch(key) { case KEY_F9: key = 0; break; case KEY_F10: key = 1; break; case KEY_F11: key = 2; break; case KEY_F12: key = 3; break; default: Int3(); } if (!Network_message_macro[key][0]) { hud_message(MSGC_GAME_FEEDBACK, TXT_NO_MACRO); return; } snprintf(Network_message, MAX_MESSAGE_LEN, "%s", Network_message_macro[key]); //added/replaced on 11/10/98 by Victor Rachels to make macros act like normal msgs multi_send_message_end(); //-replaced- Network_message_reciever = 100; //-replaced- hud_message(MSGC_GAME_FEEDBACK, "%s '%s'", TXT_SENDING, Network_message); //-replaced- multi_message_feedback(); //end this section change - VR } void multi_send_message_start() { if (Game_mode&GM_MULTI) { multi_sending_message = 1; multi_message_index = 0; Network_message[multi_message_index] = 0; } } // compare s1 with s2 ignoring case and only considering the // first strlen(s2) characters of s1 int strcasecmpbegin(const char *s1, const char *s2) { return strncasecmp(s1, s2, strlen(s2)); } //====================================================== //Added 8/28/98 by Geoff Coovert to do resend-last-message. char mekh_msg_last_sent[155]; // HUD_MESSAGE_LENGTH + 5 - cleaner than extern, etc void mekh_resend_last() { snprintf(Network_message, MAX_MESSAGE_LEN, "%s", mekh_msg_last_sent); multi_send_message_end(); }; //====================================================== void multi_send_message_end() { // int pl; //added/killed on 1/21/99 by Victor Rachels to move to command.c //-killed- //added on 11/10/98 by Victor Rachels to reduce computations //-killed- //all references to strlen(Network_message) are changed to this in this function //-killed- int NMl=strlen(Network_message); //-killed- //end this section addition - VR //end this section kill - VR //====================================================== //added on 8/28/98 by Geoff Coovert to do resend-last-message. strcpy(&mekh_msg_last_sent[0], Network_message); //====================================================== multi_message_index = 0; multi_sending_message = 0; //added on 1/10/99 by Victor Rachels to get command $ in msgs. if(Network_message[0]=='$' || Network_message[0]=='/') { if(Command_parse(Network_message+1)) return; } //end this section addition //added/moved on 1/21/99 by Victor Rachels to command.c //-moved- if((NMl>=5)&&!strnicmp("ping:", Network_message, 5)) //-moved- { //-moved- //added/modified on 8/13/98 by Matt Mueller to fix ping bug, and allow ping all //-moved- if (*(Network_message + 5)) //-moved- { //-moved- //Send only to the recepient in the message.. //-moved- for(pl = 0; pl < MAX_NUM_NET_PLAYERS; pl++) //-moved- if (!strcasecmpbegin(Players[pl].callsign, Network_message + 5)) //-moved- { //-moved- //send only to the specified user. //-moved- Network_message_reciever = pl; //-moved- sprintf(Network_message, "PING:%lu %i", timer_get_fixed_seconds(),pl); //-moved- multi_send_message(); //-moved- hud_message(MSGC_GAME_FEEDBACK, "Pinging %s...", Players[pl].callsign); //-moved- return; //-moved- } //-moved- //Bad username //-moved- hud_message(MSGC_GAME_FEEDBACK, "PING: %s doesn't exist!", Network_message + 5); //-moved- } else //-moved- { //-moved- Network_message_reciever = 100; //-moved- sprintf(Network_message, "PING:%lu", timer_get_fixed_seconds()); //-moved- multi_send_message(); //-moved- hud_message(MSGC_GAME_FEEDBACK,"Pinging..."); //-moved- } //-moved- //end modified section - Matt Mueller //-moved- return; //-moved- } //-moved- //added on 10/31/98 by Matt Mueller to allow rechecking of version info in game. //-moved- if((NMl>=4)&&!strnicmp("d1x:", Network_message, 4)) //-moved- { //-moved- if (*(Network_message + 4)) //-moved- { //-moved- for(pl = 0; pl < MAX_NUM_NET_PLAYERS; pl++) //-moved- if (!strcasecmpbegin(Players[pl].callsign, Network_message + 4)) //-moved- { //-moved- network_send_config_messages(pl,1); //-moved- return; //-moved- } //-moved- hud_message(MSGC_GAME_FEEDBACK, "d1x: %s doesn't exist!", Network_message + 4); //-moved- } //-moved- else //-moved- { //-moved- network_send_config_messages(100,1); //-moved- } //-moved- return; //-moved- } //-moved- //end addition - Matt Mueller //-moved- //added 11/09/98 by Matt Mueller/Victor Rachels //-moved- if((NMl==13)&&!strnicmp(":shortpackets",Network_message,13)) //-moved- { //-moved- if(!Network_short_packets){ //-moved- hud_message(MSGC_GAME_FEEDBACK, "now using Short Packets"); //-moved- Network_short_packets = 1; //-moved- network_send_config_messages(100,1); //-moved- }else //-moved- hud_message(MSGC_GAME_FEEDBACK, "already using Short Packets"); //-moved- //-moved- return; //-moved- } //-moved- //end addition -MM //-moved- //added 11/10/98 by Victor Rachels to add handicapping //-moved- if((NMl>9)&&!strnicmp("handicap:",Network_message,9)) //-moved- { //-moved- int t; //-moved- t=atoi(Network_message+9); //-moved- if(t==100) //-moved- { //-moved- handicap = MAX_SHIELDS; //-moved- Network_message_reciever = 100; //-moved- sprintf(Network_message,"I am no longer handicapped."); //-moved- hud_message(MSGC_GAME_FEEDBACK, "Handicap reset. Other players notified"); //-moved- multi_send_message(); //-moved- multi_message_feedback(); //-moved- } //-moved- else if(t>0 && (t<100 || (network_i_am_master()&&t<201))) //-moved- { //-moved- handicap = i2f(t); //-moved- if(network_i_am_master()) //-moved- Lhandicap = 1; //-moved- else //-moved- Lhandicap = 0; //-moved- Network_message_reciever = 100; //-moved- sprintf(Network_message,"I am using a handicap of %i.",t); //-moved- hud_message(MSGC_GAME_FEEDBACK, "Handicap set. Other players notified"); //-moved- multi_send_message(); //-moved- multi_message_feedback(); //-moved- } //-moved- else if(t > 100 && t <= 200) //-moved- { //-moved- handicap = i2f(t); //-moved- if(Lhandicap) //-moved- { //-moved- Network_message_reciever = 100; //-moved- sprintf(Network_message,"I am using handicap of %i.",t); //-moved- hud_message(MSGC_GAME_FEEDBACK, "Handicap set. Other players notified"); //-moved- multi_send_message(); //-moved- multi_message_feedback(); //-moved- } //-moved- else //-moved- { //-moved- int netmaster=network_whois_master(); //-moved- hud_message(MSGC_GAME_FEEDBACK, "Master permission required. Requesting..."); //-moved- Network_message_reciever = netmaster; //-moved- sprintf(Network_message,"%s requests handicap of %i",Players[Player_num].callsign,t); //-moved- multi_send_message(); //-moved- multi_message_feedback(); //-moved- Network_message_reciever = netmaster; //-moved- sprintf(Network_message,"Send %s:handicap %i to allow",Players[Player_num].callsign,t); //-moved- multi_send_message(); //-moved- multi_message_feedback(); //-moved- } //-moved- } //-moved- else //-moved- hud_message(MSGC_GAME_FEEDBACK, "Invalid handicap value"); //-moved- return; //-moved- } //-moved- //end addition - VR //-moved- // Begin addition by GRiM FisH //-moved- if((NMl>7)&&!strnicmp("ignore:",Network_message,7)) //-moved- { //-moved- addignore(Network_message+7); //-moved- return; //-moved- } //-moved- if((NMl>8)&&!strnicmp("ignoren:",Network_message,8)) //-moved- { //-moved- if(!isdigit(Network_message[8])) //-moved- { //-moved- hud_message(MSGC_GAME_FEEDBACK,"IGNOREN: Please use a number as input!"); //-moved- return; //-moved- } //-moved- //-moved- addignore_by_number(atoi(Network_message+8)); //-moved- return; //-moved- } //-moved- //-moved- if((NMl>9)&&!strnicmp("unignore:",Network_message,9)) //-moved- { //-moved- eraseignore(Network_message+9); //-moved- return; //-moved- } //-moved- if((NMl>10)&&!strnicmp("unignoren:",Network_message,10)) //-moved- { //-moved- if(!isdigit(Network_message[10])) //-moved- { //-moved- hud_message(MSGC_GAME_FEEDBACK,"UNIGNOREN: Please use a number as input!"); //-moved- return; //-moved- } //-moved- //-moved- eraseignore_by_number(atoi(Network_message+10)); //-moved- return; //-moved- } //-moved- if((NMl==12)&&!strnicmp(":clearignore",Network_message,12)) //-moved- { //-moved- clearignore(); //-moved- return; //-moved- } //-moved- if((NMl==11)&&!strnicmp(":listignore",Network_message,11)) //-moved- { //-moved- listignore(); //-moved- return; //-moved- } //-moved- if((NMl>5)&&!strnicmp("kick:",Network_message,5)) //-moved- { //-moved- boot(Network_message+5); //-moved- return; //-moved- } //-moved- if((NMl>6)&&!strnicmp("kickn:",Network_message,6)) //-moved- { //-moved- if(!isdigit(Network_message[6])) //-moved- { //-moved- hud_message(MSGC_GAME_FEEDBACK,"KICKN: Please use a number as input!"); //-moved- return; //-moved- } //-moved- //-moved- boot_by_number(atoi(Network_message+6)); //-moved- return; //-moved- } //-moved- if((NMl>7)&&!strnicmp("discon:",Network_message,7)) //-moved- { //-moved- discon(Network_message+7); //-moved- return; //-moved- } //-moved- if((NMl>8)&&!strnicmp("disconn:",Network_message,8)) //-moved- { //-moved- if(!isdigit(Network_message[8])) //-moved- { //-moved- hud_message(MSGC_GAME_FEEDBACK,"DISCONN: Please use a number as input!"); //-moved- return; //-moved- } //-moved- //-moved- discon_by_number(atoi(Network_message+8)); //-moved- return; //-moved- } //-moved- if((NMl>6)&&!strnicmp("ghost:",Network_message,6)) //-moved- { //-moved- ghost(Network_message+6); //-moved- return; //-moved- } //-moved- if((NMl>7)&&!strnicmp("ghostn:",Network_message,7)) //-moved- { //-moved- if(!isdigit(Network_message[7])) //-moved- { //-moved- hud_message(MSGC_GAME_FEEDBACK,"GHOSTN: Please use a number as input!"); //-moved- return; //-moved- } //-moved- //-moved- ghost_by_number(atoi(Network_message+7)); //-moved- return; //-moved- } //-moved- if((NMl>8)&&!strnicmp("unghost:",Network_message,8)) //-moved- { //-moved- unghost(Network_message+8); //-moved- return; //-moved- } //-moved- if((NMl>9)&&!strnicmp("unghostn:",Network_message,9)) //-moved- { //-moved- if(!isdigit(Network_message[9])) //-moved- { //-moved- hud_message(MSGC_GAME_FEEDBACK,"UNGHOSTN: Please use a number as input!"); //-moved- return; //-moved- } //-moved- //-moved- unghost_by_number(atoi(Network_message+9)); //-moved- return; //-moved- } //-moved- if((NMl>6)&&!strnicmp("recon:",Network_message,6)) //-moved- { //-moved- recon(Network_message+6); //-moved- return; //-moved- } //-moved- if((NMl>7)&&!strnicmp("reconn:",Network_message,7)) //-moved- { //-moved- if(!isdigit(Network_message[7])) //-moved- { //-moved- hud_message(MSGC_GAME_FEEDBACK,"RECONN: Please use a number as input!"); //-moved- return; //-moved- } //-moved- //-moved- recon_by_number(atoi(Network_message+7)); //-moved- return; //-moved- } //-moved- if((NMl>6)&&!strnicmp("pingn:",Network_message, 6)) //-moved- { //-moved- if(!isdigit(Network_message[6])) //-moved- { //-moved- hud_message(MSGC_GAME_FEEDBACK,"PINGN: Please use a number as input!"); //-moved- return; //-moved- } //-moved- //-moved- ping_by_number(atoi(Network_message+6)); //-moved- return; //-moved- } //-moved- //-moved- // End addition by GRiM FisH //-moved- //end this section change - VR from GF //end this section move - VR Network_message_reciever = 100; hud_message(MSGC_GAME_FEEDBACK, "%s '%s'", TXT_SENDING, Network_message); multi_send_message(); multi_message_feedback(); } void multi_define_macro_end() { Assert( multi_defining_message > 0 ); strcpy( Network_message_macro[multi_defining_message-1], Network_message ); write_player_file(); multi_message_index = 0; multi_defining_message = 0; } void multi_message_input_sub( int key ) { switch( key ) { case KEY_F8: case KEY_ESC: multi_sending_message = 0; multi_defining_message = 0; game_flush_inputs(); break; case KEY_LEFT: case KEY_BACKSP: case KEY_PAD4: if (multi_message_index > 0) multi_message_index--; Network_message[multi_message_index] = 0; break; case KEY_ENTER: if ( multi_sending_message ) multi_send_message_end(); else if ( multi_defining_message ) multi_define_macro_end(); game_flush_inputs(); break; default: if ( key > 0 ) { int ascii = key_to_ascii(key); if ((ascii < 255 )) { if (multi_message_index < MAX_MESSAGE_LEN-2 ) { Network_message[multi_message_index++] = ascii; Network_message[multi_message_index] = 0; } else if ( multi_sending_message ) { int i; char * ptext, * pcolon; ptext = NULL; Network_message[multi_message_index++] = ascii; Network_message[multi_message_index] = 0; for (i=multi_message_index-1; i>=0; i-- ) { if ( Network_message[i]==32 ) { ptext = &Network_message[i+1]; Network_message[i] = 0; break; } } multi_send_message_end(); if ( ptext ) { multi_sending_message = 1; pcolon = strchr( Network_message, ':' ); if ( pcolon ) strcpy( pcolon+1, ptext ); else strcpy( Network_message, ptext ); multi_message_index = strlen( Network_message ); } } } } } } void multi_send_message_dialog(void) { newmenu_item m[1]; int choice; if (!(Game_mode&GM_MULTI)) return; Network_message[0] = 0; // Get rid of old contents m[0].type=NM_TYPE_INPUT; m[0].text = Network_message; m[0].text_len = MAX_MESSAGE_LEN-1; choice = newmenu_do( NULL, TXT_SEND_MESSAGE, 1, m, NULL ); if ((choice > -1) && (strlen(Network_message) > 0)) { Network_message_reciever = 100; //hud_message(MSGC_GAME_FEEDBACK, "%s '%s'", TXT_SENDING, Network_message); multi_message_feedback(); } } void multi_do_death(int objnum) { // Do any miscellaneous stuff for a new network player after death objnum = objnum; if (!(Game_mode & GM_MULTI_COOP)) { mprintf((0, "Setting all keys for player %d.\n", Player_num)); Players[Player_num].flags |= (PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_GOLD_KEY); } } void multi_do_fire(char *buf) { ubyte weapon; int pnum; sbyte flags; fix save_charge = Fusion_charge; // Act out the actual shooting pnum = buf[1]; weapon = (int)buf[2]; flags = buf[4]; Network_laser_track = *(short *)(buf+6); Assert (pnum < N_players); //added on 03/05/99 Matt Mueller - add POS_FIRE capability if (buf[0]==MULTI_POS_FIRE) { mprintf((0,"got MULTI_POS_FIRE from %i\n",pnum)); extract_shorterpos(&Objects[Players[pnum].objnum],(shorterpos*)(buf+8)); } //end addition -MM if (Objects[Players[pnum].objnum].type == OBJ_GHOST) multi_make_ghost_player(pnum); if (weapon >= MISSILE_ADJUST) net_missile_firing(pnum, weapon, (int)buf[4]); else { if (weapon == FUSION_INDEX) { Fusion_charge = buf[4] << 12; mprintf((0, "Fusion charge X%f.\n", f2fl(Fusion_charge))); } if (weapon == LASER_INDEX) { if (flags & LASER_QUAD) Players[pnum].flags |= PLAYER_FLAGS_QUAD_LASERS; else Players[pnum].flags &= ~PLAYER_FLAGS_QUAD_LASERS; } do_laser_firing(Players[pnum].objnum, weapon, (int)buf[3], flags, (int)buf[5]); if (weapon == FUSION_INDEX) Fusion_charge = save_charge; } } void multi_do_message(char *buf) { char *colon; int pnum = buf[1]; #ifdef SHAREWARE int loc = 3; #else int loc = 2; #endif //added on 12/29/98 by Victor Rachels for irc style /me. if ((strlen(buf+loc) > 3) && !strnicmp("/ME",buf+loc,3)) { int col=gr_getcolor(player_rgb[pnum].r,player_rgb[pnum].g,player_rgb[pnum].b)+1;// note the +1 ! digi_play_sample(SOUND_HUD_MESSAGE, F1_0); //hud_message(MSGC_MULTI_USERMSG, "*%s%s", Players[pnum].callsign, buf+loc+3*sizeof(char)); hud_message(MSGC_MULTI_USERMSG, "\001%c*%s\004\003%c%s\006", col, Players[pnum].callsign, col, buf+loc+3*sizeof(char)); } //end this addition - VR else if (((colon = strrchr(buf+loc, ':')) == NULL) || (colon-(buf+loc) < 1) || (colon-(buf+loc) > CALLSIGN_LEN)) { int col=gr_getcolor(player_rgb[pnum].r,player_rgb[pnum].g,player_rgb[pnum].b)+1;// note the +1 ! digi_play_sample(SOUND_HUD_MESSAGE, F1_0); //hud_message(MSGC_MULTI_USERMSG, "%s %s '%s'", Players[(int)buf[1]].callsign, TXT_SAYS, buf+loc); hud_message(MSGC_MULTI_USERMSG, "\001%c%s\004 \003%c%s '%s'\006", col, Players[pnum].callsign, col, TXT_SAYS, buf+loc); } //added on 11/10/98 by Victor Rachels to add handicapping else if ( (!strncasecmp(Players[Player_num].callsign, buf+loc, colon-(buf+loc))) && !strncasecmp("handicap",colon+1,8)) { char *p=strchr(colon,' '); int t=atoi(p); digi_play_sample(SOUND_HUD_MESSAGE, F1_0); if(buf[1]!=network_whois_master()) hud_message(MSGC_GAME_FEEDBACK,"%s is trying to set your handicap",Players[(int)buf[1]].callsign); else if(t>0 && t<201) { handicap = i2f(t); if(t>100) Lhandicap = 1; else Lhandicap = 0; hud_message(MSGC_GAME_FEEDBACK, "Handicap set to %i by game master",t); Network_message_reciever = 100; snprintf(Network_message, MAX_MESSAGE_LEN, "Handicap set to %i by game master.",t); multi_send_message(); multi_message_feedback(); } else { hud_message(MSGC_GAME_FEEDBACK, "Invalid handicap by game master"); } } //end this section addition else if ( (!strncasecmp(Players[Player_num].callsign, buf+loc, colon-(buf+loc))) || ((Game_mode & GM_TEAM) && ( (get_team(Player_num) == atoi(buf+loc)-1) || !strncasecmp(Netgame.team_name[get_team(Player_num)], buf+loc, colon-(buf+loc)))) ) { int col=gr_getcolor(player_rgb[pnum].r,player_rgb[pnum].g,player_rgb[pnum].b)+1;// note the +1 ! digi_play_sample(SOUND_HUD_MESSAGE, F1_0); //hud_message(MSGC_MULTI_USERMSG, "%s %s '%s'", Players[(int)buf[1]].callsign, TXT_TELLS_YOU, (colon+1)); hud_message(MSGC_MULTI_USERMSG, "\001%c%s\004 \003%c%s '%s'\006", col, Players[pnum].callsign, col, TXT_TELLS_YOU, (colon+1)); } else if (colon - (buf + loc) == 4) { if (!strncasecmp("ping", buf+loc, 4)) { //Ping message, respond! //added/modified on 8/13/98 by Matt Mueller -- fix ping bugs char *p=strchr(colon,' '); if(!p || (atoi(p++)==Player_num)) { multibuf[0] = (char)MULTI_MESSAGE; multibuf[1] = (char)Player_num; sprintf(multibuf+2, "pong:%ul %i", atoi(colon + 1), buf[1]); multi_send_data(multibuf,message_length[MULTI_MESSAGE],1); } // Network_message_reciever = buf[1]; // Send to the player who pinged you // snprintf(Network_message, MAX_MESSAGE_LEN, "pong:%s", colon + 1); } if (!strncasecmp("pong", buf+loc, 4)) { //Pong message, print the results! char *p=strchr(colon,' '); if(!p || (atoi(p++)==Player_num)) { int pingtime; pingtime = timer_get_fixed_seconds() - atoi(colon + 1); if(ping_stats_on) ping_stats_received((int)buf[1],pingtime); else hud_message(MSGC_GAME_FEEDBACK, "%s %s %ums", "Ping response from ", Players[(int)buf[1]].callsign, fixmuldiv(pingtime, 1000, F1_0)); } } //end modified section - Matt Mueller //added on 8/6/98 by Matt Mueller, modified by adb, 08/15/98 if (!strncasecmp("Vd1x", buf+loc, 4)) { if (!*Net_D1xPlayer[pnum].ver){ strncpy(Net_D1xPlayer[pnum].ver, colon+1, D1XPLAYER_VER_LENGTH); Net_D1xPlayer[pnum].ver[D1XPLAYER_VER_LENGTH - 1] = 0; //added 03/04/99 Matt Mueller - new iver variable for easy version checking Net_D1xPlayer[pnum].iver=atoi(Net_D1xPlayer[pnum].ver+5)*1000+atoi(Net_D1xPlayer[pnum].ver+7)*10; hud_message(MSGC_MULTI_INFO, "%s is using %s(%i)", Players[pnum].callsign, colon+1,Net_D1xPlayer[pnum].iver); //end edit -MM } } if (!strncasecmp("Nd1x", buf+loc, 4)) { int dest = atoi(colon + 1),src=buf[1]; int mode=0,shp=0,pps=0; char *pos=colon; while (*(pos++)!=' ') if (*pos==0)break; mode=atoi(pos); if (dest==Player_num || dest==100) { //edit 4/19/99 Matt Mueller - move some stuff into the ver_set proc to reduce duplication while (*(pos++)!=' ') if (*pos==0)break; shp=atoi(pos); while (*(pos++)!=' ') if (*pos==0)break; pps=atoi(pos); multi_do_d1x_ver_set(src,shp,pps); //end edit -MM if (mode==1) network_send_config_messages(100,2); else if (mode==2||mode==4) network_send_config_messages(src,3); } //printf("a %s (dest=%i src=%i mode=%i shp=%i pps=%i)\n", buf+loc, dest, src, mode, shp, pps); } //end modified section - Matt Mueller (adb) //killed on 8/6/98 by Matt Mueller //added on 8/4/98 by Matt Mueller //changed on 8/5/98 by Matt Mueller // if (!strnicmp("Npps", buf+loc, 4)) // { // //set pps to correct value // int temp = atoi(colon + 1); // if(temp!=Network_pps) { // Network_pps =temp; // HUD_init_message("setting pps to %i", Network_pps); ////killed on 8/6/98 by Matt Mueller //// printf("setting pps to %i\n", Network_pps); ////end modified section - Matt Mueller // Network_packet_interval = F1_0 / Network_pps; // } // } // if (!strnicmp("Mshp", buf+loc, 4)) // { // int dest = atoi(colon + 1); // if (dest==Player_num || dest==100) { // //set short packets to correct value // Network_short_packets=1; // NetWantShort[buf[1]]=1; // Network_message_reciever = 100; // sprintf(Network_message, "Nshp:%i", Network_message_reciever); // multi_send_message(); // HUD_init_message("enabling short packets mode."); ////killed on 8/6/98 by Matt Mueller //// printf("enabling short packets mode.\n"); ////end modified section - Matt Mueller ////added/changed on 8/6/98 by Matt Mueller // }else { //// HUD_init_message("defaulting to long packets for joining player %s",Players[dest].callsign); // NetWantShort[dest]=0;//someone just joined, so be sure to default to long // } ////end modified section - Matt Mueller // } // if (!strnicmp("Nshp", buf+loc, 4)) // { // int dest = atoi(colon + 1); // if (dest==Player_num || dest==100) { // NetWantShort[buf[1]]=1; // Network_message_reciever = buf[1]; // sprintf(Network_message, "Rshp:%i", Network_message_reciever); // multi_send_message(); // HUD_init_message("enabling short packets for %s (N)", Players[(int)buf[1]].callsign); ////killed on 8/6/98 by Matt Mueller //// printf("enabling short packets for %s (N)\n", Players[(int)buf[1]].callsign); ////end modified section - Matt Mueller // } // } // if (!strnicmp("Rshp", buf+loc, 4)) // { // int dest = atoi(colon + 1); // if (dest==Player_num || dest==100) { // NetWantShort[buf[1]]=1; // HUD_init_message("enabling short packets for %s (R)", Players[(int)buf[1]].callsign); ////killed on 8/6/98 by Matt Mueller //// printf("enabling short packets for %s (R)\n", Players[(int)buf[1]].callsign); ////end modified section - Matt Mueller // } // } ////end modified section - Matt Mueller //end modified section - Matt Mueller } } void multi_do_position(char *buf) { // This routine does only player positions, modem game only // mprintf((0, "Got position packet.\n")); int pnum = (Player_num+1)%2; Assert(&Objects[Players[pnum].objnum] != ConsoleObject); Assert(!(Game_mode & GM_NETWORK)); extract_shortpos(&Objects[Players[pnum].objnum], (shortpos *)(buf+1)); if (Objects[Players[pnum].objnum].movement_type == MT_PHYSICS) set_thrust_from_velocity(&Objects[Players[pnum].objnum]); } void multi_do_reappear(char *buf) { short objnum; objnum = *(short *)(buf+1); Assert(objnum >= 0); // Assert(Players[Objects[objnum].id]].objnum == objnum); // mprintf((0, "Switching rendering back on for object %d.\n", objnum)); multi_make_ghost_player(Objects[objnum].id); create_player_appearance_effect(&Objects[objnum]); } void multi_do_powerup_count(char *buf) { int pnum = buf[1]; int count; int pow_count[MAX_POWERUP_TYPES]; int i, pow; memset(pow_count, 0, sizeof(pow_count)); count = 2; for (i = 0; i < NUM_PLAYER_DROP_POWERUPS; i++) { pow = player_drop_powerups[i]; if (pow == POW_VULCAN_AMMO) { pow_count[pow] = *((short *)&buf[count]); count += 2; } else pow_count[pow] = buf[count++]; } if ((multi_got_pow_count & (1 << pnum))) return; // already got pow_count from this player if (buf[0] == MULTI_START_POWERUP_COUNT) { memset(powerup_start_level, 0, sizeof(powerup_start_level)); multi_got_pow_count = -1; } else multi_got_pow_count |= (1 << pnum); pow_add_level_pow_count(pow_count); } void multi_do_player_explode(char *buf) { // Only call this for players, not robots. pnum is player number, not // Object number. object *objp; int count; int pnum; int i; char remote_created; pnum = buf[1]; #ifdef NDEBUG if ((pnum < 0) || (pnum >= N_players)) return; #else Assert(pnum >= 0); Assert(pnum < N_players); #endif #ifdef NETWORK // If we are in the process of sending objects to a new player, reset that process if (Network_send_objects) { mprintf((0, "Resetting object sync due to player explosion.\n")); Network_send_objnum = -1; } #endif // Stuff the Players structure to prepare for the explosion count = 2; Players[pnum].primary_weapon_flags = buf[count]; count++; Players[pnum].secondary_weapon_flags = buf[count]; count++; Players[pnum].laser_level = buf[count]; count++; Players[pnum].secondary_ammo[HOMING_INDEX] = buf[count]; count++; Players[pnum].secondary_ammo[CONCUSSION_INDEX] = buf[count];count++; Players[pnum].secondary_ammo[SMART_INDEX] = buf[count]; count++; Players[pnum].secondary_ammo[MEGA_INDEX] = buf[count]; count++; Players[pnum].secondary_ammo[PROXIMITY_INDEX] = buf[count]; count++; Players[pnum].primary_ammo[VULCAN_INDEX] = *(ushort *)(buf+count); count += 2; Players[pnum].flags = *(uint *)(buf+count); count += 4; objp = Objects+Players[pnum].objnum; // objp->phys_info.velocity = *(vms_vector *)(buf+16); // 12 bytes // objp->pos = *(vms_vector *)(buf+28); // 12 bytes remote_created = buf[count++]; // How many did the other guy create? Net_create_loc = 0; //added on 03/05/99 Matt Mueller - no more misplaced spew.. might not be the best way to do it though.. //(moving the player to where they died that is) if (buf[0]==MULTI_POS_PLAYER_EXPLODE) { mprintf((0,"got MULTI_POS_PLAYER_EXPLODE from %i\n",pnum)); extract_shorterpos(objp,(shorterpos*)(buf+message_length[MULTI_PLAYER_EXPLODE])); } //end addition -MM drop_player_eggs(objp); // Create mapping from remote to local numbering system mprintf((0, "I Created %d powerups, remote created %d.\n", Net_create_loc, remote_created)); // We now handle this situation gracefully, Int3 not required // if (Net_create_loc != remote_created) // Int3(); // Probably out of object array space, see Rob for (i = 0; i < remote_created; i++) { if ((i < Net_create_loc) && (*(short *)(buf+count) > 0) && (Net_create_objnums[i] > 0)) map_objnum_local_to_remote((short)Net_create_objnums[i], *(short *)(buf+count), pnum); else if (*(short *)(buf+count) <= 0) { mprintf((0, "WARNING: Remote created object has non-valid number %d (player %d)", *(short *)(buf+count), pnum)); } else { mprintf((0, "WARNING: Could not create all powerups created by player %d.\n", pnum)); } // Assert(*(short *)(buf+count) > 0); count += 2; } for (i = remote_created; i < Net_create_loc; i++) { mprintf((0, "WARNING: I Created more powerups than player %d, deleting.\n", pnum)); Objects[Net_create_objnums[i]].flags |= OF_SHOULD_BE_DEAD; } //edited on 03/05/99 Matt Mueller - no more misplaced spew if (buf[0] == MULTI_PLAYER_EXPLODE || buf[0] == MULTI_POS_PLAYER_EXPLODE) //end edit -MM { explode_badass_player(objp); objp->flags &= ~OF_SHOULD_BE_DEAD; //don't really kill player multi_make_player_ghost(pnum); } else { create_player_appearance_effect(objp); } Players[pnum].flags &= ~(PLAYER_FLAGS_CLOAKED | PLAYER_FLAGS_INVULNERABLE); Players[pnum].cloak_time = 0; } void multi_do_kill(char *buf) { int killer, killed; int count = 1; #ifndef SHAREWARE int pnum; pnum = buf[count]; if ((pnum < 0) || (pnum >= N_players)) { Int3(); // Invalid player number killed return; } killed = Players[pnum].objnum; count += 1; #else killed = objnum_remote_to_local(*(short *)(buf+count), (sbyte)buf[count+2]); count += 3; #endif killer = *(short *)(buf+count); if (killer > 0) killer = objnum_remote_to_local(killer, (sbyte)buf[count+2]); #ifdef SHAREWARE if ((Objects[killed].type != OBJ_PLAYER) && (Objects[killed].type != OBJ_GHOST)) { Int3(); mprintf( (1, "SOFT INT3: MULTI.C Non-player object %d of type %d killed! (JOHN)\n", killed, Objects[killed].type )); return; } #endif multi_compute_kill(killer, killed); } // Changed by MK on 10/20/94 to send NULL as object to net_destroy_controlcen if it got -1 // which means not a controlcen object, but contained in another object void multi_do_controlcen_destroy(char *buf) { sbyte who; short objnum; objnum = *(short *)(buf+1); who = buf[3]; if (Fuelcen_control_center_destroyed != 1) { if ((who < N_players) && (who != Player_num)) { hud_message(MSGC_MULTI_INFO, "%s %s", Players[who].callsign, TXT_HAS_DEST_CONTROL); } else if (who == Player_num) hud_message(MSGC_MULTI_INFO, TXT_YOU_DEST_CONTROL); else hud_message(MSGC_MULTI_INFO, TXT_CONTROL_DESTROYED); if (objnum != -1) net_destroy_controlcen(Objects+objnum); else net_destroy_controlcen(NULL); } } void multi_do_escape(unsigned char *buf) { int objnum; objnum = Players[(int)buf[1]].objnum; if (buf[2] == 0) { digi_play_sample(SOUND_HUD_MESSAGE, F1_0); hud_message(MSGC_MULTI_INFO, "%s %s", Players[(int)buf[1]].callsign, TXT_HAS_ESCAPED); #ifndef SHAREWARE if (Game_mode & GM_NETWORK) Players[buf[1]].connected = CONNECT_ESCAPE_TUNNEL; #endif if (!multi_goto_secret) multi_goto_secret = 2; } else if (buf[2] == 1) { digi_play_sample(SOUND_HUD_MESSAGE, F1_0); hud_message(MSGC_MULTI_INFO, "%s %s", Players[(int)buf[1]].callsign, TXT_HAS_FOUND_SECRET); #ifndef SHAREWARE if (Game_mode & GM_NETWORK) Players[(int)buf[1]].connected = CONNECT_FOUND_SECRET; #endif if (!multi_goto_secret) multi_goto_secret = 1; } create_player_appearance_effect(&Objects[objnum]); multi_make_player_ghost(buf[1]); } void multi_do_remobj(unsigned char *buf) { short objnum; // which object to remove short local_objnum; sbyte obj_owner; // which remote list is it entered in objnum = *(short *)(buf+1); obj_owner = buf[3]; Assert(objnum >= 0); if (objnum < 1) return; local_objnum = objnum_remote_to_local(objnum, obj_owner); // translate to local objnum // mprintf((0, "multi_do_remobj: %d owner %d = %d.\n", objnum, obj_owner, local_objnum)); if (local_objnum < 0) { mprintf((0, "multi_do_remobj: Could not remove referenced object.\n")); return; } if ((Objects[local_objnum].type != OBJ_POWERUP) && (Objects[local_objnum].type != OBJ_HOSTAGE)) { mprintf((0, "multi_get_remobj: tried to remove invalid type %d.\n", Objects[local_objnum].type)); return; } if (Network_send_objects && network_objnum_is_past(local_objnum)) { mprintf((0, "Resetting object sync due to object removal.\n")); Network_send_objnum = -1; } Objects[local_objnum].flags |= OF_SHOULD_BE_DEAD; // quick and painless } void multi_do_quit(unsigned char *buf) { if (Game_mode & GM_NETWORK) { int i, n = 0; digi_play_sample( SOUND_HUD_MESSAGE, F1_0 ); hud_message( MSGC_MULTI_INFO, "\002%c%s\004 %s", gr_getcolor(player_rgb[buf[1]].r,player_rgb[buf[1]].g,player_rgb[buf[1]].b)+1, Players[buf[1]].callsign, TXT_HAS_LEFT_THE_GAME); network_disconnect_player(buf[1]); if (multi_in_menu) return; for (i = 0; i < N_players; i++) if (Players[i].connected) n++; if (n == 1) { //added/changed on 10/11/98 by Victor Rachels cuz this is annoying as a box //-killed- nm_messagebox(NULL, 1, TXT_OK, TXT_YOU_ARE_ONLY); hud_message(MSGC_GAME_FEEDBACK, TXT_YOU_ARE_ONLY); //end this change - Victor Rachels } } if ((Game_mode & GM_SERIAL) || (Game_mode & GM_MODEM)) { Function_mode = FMODE_MENU; multi_quit_game = 1; multi_leave_menu = 1; nm_messagebox(NULL, 1, TXT_OK, TXT_OPPONENT_LEFT); Function_mode = FMODE_GAME; multi_reset_stuff(); } return; } void multi_do_cloak(char *buf) { int pnum; pnum = buf[1]; Assert(pnum < N_players); mprintf((0, "Cloaking player %d\n", pnum)); Players[pnum].flags |= PLAYER_FLAGS_CLOAKED; Players[pnum].cloak_time = GameTime; ai_do_cloak_stuff(); #ifndef SHAREWARE if (Game_mode & GM_MULTI_ROBOTS) multi_strip_robots(pnum); #endif if (Newdemo_state == ND_STATE_RECORDING) newdemo_record_multi_cloak(pnum); } void multi_do_decloak(char *buf) { int pnum; pnum = buf[1]; if (Newdemo_state == ND_STATE_RECORDING) newdemo_record_multi_decloak(pnum); } void multi_do_door_open(char *buf) { int segnum; short side; segment *seg; wall *w; #ifdef SHAREWARE segnum = *(int *)(buf+1); side = *(short *)(buf+5); #else segnum = *(short *)(buf+1); side = buf[3]; #endif // mprintf((0, "Opening door on side %d of segment # %d.\n", side, segnum)); if ((segnum < 0) || (segnum > Highest_segment_index) || (side < 0) || (side > 5)) { Int3(); return; } seg = &Segments[segnum]; if (seg->sides[side].wall_num == -1) { //Opening door on illegal wall Int3(); return; } w = &Walls[seg->sides[side].wall_num]; if (w->type == WALL_BLASTABLE) { if (!(w->flags & WALL_BLASTED)) { mprintf((0, "Blasting wall by remote command.\n")); wall_destroy(seg, side); } return; } else if (w->state != WALL_DOOR_OPENING) { wall_open_door(seg, side); } // else // mprintf((0, "Door already opening!\n")); } void multi_do_create_explosion(char *buf) { int pnum; int count = 1; pnum = buf[count++]; // mprintf((0, "Creating small fireball.\n")); create_small_fireball_on_object(&Objects[Players[pnum].objnum], F1_0, 1); } void multi_do_controlcen_fire(char *buf) { vms_vector to_target; int gun_num; short objnum; int count = 1; memcpy(&to_target, buf+count, 12); count += 12; gun_num = buf[count]; count += 1; objnum = *(short *)(buf+count); count += 2; Laser_create_new_easy(&to_target, &Gun_pos[gun_num], objnum, CONTROLCEN_WEAPON_NUM, 1); } void multi_do_create_powerup(char *buf) { short segnum; short objnum; int my_objnum; int pnum; int count = 1; vms_vector new_pos; char powerup_type; if (Endlevel_sequence || Fuelcen_control_center_destroyed) return; pnum = buf[count++]; powerup_type = buf[count++]; segnum = *(short *)(buf+count); count+=2; objnum = *(short *)(buf+count); count+=2; if ((segnum < 0) || (segnum > Highest_segment_index)) { Int3(); return; } #ifndef SHAREWARE new_pos = *(vms_vector *)(buf+count); count+=sizeof(vms_vector); #else compute_segment_center(&new_pos, &Segments[segnum]); #endif if (!may_create_powerup(powerup_type)) return; Net_create_loc = 0; my_objnum = call_object_create_egg(&Objects[Players[pnum].objnum], 1, OBJ_POWERUP, powerup_type); if (my_objnum < 0) { mprintf((0, "Could not create new powerup!\n")); return; } if (Network_send_objects && network_objnum_is_past(my_objnum)) { mprintf((0, "Resetting object sync due to powerup creation.\n")); Network_send_objnum = -1; } Objects[my_objnum].pos = new_pos; vm_vec_zero(&Objects[my_objnum].mtype.phys_info.velocity); obj_relink(my_objnum, segnum); map_objnum_local_to_remote(my_objnum, objnum, pnum); object_create_explosion(segnum, &new_pos, i2f(5), VCLIP_POWERUP_DISAPPEARANCE); mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum)); } void multi_do_play_sound(char *buf) { int pnum = buf[1]; #ifdef SHAREWARE int sound_num = *(int *)(buf+2); fix volume = *(fix *)(buf+6); #else int sound_num = buf[2]; fix volume = buf[3] << 12; #endif if (!Players[pnum].connected) return; Assert(Players[pnum].objnum >= 0); Assert(Players[pnum].objnum <= Highest_object_index); digi_link_sound_to_object( sound_num, Players[pnum].objnum, 0, volume); } #ifndef SHAREWARE void multi_do_score(char *buf) { int pnum = buf[1]; if ((pnum < 0) || (pnum >= N_players)) { Int3(); // Non-terminal, see rob return; } if (Newdemo_state == ND_STATE_RECORDING) newdemo_record_multi_score(pnum, *(int *)(buf+2)); Players[pnum].score = *(int *)(buf+2); multi_sort_kill_list(); } void multi_do_trigger(char *buf) { int pnum = buf[1]; int trigger = buf[2]; if ((pnum < 0) || (pnum >= N_players) || (pnum == Player_num)) { Int3(); // Got trigger from illegal playernum return; } if ((trigger < 0) || (trigger >= Num_triggers)) { Int3(); // Illegal trigger number in multiplayer return; } check_trigger_sub(trigger, pnum); } void multi_do_hostage_door_status(char *buf) { // Update hit point status of a door int count = 1; int wallnum; fix hps; wallnum = *(short *)(buf+count); count += 2; hps = *(fix *)(buf+count); count += 4; if ((wallnum < 0) || (wallnum > Num_walls) || (hps < 0) || (Walls[wallnum].type != WALL_BLASTABLE)) { Int3(); // Non-terminal, see Rob return; } // mprintf((0, "Damaging wall number %d to %f points.\n", wallnum, f2fl(hps))); if (hps < Walls[wallnum].hps) wall_damage(&Segments[Walls[wallnum].segnum], Walls[wallnum].sidenum, Walls[wallnum].hps - hps); } void multi_do_save_game(char *buf) { int count = 1; ubyte slot; uint id; char desc[25]; slot = *(ubyte *)(buf+count); count += 1; id = *(uint *)(buf+count); count += 4; memcpy( desc, &buf[count], 20 ); count += 20; multi_save_game( slot, id, desc ); } void multi_do_restore_game(char *buf) { int count = 1; ubyte slot; uint id; slot = *(ubyte *)(buf+count); count += 1; id = *(uint *)(buf+count); count += 4; multi_restore_game( slot, id ); } // void multi_do_req_player(char *buf) { netplayer_stats ps; ubyte player_n; // Send my netplayer_stats to everyone! player_n = *(ubyte *)(buf+1); if ( (player_n == Player_num) || (player_n == 255) ) { extract_netplayer_stats( &ps, &Players[Player_num] ); ps.Player_num = Player_num; ps.message_type = MULTI_SEND_PLAYER; // SET multi_send_data((ubyte*)&ps, sizeof(netplayer_stats), 1); } } void multi_do_send_player(char *buf) { // Got a player packet from someone!!! netplayer_stats * p; p = (netplayer_stats *)buf; /* Assert( p->Player_num >= 0 ); */ /* Player_num unsigned */ Assert( p->Player_num <= N_players ); mprintf(( 0, "Got netplayer_stats for player %d (I'm %d)\n", p->Player_num, Player_num )); mprintf(( 0, "Their shields are: %d\n", f2i(p->shields) )); // use_netplayer_stats( &Players[p->Player_num], p ); } #endif void multi_reset_stuff(void) { // A generic, emergency function to solve problems that crop up // when a player exits quick-out from the game because of a // serial connection loss. Fixes several weird bugs! dead_player_end(); Players[Player_num].homing_object_dist = -F1_0; // Turn off homing sound. Dead_player_camera = 0; Endlevel_sequence = 0; reset_rear_view(); } void multi_reset_player_object(object *objp) { int i; int id; //Init physics for a non-console player Assert(objp >= Objects); Assert(objp <= Objects+Highest_object_index); Assert((objp->type == OBJ_PLAYER) || (objp->type == OBJ_GHOST)); vm_vec_zero(&objp->mtype.phys_info.velocity); vm_vec_zero(&objp->mtype.phys_info.thrust); vm_vec_zero(&objp->mtype.phys_info.rotvel); vm_vec_zero(&objp->mtype.phys_info.rotthrust); objp->mtype.phys_info.brakes = objp->mtype.phys_info.turnroll = 0; objp->mtype.phys_info.mass = Player_ship->mass; objp->mtype.phys_info.drag = Player_ship->drag; // objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE | PF_USES_THRUST); objp->mtype.phys_info.flags &= ~(PF_TURNROLL | PF_LEVELLING | PF_WIGGLE); //Init render info objp->render_type = RT_POLYOBJ; objp->rtype.pobj_info.model_num = Player_ship->model_num; //what model is this? objp->rtype.pobj_info.subobj_flags = 0; //zero the flags for (i=0;irtype.pobj_info.anim_angles[i]); //reset textures for this, if not player 0 if (Game_mode & GM_TEAM) id = get_team(objp->id); else id = objp->id; if (id == 0) objp->rtype.pobj_info.alt_textures=0; else { Assert(N_PLAYER_SHIP_TEXTURES == Polygon_models[objp->rtype.pobj_info.model_num].n_textures); for (i=0;irtype.pobj_info.model_num].first_texture+i]]; multi_player_textures[id-1][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2]]; multi_player_textures[id-1][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(id-1)*2+1]]; objp->rtype.pobj_info.alt_textures = id; } // Clear misc objp->flags = 0; if (objp->type == OBJ_GHOST) objp->render_type = RT_NONE; } void multi_process_data(char *buf, int len) { // Take an entire message (that has already been checked for validity, // if necessary) and act on it. int type; len = len; type = buf[0]; if (type > MULTI_MAX_TYPE) { mprintf((1, "multi_process_data: invalid type %d.\n", type)); Int3(); return; } switch(type) { case MULTI_POSITION: if (!Endlevel_sequence) multi_do_position(buf); break; case MULTI_REAPPEAR: if (!Endlevel_sequence) multi_do_reappear(buf); break; case MULTI_FIRE: //added 03/05/99 Matt Mueller - new shorter fire packets case MULTI_POS_FIRE: //end addition -MM if (!Endlevel_sequence) multi_do_fire(buf); break; case MULTI_KILL: multi_do_kill(buf); break; case MULTI_REMOVE_OBJECT: if (!Endlevel_sequence) multi_do_remobj(buf); break; case MULTI_PLAYER_DROP: case MULTI_PLAYER_EXPLODE: //added 03/05/99 Matt Mueller - nomore explosions in the wrong place case MULTI_POS_PLAYER_EXPLODE: //end addition -MM if (!Endlevel_sequence) multi_do_player_explode(buf); break; case MULTI_MESSAGE: if (!Endlevel_sequence) multi_do_message(buf); break; case MULTI_QUIT: if (!Endlevel_sequence) multi_do_quit(buf); break; case MULTI_BEGIN_SYNC: break; case MULTI_CONTROLCEN: if (!Endlevel_sequence) multi_do_controlcen_destroy(buf); break; case MULTI_ENDLEVEL_START: if (!Endlevel_sequence) multi_do_escape(buf); break; case MULTI_END_SYNC: break; case MULTI_CLOAK: if (!Endlevel_sequence) multi_do_cloak(buf); break; case MULTI_DECLOAK: if (!Endlevel_sequence) multi_do_decloak(buf); break; case MULTI_DOOR_OPEN: if (!Endlevel_sequence) multi_do_door_open(buf); break; case MULTI_CREATE_EXPLOSION: if (!Endlevel_sequence) multi_do_create_explosion(buf); break; case MULTI_CONTROLCEN_FIRE: if (!Endlevel_sequence) multi_do_controlcen_fire(buf); break; case MULTI_CREATE_POWERUP: if (!Endlevel_sequence) multi_do_create_powerup(buf); break; case MULTI_PLAY_SOUND: if (!Endlevel_sequence) multi_do_play_sound(buf); break; #ifndef SHAREWARE case MULTI_ROBOT_CLAIM: if (!Endlevel_sequence) multi_do_claim_robot(buf); break; case MULTI_ROBOT_POSITION: if (!Endlevel_sequence) multi_do_robot_position(buf); break; case MULTI_ROBOT_EXPLODE: if (!Endlevel_sequence) multi_do_robot_explode(buf); break; case MULTI_ROBOT_RELEASE: if (!Endlevel_sequence) multi_do_release_robot(buf); break; case MULTI_ROBOT_FIRE: if (!Endlevel_sequence) multi_do_robot_fire(buf); break; case MULTI_SCORE: if (!Endlevel_sequence) multi_do_score(buf); break; case MULTI_CREATE_ROBOT: if (!Endlevel_sequence) multi_do_create_robot(buf); break; case MULTI_TRIGGER: if (!Endlevel_sequence) multi_do_trigger(buf); break; case MULTI_BOSS_ACTIONS: if (!Endlevel_sequence) multi_do_boss_actions(buf); break; case MULTI_CREATE_ROBOT_POWERUPS: if (!Endlevel_sequence) multi_do_create_robot_powerups(buf); break; case MULTI_HOSTAGE_DOOR: if (!Endlevel_sequence) multi_do_hostage_door_status(buf); break; case MULTI_SAVE_GAME: if (!Endlevel_sequence) multi_do_save_game(buf); break; case MULTI_RESTORE_GAME: if (!Endlevel_sequence) multi_do_restore_game(buf); break; case MULTI_REQ_PLAYER: if (!Endlevel_sequence) multi_do_req_player(buf); break; case MULTI_SEND_PLAYER: if (!Endlevel_sequence) multi_do_send_player(buf); break; case MULTI_PLAYER_POWERUP_COUNT: case MULTI_START_POWERUP_COUNT: if (!Endlevel_sequence) multi_do_powerup_count(buf); break; //====================================================== //Added 9/4/98 by Geoff Coovert - more packet ack stuff case MEKH_PACKET_NEEDACK: if (!Endlevel_sequence) mekh_process_packet(buf); break; case MEKH_PACKET_ACK: if (!Endlevel_sequence) mekh_gotack(buf); break; //====================================================== //added 03/04/99 Matt Mueller - new ping method. whee case MULTI_PING: if (!Endlevel_sequence) { mprintf((0,"got DIRECTPING from %i\n",buf[1])); multibuf[0]=MULTI_PONG; multibuf[1]=Player_num; memcpy(multibuf+2,buf+2,4); mekh_send_direct_packet(multibuf,2+4,buf[1]); }break; case MULTI_PONG: if (!Endlevel_sequence) { int pingtime; mprintf((0,"got DIRECTPONG from %i\n",buf[1])); pingtime = timer_get_fixed_seconds() - (u_int32_t)swapint(*(u_int32_t*)(buf+2)); if(ping_stats_on) ping_stats_received(buf[1],pingtime); else hud_message(MSGC_GAME_FEEDBACK, "%s %s %ums", "Ping response from ", Players[(int)buf[1]].callsign, fixmuldiv(pingtime, 1000, F1_0)); }break; //end addition -MM //added 04/19/99 Matt Mueller case MULTI_D1X_VER_PACKET: multi_do_d1x_ver(buf);break; //end addition -MM #endif case MULTI_ALT_VULCAN_ON: got_vulcan_info(1,buf[1]); break; case MULTI_ALT_VULCAN_OFF: got_vulcan_info(0,buf[1]); break; //added on 6/7/99 by Victor Rachels for ingame reconfig case MULTI_INGAME_CONFIG: reconfig_receive(buf,len); break; default: mprintf((1, "Invalid type in multi_process_input().\n")); Int3(); } } void multi_process_bigdata(char *buf, int len) { // Takes a bunch of messages, check them for validity, // and pass them to multi_process_data. int type, sub_len, bytes_processed = 0; while( bytes_processed < len ) { type = buf[bytes_processed]; if ( (type<0) || (type>MULTI_MAX_TYPE)) { // mprintf( (1, "multi_process_bigdata: Invalid packet type %d!\n", type )); printf( "multi_process_bigdata: Invalid packet type %d!\n", type ); return; } //====================================================== //Edit 9/5 by Geoff Coovert. More hacks for needack sizing. Dammit if (type == MEKH_PACKET_NEEDACK) { sub_len = message_length[(int)buf[7]] + 7; } else sub_len = message_length[type]; //====================================================== Assert(sub_len > 0); if ( (bytes_processed+sub_len) > len ) { // mprintf( (1, "multi_process_bigdata: packet type %d too short (%d>%d)!\n", type, (bytes_processed+sub_len), len )); printf( "multi_process_bigdata: packet type %d too short (%d>%d)!\n", type, (bytes_processed+sub_len), len ); Int3(); return; } multi_process_data(&buf[bytes_processed], sub_len); bytes_processed += sub_len; } } // // Part 2 : Functions that send communication messages to inform the other // players of something we did. // //added/edited on 04/16/99 by Victor Rachels - add single send capability for alt vulcan void multi_send_fire(int pl) { //edited on 03/05/99 Matt Mueller - add POS_FIRE capability if (!Network_laser_fired) return; multibuf[1] = (char)Player_num; multibuf[2] = (char)Network_laser_gun; multibuf[3] = (char)Network_laser_level; multibuf[4] = (char)Network_laser_flags; multibuf[5] = (char)Network_laser_fired; *(short *)(multibuf+6) = Network_laser_track; if(Game_mode & GM_NETWORK) { int plnum; create_shorterpos((shorterpos*)(multibuf+8),&Objects[Players[Player_num].objnum]); if(pl<100) { if (Net_D1xPlayer[pl].iver 0) && (player < N_players)) hud_message(MSGC_MULTI_INFO, "%s %s", Players[player].callsign, TXT_HAS_DEST_CONTROL); else hud_message(MSGC_MULTI_INFO, TXT_CONTROL_DESTROYED); multibuf[0] = (char)MULTI_CONTROLCEN; *(ushort *)(multibuf+1) = objnum; multibuf[3] = player; multi_send_data(multibuf, 4, 2); } void multi_send_endlevel_start(int secret) { multibuf[0] = (char)MULTI_ENDLEVEL_START; multibuf[1] = Player_num; multibuf[2] = (char)secret; if ((secret) && !multi_goto_secret) multi_goto_secret = 1; else if (!multi_goto_secret) multi_goto_secret = 2; multi_send_data(multibuf, 3, 1); if (Game_mode & GM_NETWORK) { Players[Player_num].connected = 5; network_send_endlevel_packet(); } } //added on 11/9/98 by Victor Rachels to add observer mode void multi_send_observerghost(int pl) { if(!I_am_observer) return; multibuf[0] = (char)MULTI_ENDLEVEL_START; multibuf[1] = Player_num; multibuf[2] = '9'; //added/edited on 11/24/99 by Victor Rachels to make sending right if(pl==100) mekh_send_direct_broadcast(multibuf,3); else mekh_send_direct_packet(multibuf,3,pl); //end this section addition/change - VR } //end this section addition - VR void multi_send_powerup_count(char type, int *pow_count) { int i, pow; int count = 0; multibuf[count++] = type; multibuf[count++] = Player_num; for (i = 0; i < NUM_PLAYER_DROP_POWERUPS; i++) { pow = player_drop_powerups[i]; if (pow == POW_VULCAN_AMMO) { *((short *)&multibuf[count]) = pow_count[pow]; count += 2; } else multibuf[count++] = pow_count[pow]; } multi_send_data(multibuf, count, 2); } void multi_send_player_powerup_count() { int pow_count[MAX_POWERUP_TYPES]; player_to_pow_count(&Players[Player_num], pow_count); multi_send_powerup_count(MULTI_PLAYER_POWERUP_COUNT, pow_count); } void multi_send_start_powerup_count() { multi_send_powerup_count(MULTI_START_POWERUP_COUNT, powerup_start_level); } void multi_send_player_explode(char type) { int count = 0; int i; Assert( (type == MULTI_PLAYER_DROP) || (type == MULTI_PLAYER_EXPLODE) ); multi_send_position(Players[Player_num].objnum); if (Network_send_objects) { mprintf((0, "Resetting object sync due to player explosion.\n")); Network_send_objnum = -1; } multibuf[count++] = type; multibuf[count++] = Player_num; multibuf[count++] = (char)Players[Player_num].primary_weapon_flags; multibuf[count++] = (char)Players[Player_num].secondary_weapon_flags; multibuf[count++] = (char)Players[Player_num].laser_level; multibuf[count++] = (char)Players[Player_num].secondary_ammo[HOMING_INDEX]; multibuf[count++] = (char)Players[Player_num].secondary_ammo[CONCUSSION_INDEX]; multibuf[count++] = (char)Players[Player_num].secondary_ammo[SMART_INDEX]; multibuf[count++] = (char)Players[Player_num].secondary_ammo[MEGA_INDEX]; multibuf[count++] = (char)Players[Player_num].secondary_ammo[PROXIMITY_INDEX]; *(ushort *)(multibuf+count) = (ushort)Players[Player_num].primary_ammo[VULCAN_INDEX]; count += 2; *(uint *)(multibuf+count) = (uint)Players[Player_num].flags; count += 4; multibuf[count++] = Net_create_loc; Assert(Net_create_loc <= MAX_NET_CREATE_OBJECTS); memset(multibuf+count, -1, MAX_NET_CREATE_OBJECTS*sizeof(short)); mprintf((0, "Created %d explosion objects.\n", Net_create_loc)); for (i = 0; i < Net_create_loc; i++) { *(short *)(multibuf+count) = (short)Net_create_objnums[i]; count += 2; if (Net_create_objnums[i] <= 0) { #if 0 // Now legal, happens if there are too much powerups in mine Int3(); // Illegal value in created egg object numbers #endif continue; } // We created these objs so our local number = the network number map_objnum_local_to_local((short)Net_create_objnums[i]); } Net_create_loc = 0; mprintf((1, "explode message size = %d, max = %d.\n", count, message_length[MULTI_PLAYER_EXPLODE])); if (count > message_length[MULTI_PLAYER_EXPLODE]) { Int3(); // See Rob } //edited on 03/05/99 Matt Mueller - no more misplaced spew.. if(type == MULTI_PLAYER_EXPLODE) { // int plnum; memcpy(multibuf2,multibuf,message_length[MULTI_PLAYER_EXPLODE]); multibuf2[0]=MULTI_POS_PLAYER_EXPLODE; create_shorterpos((shorterpos*)(multibuf2+message_length[MULTI_PLAYER_EXPLODE]), &Objects[Players[Player_num].objnum]); mekh_send_broadcast_needver(D1X_POS_EXPLODE_IVER, multibuf2,message_length[MULTI_POS_PLAYER_EXPLODE], multibuf,message_length[MULTI_PLAYER_EXPLODE]); // for (plnum=0;plnum -1) *(short *)(multibuf+count) = (short)objnum_local_to_remote(killer_objnum, (sbyte *)&multibuf[count+2]); else { *(short *)(multibuf+count) = -1; multibuf[count+2] = (char)-1; } count += 3; multi_compute_kill(killer_objnum, objnum); multi_send_data(multibuf, count, 1); #ifndef SHAREWARE if (Game_mode & GM_MULTI_ROBOTS) multi_strip_robots(Player_num); #endif } void multi_send_remobj(int objnum) { // Tell the other guy to remove an object from his list sbyte obj_owner; short remote_objnum; multibuf[0] = (char)MULTI_REMOVE_OBJECT; remote_objnum = objnum_local_to_remote((short)objnum, &obj_owner); *(short *)(multibuf+1) = remote_objnum; // Map to network objnums multibuf[3] = obj_owner; // mprintf((0, "multi_send_remobj: %d = %d owner %d.\n", objnum, remote_objnum, obj_owner)); multi_send_data(multibuf, 4, 1); if (Network_send_objects && network_objnum_is_past(objnum)) { mprintf((0, "Resetting object sync due to object removal.\n")); Network_send_objnum = -1; } } void multi_send_quit(int why) { // I am quitting the game, tell the other guy the bad news. Assert (why == MULTI_QUIT); multibuf[0] = (char)why; multibuf[1] = Player_num; multi_send_data(multibuf, 2, 1); } void multi_send_cloak(void) { // Broadcast a change in our pflags (made to support cloaking) multibuf[0] = MULTI_CLOAK; multibuf[1] = (char)Player_num; multi_send_data(multibuf, 2, 1); #ifndef SHAREWARE if (Game_mode & GM_MULTI_ROBOTS) multi_strip_robots(Player_num); #endif } void multi_send_decloak(void) { // Broadcast a change in our pflags (made to support cloaking) multibuf[0] = MULTI_DECLOAK; multibuf[1] = (char)Player_num; multi_send_data(multibuf, 2, 1); } void multi_send_door_open(int segnum, int side) { // When we open a door make sure everyone else opens that door //added on 11/20/99 by Victor Rachels to add observer mode if(I_am_observer) return; //end this section addition - VR multibuf[0] = MULTI_DOOR_OPEN; #ifdef SHAREWARE *(int *)(multibuf+1) = segnum; *(short *)(multibuf+5) = (short)side; multi_send_data(multibuf, 7, 1); #else *(short *)(multibuf+1) = (short)segnum; multibuf[3] = (sbyte)side; multi_send_data(multibuf, 4, 1); #endif } // // Part 3 : Functions that change or prepare the game for multiplayer use. // Not including functions needed to syncronize or start the // particular type of multiplayer game. Includes preparing the // mines, player structures, etc. void multi_send_create_explosion(int pnum) { // Send all data needed to create a remote explosion int count = 0; multibuf[count] = MULTI_CREATE_EXPLOSION; count += 1; multibuf[count] = (sbyte)pnum; count += 1; // ----------- // Total size = 2 multi_send_data(multibuf, count, 0); } void multi_send_controlcen_fire(vms_vector *to_goal, int best_gun_num, int objnum) { int count = 0; multibuf[count] = MULTI_CONTROLCEN_FIRE; count += 1; memcpy(multibuf+count, to_goal, 12); count += 12; multibuf[count] = (char)best_gun_num; count += 1; *(short *)(multibuf+count) = (short)objnum; count += 2; // ------------ // Total = 16 multi_send_data(multibuf, count, 0); } void multi_send_create_powerup(int powerup_type, int segnum, int objnum, vms_vector *pos) { // Create a powerup on a remote machine, used for remote // placement of used powerups like missiles and cloaking // powerups. int count = 0; multibuf[count] = MULTI_CREATE_POWERUP; count += 1; multibuf[count] = Player_num; count += 1; multibuf[count] = powerup_type; count += 1; *(short *)(multibuf+count) = (short)segnum; count += 2; *(short *)(multibuf+count) = (short)objnum; count += 2; #ifndef SHAREWARE *(vms_vector *)(multibuf+count) = *pos; count += sizeof(vms_vector); #endif // ----------- // Total = 19 multi_send_data(multibuf, count, 1); if (Network_send_objects && network_objnum_is_past(objnum)) { mprintf((0, "Resetting object sync due to powerup creation.\n")); Network_send_objnum = -1; } mprintf((0, "Creating powerup type %d in segment %i.\n", powerup_type, segnum)); map_objnum_local_to_local(objnum); } void multi_send_play_sound(int sound_num, fix volume) { int count = 0; //added on 11/20/99 by Victor Rachels to add observer mode if(I_am_observer) return; //end this section addition - VR multibuf[count] = MULTI_PLAY_SOUND; count += 1; multibuf[count] = Player_num; count += 1; #ifdef SHAREWARE *(int *)(multibuf+count) = sound_num; count += 4; *(fix *)(multibuf+count) = volume; count += 4; // ----------- // Total = 10 #else multibuf[count] = (char)sound_num; count += 1; multibuf[count] = (char)(volume >> 12); count += 1; // ----------- // Total = 4 #endif multi_send_data(multibuf, count, 1); } void multi_send_audio_taunt(int taunt_num) { #ifdef AUDIO_TAUNTS int audio_taunts[4] = { // Begin addition by GF SOUND_CONTROL_CENTER_WARNING_SIREN, SOUND_HOMING_WARNING, SOUND_CONTROL_CENTER_DESTROYED, SOUND_MINE_BLEW_UP // End addition by GF }; Assert(taunt_num >= 0); Assert(taunt_num < 4); digi_play_sample( audio_taunts[taunt_num], F1_0 ); multi_send_play_sound(audio_taunts[taunt_num], F1_0); #endif } #ifndef SHAREWARE void multi_send_score(void) { // Send my current score to all other players so it will remain // synced. int count = 0; if (Game_mode & GM_MULTI_COOP) { multi_sort_kill_list(); multibuf[count] = MULTI_SCORE; count += 1; multibuf[count] = Player_num; count += 1; *(int *)(multibuf+count) = Players[Player_num].score; count += 4; multi_send_data(multibuf, count, 0); } } void multi_send_save_game(ubyte slot, uint id, char * desc) { int count = 0; multibuf[count] = MULTI_SAVE_GAME; count += 1; multibuf[count] = slot; count += 1; // Save slot=0 *(uint *)(multibuf+count) = id; count += 4; // Save id memcpy( &multibuf[count], desc, 20 ); count += 20; multi_send_data(multibuf, count, 2); } void multi_send_restore_game(ubyte slot, uint id) { int count = 0; multibuf[count] = MULTI_RESTORE_GAME; count += 1; multibuf[count] = slot; count += 1; // Save slot=0 *(uint *)(multibuf+count) = id; count += 4; // Save id multi_send_data(multibuf, count, 2); } void multi_send_netplayer_stats_request(ubyte player_num) { int count = 0; multibuf[count] = MULTI_REQ_PLAYER; count += 1; multibuf[count] = player_num; count += 1; multi_send_data(multibuf, count, 2 ); } void multi_send_trigger(int triggernum) { // Send an event to trigger something in the mine int count = 0; //added on 11/20/99 by Victor Rachels to add observer mode if(I_am_observer) return; //end this section addition - VR multibuf[count] = MULTI_TRIGGER; count += 1; multibuf[count] = Player_num; count += 1; multibuf[count] = (ubyte)triggernum; count += 1; multi_send_data(multibuf, count, 2); } void multi_send_hostage_door_status(int wallnum) { // Tell the other player what the hit point status of a hostage door // should be int count = 0; Assert(Walls[wallnum].type == WALL_BLASTABLE); multibuf[count] = MULTI_HOSTAGE_DOOR; count += 1; *(short *)(multibuf+count) = wallnum; count += 2; *(fix *)(multibuf+count) = Walls[wallnum].hps; count += 4; // mprintf((0, "Door %d damaged by %f points.\n", wallnum, f2fl(Walls[wallnum].hps))); multi_send_data(multibuf, count, 0); } #endif void multi_prep_level(void) { // Do any special stuff to the level required for serial games // before we begin playing in it. // Player_num MUST be set before calling this procedure. // This function must be called before checksuming the Object array, // since the resulting checksum with depend on the value of Player_num // at the time this is called. int i; int cloak_count, inv_count; Assert(Game_mode & GM_MULTI); Assert(NumNetPlayerPositions > 0); for (i = 0; i < NumNetPlayerPositions; i++) { if (i != Player_num) Objects[Players[i].objnum].control_type = CT_REMOTE; Objects[Players[i].objnum].movement_type = MT_PHYSICS; multi_reset_player_object(&Objects[Players[i].objnum]); LastPacketTime[i] = 0; } #ifndef SHAREWARE for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++) { robot_controlled[i] = -1; robot_agitation[i] = 0; robot_fired[i] = 0; } #endif Viewer = ConsoleObject = &Objects[Players[Player_num].objnum]; if (!(Game_mode & GM_MULTI_COOP)) { multi_delete_extra_objects(); // Removes monsters from level } if (Game_mode & GM_MULTI_ROBOTS) { multi_set_robot_ai(); // Set all Robot AI to types we can cope with } inv_count = 0; cloak_count = 0; for (i=0; i<=Highest_object_index; i++) { int objnum; if ((Objects[i].type == OBJ_HOSTAGE) && !(Game_mode & GM_MULTI_COOP)) { 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); obj_delete(i); if (objnum != -1) { Objects[objnum].rtype.vclip_info.vclip_num = Powerup_info[POW_SHIELD_BOOST].vclip_num; Objects[objnum].rtype.vclip_info.frametime = Vclip[Objects[objnum].rtype.vclip_info.vclip_num].frame_time; Objects[objnum].rtype.vclip_info.framenum = 0; Objects[objnum].mtype.phys_info.drag = 512; //1024; Objects[objnum].mtype.phys_info.mass = F1_0; vm_vec_zero(&Objects[objnum].mtype.phys_info.velocity); } continue; } if (Objects[i].type == OBJ_POWERUP) { if (Objects[i].id == POW_EXTRA_LIFE) { Objects[i].id = POW_INVULNERABILITY; Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num; Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time; } if ((multi_allow_powerup & multi_allow_powerup_mask[Objects[i].id]) != multi_allow_powerup_mask[Objects[i].id]) { Objects[i].id = POW_SHIELD_BOOST; Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num; Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time; } if (Game_mode & GM_MULTI_COOP) continue; if ((Objects[i].id >= POW_KEY_BLUE) && (Objects[i].id <= POW_KEY_GOLD)) { Objects[i].id = POW_SHIELD_BOOST; Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num; Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time; } if (Objects[i].id == POW_INVULNERABILITY) { if (inv_count >= 3) { mprintf((0, "Bashing Invulnerability object #%i to shield.\n", i)); Objects[i].id = POW_SHIELD_BOOST; Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num; Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time; } else inv_count++; } if (Objects[i].id == POW_CLOAK) { if (cloak_count >= 3) { mprintf((0, "Bashing Cloak object #%i to shield.\n", i)); Objects[i].id = POW_SHIELD_BOOST; Objects[i].rtype.vclip_info.vclip_num = Powerup_info[Objects[i].id].vclip_num; Objects[i].rtype.vclip_info.frametime = Vclip[Objects[i].rtype.vclip_info.vclip_num].frame_time; } else cloak_count++; } } } multi_sort_kill_list(); multi_show_player_list(); ConsoleObject->control_type = CT_FLYING; reset_player_object(); multi_got_pow_count = 0; // send player powerups (assumes sync already send) if ((Game_mode & GM_NETWORK) && Netgame.protocol_version == MULTI_PROTO_D1X_VER && !Network_rejoined) multi_send_player_powerup_count(); } void multi_set_robot_ai(void) { // Go through the objects array looking for robots and setting // them to certain supported types of NET AI behavior. // int i; // // for (i = 0; i <= Highest_object_index; i++) // { // if (Objects[i].type == OBJ_ROBOT) { // Objects[i].ai_info.REMOTE_OWNER = -1; // if (Objects[i].ai_info.behavior == AIB_STATION) // Objects[i].ai_info.behavior = AIB_NORMAL; // } // } } int multi_delete_extra_objects() { int i; int nnp=0; object *objp; // Go through the object list and remove any objects not used in // 'Anarchy!' games. // This function also prints the total number of available multiplayer // positions in this level, even though this should always be 8 or more! objp = Objects; for (i=0;i<=Highest_object_index;i++) { if ((objp->type==OBJ_PLAYER) || (objp->type==OBJ_GHOST)) nnp++; else if ((objp->type==OBJ_ROBOT) && (Game_mode & GM_MULTI_ROBOTS)) ; else if ( (objp->type!=OBJ_NONE) && (objp->type!=OBJ_PLAYER) && (objp->type!=OBJ_POWERUP) && (objp->type!=OBJ_CNTRLCEN) && (objp->type!=OBJ_HOSTAGE) ) obj_delete(i); objp++; } return nnp; } int network_i_am_master(void) { // I am the lowest numbered player in this game? int i; if (!(Game_mode & GM_NETWORK)) return (Player_num == 0); for (i = 0; i < Player_num; i++) if (Players[i].connected) return 0; return 1; } void change_playernum_to( int new_Player_num ) { if (Player_num > -1) memcpy( Players[new_Player_num].callsign, Players[Player_num].callsign, CALLSIGN_LEN+1 ); Player_num = new_Player_num; } #ifndef SHAREWARE void multi_initiate_save_game() { uint game_id; int i, slot; char filename[128]; char desc[24]; if ((Endlevel_sequence) || (Fuelcen_control_center_destroyed)) return; // multi_send_netplayer_stats_request(255); // return; stop_time(); slot = state_get_save_file(filename, desc, 1 ); if (!slot) { start_time(); return; } slot--; start_time(); // Make a unique game id game_id = timer_get_fixed_seconds(); game_id ^= N_players<<4; for (i=0; iflags = pd->flags; // Powerup flags, see below... ps->energy = pd->energy; // Amount of energy remaining. ps->shields = pd->shields; // shields remaining (protection) ps->lives = pd->lives; // Lives remaining, 0 = game over. ps->laser_level = pd->laser_level; // Current level of the laser. ps->primary_weapon_flags=pd->primary_weapon_flags; // bit set indicates the player has this weapon. ps->secondary_weapon_flags=pd->secondary_weapon_flags; // bit set indicates the player has this weapon. memcpy( ps->primary_ammo, pd->primary_ammo, MAX_PRIMARY_WEAPONS*sizeof(short) ); // How much ammo of each type. memcpy( ps->secondary_ammo, pd->secondary_ammo, MAX_SECONDARY_WEAPONS*sizeof(short) ); // How much ammo of each type. ps->last_score=pd->last_score; // Score at beginning of current level. ps->score=pd->score; // Current score. ps->cloak_time=pd->cloak_time; // Time cloaked ps->homing_object_dist=pd->homing_object_dist; // Distance of nearest homing object. ps->invulnerable_time=pd->invulnerable_time; // Time invulnerable ps->net_killed_total=pd->net_killed_total; // Number of times killed total ps->net_kills_total=pd->net_kills_total; // Number of net kills total ps->num_kills_level=pd->num_kills_level; // Number of kills this level ps->num_kills_total=pd->num_kills_total; // Number of kills total ps->num_robots_level=pd->num_robots_level; // Number of initial robots this level ps->num_robots_total=pd->num_robots_total; // Number of robots total ps->hostages_rescued_total=pd->hostages_rescued_total; // Total number of hostages rescued. ps->hostages_total=pd->hostages_total; // Total number of hostages. ps->hostages_on_board=pd->hostages_on_board; // Number of hostages on ship. } void use_netplayer_stats( player * ps, netplayer_stats *pd ) { ps->flags = pd->flags; // Powerup flags, see below... ps->energy = pd->energy; // Amount of energy remaining. ps->shields = pd->shields; // shields remaining (protection) ps->lives = pd->lives; // Lives remaining, 0 = game over. ps->laser_level = pd->laser_level; // Current level of the laser. ps->primary_weapon_flags=pd->primary_weapon_flags; // bit set indicates the player has this weapon. ps->secondary_weapon_flags=pd->secondary_weapon_flags; // bit set indicates the player has this weapon. memcpy( ps->primary_ammo, pd->primary_ammo, MAX_PRIMARY_WEAPONS*sizeof(short) ); // How much ammo of each type. memcpy( ps->secondary_ammo, pd->secondary_ammo, MAX_SECONDARY_WEAPONS*sizeof(short) ); // How much ammo of each type. ps->last_score=pd->last_score; // Score at beginning of current level. ps->score=pd->score; // Current score. ps->cloak_time=pd->cloak_time; // Time cloaked ps->homing_object_dist=pd->homing_object_dist; // Distance of nearest homing object. ps->invulnerable_time=pd->invulnerable_time; // Time invulnerable ps->net_killed_total=pd->net_killed_total; // Number of times killed total ps->net_kills_total=pd->net_kills_total; // Number of net kills total ps->num_kills_level=pd->num_kills_level; // Number of kills this level ps->num_kills_total=pd->num_kills_total; // Number of kills total ps->num_robots_level=pd->num_robots_level; // Number of initial robots this level ps->num_robots_total=pd->num_robots_total; // Number of robots total ps->hostages_rescued_total=pd->hostages_rescued_total; // Total number of hostages rescued. ps->hostages_total=pd->hostages_total; // Total number of hostages. ps->hostages_on_board=pd->hostages_on_board; // Number of hostages on ship. } #endif