// draw a frame of the rotating lock triangles animation void HudGaugeLock::renderLockTriangles(int center_x, int center_y, float frametime) { if ( Lock_anim.first_frame == -1 ) { renderLockTrianglesOld(center_x, center_y, Lock_target_box_width/2); } else { // render the anim Lock_anim.sx = center_x - Lockspin_half_w; Lock_anim.sy = center_y - Lockspin_half_h; // if it's still animating if(Lock_anim.time_elapsed < Lock_anim.total_time){ if(loop_locked_anim) { hud_anim_render(&Lock_anim, frametime, 1, 1, 0); } else { hud_anim_render(&Lock_anim, frametime, 1, 0, 1); } } else { // if the timestamp is unset or expired if((Lock_gauge_draw_stamp < 0) || timestamp_elapsed(Lock_gauge_draw_stamp)){ // reset timestamp Lock_gauge_draw_stamp = timestamp(1000 / (2 * LOCK_GAUGE_BLINK_RATE)); // switch between draw and don't-draw Lock_gauge_draw = !Lock_gauge_draw; } // maybe draw the anim Lock_gauge.time_elapsed = 0.0f; if(Lock_gauge_draw){ if ( loop_locked_anim ) { hud_anim_render(&Lock_anim, frametime, 1, 1, 0); } else { hud_anim_render(&Lock_anim, frametime, 1, 0, 1); } } } } }
/** * HudGaugeMessages::render() will display the active HUD messages on the HUD. It will scroll * the messages up when a new message arrives. */ void HudGaugeMessages::render(float frametime) { hud_set_default_color(); // dependant on max_width, max_lines, and line_height setClip(position[0], position[1], Window_width, Window_height+2); for ( SCP_vector<Hud_display_info>::iterator m = active_messages.begin(); m != active_messages.end(); ++m) { if ( !timestamp_elapsed(m->total_life) ) { if ( !(Player->flags & PLAYER_FLAGS_MSG_MODE) || !Hidden_by_comms_menu) { // set the appropriate color if ( m->msg.source ) { setGaugeColor(HUD_C_BRIGHT); } else { setGaugeColor(); } // print the message out renderPrintf(m->msg.x, m->y, "%s", m->msg.text.c_str()); } } } }
// blip is for a target immune to sensors, so cause to flicker in/out with mild distortion void HudGaugeRadarOrb::blipDrawFlicker(blip *b, vec3d *pos) { int flicker_index; float dist=vm_vec_normalize(pos); vec3d out; float distortion_angle=10; if ( (b-Blips) & 1 ) { flicker_index=0; } else { flicker_index=1; } if ( timestamp_elapsed(Radar_flicker_timer[flicker_index]) ) { Radar_flicker_timer[flicker_index] = timestamp_rand(50,1000); Radar_flicker_on[flicker_index] ^= 1; } if ( !Radar_flicker_on[flicker_index] ) { return; } if ( rand() & 1 ) { distortion_angle *= frand_range(0.1f,2.0f); dist *= frand_range(0.75f, 1.25f); if (dist > 1.25f) dist = 1.25f; if (dist < 0.75f) dist = 0.75f; } vm_vec_random_cone(&out,pos,distortion_angle); vm_vec_scale(&out,dist); drawContactHtl(&out,b->rad); }
void HudGaugeThreatIndicator::renderLaserThreat() { int frame_offset, num_frames; //Check how many frames the ani actually has num_frames = laser_warn.num_frames; //We need at least two frames here Assert( num_frames >= 2 ); if ( Player->threat_flags & THREAT_DUMBFIRE ) { if ( timestamp_elapsed(laser_warn_timer) ) { laser_warn_timer = timestamp(THREAT_DUMBFIRE_FLASH); laser_warn_frame++; if ( laser_warn_frame > (num_frames - 1) ) { //The first frame being the default "off" setting, we need to cycle through all the other frames laser_warn_frame = 1; } } frame_offset = laser_warn_frame; } else { frame_offset = 0; } renderBitmap(laser_warn.first_frame + frame_offset, position[0] + Laser_warn_offsets[0], position[1] + Laser_warn_offsets[1]); }
// blip is for a target immune to sensors, so cause to flicker in/out with mild distortion void HudGaugeRadarDradis::blipDrawFlicker(blip *b, vec3d *pos, float alpha) { int flicker_index; float dist=vm_vec_normalize(pos); vec3d out; float distortion_angle=10; if ((b-Blips) & 1) flicker_index=0; else flicker_index=1; if (timestamp_elapsed(Radar_flicker_timer[flicker_index])) { Radar_flicker_timer[flicker_index] = timestamp_rand(50,1000); Radar_flicker_on[flicker_index] ^= 1; } if (!Radar_flicker_on[flicker_index]) return; if (rand() & 1) { distortion_angle *= frand_range(0.1f,2.0f); dist *= frand_range(0.75f, 1.25f); if (dist > 1.0f) dist = 1.0f; if (dist < 0.1f) dist = 0.1f; } vm_vec_random_cone(&out,pos,distortion_angle); vm_vec_scale(&out,dist); drawContact(&out, -1, unknown_contact_icon, b->dist, alpha, 1.0f); }
// function looks at the flying controls and the current velocity to determine a goal velocity // function determines velocity in object's reference frame and goal velocity in object's reference frame void physics_read_flying_controls( matrix * orient, physics_info * pi, control_info * ci, float sim_time, vec3d *wash_rot) { vec3d goal_vel; // goal velocity in local coords, *not* accounting for ramping of velcity float ramp_time_const; // time constant for velocity ramping // apply throttle, unless reverse thrusters are held down if (ci->forward != -1.0f) ci->forward += (ci->forward_cruise_percent / 100.0f); // give control input to cause rotation in engine wash extern int Wash_on; if ( wash_rot && Wash_on ) { ci->pitch += wash_rot->xyz.x; ci->bank += wash_rot->xyz.z; ci->heading += wash_rot->xyz.y; } if (ci->pitch > 1.0f ) ci->pitch = 1.0f; else if (ci->pitch < -1.0f ) ci->pitch = -1.0f; if (ci->vertical > 1.0f ) ci->vertical = 1.0f; else if (ci->vertical < -1.0f ) ci->vertical = -1.0f; if (ci->heading > 1.0f ) ci->heading = 1.0f; else if (ci->heading < -1.0f ) ci->heading = -1.0f; if (ci->sideways > 1.0f ) ci->sideways = 1.0f; else if (ci->sideways < -1.0f ) ci->sideways = -1.0f; if (ci->bank > 1.0f ) ci->bank = 1.0f; else if (ci->bank < -1.0f ) ci->bank = -1.0f; if ( pi->flags & PF_AFTERBURNER_ON ){ //SparK: modifield to accept reverse burners if (!(pi->afterburner_max_reverse_vel > 0.0f)){ ci->forward = 1.0f; } } if (ci->forward > 1.0f ) ci->forward = 1.0f; else if (ci->forward < -1.0f ) ci->forward = -1.0f; if (!Flight_controls_follow_eyepoint_orientation || (Player_obj == NULL) || (Player_obj->type != OBJ_SHIP)) { // Default behavior; eyepoint orientation has no effect on controls pi->desired_rotvel.xyz.x = ci->pitch * pi->max_rotvel.xyz.x; pi->desired_rotvel.xyz.y = ci->heading * pi->max_rotvel.xyz.y; } else { // Optional behavior; pitch and yaw are always relative to the eyepoint // orientation (excluding slew) vec3d tmp_vec, new_rotvel; matrix tmp_mat, eyemat, rotvelmat; ship_get_eye(&tmp_vec, &eyemat, Player_obj, false); vm_copy_transpose_matrix(&tmp_mat, &Player_obj->orient); vm_matrix_x_matrix(&rotvelmat, &tmp_mat, &eyemat); vm_vec_rotate(&new_rotvel, &pi->max_rotvel, &rotvelmat); vm_vec_unrotate(&tmp_vec, &pi->max_rotvel, &rotvelmat); new_rotvel.xyz.x = tmp_vec.xyz.x; new_rotvel.xyz.x = ci->pitch * new_rotvel.xyz.x; new_rotvel.xyz.y = ci->heading * new_rotvel.xyz.y; vm_vec_unrotate(&tmp_vec, &new_rotvel, &rotvelmat); pi->desired_rotvel = tmp_vec; } float delta_bank; #ifdef BANK_WHEN_TURN // To change direction of bank, negate the whole expression. // To increase magnitude of banking, decrease denominator. // Adam: The following statement is all the math for banking while turning. delta_bank = - (ci->heading * pi->max_rotvel.xyz.y) * pi->delta_bank_const; #else delta_bank = 0.0f; #endif pi->desired_rotvel.xyz.z = ci->bank * pi->max_rotvel.xyz.z + delta_bank; pi->forward_thrust = ci->forward; pi->vert_thrust = ci->vertical; //added these two in order to get side and forward thrusters pi->side_thrust = ci->sideways; //to glow brighter when the ship is moving in the right direction -Bobboau if ( pi->flags & PF_AFTERBURNER_ON ) { goal_vel.xyz.x = ci->sideways*pi->afterburner_max_vel.xyz.x; goal_vel.xyz.y = ci->vertical*pi->afterburner_max_vel.xyz.y; if(ci->forward < 0.0f) goal_vel.xyz.z = ci->forward* pi->afterburner_max_reverse_vel; else goal_vel.xyz.z = ci->forward* pi->afterburner_max_vel.xyz.z; } else if ( pi->flags & PF_BOOSTER_ON ) { goal_vel.xyz.x = ci->sideways*pi->booster_max_vel.xyz.x; goal_vel.xyz.y = ci->vertical*pi->booster_max_vel.xyz.y; goal_vel.xyz.z = ci->forward* pi->booster_max_vel.xyz.z; } else { goal_vel.xyz.x = ci->sideways*pi->max_vel.xyz.x; goal_vel.xyz.y = ci->vertical*pi->max_vel.xyz.y; goal_vel.xyz.z = ci->forward* pi->max_vel.xyz.z; } if ( goal_vel.xyz.z < -pi->max_rear_vel && !(pi->flags & PF_AFTERBURNER_ON) ) goal_vel.xyz.z = -pi->max_rear_vel; if ( pi->flags & PF_ACCELERATES ) { // // Determine *resultant* DESIRED VELOCITY (desired_vel) accounting for RAMPING of velocity // Use LOCAL coordinates // if slide_enabled, ramp velocity for x and y, otherwise set goal (0) // always ramp velocity for z // // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast. // Scale according to reduced_damp_time_expansion. float reduced_damp_ramp_time_expansion; if ( pi->flags & PF_REDUCED_DAMP && !timestamp_elapsed(pi->reduced_damp_decay) ) { float reduced_damp_fraction_time_left = timestamp_until( pi->reduced_damp_decay ) / (float) REDUCED_DAMP_TIME; reduced_damp_ramp_time_expansion = 1.0f + (REDUCED_DAMP_FACTOR-1) * reduced_damp_fraction_time_left; } else { reduced_damp_ramp_time_expansion = 1.0f; } if (pi->flags & PF_SLIDE_ENABLED) { // determine the local velocity // deterimine whether accelerating or decleration toward goal for x if ( goal_vel.xyz.x > 0.0f ) { if ( goal_vel.xyz.x >= pi->prev_ramp_vel.xyz.x ) ramp_time_const = pi->slide_accel_time_const; else ramp_time_const = pi->slide_decel_time_const; } else if ( goal_vel.xyz.x < 0.0f ) { if ( goal_vel.xyz.x <= pi->prev_ramp_vel.xyz.x ) ramp_time_const = pi->slide_accel_time_const; else ramp_time_const = pi->slide_decel_time_const; } else { ramp_time_const = pi->slide_decel_time_const; } // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast if ( pi->flags & PF_REDUCED_DAMP ) { ramp_time_const *= reduced_damp_ramp_time_expansion; } pi->prev_ramp_vel.xyz.x = velocity_ramp(pi->prev_ramp_vel.xyz.x, goal_vel.xyz.x, ramp_time_const, sim_time); // deterimine whether accelerating or decleration toward goal for y if ( goal_vel.xyz.y > 0.0f ) { if ( goal_vel.xyz.y >= pi->prev_ramp_vel.xyz.y ) ramp_time_const = pi->slide_accel_time_const; else ramp_time_const = pi->slide_decel_time_const; } else if ( goal_vel.xyz.y < 0.0f ) { if ( goal_vel.xyz.y <= pi->prev_ramp_vel.xyz.y ) ramp_time_const = pi->slide_accel_time_const; else ramp_time_const = pi->slide_decel_time_const; } else { ramp_time_const = pi->slide_decel_time_const; } // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast if ( pi->flags & PF_REDUCED_DAMP ) { ramp_time_const *= reduced_damp_ramp_time_expansion; } pi->prev_ramp_vel.xyz.y = velocity_ramp( pi->prev_ramp_vel.xyz.y, goal_vel.xyz.y, ramp_time_const, sim_time); } else { // slide not enabled pi->prev_ramp_vel.xyz.x = 0.0f; pi->prev_ramp_vel.xyz.y = 0.0f; } // deterimine whether accelerating or decleration toward goal for z if ( goal_vel.xyz.z > 0.0f ) { if ( goal_vel.xyz.z >= pi->prev_ramp_vel.xyz.z ) { if ( pi->flags & PF_AFTERBURNER_ON ) ramp_time_const = pi->afterburner_forward_accel_time_const; else if (pi->flags & PF_BOOSTER_ON) ramp_time_const = pi->booster_forward_accel_time_const; else ramp_time_const = pi->forward_accel_time_const; } else { ramp_time_const = pi->forward_decel_time_const; } } else if ( goal_vel.xyz.z < 0.0f ) { if ( pi->flags & PF_AFTERBURNER_ON ) ramp_time_const = pi->afterburner_reverse_accel; else ramp_time_const = pi->forward_decel_time_const; } else { ramp_time_const = pi->forward_decel_time_const; } // If reduced damp in effect, then adjust ramp_velocity and desired_velocity can not change as fast if ( pi->flags & PF_REDUCED_DAMP ) { ramp_time_const *= reduced_damp_ramp_time_expansion; } pi->prev_ramp_vel.xyz.z = velocity_ramp(pi->prev_ramp_vel.xyz.z, goal_vel.xyz.z, ramp_time_const, sim_time); //Deternine the current dynamic glide cap, and ramp to it //This is outside the normal "glide" block since we want the cap to adjust whether or not the ship is in glide mode float dynamic_glide_cap_goal = 0.0; if (pi->flags & PF_AFTERBURNER_ON) { dynamic_glide_cap_goal = ( goal_vel.xyz.z >= 0.0f ) ? pi->afterburner_max_vel.xyz.z : pi->afterburner_max_reverse_vel; } else { //Use the maximum value in X, Y, and Z (including overclocking) dynamic_glide_cap_goal = MAX(MAX(pi->max_vel.xyz.x,pi->max_vel.xyz.y), pi->max_vel.xyz.z); } pi->cur_glide_cap = velocity_ramp(pi->cur_glide_cap, dynamic_glide_cap_goal, ramp_time_const, sim_time); if ( (pi->flags & PF_GLIDING) || (pi->flags & PF_FORCE_GLIDE ) ) { pi->desired_vel = pi->vel; //SUSHI: A (hopefully better) approach to dealing with accelerations in glide mode //Get *actual* current velocities along each axis and use those instead of ramped velocities vec3d local_vel; vm_vec_rotate(&local_vel, &pi->vel, orient); //Having pi->glide_cap == 0 means we're using a dynamic glide cap float curGlideCap = 0.0f; if (pi->glide_cap == 0.0f) curGlideCap = pi->cur_glide_cap; else curGlideCap = pi->glide_cap; //If we're near the (positive) glide cap, decay velocity where we aren't thrusting //This is a hack, but makes the flight feel a lot smoother //Don't do this if we aren't applying any thrust, we have no glide cap, or the accel multiplier is 0 (no thrust while gliding) float cap_decay_threshold = 0.95f; float cap_decay_amount = 0.2f; if (curGlideCap >= 0.0f && vm_vec_mag(&pi->desired_vel) >= cap_decay_threshold * curGlideCap && vm_vec_mag(&goal_vel) > 0.0f && pi->glide_accel_mult != 0.0f) { if (goal_vel.xyz.x == 0.0f) vm_vec_scale_add2(&pi->desired_vel, &orient->vec.rvec, -cap_decay_amount * local_vel.xyz.x); if (goal_vel.xyz.y == 0.0f) vm_vec_scale_add2(&pi->desired_vel, &orient->vec.uvec, -cap_decay_amount * local_vel.xyz.y); if (goal_vel.xyz.z == 0.0f) vm_vec_scale_add2(&pi->desired_vel, &orient->vec.fvec, -cap_decay_amount * local_vel.xyz.z); } //The glide_ramp function uses (basically) the same math as the velocity ramp so that thruster power is consistent //Only ramp if the glide cap is positive float xVal = glide_ramp(local_vel.xyz.x, goal_vel.xyz.x, pi->slide_accel_time_const, pi->glide_accel_mult, sim_time); float yVal = glide_ramp(local_vel.xyz.y, goal_vel.xyz.y, pi->slide_accel_time_const, pi->glide_accel_mult, sim_time); float zVal = 0.0; if (pi->flags & PF_AFTERBURNER_ON) zVal = glide_ramp(local_vel.xyz.z, goal_vel.xyz.z, pi->afterburner_forward_accel_time_const, pi->glide_accel_mult, sim_time); else { if (goal_vel.xyz.z >= 0.0f) zVal = glide_ramp(local_vel.xyz.z, goal_vel.xyz.z, pi->forward_accel_time_const, pi->glide_accel_mult, sim_time); else zVal = glide_ramp(local_vel.xyz.z, goal_vel.xyz.z, pi->forward_decel_time_const, pi->glide_accel_mult, sim_time); } //Compensate for effect of dampening: normal flight cheats here, so /we make up for it this way so glide acts the same way xVal *= pi->side_slip_time_const / sim_time; yVal *= pi->side_slip_time_const / sim_time; if (pi->use_newtonian_damp) zVal *= pi->side_slip_time_const / sim_time; vm_vec_scale_add2(&pi->desired_vel, &orient->vec.fvec, zVal); vm_vec_scale_add2(&pi->desired_vel, &orient->vec.rvec, xVal); vm_vec_scale_add2(&pi->desired_vel, &orient->vec.uvec, yVal); // Only do the glide cap if we have one and are actively thrusting in some direction. if ( curGlideCap >= 0.0f && (ci->forward != 0.0f || ci->sideways != 0.0f || ci->vertical != 0.0f) ) { float currentmag = vm_vec_mag(&pi->desired_vel); if ( currentmag > curGlideCap ) { vm_vec_scale( &pi->desired_vel, curGlideCap / currentmag ); } } } else { // this translates local desired velocities to world velocities vm_vec_zero(&pi->desired_vel); vm_vec_scale_add2( &pi->desired_vel, &orient->vec.rvec, pi->prev_ramp_vel.xyz.x ); vm_vec_scale_add2( &pi->desired_vel, &orient->vec.uvec, pi->prev_ramp_vel.xyz.y ); vm_vec_scale_add2( &pi->desired_vel, &orient->vec.fvec, pi->prev_ramp_vel.xyz.z ); } } else // object does not accelerate (PF_ACCELERATES not set) pi->desired_vel = pi->vel; }
void credits_do_frame(float frametime) { GR_DEBUG_SCOPE("Credits do frame"); int i, k, next, percent, bm1, bm2; int bx1, by1, bw1, bh1; int bx2, by2, bw2, bh2; // Use this id to trigger the start of music playing on the credits screen if ( timestamp_elapsed(Credits_music_begin_timestamp) ) { Credits_music_begin_timestamp = 0; credits_start_music(); } k = Ui_window.process(); switch (k) { case KEY_ESC: gameseq_post_event(GS_EVENT_MAIN_MENU); key_flush(); break; case KEY_CTRLED | KEY_UP: case KEY_SHIFTED | KEY_TAB: if ( !(Player->flags & PLAYER_FLAGS_IS_MULTI) ) { credits_screen_button_pressed(CUTSCENES_BUTTON); break; } // else, react like tab key. case KEY_CTRLED | KEY_DOWN: case KEY_TAB: credits_screen_button_pressed(TECH_DATABASE_BUTTON); break; default: break; } // end switch for (i=0; i<NUM_BUTTONS; i++){ if (Buttons[i][gr_screen.res].button.pressed()){ if (credits_screen_button_pressed(i)){ return; } } } gr_reset_clip(); GR_MAYBE_CLEAR_RES(Background_bitmap); if (Background_bitmap >= 0) { gr_set_bitmap(Background_bitmap); gr_bitmap(0, 0, GR_RESIZE_MENU); } percent = (int) (100.0f - (Credits_artwork_display_time - Credits_counter) * 100.0f / Credits_artwork_fade_time); if (percent < 0){ percent = 0; } next = Credits_artwork_index + 1; if (next >= Credits_num_images){ next = 0; } if (Credits_bmps[Credits_artwork_index] < 0) { char buf[40]; if (gr_screen.res == GR_1024) { sprintf(buf, NOX("2_CrIm%.2d"), Credits_artwork_index); } else { sprintf(buf, NOX("CrIm%.2d"), Credits_artwork_index); } Credits_bmps[Credits_artwork_index] = bm_load(buf); } if (Credits_bmps[next] < 0) { char buf[40]; if (gr_screen.res == GR_1024) { sprintf(buf, NOX("2_CrIm%.2d"), next); } else { sprintf(buf, NOX("CrIm%.2d"), next); } Credits_bmps[next] = bm_load(buf); } bm1 = Credits_bmps[Credits_artwork_index]; bm2 = Credits_bmps[next]; if((bm1 != -1) && (bm2 != -1)){ GR_DEBUG_SCOPE("Render credits bitmap"); Assert(percent >= 0 && percent <= 100); // get width and height bm_get_info(bm1, &bw1, &bh1, NULL, NULL, NULL); bm_get_info(bm2, &bw2, &bh2, NULL, NULL, NULL); // determine where to draw the coords bx1 = Credits_image_coords[gr_screen.res][CREDITS_X_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_W_COORD] - bw1)/2); by1 = Credits_image_coords[gr_screen.res][CREDITS_Y_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_H_COORD] - bh1)/2); bx2 = Credits_image_coords[gr_screen.res][CREDITS_X_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_W_COORD] - bw2)/2); by2 = Credits_image_coords[gr_screen.res][CREDITS_Y_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_H_COORD] - bh2)/2); auto alpha = (float)percent / 100.0f; gr_set_bitmap(bm1, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.0f - alpha); gr_bitmap(bx1, by1, GR_RESIZE_MENU); gr_set_bitmap(bm2, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha); gr_bitmap(bx2, by2, GR_RESIZE_MENU); } Ui_window.draw(); for (i=TECH_DATABASE_BUTTON; i<=CREDITS_BUTTON; i++){ if (Buttons[i][gr_screen.res].button.button_down()){ break; } } if (i > CREDITS_BUTTON){ Buttons[CREDITS_BUTTON][gr_screen.res].button.draw_forced(2); } gr_set_clip(Credits_text_coords[gr_screen.res][CREDITS_X_COORD], Credits_text_coords[gr_screen.res][CREDITS_Y_COORD], Credits_text_coords[gr_screen.res][CREDITS_W_COORD], Credits_text_coords[gr_screen.res][CREDITS_H_COORD], GR_RESIZE_MENU); font::set_font(font::FONT1); gr_set_color_fast(&Color_normal); int y_offset = 0; for (SCP_vector<SCP_string>::iterator iter = Credit_text_parts.begin(); iter != Credit_text_parts.end(); ++iter) { size_t currentPos = 0; size_t lineEnd; do { int height; int width; lineEnd = iter->find('\n', currentPos); auto length = lineEnd - currentPos; if (lineEnd == SCP_string::npos) { length = std::numeric_limits<size_t>::max(); } gr_get_string_size(&width, &height, iter->c_str() + currentPos, static_cast<int>(length)); // Check if the text part is actually visible if (Credit_position + y_offset + height > 0.0f) { float x = static_cast<float>((gr_screen.clip_width_unscaled - width) / 2); gr_string(x, Credit_position + y_offset, iter->c_str() + currentPos, GR_RESIZE_MENU, static_cast<int>(length)); } y_offset += height; currentPos = lineEnd + 1; } while (lineEnd < iter->length() && lineEnd != SCP_string::npos); } int temp_time; temp_time = timer_get_milliseconds(); Credits_frametime = temp_time - Credits_last_time; Credits_last_time = temp_time; timestamp_inc(i2f(Credits_frametime) / TIMESTAMP_FREQUENCY); float fl_frametime = i2fl(Credits_frametime) / 1000.f; if (keyd_pressed[KEY_LSHIFT]) { Credit_position -= fl_frametime * Credits_scroll_rate * 4.0f; } else { Credit_position -= fl_frametime * Credits_scroll_rate; } if (Credit_position < Credit_stop_pos){ Credit_position = Credit_start_pos; } Credits_counter += fl_frametime; while (Credits_counter >= Credits_artwork_display_time) { Credits_counter -= Credits_artwork_display_time; Credits_artwork_index = next; } gr_flip(); }
void physics_sim_rot(matrix * orient, physics_info * pi, float sim_time ) { angles tangles; vec3d new_vel; matrix tmp; float shock_amplitude; float rotdamp; float shock_fraction_time_left; Assert(is_valid_matrix(orient)); Assert(is_valid_vec(&pi->rotvel)); Assert(is_valid_vec(&pi->desired_rotvel)); // Handle special case of shockwave shock_amplitude = 0.0f; if ( pi->flags & PF_IN_SHOCKWAVE ) { if ( timestamp_elapsed(pi->shockwave_decay) ) { pi->flags &= ~PF_IN_SHOCKWAVE; rotdamp = pi->rotdamp; } else { shock_fraction_time_left = timestamp_until( pi->shockwave_decay ) / (float) SW_BLAST_DURATION; rotdamp = pi->rotdamp + pi->rotdamp * (SW_ROT_FACTOR - 1) * shock_fraction_time_left; shock_amplitude = pi->shockwave_shake_amp * shock_fraction_time_left; } } else { rotdamp = pi->rotdamp; } // Do rotational physics with given damping apply_physics( rotdamp, pi->desired_rotvel.xyz.x, pi->rotvel.xyz.x, sim_time, &new_vel.xyz.x, NULL ); apply_physics( rotdamp, pi->desired_rotvel.xyz.y, pi->rotvel.xyz.y, sim_time, &new_vel.xyz.y, NULL ); apply_physics( rotdamp, pi->desired_rotvel.xyz.z, pi->rotvel.xyz.z, sim_time, &new_vel.xyz.z, NULL ); Assert(is_valid_vec(&new_vel)); pi->rotvel = new_vel; tangles.p = pi->rotvel.xyz.x*sim_time; tangles.h = pi->rotvel.xyz.y*sim_time; tangles.b = pi->rotvel.xyz.z*sim_time; /* // Make ship shake due to afterburner. if (pi->flags & PF_AFTERBURNER_ON || !timestamp_elapsed(pi->afterburner_decay) ) { float max_speed; max_speed = vm_vec_mag_quick(&pi->max_vel); tangles.p += (float) (rand()-RAND_MAX_2) * RAND_MAX_1f * pi->speed/max_speed/64.0f; tangles.h += (float) (rand()-RAND_MAX_2) * RAND_MAX_1f * pi->speed/max_speed/64.0f; if ( pi->flags & PF_AFTERBURNER_ON ) { pi->afterburner_decay = timestamp(ABURN_DECAY_TIME); } } */ // Make ship shake due to shockwave, decreasing in amplitude at the end of the shockwave if ( pi->flags & PF_IN_SHOCKWAVE ) { tangles.p += (float) (myrand()-RAND_MAX_2) * RAND_MAX_1f * shock_amplitude; tangles.h += (float) (myrand()-RAND_MAX_2) * RAND_MAX_1f * shock_amplitude; } vm_angles_2_matrix(&pi->last_rotmat, &tangles ); vm_matrix_x_matrix( &tmp, orient, &pi->last_rotmat ); *orient = tmp; vm_orthogonalize_matrix(orient); }
// Adds velocity to position // finds velocity and displacement in local coords void physics_sim_vel(vec3d * position, physics_info * pi, float sim_time, matrix *orient) { vec3d local_disp; // displacement in this frame vec3d local_v_in; // velocity in local coords at the start of this frame vec3d local_desired_vel; // desired velocity in local coords vec3d local_v_out; // velocity in local coords following this frame vec3d damp; // Maybe clear the reduced_damp flag. // This fixes the problem of the player getting near-instantaneous acceleration under unknown circumstances. // The larger problem is probably that PF_USE_VEL is getting stuck set. if ((pi->flags & PF_REDUCED_DAMP) && (timestamp_elapsed(pi->reduced_damp_decay))) { pi->flags &= ~PF_REDUCED_DAMP; } // Set up damping constants based on special conditions // ie. shockwave, collision, weapon, dead if (pi->flags & PF_DEAD_DAMP) { // side_slip_time_const is already quite large and now needs to be applied in all directions vm_vec_make( &damp, pi->side_slip_time_const, pi->side_slip_time_const, pi->side_slip_time_const ); } else if (pi->flags & PF_REDUCED_DAMP) { // case of shock, weapon, collide, etc. if ( timestamp_elapsed(pi->reduced_damp_decay) ) { vm_vec_make( &damp, pi->side_slip_time_const, pi->side_slip_time_const, 0.0f ); } else { // damp is multiplied by fraction and not fraction^2, gives better collision separation float reduced_damp_fraction_time_left = timestamp_until( pi->reduced_damp_decay ) / (float) REDUCED_DAMP_TIME; damp.xyz.x = pi->side_slip_time_const * ( 1 + (REDUCED_DAMP_FACTOR-1) * reduced_damp_fraction_time_left ); damp.xyz.y = pi->side_slip_time_const * ( 1 + (REDUCED_DAMP_FACTOR-1) * reduced_damp_fraction_time_left ); damp.xyz.z = pi->side_slip_time_const * reduced_damp_fraction_time_left * REDUCED_DAMP_FACTOR; } } else { // regular damping if (pi->use_newtonian_damp) { vm_vec_make( &damp, pi->side_slip_time_const, pi->side_slip_time_const, pi->side_slip_time_const ); } else { vm_vec_make( &damp, pi->side_slip_time_const, pi->side_slip_time_const, 0.0f ); } } // Note: CANNOT maintain a *local velocity* since a rotation can occur in this frame. // thus the local velocity of in the next frame can be different (this would require rotate to change local vel // and this is not desired // get local components of current velocity vm_vec_rotate (&local_v_in, &pi->vel, orient); // get local components of desired velocity vm_vec_rotate (&local_desired_vel, &pi->desired_vel, orient); // find updated LOCAL velocity and position in the local x direction apply_physics (damp.xyz.x, local_desired_vel.xyz.x, local_v_in.xyz.x, sim_time, &local_v_out.xyz.x, &local_disp.xyz.x); // find updated LOCAL velocity and position in the local y direction apply_physics (damp.xyz.y, local_desired_vel.xyz.y, local_v_in.xyz.y, sim_time, &local_v_out.xyz.y, &local_disp.xyz.y); // find updated LOCAL velocity and position in the local z direction // for player ship, damp should normally be zero, but may be altered in a shockwave // in death, shockwave,etc. we want damping time const large for all 3 axes // warp in test - make excessive speed drop exponentially from max allowed // become (0.01x in 3 sec) int special_warp_in = FALSE; float excess = local_v_in.xyz.z - pi->max_vel.xyz.z; if (excess > 5 && (pi->flags & PF_SPECIAL_WARP_IN)) { special_warp_in = TRUE; float exp_factor = float(exp(-sim_time / SPECIAL_WARP_T_CONST)); local_v_out.xyz.z = pi->max_vel.xyz.z + excess * exp_factor; local_disp.xyz.z = (pi->max_vel.xyz.z * sim_time) + excess * (float(SPECIAL_WARP_T_CONST) * (1.0f - exp_factor)); } else if (pi->flags & PF_SPECIAL_WARP_OUT) { float exp_factor = float(exp(-sim_time / SPECIAL_WARP_T_CONST)); vec3d temp; vm_vec_rotate(&temp, &pi->prev_ramp_vel, orient); float deficeit = temp.xyz.z - local_v_in.xyz.z; local_v_out.xyz.z = local_v_in.xyz.z + deficeit * (1.0f - exp_factor); local_disp.xyz.z = (local_v_in.xyz.z * sim_time) + deficeit * (sim_time - (float(SPECIAL_WARP_T_CONST) * (1.0f - exp_factor))); } else { apply_physics (damp.xyz.z, local_desired_vel.xyz.z, local_v_in.xyz.z, sim_time, &local_v_out.xyz.z, &local_disp.xyz.z); } // maybe turn off special warp in flag if ((pi->flags & PF_SPECIAL_WARP_IN) && (excess < 5)) { pi->flags &= ~(PF_SPECIAL_WARP_IN); } // update world position from local to world coords using orient vec3d world_disp; vm_vec_unrotate (&world_disp, &local_disp, orient); vm_vec_add2 (position, &world_disp); // update world velocity vm_vec_unrotate(&pi->vel, &local_v_out, orient); if (special_warp_in) { vm_vec_rotate(&pi->prev_ramp_vel, &pi->vel, orient); } }
void credits_do_frame(float frametime) { int i, k, next, percent, bm1, bm2; int bx1, by1, bw1, bh1; int bx2, by2, bw2, bh2; // Use this id to trigger the start of music playing on the credits screen if ( timestamp_elapsed(Credits_music_begin_timestamp) ) { Credits_music_begin_timestamp = 0; credits_start_music(); } k = Ui_window.process(); switch (k) { case KEY_ESC: gameseq_post_event(GS_EVENT_MAIN_MENU); key_flush(); break; case KEY_CTRLED | KEY_UP: case KEY_SHIFTED | KEY_TAB: if ( !(Player->flags & PLAYER_FLAGS_IS_MULTI) ) { credits_screen_button_pressed(CUTSCENES_BUTTON); break; } // else, react like tab key. case KEY_CTRLED | KEY_DOWN: case KEY_TAB: credits_screen_button_pressed(TECH_DATABASE_BUTTON); break; default: break; } // end switch for (i=0; i<NUM_BUTTONS; i++){ if (Buttons[i][gr_screen.res].button.pressed()){ if (credits_screen_button_pressed(i)){ return; } } } gr_reset_clip(); GR_MAYBE_CLEAR_RES(Background_bitmap); if (Background_bitmap >= 0) { gr_set_bitmap(Background_bitmap); gr_bitmap(0, 0); } percent = (int) (100.0f - (CREDITS_ARTWORK_DISPLAY_TIME - Credits_counter) * 100.0f / CREDITS_ARTWORK_FADE_TIME); if (percent < 0){ percent = 0; } next = Credits_artwork_index + 1; if (next >= NUM_IMAGES){ next = 0; } if (Credits_bmps[Credits_artwork_index] < 0) { char buf[40]; if (gr_screen.res == GR_1024) { sprintf(buf, NOX("2_CrIm%.2d"), Credits_artwork_index); } else { sprintf(buf, NOX("CrIm%.2d"), Credits_artwork_index); } Credits_bmps[Credits_artwork_index] = bm_load(buf); } if (Credits_bmps[next] < 0) { char buf[40]; if (gr_screen.res == GR_1024) { sprintf(buf, NOX("2_CrIm%.2d"), Credits_artwork_index); } else { sprintf(buf, NOX("CrIm%.2d"), next); } Credits_bmps[next] = bm_load(buf); } bm1 = Credits_bmps[Credits_artwork_index]; bm2 = Credits_bmps[next]; if((bm1 != -1) && (bm2 != -1)){ Assert(percent >= 0 && percent <= 100); // get width and height bm_get_info(bm1, &bw1, &bh1, NULL, NULL, NULL); bm_get_info(bm2, &bw2, &bh2, NULL, NULL, NULL); // determine where to draw the coords bx1 = Credits_image_coords[gr_screen.res][CREDITS_X_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_W_COORD] - bw1)/2); by1 = Credits_image_coords[gr_screen.res][CREDITS_Y_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_H_COORD] - bh1)/2); bx2 = Credits_image_coords[gr_screen.res][CREDITS_X_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_W_COORD] - bw2)/2); by2 = Credits_image_coords[gr_screen.res][CREDITS_Y_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_H_COORD] - bh2)/2); gr_cross_fade(bm1, bm2, bx1, by1, bx2, by2, (float)percent / 100.0f); } /* if (CreditsWin01 != -1) { gr_set_bitmap(CreditsWin01); gr_bitmap(233, 5); } if (CreditsWin02 != -1) { gr_set_bitmap(CreditsWin02); gr_bitmap(616, 8); } if (CreditsWin03 != -1) { gr_set_bitmap(CreditsWin03); gr_bitmap(233, 299); } if (CreditsWin04 != -1) { gr_set_bitmap(CreditsWin04); gr_bitmap(215, 8); } */ Ui_window.draw(); for (i=TECH_DATABASE_BUTTON; i<=CREDITS_BUTTON; i++){ if (Buttons[i][gr_screen.res].button.button_down()){ break; } } if (i > CREDITS_BUTTON){ Buttons[CREDITS_BUTTON][gr_screen.res].button.draw_forced(2); } gr_set_clip(Credits_text_coords[gr_screen.res][CREDITS_X_COORD], Credits_text_coords[gr_screen.res][CREDITS_Y_COORD], Credits_text_coords[gr_screen.res][CREDITS_W_COORD], Credits_text_coords[gr_screen.res][CREDITS_H_COORD]); gr_set_font(FONT1); gr_set_color_fast(&Color_normal); int sy; if ( Credit_position > 0 ) { sy = fl2i(Credit_position+0.5f); } else { sy = fl2i(Credit_position-0.5f); } gr_string(0x8000, sy, Credit_text); int temp_time; temp_time = timer_get_milliseconds(); Credits_frametime = temp_time - Credits_last_time; Credits_last_time = temp_time; timestamp_inc(Credits_frametime / 1000.0f); float fl_frametime = i2fl(Credits_frametime) / 1000.f; if (keyd_pressed[KEY_LSHIFT]) { Credit_position -= fl_frametime * CREDITS_SCROLL_RATE * 4.0f; } else { Credit_position -= fl_frametime * CREDITS_SCROLL_RATE; } if (Credit_position < Credit_stop_pos){ Credit_position = Credit_start_pos; } Credits_counter += fl_frametime; while (Credits_counter >= CREDITS_ARTWORK_DISPLAY_TIME) { Credits_counter -= CREDITS_ARTWORK_DISPLAY_TIME; Credits_artwork_index = next; } gr_flip(); }
// ---------------------------------------------------------------------------- // afterburners_update() // // Update the state of the afterburner fuel remaining for an object using the // afterburner. // // for the player ship, key_up_time() is called for the afterburner key to // detect when afterburners disengage. // // input: *objp => pointer to the object starting afterburners // fl_frametime => time in seconds of the last frame // void afterburners_update(object* objp, float fl_frametime) { Assert(objp != NULL); Assert(objp->type == OBJ_SHIP); Assert(objp->instance >= 0 && objp->instance < MAX_SHIPS); ship_info* sip; ship* shipp; static int volume_chg_timer = 1; shipp = &Ships[objp->instance]; Assert(shipp->ship_info_index >= 0 && shipp->ship_info_index < Num_ship_classes); sip = &Ship_info[shipp->ship_info_index]; if ((objp->flags & OF_PLAYER_SHIP) && (Game_mode & GM_DEAD)) { return; } if (!(sip->flags & SIF_AFTERBURNER)) { return; // nothing to update, afterburners are not even on the ship } //shut the afterburners off if we're using the booster tertiary //shut the afterburners off if we're in glide mode. if ((objp->phys_info.flags & PF_AFTERBURNER_ON) && ((objp->phys_info.flags & PF_BOOSTER_ON) || (objp->phys_info.flags & PF_GLIDING))) { if (objp == Player_obj) afterburner_stop_sounds(); afterburners_stop(objp); return; } if (objp == Player_obj) { if (!timestamp_elapsed(Player_disengage_timer)) { float remaining; remaining = timestamp_until(Player_disengage_timer) / i2fl(DISENGAGE_TIME); if (remaining <= 0) { afterburner_stop_sounds(); } else { if (remaining > 1.0f) { remaining = 1.0f; } snd_set_volume(Player_afterburner_loop_id, remaining * Player_afterburner_vol); } } else { if (Player_disengage_timer != 1) { afterburner_stop_sounds(); } } } // single player, multiplayer servers, and clients for their own ships if (!(Game_mode & GM_MULTIPLAYER) || MULTIPLAYER_MASTER || (objp == Player_obj)) { if (!(objp->phys_info.flags & PF_AFTERBURNER_ON)) { // Recover afterburner fuel if (shipp->afterburner_fuel < sip->afterburner_fuel_capacity) { float recharge_scale; recharge_scale = Energy_levels[shipp->engine_recharge_index] * 2.0f * The_mission.ai_profile-> afterburner_recharge_scale[Game_skill_level]; shipp->afterburner_fuel += (sip->afterburner_recover_rate * fl_frametime * recharge_scale); if (shipp->afterburner_fuel > sip->afterburner_fuel_capacity) { shipp->afterburner_fuel = sip->afterburner_fuel_capacity; } } return; } else { // Check if there is enough afterburner fuel if (shipp->afterburner_fuel <= 0) { shipp->afterburner_fuel = 0.0f; afterburners_stop(objp); return; } } // afterburners are firing at this point // Reduce the afterburner fuel shipp->afterburner_fuel -= (sip->afterburner_burn_rate * fl_frametime); if (shipp->afterburner_fuel < 0.0f) { shipp->afterburner_fuel = 0.0f; } } if (objp == Player_obj) { if ((Viewer_mode & VM_NOT_COCKPIT)) { // stop afterburner sound if it is playing if (Player_afterburner_loop_id != -1) { snd_stop(Player_afterburner_loop_id); Player_afterburner_loop_id = -1; } return; } if (timestamp_elapsed(Player_afterburner_loop_delay)) { Player_afterburner_vol = Snds[SND_ABURN_LOOP].default_volume; Player_afterburner_loop_delay = timestamp(50); if (Player_afterburner_loop_id == -1) { Player_afterburner_loop_id = snd_play_looping(&Snds[SND_ABURN_LOOP], 0.0f, -1, -1, Player_afterburner_vol); //snd_set_volume(Player_afterburner_loop_id, Player_afterburner_vol); // nprintf(("Alan","PLAY LOOPING SOUND\n")); } } // Reduce the volume of the afterburner sound if near the end if (timestamp_elapsed(volume_chg_timer)) { float percent_afterburner_left; percent_afterburner_left = shipp->afterburner_fuel / sip->afterburner_fuel_capacity; volume_chg_timer = timestamp(AFTERBURNER_VOLUME_UPDATE); if (percent_afterburner_left < AFTERBURNER_PERCENT_VOL_ATTENUATE) { Player_afterburner_vol = percent_afterburner_left * (1 / AFTERBURNER_PERCENT_VOL_ATTENUATE) * Snds[SND_ABURN_LOOP].default_volume; snd_set_volume(Player_afterburner_loop_id, Player_afterburner_vol); } } // end if (timestamp_elapsed(volume_chg_timer)) } }
// process() is called to process the button, which amounts to: // If mouse is over button, hilight it // If highlighted and mouse button down, flag button as down // If hotkey pressed, flag button as down // If hotkey_if_focus pressed, and button has focus, flag button as down // Set various BF_JUST_* flags if events changed from last frame // void UI_BUTTON::process(int focus) { int mouse_on_me, old_flags; old_flags = m_flags; frame_reset(); // check mouse over control and handle hilighting state mouse_on_me = is_mouse_on(); // if gadget is disabled, force button up and return if (disabled_flag) { if (old_flags & BF_DOWN){ m_flags |= BF_JUST_RELEASED; } if (!hidden && !my_wnd->use_hack_to_get_around_stupid_problem_flag) { if (mouse_on_me && B1_JUST_PRESSED){ gamesnd_play_iface(SND_GENERAL_FAIL); } if ( (hotkey >= 0) && (my_wnd->keypress == hotkey) ){ gamesnd_play_iface(SND_GENERAL_FAIL); } } // do callback if the button is disabled if (mouse_on_me && B1_JUST_PRESSED){ if (m_disabled_function != NULL) { m_disabled_function(); } } return; } // check focus and derived focus with one variable if (my_wnd->selected_gadget == this) { focus = 1; } // show alternate cursor, perhaps? maybe_show_custom_cursor(); if ( !mouse_on_me ) { next_repeat = 0; } else { m_flags |= BF_HIGHLIGHTED; if ( !(old_flags & BF_HIGHLIGHTED) ) { int do_callback = 1; m_flags |= BF_JUST_HIGHLIGHTED; // if a callback exists, call it if (m_just_highlighted_function) { if ( m_flags & BF_SKIP_FIRST_HIGHLIGHT_CALLBACK ) { if ( first_callback ) { do_callback = 0; } } first_callback = 0; if ( do_callback ) { m_just_highlighted_function(); } } } } // check if mouse is pressed if ( B1_PRESSED && mouse_on_me ) { m_flags |= BF_DOWN; capture_mouse(); } // check if hotkey is down or not if ( (hotkey >= 0) && (my_wnd->keypress == hotkey) ) { m_flags |= BF_DOWN | BF_CLICKED; } // only check for space/enter keystrokes if we are not ignoring the focus (this is the // default behavior) if ( !(m_flags & BF_IGNORE_FOCUS) ) { if ( focus && (hotkey_if_focus >= 0) ) { if (my_wnd->keypress == hotkey_if_focus) m_flags |= BF_DOWN | BF_CLICKED; if ( (hotkey_if_focus == KEY_SPACEBAR) && (my_wnd->keypress == KEY_ENTER) ) m_flags |= BF_DOWN | BF_CLICKED; } } // handler for button not down if ( !(m_flags & BF_DOWN) ) { next_repeat = 0; if ( (old_flags & BF_DOWN) && !(old_flags & BF_CLICKED) ) // check for release of mouse, not hotkey m_flags |= BF_JUST_RELEASED; // non-repeating buttons behave sort of uniquely.. They activate when released over button if (!(m_flags & BF_REPEATS)) { if ( (m_flags & BF_JUST_RELEASED) && (m_flags & BF_HIGHLIGHTED) ) m_flags |= BF_CLICKED; } return; } // check if button just went down this frame if ( !(old_flags & BF_DOWN) ) { m_flags |= BF_JUST_PRESSED; m_press_linger = timestamp(100); if (user_function) user_function(); if (m_flags & BF_REPEATS) { next_repeat = timestamp(B_REPEAT_TIME * 3); m_flags |= BF_CLICKED; } } // check if a repeat event should occur if ( timestamp_elapsed(next_repeat) && (m_flags & BF_REPEATS) ) { next_repeat = timestamp(B_REPEAT_TIME); m_flags |= BF_CLICKED; m_press_linger = timestamp(100); } // check for double click occurance if (B1_DOUBLE_CLICKED && mouse_on_me) { m_flags |= BF_DOUBLE_CLICKED; m_press_linger = timestamp(100); } }
// ------------------------------------------------------------------ // swarm_update_direction() // // Check if we want to update the direction of a swarm missile. // void swarm_update_direction(object *objp, float frametime) { weapon_info *wip; weapon *wp; object *hobjp; swarm_info *swarmp; vec3d obj_to_target; float vel, target_dist, radius, missile_speed, missile_dist; physics_info *pi; Assert(objp->instance >= 0 && objp->instance < MAX_WEAPONS); wp = &Weapons[objp->instance]; if (wp->swarm_index == -1) { return; } wip = &Weapon_info[wp->weapon_info_index]; hobjp = wp->homing_object; pi = &Objects[wp->objnum].phys_info; swarmp = &Swarm_missiles[wp->swarm_index]; // check if homing is lost.. if it is then get a new path to move swarm missile along if ( swarmp->homing_objnum != -1 && hobjp == &obj_used_list ) { swarmp->change_timestamp = 1; swarmp->path_num = -1; swarmp->homing_objnum = -1; } if ( hobjp != &obj_used_list ) { swarmp->homing_objnum = OBJ_INDEX(hobjp); } if ( timestamp_elapsed(swarmp->change_timestamp) ) { if ( swarmp->path_num == -1 ) { if ( Objects[objp->parent].type != OBJ_SHIP ) { //AL: parent ship died... so just pick some random paths swarmp->path_num = myrand()%4; } else { ship *parent_shipp; parent_shipp = &Ships[Objects[objp->parent].instance]; swarmp->path_num = (parent_shipp->next_swarm_path++)%4; if ( parent_shipp->next_swarm_path%4 == 0 ) { swarmp->flags ^= SWARM_POSITIVE_PATH; } } vm_vec_scale_add(&swarmp->original_target, &objp->pos, &objp->orient.vec.fvec, SWARM_CONE_LENGTH); swarmp->circle_rvec = objp->orient.vec.rvec; swarmp->circle_uvec = objp->orient.vec.uvec; swarmp->change_count = 1; swarmp->change_time = fl2i(SWARM_CHANGE_DIR_TIME + SWARM_TIME_VARIANCE*(frand() - 0.5f) * 2); vm_vec_zero(&swarmp->last_offset); missile_speed = pi->speed; missile_dist = missile_speed * swarmp->change_time/1000.0f; if ( missile_dist < SWARM_DIST_OFFSET ) { missile_dist=i2fl(SWARM_DIST_OFFSET); } swarmp->angle_offset = (float)(asin(SWARM_DIST_OFFSET / missile_dist)); Assert(!_isnan(swarmp->angle_offset) ); } swarmp->change_timestamp = timestamp(swarmp->change_time); // check if swarm missile is homing, if so need to calculate a new target pos to turn towards if ( hobjp != &obj_used_list && f2fl(Missiontime - wp->creation_time) > 0.5f && ( f2fl(Missiontime - wp->creation_time) > wip->free_flight_time ) ) { swarmp->original_target = wp->homing_pos; // Calculate a rvec and uvec that will determine the displacement from the // intended target. Use crossprod to generate a right vector, from the missile // up vector and the vector connecting missile to the homing object. swarmp->circle_uvec = objp->orient.vec.uvec; swarmp->circle_rvec = objp->orient.vec.rvec; missile_speed = pi->speed; missile_dist = missile_speed * swarmp->change_time/1000.0f; if ( missile_dist < SWARM_DIST_OFFSET ) { missile_dist = i2fl(SWARM_DIST_OFFSET); } swarmp->angle_offset = (float)(asin(SWARM_DIST_OFFSET / missile_dist)); Assert(!_isnan(swarmp->angle_offset) ); } vm_vec_sub(&obj_to_target, &swarmp->original_target, &objp->pos); target_dist = vm_vec_mag_quick(&obj_to_target); swarmp->last_dist = target_dist; // If homing swarm missile is close to target, let missile home in on original target if ( target_dist < SWARM_DIST_STOP_SWARMING ) { swarmp->new_target = swarmp->original_target; goto swarm_new_target_calced; } radius = (float)tan(swarmp->angle_offset) * target_dist; vec3d rvec_component, uvec_component; swarmp->change_count++; if ( swarmp->change_count > 2 ) { swarmp->flags ^= SWARM_POSITIVE_PATH; swarmp->change_count = 0; } // pick a new path number to follow once at center if ( swarmp->change_count == 1 ) { swarmp->path_num = swarmp->path_num + myrand()%3; if ( swarmp->path_num > 3 ) { swarmp->path_num = 0; } } vm_vec_zero(&rvec_component); vm_vec_zero(&uvec_component); switch ( swarmp->path_num ) { case 0: // straight up and down if ( swarmp->flags & SWARM_POSITIVE_PATH ) vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, radius); else vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, -radius); break; case 1: // left/right if ( swarmp->flags & SWARM_POSITIVE_PATH ) vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, radius); else vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, -radius); break; case 2: // top/right - bottom/left if ( swarmp->flags & SWARM_POSITIVE_PATH ) { vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, radius); vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, radius); } else { vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, -radius); vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, -radius); } break; case 3: // top-left - bottom/right if ( swarmp->flags & SWARM_POSITIVE_PATH ) { vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, -radius); vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, radius); } else { vm_vec_copy_scale( &rvec_component, &swarmp->circle_rvec, radius); vm_vec_copy_scale( &uvec_component, &swarmp->circle_uvec, -radius); } break; default: Int3(); break; } swarmp->new_target = swarmp->original_target; vm_vec_zero(&swarmp->last_offset); vm_vec_add(&swarmp->last_offset, &uvec_component, &rvec_component); vm_vec_add2(&swarmp->new_target, &swarmp->last_offset); } else { if ( hobjp != &obj_used_list && f2fl(Missiontime - wp->creation_time) > 0.5f ) { swarmp->new_target = swarmp->original_target; if ( swarmp->last_dist < SWARM_DIST_STOP_SWARMING ) { swarmp->new_target = wp->homing_pos; goto swarm_new_target_calced; } vm_vec_add2(&swarmp->new_target, &swarmp->last_offset); } } swarm_new_target_calced: ai_turn_towards_vector(&swarmp->new_target, objp, frametime, wip->turn_time, NULL, NULL, 0.0f, 0); vel = vm_vec_mag(&objp->phys_info.desired_vel); vm_vec_copy_scale(&objp->phys_info.desired_vel, &objp->orient.vec.fvec, vel); }
void obj_collide_pair(object *A, object *B) { uint ctype; int (*check_collision)( obj_pair *pair ); int swapped = 0; check_collision = NULL; if ( A==B ) return; // Don't check collisions with yourself if ( !(A->flags&OF_COLLIDES) ) return; // This object doesn't collide with anything if ( !(B->flags&OF_COLLIDES) ) return; // This object doesn't collide with anything // Make sure you're not checking a parent with it's kid or vicy-versy // if ( A->parent_sig == B->signature && !(A->type == OBJ_SHIP && B->type == OBJ_DEBRIS) ) return; // if ( B->parent_sig == A->signature && !(A->type == OBJ_DEBRIS && B->type == OBJ_SHIP) ) return; if ( reject_obj_pair_on_parent(A,B) ) { return; } Assert( A->type < 127 ); Assert( B->type < 127 ); ctype = COLLISION_OF(A->type,B->type); switch( ctype ) { case COLLISION_OF(OBJ_WEAPON,OBJ_SHIP): swapped = 1; check_collision = collide_ship_weapon; break; case COLLISION_OF(OBJ_SHIP, OBJ_WEAPON): check_collision = collide_ship_weapon; break; case COLLISION_OF(OBJ_DEBRIS, OBJ_WEAPON): check_collision = collide_debris_weapon; break; case COLLISION_OF(OBJ_WEAPON, OBJ_DEBRIS): swapped = 1; check_collision = collide_debris_weapon; break; case COLLISION_OF(OBJ_DEBRIS, OBJ_SHIP): check_collision = collide_debris_ship; break; case COLLISION_OF(OBJ_SHIP, OBJ_DEBRIS): check_collision = collide_debris_ship; swapped = 1; break; case COLLISION_OF(OBJ_ASTEROID, OBJ_WEAPON): // Only check collision's with player weapons // if ( Objects[B->parent].flags & OF_PLAYER_SHIP ) { check_collision = collide_asteroid_weapon; // } break; case COLLISION_OF(OBJ_WEAPON, OBJ_ASTEROID): swapped = 1; // Only check collision's with player weapons // if ( Objects[A->parent].flags & OF_PLAYER_SHIP ) { check_collision = collide_asteroid_weapon; // } break; case COLLISION_OF(OBJ_ASTEROID, OBJ_SHIP): // Only check collisions with player ships // if ( B->flags & OF_PLAYER_SHIP ) { check_collision = collide_asteroid_ship; // } break; case COLLISION_OF(OBJ_SHIP, OBJ_ASTEROID): // Only check collisions with player ships // if ( A->flags & OF_PLAYER_SHIP ) { check_collision = collide_asteroid_ship; // } swapped = 1; break; case COLLISION_OF(OBJ_SHIP,OBJ_SHIP): check_collision = collide_ship_ship; break; case COLLISION_OF(OBJ_SHIP, OBJ_BEAM): if(beam_collide_early_out(B, A)){ return; } swapped = 1; check_collision = beam_collide_ship; break; case COLLISION_OF(OBJ_BEAM, OBJ_SHIP): if(beam_collide_early_out(A, B)){ return; } check_collision = beam_collide_ship; break; case COLLISION_OF(OBJ_ASTEROID, OBJ_BEAM): if(beam_collide_early_out(B, A)) { return; } swapped = 1; check_collision = beam_collide_asteroid; break; case COLLISION_OF(OBJ_BEAM, OBJ_ASTEROID): if(beam_collide_early_out(A, B)){ return; } check_collision = beam_collide_asteroid; break; case COLLISION_OF(OBJ_DEBRIS, OBJ_BEAM): if(beam_collide_early_out(B, A)) { return; } swapped = 1; check_collision = beam_collide_debris; break; case COLLISION_OF(OBJ_BEAM, OBJ_DEBRIS): if(beam_collide_early_out(A, B)){ return; } check_collision = beam_collide_debris; break; case COLLISION_OF(OBJ_WEAPON, OBJ_BEAM): if(beam_collide_early_out(B, A)) { return; } swapped = 1; check_collision = beam_collide_missile; break; case COLLISION_OF(OBJ_BEAM, OBJ_WEAPON): if(beam_collide_early_out(A, B)){ return; } check_collision = beam_collide_missile; break; case COLLISION_OF(OBJ_WEAPON, OBJ_WEAPON): { weapon_info *awip, *bwip; awip = &Weapon_info[Weapons[A->instance].weapon_info_index]; bwip = &Weapon_info[Weapons[B->instance].weapon_info_index]; if ((awip->weapon_hitpoints > 0) || (bwip->weapon_hitpoints > 0)) { if (bwip->weapon_hitpoints == 0) { check_collision = collide_weapon_weapon; swapped=1; } else { check_collision = collide_weapon_weapon; } } break; } default: return; } if ( !check_collision ) return; // Swap them if needed if ( swapped ) { object *tmp = A; A = B; B = tmp; } collider_pair *collision_info = NULL; bool valid = false; uint key = (OBJ_INDEX(A) << 12) + OBJ_INDEX(B); collision_info = &Collision_cached_pairs[key]; if ( collision_info->initialized ) { // make sure we're referring to the correct objects in case the original pair was deleted if ( collision_info->signature_a == collision_info->a->signature && collision_info->signature_b == collision_info->b->signature ) { valid = true; } else { collision_info->a = A; collision_info->b = B; collision_info->signature_a = A->signature; collision_info->signature_b = B->signature; collision_info->next_check_time = timestamp(0); } } else { collision_info->a = A; collision_info->b = B; collision_info->signature_a = A->signature; collision_info->signature_b = B->signature; collision_info->initialized = true; collision_info->next_check_time = timestamp(0); } if ( valid && A->type != OBJ_BEAM ) { // if this signature is valid, make the necessary checks to see if we need to collide check if ( collision_info->next_check_time == -1 ) { return; } else { if ( !timestamp_elapsed(collision_info->next_check_time) ) { return; } } } else { //if ( A->type == OBJ_BEAM ) { //if(beam_collide_early_out(A, B)){ //collision_info->next_check_time = -1; //return; //} //} // only check debris:weapon collisions for player if (check_collision == collide_debris_weapon) { // weapon is B if ( !(Weapon_info[Weapons[B->instance].weapon_info_index].wi_flags & WIF_TURNS) ) { // check for dumbfire weapon // check if debris is behind laser float vdot; if (Weapon_info[Weapons[B->instance].weapon_info_index].subtype == WP_LASER) { vec3d velocity_rel_weapon; vm_vec_sub(&velocity_rel_weapon, &B->phys_info.vel, &A->phys_info.vel); vdot = -vm_vec_dot(&velocity_rel_weapon, &B->orient.vec.fvec); } else { vdot = vm_vec_dot( &A->phys_info.vel, &B->phys_info.vel); } if ( vdot <= 0.0f ) { // They're heading in opposite directions... // check their positions vec3d weapon2other; vm_vec_sub( &weapon2other, &A->pos, &B->pos ); float pdot = vm_vec_dot( &B->orient.vec.fvec, &weapon2other ); if ( pdot <= -A->radius ) { // The other object is behind the weapon by more than // its radius, so it will never hit... collision_info->next_check_time = -1; return; } } // check dist vs. dist moved during weapon lifetime vec3d delta_v; vm_vec_sub(&delta_v, &B->phys_info.vel, &A->phys_info.vel); if (vm_vec_dist_squared(&A->pos, &B->pos) > (vm_vec_mag_squared(&delta_v)*Weapons[B->instance].lifeleft*Weapons[B->instance].lifeleft)) { collision_info->next_check_time = -1; return; } // for nonplayer ships, only create collision pair if close enough if ( (B->parent >= 0) && !(Objects[B->parent].flags & OF_PLAYER_SHIP) && (vm_vec_dist(&B->pos, &A->pos) < (4.0f*A->radius + 200.0f)) ) { collision_info->next_check_time = -1; return; } } } // don't check same team laser:ship collisions on small ships if not player if (check_collision == collide_ship_weapon) { // weapon is B if ( (B->parent >= 0) && !(Objects[B->parent].flags & OF_PLAYER_SHIP) && (Ships[Objects[B->parent].instance].team == Ships[A->instance].team) && (Ship_info[Ships[A->instance].ship_info_index].flags & SIF_SMALL_SHIP) && (Weapon_info[Weapons[B->instance].weapon_info_index].subtype == WP_LASER) ) { collision_info->next_check_time = -1; return; } } } obj_pair new_pair; new_pair.a = A; new_pair.b = B; new_pair.check_collision = check_collision; new_pair.next_check_time = collision_info->next_check_time; if ( check_collision(&new_pair) ) { // don't have to check ever again collision_info->next_check_time = -1; } else { collision_info->next_check_time = new_pair.next_check_time; } }
void obj_check_all_collisions() { obj_pair *parent, *tmp; #ifdef PAIR_STATS // debug info float avg_time_to_next_check = 0.0f; #endif if (Cmdline_dis_collisions) return; if ( !(Game_detail_flags & DETAIL_FLAG_COLLISION) ) return; parent = &pair_used_list; tmp = parent->next; Num_pairs_checked = 0; while (tmp != NULL) { int removed = 0; if ( !timestamp_elapsed(tmp->next_check_time) ) goto NextPair; if ( (tmp->a) && (tmp->b) ) { Num_pairs_checked++; if ( (*tmp->check_collision)(tmp) ) { // We never need to check this pair again. #if 0 //def DONT_REMOVE_PAIRS // Never check it again, but keep the pair around // (useful for debugging) tmp->next_check_time = timestamp(-1); #else // Never check it again, so remove the pair removed = 1; tmp->a->num_pairs--; Assert( tmp->a->num_pairs > -1 ); tmp->b->num_pairs--; Assert( tmp->b->num_pairs > -1 ); Num_pairs--; // Assert(Num_pairs >= 0); parent->next = tmp->next; tmp->a = tmp->b = NULL; tmp->next = pair_free_list.next; pair_free_list.next = tmp; tmp = parent->next; #endif } } NextPair: if ( !removed ) { parent = tmp; tmp = tmp->next; #ifdef PAIR_STATS // debug info if (tmp) { int add_time = timestamp_until( tmp->next_check_time ); if (add_time > 0) avg_time_to_next_check += (float) add_time; } #endif } } MONITOR_INC(NumPairs, Num_pairs); MONITOR_INC(NumPairsChecked, Num_pairs_checked); #ifdef PAIR_STATS avg_time_to_next_check = avg_time_to_next_check / Num_pairs; extern int Num_hull_pieces; extern int Weapons_created; // mprintf(( "[pairs checked: %d, start_pairs: %d, num obj: %d, avg next time: %f]\n", n, org_pairs, Num_objects, avg_time_to_next_check )); // mprintf(( "[Num_hull_pieces: %3d, Num_weapons_created: %3d, pairs_not_created: %3d, pairs_created: %3d, percent new saved: %9.5f]\n", Num_hull_pieces, Weapons_created, pairs_not_created, Pairs_created, 100.0f*(float)pairs_not_created/(float)(pairs_not_created + Pairs_created) )); mprintf(( "[pairs_created: %3d, pairs_not_created: %3d, percent saved %6.3f]\n", Pairs_created, pairs_not_created, 100.0f*pairs_not_created/(Pairs_created+pairs_not_created) )); pairs_not_created = 0; Weapons_created = 0; Pairs_created = 0; #endif // What percent of the pairs did we check? // FYI: (n*(n-1))/2 is the total number of checks required for comparing n objects. // if ( org_pairs > 1 ) { // Object_checked_percentage = (i2fl(n)*100.0f) / i2fl(org_pairs); // } else { // Object_checked_percentage = 0.0f; // } }
// select for multi_lag int multi_lag_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *except_fds, const timeval *timeout) { char t_buf[1024]; int t_from_len; SOCKADDR_IN ip_addr; int ret_val; lag_buf *moveup, *item; Assert(readfds != NULL); Assert(writefds == NULL); Assert(except_fds == NULL); // clear out addresses memset(&ip_addr, 0, sizeof(SOCKADDR_IN)); // if there's data on the socket, read it if(select(nfds, readfds, writefds, except_fds, timeout)){ // read the data and stuff it Assertion(Tcp_active, "multi_lag_select(): TCP/IP is not active!"); t_from_len = sizeof(SOCKADDR_IN); ret_val = recvfrom(readfds->fd_array[0], t_buf, 1024, 0, (SOCKADDR*)&ip_addr, &t_from_len); // wacky socket error if(ret_val == SOCKET_ERROR){ return SOCKET_ERROR; } // if we should be dropping this packet if(!multi_lag_should_be_lost()){ // get a free packet buf and stuff the data item = multi_lag_get_free(); if(item){ Assert(ret_val < 700); memcpy(item->data, t_buf, ret_val); item->data_len = ret_val; item->ip_addr = ip_addr; item->socket = readfds->fd_array[0]; item->stamp = timestamp(multi_lag_get_random_lag()); } } } // always unset the readfds readfds->fd_count = 0; // now determine if we have any pending packets - find the first one // NOTE : this _could_ be the packet we just read. In fact, with a 0 lag, this will always be the case moveup=GET_FIRST(&Lag_used_list); while ( moveup!=END_OF_LIST(&Lag_used_list) ) { // if the timestamp has elapsed and we have a matching socket if((readfds->fd_array[0] == (SOCKET)moveup->socket) && ((moveup->stamp <= 0) || timestamp_elapsed(moveup->stamp))){ // set this so we think select returned yes readfds->fd_count = 1; return 1; } moveup = GET_NEXT(moveup); } // no data return 0; }
// 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); } } }
/** * 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 ); } }
void radar_plot_object( object *objp ) { vector pos, tempv; float dist, rscale, zdist, max_radar_dist; int xpos, ypos, color=0; vector *world_pos = &objp->pos; float awacs_level; // don't process anything here. Somehow, a jumpnode object caused this function // to get entered on server side. if( Game_mode & GM_STANDALONE_SERVER ){ return; } // multiplayer clients ingame joining should skip this function if ( MULTIPLAYER_CLIENT && (Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ){ return; } // get team-wide awacs level for the object if not ship int ship_is_visible = 0; if (objp->type == OBJ_SHIP) { if (Player_ship != NULL) { if (ship_is_visible_by_team(objp->instance, Player_ship->team)) { ship_is_visible = 1; } } } // only check awacs level if ship is not visible by team awacs_level = 1.5f; if (Player_ship != NULL && !ship_is_visible) { awacs_level = awacs_get_level(objp, Player_ship); } // if the awacs level is unviewable - bail if(awacs_level < 0.0f && !See_all){ return; } // Apply object type filters switch ( objp->type ) { case OBJ_SHIP: // Place to cull ships, such as NavBuoys break; case OBJ_JUMP_NODE: // filter jump nodes here if required break; case OBJ_WEAPON: { // if not a bomb, return if ( !(Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB) ) { return; } // if bomb is on same team as player, return if ( (obj_team(objp) == Player_ship->team) && (Player_ship->team != TEAM_TRAITOR) ) { return; } break; } default: return; // if any other kind of object, don't want to show on radar break; } // end switch // JAS -- new way of getting the rotated point that doesn't require this to be // in a g3_start_frame/end_frame block. vm_vec_sub(&tempv,world_pos,&Player_obj->pos); vm_vec_rotate( &pos, &tempv, &Player_obj->orient ); // Apply range filter dist = vm_vec_dist(world_pos, &Player_obj->pos); max_radar_dist = Radar_ranges[HUD_config.rp_dist]; if ( dist > max_radar_dist ){ return; } if ( dist < pos.xyz.z ) { rscale = 0.0f; } else { rscale = (float) acos( pos.xyz.z/dist ) / 3.14159f; //2.0f; } zdist = fl_sqrt( (pos.xyz.x*pos.xyz.x)+(pos.xyz.y*pos.xyz.y) ); float new_x_dist, clipped_x_dist; float new_y_dist, clipped_y_dist; if (zdist < 0.01f ) { new_x_dist = 0.0f; new_y_dist = 0.0f; } else { new_x_dist = (pos.xyz.x/zdist) * rscale * radx; new_y_dist = (pos.xyz.y/zdist) * rscale * rady; // force new_x_dist and new_y_dist to be inside the radar float hypotenuse; float max_radius; hypotenuse = (float)_hypot(new_x_dist, new_y_dist); max_radius = i2fl(Radar_radius[gr_screen.res][0] - 5); if (hypotenuse >= (max_radius) ) { clipped_x_dist = max_radius * (new_x_dist / hypotenuse); clipped_y_dist = max_radius * (new_y_dist / hypotenuse); new_x_dist = clipped_x_dist; new_y_dist = clipped_y_dist; } } xpos = fl2i( Radar_center[gr_screen.res][0] + new_x_dist ); ypos = fl2i( Radar_center[gr_screen.res][1] - new_y_dist ); color = radar_blip_color(objp); // Determine the distance at which we will dim the radar blip if ( timestamp_elapsed(Radar_calc_dim_dist_timer) ) { Radar_calc_dim_dist_timer=timestamp(1000); Radar_dim_range = player_farthest_weapon_range(); if ( Radar_dim_range <= 0 ) { Radar_dim_range=1500.0f; } } blip *b; int blip_dim=0; if ( dist > Radar_dim_range ) { blip_dim=1; } if ( N_blips >= MAX_BLIPS ) { // out of blips, don't plot Int3(); return; } b = &Blips[N_blips]; b->flags=0; // flag the blip as a current target if it is if (OBJ_INDEX(objp) == Player_ai->target_objnum) { b->flags |= BLIP_CURRENT_TARGET; blip_dim = 0; } if ( blip_dim ) { list_append( &Blip_dim_list[color], b ); } else { list_append( &Blip_bright_list[color], b ); } b->x = xpos; b->y = ypos; // see if blip should be drawn distorted if (objp->type == OBJ_SHIP) { // ships specifically hidden from sensors if ( Ships[objp->instance].flags & SF_HIDDEN_FROM_SENSORS ) { b->flags |= BLIP_DRAW_DISTORTED; } // determine if its AWACS distorted if ( awacs_level < 1.0f ){ b->flags |= BLIP_DRAW_DISTORTED; } } N_blips++; }
/** * Draw the shockwave identified by handle * * @param objp pointer to shockwave object */ void shockwave_render_DEPRECATED(object *objp) { shockwave *sw; vertex p; Assert(objp->type == OBJ_SHOCKWAVE); Assert(objp->instance >= 0 && objp->instance < MAX_SHOCKWAVES); memset(&p, 0, sizeof(p)); sw = &Shockwaves[objp->instance]; if( (sw->delay_stamp != -1) && !timestamp_elapsed(sw->delay_stamp)){ return; } if ( (sw->current_bitmap < 0) && (sw->model_id < 0) ) return; // turn off fogging if(The_mission.flags & MISSION_FLAG_FULLNEB){ gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0); } if (sw->model_id > -1) { float model_Interp_scale_xyz = sw->radius / 50.0f; model_set_warp_globals( model_Interp_scale_xyz, model_Interp_scale_xyz, model_Interp_scale_xyz, -1, 1.0f - (sw->radius/sw->outer_radius) ); float dist = vm_vec_dist_quick( &sw->pos, &Eye_position ); model_set_detail_level((int)(dist / (sw->radius * 10.0f))); model_render_DEPRECATED( sw->model_id, &Objects[sw->objnum].orient, &sw->pos, MR_DEPRECATED_NO_LIGHTING | MR_DEPRECATED_NO_FOGGING | MR_DEPRECATED_NORMAL | MR_DEPRECATED_CENTER_ALPHA | MR_DEPRECATED_NO_CULL, sw->objnum); model_set_warp_globals(); if(Cmdline_fb_explosions) { g3_transfer_vertex(&p, &sw->pos); distortion_add_bitmap_rotated( Shockwave_info[1].bitmap_id+shockwave_get_framenum(objp->instance, 94), TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD | TMAP_FLAG_DISTORTION, &p, fl_radians(sw->rot_angles.p), sw->radius, ((sw->time_elapsed/sw->total_time)>0.9f)?(1.0f-(sw->time_elapsed/sw->total_time))*10.0f:1.0f ); } }else{ if (!Cmdline_nohtl) { g3_transfer_vertex(&p, &sw->pos); } else { g3_rotate_vertex(&p, &sw->pos); } if(Cmdline_fb_explosions) { distortion_add_bitmap_rotated( sw->current_bitmap, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD | TMAP_FLAG_DISTORTION, &p, fl_radians(sw->rot_angles.p), sw->radius, ((sw->time_elapsed/sw->total_time)>0.9f)?(1.0f-(sw->time_elapsed/sw->total_time))*10.0f:1.0f ); } batch_add_bitmap_rotated( sw->current_bitmap, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD, &p, fl_radians(sw->rot_angles.p), sw->radius ); } }
void radar_frame_render(float frametime) { float sensors_str; int ok_to_blit_radar; ok_to_blit_radar = 1; sensors_str = ship_get_subsystem_strength( Player_ship, SUBSYSTEM_SENSORS ); if ( ship_subsys_disrupted(Player_ship, SUBSYSTEM_SENSORS) ) { sensors_str = MIN_SENSOR_STR_TO_RADAR-1; } // note that on lowest skill level, there is no radar effects due to sensors damage if ( (Game_skill_level == 0) || (sensors_str > SENSOR_STR_RADAR_NO_EFFECTS) ) { Radar_static_playing = 0; Radar_static_next = 0; Radar_death_timer = 0; Radar_avail_prev_frame = 1; } else if ( sensors_str < MIN_SENSOR_STR_TO_RADAR ) { if ( Radar_avail_prev_frame ) { Radar_death_timer = timestamp(2000); Radar_static_next = 1; } Radar_avail_prev_frame = 0; } else { Radar_death_timer = 0; if ( Radar_static_next == 0 ) Radar_static_next = 1; } if ( timestamp_elapsed(Radar_death_timer) ) { ok_to_blit_radar = 0; } hud_set_gauge_color(HUD_RADAR); radar_blit_gauge(); radar_draw_range(); if ( timestamp_elapsed(Radar_static_next) ) { Radar_static_playing ^= 1; Radar_static_next = timestamp_rand(50, 750); } // if the emp effect is active, always draw the radar wackily if(emp_active_local()){ Radar_static_playing = 1; } if ( ok_to_blit_radar ) { if ( Radar_static_playing ) { radar_draw_blips_sorted(1); // passing 1 means to draw distorted if ( Radar_static_looping == -1 ) { Radar_static_looping = snd_play_looping(&Snds[SND_STATIC]); } } else { radar_draw_blips_sorted(); if ( Radar_static_looping != -1 ) { snd_stop(Radar_static_looping); Radar_static_looping = -1; } } } else { if ( Radar_static_looping != -1 ) { snd_stop(Radar_static_looping); Radar_static_looping = -1; } } }
// maybe reformat a string void emp_maybe_reformat_text(char *text, int max_len, int gauge_id) { wacky_text *wt; // if the EMP effect is not active, never reformat it if(!emp_active_local()){ return; } // randomly _don't_ apply text craziness if(frand_range(0.0f, 1.0f) > Emp_intensity){ return; } // if the gauge is EG_NULL, empty the string if(gauge_id == EG_NULL){ strcpy(text, ""); return; } // if this gauge has not been wacked out, or if the timestamp has expired, we // neeed to wack it out again Assert((gauge_id >= EG_NULL) && (gauge_id < NUM_TEXT_STAMPS)); wt = &Emp_wacky_text[gauge_id]; if((wt->stamp == -1) || timestamp_elapsed(wt->stamp)){ // reformat specific gauges differently switch(gauge_id){ // weapons case EG_WEAPON_TITLE: case EG_WEAPON_P1: case EG_WEAPON_P2: case EG_WEAPON_P3: case EG_WEAPON_S1: case EG_WEAPON_S2: int wep_index; wep_index = (int)frand_range(0.0f, (float)(MAX_WEAPON_TYPES - 1)); strcpy_s(wt->str, Weapon_info[ wep_index >= MAX_WEAPON_TYPES ? 0 : wep_index ].name); break; // escort list case EG_ESCORT1: case EG_ESCORT2: case EG_ESCORT3: // choose a random ship int shipnum; shipnum = ship_get_random_targetable_ship(); if(shipnum >= 0){ strcpy_s(wt->str, Ships[shipnum].ship_name); } break; // directives title case EG_OBJ_TITLE: strcpy_s(wt->str, ""); break; // directives themselves case EG_OBJ1: case EG_OBJ2: case EG_OBJ3: case EG_OBJ4: case EG_OBJ5: strcpy_s(wt->str, text); emp_randomize_chars(wt->str); break; // target box info case EG_TBOX_EXTRA1: case EG_TBOX_EXTRA2: case EG_TBOX_EXTRA3: case EG_TBOX_CLASS: case EG_TBOX_DIST: case EG_TBOX_CARGO: case EG_TBOX_HULL: case EG_TBOX_NAME: case EG_TBOX_INTEG: strcpy_s(wt->str, text); emp_randomize_chars(wt->str); break; // squadmsg menu case EG_SQ1: case EG_SQ2: case EG_SQ3: case EG_SQ4: case EG_SQ5: case EG_SQ6: case EG_SQ7: case EG_SQ8: case EG_SQ9: case EG_SQ10: strcpy_s(wt->str, text); emp_randomize_chars(wt->str); break; // default default : return; } // recalculate the timestamp wt->stamp = timestamp((int)frand_range(100.0f, 750.0f * (1.0f - Emp_intensity))); // copy the text strcpy(text, wt->str); } // otherwise, use what we calculated last time else { strcpy(text, wt->str); } // watch out for '#' - Goober5000 end_string_at_first_hash_symbol(text); }
// check if ship has turret ready to fire swarm type missiles void turret_swarm_maybe_fire_missile(int shipnum) { ship *shipp = &Ships[shipnum]; ship_subsys *subsys; turret_swarm_info *tsi; object *parent_obj, *target_obj; int num_turret_swarm_turrets_left; int k, j; weapon_info *wip; // check if ship has any turrets ready to fire if (shipp->num_turret_swarm_info <= 0) { Assert(shipp->num_turret_swarm_info == 0); return; } // ship obj which has fired turret swarm missiles parent_obj = &Objects[shipp->objnum]; num_turret_swarm_turrets_left = shipp->num_turret_swarm_info; // search ship subsystems for turrets with valid turret_swarm_info_index for (subsys = GET_FIRST(&shipp->subsys_list); subsys != END_OF_LIST(&shipp->subsys_list); subsys = GET_NEXT(subsys)) { if (subsys->turret_swarm_num > 0) { int swarms_per_turret = subsys->turret_swarm_num; for (k = 0; k < swarms_per_turret; k++) { int turret_tsi = subsys->turret_swarm_info_index[k]; num_turret_swarm_turrets_left--; Assert(num_turret_swarm_turrets_left >= 0); // get turret_swarm_info Assert( (turret_tsi >= 0) && (turret_tsi < MAX_TURRET_SWARM_INFO) ); tsi = &Turret_swarm_info[turret_tsi]; wip = &Weapon_info[tsi->weapon_class]; // check if parent ship is valid (via signature) if ( tsi->parent_sig == parent_obj->signature ) { // make sure we have the right turret. Assert(tsi->turret == subsys); // check if time to fire if (timestamp_elapsed(tsi->time_to_fire)) { Assert(tsi->num_to_launch > 0); // check target still alive if (tsi->target_objnum > -1) { target_obj= &Objects[tsi->target_objnum]; if (target_obj->signature != tsi->target_sig) { // poor target, it died tsi->target_objnum = -1; } } // make sure turret is still alive and fire swarmer if (subsys->current_hits > 0) { turret_swarm_fire_from_turret(tsi); } // *Get timestamp from weapon info's -Et1 if (wip->wi_flags & WIF_SWARM) { tsi->time_to_fire = timestamp( wip->SwarmWait ); } else { tsi->time_to_fire = timestamp( wip->cs_delay ); } // do book keeping tsi->num_to_launch--; if (tsi->num_to_launch == 0) { // we are done firing, so see about resetting any animation timestamps for reversal (closing)... // (I figure that a good estimate is to trigger a close after three additional swarms had fired - taylor) if (subsys->turret_animation_position == MA_POS_READY) subsys->turret_animation_done_time = timestamp( Weapon_info[tsi->weapon_class].SwarmWait * 3); shipp->num_turret_swarm_info--; subsys->turret_swarm_num--; turret_swarm_delete(subsys->turret_swarm_info_index[k]); subsys->turret_swarm_info_index[k] = -1; } } } else { Warning(LOCATION, "Found turret swarm info on ship: %s with turret: %s, but signature does not match.", shipp->ship_name, subsys->system_info->subobj_name); shipp->num_turret_swarm_info--; subsys->turret_swarm_num--; turret_swarm_delete(subsys->turret_swarm_info_index[k]); subsys->turret_swarm_info_index[k] = -1; } } //swarm reset stuff for (k = 0; k < (MAX_TFP - 1); k++) { for (j = (k + 1); j < MAX_TFP; j++) { if ((subsys->turret_swarm_info_index[k] == -1) && (subsys->turret_swarm_info_index[j] != -1)) { subsys->turret_swarm_info_index[k] = subsys->turret_swarm_info_index[j]; subsys->turret_swarm_info_index[j] = -1; } } } } } Assert(num_turret_swarm_turrets_left == 0); }
void radar_plot_object( object *objp ) { vec3d pos, tempv; float awacs_level, dist, max_radar_dist; vec3d world_pos = objp->pos; SCP_list<CJumpNode>::iterator jnp; // don't process anything here. Somehow, a jumpnode object caused this function // to get entered on server side. if( Game_mode & GM_STANDALONE_SERVER ){ return; } // multiplayer clients ingame joining should skip this function if ( MULTIPLAYER_CLIENT && (Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ){ return; } // get team-wide awacs level for the object if not ship int ship_is_visible = 0; if (objp->type == OBJ_SHIP) { if (Player_ship != NULL) { if (ship_is_visible_by_team(objp, Player_ship)) { ship_is_visible = 1; } } } // only check awacs level if ship is not visible by team awacs_level = 1.5f; if (Player_ship != NULL && !ship_is_visible) { awacs_level = awacs_get_level(objp, Player_ship); } // if the awacs level is unviewable - bail if(awacs_level < 0.0f && !See_all){ return; } // Apply object type filters switch (objp->type) { case OBJ_SHIP: // Place to cull ships, such as NavBuoys break; case OBJ_JUMP_NODE: { for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) { if(jnp->GetSCPObject() == objp) break; } // don't plot hidden jump nodes if ( jnp->IsHidden() ) return; // filter jump nodes here if required break; } case OBJ_WEAPON: { // if not a bomb, return if ( !(Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags2 & WIF2_SHOWN_ON_RADAR) ) if ( !(Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB) ) return; // if explicitly hidden, return if (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags2 & WIF2_DONT_SHOW_ON_RADAR) return; // if we don't attack the bomb, return if ( (!(Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags2 & WIF2_SHOW_FRIENDLY)) && (!iff_x_attacks_y(Player_ship->team, obj_team(objp)))) return; // if a local ssm is in subspace, return if (Weapons[objp->instance].lssm_stage == 3) return; // if corkscrew missile use last frame pos for pos if ( (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_CORKSCREW) ) world_pos = objp->last_pos; break; } // if any other kind of object, don't show it on radar default: return; } // Retrieve the eye orientation so we can position the blips relative to it matrix eye_orient; if (Player_obj->type == OBJ_SHIP) ship_get_eye(&tempv, &eye_orient, Player_obj, false , false); else eye_orient = Player_obj->orient; // JAS -- new way of getting the rotated point that doesn't require this to be // in a g3_start_frame/end_frame block. vm_vec_sub(&tempv, &world_pos, &Player_obj->pos); vm_vec_rotate(&pos, &tempv, &eye_orient); // Apply range filter dist = vm_vec_dist(&world_pos, &Player_obj->pos); max_radar_dist = Radar_ranges[HUD_config.rp_dist]; if (dist > max_radar_dist) { return; } // determine the range within which the radar blip is bright if (timestamp_elapsed(Radar_calc_bright_dist_timer)) { Radar_calc_bright_dist_timer = timestamp(1000); Radar_bright_range = player_farthest_weapon_range(); if (Radar_bright_range <= 0) Radar_bright_range = 1500.0f; } blip *b; int blip_bright = 0; int blip_type = 0; if (N_blips >= MAX_BLIPS) { return; } b = &Blips[N_blips]; b->flags = 0; // bright if within range blip_bright = (dist <= Radar_bright_range); // flag the blip as a current target if it is if (OBJ_INDEX(objp) == Player_ai->target_objnum) { b->flags |= BLIP_CURRENT_TARGET; blip_bright = 1; } radar_stuff_blip_info(objp, blip_bright, &b->blip_color, &blip_type); if (blip_bright) list_append(&Blip_bright_list[blip_type], b); else list_append(&Blip_dim_list[blip_type], b); b->position = pos; b->dist = dist; b->objp = objp; b->radar_image_2d = -1; b->radar_color_image_2d = -1; b->radar_image_size = -1; b->radar_projection_size = 1.0f; // see if blip should be drawn distorted // also determine if alternate image was defined for this ship if (objp->type == OBJ_SHIP) { // ships specifically hidden from sensors if (Ships[objp->instance].flags & SF_HIDDEN_FROM_SENSORS) b->flags |= BLIP_DRAW_DISTORTED; // determine if its AWACS distorted if (awacs_level < 1.0f) b->flags |= BLIP_DRAW_DISTORTED; ship_info *Iff_ship_info = &Ship_info[Ships[objp->instance].ship_info_index]; if (Iff_ship_info->radar_image_2d_idx >= 0 || Iff_ship_info->radar_color_image_2d_idx >= 0) { b->radar_image_2d = Iff_ship_info->radar_image_2d_idx; b->radar_color_image_2d = Iff_ship_info->radar_color_image_2d_idx; b->radar_image_size = Iff_ship_info->radar_image_size; b->radar_projection_size = Iff_ship_info->radar_projection_size_mult; } } // don't distort the sensor blips if the player has primitive sensors and the nebula effect // is not active if (Player_ship->flags2 & SF2_PRIMITIVE_SENSORS) { if (!(The_mission.flags & MISSION_FLAG_FULLNEB)) b->flags &= ~BLIP_DRAW_DISTORTED; } N_blips++; }
/** * Simulate a single shockwave. If the shockwave radius exceeds outer_radius, then * delete the shockwave. * * @param shockwave_objp object pointer that points to shockwave object * @param frametime time to simulate shockwave */ void shockwave_move(object *shockwave_objp, float frametime) { shockwave *sw; object *objp; float blast,damage; int i; Assert(shockwave_objp->type == OBJ_SHOCKWAVE); Assert(shockwave_objp->instance >= 0 && shockwave_objp->instance < MAX_SHOCKWAVES); sw = &Shockwaves[shockwave_objp->instance]; // if the shockwave has a delay on it if(sw->delay_stamp != -1){ if(timestamp_elapsed(sw->delay_stamp)){ sw->delay_stamp = -1; } else { return; } } sw->time_elapsed += frametime; shockwave_set_framenum(shockwave_objp->instance); sw->radius += (frametime * sw->speed); if ( sw->radius > sw->outer_radius ) { sw->radius = sw->outer_radius; shockwave_objp->flags |= OF_SHOULD_BE_DEAD; return; } // blast ships and asteroids // And (some) weapons for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) && (objp->type != OBJ_WEAPON)) { continue; } if ( objp->type == OBJ_WEAPON ) { // only apply to missiles with hitpoints weapon_info* wip = &Weapon_info[Weapons[objp->instance].weapon_info_index]; if (wip->weapon_hitpoints <= 0 || !(wip->wi_flags2 & WIF2_TAKES_SHOCKWAVE_DAMAGE)) continue; if (sw->weapon_info_index >= 0) { if (Weapon_info[sw->weapon_info_index].wi_flags2 & WIF2_CIWS) { continue; } } } if ( objp->type == OBJ_SHIP ) { // don't blast navbuoys if ( ship_get_SIF(objp->instance) & SIF_NAVBUOY ) { continue; } } // only apply damage to a ship once from a shockwave for ( i = 0; i < sw->num_objs_hit; i++ ) { if ( objp->signature == sw->obj_sig_hitlist[i] ){ break; } } if ( i < sw->num_objs_hit ){ continue; } if ( weapon_area_calc_damage(objp, &sw->pos, sw->inner_radius, sw->outer_radius, sw->blast, sw->damage, &blast, &damage, sw->radius) == -1 ){ continue; } // okay, we have damage applied, record the object signature so we don't repeatedly apply damage Assert(sw->num_objs_hit < SW_MAX_OBJS_HIT); if ( sw->num_objs_hit >= SW_MAX_OBJS_HIT) { sw->num_objs_hit--; } weapon_info* wip = NULL; switch(objp->type) { case OBJ_SHIP: sw->obj_sig_hitlist[sw->num_objs_hit++] = objp->signature; // If we're doing an AoE Electronics shockwave, do the electronics stuff. -MageKing17 if ( (sw->weapon_info_index >= 0) && (Weapon_info[sw->weapon_info_index].wi_flags3 & WIF3_AOE_ELECTRONICS) && !(objp->flags & OF_INVULNERABLE) ) { weapon_do_electronics_effect(objp, &sw->pos, sw->weapon_info_index); } ship_apply_global_damage(objp, shockwave_objp, &sw->pos, damage ); weapon_area_apply_blast(NULL, objp, &sw->pos, blast, 1); break; case OBJ_ASTEROID: asteroid_hit(objp, NULL, NULL, damage); break; case OBJ_WEAPON: wip = &Weapon_info[Weapons[objp->instance].weapon_info_index]; if (wip->armor_type_idx >= 0) damage = Armor_types[wip->armor_type_idx].GetDamage(damage, shockwave_get_damage_type_idx(shockwave_objp->instance),1.0f); objp->hull_strength -= damage; if (objp->hull_strength < 0.0f) { Weapons[objp->instance].lifeleft = 0.01f; Weapons[objp->instance].weapon_flags |= WF_DESTROYED_BY_WEAPON; } break; default: Int3(); break; } // If this shockwave hit the player, play shockwave impact sound if ( objp == Player_obj ) { float full_damage, vol_scale; if (sw->weapon_info_index >= 0) { full_damage = Weapon_info[sw->weapon_info_index].damage; } else { full_damage = sw->damage; } if (full_damage != 0.0f) { vol_scale = MAX(0.4f, damage/full_damage); } else { vol_scale = 1.0f; } snd_play( &Snds[SND_SHOCKWAVE_IMPACT], 0.0f, vol_scale ); } } // end for }
void HudGaugeRadarDradis::render(float frametime) { float sensors_str; int ok_to_blit_radar; ok_to_blit_radar = 1; sensors_str = ship_get_subsystem_strength(Player_ship, SUBSYSTEM_SENSORS); if (ship_subsys_disrupted(Player_ship, SUBSYSTEM_SENSORS)) sensors_str = MIN_SENSOR_STR_TO_RADAR - 1; // note that on lowest skill level, there is no radar effects due to sensors damage if ((Game_skill_level == 0) || (sensors_str > SENSOR_STR_RADAR_NO_EFFECTS)) { Radar_static_playing = 0; Radar_static_next = 0; Radar_death_timer = 0; Radar_avail_prev_frame = 1; } else if (sensors_str < MIN_SENSOR_STR_TO_RADAR) { if (Radar_avail_prev_frame) { Radar_death_timer = timestamp(2000); Radar_static_next = 1; } Radar_avail_prev_frame = 0; } else { Radar_death_timer = 0; if (Radar_static_next == 0) Radar_static_next = 1; } if (timestamp_elapsed(Radar_death_timer)) ok_to_blit_radar = 0; setupViewHtl(); //WMC - This strikes me as a bit hackish bool g3_yourself = !g3_in_frame(); if(g3_yourself) g3_start_frame(1); drawSweeps(); if (timestamp_elapsed(Radar_static_next)) { Radar_static_playing ^= 1; Radar_static_next = timestamp_rand(50, 750); } // if the emp effect is active, always draw the radar wackily if (emp_active_local()) Radar_static_playing = 1; if (ok_to_blit_radar) { if (Radar_static_playing) { drawBlipsSorted(1); // passing 1 means to draw distorted if (Radar_static_looping == -1) Radar_static_looping = snd_play_looping(&Snds[SND_STATIC]); } else { drawBlipsSorted(0); if (Radar_static_looping != -1) { snd_stop(Radar_static_looping); Radar_static_looping = -1; } } } else { if (Radar_static_looping != -1) { snd_stop(Radar_static_looping); Radar_static_looping = -1; } } if(g3_yourself) g3_end_frame(); doneDrawingHtl(); }
void shockwave_render(object *objp, draw_list *scene) { shockwave *sw; vertex p; Assert(objp->type == OBJ_SHOCKWAVE); Assert(objp->instance >= 0 && objp->instance < MAX_SHOCKWAVES); sw = &Shockwaves[objp->instance]; if( (sw->delay_stamp != -1) && !timestamp_elapsed(sw->delay_stamp)){ return; } if ( (sw->current_bitmap < 0) && (sw->model_id < 0) ) return; if (sw->model_id > -1) { vec3d scale; scale.xyz.x = scale.xyz.y = scale.xyz.z = sw->radius / 50.0f; model_render_params render_info; render_info.set_warp_params(-1, 1.0f - (sw->radius/sw->outer_radius), scale); float dist = vm_vec_dist_quick( &sw->pos, &Eye_position ); render_info.set_detail_level_lock((int)(dist / (sw->radius * 10.0f))); render_info.set_flags(MR_NO_LIGHTING | MR_NO_FOGGING | MR_NORMAL | MR_CENTER_ALPHA | MR_NO_CULL | MR_NO_BATCH); render_info.set_object_number(sw->objnum); model_render_queue( &render_info, scene, sw->model_id, &Objects[sw->objnum].orient, &sw->pos); if ( Cmdline_fb_explosions ) { g3_transfer_vertex(&p, &sw->pos); distortion_add_bitmap_rotated( Shockwave_info[1].bitmap_id+shockwave_get_framenum(objp->instance, 94), TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD | TMAP_FLAG_DISTORTION, &p, fl_radians(sw->rot_angles.p), sw->radius, ((sw->time_elapsed/sw->total_time)>0.9f)?(1.0f-(sw->time_elapsed/sw->total_time))*10.0f:1.0f ); } } else { if (!Cmdline_nohtl) { g3_transfer_vertex(&p, &sw->pos); } else { g3_rotate_vertex(&p, &sw->pos); } if ( Cmdline_fb_explosions ) { distortion_add_bitmap_rotated( sw->current_bitmap, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD | TMAP_FLAG_DISTORTION, &p, fl_radians(sw->rot_angles.p), sw->radius, ((sw->time_elapsed/sw->total_time)>0.9f)?(1.0f-(sw->time_elapsed/sw->total_time))*10.0f:1.0f ); } batch_add_bitmap_rotated( sw->current_bitmap, TMAP_FLAG_TEXTURED | TMAP_HTL_3D_UNLIT | TMAP_FLAG_SOFT_QUAD, &p, fl_radians(sw->rot_angles.p), sw->radius ); } }
void HudGaugeRadarDradis::doBeeps() { if (!this->shouldDoSounds()) { return; } if (Missiontime == 0 || Missiontime == Frametime) { // don't play sounds in first frame return; } if (arrival_beep_snd < 0 && departure_beep_snd < 0 && m_stealth_arrival_snd < 0 && stealth_departure_snd < 0) { return; } bool departure_happened = false; bool stealth_departure_happened = false; bool arrival_happened = false; bool stealth_arrival_happened = false; for (int i = 0; i < MAX_SHIPS; i++) { ship * shipp = &Ships[i]; if (shipp->objnum >= 0) { if (shipp->radar_visible_since >= 0 || shipp->radar_last_contact >= 0) { if (shipp->radar_visible_since == Missiontime) { if (shipp->radar_current_status == DISTORTED) { stealth_arrival_happened = true; } else { arrival_happened = true; } } else if (shipp->radar_visible_since < 0 && shipp->radar_last_contact == Missiontime) { if (shipp->radar_last_status == DISTORTED) { stealth_departure_happened = true; } else { departure_happened = true; } } } } } if (timestamp_elapsed(arrival_beep_next_check)) { if (arrival_beep_snd >= 0 && arrival_happened) { snd_play(&Snds[arrival_beep_snd]); arrival_beep_next_check = timestamp(arrival_beep_delay); } else if (m_stealth_arrival_snd >= 0 && stealth_arrival_happened) { snd_play(&Snds[m_stealth_arrival_snd]); arrival_beep_next_check = timestamp(arrival_beep_delay); } } if (timestamp_elapsed(departure_beep_next_check)) { if (departure_beep_snd >= 0 && departure_happened) { snd_play(&Snds[departure_beep_snd]); departure_beep_next_check = timestamp(departure_beep_delay); } else if (stealth_departure_snd >= 0 && stealth_departure_happened) { snd_play(&Snds[stealth_departure_snd]); departure_beep_next_check = timestamp(departure_beep_delay); } } }
// ------------------------------------------------------------------------------------ // shockwave_move() // // Simulate a single shockwave. If the shockwave radius exceeds outer_radius, then // delete the shockwave. // // input: ojbp => object pointer that points to shockwave object // frametime => time to simulate shockwave // void shockwave_move(object *shockwave_objp, float frametime) { shockwave *sw; object *objp; float blast,damage; int i; Assert(shockwave_objp->type == OBJ_SHOCKWAVE); Assert(shockwave_objp->instance >= 0 && shockwave_objp->instance < MAX_SHOCKWAVES); sw = &Shockwaves[shockwave_objp->instance]; // if the shockwave has a delay on it if(sw->delay_stamp != -1){ if(timestamp_elapsed(sw->delay_stamp)){ sw->delay_stamp = -1; } else { return; } } sw->time_elapsed += frametime; /* if ( sw->time_elapsed > sw->total_time ) { shockwave_objp->flags |= OF_SHOULD_BE_DEAD; } */ shockwave_set_framenum(shockwave_objp->instance); sw->radius += (frametime * sw->speed); if ( sw->radius > sw->outer_radius ) { sw->radius = sw->outer_radius; shockwave_objp->flags |= OF_SHOULD_BE_DEAD; return; } // blast ships and asteroids for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) ) { continue; } if ( objp->type == OBJ_SHIP ) { // don't blast navbuoys if ( ship_get_SIF(objp->instance) & SIF_NAVBUOY ) { continue; } } // only apply damage to a ship once from a shockwave for ( i = 0; i < sw->num_objs_hit; i++ ) { if ( objp->signature == sw->obj_sig_hitlist[i] ){ break; } } if ( i < sw->num_objs_hit ){ continue; } if ( weapon_area_calc_damage(objp, &sw->pos, sw->inner_radius, sw->outer_radius, sw->blast, sw->damage, &blast, &damage, sw->radius) == -1 ){ continue; } // okay, we have damage applied, record the object signature so we don't repeatedly apply damage Assert(sw->num_objs_hit < SW_MAX_OBJS_HIT); if ( sw->num_objs_hit >= SW_MAX_OBJS_HIT) { sw->num_objs_hit--; } switch(objp->type) { case OBJ_SHIP: sw->obj_sig_hitlist[sw->num_objs_hit++] = objp->signature; ship_apply_global_damage(objp, shockwave_objp, &sw->pos, damage ); weapon_area_apply_blast(NULL, objp, &sw->pos, blast, 1); break; case OBJ_ASTEROID: asteroid_hit(objp, NULL, NULL, damage); break; default: Int3(); break; } // If this shockwave hit the player, play shockwave impact sound if ( objp == Player_obj ) { snd_play( &Snds[SND_SHOCKWAVE_IMPACT], 0.0f, MAX(0.4f, damage/Weapon_info[sw->weapon_info_index].damage) ); } } // end for }
// displays (renders) the training message to the screen void message_training_display() { char *str, buf[256]; int i, z, x, y, height, mode, count; Training_msg_visible = 0; message_training_que_check(); training_obj_display(); if (Training_failure){ return; } if (timestamp_elapsed(Training_msg_timestamp) || !strlen(Training_text)){ return; } message_translate_tokens(Training_buf, Training_text); training_process_msg(Training_text); Training_num_lines = split_str(Training_buf, TRAINING_LINE_WIDTH, Training_line_sizes, Training_lines, MAX_TRAINING_MSG_LINES); Assert(Training_num_lines > 0); for (i=0; i<Training_num_lines; i++) { Training_lines[i][Training_line_sizes[i]] = 0; drop_leading_white_space(Training_lines[i]); } if (Training_num_lines <= 0){ return; } height = gr_get_font_height(); gr_set_shader(&Training_msg_glass); gr_shade(Training_msg_window_coords[gr_screen.res][0], Training_msg_window_coords[gr_screen.res][1], TRAINING_MSG_WINDOW_WIDTH, Training_num_lines * height + height); gr_set_color_fast(&Color_bright_blue); mode = count = 0; Training_msg_visible = 1; for (i=0; i<Training_num_lines; i++) { // loop through all lines of message str = Training_lines[i]; z = 0; x = Training_msg_window_coords[gr_screen.res][0] + (TRAINING_MSG_WINDOW_WIDTH - TRAINING_LINE_WIDTH) / 2; y = Training_msg_window_coords[gr_screen.res][1] + i * height + height / 2 + 1; while (*str) { // loop through each character of each line if ((count < MAX_TRAINING_MSG_MODS) && (str == Training_msg_mods[count].pos)) { buf[z] = 0; gr_printf(x, y, buf); gr_get_string_size(&z, NULL, buf); x += z; z = 0; mode = Training_msg_mods[count++].mode; switch (mode) { case TMMOD_NORMAL: gr_set_color_fast(&Color_bright_blue); break; case TMMOD_BOLD: gr_set_color_fast(&Color_white); break; } } buf[z++] = *str++; } if (z) { buf[z] = 0; gr_printf(x, y, "%s", buf); } } Training_msg_method = 0; // if (Training_msg_method) { // char *msg = "Press a key to continue"; // gr_get_string_size(&i, NULL, msg); // gr_printf(TRAINING_MSG_WINDOW_X + TRAINING_MSG_WINDOW_WIDTH / 2 - i / 2, TRAINING_MSG_WINDOW_Y + (Training_num_lines + 2) * height, msg); // } if ((Training_voice >= 0) && (Training_num_lines > 0) && !(Training_msg_timestamp)) { if (Training_voice_type) z = audiostream_is_playing(Training_voice_handle); else z = snd_is_playing(Training_voice_handle); if (!z) Training_msg_timestamp = timestamp(2000); // 2 second delay } }