/* 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-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. */ /* * * Multiplayer code for network play. * */ #include #include #include #include #include #include "u_mem.h" #include "strutil.h" #include "game.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 "console.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 "kconfig.h" #include "newdemo.h" #include "text.h" #include "kmatrix.h" #include "multibot.h" #include "gameseq.h" #include "physics.h" #include "config.h" #include "ai.h" #include "switch.h" #include "textures.h" #include "byteswap.h" #include "sounds.h" #include "args.h" #include "effects.h" #include "iff.h" #include "state.h" #ifdef USE_IPX #include "net_ipx.h" #endif #ifdef USE_UDP #include "net_udp.h" #endif void multi_reset_player_object(object *objp); void multi_reset_object_texture(object *objp); void multi_add_lifetime_killed(); void multi_add_lifetime_kills(); void multi_send_play_by_play(int num,int spnum,int dpnum); void multi_send_heartbeat(); void multi_powcap_cap_objects(); void multi_powcap_adjust_remote_cap(int pnum); void multi_set_robot_ai(void); void multi_send_powcap_update(); void bash_to_shield(int i,char *s); void init_hoard_data(); void multi_apply_goal_textures(); int find_goal_texture(ubyte t); void multi_do_capture_bonus(char *buf); void multi_do_orb_bonus(char *buf); void multi_send_drop_flag(int objnum,int seed); void multi_send_ranking(); void multi_do_play_by_play(char *buf); void multi_new_bounty_target( int pnum ); void multi_do_bounty( char *buf ); void multi_save_game(ubyte slot, uint id, char *desc); void multi_restore_game(ubyte slot, uint id); void multi_do_save_game(char *buf); void multi_do_restore_game(char *buf); void multi_do_msgsend_state(char *buf); void multi_send_msgsend_state(int state); void multi_send_gmode_update(); void multi_do_gmode_update(char *buf); // // Local macros and prototypes // // LOCALIZE ME!! #define vm_angvec_zero(v) (v)->p=(v)->b=(v)->h=0 void drop_player_eggs(object *player); // from collide.c // // Global variables // int multi_protocol=0; // set and determinate used protocol extern vms_vector MarkerPoint[]; extern char MarkerMessage[16][40]; extern char MarkerOwner[16][40]; extern int MarkerObject[]; 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; char Multi_is_guided=0; char PKilledFlags[MAX_NUM_NET_PLAYERS]; int Bounty_target = 0; int multi_sending_message[MAX_NUM_NET_PLAYERS] = { 0,0,0,0,0,0,0,0 }; int multi_defining_message = 0; int multi_message_index = 0; char multibuf[MAX_MULTI_MESSAGE_LEN+4]; // This is where multiplayer message are built 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_status = 0; 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]; 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_quit_game = 0; int PacketUrgent = 0; char *GMNames[9]={"Anarchy","Team Anarchy","Robo Anarchy","Cooperative","Capture the Flag","Hoard","Team Hoard","Bounty","Unknown"}; char *GMNamesShrt[9]={"ANRCHY","TEAM","ROBO","COOP","FLAG","HOARD","TMHOARD","BOUNTY","UNKNOWN"}; // For rejoin object syncing (used here and all protocols - globally) int Network_send_objects = 0; // Are we in the process of sending objects to a player? int Network_send_object_mode = 0; // What type of objects are we sending, static or dynamic? int Network_send_objnum = -1; // What object are we sending next? int Network_rejoined = 0; // Did WE rejoin this game? int Network_new_game = 0; // Is this the first level of a new game? int Network_sending_extras=0; int VerifyPlayerJoined=-1; // Player (num) to enter game before any ingame/extra stuff is being sent int Player_joining_extras=-1; // This is so we know who to send 'latecomer' packets to. int Network_player_added = 0; // Is this a new player or a returning player? ushort my_segments_checksum = 0; netgame_info Netgame; bitmap_index multi_player_textures[MAX_NUM_NET_PLAYERS][N_PLAYER_SHIP_TEXTURES]; // Globals for protocol-bound Refuse-functions char RefuseThisPlayer=0,WaitForRefuseAnswer=0,RefuseTeam,RefusePlayerName[12]; fix64 RefuseTimeLimit=0; int message_length[MULTI_MAX_TYPE+1] = { 25, // POSITION 3, // REAPPEAR 8, // FIRE 5, // KILL 4, // REMOVE_OBJECT 97+9, // PLAYER_EXPLODE 37, // MESSAGE (MAX_MESSAGE_LENGTH = 40) 2, // QUIT 4, // PLAY_SOUND 41, // BEGIN_SYNC 4, // CONTROLCEN 5, // CLAIM ROBOT 4, // END_SYNC 2, // CLOAK 3, // ENDLEVEL_START 5, // DOOR_OPEN 2, // CREATE_EXPLOSION 16, // CONTROLCEN_FIRE 97+9, // PLAYER_DROP 19, // CREATE_POWERUP 9, // MISSILE_TRACK 2, // DE-CLOAK 2, // MENU_CHOICE 28, // ROBOT_POSITION (shortpos_length (23) + 5 = 28) 9, // 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]) // obsolete 2+4, // RESTORE_GAME (ubyte slot, uint id) // obsolete -1, // MULTI_REQ_PLAYER - NEVER USED -1, // MULTI_SEND_PLAYER - NEVER USED 55, // MULTI_MARKER 12, // MULTI_DROP_WEAPON 3+sizeof(shortpos), // MULTI_GUIDED, IF SHORTPOS CHANGES, CHANGE MAC VALUE BELOW 11, // MULTI_STOLEN_ITEMS 6, // MULTI_WALL_STATUS 5, // MULTI_HEARTBEAT 9, // MULTI_KILLGOALS 9, // MULTI_SEISMIC 18, // MULTI_LIGHT 2, // MULTI_START_TRIGGER 6, // MULTI_FLAGS 2, // MULTI_DROP_BLOB MAX_POWERUP_TYPES+1, // MULTI_POWCAP_UPDATE sizeof(active_door)+3, // MULTI_ACTIVE_DOOR 4, // MULTI_SOUND_FUNCTION 2, // MULTI_CAPTURE_BONUS 2, // MULTI_GOT_FLAG 12, // MULTI_DROP_FLAG 1, // MULTI_ROBOT_CONTROLS - UNUSED 2, // MULTI_FINISH_GAME 3, // MULTI_RANK 1, // MULTI_MODEM_PING 1, // MULTI_MODEM_PING_RETURN 3, // MULTI_ORB_BONUS 2, // MULTI_GOT_ORB 12, // MULTI_DROP_ORB 4, // MULTI_PLAY_BY_PLAY 2, // MULTI_DO_BOUNTY 3, // MULTI_TYPING_STATE 3, // MULTI_GMODE_UPDATE 7, // MULTI_KILL_HOST 5, // MULTI_KILL_CLIENT }; char PowerupsInMine[MAX_POWERUP_TYPES],MaxPowerupsAllowed[MAX_POWERUP_TYPES]; extern fix ThisLevelTime; char *RankStrings[]={"(unpatched) ","Cadet ","Ensign ","Lieutenant ","Lt.Commander ", "Commander ","Captain ","Vice Admiral ","Admiral ","Demigod "}; char *multi_allow_powerup_text[MULTI_ALLOW_POWERUP_MAX] = { "Laser upgrade", "Super lasers", "Quad Lasers", "Vulcan cannon", "Gauss cannon", "Spreadfire cannon", "Helix cannon", "Plasma cannon", "Phoenix cannon", "Fusion cannon", "Omega cannon", "Flash Missiles", "Homing Missiles", "Guided Missiles", "Proximity Bombs", "Smart Mines", "Smart Missiles", "Mercury Missiles", "Mega Missiles", "EarthShaker Missiles", "Cloaking", "Invulnerability", "Afterburners", "Ammo rack", "Energy Converter", "Headlight" }; int GetMyNetRanking() { int rank, eff; if (PlayerCfg.NetlifeKills+PlayerCfg.NetlifeKilled==0) return (1); rank=(int) (((float)PlayerCfg.NetlifeKills/3000.0)*8.0); eff=(int)((float)((float)PlayerCfg.NetlifeKills/((float)PlayerCfg.NetlifeKilled+(float)PlayerCfg.NetlifeKills))*100.0); if (rank>8) rank=8; if (eff<0) eff=0; if (eff<60) rank-=((59-eff)/10); if (rank<0) rank=0; if (rank>8) rank=8; return (rank+1); } void ClipRank (ubyte *rank) { // This function insures no crashes when dealing with D2 1.0 if (*rank > 9) *rank = 0; } // // 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) { return(-1); } 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]; 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(local_objnum < MAX_OBJECTS); Assert(remote_objnum > -1); Assert(remote_objnum < MAX_OBJECTS); Assert(owner > -1); Assert(owner != Player_num); 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; } void reset_network_objects() { memset(local_to_remote, -1, MAX_OBJECTS*sizeof(short)); memset(remote_to_local, -1, MAX_NUM_NET_PLAYERS*MAX_OBJECTS*sizeof(short)); memset(object_owner, -1, MAX_OBJECTS); } int multi_objnum_is_past(int objnum) { switch (multi_protocol) { #ifdef USE_IPX case MULTI_PROTO_IPX: return net_ipx_objnum_is_past(objnum); break; #endif case MULTI_PROTO_UDP: #ifdef USE_UDP return net_udp_objnum_is_past(objnum); break; #endif default: Error("Protocol handling missing in multi_objnum_is_past\n"); break; } } // // Part 1 : functions whose main purpose in life is to divert the flow // of execution to either network specific code based // on the curretn Game_mode value. // void multi_endlevel_score(void) { int i, old_connect=0; // 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; if (Players[Player_num].connected!=CONNECT_DIED_IN_MINE) Players[Player_num].connected = CONNECT_END_MENU; Network_status = NETSTAT_ENDLEVEL; } #endif // Do the actual screen we wish to show if (multi_protocol == MULTI_PROTO_IPX) kmatrix_ipx_view(Game_mode & GM_NETWORK); else kmatrix_view(Game_mode & GM_NETWORK); // Restore connect state if (Game_mode & GM_NETWORK) { Players[Player_num].connected = old_connect; } if (Game_mode & GM_MULTI_COOP) { for (i = 0; i < MaxNumNetPlayers; i++) // Reset keys Players[i].flags &= ~(PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_GOLD_KEY); } for (i = 0; i < MaxNumNetPlayers; i++) Players[i].flags &= ~(PLAYER_FLAGS_FLAG); // Clear capture flag for (i=0;i= MAX_NUM_NET_PLAYERS) || (playernum < 0)) { Int3(); // Non-terminal, see Rob return; } obj = &Objects[Players[playernum].objnum]; obj->type = OBJ_GHOST; obj->render_type = RT_NONE; obj->movement_type = MT_NONE; multi_reset_player_object(obj); if (Game_mode & GM_MULTI_ROBOTS) multi_strip_robots(playernum); } void multi_make_ghost_player(int playernum) { object *obj; if ((playernum == Player_num) || (playernum >= MAX_NUM_NET_PLAYERS)) { Int3(); // Non-terminal, see rob return; } 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++) { if (Game_mode & GM_MULTI_COOP) kills[i] = Players[i].score; else if (Show_kill_list==2) { if (Players[i].net_killed_total+Players[i].net_kills_total==0) kills[i]=-1; // always draw the ones without any ratio last else kills[i]=(int)((float)((float)Players[i].net_kills_total/((float)Players[i].net_killed_total+(float)Players[i].net_kills_total))*100.0); } else 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; } } } } extern object *obj_find_first_of_type (int); char Multi_killed_yourself=0; 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,killer_id; int TheGoal; char killed_name[(CALLSIGN_LEN*2)+4]; char killer_name[(CALLSIGN_LEN*2)+4]; Multi_killed_yourself=0; // Both object numbers are localized already! 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; killer_id = Objects[killer].id; if ((killed_type != OBJ_PLAYER) && (killed_type != OBJ_GHOST)) { Int3(); // compute_kill passed non-player object! return; } killed_pnum = Objects[killed].id; PKilledFlags[killed_pnum]=1; 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); if (Newdemo_state == ND_STATE_RECORDING) newdemo_record_multi_death(killed_pnum); digi_play_sample( SOUND_HUD_KILL, F3_0 ); if (Control_center_destroyed) Players[killed_pnum].connected=CONNECT_DIED_IN_MINE; if (killer_type == OBJ_CNTRLCEN) { Players[killed_pnum].net_killed_total++; Players[killed_pnum].net_kills_total--; if (Newdemo_state == ND_STATE_RECORDING) newdemo_record_multi_kill(killed_pnum, -1); if (killed_pnum == Player_num) { HUD_init_message(HM_MULTI, "%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_NONPLAY); multi_add_lifetime_killed (); } else HUD_init_message(HM_MULTI, "%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_NONPLAY ); return; } else if ((killer_type != OBJ_PLAYER) && (killer_type != OBJ_GHOST)) { if (killer_id==PMINE_ID && killer_type!=OBJ_ROBOT) { if (killed_pnum == Player_num) HUD_init_message(HM_MULTI, "You were killed by a mine!"); else HUD_init_message(HM_MULTI, "%s was killed by a mine!",killed_name); } else { if (killed_pnum == Player_num) { HUD_init_message(HM_MULTI, "%s %s.", TXT_YOU_WERE, TXT_KILLED_BY_ROBOT); multi_add_lifetime_killed(); } else HUD_init_message(HM_MULTI, "%s %s %s.", killed_name, TXT_WAS, TXT_KILLED_BY_ROBOT ); } Players[killed_pnum].net_killed_total++; return; } 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_HOARD)) { 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; if (Newdemo_state == ND_STATE_RECORDING) newdemo_record_multi_kill(killed_pnum, -1); } kill_matrix[killed_pnum][killed_pnum] += 1; // # of suicides if (killer_pnum == Player_num) { HUD_init_message(HM_MULTI, "%s %s %s!", TXT_YOU, TXT_KILLED, TXT_YOURSELF ); Multi_killed_yourself=1; multi_add_lifetime_killed(); } else HUD_init_message(HM_MULTI, "%s %s", killed_name, TXT_SUICIDE); /* Bounty mode needs some lovin' */ if( Game_mode & GM_BOUNTY && killed_pnum == Bounty_target && multi_i_am_master() ) { /* Select a random number */ int new = d_rand() % MAX_NUM_NET_PLAYERS; /* Make sure they're valid: Don't check against kill flags, * just in case everyone's dead! */ while( !Players[new].connected ) new = d_rand() % MAX_NUM_NET_PLAYERS; /* Select new target - it will be sent later when we're done with this function */ multi_new_bounty_target( new ); } } else { if (!(Game_mode & GM_HOARD)) { 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; } if( Game_mode & GM_BOUNTY ) { /* Did the target die? Did the target get a kill? */ if( killed_pnum == Bounty_target || killer_pnum == Bounty_target ) { /* Increment kill counts */ Players[killer_pnum].net_kills_total++; Players[killer_pnum].KillGoalCount++; /* Record the kill in a demo */ if( Newdemo_state == ND_STATE_RECORDING ) newdemo_record_multi_kill( killer_pnum, 1 ); /* If the target died, the new one is set! */ if( killed_pnum == Bounty_target ) multi_new_bounty_target( killer_pnum ); } } else { Players[killer_pnum].net_kills_total += 1; Players[killer_pnum].KillGoalCount+=1; } if (Newdemo_state == ND_STATE_RECORDING && !( Game_mode & GM_BOUNTY ) ) newdemo_record_multi_kill(killer_pnum, 1); } else { if (Game_mode & GM_TEAM) { if (killed_pnum==Player_num && get_team(killed_pnum) == get_team(killer_pnum)) Multi_killed_yourself=1; } } kill_matrix[killer_pnum][killed_pnum] += 1; Players[killed_pnum].net_killed_total += 1; if (killer_pnum == Player_num) { HUD_init_message(HM_MULTI, "%s %s %s!", TXT_YOU, TXT_KILLED, killed_name); multi_add_lifetime_kills(); if ((Game_mode & GM_MULTI_COOP) && (Players[Player_num].score >= 1000)) add_points_to_score(-1000); } else if (killed_pnum == Player_num) { HUD_init_message(HM_MULTI, "%s %s %s!", killer_name, TXT_KILLED, TXT_YOU); multi_add_lifetime_killed(); if (Game_mode & GM_HOARD) { if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]>3) multi_send_play_by_play (1,killer_pnum,Player_num); else if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]>0) multi_send_play_by_play (0,killer_pnum,Player_num); } } else HUD_init_message(HM_MULTI, "%s %s %s!", killer_name, TXT_KILLED, killed_name); } TheGoal=Netgame.KillGoal*5; if (Netgame.KillGoal>0) { if (Players[killer_pnum].KillGoalCount>=TheGoal) { if (killer_pnum==Player_num) { HUD_init_message(HM_MULTI, "You reached the kill goal!"); Players[Player_num].shields=i2f(200); } else HUD_init_message(HM_MULTI, "%s has reached the kill goal!",Players[killer_pnum].callsign); HUD_init_message(HM_MULTI, "The control center has been destroyed!"); net_destroy_controlcen (obj_find_first_of_type (OBJ_CNTRLCEN)); } } multi_sort_kill_list(); multi_show_player_list(); Players[killed_pnum].flags&=(~(PLAYER_FLAGS_HEADLIGHT_ON)); // clear the killed guys flags/headlights } void multi_do_protocol_frame(int force, int listen) { switch (multi_protocol) { #ifdef USE_IPX case MULTI_PROTO_IPX: net_ipx_do_frame(force, listen); break; #endif #ifdef USE_UDP case MULTI_PROTO_UDP: net_udp_do_frame(force, listen); break; #endif default: Error("Protocol handling missing in multi_do_protocol_frame\n"); break; } } void multi_do_frame(void) { static int lasttime=0; static fix64 last_update_time = 0; int i; if (!(Game_mode & GM_MULTI) || Newdemo_state == ND_STATE_PLAYBACK) { Int3(); return; } if ((Game_mode & GM_NETWORK) && Netgame.PlayTimeAllowed && lasttime!=f2i (ThisLevelTime)) { for (i=0;i= last_update_time + (F1_0*2)) { multi_send_gmode_update(); last_update_time = timer_query(); } multi_send_message(); // Send any waiting messages if (Game_mode & GM_MULTI_ROBOTS) { multi_check_robot_timeout(); } multi_do_protocol_frame(0, 1); if (multi_quit_game) { multi_quit_game = 0; if (Game_wind) window_close(Game_wind); } } void multi_send_data(char *buf, int len, int priority) { Assert(len == message_length[(int)buf[0]]); Assert(buf[0] <= MULTI_MAX_TYPE); if (Game_mode & GM_NETWORK) { switch (multi_protocol) { #ifdef USE_IPX case MULTI_PROTO_IPX: net_ipx_send_data((unsigned char *)buf, len, priority); break; #endif #ifdef USE_UDP case MULTI_PROTO_UDP: net_udp_send_data((unsigned char *)buf, len, priority); break; #endif default: Error("Protocol handling missing in multi_send_data\n"); break; } } } void multi_send_data_direct(unsigned char *buf, int len, int pnum, int priority) { Assert(len == message_length[(int)buf[0]]); Assert(buf[0] <= MULTI_MAX_TYPE); Assert(pnum >= 0 && pnum < MAX_NUM_NET_PLAYERS); switch (multi_protocol) { #ifdef USE_IPX case MULTI_PROTO_IPX: net_ipx_send_naked_packet(multibuf, len, pnum); break; #endif #ifdef USE_UDP case MULTI_PROTO_UDP: net_udp_send_mdata_direct((ubyte *)multibuf, len, pnum, priority); break; #endif default: Error("Protocol handling missing in multi_send_data_direct\n"); break; } } void multi_leave_game(void) { if (!(Game_mode & GM_MULTI)) return; if (Game_mode & GM_NETWORK) { Net_create_loc = 0; multi_send_position(Players[Player_num].objnum); multi_powcap_cap_objects(); if (!Player_eggs_dropped) { drop_player_eggs(ConsoleObject); Player_eggs_dropped = 1; } multi_send_player_explode(MULTI_PLAYER_DROP); } multi_send_quit(MULTI_QUIT); if (Game_mode & GM_NETWORK) { switch (multi_protocol) { #ifdef USE_IPX case MULTI_PROTO_IPX: net_ipx_leave_game(); break; #endif #ifdef USE_UDP case MULTI_PROTO_UDP: net_udp_leave_game(); break; #endif default: Error("Protocol handling missing in multi_leave_game\n"); break; } } } 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; switch (multi_protocol) { #ifdef USE_IPX case MULTI_PROTO_IPX: result = net_ipx_endlevel(secret); break; #endif #ifdef USE_UDP case MULTI_PROTO_UDP: result = net_udp_endlevel(secret); break; #endif default: Error("Protocol handling missing in multi_endlevel\n"); break; } return(result); } int multi_endlevel_poll1( newmenu *menu, d_event *event, void *userdata ) { switch (multi_protocol) { #ifdef USE_IPX case MULTI_PROTO_IPX: return net_ipx_kmatrix_poll1( menu, event, userdata ); break; #endif #ifdef USE_UDP case MULTI_PROTO_UDP: return net_udp_kmatrix_poll1( menu, event, userdata ); break; #endif default: Error("Protocol handling missing in multi_endlevel_poll1\n"); break; } return 0; // kill warning } int multi_endlevel_poll2( newmenu *menu, d_event *event, void *userdata ) { switch (multi_protocol) { case MULTI_PROTO_IPX: #ifdef USE_IPX net_ipx_kmatrix_poll2( menu, event, userdata ); break; #endif #ifdef USE_UDP case MULTI_PROTO_UDP: return net_udp_kmatrix_poll2( menu, event, userdata ); break; #endif default: Error("Protocol handling missing in multi_endlevel_poll2\n"); break; } return 0; } void multi_send_endlevel_packet() { switch (multi_protocol) { #ifdef USE_IPX case MULTI_PROTO_IPX: net_ipx_send_endlevel_packet(); break; #endif #ifdef USE_UDP case MULTI_PROTO_UDP: net_udp_send_endlevel_packet(); break; #endif default: Error("Protocol handling missing in multi_send_endlevel_packet\n"); break; } } // // Part 2 : functions that act on network messages and change the // the state of the game in some way. // 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) { key_toggle_repeat(1); 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 = strstr(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 (!strnicmp(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 ((!strnicmp(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_init_message(HM_MULTI, feedback_result); //sprintf (temp,"%s",colon); //sprintf (Network_message,"%s",temp); } } 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 (!PlayerCfg.NetworkMessageMacro[key][0]) { HUD_init_message(HM_MULTI, TXT_NO_MACRO); return; } strcpy(Network_message, PlayerCfg.NetworkMessageMacro[key]); Network_message_reciever = 100; HUD_init_message(HM_MULTI, "%s '%s'", TXT_SENDING, Network_message); multi_message_feedback(); } void multi_send_message_start() { if (Game_mode&GM_MULTI) { multi_sending_message[Player_num] = 1; multi_send_msgsend_state(1); multi_message_index = 0; Network_message[multi_message_index] = 0; key_toggle_repeat(1); } } extern fix StartingShields; extern int multi_who_is_master(); extern char NameReturning; extern int force_cockpit_redraw; void multi_send_message_end() { char *mytempbuf; int i,t; Network_message_reciever = 100; #ifdef USE_IPX if (!strnicmp (Network_message,"/Names",6) && multi_protocol == MULTI_PROTO_IPX) { NameReturning=1-NameReturning; HUD_init_message(HM_MULTI, "Name returning is now %s.",NameReturning?"active":"disabled"); } else #endif if (!strnicmp (Network_message,"/Handicap: ",11)) { mytempbuf=&Network_message[11]; StartingShields=atol (mytempbuf); if (StartingShields<10) StartingShields=10; if (StartingShields>100) { sprintf (Network_message,"%s has tried to cheat!",Players[Player_num].callsign); StartingShields=100; } else sprintf (Network_message,"%s handicap is now %d",Players[Player_num].callsign,StartingShields); HUD_init_message(HM_MULTI, "Telling others of your handicap of %d!",StartingShields); StartingShields=i2f(StartingShields); } else if (!strnicmp (Network_message,"/move: ",7)) { if ((Game_mode & GM_NETWORK) && (Game_mode & GM_TEAM)) { int name_index=7; if (strlen(Network_message) > 7) while (Network_message[name_index] == ' ') name_index++; if (!multi_i_am_master()) { HUD_init_message(HM_MULTI, "Only %s can move players!",Players[multi_who_is_master()].callsign); return; } if (strlen(Network_message)<=name_index) { HUD_init_message(HM_MULTI, "You must specify a name to move"); return; } for (i = 0; i < N_players; i++) if ((!strnicmp(Players[i].callsign, &Network_message[name_index], strlen(Network_message)-name_index)) && (Players[i].connected)) { if ((Game_mode & GM_CAPTURE) && (Players[i].flags & PLAYER_FLAGS_FLAG)) { HUD_init_message(HM_MULTI, "Can't move player because s/he has a flag!"); return; } if (Netgame.team_vector & (1<