// maybe distribute the event/goal score between the players on that team void multi_player_maybe_add_score (int score, int team) { int players_in_team = 0; int idx; // if i'm not the server of a multiplayer game, bail here if(!MULTIPLAYER_MASTER){ return; } if (!(The_mission.ai_profile->flags & AIPF_ALLOW_MULTI_EVENT_SCORING)) { return; } // first count the number of players in this team for (idx=0; idx<MAX_PLAYERS; idx++) { if (MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].p_info.team == team)) { players_in_team++; } } if (!players_in_team) { return; } for (idx=0; idx<MAX_PLAYERS; idx++) { if (MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].p_info.team == team)) { Net_players[idx].m_player->stats.m_score += (int)(score * scoring_get_scale_factor() / players_in_team ); } } }
// (client) call when receiving a packet indicating we should pause void multi_pause_pause() { int idx; // if we're already paused, don't do anything if(Multi_pause_status){ return; } // sanity check Assert(!Multi_pause_status); // mark the game as being paused Multi_pause_status = 1; // if we're not already in the pause state if(gameseq_get_state() != GS_STATE_MULTI_PAUSED){ // jump into the paused state gameseq_post_event(GS_EVENT_MULTI_PAUSE); // mark the netgame state Netgame.game_state = NETGAME_STATE_PAUSED; } // if we're the server of the game, send a packet which will pause the clients in the game now if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ for(idx=0;idx<MAX_PLAYERS;idx++){ if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){ send_client_update_packet(&Net_players[idx]); } } } }
// (client) call when receiving a packet indicating we should unpause void multi_pause_unpause() { int idx; // if we're already unpaused, don't do anything if(!Multi_pause_status){ return; } // sanity check Assert(Multi_pause_status); // mark the game as being unpaused Multi_pause_status = 0; // pop us out of any necessary states (including the pause state !!) multi_handle_state_special(); // mark the netgame state Netgame.game_state = NETGAME_STATE_IN_MISSION; // if we're the server of the game, send a packet which will unpause the clients in the game now // if we're the server of the game, send a packet which will pause the clients in the game now if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ for(idx=0;idx<MAX_PLAYERS;idx++){ if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){ send_client_update_packet(&Net_players[idx]); } } } }
// debug console function called to determine which player to kick void multi_dcf_kick() { int player_num,idx; SCP_string arg; // get the callsign of the player to kick dc_stuff_string(arg); player_num = -1; for(idx=0;idx<MAX_PLAYERS;idx++){ if(MULTI_CONNECTED(Net_players[idx]) && (stricmp(Net_players[idx].m_player->callsign, arg.c_str()) == 0)) { player_num = idx; break; } } // if we didn't find the player, notify of the results if(player_num == -1){ dc_printf("Could not find player %s to kick!", arg.c_str()); } // if we found the guy, then try and kick him else { multi_kick_player(player_num); } }
// debug console function called to determine which player to kick void multi_dcf_kick() { int player_num,idx; // get the callsign of the player to kick dc_get_arg(ARG_STRING); if(strlen(Dc_arg) == 0){ dc_printf("Invalid player callsign!\n"); return ; } player_num = -1; for(idx=0;idx<MAX_PLAYERS;idx++){ if(MULTI_CONNECTED(Net_players[idx]) && (stricmp(Net_players[idx].player->callsign,Dc_arg)==0)){ player_num = idx; break; } } // if we didn't find the player, notify of the results if(player_num == -1){ dc_printf("Could not find player %s to kick!",Dc_arg); } // if we found the guy, then try and kick him else { multi_kick_player(player_num); } }
// close void multi_df_debrief_close() { int idx; scoring_struct *sc; // shutdown the chatbox chatbox_close(); // if stats weren't accepted, backout my own stats if (multi_debrief_stats_accept_code() != 1) { // if stats weren't accepted, backout my own stats if (multi_debrief_stats_accept_code() != 1) { if(MULTIPLAYER_MASTER){ for(idx=0; idx<MAX_PLAYERS; idx++){ if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].m_player != NULL)){ sc = &Net_players[idx].m_player->stats; scoring_backout_accept(sc); if (Net_player == &Net_players[idx]) { Pilot.update_stats_backout( sc ); } } } } else { scoring_backout_accept( &Player->stats ); Pilot.update_stats_backout( &Player->stats ); } } } // music stuff common_music_close(); }
// create objects for all known observers in the game at level start // call this before entering a mission // this implies for the local player in the case of a client or for _all_ players in the case of a server void multi_obs_level_init() { int idx; // unset the OBS_PLAYER flag here for all net players for (idx = 0; idx < MAX_PLAYERS; idx++) { Net_players[idx].flags &= ~(NETINFO_FLAG_OBS_PLAYER); } // if i'm a client and I'm an observer, create an object for myself if (!(Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)) { // create my own observer object and setup other misc. data multi_obs_create_observer_client(); } // otherwise create stuff for all (permanent) observers in the game else { for (idx = 0; idx < MAX_PLAYERS; idx++) { if (MULTI_CONNECTED(Net_players[idx]) && MULTI_OBSERVER(Net_players[idx])) { // make an observer object for the guy multi_obs_create_observer(&Net_players[idx]); } } } }
// merge any mission stats accumulated into the alltime stats (as well as updating per campaign stats) void scoring_level_close(int accepted) { // want to calculate any other statistics. if (The_mission.game_type == MISSION_TYPE_TRAINING){ // call scoring_do_accept // this will grant any potential medals and then early bail, and // then we will early bail scoring_do_accept(&Player->stats); Pilot.update_stats(&Player->stats, true); return; } if(accepted){ // apply mission stats for all players in the game int idx; scoring_struct *sc; if(Game_mode & GM_MULTIPLAYER){ nprintf(("Network","Storing stats for all players now\n")); for(idx=0;idx<MAX_PLAYERS;idx++){ if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){ // get the scoring struct sc = &Net_players[idx].m_player->stats; scoring_do_accept( sc ); if (Net_player == &Net_players[idx]) { Pilot.update_stats(sc); } } } } else { nprintf(("General","Storing stats now\n")); scoring_do_accept( &Player->stats ); } // If this mission doesn't allow promotion or badges // then be sure that these don't get done. Don't allow promotions or badges when // playing normally and not in a campaign. if ( (The_mission.flags & MISSION_FLAG_NO_PROMOTION) || ((Game_mode & GM_NORMAL) && !(Game_mode & GM_CAMPAIGN_MODE)) ) { if ( Player->stats.m_promotion_earned != -1) { Player->stats.rank--; Player->stats.m_promotion_earned = -1; } // if a badge was earned, take it back if ( Player->stats.m_badge_earned.size() ){ for (size_t medal = 0; medal < Player->stats.m_badge_earned.size(); medal++) { Player->stats.medal_counts[Player->stats.m_badge_earned[medal]] = 0; } Player->stats.m_badge_earned.clear(); } } if ( !(Game_mode & GM_MULTIPLAYER) ) { Pilot.update_stats(&Player->stats); } } }
// evaluate if a wing SQUADMATE MESSAGE command should be sent to a player // return 0 if at least one ai ship got the order, 1 if only players int multi_msg_eval_wing_squadmsg(int wingnum,int command,ai_info *aif, int player_num) { int idx; ushort net_sig; int subsys_type; int sent_count; // get the index of the sender if(player_num == -1) player_num = MY_NET_PLAYER_NUM; // get the target information if(aif->target_objnum == -1){ net_sig = 0; } else { net_sig = Objects[aif->target_objnum].net_signature; } subsys_type = -1; if((aif->targeted_subsys == NULL) || (aif->targeted_subsys->system_info == NULL)){ subsys_type = -1; } else { subsys_type = aif->targeted_subsys->system_info->type; } // go through all netplayers and find all matched sent_count = Wings[wingnum].current_count; for(idx=0;idx<MAX_PLAYERS;idx++){ if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){ // if he is in the wing, send him the message if(multi_msg_player_in_wing(wingnum,&Net_players[idx])){ // if this was the sender himself, just decrement the count if(idx == player_num){ sent_count--; continue; } // if its me, just display locally if(&Net_players[idx] == Net_player){ multi_msg_show_squadmsg(&Net_players[player_num],command,net_sig,subsys_type); sent_count--; } // otherwise send it to who is supposed to get it else { multi_msg_send_squadmsg_packet(&Net_players[idx],&Net_players[player_num],command,net_sig,subsys_type); sent_count--; } } } } // if all the ships which got the message were players, return 1 return !sent_count; }
void hud_obs_render_players_all() { int idx,count; // render kills and stats information for all players count = 0; for(idx=0;idx<MAX_PLAYERS;idx++){ if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) ){ hud_obs_render_player(count,&Net_players[idx]); } } }
// send a ping to all players void multi_ping_send_all() { int idx; for(idx = 0;idx < MAX_PLAYERS;idx++){ if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != NULL) && (Net_player->player_id != Net_players[idx].player_id)){ // start the ping multi_ping_start(&Net_players[idx].s_info.ping); // send the ping packet send_ping(&Net_players[idx].p_info.addr); } } }
// do all sync related data stuff (server-side only) void multi_data_do() { int idx, p_idx; // only do this once a second if ((time(NULL) - Multi_data_time) < 1) { return; } // maybe try and reload player squad logo bitmaps multi_data_maybe_reload(); // reset the time Multi_data_time = time(NULL); // if I'm the server if (Net_player && (Net_player->flags & NETINFO_FLAG_AM_MASTER)) { // for all valid files for (idx = 0; idx < MAX_MULTI_DATA; idx++) { // a valid file that isn't currently xferring (ie, anything we've got completely on our hard drive) if (Multi_data[idx].used && (multi_xfer_lookup(Multi_data[idx].filename) < 0)) { // send it to all players who need it for (p_idx = 0; p_idx < MAX_PLAYERS; p_idx++) { // if he doesn't have it if ((Net_player != &Net_players[p_idx]) && MULTI_CONNECTED(Net_players[p_idx]) && (Net_players[p_idx].reliable_socket != INVALID_SOCKET) && (Multi_data[idx].status[p_idx] == 0)) { // queue up the file to send to him, or at least try to if (multi_xfer_send_file(Net_players[p_idx].reliable_socket, Multi_data[idx].filename, CF_TYPE_ANY, MULTI_XFER_FLAG_AUTODESTROY | MULTI_XFER_FLAG_QUEUE) < 0) { nprintf(("Network", "Failed to send data file! Trying again later...\n")); } else { // mark his status Multi_data[idx].status[p_idx] = 1; } } } } } } }
int find_netplayer_n(int n) { int idx; int target; target = n; n=0; for(idx=0;idx<MAX_PLAYERS;idx++){ if(MULTI_CONNECTED(Net_players[idx])){ n++; if(n == target) return idx; } } return -1; }
// process all kick details (disconnecting players who have been kicked but haven't closed their socket) void multi_kick_process() { int idx; // if i'm not the server, don't do anything if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){ return; } // disconnect any kicked players who have timed out on leaving for(idx=0;idx<MAX_PLAYERS;idx++){ if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].s_info.kick_timestamp != -1) && timestamp_elapsed(Net_players[idx].s_info.kick_timestamp) ){ delete_player(idx, Net_players[idx].s_info.kick_reason); } } }
// server should check to see if any respawned players have run out of their invulnerability void multi_respawn_handle_invul_players() { int idx; object *objp; for(idx=0;idx<MAX_PLAYERS;idx++){ if(MULTI_CONNECTED(Net_players[idx]) && (Objects[Net_players[idx].player->objnum].flags & OF_INVULNERABLE)){ // make him normal (_non_ invulnerable) on either of 2 conditions : // 1.) More than 5 seconds have passed // 2.) He's fired either a primary or a secondary weapon if( ((Net_players[idx].s_info.invul_timestamp != -1) && (timestamp_elapsed(Net_players[idx].s_info.invul_timestamp))) || ((Net_players[idx].player->ci.fire_primary_count > 0) || (Net_players[idx].player->ci.fire_secondary_count > 0)) ) { objp = &Objects[Net_players[idx].player->objnum]; obj_set_flags(objp,objp->flags & ~(OF_INVULNERABLE)); } } } }
// maybe try and reload player squad logo bitmaps void multi_data_maybe_reload() { int idx; // go through all connected and try to reload their images if necessary for(idx=0; idx<MAX_PLAYERS; idx++){ if(MULTI_CONNECTED(Net_players[idx]) && strlen(Net_players[idx].m_player->m_squad_filename) && (Net_players[idx].m_player->insignia_texture == -1)){ Net_players[idx].m_player->insignia_texture = bm_load_duplicate(Net_players[idx].m_player->m_squad_filename); // if the bitmap loaded properly, lock it as a transparent texture if(Net_players[idx].m_player->insignia_texture != -1){ bm_lock(Net_players[idx].m_player->insignia_texture, 16, BMP_TEX_XPARENT); bm_unlock(Net_players[idx].m_player->insignia_texture); } } } }
// evaluate if a ship SQUADMATE MESSAGE command should be sent to a player // return 0 if not sent to a netplayer, 1 if it was int multi_msg_eval_ship_squadmsg(int shipnum,int command,ai_info *aif, int player_num) { int idx; ushort net_sig; int subsys_type; // get the index of the sender if ( player_num == -1 ) player_num = MY_NET_PLAYER_NUM; // get the target information if(aif->target_objnum == -1){ net_sig = 0; } else { net_sig = Objects[aif->target_objnum].net_signature; } subsys_type = -1; if((aif->targeted_subsys == NULL) || (aif->targeted_subsys->system_info == NULL)){ subsys_type = -1; } else { subsys_type = aif->targeted_subsys->system_info->type; } // go through all netplayers and find all matched for(idx=0;idx<MAX_PLAYERS;idx++){ if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (idx != player_num)){ // if he is in the ship, send him the message if(multi_msg_player_in_ship(shipnum,&Net_players[idx])){ // if its me, just display locall if(&Net_players[idx] == Net_player){ multi_msg_show_squadmsg(&Net_players[player_num],command,net_sig,subsys_type); return 1; } // otherwise send it to who is supposed to get it else { multi_msg_send_squadmsg_packet(&Net_players[idx],&Net_players[player_num],command,net_sig,subsys_type); return 1; } } } } // this will let the messaging system show a response to the sender of the packet return 0; }
void FS2NetD_CheckDuplicateLogin() { int buffer_size; char buffer[BASE_PACKET_SIZE + sizeof(int) + sizeof(ubyte) + (MAX_PLAYERS * sizeof(int)) + 10]; int ids_count = 0; int *ids = new int[MAX_PLAYERS]; int idx; if ( !ids ) { return; } for (idx = 0; idx < MAX_PLAYERS; idx++) { if ( MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) ) { if ( (Net_players[idx].tracker_player_id >= 0) && (Net_players[idx].tracker_player_id != Multi_tracker_id) ) { ids[ids_count] = Net_players[idx].tracker_player_id; ids_count++; } } } if ( !ids_count ) { delete [] ids; return; } INIT_PACKET( PCKT_DUP_LOGIN_RQST ); PXO_ADD_INT( Multi_tracker_id ); Assert( MAX_PLAYERS <= 255 ); ubyte tvar = (ubyte)ids_count; PXO_ADD_DATA( tvar ); for (idx = 0; idx < ids_count; idx++) { PXO_ADD_INT( ids[idx] ); } DONE_PACKET(); FS2NetD_SendData(buffer, buffer_size); delete [] ids; }
// call once per level just before entering the mission void multi_df_level_pre_enter() { int idx; // if we're not in dogfight mode, do nothing if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){ return; } // go through all player ships and make them hostile for(idx=0; idx<MAX_PLAYERS; idx++){ if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx]) && (Net_players[idx].m_player != NULL) && (Net_players[idx].m_player->objnum >= 0) && (Objects[Net_players[idx].m_player->objnum].type == OBJ_SHIP)){ Ships[Objects[Net_players[idx].m_player->objnum].instance].team = Iff_traitor; } } // }
// lookup the "next" player in the netplayer list, return null if not found net_player *multi_pinfo_get_next_player(net_player *np) { int start_index; int idx; // get the starting index to look from start_index = NET_PLAYER_INDEX(np); if(start_index < (MAX_PLAYERS - 1)){ // look forwards for(idx=start_index+1; idx<MAX_PLAYERS; idx++){ if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){ return &Net_players[idx]; } } } return NULL; }
// lookup the "previous" player in the netplayer list, return null if not found net_player *multi_pinfo_get_prev_player(net_player *np) { int start_index; int idx; // get the starting index to look from start_index = NET_PLAYER_INDEX(np); if(start_index > 0){ // look backwards for(idx=start_index-1; idx>=0; idx--){ if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){ return &Net_players[idx]; } } } return NULL; }
// called when server is waiting for clients to disconnect int multi_endgame_server_ok_to_leave() { int idx; // check to see if our client disconnect timestamp has elapsed if ( Multi_endgame_server_wait_stamp > 0.0f ) { Multi_endgame_server_wait_stamp -= flFrametime; if ( Multi_endgame_server_wait_stamp <= 0.0f ) { return 1; } } // check to see if all clients have disconnected for(idx=0;idx<MAX_PLAYERS;idx++){ if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){ return 0; } } // all conditions passed return 1; }
// setup kill matrix data void multi_df_setup_kill_matrix() { int idx, s_idx; multi_df_score *s; Multi_df_score_count = 0; // add players as necessary for(idx=0; idx<MAX_PLAYERS; idx++){ if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].m_player != NULL)){ // stuff data for this guy s = &Multi_df_score[Multi_df_score_count++]; ml_printf("Dogfight debrief stats for %s", Net_players[idx].m_player->callsign); for(s_idx=0; s_idx<MAX_PLAYERS; s_idx++){ ml_printf("%d", Net_players[idx].m_player->stats.m_dogfight_kills[s_idx]); } s->stats = Net_players[idx].m_player->stats; strcpy_s(s->callsign, Net_players[idx].m_player->callsign); s->np_index = idx; } } }
// create complete priority sorted escort list for all active ships // escorts - array of escort info // num_escorts - number of escorts requests in field of active ships // This will be culled to MAX_ESCORTS, selecting the top set from escorts void hud_create_complete_escort_list(escort_info *escorts, int *num_escorts) { ship_obj *so; object *objp; // start with none on list *num_escorts = 0; int idx; // multiplayer dogfight if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT)){ for(idx=0; idx<MAX_PLAYERS; idx++){ // break out of the loop when we have reached our max if ( *num_escorts == MAX_COMPLETE_ESCORT_LIST ) { mprintf(("exceeded max ships in big escort list")); break; } // is this a valid player if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){ // add the ship escorts[*num_escorts].objnum = -1; escorts[*num_escorts].obj_signature = -1; escorts[*num_escorts].priority = -1; escorts[*num_escorts].np_id = Net_players[idx].player_id; (*num_escorts)++; } } } // all others else { for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { Assert( so->objnum >= 0 && so->objnum < MAX_OBJECTS); if((so->objnum < 0) || (so->objnum >= MAX_OBJECTS)){ continue; } objp = &Objects[so->objnum]; Assert( objp->type == OBJ_SHIP ); if(objp->type != OBJ_SHIP){ continue; } // break out of the loop when we have reached our max if ( *num_escorts == MAX_COMPLETE_ESCORT_LIST ) { mprintf(("exceeded max ships in big escort list")); break; } // only process ships that might be on the list if ( !(Ships[objp->instance].flags & SF_ESCORT) ){ continue; } // only process ships that can be seen by sensors if ( (Ships[objp->instance].flags & SF_HIDDEN_FROM_SENSORS) ){ continue; } // don't process most stealth ships if ( (Ships[objp->instance].flags2 & SF2_STEALTH) ) { if ( Ships[objp->instance].team == Player_ship->team ) { // friendly stealths are only not seen when explicitly specified if ( Ships[objp->instance].flags2 & SF2_FRIENDLY_STEALTH_INVIS ) { continue; } } // non-friendly stealths are never seen else { continue; } } // don't process ships that are dying, or objects that should be dead if ( (Ships[objp->instance].flags & (SF_DYING|SF_DEPARTING)) || (objp->flags & OF_SHOULD_BE_DEAD) ){ continue; } // add the ship escorts[*num_escorts].objnum = so->objnum; escorts[*num_escorts].obj_signature = objp->signature; escorts[*num_escorts].priority = Ships[objp->instance].escort_priority; escorts[*num_escorts].np_id = -1; (*num_escorts)++; } } }
// evaluate a kill on a ship int scoring_eval_kill(object *ship_objp) { float max_damage_pct; // the pct% of total damage the max damage object did int max_damage_index; // the index into the dying ship's damage_ship[] array corresponding the greatest amount of damage int killer_sig; // signature of the guy getting credit for the kill (or -1 if none) int idx,net_player_num; player *plr; // pointer to a player struct if it was a player who got the kill net_player *net_plr = NULL; ship *dead_ship; // the ship which was killed net_player *dead_plr = NULL; float scoring_scale_by_damage = 1; // percentage to scale the killer's score by if we score based on the amount of damage caused int kill_score, assist_score; bool is_enemy_player = false; // true if the player just killed an enemy player ship // multiplayer clients bail here if(MULTIPLAYER_CLIENT){ return -1; } // we don't evaluate kills on anything except ships if(ship_objp->type != OBJ_SHIP){ return -1; } if((ship_objp->instance < 0) || (ship_objp->instance >= MAX_SHIPS)){ return -1; } // assign the dead ship dead_ship = &Ships[ship_objp->instance]; // evaluate player deaths if(Game_mode & GM_MULTIPLAYER){ net_player_num = multi_find_player_by_object(ship_objp); if(net_player_num != -1){ Net_players[net_player_num].m_player->stats.m_player_deaths++; nprintf(("Network","Setting player %s deaths to %d\n",Net_players[net_player_num].m_player->callsign,Net_players[net_player_num].m_player->stats.m_player_deaths)); dead_plr = &Net_players[net_player_num]; is_enemy_player = true; } } else { if(ship_objp == Player_obj){ Player->stats.m_player_deaths++; } } net_player_num = -1; // clear out invalid damager ships for(idx=0; idx<MAX_DAMAGE_SLOTS; idx++){ if((dead_ship->damage_ship_id[idx] >= 0) && (ship_get_by_signature(dead_ship->damage_ship_id[idx]) < 0)){ dead_ship->damage_ship[idx] = 0.0f; dead_ship->damage_ship_id[idx] = -1; } } // determine which object did the most damage to the dying object, and how much damage that was max_damage_index = -1; for(idx=0;idx<MAX_DAMAGE_SLOTS;idx++){ // bogus ship if(dead_ship->damage_ship_id[idx] < 0){ continue; } // if this slot did more damage then the next highest slot if((max_damage_index == -1) || (dead_ship->damage_ship[idx] > dead_ship->damage_ship[max_damage_index])){ max_damage_index = idx; } } // doh if((max_damage_index < 0) || (max_damage_index >= MAX_DAMAGE_SLOTS)){ return -1; } // the pct of total damage applied to this ship max_damage_pct = dead_ship->damage_ship[max_damage_index] / dead_ship->total_damage_received; CLAMP(max_damage_pct, 0.0f, 1.0f); // only evaluate if the max damage % is high enough to record a kill and it was done by a valid object if((max_damage_pct >= Kill_percentage) && (dead_ship->damage_ship_id[max_damage_index] >= 0)){ // set killer_sig for this ship to the signature of the guy who gets credit for the kill killer_sig = dead_ship->damage_ship_id[max_damage_index]; // set the scale value if we only award 100% score for 100% damage if (The_mission.ai_profile->flags & AIPF_KILL_SCORING_SCALES_WITH_DAMAGE) { scoring_scale_by_damage = max_damage_pct; } // null this out for now plr = NULL; net_plr = NULL; // get the player (whether single or multiplayer) net_player_num = -1; if(Game_mode & GM_MULTIPLAYER){ net_player_num = multi_find_player_by_signature(killer_sig); if(net_player_num != -1){ plr = Net_players[net_player_num].m_player; net_plr = &Net_players[net_player_num]; } } else { if(Objects[Player->objnum].signature == killer_sig){ plr = Player; } } // if we found a valid player, evaluate some kill details if(plr != NULL){ int si_index; // bogus if((plr->objnum < 0) || (plr->objnum >= MAX_OBJECTS)){ return -1; } // get the ship info index of the ship type of this kill. we need to take ship // copies into account here. si_index = dead_ship->ship_info_index; if (Ship_info[si_index].flags & SIF_SHIP_COPY) { char temp[NAME_LENGTH]; strcpy_s(temp, Ship_info[si_index].name); end_string_at_first_hash_symbol(temp); // Goober5000 - previous error checking guarantees that this will be >= 0 si_index = ship_info_lookup(temp); } // if he killed a guy on his own team increment his bonehead kills if((Ships[Objects[plr->objnum].instance].team == dead_ship->team) && !MULTI_DOGFIGHT ){ if (!(The_mission.flags & MISSION_FLAG_NO_TRAITOR)) { plr->stats.m_bonehead_kills++; kill_score = -(int)(dead_ship->score * scoring_get_scale_factor()); plr->stats.m_score += kill_score; if(net_plr != NULL ) { multi_team_maybe_add_score(-(dead_ship->score), net_plr->p_info.team); } } } // otherwise increment his valid kill count and score else { // dogfight mode if(MULTI_DOGFIGHT && (multi_find_player_by_object(ship_objp) < 0)){ // don't add a kill for dogfight kills on non-players } else { plr->stats.m_okKills[si_index]++; plr->stats.m_kill_count_ok++; // only computer controlled enemies should scale with difficulty if (is_enemy_player) { kill_score = (int)(dead_ship->score * scoring_scale_by_damage); } else { kill_score = (int)(dead_ship->score * scoring_get_scale_factor() * scoring_scale_by_damage); } plr->stats.m_score += kill_score; hud_gauge_popup_start(HUD_KILLS_GAUGE); #ifdef SCORING_DEBUG char kill_score_text[1024] = ""; sprintf(kill_score_text, "SCORING : %s killed a ship worth %d points and gets %d pts for the kill\n", plr->callsign, dead_ship->score, kill_score); if (MULTIPLAYER_MASTER) { send_game_chat_packet(Net_player, kill_score_text, MULTI_MSG_ALL); } HUD_printf(kill_score_text); mprintf((kill_score_text)); #endif // multiplayer if(net_plr != NULL){ multi_team_maybe_add_score(dead_ship->score , net_plr->p_info.team); // award teammates % of score value for big ship kills // not in dogfight tho // and not if there is no assist threshold (as otherwise assists could get higher scores than kills) if (!(Netgame.type_flags & NG_TYPE_DOGFIGHT) && (Ship_info[dead_ship->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) { for (idx=0; idx<MAX_PLAYERS; idx++) { if (MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].p_info.team == net_plr->p_info.team) && (&Net_players[idx] != net_plr)) { assist_score = (int)(dead_ship->score * The_mission.ai_profile->assist_award_percentage_scale[Game_skill_level]); Net_players[idx].m_player->stats.m_score += assist_score; #ifdef SCORING_DEBUG // DEBUG CODE TO TEST NEW SCORING char score_text[1024] = ""; sprintf(score_text, "SCORING : All team mates get %d pts for helping kill the capship\n", assist_score); send_game_chat_packet(Net_player, score_text, MULTI_MSG_ALL); HUD_printf(score_text); mprintf((score_text)); #endif } } } // death message if((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (net_plr != NULL) && (dead_plr != NULL) && (net_plr->m_player != NULL) && (dead_plr->m_player != NULL)){ char dead_text[1024] = ""; sprintf(dead_text, "%s gets the kill for %s", net_plr->m_player->callsign, dead_plr->m_player->callsign); send_game_chat_packet(Net_player, dead_text, MULTI_MSG_ALL, NULL, NULL, 2); HUD_printf(dead_text); } } } } // increment his all-encompassing kills plr->stats.m_kills[si_index]++; plr->stats.m_kill_count++; // update everyone on this guy's kills if this is multiplayer if(MULTIPLAYER_MASTER && (net_player_num != -1)){ // send appropriate stats if(Netgame.type_flags & NG_TYPE_DOGFIGHT){ // evaluate dogfight kills multi_df_eval_kill(&Net_players[net_player_num], ship_objp); // update stats send_player_stats_block_packet(&Net_players[net_player_num], STATS_DOGFIGHT_KILLS); } else { send_player_stats_block_packet(&Net_players[net_player_num], STATS_MISSION_KILLS); } } } } else { // set killer_sig for this ship to -1, indicating no one got the kill for it killer_sig = -1; } // pass in the guy who got the credit for the kill (if any), so that he doesn't also // get credit for an assist scoring_eval_assists(dead_ship,killer_sig, is_enemy_player); #ifdef SCORING_DEBUG if (Game_mode & GM_MULTIPLAYER) { char buf[256]; sprintf(Scoring_debug_text, "SCORING : %s killed.\nDamage by ship:\n\n", Ship_info[dead_ship->ship_info_index].name); // show damage done by player for (int i=0; i<MAX_DAMAGE_SLOTS; i++) { int net_player_num = multi_find_player_by_signature(dead_ship->damage_ship_id[i]); if (net_player_num != -1) { plr = Net_players[net_player_num].m_player; sprintf(buf, "%s: %f", plr->callsign, dead_ship->damage_ship[i]); if (dead_ship->damage_ship_id[i] == killer_sig ) { strcat_s(buf, " KILLER\n"); } else { strcat_s(buf, "\n"); } strcat_s(Scoring_debug_text, buf); } } mprintf ((Scoring_debug_text)); } #endif return max_damage_index; }
// create complete priority sorted escort list for all active ships // escorts - array of escort info // num_escorts - number of escorts requests in field of active ships // This will be culled to MAX_ESCORTS, selecting the top set from escorts void hud_create_complete_escort_list(escort_info *escorts, int *num_escorts) { ship_obj *so; object *objp; // start with none on list *num_escorts = 0; int idx; // multiplayer dogfight if(MULTI_DOGFIGHT){ for(idx=0; idx<MAX_PLAYERS; idx++){ // break out of the loop when we have reached our max if ( *num_escorts == MAX_COMPLETE_ESCORT_LIST ) { mprintf(("exceeded max ships in big escort list\n")); break; } // is this a valid player if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){ // add the ship escorts[*num_escorts].objnum = -1; escorts[*num_escorts].obj_signature = -1; escorts[*num_escorts].priority = -1; escorts[*num_escorts].np_id = Net_players[idx].player_id; escorts[*num_escorts].escort_hit_timer = 0; escorts[*num_escorts].escort_hit_next_flash = 0; escorts[*num_escorts].escort_show_bright = false; (*num_escorts)++; } } } // all others else { for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { Assert( so->objnum >= 0 && so->objnum < MAX_OBJECTS); if((so->objnum < 0) || (so->objnum >= MAX_OBJECTS)){ continue; } objp = &Objects[so->objnum]; Assert( objp->type == OBJ_SHIP ); if(objp->type != OBJ_SHIP){ continue; } // break out of the loop when we have reached our max if ( *num_escorts == MAX_COMPLETE_ESCORT_LIST ) { mprintf(("exceeded max ships in big escort list\n")); break; } // only process ships that might be on the list if ( !(Ships[objp->instance].flags[Ship::Ship_Flags::Escort]) ){ continue; } // only process ships that can be seen by sensors if ( (Ships[objp->instance].flags[Ship::Ship_Flags::Hidden_from_sensors]) ){ continue; } // don't process most stealth ships if ( (Ships[objp->instance].flags[Ship::Ship_Flags::Stealth]) ) { if ( Ships[objp->instance].team == Player_ship->team ) { // friendly stealths are only not seen when explicitly specified if ( Ships[objp->instance].flags[Ship::Ship_Flags::Friendly_stealth_invis] ) { continue; } } // non-friendly stealths are never seen else { continue; } } // don't process objects that should be dead if ( objp->flags[Object::Object_Flags::Should_be_dead] ) { continue; } // add the ship escorts[*num_escorts].objnum = so->objnum; escorts[*num_escorts].obj_signature = objp->signature; escorts[*num_escorts].priority = Ships[objp->instance].escort_priority; escorts[*num_escorts].np_id = -1; escorts[*num_escorts].escort_hit_timer = 0; escorts[*num_escorts].escort_hit_next_flash = 0; escorts[*num_escorts].escort_show_bright = false; (*num_escorts)++; } } }
// blit the kill matrix void multi_df_blit_kill_matrix() { int idx, s_idx, str_len; int cx, cy; char squashed_string[CALLSIGN_LEN+1] = ""; int dy = gr_get_font_height() + 1; // max width of an individual item, and the text that can be in that item float max_item_width = ((float)Multi_df_display_coords[gr_screen.res][2] - 40.0f) / (float)(Multi_df_score_count + 1); float max_text_width = max_item_width * 0.8f; // start x for the top bar (one item to the right) int top_x_start = Multi_df_display_coords[gr_screen.res][0] + (int)max_item_width; int top_y_start = Multi_df_display_coords[gr_screen.res][1]; // start x for the side bar int side_x_start = Multi_df_display_coords[gr_screen.res][0]; int side_y_start = Multi_df_display_coords[gr_screen.res][1] + dy; // draw the top bar cx = top_x_start; cy = top_y_start; for(idx=0; idx<Multi_df_score_count; idx++){ // force the string to fit nicely strcpy_s(squashed_string, Multi_df_score[idx].callsign); font::force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width); gr_get_string_size(&str_len, NULL, squashed_string); // set color and blit the string Assert(Multi_df_score[idx].np_index >= 0); if(Multi_df_score[idx].np_index >= 0){ gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]); } gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string, GR_RESIZE_MENU); // next spot cx += (int)max_item_width; } // draw the rest of the scoreboard cx = side_x_start; cy = side_y_start; int row_total; for(idx=0; idx<Multi_df_score_count; idx++){ // draw a check if necessary if(!MULTI_CONNECTED(Net_players[Multi_df_score[idx].np_index]) || (Net_players[Multi_df_score[idx].np_index].state == NETPLAYER_STATE_DEBRIEF_ACCEPT) || (Net_players[Multi_df_score[idx].np_index].state == NETPLAYER_STATE_DEBRIEF_REPLAY)){ if(Multi_common_icons[MICON_VALID] != -1){ gr_set_bitmap(Multi_common_icons[MICON_VALID]); gr_bitmap(Multi_df_check_coords[gr_screen.res], cy, GR_RESIZE_MENU); } } // draw the name cx = Multi_df_display_coords[gr_screen.res][0]; strcpy_s(squashed_string, Multi_df_score[idx].callsign); font::force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width); gr_get_string_size(&str_len, NULL, squashed_string); Assert(Multi_df_score[idx].np_index >= 0); if(Multi_df_score[idx].np_index >= 0){ gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]); } gr_string(cx, cy, squashed_string, GR_RESIZE_MENU); cx = top_x_start; row_total = 0; for(s_idx=0; s_idx<Multi_df_score_count; s_idx++){ // stuff the string to be displayed and select the proper display color if(s_idx == idx){ strcpy_s(squashed_string, "-"); gr_set_color_fast(&Color_grey); } else { row_total += multi_df_stuff_kills(squashed_string, idx, s_idx); Assert(Multi_df_score[idx].np_index >= 0); if(Multi_df_score[idx].np_index >= 0){ gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]); } } // draw the string font::force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width); gr_get_string_size(&str_len, NULL, squashed_string); gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string, GR_RESIZE_MENU); // next spot cx += (int)max_item_width; } // draw the row total gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]); sprintf(squashed_string, "(%d)", row_total); gr_get_string_size(&str_len, NULL, squashed_string); gr_string(Multi_df_display_coords[gr_screen.res][0] + Multi_df_display_coords[gr_screen.res][2] - (MULTI_DF_TOTAL_ADJUST + str_len), cy, squashed_string, GR_RESIZE_MENU); cy += dy; } }
// process an incoming multi options packet void multi_options_process_packet(unsigned char *data, header *hinfo) { ubyte code; multi_local_options bogus; int idx,player_index; char str[255]; int offset = HEADER_LENGTH; // find out who is sending this data player_index = find_player_id(hinfo->id); if (player_index < 0) { nprintf(("Network", "Received packet from unknown player!\n")); return; } // get the packet code GET_DATA(code); switch(code){ // get the start game options case MULTI_OPTION_START_GAME: Assert(Game_mode & GM_STANDALONE_SERVER); // get the netgame name GET_STRING(Netgame.name); // get the netgame mode GET_INT(Netgame.mode); // get the security # GET_INT(Netgame.security); // get mode specific data switch(Netgame.mode){ case NG_MODE_PASSWORD: GET_STRING(Netgame.passwd); break; case NG_MODE_RANK_ABOVE: case NG_MODE_RANK_BELOW: GET_INT(Netgame.rank_base); break; } // update standalone stuff std_connect_set_gamename(Netgame.name); std_multi_update_netgame_info_controls(); break; // get mission choice options case MULTI_OPTION_MISSION: netgame_info ng; char title[NAME_LENGTH+1]; int campaign_type,max_players; memset(&ng,0,sizeof(netgame_info)); Assert(Game_mode & GM_STANDALONE_SERVER); // coop or team vs. team mode GET_INT(ng.type_flags); if((ng.type_flags & NG_TYPE_TEAM) && !(Netgame.type_flags & NG_TYPE_TEAM)){ multi_team_reset(); } // if squad war was switched on if((ng.type_flags & NG_TYPE_SW) && !(Netgame.type_flags & NG_TYPE_SW)){ mprintf(("STANDALONE TURNED ON SQUAD WAR!!\n")); } Netgame.type_flags = ng.type_flags; // new respawn count GET_UINT(Netgame.respawn); // name string memset(str,0,255); GET_DATA(code); // campaign mode if(code){ GET_STRING(ng.campaign_name); // set the netgame max players here if the filename has changed if(strcmp(Netgame.campaign_name,ng.campaign_name) != 0){ memset(title,0,NAME_LENGTH+1); if(!mission_campaign_get_info(ng.campaign_name,title,&campaign_type,&max_players)){ Netgame.max_players = 0; } else { Netgame.max_players = max_players; } strcpy_s(Netgame.campaign_name,ng.campaign_name); } Netgame.campaign_mode = 1; // put brackets around the campaign name if(Game_mode & GM_STANDALONE_SERVER){ strcpy_s(str,"("); strcat_s(str,Netgame.campaign_name); strcat_s(str,")"); std_multi_set_standalone_mission_name(str); } } // non-campaign mode else { GET_STRING(ng.mission_name); if(strcmp(Netgame.mission_name,ng.mission_name) != 0){ if(strlen(ng.mission_name)){ Netgame.max_players = mission_parse_get_multi_mission_info( ng.mission_name ); } else { // setting this to -1 will prevent us from being seen on the network Netgame.max_players = -1; } strcpy_s(Netgame.mission_name,ng.mission_name); strcpy_s(Game_current_mission_filename,Netgame.mission_name); } Netgame.campaign_mode = 0; // set the mission name if(Game_mode & GM_STANDALONE_SERVER){ std_multi_set_standalone_mission_name(Netgame.mission_name); } } // update FS2NetD as well if (MULTI_IS_TRACKER_GAME) { fs2netd_gameserver_update(true); } send_netgame_update_packet(); break; // get the netgame options case MULTI_OPTION_SERVER: get_server_options(data, &offset, &Netgame.options); // if we're a standalone set for no sound, do so here if((Game_mode & GM_STANDALONE_SERVER) && !Multi_options_g.std_voice){ Netgame.options.flags |= MSO_FLAG_NO_VOICE; } else { // maybe update the quality of sound multi_voice_maybe_update_vars(Netgame.options.voice_qos,Netgame.options.voice_record_time); } // set the skill level Game_skill_level = Netgame.options.skill_level; if((Game_mode & GM_STANDALONE_SERVER) && !(Game_mode & GM_CAMPAIGN_MODE)){ Netgame.respawn = Netgame.options.respawn; } // if we have the "temp closed" flag toggle if(Netgame.options.flags & MLO_FLAG_TEMP_CLOSED){ Netgame.flags ^= NG_FLAG_TEMP_CLOSED; } Netgame.options.flags &= ~(MLO_FLAG_TEMP_CLOSED); // if i'm the standalone server, I should rebroadcast to all other players if(Game_mode & GM_STANDALONE_SERVER){ for(idx=0;idx<MAX_PLAYERS;idx++){ if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx]) && (&Net_players[idx] != &Net_players[player_index]) ){ multi_io_send_reliable(&Net_players[idx], data, offset); } } send_netgame_update_packet(); } break; // local netplayer options case MULTI_OPTION_LOCAL: if(player_index == -1){ get_local_options(data, &offset, &bogus); } else { get_local_options(data, &offset, &Net_players[player_index].p_info.options); //If the client has sent an object update higher than that which the server allows, reset it if (Net_player->flags & NETINFO_FLAG_AM_MASTER) { if (Net_players[player_index].p_info.options.obj_update_level > Cmdline_objupd) { Net_players[player_index].p_info.options.obj_update_level = Cmdline_objupd; } } } break; } PACKET_SET_SIZE(); }