void mission_goal_status_change( int goal_num, int new_status) { int type; Assert(goal_num < Num_goals); Assert((new_status == GOAL_FAILED) || (new_status == GOAL_COMPLETE)); // if in a multiplayer game, send a status change to clients if ( MULTIPLAYER_MASTER ){ send_mission_goal_info_packet( goal_num, new_status, -1 ); } type = Mission_goals[goal_num].type & GOAL_TYPE_MASK; Mission_goals[goal_num].satisfied = new_status; if ( new_status == GOAL_FAILED ) { // don't display bonus goal failure if ( type != BONUS_GOAL ) { // only do HUD and music is goals are my teams goals. if ( (Game_mode & GM_NORMAL) || ((Net_player != NULL) && (Net_player->p_info.team == Mission_goals[goal_num].team)) ) { hud_add_objective_messsage(type, new_status); if ( !Mission_goals[goal_num].flags & MGF_NO_MUSIC ) { // maybe play event music event_music_primary_goal_failed(); } //HUD_sourced_printf(HUD_SOURCE_FAILED, "%s goal failed at time %6.1f!", Goal_type_text(type), f2fl(Missiontime) ); } } mission_log_add_entry( LOG_GOAL_FAILED, Mission_goals[goal_num].name, NULL, goal_num ); } else if ( new_status == GOAL_COMPLETE ) { if ( (Game_mode & GM_NORMAL) || ((Net_player != NULL) && (Net_player->p_info.team == Mission_goals[goal_num].team))) { hud_add_objective_messsage(type, new_status); // cue for Event Music if ( !(Mission_goals[goal_num].flags & MGF_NO_MUSIC) ) { event_music_primary_goals_met(); } mission_log_add_entry( LOG_GOAL_SATISFIED, Mission_goals[goal_num].name, NULL, goal_num ); } if(Game_mode & GM_MULTIPLAYER){ // squad war multi_team_maybe_add_score((int)(Mission_goals[goal_num].score * scoring_get_scale_factor()), Mission_goals[goal_num].team); } else { // deal with the score Player->stats.m_score += (int)(Mission_goals[goal_num].score * scoring_get_scale_factor()); } } }
//evaluate a kill on a weapon, right now this is only called on bombs. -Halleck int scoring_eval_kill_on_weapon(object *weapon_obj, object *other_obj) { 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; 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; weapon *dead_wp; // the weapon that was killed weapon_info *dead_wip; // info on the weapon that was killed if((weapon_obj->instance < 0) || (weapon_obj->instance >= MAX_WEAPONS)){ return -1; } dead_wp = &Weapons[weapon_obj->instance]; //assign the dead weapon dead_wip = &Weapon_info[dead_wp->weapon_info_index]; // multiplayer clients bail here if(MULTIPLAYER_CLIENT){ return -1; } // we don't evaluate kills on anything except weapons if(weapon_obj->type != OBJ_WEAPON){ return -1; } // we don't evaluate kills on anything except bombs, currently. -Halleck if(!(dead_wip->wi_flags & WIF_BOMB)) { return -1; } // clear out invalid damager ships for(idx=0; idx<MAX_DAMAGE_SLOTS; idx++){ if((dead_wp->damage_ship_id[idx] >= 0) && (ship_get_by_signature(dead_wp->damage_ship_id[idx]) < 0)){ dead_wp->damage_ship[idx] = 0.0f; dead_wp->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_wp->damage_ship_id[idx] < 0){ continue; } // if this slot did more damage then the next highest slot if((max_damage_index == -1) || (dead_wp->damage_ship[idx] > dead_wp->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_wp->damage_ship[max_damage_index] / dead_wp->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_wp->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_wp->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_wp->weapon_info_index; // if he killed a guy on his own team increment his bonehead kills if((Ships[Objects[plr->objnum].instance].team == dead_wp->team) && !MULTI_DOGFIGHT ){ if (!(The_mission.flags & MISSION_FLAG_NO_TRAITOR)) { plr->stats.m_bonehead_kills++; kill_score = -(int)(dead_wip->score * scoring_get_scale_factor()); plr->stats.m_score += kill_score; if(net_plr != NULL ) { multi_team_maybe_add_score(-(dead_wip->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(weapon_obj) < 0)){ // don't add a kill for dogfight kills on non-players } else { // bombs don't scale with difficulty at the moment. If we change this we want to *scoring_get_scale_factor() too kill_score = (int)(dead_wip->score * 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 %i points and gets %i pts for the kill", plr->callsign, dead_wip->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_wip->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) /* Below is N/A. -Halleck if (!(Netgame.type_flags & NG_TYPE_DOGFIGHT) && (Ship_info[dead_wp->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_wip->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", 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 /*Not really a kill. -Halleck 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], weapon_obj); // 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 /* TODO: ADD ASSIST CALC BACK IN. -Halleck scoring_eval_assists(dead_wp,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", dead_wip->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_wp->damage_ship_id[i]); if (net_player_num != -1) { plr = Net_players[net_player_num].m_player; sprintf(buf, "%s: %f", plr->callsign, dead_wp->damage_ship[i]); if (dead_wp->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; }
//evaluate a kill on a weapon, right now this is only called on bombs. -Halleck int scoring_eval_kill_on_weapon(object *weapon_obj, object *other_obj) { int killer_sig; // signature of the guy getting credit for the kill (or -1 if none) int net_player_num; player *plr; // pointer to a player struct if it was a player who got the kill net_player *net_plr = NULL; int kill_score; // multiplayer clients bail here if(MULTIPLAYER_CLIENT){ return -1; } // we don't evaluate kills on anything except weapons // also make sure there was a killer, and that it was a ship if((weapon_obj->type != OBJ_WEAPON) || (weapon_obj->instance < 0) || (weapon_obj->instance >= MAX_WEAPONS) || (other_obj == nullptr) || (other_obj->type != OBJ_WEAPON) || (other_obj->instance < 0) || (other_obj->instance >= MAX_WEAPONS) || (other_obj->parent == -1) || (Objects[other_obj->parent].type != OBJ_SHIP)) { return -1; } weapon *dead_wp = &Weapons[weapon_obj->instance]; // the weapon that was killed weapon_info *dead_wip = &Weapon_info[dead_wp->weapon_info_index]; // info on the weapon that was killed // we don't evaluate kills on anything except bombs, currently. -Halleck if(!(dead_wip->wi_flags & WIF_BOMB)) { return -1; } // set killer_sig for this weapon to the signature of the guy who gets credit for the kill killer_sig = Objects[other_obj->parent].signature; // only evaluate if the kill was done by a valid object if(killer_sig >= 0) { // 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){ // bogus if((plr->objnum < 0) || (plr->objnum >= MAX_OBJECTS)){ return -1; } // if he killed a bomb on his own team increment his bonehead kills if((Ships[Objects[plr->objnum].instance].team == dead_wp->team) && !MULTI_DOGFIGHT ){ if (!(The_mission.flags & MISSION_FLAG_NO_TRAITOR)) { plr->stats.m_bonehead_kills++; kill_score = -(int)(dead_wip->score * scoring_get_scale_factor()); plr->stats.m_score += kill_score; if(net_plr != NULL ) { multi_team_maybe_add_score(-(dead_wip->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(weapon_obj) < 0)){ // don't add a kill for dogfight kills on non-players } else { // bombs don't scale with difficulty at the moment. If we change this we want to *scoring_get_scale_factor() kill_score = dead_wip->score; 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 bomb worth %i points and gets %i pts for the kill", plr->callsign, dead_wip->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_wip->score , net_plr->p_info.team); } } } } } #ifdef SCORING_DEBUG if (Game_mode & GM_MULTIPLAYER) { sprintf(Scoring_debug_text, "SCORING : %s killed.\nKilled by player:\n", dead_wip->name); int net_player_num = multi_find_player_by_signature(killer_sig); if (net_player_num != -1) { plr = Net_players[net_player_num].m_player; char buf[256]; sprintf(buf, " %s\n", plr->callsign); strcat_s(Scoring_debug_text, buf); } mprintf ((Scoring_debug_text)); } #endif return killer_sig; }
// function which evaluates and processes the given event void mission_process_event( int event ) { int store_flags = Mission_events[event].flags; int store_formula = Mission_events[event].formula; int store_result = Mission_events[event].result; int store_count = Mission_events[event].count; int result, sindex; bool bump_timestamp = false; Log_event = false; Directive_count = 0; Event_index = event; sindex = Mission_events[event].formula; result = Mission_events[event].result; // if chained, insure that previous event is true and next event is false if (Mission_events[event].chain_delay >= 0) { // this indicates it's chained // What everyone expected the chaining behavior to be, as specified in Karajorma's original fix to Mantis #82 if (Alternate_chaining_behavior) { if (event > 0){ if (!Mission_events[event - 1].result || ((fix) Mission_events[event - 1].satisfied_time + i2f(Mission_events[event].chain_delay) > Missiontime)){ sindex = -1; // bypass evaluation } } } // Volition's original chaining behavior as used in retail and demonstrated in e.g. btm-01.fsm (or btm-01.fs2 in the Port) else { if (event > 0){ if (!Mission_events[event - 1].result || ((fix) Mission_events[event - 1].timestamp + i2f(Mission_events[event].chain_delay) > Missiontime)){ sindex = -1; // bypass evaluation } } if ((event < Num_mission_events - 1) && Mission_events[event + 1].result && (Mission_events[event + 1].chain_delay >= 0)){ sindex = -1; // bypass evaluation } } } if (sindex >= 0) { Sexp_useful_number = 1; if (Snapshot_all_events || Mission_events[event].mission_log_flags != 0) { Log_event = true; Current_event_log_buffer = &Mission_events[event].event_log_buffer; Current_event_log_variable_buffer = &Mission_events[event].event_log_variable_buffer; Current_event_log_argument_buffer = &Mission_events[event].event_log_argument_buffer; } result = eval_sexp(sindex); // if the directive count is a special value, deal with that first. Mark the event as a special // event, and unmark it when the directive is true again. if ( (Directive_count == DIRECTIVE_WING_ZERO) && !(Mission_events[event].flags & MEF_DIRECTIVE_SPECIAL) ) { // make it special - which basically just means that its true until the next wave arrives mission_event_set_directive_special(event); Directive_count = 0; } else if ( (Mission_events[event].flags & MEF_DIRECTIVE_SPECIAL) && Directive_count > 1 ) { // make it non special mission_event_unset_directive_special(event); } if (Mission_events[event].count || (Directive_count > 1)){ Mission_events[event].count = Directive_count; } if (Sexp_useful_number){ Mission_events[event].flags |= MEF_CURRENT; } if ((Mission_events[event].mission_log_flags != 0) || Snapshot_all_events){ maybe_write_to_event_log(result); } } Log_event = false; Event_index = -1; Mission_events[event].result = result; // if the sexpression is known false, then no need to evaluate anymore if ((sindex >= 0) && (Sexp_nodes[sindex].value == SEXP_KNOWN_FALSE)) { Mission_events[event].timestamp = (int) Missiontime; Mission_events[event].satisfied_time = Missiontime; // _argv[-1] - repeat_count of -1 would mean repeat indefinitely, so set to 0 instead. Mission_events[event].repeat_count = 0; Mission_events[event].formula = -1; return; } if (result && !Mission_events[event].satisfied_time) { Mission_events[event].satisfied_time = Missiontime; if ( Mission_events[event].objective_text ) { Mission_directive_sound_timestamp = timestamp(DIRECTIVE_SOUND_DELAY); } } // decrement the trigger count. When at 0, set the repeat count to 0 so we don't eval this function anymore if (result && (Mission_events[event].trigger_count != 0) && (Mission_events[event].flags & MEF_USING_TRIGGER_COUNT) ) { if (Mission_events[event].trigger_count > 0) Mission_events[event].trigger_count--; if (Mission_events[event].trigger_count == 0) { Mission_events[event].repeat_count = 0; } else { bump_timestamp = true; } } // decrement the repeat count. When at 0, don't eval this function anymore if ( result || timestamp_valid(Mission_events[event].timestamp) ) { // _argv[-1] - negative repeat count means repeat indefinitely. if ( Mission_events[event].repeat_count > 0 ) Mission_events[event].repeat_count--; if ( Mission_events[event].repeat_count == 0 ) { Mission_events[event].timestamp = (int)Missiontime; Mission_events[event].formula = -1; if(Game_mode & GM_MULTIPLAYER){ // multiplayer missions (scoring is scaled in the multi_team_maybe_add_score() function) multi_team_maybe_add_score(Mission_events[event].score, Mission_events[event].team); multi_player_maybe_add_score(Mission_events[event].score, Mission_events[event].team); } else { // deal with the player's score Player->stats.m_score += (int)(Mission_events[event].score * scoring_get_scale_factor()); } } // Set the timestamp for the next check on this event unless we only have a trigger count and no repeat count and // this event didn't trigger this frame. else if (bump_timestamp || (!((Mission_events[event].repeat_count == -1) && (Mission_events[event].flags & MEF_USING_TRIGGER_COUNT) && (Mission_events[event].trigger_count != 0)))) { // set the timestamp to time out 'interval' seconds in the future. Mission_events[event].timestamp = timestamp( Mission_events[event].interval * 1000 ); } } // see if anything has changed if(MULTIPLAYER_MASTER && ((store_flags != Mission_events[event].flags) || (store_formula != Mission_events[event].formula) || (store_result != Mission_events[event].result) || (store_count != Mission_events[event].count)) ){ send_event_update_packet(event); } }
// function which evaluates and processes the given event void mission_process_event( int event ) { int store_flags = Mission_events[event].flags; int store_formula = Mission_events[event].formula; int store_result = Mission_events[event].result; int store_count = Mission_events[event].count; int result, sindex; Directive_count = 0; Event_index = event; sindex = Mission_events[event].formula; result = Mission_events[event].result; // if chained, insure that previous event is true and next event is false if (Mission_events[event].chain_delay >= 0) { // this indicates it's chained if (event > 0){ if (!Mission_events[event - 1].result || ((fix) Mission_events[event - 1].timestamp + i2f(Mission_events[event].chain_delay) > Missiontime)){ sindex = -1; // bypass evaluation } } if ((event < Num_mission_events - 1) && Mission_events[event + 1].result && (Mission_events[event + 1].chain_delay >= 0)){ sindex = -1; // bypass evaluation } } if (sindex >= 0) { Sexp_useful_number = 1; result = eval_sexp(sindex); // if the directive count is a special value, deal with that first. Mark the event as a special // event, and unmark it when the directive is true again. if ( (Directive_count == DIRECTIVE_WING_ZERO) && !(Mission_events[event].flags & MEF_DIRECTIVE_SPECIAL) ) { // make it special - which basically just means that its true until the next wave arrives mission_event_set_directive_special(event); Directive_count = 0; } else if ( (Mission_events[event].flags & MEF_DIRECTIVE_SPECIAL) && Directive_count > 1 ) { // make it non special mission_event_unset_directive_special(event); } if (Mission_events[event].count || (Directive_count > 1)){ Mission_events[event].count = Directive_count; } if (Sexp_useful_number){ Mission_events[event].flags |= MEF_CURRENT; } } Event_index = 0; Mission_events[event].result = result; // if the sexpression is known false, then no need to evaluate anymore if ((sindex >= 0) && (Sexp_nodes[sindex].value == SEXP_KNOWN_FALSE)) { Mission_events[event].timestamp = (int) Missiontime; Mission_events[event].satisfied_time = Missiontime; Mission_events[event].repeat_count = -1; Mission_events[event].formula = -1; return; } if (result && !Mission_events[event].satisfied_time) { Mission_events[event].satisfied_time = Missiontime; if ( Mission_events[event].objective_text ) { Mission_directive_sound_timestamp = timestamp(DIRECTIVE_SOUND_DELAY); } } // decrement the repeat count. When at 0, don't eval this function anymore if ( result || timestamp_valid(Mission_events[event].timestamp) ) { Mission_events[event].repeat_count--; if ( Mission_events[event].repeat_count <= 0 ) { Mission_events[event].timestamp = (int)Missiontime; Mission_events[event].formula = -1; if(Game_mode & GM_MULTIPLAYER){ // squad war multi_team_maybe_add_score((int)(Mission_events[event].score * scoring_get_scale_factor()), Mission_events[event].team); } else { // deal with the player's score Player->stats.m_score += (int)(Mission_events[event].score * scoring_get_scale_factor()); } } else { // set the timestamp to time out 'interval' seconds in the future. We must also reset the // value at the sexpresion node to unknown so that it will get reevaled Mission_events[event].timestamp = timestamp( Mission_events[event].interval * 1000 ); // Sexp_nodes[Mission_events[event].formula].value = SEXP_UNKNOWN; } } // see if anything has changed if(MULTIPLAYER_MASTER && ((store_flags != Mission_events[event].flags) || (store_formula != Mission_events[event].formula) || (store_result != Mission_events[event].result) || (store_count != Mission_events[event].count)) ){ send_event_update_packet(event); } }