// called from the game loop to check if we should actually do the red-alert void red_alert_maybe_move_to_next_mission() { // if the timestamp is invalid, do nothing. if ( !timestamp_valid(Red_alert_new_mission_timestamp) ) return; // return if the timestamp hasn't elapsed yet if ( !timestamp_elapsed(Red_alert_new_mission_timestamp) ) return; // basic premise here is to stop the current mission, and then set the next mission in the campaign // which better be a red alert mission if ( Game_mode & GM_CAMPAIGN_MODE ) { red_alert_store_wingman_status(); mission_goal_fail_incomplete(); mission_campaign_store_goals_and_events_and_variables(); scoring_level_close(); mission_campaign_eval_next_mission(); mission_campaign_mission_over(); // CD CHECK gameseq_post_event(GS_EVENT_START_GAME); } else { gameseq_post_event(GS_EVENT_END_GAME); } }
// ---------------------------------------------------------------------------- // update_reduced_damp_timestamp() // void update_reduced_damp_timestamp( physics_info *pi, float impulse ) { // Compute duration of reduced damp from present // Compare with current value and increase if greater, otherwise ignore int reduced_damp_decay_time; reduced_damp_decay_time = (int) (REDUCED_DAMP_TIME * impulse / (REDUCED_DAMP_VEL * pi->mass)); if ( reduced_damp_decay_time > REDUCED_DAMP_TIME ) reduced_damp_decay_time = REDUCED_DAMP_TIME; // Reset timestamp if larger than current (if any) if ( timestamp_valid( pi->reduced_damp_decay ) ) { int time_left = timestamp_until( pi->reduced_damp_decay ); if ( time_left > 0 ) { // increment old time, but apply cap int new_time = reduced_damp_decay_time + time_left; if ( new_time < REDUCED_DAMP_TIME ) { pi->reduced_damp_decay = timestamp( new_time ); } } else { pi->reduced_damp_decay = timestamp( reduced_damp_decay_time ); } } else { // set if not valid pi->reduced_damp_decay = timestamp( reduced_damp_decay_time ); } }
/** * Render debris */ void debris_render(object * obj) { int i, num, swapped; polymodel *pm; debris *db; swapped = -1; pm = NULL; num = obj->instance; Assert(num >= 0 && num < MAX_DEBRIS_PIECES); db = &Debris[num]; Assert(db->flags & DEBRIS_USED); texture_info *tbase = NULL; model_clear_instance( db->model_num ); // Swap in a different texture depending on the species if (db->species >= 0) { pm = model_get( db->model_num ); //WMC - Someday, we should have glowing debris. if ( pm != NULL && (pm->n_textures == 1) ) { tbase = &pm->maps[0].textures[TM_BASE_TYPE]; swapped = tbase->GetTexture(); tbase->SetTexture(Species_info[db->species].debris_texture.bitmap_id); } } // Only render electrical arcs if within 500m of the eye (for a 10m piece) if ( vm_vec_dist_quick( &obj->pos, &Eye_position ) < obj->radius*50.0f ) { for (i=0; i<MAX_DEBRIS_ARCS; i++ ) { if ( timestamp_valid( db->arc_timestamp[i] ) ) { model_add_arc( db->model_num, db->submodel_num, &db->arc_pts[i][0], &db->arc_pts[i][1], MARC_TYPE_NORMAL ); } } } if ( db->is_hull ) { MONITOR_INC(NumHullDebrisRend,1); submodel_render( db->model_num, db->submodel_num, &obj->orient, &obj->pos ); } else { MONITOR_INC(NumSmallDebrisRend,1); submodel_render( db->model_num, db->submodel_num, &obj->orient, &obj->pos, MR_NO_LIGHTING ); } if (tbase != NULL && (swapped!=-1) && pm) { tbase->SetTexture(swapped); } }
/** * Do various updates to debris: check if time to die, start fireballs * Maybe delete debris if it's very far away from player. * * @param obj pointer to debris object * @param frame_time time elapsed since last debris_move() called */ void debris_process_post(object * obj, float frame_time) { int i, num; num = obj->instance; int objnum = OBJ_INDEX(obj); Assert( Debris[num].objnum == objnum ); debris *db = &Debris[num]; if ( db->is_hull ) { MONITOR_INC(NumHullDebris,1); radar_plot_object( obj ); if ( timestamp_elapsed(db->sound_delay) ) { obj_snd_assign(objnum, SND_DEBRIS, &vmd_zero_vector, 0); db->sound_delay = 0; } } else { MONITOR_INC(NumSmallDebris,1); } if ( db->lifeleft >= 0.0f) { db->lifeleft -= frame_time; if ( db->lifeleft < 0.0f ) { debris_start_death_roll(obj, db); } } maybe_delete_debris(db); // Make this debris go away if it's very far away. // ================== DO THE ELECTRIC ARCING STUFF ===================== if ( db->arc_frequency <= 0 ) { return; // If arc_frequency <= 0, this piece has no arcs on it } if ( !timestamp_elapsed(db->fire_timeout) && timestamp_elapsed(db->next_fireball)) { db->next_fireball = timestamp_rand(db->arc_frequency,db->arc_frequency*2 ); db->arc_frequency += 100; if (db->is_hull) { int n, n_arcs = ((rand()>>5) % 3)+1; // Create 1-3 sparks vec3d v1, v2, v3, v4; if ( Cmdline_old_collision_sys ) { submodel_get_two_random_points( db->model_num, db->submodel_num, &v1, &v2 ); submodel_get_two_random_points( db->model_num, db->submodel_num, &v3, &v4 ); } else { submodel_get_two_random_points_better( db->model_num, db->submodel_num, &v1, &v2 ); submodel_get_two_random_points_better( db->model_num, db->submodel_num, &v3, &v4 ); } n = 0; int a = 100, b = 1000; int lifetime = (myrand()%((b)-(a)+1))+(a); // Create the spark effects for (i=0; i<MAX_DEBRIS_ARCS; i++ ) { if ( !timestamp_valid( db->arc_timestamp[i] ) ) { db->arc_timestamp[i] = timestamp(lifetime); // live up to a second switch( n ) { case 0: db->arc_pts[i][0] = v1; db->arc_pts[i][1] = v2; break; case 1: db->arc_pts[i][0] = v2; db->arc_pts[i][1] = v3; break; case 2: db->arc_pts[i][0] = v2; db->arc_pts[i][1] = v4; break; default: Int3(); } n++; if ( n == n_arcs ) break; // Don't need to create anymore } } // rotate v2 out of local coordinates into world. // Use v2 since it is used in every bolt. See above switch(). vec3d snd_pos; vm_vec_unrotate(&snd_pos, &v2, &obj->orient); vm_vec_add2(&snd_pos, &obj->pos ); //Play a sound effect if ( lifetime > 750 ) { // 1.00 second effect snd_play_3d( gamesnd_get_game_sound(SND_DEBRIS_ARC_05), &snd_pos, &View_position, obj->radius ); } else if ( lifetime > 500 ) { // 0.75 second effect snd_play_3d( gamesnd_get_game_sound(SND_DEBRIS_ARC_04), &snd_pos, &View_position, obj->radius ); } else if ( lifetime > 250 ) { // 0.50 second effect snd_play_3d( gamesnd_get_game_sound(SND_DEBRIS_ARC_03), &snd_pos, &View_position, obj->radius ); } else if ( lifetime > 100 ) { // 0.25 second effect snd_play_3d( gamesnd_get_game_sound(SND_DEBRIS_ARC_02), &snd_pos, &View_position, obj->radius ); } else { // 0.10 second effect snd_play_3d( gamesnd_get_game_sound(SND_DEBRIS_ARC_01), &snd_pos, &View_position, obj->radius ); } }
// called from HUD code to see if we're red-alerting int red_alert_in_progress() { // it is specifically a question of whether the timestamp is running return timestamp_valid(Red_alert_new_mission_timestamp); }
// 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); } }
void mission_eval_goals() { int i, result; // before checking whether or not we should evaluate goals, we should run through the events and // process any whose timestamp is valid and has expired. This would catch repeating events only for (i=0; i<Num_mission_events; i++) { if (Mission_events[i].formula != -1) { if ( !timestamp_valid(Mission_events[i].timestamp) || !timestamp_elapsed(Mission_events[i].timestamp) ){ continue; } // if we get here, then the timestamp on the event has popped -- we should reevaluate PROFILE("Repeating events", mission_process_event(i)); } } if ( !timestamp_elapsed(Mission_goal_timestamp) ){ return; } // first evaluate the players goals for (i=0; i<Num_goals; i++) { // don't evaluate invalid goals if (Mission_goals[i].type & INVALID_GOAL){ continue; } if (Mission_goals[i].satisfied == GOAL_INCOMPLETE) { result = eval_sexp(Mission_goals[i].formula); if ( Sexp_nodes[Mission_goals[i].formula].value == SEXP_KNOWN_FALSE ) { mission_goal_status_change( i, GOAL_FAILED ); } else if (result) { mission_goal_status_change(i, GOAL_COMPLETE ); } // end if result } // end if goals[i].satsified != GOAL_COMPLETE } // end for // now evaluate any mission events for (i=0; i<Num_mission_events; i++) { if ( Mission_events[i].formula != -1 ) { // only evaluate this event if the timestamp is not valid. We do this since // we will evaluate repeatable events at the top of the file so we can get // the exact interval that the designer asked for. if ( !timestamp_valid( Mission_events[i].timestamp) ){ PROFILE("Nonrepeating events", mission_process_event( i )); } } } // send and remaining sexp data to the clients if (MULTIPLAYER_MASTER) { multi_sexp_flush_packet(); } if (The_mission.game_type & MISSION_TYPE_TRAINING){ Mission_goal_timestamp = timestamp(GOAL_TIMESTAMP_TRAINING); } else { Mission_goal_timestamp = timestamp(GOAL_TIMESTAMP); } if ( !hud_disabled() && hud_gauge_active(HUD_DIRECTIVES_VIEW) ) { mission_maybe_play_directive_success_sound(); } // update goal status if playing on a multiplayer standalone server if (Game_mode & GM_STANDALONE_SERVER){ std_multi_update_goals(); } Snapshot_all_events = false; }
void mission_eval_goals() { int i, result, goal_changed = 0; // before checking whether or not we should evaluate goals, we should run through the events and // process any whose timestamp is valid and has expired. This would catch repeating events only for (i=0; i<Num_mission_events; i++) { if (Mission_events[i].formula != -1) { if ( !timestamp_valid(Mission_events[i].timestamp) || !timestamp_elapsed(Mission_events[i].timestamp) ){ continue; } // if we get here, then the timestamp on the event has popped -- we should reevaluate mission_process_event(i); } } if ( !timestamp_elapsed(Mission_goal_timestamp) ){ return; } // first evaluate the players goals for (i=0; i<Num_goals; i++) { // don't evaluate invalid goals if (Mission_goals[i].type & INVALID_GOAL){ continue; } if (Mission_goals[i].satisfied == GOAL_INCOMPLETE) { result = eval_sexp(Mission_goals[i].formula); if ( Sexp_nodes[Mission_goals[i].formula].value == SEXP_KNOWN_FALSE ) { goal_changed = 1; mission_goal_status_change( i, GOAL_FAILED ); } else if (result) { goal_changed = 1; mission_goal_status_change(i, GOAL_COMPLETE ); } // end if result // tell the player how to end the mission //if ( goal_changed && mission_evaluate_primary_goals() != PRIMARY_GOALS_INCOMPLETE ) { // HUD_sourced_printf(HUD_SOURCE_IMPORTANT, "Press %s to end mission and return to base", textify_scancode(Control_config[END_MISSION].key_id) ); //} } // end if goals[i].satsified != GOAL_COMPLETE } // end for // now evaluate any mission events for (i=0; i<Num_mission_events; i++) { if ( Mission_events[i].formula != -1 ) { // only evaluate this event if the timestamp is not valid. We do this since // we will evaluate repeatable events at the top of the file so we can get // the exact interval that the designer asked for. if ( !timestamp_valid( Mission_events[i].timestamp) ){ mission_process_event( i ); } } } if (The_mission.game_type & MISSION_TYPE_TRAINING){ Mission_goal_timestamp = timestamp(GOAL_TIMESTAMP_TRAINING); } else { Mission_goal_timestamp = timestamp(GOAL_TIMESTAMP); } if ( !hud_disabled() && hud_gauge_active(HUD_DIRECTIVES_VIEW) ) { mission_maybe_play_directive_success_sound(); } // update goal status if playing on a multiplayer standalone server if (Game_mode & GM_STANDALONE_SERVER){ std_multi_update_goals(); } }
// 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); } }