// ----------------------------------------------------------------------------- // Increase the size of Cursegp in dimension dimension by amount int segsize_common(int dimension, fix amount) { int i; int propagated[MAX_SIDES_PER_SEGMENT]; vms_vector uvec, rvec, fvec, scalevec; Degenerate_segment_found = 0; med_scale_segment_new(Cursegp, dimension, amount); med_extract_up_vector_from_segment_side(Cursegp, Curside, &uvec); med_extract_right_vector_from_segment_side(Cursegp, Curside, &rvec); extract_forward_vector_from_segment(Cursegp, &fvec); scalevec.x = vm_vec_mag(&rvec); scalevec.y = vm_vec_mag(&uvec); scalevec.z = vm_vec_mag(&fvec); if (Degenerate_segment_found) { Degenerate_segment_found = 0; // mprintf(0, "Applying scale would create degenerate segments. Aborting scale.\n"); editor_status("Applying scale would create degenerate segments. Aborting scale."); med_scale_segment_new(Cursegp, dimension, -amount); return 1; } med_create_new_segment(&scalevec); // For all segments to which Cursegp is connected, propagate tmap (uv coordinates) from the connected // segment back to Cursegp. This will meaningfully propagate uv coordinates to all sides which havve // an incident edge. It will also do some sides more than once. And it is probably just not what you want. for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) propagated[i] = 0; for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) if (IS_CHILD(Cursegp->children[i])) { int s; for (s=0; s<MAX_SIDES_PER_SEGMENT; s++) propagated[s]++; propagated[Side_opposite[i]]--; med_propagate_tmaps_to_segments(&Segments[Cursegp->children[i]],Cursegp,1); } // Now, for all sides that were not adjacent to another side, and therefore did not get tmaps // propagated to them, treat as a back side. for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) if (!propagated[i]) { med_propagate_tmaps_to_back_side(Cursegp, i, 1); } // New stuff, assign default texture to all affected sides. Update_flags |= UF_WORLD_CHANGED; mine_changed = 1; return 1; }
void create_low_detail_poly(int global_index, vec3d *tcp, vec3d *rightv, vec3d *upv) { float scale; gshield_tri *trip; trip = &Global_tris[global_index]; scale = vm_vec_mag(tcp) * 2.0f; vm_vec_scale_add(&trip->verts[0].pos, tcp, rightv, -scale/2.0f); vm_vec_scale_add2(&trip->verts[0].pos, upv, scale/2.0f); vm_vec_scale_add(&trip->verts[1].pos, &trip->verts[0].pos, rightv, scale); vm_vec_scale_add(&trip->verts[2].pos, &trip->verts[1].pos, upv, -scale); vm_vec_scale_add(&trip->verts[3].pos, &trip->verts[2].pos, rightv, -scale); // Set u, v coordinates. // Note, this need only be done once, as it's common for all explosions. trip->verts[0].u = 0.0f; trip->verts[0].v = 0.0f; trip->verts[1].u = 1.0f; trip->verts[1].v = 0.0f; trip->verts[2].u = 1.0f; trip->verts[2].v = 1.0f; trip->verts[3].u = 0.0f; trip->verts[3].v = 1.0f; }
// ----------------------------------------------------------------------------- // Applies an instantaneous whack on an object, resulting in an instantaneous // change in orientation. void phys_apply_rot(object *obj,vms_vector *force_vec) { fix rate, vecmag; if (obj->movement_type != MT_PHYSICS) return; vecmag = vm_vec_mag(force_vec)/8; if (vecmag < F1_0/256) rate = 4*F1_0; else if (vecmag < obj->mtype.phys_info.mass >> 14) rate = 4*F1_0; else { rate = fixdiv(obj->mtype.phys_info.mass, vecmag); if (obj->type == OBJ_ROBOT) { if (rate < F1_0/4) rate = F1_0/4; obj->ctype.ai_info.SKIP_AI_COUNT = 2; } else { if (rate < F1_0/2) rate = F1_0/2; } } // Turn amount inversely proportional to mass. Third parameter is seconds to do 360 turn. physics_turn_towards_vector(force_vec, obj, rate); }
// if i'm an observer, zoom to near my targted object (if any) void multi_obs_zoom_to_target() { vec3d direct; float dist; // if i'm not an observer, do nothing if (!(Game_mode & GM_MULTIPLAYER) || (Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type != OBJ_OBSERVER)) { return; } // if I have no targeted object, do nothing if (Player_ai->target_objnum == -1) { return; } // get the normalized direction vector between the observer and the targeted object vm_vec_sub(&direct, &Objects[Player_ai->target_objnum].pos, &Player_obj->pos); dist = vm_vec_mag(&direct); vm_vec_normalize(&direct); // orient the guy correctly vm_vec_ang_2_matrix(&Player_obj->orient, &direct, 0.0f); // keep about 3 object radii away when moving dist -= (Objects[Player_ai->target_objnum].radius * 3.0f); // get the movement vector vm_vec_scale(&direct, dist); // move vm_vec_add2(&Player_obj->pos, &direct); }
// given a just fired flak shell, pick a detonating distance for it void flak_pick_range(object *objp, vector *predicted_target_pos, float weapon_subsys_strength) { float final_range; vector temp; // make sure this flak object is valid Assert(objp->type == OBJ_WEAPON); Assert(objp->instance >= 0); Assert(Weapons[objp->instance].weapon_info_index >= 0); Assert(Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_FLAK); // if the flak index is invalid, do nothing - if this fails the flak simply becomes a non-rendering bullet if(Weapons[objp->instance].flak_index < 0){ return; } // get the range to the target vm_vec_sub(&temp, &objp->pos, predicted_target_pos); final_range = vm_vec_mag(&temp); // add in some randomness final_range += (Flak_range + (Flak_range * 0.65f * (1.0f - weapon_subsys_strength))) * frand_range(-1.0f, 1.0f); // make sure we're firing at least 10 meters away if(final_range < 10.0f){ final_range = 10.0f; } // set the range flak_set_range(objp, &objp->pos, final_range); }
/* This works like any other clip plane... if this is enabled and you rotate a point, the CC_OFF_USER bit will be set in the clipping codes. It is completely handled by most g3_draw primitives, except maybe lines. As far as performance, when enabled, it will slow down each point rotation (or g3_code_vertex call) by a vector subtraction and dot product. It won't slow anything down for polys that are completely clipped on or off by the plane, and will slow each clipped polygon by not much more than any other clipping we do. */ void g3_start_user_clip_plane( vector *plane_point, vector *plane_normal ) { float mag = vm_vec_mag( plane_normal ); if ( (mag < 0.1f) || (mag > 1.5f ) ) { // Invalid plane_normal passed in. Get Allender (since it is // probably a ship warp in bug:) or John. Int3(); return; } G3_user_clip = 1; if(!Cmdline_nohtl) { G3_user_clip_normal = *plane_normal; G3_user_clip_point = *plane_point; // return; gr_start_clip(); } vm_vec_rotate(&G3_user_clip_normal, plane_normal, &View_matrix ); vm_vec_normalize(&G3_user_clip_normal); vector tempv; vm_vec_sub(&tempv,plane_point,&View_position); vm_vec_rotate(&G3_user_clip_point,&tempv,&View_matrix ); }
int hud_lock_world_pos_in_range(vector *target_world_pos, vector *vec_to_target) { float dist_to_target, weapon_range; weapon_info *wip; ship_weapon *swp; int target_in_range=1; swp = &Player_ship->weapons; wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]]; vm_vec_sub(vec_to_target, target_world_pos, &Player_obj->pos); dist_to_target = vm_vec_mag(vec_to_target); //local ssms are always in range :) if (wip->wi_flags2 & WIF2_LOCAL_SSM) weapon_range=wip->lssm_lock_range; else // calculate the range of the weapon, and only display the lead target indicator when // if the weapon can actually hit the target weapon_range = wip->max_speed * wip->lifetime; // reduce firing range in nebula if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) { weapon_range *= 0.8f; } if (dist_to_target > weapon_range) { target_in_range=0; } return target_in_range; }
//find the distance between a segment and a point static fix compute_dist(const vcsegptr_t seg,const vms_vector &pos) { auto delta = compute_segment_center(seg); vm_vec_sub2(delta,pos); return vm_vec_mag(delta); }
void physics_apply_whack(vec3d *impulse, vec3d *pos, physics_info *pi, matrix *orient, float mass) { vec3d local_torque, torque; // vec3d npos; // Detect null vector. if ((fl_abs(impulse->xyz.x) <= WHACK_LIMIT) && (fl_abs(impulse->xyz.y) <= WHACK_LIMIT) && (fl_abs(impulse->xyz.z) <= WHACK_LIMIT)) return; // first do the rotational velocity // calculate the torque on the body based on the point on the // object that was hit and the momentum being applied to the object vm_vec_crossprod(&torque, pos, impulse); vm_vec_rotate ( &local_torque, &torque, orient ); vec3d delta_rotvel; vm_vec_rotate( &delta_rotvel, &local_torque, &pi->I_body_inv ); vm_vec_scale ( &delta_rotvel, (float) ROTVEL_WHACK_CONST ); vm_vec_add2( &pi->rotvel, &delta_rotvel ); //mprintf(("Whack: %7.3f %7.3f %7.3f\n", pi->rotvel.xyz.x, pi->rotvel.xyz.y, pi->rotvel.xyz.z)); // instant whack on the velocity // reduce damping on all axes pi->flags |= PF_REDUCED_DAMP; update_reduced_damp_timestamp( pi, vm_vec_mag(impulse) ); // find time for shake from weapon to end int dtime = timestamp_until(pi->afterburner_decay); if (dtime < WEAPON_SHAKE_TIME) { pi->afterburner_decay = timestamp( WEAPON_SHAKE_TIME ); } // Goober5000 - pi->mass should probably be just mass, as specified in the header vm_vec_scale_add2( &pi->vel, impulse, 1.0f / mass ); if (!(pi->flags & PF_USE_VEL) && (vm_vec_mag_squared(&pi->vel) > MAX_SHIP_SPEED*MAX_SHIP_SPEED)) { // Get DaveA nprintf(("Physics", "speed reset in physics_apply_whack [speed: %f]\n", vm_vec_mag(&pi->vel))); vm_vec_normalize(&pi->vel); vm_vec_scale(&pi->vel, (float)RESET_SHIP_SPEED); } vm_vec_rotate( &pi->prev_ramp_vel, &pi->vel, orient ); // set so velocity will ramp starting from current speed // ramped velocity is now affected by collision }
// --------------------------------------------------------------------------- int PerturbCursideCommon(fix amount) { int saveSegSizeMode = SegSizeMode; vms_vector fvec, rvec, uvec; fix fmag, rmag, umag; int v; SegSizeMode = SEGSIZEMODE_CURSIDE; Modified_vertex_index = 0; extract_forward_vector_from_segment(Cursegp, &fvec); extract_right_vector_from_segment(Cursegp, &rvec); extract_up_vector_from_segment(Cursegp, &uvec); fmag = vm_vec_mag(&fvec); rmag = vm_vec_mag(&rvec); umag = vm_vec_mag(&uvec); for (v=0; v<4; v++) { vms_vector perturb_vec; perturb_vec.x = fixmul(rmag, rand()*2 - 32767); perturb_vec.y = fixmul(umag, rand()*2 - 32767); perturb_vec.z = fixmul(fmag, rand()*2 - 32767); scale_vert(Cursegp, Cursegp->verts[Side_to_verts[Curside][v]], &perturb_vec, amount); } // validate_segment(Cursegp); // if (SegSizeMode) { // for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) // if (Cursegp->children[i] != -1) // validate_segment(&Segments[Cursegp->children[i]]); // } validate_modified_segments(); SegSizeMode = saveSegSizeMode; Update_flags |= UF_WORLD_CHANGED; mine_changed = 1; return 1; }
//find the distance between a segment and a point fix compute_dist(segment *seg,vms_vector *pos) { vms_vector delta; compute_segment_center(&delta,seg); vm_vec_sub2(&delta,pos); return vm_vec_mag(&delta); }
// maybe detonate a flak shell early/late (call from weapon_process_pre(...)) void flak_maybe_detonate(object *objp) { vector temp; // multiplayer clients should never detonate flak early // if(MULTIPLAYER_CLIENT){ // return; // } // if the shell has gone past its range, blow it up vm_vec_sub(&temp, &objp->pos, &Flak[Weapons[objp->instance].flak_index].start_pos); if(vm_vec_mag(&temp) >= Flak[Weapons[objp->instance].flak_index].range){ weapon_detonate(objp); } }
void physics_collide_whack( vec3d *impulse, vec3d *world_delta_rotvel, physics_info *pi, matrix *orient, bool is_landing ) { vec3d body_delta_rotvel; // Detect null vector. if ((fl_abs(impulse->xyz.x) <= WHACK_LIMIT) && (fl_abs(impulse->xyz.y) <= WHACK_LIMIT) && (fl_abs(impulse->xyz.z) <= WHACK_LIMIT)) return; vm_vec_rotate( &body_delta_rotvel, world_delta_rotvel, orient ); // vm_vec_scale( &body_delta_rotvel, (float) ROTVEL_COLLIDE_WHACK_CONST ); vm_vec_add2( &pi->rotvel, &body_delta_rotvel ); update_reduced_damp_timestamp( pi, vm_vec_mag(impulse) ); // find time for shake from weapon to end if (!is_landing) { int dtime = timestamp_until(pi->afterburner_decay); if (dtime < WEAPON_SHAKE_TIME) { pi->afterburner_decay = timestamp( WEAPON_SHAKE_TIME ); } } pi->flags |= PF_REDUCED_DAMP; vm_vec_scale_add2( &pi->vel, impulse, 1.0f / pi->mass ); // check that collision does not give ship too much speed // reset if too high if (!(pi->flags & PF_USE_VEL) && (vm_vec_mag_squared(&pi->vel) > MAX_SHIP_SPEED*MAX_SHIP_SPEED)) { // Get DaveA nprintf(("Physics", "speed reset in physics_collide_whack [speed: %f]\n", vm_vec_mag(&pi->vel))); vm_vec_normalize(&pi->vel); vm_vec_scale(&pi->vel, (float)RESET_SHIP_SPEED); } vm_vec_rotate( &pi->prev_ramp_vel, &pi->vel, orient ); // set so velocity will ramp starting from current speed // ramped velocity is now affected by collision // rotate previous ramp velocity (in model coord) to be same as vel (in world coords) }
// ----------------------------------------------------------------------------------------------------------- // Simulate a physics object for this frame void physics_sim(vec3d* position, matrix* orient, physics_info* pi, float sim_time) { // check flag which tells us whether or not to do velocity translation if (pi->flags & PF_CONST_VEL) { vm_vec_scale_add2(position, &pi->vel, sim_time); } else { physics_sim_vel(position, pi, sim_time, orient); physics_sim_rot(orient, pi, sim_time); pi->speed = vm_vec_mag(&pi->vel); // Note, cannot use quick version, causes cumulative error, increasing speed. pi->fspeed = vm_vec_dot(&orient->vec.fvec, &pi->vel); // instead of vector magnitude -- use only forward vector since we are only interested in forward velocity } }
// ----------------------------------------------------------------------------- // Applies an instantaneous whack on an object, resulting in an instantaneous // change in orientation. void phys_apply_rot(object *obj,vms_vector *force_vec) { fix rate, vecmag; if (obj->movement_type != MT_PHYSICS) return; vecmag = vm_vec_mag(force_vec)/8; if (vecmag < F1_0/256) rate = 4*F1_0; else if (vecmag < obj->mtype.phys_info.mass >> 14) rate = 4*F1_0; else { rate = fixdiv(obj->mtype.phys_info.mass, vecmag); if (obj->type == OBJ_ROBOT) { if (rate < F1_0/4) rate = F1_0/4; // Changed by mk, 10/24/95, claw guys should not slow down when attacking! if (!Robot_info[obj->id].thief && !Robot_info[obj->id].attack_type) { if (obj->ctype.ai_info.SKIP_AI_COUNT * FrameTime < 3*F1_0/4) { fix tval = fixdiv(F1_0, 8*FrameTime); int addval; addval = f2i(tval); if ( (d_rand() * 2) < (tval & 0xffff)) addval++; obj->ctype.ai_info.SKIP_AI_COUNT += addval; // -- mk: too much stuff making hard to see my debug messages...mprintf((0, "FrameTime = %7.3f, addval = %i\n", f2fl(FrameTime), addval)); } } } else { if (rate < F1_0/2) rate = F1_0/2; } } // Turn amount inversely proportional to mass. Third parameter is seconds to do 360 turn. physics_turn_towards_vector(force_vec, obj, rate); }
void physics_apply_shock(vec3d *direction_vec, float pressure, physics_info *pi, matrix *orient, vec3d *min, vec3d *max, float radius) { vec3d normal; vec3d local_torque, temp_torque, torque; vec3d impact_vec; vec3d area; vec3d sin; if (radius > MAX_RADIUS) { return; } vm_vec_normalize_safe ( direction_vec ); area.xyz.x = (max->xyz.y - min->xyz.z) * (max->xyz.z - min->xyz.z); area.xyz.y = (max->xyz.x - min->xyz.x) * (max->xyz.z - min->xyz.z); area.xyz.z = (max->xyz.x - min->xyz.x) * (max->xyz.y - min->xyz.y); normal.xyz.x = vm_vec_dotprod( direction_vec, &orient->vec.rvec ); normal.xyz.y = vm_vec_dotprod( direction_vec, &orient->vec.uvec ); normal.xyz.z = vm_vec_dotprod( direction_vec, &orient->vec.fvec ); sin.xyz.x = fl_sqrt( fl_abs(1.0f - normal.xyz.x*normal.xyz.x) ); sin.xyz.y = fl_sqrt( fl_abs(1.0f - normal.xyz.y*normal.xyz.y) ); sin.xyz.z = fl_sqrt( fl_abs(1.0f - normal.xyz.z*normal.xyz.z) ); vm_vec_make( &torque, 0.0f, 0.0f, 0.0f ); // find the torque exerted due to the shockwave hitting each face // model the effect of the shockwave as if the shockwave were a plane of projectiles, // all moving in the direction direction_vec. then find the torque as the cross prod // of the force (pressure * area * normal * sin * scale * mass) // normal takes account the fraction of the surface exposed to the shockwave // the sin term is not technically needed but "feels" better // scale factors out the increase in area with larger objects // more massive objects get less rotation // find torque due to forces on the right/left face if ( normal.xyz.x < 0.0f ) // normal < 0, hits the right face vm_vec_copy_scale( &impact_vec, &orient->vec.rvec, max->xyz.x * pressure * area.xyz.x * normal.xyz.x * sin.xyz.x / pi->mass ); else // normal > 0, hits the left face vm_vec_copy_scale( &impact_vec, &orient->vec.rvec, min->xyz.x * pressure * area.xyz.x * -normal.xyz.x * sin.xyz.x / pi->mass ); vm_vec_crossprod( &temp_torque, &impact_vec, direction_vec ); vm_vec_add2( &torque, &temp_torque ); // find torque due to forces on the up/down face if ( normal.xyz.y < 0.0f ) vm_vec_copy_scale( &impact_vec, &orient->vec.uvec, max->xyz.y * pressure * area.xyz.y * normal.xyz.y * sin.xyz.y / pi->mass ); else vm_vec_copy_scale( &impact_vec, &orient->vec.uvec, min->xyz.y * pressure * area.xyz.y * -normal.xyz.y * sin.xyz.y / pi->mass ); vm_vec_crossprod( &temp_torque, &impact_vec, direction_vec ); vm_vec_add2( &torque, &temp_torque ); // find torque due to forces on the forward/backward face if ( normal.xyz.z < 0.0f ) vm_vec_copy_scale( &impact_vec, &orient->vec.fvec, max->xyz.z * pressure * area.xyz.z * normal.xyz.z * sin.xyz.z / pi->mass ); else vm_vec_copy_scale( &impact_vec, &orient->vec.fvec, min->xyz.z * pressure * area.xyz.z * -normal.xyz.z * sin.xyz.z / pi->mass ); vm_vec_crossprod( &temp_torque, &impact_vec, direction_vec ); vm_vec_add2( &torque, &temp_torque ); // compute delta rotvel, scale according to blast and radius float scale; if (radius < MIN_RADIUS) { scale = 1.0f; } else { scale = (MAX_RADIUS - radius)/(MAX_RADIUS-MIN_RADIUS); } // set shockwave shake amplitude, duration, flag pi->shockwave_shake_amp = (float)(MAX_SHAKE*(pressure/STD_PRESSURE)*scale); pi->shockwave_decay = timestamp( SW_BLAST_DURATION ); pi->flags |= PF_IN_SHOCKWAVE; // safety dance if (!(IS_VEC_NULL_SQ_SAFE(&torque))) { vec3d delta_rotvel; vm_vec_rotate( &local_torque, &torque, orient ); vm_vec_copy_normalize(&delta_rotvel, &local_torque); vm_vec_scale(&delta_rotvel, (float)(MAX_ROTVEL*(pressure/STD_PRESSURE)*scale)); // nprintf(("Physics", "rotvel scale %f\n", (MAX_ROTVEL*(pressure/STD_PRESSURE)*scale))); vm_vec_add2(&pi->rotvel, &delta_rotvel); } // set reduced translational damping, set flags float velocity_scale = (float)MAX_VEL*scale; pi->flags |= PF_REDUCED_DAMP; update_reduced_damp_timestamp( pi, velocity_scale*pi->mass ); vm_vec_scale_add2( &pi->vel, direction_vec, velocity_scale ); vm_vec_rotate(&pi->prev_ramp_vel, &pi->vel, orient); // set so velocity will ramp starting from current speed // check that kick from shockwave is not too large if (!(pi->flags & PF_USE_VEL) && (vm_vec_mag_squared(&pi->vel) > MAX_SHIP_SPEED*MAX_SHIP_SPEED)) { // Get DaveA nprintf(("Physics", "speed reset in physics_apply_shock [speed: %f]\n", vm_vec_mag(&pi->vel))); vm_vec_normalize(&pi->vel); vm_vec_scale(&pi->vel, (float)RESET_SHIP_SPEED); } }
int generate_curve( fix r1scale, fix r4scale ) { vms_vector vec_dir, tvec; vms_vector coord,prev_point; vms_equation coeffs; fix enddist, nextdist; int firstsegflag; fix t, maxscale; fixang rangle, uangle; const vcsegptr_t cursegp = Cursegp; compute_center_point_on_side(p1, cursegp, Curside); switch( Curside ) { case WLEFT: extract_right_vector_from_segment(cursegp, r1); vm_vec_scale(r1, -F1_0 ); break; case WTOP: extract_up_vector_from_segment(cursegp, r1); break; case WRIGHT: extract_right_vector_from_segment(cursegp, r1); break; case WBOTTOM: extract_up_vector_from_segment(cursegp, r1); vm_vec_scale(r1, -F1_0 ); break; case WBACK: extract_forward_vector_from_segment(cursegp, r1); break; case WFRONT: extract_forward_vector_from_segment(cursegp, r1); vm_vec_scale(r1, -F1_0 ); break; } const vcsegptr_t markedsegp = Markedsegp; compute_center_point_on_side(p4, markedsegp, Markedside); switch( Markedside ) { case WLEFT: extract_right_vector_from_segment(markedsegp, r4); extract_up_vector_from_segment(markedsegp, r4t); break; case WTOP: extract_up_vector_from_segment(markedsegp, r4); vm_vec_scale(r4, -F1_0 ); extract_forward_vector_from_segment(markedsegp, r4t); vm_vec_scale(r4t, -F1_0 ); break; case WRIGHT: extract_right_vector_from_segment(markedsegp, r4); vm_vec_scale(r4, -F1_0 ); extract_up_vector_from_segment(markedsegp, r4t); break; case WBOTTOM: extract_up_vector_from_segment(markedsegp, r4); extract_forward_vector_from_segment(markedsegp, r4t); break; case WBACK: extract_forward_vector_from_segment(markedsegp, r4); vm_vec_scale(r4, -F1_0 ); extract_up_vector_from_segment(markedsegp, r4t); break; case WFRONT: extract_forward_vector_from_segment(markedsegp, r4); extract_up_vector_from_segment(markedsegp, r4t); break; } r1save = r1; tvec = r1; vm_vec_scale(r1,r1scale); vm_vec_scale(r4,r4scale); create_curve( p1, p4, r1, r4, coeffs ); OriginalSeg = Cursegp; OriginalMarkedSeg = Markedsegp; OriginalSide = Curside; OriginalMarkedSide = Markedside; CurveNumSegs = 0; coord = prev_point = p1; t=0; firstsegflag = 1; enddist = F1_0; nextdist = 0; while ( enddist > fixmul( nextdist, 1.5*F1_0 )) { vms_matrix rotmat; if (firstsegflag==1) firstsegflag=0; else extract_forward_vector_from_segment(cursegp, tvec); nextdist = vm_vec_mag(tvec); // nextdist := distance to next point t = curve_dist(&coeffs, 3, t, prev_point, nextdist); // t = argument at which function is forward vector magnitude units away from prev_point (in 3-space, not along curve) coord = evaluate_curve(&coeffs, 3, t); // coord := point about forward vector magnitude units away from prev_point enddist = vm_vec_dist(coord, p4); // enddist := distance from current to end point, vec_dir used as a temporary variable //vm_vec_normalize(vm_vec_sub(&vec_dir, &coord, &prev_point)); vm_vec_normalized_dir(vec_dir, coord, prev_point); if (!med_attach_segment(Cursegp, vmsegptr(&New_segment), Curside, AttachSide)) { med_extract_matrix_from_segment(cursegp, &rotmat); // rotmat := matrix describing orientation of Cursegp const auto tdest = vm_vec_rotate(vec_dir,rotmat); // tdest := vec_dir in reference frame of Cursegp vec_dir = tdest; const auto rotmat2 = vm_vector_2_matrix(vec_dir,nullptr,nullptr); med_rotate_segment( Cursegp, rotmat2 ); prev_point = coord; Curside = Side_opposite[AttachSide]; CurveSegs[CurveNumSegs]=Cursegp; CurveNumSegs++; } else return 0; } extract_up_vector_from_segment(cursegp, tvec); uangle = vm_vec_delta_ang( tvec, r4t, r4 ); if (uangle >= F1_0 * 1/8) uangle -= F1_0 * 1/4; if (uangle >= F1_0 * 1/8) uangle -= F1_0 * 1/4; if (uangle <= -F1_0 * 1/8) uangle += F1_0 * 1/4; if (uangle <= -F1_0 * 1/8) uangle += F1_0 * 1/4; extract_right_vector_from_segment(cursegp, tvec); rangle = vm_vec_delta_ang( tvec, r4t, r4 ); if (rangle >= F1_0/8) rangle -= F1_0/4; if (rangle >= F1_0/8) rangle -= F1_0/4; if (rangle <= -F1_0/8) rangle += F1_0/4; if (rangle <= -F1_0/8) rangle += F1_0/4; if ((uangle != 0) && (rangle != 0)) { maxscale = CurveNumSegs*F1_0; generate_banked_curve(maxscale, coeffs); } if (CurveNumSegs) { med_form_bridge_segment( Cursegp, Side_opposite[AttachSide], Markedsegp, Markedside ); CurveSegs[CurveNumSegs] = vmsegptr(Markedsegp->children[Markedside]); CurveNumSegs++; } Cursegp = OriginalSeg; Curside = OriginalSide; med_create_new_segment_from_cursegp(); //warn_if_concave_segments(); if (CurveNumSegs) return 1; else return 0; }
// 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 do_object_physics( object * obj ) { vms_angvec rotang; vms_vector frame_vec; //movement in this frame vms_vector new_pos,ipos; //position after this frame int iseg; int hit; vms_matrix rotmat,new_pm; int count=0; short joy_x,joy_y,btns; int joyx_moved,joyy_moved; fix speed; vms_vector *desired_upvec; fixang delta_ang,roll_ang; vms_vector forvec = {0,0,f1_0}; vms_matrix temp_matrix; //check keys rotang.pitch = ROT_SPEED * (key_down_time(KEY_UP) - key_down_time(KEY_DOWN)); rotang.head = ROT_SPEED * (key_down_time(KEY_RIGHT) - key_down_time(KEY_LEFT)); rotang.bank = 0; //check for joystick movement joy_get_pos(&joy_x,&joy_y); btns=joy_get_btns(); joyx_moved = (abs(joy_x - _old_joy_x)>JOY_NULL); joyy_moved = (abs(joy_y - _old_joy_y)>JOY_NULL); if (abs(joy_x) < JOY_NULL) joy_x = 0; if (abs(joy_y) < JOY_NULL) joy_y = 0; if (!rotang.pitch) rotang.pitch = fixmul(-joy_y * 128,FrameTime); if (!rotang.head) rotang.head = fixmul(joy_x * 128,FrameTime); if (joyx_moved) _old_joy_x = joy_x; if (joyy_moved) _old_joy_y = joy_y; speed = ((btns&2) || keyd_pressed[KEY_A])?SLOW_SPEED*3:(keyd_pressed[KEY_Z]?SLOW_SPEED/2:SLOW_SPEED); //now build matrices, do rotations, etc., etc. vm_angles_2_matrix(&rotmat,&rotang); vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat); obj->orient = new_pm; //move player vm_vec_copy_scale(&obj->velocity,&obj->orient.fvec,speed); vm_vec_copy_scale(&frame_vec,&obj->velocity,FrameTime); do { fix wall_part; vms_vector tvec; count++; vm_vec_add(&new_pos,&obj->pos,&frame_vec); hit = find_vector_intersection(&ipos,&iseg,&obj->pos,obj->seg_id,&new_pos,obj->size,-1); obj->seg_id = iseg; obj->pos = ipos; //-FIXJOHN-if (hit==HIT_OBJECT) ExplodeObject(hit_objnum); if (hit==HIT_WALL) { vm_vec_sub(&frame_vec,&new_pos,&obj->pos); //part through wall wall_part = vm_vec_dot(wall_norm,&frame_vec); vm_vec_copy_scale(&tvec,wall_norm,wall_part); if ((wall_part == 0) || (vm_vec_mag(&tvec) < 5)) Int3(); vm_vec_sub2(&frame_vec,&tvec); } } while (hit == HIT_WALL); Assert(check_point_in_seg(&obj->pos,obj->seg_id,0).centermask==0); //now bank player according to segment orientation desired_upvec = &Segments[obj->seg_id].sides[3].faces[0].normal; if (labs(vm_vec_dot(desired_upvec,&obj->orient.fvec)) < f1_0/2) { vm_vector_2_matrix(&temp_matrix,&obj->orient.fvec,desired_upvec,NULL); delta_ang = vm_vec_delta_ang(&obj->orient.uvec,&temp_matrix.uvec,&obj->orient.fvec); if (rotang.head) delta_ang += (rotang.head<0)?TURNROLL_ANG:-TURNROLL_ANG; if (abs(delta_ang) > DAMP_ANG) { roll_ang = fixmul(FrameTime,ROLL_RATE); if (abs(delta_ang) < roll_ang) roll_ang = delta_ang; else if (delta_ang<0) roll_ang = -roll_ang; vm_vec_ang_2_matrix(&rotmat,&forvec,roll_ang); vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat); obj->orient = new_pm; } } }
// ------------------------------------------------------------------ // 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 generate_banked_curve(fix maxscale, vms_equation coeffs) { vms_vector vec_dir, tvec, b4r4t; vms_vector coord,prev_point; fix enddist, nextdist; int firstsegflag; fixang rangle, uangle, angle, scaled_ang=0; fix t; if (CurveNumSegs) { const vcsegptr_t cursegp = Cursegp; extract_up_vector_from_segment(cursegp, b4r4t); uangle = vm_vec_delta_ang( b4r4t, r4t, r4 ); if (uangle >= F1_0 * 1/8) uangle -= F1_0 * 1/4; if (uangle >= F1_0 * 1/8) uangle -= F1_0 * 1/4; if (uangle <= -F1_0 * 1/8) uangle += F1_0 * 1/4; if (uangle <= -F1_0 * 1/8) uangle += F1_0 * 1/4; extract_right_vector_from_segment(cursegp, b4r4t); rangle = vm_vec_delta_ang( b4r4t, r4t, r4 ); if (rangle >= F1_0/8) rangle -= F1_0/4; if (rangle >= F1_0/8) rangle -= F1_0/4; if (rangle <= -F1_0/8) rangle += F1_0/4; if (rangle <= -F1_0/8) rangle += F1_0/4; angle = uangle; if (abs(rangle) < abs(uangle)) angle = rangle; delete_curve(); coord = prev_point = p1; #define MAGIC_NUM 0.707*F1_0 if (maxscale) scaled_ang = fixdiv(angle,fixmul(maxscale,MAGIC_NUM)); t=0; tvec = r1save; firstsegflag = 1; enddist = F1_0; nextdist = 0; while ( enddist > fixmul( nextdist, 1.5*F1_0 )) { vms_matrix rotmat; if (firstsegflag==1) firstsegflag=0; else extract_forward_vector_from_segment(cursegp, tvec); nextdist = vm_vec_mag(tvec); // nextdist := distance to next point t = curve_dist(&coeffs, 3, t, prev_point, nextdist); // t = argument at which function is forward vector magnitude units away from prev_point (in 3-space, not along curve) coord = evaluate_curve(&coeffs, 3, t); // coord := point about forward vector magnitude units away from prev_point enddist = vm_vec_dist(coord, p4); // enddist := distance from current to end point, vec_dir used as a temporary variable //vm_vec_normalize(vm_vec_sub(&vec_dir, &coord, &prev_point)); vm_vec_normalized_dir(vec_dir, coord, prev_point); if (!med_attach_segment(Cursegp, vmsegptr(&New_segment), Curside, AttachSide)) { med_extract_matrix_from_segment(cursegp, &rotmat); // rotmat := matrix describing orientation of Cursegp const auto tdest = vm_vec_rotate(vec_dir,rotmat); // tdest := vec_dir in reference frame of Cursegp vec_dir = tdest; const auto rotmat2 = vm_vec_ang_2_matrix(vec_dir,scaled_ang); med_rotate_segment( Cursegp, rotmat2 ); prev_point = coord; Curside = Side_opposite[AttachSide]; CurveSegs[CurveNumSegs]=Cursegp; CurveNumSegs++; } } } }
// ----------------------------------------------------------------------------------------------------------- //Simulate a physics object for this frame void do_physics_sim(object *obj) { int ignore_obj_list[MAX_IGNORE_OBJS],n_ignore_objs; int iseg; int try_again; int fate=0; vms_vector frame_vec; //movement in this frame vms_vector new_pos,ipos; //position after this frame int count=0; int objnum; int WallHitSeg, WallHitSide; fvi_info hit_info; fvi_query fq; vms_vector save_pos; int save_seg; fix drag; fix sim_time; vms_vector start_pos; int obj_stopped=0; fix moved_time; //how long objected moved before hit something physics_info *pi; int orig_segnum = obj->segnum; fix PhysTime = (FrameTime<F1_0/30?F1_0/30:FrameTime); Assert(obj->movement_type == MT_PHYSICS); #ifndef NDEBUG if (Dont_move_ai_objects) if (obj->control_type == CT_AI) return; #endif pi = &obj->mtype.phys_info; do_physics_sim_rot(obj); if (!(pi->velocity.x || pi->velocity.y || pi->velocity.z || pi->thrust.x || pi->thrust.y || pi->thrust.z)) return; objnum = obj-Objects; n_phys_segs = 0; /* As this engine was not designed for that high FPS as we intend, we use F1_0/30 max. for sim_time to ensure scaling and dot products stay accurate and reliable. The object position intended for this frame will be scaled down later, after the main collision-loop is done. This won't make collision results be equal in all FPS settings, but hopefully more accurate, the higher our FPS are. */ sim_time = PhysTime; //FrameTime; //debug_obj = obj; #ifdef EXTRA_DEBUG //check for correct object segment if(!get_seg_masks(&obj->pos,obj->segnum,0,__FILE__,__LINE__).centermask==0) { //Int3(); Removed by Rob 10/5/94 if (!update_object_seg(obj)) { if (!(Game_mode & GM_MULTI)) Int3(); compute_segment_center(&obj->pos,&Segments[obj->segnum]); obj->pos.x += objnum; } } #endif start_pos = obj->pos; n_ignore_objs = 0; Assert(obj->mtype.phys_info.brakes==0); //brakes not used anymore? //if uses thrust, cannot have zero drag Assert(!(obj->mtype.phys_info.flags&PF_USES_THRUST) || obj->mtype.phys_info.drag!=0); //do thrust & drag // NOTE: this always must be dependent on FrameTime, if sim_time differs! if ((drag = obj->mtype.phys_info.drag) != 0) { int count; vms_vector accel; fix r,k,have_accel; count = FrameTime / FT; r = FrameTime % FT; k = fixdiv(r,FT); if (obj->mtype.phys_info.flags & PF_USES_THRUST) { vm_vec_copy_scale(&accel,&obj->mtype.phys_info.thrust,fixdiv(f1_0,obj->mtype.phys_info.mass)); have_accel = (accel.x || accel.y || accel.z); while (count--) { if (have_accel) vm_vec_add2(&obj->mtype.phys_info.velocity,&accel); vm_vec_scale(&obj->mtype.phys_info.velocity,f1_0-drag); } //do linear scale on remaining bit of time vm_vec_scale_add2(&obj->mtype.phys_info.velocity,&accel,k); if (drag) vm_vec_scale(&obj->mtype.phys_info.velocity,f1_0-fixmul(k,drag)); } else if (drag) { fix total_drag=f1_0; while (count--) total_drag = fixmul(total_drag,f1_0-drag); //do linear scale on remaining bit of time total_drag = fixmul(total_drag,f1_0-fixmul(k,drag)); vm_vec_scale(&obj->mtype.phys_info.velocity,total_drag); } } do { try_again = 0; //Move the object vm_vec_copy_scale(&frame_vec, &obj->mtype.phys_info.velocity, sim_time); if ( (frame_vec.x==0) && (frame_vec.y==0) && (frame_vec.z==0) ) break; count++; // If retry count is getting large, then we are trying to do something stupid. if (count > 8) break; // in original code this was 3 for all non-player objects. still leave us some limit in case fvi goes apeshit. vm_vec_add(&new_pos,&obj->pos,&frame_vec); ignore_obj_list[n_ignore_objs] = -1; fq.p0 = &obj->pos; fq.startseg = obj->segnum; fq.p1 = &new_pos; fq.rad = obj->size; fq.thisobjnum = objnum; fq.ignore_obj_list = ignore_obj_list; fq.flags = FQ_CHECK_OBJS; if (obj->type == OBJ_WEAPON) fq.flags |= FQ_TRANSPOINT; if (obj->type == OBJ_PLAYER) fq.flags |= FQ_GET_SEGLIST; fate = find_vector_intersection(&fq,&hit_info); // Matt: Mike's hack. if (fate == HIT_OBJECT) { object *objp = &Objects[hit_info.hit_object]; if (((objp->type == OBJ_WEAPON) && (objp->id == PROXIMITY_ID)) || objp->type == OBJ_POWERUP) // do not increase count for powerups since they *should* not change our movement count--; } #ifndef NDEBUG if (fate == HIT_BAD_P0) { Int3(); } #endif if (obj->type == OBJ_PLAYER) { int i; if (n_phys_segs && phys_seglist[n_phys_segs-1]==hit_info.seglist[0]) n_phys_segs--; for (i=0;(i<hit_info.n_segs) && (n_phys_segs<MAX_FVI_SEGS-1); ) phys_seglist[n_phys_segs++] = hit_info.seglist[i++]; } ipos = hit_info.hit_pnt; iseg = hit_info.hit_seg; WallHitSide = hit_info.hit_side; WallHitSeg = hit_info.hit_side_seg; if (iseg==-1) { //some sort of horrible error if (obj->type == OBJ_WEAPON) obj->flags |= OF_SHOULD_BE_DEAD; break; } Assert(!((fate==HIT_WALL) && ((WallHitSeg == -1) || (WallHitSeg > Highest_segment_index)))); //if(!get_seg_masks(&hit_info.hit_pnt,hit_info.hit_seg,0).centermask==0) // Int3(); save_pos = obj->pos; //save the object's position save_seg = obj->segnum; // update object's position and segment number obj->pos = ipos; if ( iseg != obj->segnum ) obj_relink(objnum, iseg ); //if start point not in segment, move object to center of segment if (get_seg_masks(&obj->pos,obj->segnum,0,__FILE__,__LINE__).centermask!=0) { int n; if ((n=find_object_seg(obj))==-1) { //Int3(); if (obj->type==OBJ_PLAYER && (n=find_point_seg(&obj->last_pos,obj->segnum))!=-1) { obj->pos = obj->last_pos; obj_relink(objnum, n ); } else { compute_segment_center(&obj->pos,&Segments[obj->segnum]); obj->pos.x += objnum; } if (obj->type == OBJ_WEAPON) obj->flags |= OF_SHOULD_BE_DEAD; } return; } //calulate new sim time { //vms_vector moved_vec; vms_vector moved_vec_n; fix attempted_dist,actual_dist; actual_dist = vm_vec_normalized_dir(&moved_vec_n,&obj->pos,&save_pos); if (fate==HIT_WALL && vm_vec_dot(&moved_vec_n,&frame_vec) < 0) { //moved backwards //don't change position or sim_time obj->pos = save_pos; //iseg = obj->segnum; //don't change segment obj_relink(objnum, save_seg ); moved_time = 0; } else { fix old_sim_time; attempted_dist = vm_vec_mag(&frame_vec); old_sim_time = sim_time; sim_time = fixmuldiv(sim_time,attempted_dist-actual_dist,attempted_dist); moved_time = old_sim_time - sim_time; if (sim_time < 0 || sim_time>old_sim_time) { sim_time = old_sim_time; //WHY DOES THIS HAPPEN?? moved_time = 0; } } } switch( fate ) { case HIT_WALL: { vms_vector moved_v; fix hit_speed=0, wall_part=0; // Find hit speed vm_vec_sub(&moved_v,&obj->pos,&save_pos); wall_part = vm_vec_dot(&moved_v,&hit_info.hit_wallnorm); if ((wall_part != 0 && moved_time>0 && (hit_speed=-fixdiv(wall_part,moved_time))>0) || obj->type == OBJ_WEAPON || obj->type == OBJ_DEBRIS) collide_object_with_wall( obj, hit_speed, WallHitSeg, WallHitSide, &hit_info.hit_pnt ); if (obj->type == OBJ_PLAYER) scrape_player_on_wall(obj, WallHitSeg, WallHitSide, &hit_info.hit_pnt ); Assert( WallHitSeg > -1 ); Assert( WallHitSide > -1 ); if ( !(obj->flags&OF_SHOULD_BE_DEAD) ) { Assert(! (obj->mtype.phys_info.flags & PF_STICK && obj->mtype.phys_info.flags & PF_BOUNCE)); //can't be bounce and stick if (obj->mtype.phys_info.flags & PF_STICK) { //stop moving add_stuck_object(obj, WallHitSeg, WallHitSide); vm_vec_zero(&obj->mtype.phys_info.velocity); obj_stopped = 1; try_again = 0; } else { // Slide object along wall //We're constrained by wall, so subtract wall part from //velocity vector wall_part = vm_vec_dot(&hit_info.hit_wallnorm,&obj->mtype.phys_info.velocity); // if wall_part, make sure the value is sane enough to get usable velocity computed if (wall_part < 0 && wall_part > -f1_0) wall_part = -f1_0; if (wall_part > 0 && wall_part < f1_0) wall_part = f1_0; if (obj->mtype.phys_info.flags & PF_BOUNCE) //bounce off wall wall_part *= 2; //Subtract out wall part twice to achieve bounce vm_vec_scale_add2(&obj->mtype.phys_info.velocity,&hit_info.hit_wallnorm,-wall_part); try_again = 1; } } break; } case HIT_OBJECT: { vms_vector old_vel; // Mark the hit object so that on a retry the fvi code // ignores this object. Assert(hit_info.hit_object != -1); // Calculcate the hit point between the two objects. { vms_vector *ppos0, *ppos1, pos_hit; fix size0, size1; ppos0 = &Objects[hit_info.hit_object].pos; ppos1 = &obj->pos; size0 = Objects[hit_info.hit_object].size; size1 = obj->size; Assert(size0+size1 != 0); // Error, both sizes are 0, so how did they collide, anyway?!? //vm_vec_scale(vm_vec_sub(&pos_hit, ppos1, ppos0), fixdiv(size0, size0 + size1)); //vm_vec_add2(&pos_hit, ppos0); vm_vec_sub(&pos_hit, ppos1, ppos0); vm_vec_scale_add(&pos_hit,ppos0,&pos_hit,fixdiv(size0, size0 + size1)); old_vel = obj->mtype.phys_info.velocity; collide_two_objects( obj, &Objects[hit_info.hit_object], &pos_hit); } // Let object continue its movement if ( !(obj->flags&OF_SHOULD_BE_DEAD) ) { //obj->pos = save_pos; if (obj->mtype.phys_info.flags&PF_PERSISTENT || (old_vel.x == obj->mtype.phys_info.velocity.x && old_vel.y == obj->mtype.phys_info.velocity.y && old_vel.z == obj->mtype.phys_info.velocity.z)) { //if (Objects[hit_info.hit_object].type == OBJ_POWERUP) ignore_obj_list[n_ignore_objs++] = hit_info.hit_object; try_again = 1; } } break; } case HIT_NONE: break; #ifndef NDEBUG case HIT_BAD_P0: Int3(); // Unexpected collision type: start point not in specified segment. break; default: // Unknown collision type returned from find_vector_intersection!! Int3(); break; #endif } } while ( try_again ); // Pass retry count info to AI. if (obj->control_type == CT_AI) { if (count > 0) { Ai_local_info[objnum].retry_count = count-1; Total_retries += count-1; Total_sims++; } } // As sim_time may not base on FrameTime, scale actual object position to get accurate movement if (PhysTime/FrameTime > 0) { vms_vector md; vm_vec_sub(&md, &obj->pos, &start_pos); vm_vec_scale(&md, F1_0/((float)PhysTime/FrameTime)); vm_vec_add(&obj->pos,&start_pos, &md); //check for and update correct object segment if(!get_seg_masks(&obj->pos, obj->segnum, 0, __FILE__, __LINE__).centermask == 0) { if (!update_object_seg(obj)) { if (!(Game_mode & GM_MULTI)) Int3(); compute_segment_center(&obj->pos,&Segments[obj->segnum]); obj->pos.x += objnum; } } } // After collision with objects and walls, set velocity from actual movement if (!obj_stopped && ((obj->type == OBJ_PLAYER) || (obj->type == OBJ_ROBOT) || (obj->type == OBJ_DEBRIS)) && ((fate == HIT_WALL) || (fate == HIT_OBJECT) || (fate == HIT_BAD_P0)) ) { vms_vector moved_vec; vm_vec_sub(&moved_vec,&obj->pos,&start_pos); vm_vec_copy_scale(&obj->mtype.phys_info.velocity,&moved_vec,fixdiv(f1_0,FrameTime)); } fix_illegal_wall_intersection(obj, &start_pos); //Assert(check_point_in_seg(&obj->pos,obj->segnum,0).centermask==0); //if (obj->control_type == CT_FLYING) if (obj->mtype.phys_info.flags & PF_LEVELLING) do_physics_align_object( obj ); //hack to keep player from going through closed doors if (obj->type==OBJ_PLAYER && obj->segnum!=orig_segnum && (!cheats.ghostphysics) ) { int sidenum; sidenum = find_connect_side(&Segments[obj->segnum],&Segments[orig_segnum]); if (sidenum != -1) { if (! (WALL_IS_DOORWAY(&Segments[orig_segnum],sidenum) & WID_FLY_FLAG)) { side *s; int vertnum,num_faces,i; fix dist; int vertex_list[6]; //bump object back s = &Segments[orig_segnum].sides[sidenum]; create_abs_vertex_lists( &num_faces, vertex_list, orig_segnum, sidenum, __FILE__,__LINE__); //let's pretend this wall is not triangulated vertnum = vertex_list[0]; for (i=1;i<4;i++) if (vertex_list[i] < vertnum) vertnum = vertex_list[i]; #ifdef COMPACT_SEGS { vms_vector _vn; get_side_normal(&Segments[orig_segnum], sidenum, 0, &_vn ); dist = vm_dist_to_plane(&start_pos, &_vn, &Vertices[vertnum]); vm_vec_scale_add(&obj->pos,&start_pos,&_vn,obj->size-dist); } #else dist = vm_dist_to_plane(&start_pos, &s->normals[0], &Vertices[vertnum]); vm_vec_scale_add(&obj->pos,&start_pos,&s->normals[0],obj->size-dist); #endif update_object_seg(obj); } } } //--WE ALWYS WANT THIS IN, MATT AND MIKE DECISION ON 12/10/94, TWO MONTHS AFTER FINAL #ifndef NDEBUG //if end point not in segment, move object to last pos, or segment center if (get_seg_masks(&obj->pos,obj->segnum,0,__FILE__,__LINE__).centermask!=0) { if (find_object_seg(obj)==-1) { int n; //Int3(); if (obj->type==OBJ_PLAYER && (n=find_point_seg(&obj->last_pos,obj->segnum))!=-1) { obj->pos = obj->last_pos; obj_relink(objnum, n ); } else { compute_segment_center(&obj->pos,&Segments[obj->segnum]); obj->pos.x += objnum; } if (obj->type == OBJ_WEAPON) obj->flags |= OF_SHOULD_BE_DEAD; } } //--WE ALWYS WANT THIS IN, MATT AND MIKE DECISION ON 12/10/94, TWO MONTHS AFTER FINAL #endif }
// ******************************************************************************************** // Engages autopilot // This does: // * Control switched from player to AI // * Time compression to 32x // * Lock time compression -WMC // * Tell AI to fly to targeted Nav Point (for all nav-status wings/ships) // * Sets max waypoint speed to the best-speed of the slowest ship tagged bool StartAutopilot() { // Check for support ship and dismiss it if it is not doing anything. // If the support ship is doing something then tell the user such. for ( object *objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { if ((objp->type == OBJ_SHIP) && !(objp->flags & OF_SHOULD_BE_DEAD)) { Assertion((objp->instance >= 0) && (objp->instance < MAX_SHIPS), "objp does not have a valid pointer to a ship. Pointer is %d, which is smaller than 0 or bigger than %d", objp->instance, MAX_SHIPS); ship *shipp = &Ships[objp->instance]; if (shipp->team != Player_ship->team) continue; Assertion((shipp->ship_info_index >= 0) && (shipp->ship_info_index < MAX_SHIP_CLASSES), "Ship '%s' does not have a valid pointer to a ship class. Pointer is %d, which is smaller than 0 or bigger than %d", shipp->ship_name, shipp->ship_info_index, MAX_SHIP_CLASSES); ship_info *sip = &Ship_info[shipp->ship_info_index]; if ( !(sip->flags & SIF_SUPPORT) ) continue; // don't deal with dying or departing support ships if ( shipp->flags & (SF_DYING | SF_DEPARTING) ) continue; Assert(shipp->ai_index != -1); ai_info* support_ship_aip = &(Ai_info[Ships[objp->instance].ai_index]); // is support ship trying to rearm-repair if ( ai_find_goal_index( support_ship_aip->goals, AI_GOAL_REARM_REPAIR ) == -1 ) { // no, so tell it to depart ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_WARP, -1, NULL, support_ship_aip ); } else { // yes send_autopilot_msgID(NP_MSG_FAIL_SUPPORT_WORKING); return false; } } } if (!CanAutopilot()) return false; AutoPilotEngaged = true; // find the ship that is "leading" all of the ships when the player starts // autopilot // by default the ship that is leading the autopilot session the player's // wing leader (if the player is the wing leader then it will be the // player). // TODO:implement a way to allow a FREDer to say a different ship is leader Autopilot_flight_leader = get_wing_leader(Player_ship->wingnum); if ( Autopilot_flight_leader == NULL ) { // force player to be the leader if he doesn't have a wing Autopilot_flight_leader = Player_obj; } if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) LockAPConv = timestamp(); // lock convergence instantly else LockAPConv = timestamp(3000); // 3 seconds before we lock convergence Player_use_ai = 1; set_time_compression(1); lock_time_compression(true); // determine speed cap int i,j, wcount=1, tc_factor=1; float speed_cap = 1000000.0; // 1m is a safe starting point float radius = Player_obj->radius, distance = 0.0f, ftemp; bool capshipPresent = false; int capship_counts[3]; // three size classes capship_counts[0] = 0; capship_counts[1] = 0; capship_counts[2] = 0; int capship_placed[3]; // three size classes capship_placed[0] = 0; capship_placed[1] = 0; capship_placed[2] = 0; float capship_spreads[3]; capship_spreads[0] = 0.0f; capship_spreads[1] = 0.0f; capship_spreads[2] = 0.0f; SCP_vector<int> capIndexes; // empty the autopilot wings map autopilot_wings.clear(); // vars for usage w/ cinematic vec3d pos, norm1, perp, tpos, rpos = Player_obj->pos, zero; memset(&zero, 0, sizeof(vec3d)); // instantly turn player toward tpos if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) { vm_vec_sub(&norm1, Navs[CurrentNav].GetPosition(), &Player_obj->pos); vm_vector_2_matrix(&Player_obj->orient, &norm1, NULL, NULL); } for (i = 0; i < MAX_SHIPS; i++) { if (Ships[i].objnum != -1 && (Ships[i].flags2 & SF2_NAVPOINT_CARRY || (Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY) ) ) { if (speed_cap > vm_vec_mag(&Ship_info[Ships[i].ship_info_index].max_vel)) speed_cap = vm_vec_mag(&Ship_info[Ships[i].ship_info_index].max_vel); } } // damp speed_cap to 90% of actual -- to make sure ships stay in formation if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) speed_cap = 0.90f * speed_cap; if ( speed_cap < 1.0f ) { /* We need to deal with this so that incorrectly flagged ships will not cause the engine to fail to limit all the ships speeds correctly. */ Warning(LOCATION, "Ship speed cap is way too small (%f)!\n" "This is normally caused by a ship that has nav-carry-status set, but cannot move itself (like a Cargo container).\n" "Speed cap has been set to 1.0 m/s.", speed_cap); speed_cap = 1.0f; } ramp_bias = speed_cap/50.0f; // assign ship goals // when assigning goals to individual ships only do so if Ships[shipnum].wingnum != -1 // we will assign wing goals below for (i = 0; i < MAX_SHIPS; i++) { if (Ships[i].objnum != -1 && (Ships[i].flags2 & SF2_NAVPOINT_CARRY || (Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY) ) ) { // do we have capital ships in the area? if (Ship_info[Ships[i].ship_info_index].flags & ( SIF_CRUISER | SIF_CAPITAL | SIF_SUPERCAP | SIF_CORVETTE | SIF_AWACS | SIF_GAS_MINER | SIF_FREIGHTER | SIF_TRANSPORT)) { capshipPresent = true; capIndexes.push_back(i); // ok.. what size class if (Ship_info[Ships[i].ship_info_index].flags & (SIF_CAPITAL | SIF_SUPERCAP)) { capship_counts[0]++; if (capship_spreads[0] < Objects[Ships[i].objnum].radius) capship_spreads[0] = Objects[Ships[i].objnum].radius; } else if (Ship_info[Ships[i].ship_info_index].flags & (SIF_CORVETTE)) { capship_counts[1]++; if (capship_spreads[1] < Objects[Ships[i].objnum].radius) capship_spreads[1] = Objects[Ships[i].objnum].radius; } else { capship_counts[2]++; if (capship_spreads[2] < Objects[Ships[i].objnum].radius) capship_spreads[2] = Objects[Ships[i].objnum].radius; } } // check for bigger radius for usage later /*if (!vm_vec_cmp(&rpos, &Player_obj->pos)) // want to make sure rpos isn't player pos - we can worry about it being largest object's later { rpos = Objects[Ships[i].objnum].pos; }*/ if (Objects[Ships[i].objnum].radius > radius) { rpos = Objects[Ships[i].objnum].pos; radius = Objects[Ships[i].objnum].radius; } if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) {// instantly turn the ship to match the direction player is looking //vm_vec_sub(&norm1, Navs[CurrentNav].GetPosition(), &Player_obj->pos); vm_vector_2_matrix(&Objects[Ships[i].objnum].orient, &norm1, NULL, NULL); } // snap wings into formation if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS && // only if using cinematics (Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY) // only if in a wing && Autopilot_flight_leader != &Objects[Ships[i].objnum]) //only if not flight leader's object { ai_info *aip = &Ai_info[Ships[i].ai_index]; int wingnum = aip->wing, wing_index = get_wing_index(&Objects[Ships[i].objnum], wingnum); vec3d goal_point; object *leader_objp = get_wing_leader(wingnum); if (leader_objp != &Objects[Ships[i].objnum]) { // not leader.. get our position relative to leader get_absolute_wing_pos_autopilot(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT); } else { ai_clear_wing_goals(wingnum); j = 1+int( (float)floor(double(wcount-1)/2.0) ); switch (wcount % 2) { case 1: // back-left vm_vec_add(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); //vm_vec_sub(&perp, &perp, &Player_obj->orient.vec.fvec); vm_vec_normalize(&perp); vm_vec_scale(&perp, -166.0f*j); // 166m is supposedly the optimal range according to tolwyn vm_vec_add(&goal_point, &Autopilot_flight_leader->pos, &perp); break; default: //back-right case 0: vm_vec_add(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); //vm_vec_sub(&perp, &perp, &Player_obj->orient.vec.fvec); vm_vec_normalize(&perp); vm_vec_scale(&perp, 166.0f*j); vm_vec_add(&goal_point, &Autopilot_flight_leader->pos, &perp); break; } autopilot_wings[wingnum] = wcount; wcount++; } Objects[Ships[i].objnum].pos = goal_point; if (vm_vec_dist_quick(&Autopilot_flight_leader->pos, &Objects[Ships[i].objnum].pos) > distance) { distance = vm_vec_dist_quick(&Autopilot_flight_leader->pos, &Objects[Ships[i].objnum].pos); } } // lock primary and secondary weapons if ( LockWeaponsDuringAutopilot ) Ships[i].flags2 |= (SF2_PRIMARIES_LOCKED | SF2_SECONDARIES_LOCKED); // clear the ship goals and cap the waypoint speed ai_clear_ship_goals(&Ai_info[Ships[i].ai_index]); Ai_info[Ships[i].ai_index].waypoint_speed_cap = (int)speed_cap; // if they're not part of a wing set their goal if (Ships[i].wingnum == -1 || The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) { if (Navs[CurrentNav].flags & NP_WAYPOINT) { ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_WAYPOINTS_ONCE, 0, ((waypoint_list*)Navs[CurrentNav].target_obj)->get_name(), &Ai_info[Ships[i].ai_index] ); //fixup has to wait until after wing goals } else { ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_FLY_TO_SHIP, 0, ((ship*)Navs[CurrentNav].target_obj)->ship_name, &Ai_info[Ships[i].ai_index] ); } } } } // assign wing goals if (!(The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS)) { for (i = 0; i < MAX_WINGS; i++) { if (Wings[i].flags & WF_NAV_CARRY ) { //ai_add_ship_goal_player( int type, int mode, int submode, char *shipname, ai_info *aip ); //ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_STAY_NEAR_SHIP, 0, target_shipname, wingnum ); //ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_WAYPOINTS_ONCE, 0, target_shipname, wingnum ); //ai_clear_ship_goals( &(Ai_info[Ships[num].ai_index]) ); ai_clear_wing_goals( i ); if (Navs[CurrentNav].flags & NP_WAYPOINT) { ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_WAYPOINTS_ONCE, 0, ((waypoint_list*)Navs[CurrentNav].target_obj)->get_name(), i ); // "fix up" the goal for (j = 0; j < MAX_AI_GOALS; j++) { if (Wings[i].ai_goals[j].ai_mode == AI_GOAL_WAYPOINTS_ONCE || Wings[i].ai_goals[j].ai_mode == AIM_WAYPOINTS ) { autopilot_ai_waypoint_goal_fixup(&(Wings[i].ai_goals[j])); } } } else { ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_FLY_TO_SHIP, 0, ((ship*)Navs[CurrentNav].target_obj)->ship_name, i ); } } } } // fixup has to go down here because ships are assigned goals during wing goals as well for (i = 0; i < MAX_SHIPS; i++) { if (Ships[i].objnum != -1) { if (Ships[i].flags2 & SF2_NAVPOINT_CARRY || (Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY)) for (j = 0; j < MAX_AI_GOALS; j++) { if (Ai_info[Ships[i].ai_index].goals[j].ai_mode == AI_GOAL_WAYPOINTS_ONCE || Ai_info[Ships[i].ai_index].goals[j].ai_mode == AIM_WAYPOINTS) { autopilot_ai_waypoint_goal_fixup( &(Ai_info[Ships[i].ai_index].goals[j]) ); // formation fixup //ai_form_on_wing(&Objects[Ships[i].objnum], &Objects[Player_ship->objnum]); } } } } start_dist = DistanceTo(CurrentNav); // ----------------------------- setup cinematic ----------------------------- if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) { if (capshipPresent) { // position capships vec3d right, front, up, offset; for (SCP_vector<int>::iterator idx = capIndexes.begin(); idx != capIndexes.end(); ++idx) { vm_vec_add(&right, &Autopilot_flight_leader->orient.vec.rvec, &zero); vm_vec_add(&front, &Autopilot_flight_leader->orient.vec.fvec, &zero); vm_vec_add(&up, &Autopilot_flight_leader->orient.vec.uvec, &zero); vm_vec_add(&offset, &zero, &zero); if (Ship_info[Ships[*idx].ship_info_index].flags & (SIF_CAPITAL | SIF_SUPERCAP)) { //0 - below - three lines of position // front/back to zero vm_vec_add(&front, &zero, &zero); // position below vm_vec_scale(&up, capship_spreads[0]); // scale the up vector by the radius of the largest ship in this formation part switch (capship_placed[0] % 3) { case 1: // right vm_vec_scale(&right, capship_spreads[0]); break; case 2: // left vm_vec_scale(&right, -capship_spreads[0]); break; default: // straight case 0: vm_vec_add(&right, &zero, &zero); vm_vec_scale(&up, 1.5); // add an extra half-radius break; } // scale by row vm_vec_scale(&right, (1+((float)floor((float)capship_placed[0]/3)))); vm_vec_scale(&up, -(1+((float)floor((float)capship_placed[0]/3)))); capship_placed[0]++; } else if (Ship_info[Ships[*idx].ship_info_index].flags & SIF_CORVETTE) { //1 above - 3 lines of position // front/back to zero vm_vec_add(&front, &zero, &zero); // position below vm_vec_scale(&up, capship_spreads[1]); // scale the up vector by the radius of the largest ship in this formation part switch (capship_placed[1] % 3) { case 1: // right vm_vec_scale(&right, capship_spreads[1]); break; case 2: // left vm_vec_scale(&right, -capship_spreads[1]); break; default: // straight case 0: vm_vec_add(&right, &zero, &zero); vm_vec_scale(&up, 1.5); // add an extra half-radius break; } // scale by row vm_vec_scale(&right, (1+((float)floor((float)capship_placed[1]/3)))); vm_vec_scale(&up, (1+((float)floor((float)capship_placed[1]/3)))); // move ourselves up and out of the way of the smaller ships vm_vec_add(&perp, &Autopilot_flight_leader->orient.vec.uvec, &zero); vm_vec_scale(&perp, capship_spreads[2]); vm_vec_add(&up, &up, &perp); capship_placed[1]++; } else { //2 either side - 6 lines of position (right (dir, front, back), left (dir, front, back) ) // placing pattern: right, left, front right, front left, rear right, rear left // up/down to zero vm_vec_add(&up, &zero, &zero); switch (capship_placed[2] % 6) { case 5: // rear left vm_vec_scale(&right, -capship_spreads[2]); vm_vec_scale(&front, -capship_spreads[2]); break; case 4: // rear right vm_vec_scale(&right, capship_spreads[2]); vm_vec_scale(&front, -capship_spreads[2]); break; case 3: // front left vm_vec_scale(&right, -capship_spreads[2]); vm_vec_scale(&front, capship_spreads[2]); break; case 2: // front right vm_vec_scale(&right, capship_spreads[2]); vm_vec_scale(&front, capship_spreads[2]); break; case 1: // straight left vm_vec_scale(&right, 1.5); vm_vec_scale(&right, -capship_spreads[2]); vm_vec_add(&front, &zero, &zero); break; default: // straight right case 0: vm_vec_scale(&right, 1.5); vm_vec_scale(&right, capship_spreads[2]); vm_vec_add(&front, &zero, &zero); break; } // these ships seem to pack a little too tightly vm_vec_scale(&right, 2*(1+((float)floor((float)capship_placed[2]/3)))); vm_vec_scale(&front, 2*(1+((float)floor((float)capship_placed[2]/3)))); // move "out" by 166*(wcount-1) so we don't bump into fighters vm_vec_add(&perp, &Autopilot_flight_leader->orient.vec.rvec, &zero); vm_vec_scale(&perp, 166.0f*float(wcount-1)); if ( (capship_placed[2] % 2) == 0) vm_vec_add(&right, &right, &perp); else vm_vec_sub(&right, &right, &perp); capship_placed[2]++; } // integrate the up/down componant vm_vec_add(&offset, &offset, &up); //integrate the left/right componant vm_vec_add(&offset, &offset, &right); //integrate the left/right componant vm_vec_add(&offset, &offset, &front); // global scale the position by 50% //vm_vec_scale(&offset, 1.5); vm_vec_add(&Objects[Ships[*idx].objnum].pos, &Autopilot_flight_leader->pos, &offset); if (vm_vec_dist_quick(&Autopilot_flight_leader->pos, &Objects[Ships[*idx].objnum].pos) > distance) { distance = vm_vec_dist_quick(&Autopilot_flight_leader->pos, &Objects[Ships[*idx].objnum].pos); } } } ftemp = floor(Autopilot_flight_leader->phys_info.max_vel.xyz.z/speed_cap); if (ftemp >= 2.0f && ftemp < 4.0f) tc_factor = 2; else if (ftemp >= 4.0f && ftemp < 8.0f) tc_factor = 4; else if (ftemp >= 8.0f) tc_factor = 8; tpos = *Navs[CurrentNav].GetPosition(); // determine distance toward nav at which camera will be vm_vec_sub(&pos, &tpos, &Autopilot_flight_leader->pos); vm_vec_normalize(&pos); // pos is now a unit vector in the direction we will be moving the camera //norm1 = pos; vm_vec_scale(&pos, 5*speed_cap*tc_factor); // pos is now scaled by 5 times the speed (5 seconds ahead) vm_vec_add(&pos, &pos, &Autopilot_flight_leader->pos); // pos is now 5*speed cap in front of player ship switch (myrand()%24) // 8 different ways of getting perp points // 4 of which will not be used when capships are present (anything below, or straight above) { case 1: // down case 9: case 16: if (capship_placed[0] == 0) vm_vec_sub(&perp, &zero, &Autopilot_flight_leader->orient.vec.uvec); else { // become up-left vm_vec_add(&perp, &zero, &Autopilot_flight_leader->orient.vec.uvec); vm_vec_sub(&perp, &perp, &Autopilot_flight_leader->orient.vec.rvec); } break; case 2: // up case 10: case 23: vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); if (capshipPresent) // become up-right vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.rvec); break; case 3: // left case 11: case 22: vm_vec_sub(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); break; case 4: // up-left case 12: case 21: vm_vec_sub(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); break; case 5: // up-right case 13: case 20: vm_vec_add(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); break; case 6: // down-left case 14: case 19: vm_vec_sub(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); if (capship_placed[0] < 2) vm_vec_sub(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); else vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); break; case 7: // down-right case 15: case 18: vm_vec_add(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); if (capship_placed[0] < 1) vm_vec_sub(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); else vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); break; default: case 0: // right case 8: case 17: perp = Autopilot_flight_leader->orient.vec.rvec; break; } vm_vec_normalize(&perp); //vm_vec_scale(&perp, 2*radius+distance); vm_vec_scale(&perp, Autopilot_flight_leader->radius+radius); // randomly scale up/down by up to 20% j = 20-myrand()%40; // [-20,20] vm_vec_scale(&perp, 1.0f+(float(j)/100.0f)); vm_vec_add(&cameraPos, &pos, &perp); if (capshipPresent) { vm_vec_normalize(&perp); // place it behind //vm_vec_copy_scale(&norm1, &Player_obj->orient.vec.fvec, -2*(Player_obj->radius+radius*(1.0f+(float(j)/100.0f)))); //vm_vec_add(&cameraTarget, &cameraTarget, &norm1); vm_vec_copy_scale(&cameraTarget,&perp, radius/5.0f); //vm_vec_scale(&cameraTarget, Player_obj->radius+radius*(1.0f+(float(j)/100.0f))); //vm_vec_add(&cameraTarget, &pos, &cameraTarget); //CameraSpeed = (radius+distance)/25; //vm_vec_add(&cameraTarget, &zero, &perp); //vm_vec_scale(&CameraVelocity, (radius+distance/100.f)); //vm_vec_scale(&CameraVelocity, 1.0f/float(NPS_TICKRATE*tc_factor)); } else { vm_vec_add(&cameraTarget, &zero, &zero); //CameraSpeed = 0; } //CameraMoving = false; EndAPCinematic = timestamp((10000*tc_factor)+NPS_TICKRATE); // 10 objective seconds before end of cinematic MoveCamera = timestamp((5500*tc_factor)+NPS_TICKRATE); camMovingTime = int(4.5*float(tc_factor)); set_time_compression((float)tc_factor); } return true; }
// apply the EMP effect to all relevant ships void emp_apply(vec3d *pos, float inner_radius, float outer_radius, float emp_intensity, float emp_time, bool use_emp_time_for_capship_turrets) { float actual_intensity, actual_time; vec3d dist; float dist_mag; float scale_factor; object *target; ship_obj *so; missile_obj *mo; ship_subsys *moveup; weapon_info *wip_target; // all machines check to see if the blast hit a bomb. if so, shut it down (can't move anymore) for( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) { target = &Objects[mo->objnum]; if(target->type != OBJ_WEAPON){ continue; } Assert(target->instance >= 0); if(target->instance < 0){ continue; } Assert(Weapons[target->instance].weapon_info_index >= 0); if(Weapons[target->instance].weapon_info_index < 0){ continue; } // if we have a bomb weapon wip_target = &Weapon_info[Weapons[target->instance].weapon_info_index]; if((wip_target->weapon_hitpoints > 0) && !(wip_target->wi_flags2 & WIF2_NO_EMP_KILL)) { // get the distance between the detonation and the target object vm_vec_sub(&dist, &target->pos, pos); dist_mag = vm_vec_mag(&dist); // if the bomb was within 1/4 of the outer radius, castrate it if(dist_mag <= (outer_radius * 0.25f)){ // memset(&target->phys_info, 0, sizeof(physics_info)); Weapons[target->instance].weapon_flags |= WF_DEAD_IN_WATER; mprintf(("EMP killing weapon\n")); } } } // if I'm only a client in a multiplayer game, do nothing if(MULTIPLAYER_CLIENT){ return; } // See if there are any friendly ships present, if so return without preventing msg for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { target = &Objects[so->objnum]; if(target->type != OBJ_SHIP){ continue; } Assert(Objects[so->objnum].instance >= 0); if(Objects[so->objnum].instance < 0){ continue; } Assert(Ships[Objects[so->objnum].instance].ship_info_index >= 0); if(Ships[Objects[so->objnum].instance].ship_info_index < 0){ continue; } // if the ship is a cruiser or cap ship, only apply the EMP effect to turrets if(Ship_info[Ships[target->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) { float capship_emp_time = use_emp_time_for_capship_turrets ? emp_time : MAX_TURRET_DISRUPT_TIME; moveup = &Ships[target->instance].subsys_list; if(moveup->next != NULL){ moveup = moveup->next; } while(moveup != &Ships[target->instance].subsys_list){ // if this is a turret, disrupt it if((moveup->system_info != NULL) && (moveup->system_info->type == SUBSYSTEM_TURRET)){ vec3d actual_pos; // get the distance to the subsys vm_vec_unrotate(&actual_pos, &moveup->system_info->pnt, &target->orient); vm_vec_add2(&actual_pos, &target->pos); vm_vec_sub(&dist, &actual_pos, pos); dist_mag = vm_vec_mag(&dist); // if for some reason, the object was outside the blast, radius if(dist_mag > outer_radius){ // next item moveup = moveup->next; continue; } // compute a scale factor for the emp effect scale_factor = 1.0f; if(dist_mag >= inner_radius){ scale_factor = 1.0f - (dist_mag / outer_radius); } scale_factor -= Ship_info[Ships[target->instance].ship_info_index].emp_resistance_mod; if (scale_factor < 0.0f) { moveup = moveup->next; continue; } // disrupt the turret ship_subsys_set_disrupted(moveup, (int)(capship_emp_time * scale_factor)); mprintf(("EMP disrupting subsys %s on ship %s (%f, %f)\n", moveup->system_info->subobj_name, Ships[Objects[so->objnum].instance].ship_name, scale_factor, capship_emp_time * scale_factor)); } // next item moveup = moveup->next; } } // otherwise coat the whole ship with the effect. mmmmmmmmm. else { // get the distance between the detonation and the target object vm_vec_sub(&dist, &target->pos, pos); dist_mag = vm_vec_mag(&dist); // if for some reason, the object was outside the blast, radius if(dist_mag > outer_radius){ continue; } // compute a scale factor for the emp effect scale_factor = 1.0f; if(dist_mag >= inner_radius){ scale_factor = 1.0f - (dist_mag / outer_radius); } scale_factor -= Ship_info[Ships[target->instance].ship_info_index].emp_resistance_mod; if (scale_factor < 0.0f) { continue; } // calculate actual EMP effect values actual_intensity = emp_intensity * scale_factor; actual_time = emp_time * scale_factor; mprintf(("EMP effect s : %f, i : %f, t : %f\n", scale_factor, actual_intensity, actual_time)); // if this effect happened to be on me, start it now if((target == Player_obj) && !(Game_mode & GM_STANDALONE_SERVER)){ emp_start_local(actual_intensity, actual_time); } // if this is a multiplayer game, notify other players of the effect if(Game_mode & GM_MULTIPLAYER){ Assert(MULTIPLAYER_MASTER); send_emp_effect(target->net_signature, actual_intensity, actual_time); } // now be sure to start the emp effect for the ship itself emp_start_ship(target, actual_intensity, actual_time); } } }
/** * Checks asteroid-ship collisions. * @param pair obj_pair pointer to the two objects. pair->a is asteroid and pair->b is ship. * @return 1 if all future collisions between these can be ignored */ int collide_asteroid_ship( obj_pair * pair ) { if (!Asteroids_enabled) return 0; float dist; object *pasteroid = pair->a; object *pship = pair->b; // Don't check collisions for warping out player if ( Player->control_mode != PCM_NORMAL ) { if ( pship == Player_obj ) return 0; } if (pasteroid->hull_strength < 0.0f) return 0; Assert( pasteroid->type == OBJ_ASTEROID ); Assert( pship->type == OBJ_SHIP ); dist = vm_vec_dist( &pasteroid->pos, &pship->pos ); if ( dist < pasteroid->radius + pship->radius ) { int hit; vec3d hitpos; // create and initialize ship_ship_hit_info struct collision_info_struct asteroid_hit_info; init_collision_info_struct(&asteroid_hit_info); if ( pasteroid->phys_info.mass > pship->phys_info.mass ) { asteroid_hit_info.heavy = pasteroid; asteroid_hit_info.light = pship; } else { asteroid_hit_info.heavy = pship; asteroid_hit_info.light = pasteroid; } hit = asteroid_check_collision(pasteroid, pship, &hitpos, &asteroid_hit_info ); if ( hit ) { //Scripting support (WMC) Script_system.SetHookObjects(4, "Ship", pship, "Asteroid", pasteroid, "Self",pship, "Object", pasteroid); bool ship_override = Script_system.IsConditionOverride(CHA_COLLIDEASTEROID, pship); Script_system.SetHookObjects(2, "Self",pasteroid, "Object", pship); bool asteroid_override = Script_system.IsConditionOverride(CHA_COLLIDESHIP, pasteroid); if(!ship_override && !asteroid_override) { float ship_damage; float asteroid_damage; vec3d asteroid_vel = pasteroid->phys_info.vel; // do collision physics calculate_ship_ship_collision_physics( &asteroid_hit_info ); if ( asteroid_hit_info.impulse < 0.5f ) return 0; // limit damage from impulse by making max impulse (for damage) 2*m*v_max_relative float max_ship_impulse = (2.0f*pship->phys_info.max_vel.xyz.z+vm_vec_mag_quick(&asteroid_vel)) * (pship->phys_info.mass*pasteroid->phys_info.mass) / (pship->phys_info.mass + pasteroid->phys_info.mass); if (asteroid_hit_info.impulse > max_ship_impulse) { ship_damage = 0.001f * max_ship_impulse; } else { ship_damage = 0.001f * asteroid_hit_info.impulse; // Cut collision-based damage in half. } // Decrease heavy damage by 2x. if (ship_damage > 5.0f) ship_damage = 5.0f + (ship_damage - 5.0f)/2.0f; if ((ship_damage > 500.0f) && (ship_damage > Ships[pship->instance].ship_max_hull_strength/8.0f)) { ship_damage = Ships[pship->instance].ship_max_hull_strength/8.0f; nprintf(("AI", "Pinning damage to %s from asteroid at %7.3f (%7.3f percent)\n", Ships[pship->instance].ship_name, ship_damage, 100.0f * ship_damage/Ships[pship->instance].ship_max_hull_strength)); } // Decrease damage during warp out because it's annoying when your escoree dies during warp out. if (Ai_info[Ships[pship->instance].ai_index].mode == AIM_WARP_OUT) ship_damage /= 3.0f; // calculate asteroid damage and set asteroid damage to greater or asteroid and ship // asteroid damage is needed since we can really whack some small asteroid with afterburner and not do // significant damage to ship but the asteroid goes off faster than afterburner speed. asteroid_damage = asteroid_hit_info.impulse/pasteroid->phys_info.mass; // ie, delta velocity of asteroid asteroid_damage = (asteroid_damage > ship_damage) ? asteroid_damage : ship_damage; // apply damage to asteroid asteroid_hit( pasteroid, pship, &hitpos, asteroid_damage); // speed => damage //extern fix Missiontime; int quadrant_num; if ( asteroid_hit_info.heavy == pship ) { quadrant_num = get_ship_quadrant_from_global(&hitpos, pship); if ((pship->flags[Object::Object_Flags::No_shields]) || !ship_is_shield_up(pship, quadrant_num) ) { quadrant_num = -1; } ship_apply_local_damage(asteroid_hit_info.heavy, asteroid_hit_info.light, &hitpos, ship_damage, quadrant_num, CREATE_SPARKS, asteroid_hit_info.submodel_num); } else { // don't draw sparks (using sphere hitpos) ship_apply_local_damage(asteroid_hit_info.light, asteroid_hit_info.heavy, &hitpos, ship_damage, MISS_SHIELDS, NO_SPARKS); } // maybe print Collision on HUD if ( pship == Player_obj ) { hud_start_text_flash(XSTR("Collision", 1431), 2000); } collide_ship_ship_do_sound(&hitpos, pship, pasteroid, pship==Player_obj); } Script_system.SetHookObjects(2, "Self",pship, "Object", pasteroid); if(!(asteroid_override && !ship_override)) Script_system.RunCondition(CHA_COLLIDEASTEROID, '\0', NULL, pship); Script_system.SetHookObjects(2, "Self",pasteroid, "Object", pship); if((asteroid_override && !ship_override) || (!asteroid_override && !ship_override)) Script_system.RunCondition(CHA_COLLIDESHIP, '\0', NULL, pasteroid); Script_system.RemHookVars(4, "Ship", "Asteroid", "Self", "Object"); return 0; } return 0; } else { // estimate earliest time at which pair can hit float asteroid_max_speed, ship_max_speed, time; ship *shipp = &Ships[pship->instance]; asteroid_max_speed = vm_vec_mag(&pasteroid->phys_info.vel); // Asteroid... vel gets reset, not max vel.z asteroid_max_speed = MAX(asteroid_max_speed, 10.0f); if (ship_is_beginning_warpout_speedup(pship)) { ship_max_speed = MAX(ship_get_max_speed(shipp), ship_get_warpout_speed(pship)); } else { ship_max_speed = ship_get_max_speed(shipp); } ship_max_speed = MAX(ship_max_speed, 10.0f); ship_max_speed = MAX(ship_max_speed, pship->phys_info.vel.xyz.z); time = 1000.0f * (dist - pship->radius - pasteroid->radius - 10.0f) / (asteroid_max_speed + ship_max_speed); // 10.0f is a safety factor time -= 200.0f; // allow one frame slow frame at ~5 fps if (time > 100) { pair->next_check_time = timestamp( fl2i(time) ); } else { pair->next_check_time = timestamp(0); // check next time } return 0; } }
void asteroid_editor::update_init() { int num_asteroids, idx, cur_choice; UpdateData(TRUE); if (last_field >= 0) { // store into temp asteroid field num_asteroids = a_field[last_field].num_initial_asteroids; a_field[last_field].num_initial_asteroids = m_enable_asteroids ? m_density : 0; if (a_field[last_field].num_initial_asteroids < 0) a_field[last_field].num_initial_asteroids = 0; if (a_field[last_field].num_initial_asteroids > MAX_ASTEROIDS) a_field[last_field].num_initial_asteroids = MAX_ASTEROIDS; if (num_asteroids != a_field[last_field].num_initial_asteroids) set_modified(); vec3d vel_vec = {1.0f, 0.0f, 0.0f}; vm_vec_scale(&vel_vec, (float) m_avg_speed); MODIFY(a_field[last_field].vel.xyz.x, vel_vec.xyz.x); MODIFY(a_field[last_field].vel.xyz.y, vel_vec.xyz.y); MODIFY(a_field[last_field].vel.xyz.z, vel_vec.xyz.z); MODIFY(a_field[last_field].min_bound.xyz.x, (float) atof(m_min_x)); MODIFY(a_field[last_field].min_bound.xyz.y, (float) atof(m_min_y)); MODIFY(a_field[last_field].min_bound.xyz.z, (float) atof(m_min_z)); MODIFY(a_field[last_field].max_bound.xyz.x, (float) atof(m_max_x)); MODIFY(a_field[last_field].max_bound.xyz.y, (float) atof(m_max_y)); MODIFY(a_field[last_field].max_bound.xyz.z, (float) atof(m_max_z)); // type of field MODIFY(a_field[last_field].field_type, m_field_type); MODIFY(a_field[last_field].debris_genre, m_debris_genre); if ( (m_field_type == FT_PASSIVE) && (m_debris_genre == DG_SHIP) ) { // we should have ship debris for (idx=0; idx<MAX_ACTIVE_DEBRIS_TYPES; idx++) { // loop over combo boxes, store the item data of the cur selection, -1 in no cur selection int cur_sel = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->GetCurSel(); if (cur_sel != CB_ERR) { cur_choice = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->GetItemData(cur_sel); } else { cur_choice = -1; } MODIFY(a_field[cur_field].field_debris_type[idx], cur_choice); } } if ( m_debris_genre == DG_ASTEROID ) { if ( ((CButton *)GetDlgItem(IDC_SUBTYPE1))->GetCheck() == 1) { cur_choice = 1; } else { cur_choice = -1; } MODIFY(a_field[cur_field].field_debris_type[0], cur_choice); if ( ((CButton *)GetDlgItem(IDC_SUBTYPE2))->GetCheck() == 1) { cur_choice = 1; } else { cur_choice = -1; } MODIFY(a_field[cur_field].field_debris_type[1], cur_choice); if ( ((CButton *)GetDlgItem(IDC_SUBTYPE3))->GetCheck() == 1) { cur_choice = 1; } else { cur_choice = -1; } MODIFY(a_field[cur_field].field_debris_type[2], cur_choice); } MODIFY(a_field[last_field].has_inner_bound, m_enable_inner_bounds); MODIFY(a_field[last_field].inner_min_bound.xyz.x, (float) atof(m_box_min_x)); MODIFY(a_field[last_field].inner_min_bound.xyz.y, (float) atof(m_box_min_y)); MODIFY(a_field[last_field].inner_min_bound.xyz.z, (float) atof(m_box_min_z)); MODIFY(a_field[last_field].inner_max_bound.xyz.x, (float) atof(m_box_max_x)); MODIFY(a_field[last_field].inner_max_bound.xyz.y, (float) atof(m_box_max_y)); MODIFY(a_field[last_field].inner_max_bound.xyz.z, (float) atof(m_box_max_z)); } Assert(cur_field >= 0); // get from temp asteroid field into class m_enable_asteroids = a_field[cur_field].num_initial_asteroids ? TRUE : FALSE; m_enable_inner_bounds = a_field[cur_field].has_inner_bound ? TRUE : FALSE; m_density = a_field[cur_field].num_initial_asteroids; if (!m_enable_asteroids) m_density = 10; // set field type m_field_type = a_field[cur_field].field_type; m_debris_genre = a_field[cur_field].debris_genre; // m_debris_species = a_field[cur_field].debris_species; m_avg_speed = (int) vm_vec_mag(&a_field[cur_field].vel); m_min_x.Format("%.1f", a_field[cur_field].min_bound.xyz.x); m_min_y.Format("%.1f", a_field[cur_field].min_bound.xyz.y); m_min_z.Format("%.1f", a_field[cur_field].min_bound.xyz.z); m_max_x.Format("%.1f", a_field[cur_field].max_bound.xyz.x); m_max_y.Format("%.1f", a_field[cur_field].max_bound.xyz.y); m_max_z.Format("%.1f", a_field[cur_field].max_bound.xyz.z); m_box_min_x.Format("%.1f", a_field[cur_field].inner_min_bound.xyz.x); m_box_min_y.Format("%.1f", a_field[cur_field].inner_min_bound.xyz.y); m_box_min_z.Format("%.1f", a_field[cur_field].inner_min_bound.xyz.z); m_box_max_x.Format("%.1f", a_field[cur_field].inner_max_bound.xyz.x); m_box_max_y.Format("%.1f", a_field[cur_field].inner_max_bound.xyz.y); m_box_max_z.Format("%.1f", a_field[cur_field].inner_max_bound.xyz.z); // set up combo boxes uint i; int j, k, index, box_index; // add "None" to each box for (k = 0; k < MAX_ACTIVE_DEBRIS_TYPES; k++) { box_index = ((CComboBox*)GetDlgItem(Dlg_id[k]))->AddString("None"); ((CComboBox*)GetDlgItem(Dlg_id[k]))->SetItemData(box_index, -1); } // now add each kind of debris to each box CString name; char *debris_size[NUM_DEBRIS_SIZES] = { "Small", "Medium", "Large" }; // each species for (i = 0; i < Species_info.size(); i++) { // each size for (j = 0; j < NUM_DEBRIS_SIZES; j++) { name = CString(Species_info[i].species_name) + " " + debris_size[j]; index = NUM_DEBRIS_SIZES + ((i * NUM_DEBRIS_SIZES) + j); // each box for (k = 0; k < MAX_ACTIVE_DEBRIS_TYPES; k++) { box_index = ((CComboBox*)GetDlgItem(Dlg_id[k]))->AddString(name); ((CComboBox*)GetDlgItem(Dlg_id[k]))->SetItemData(box_index, index); } } } // set active debris type for each combo box int box_count, cur_box_data; for (idx=0;idx<MAX_ACTIVE_DEBRIS_TYPES; idx++) { // Only set selection if not "None" if (a_field[cur_field].field_debris_type[idx] != -1) { box_count = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->GetCount(); for (box_index=0; box_index<box_count; box_index++) { cur_box_data = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->GetItemData(box_index); if (cur_box_data == a_field[cur_field].field_debris_type[idx]) { // set cur sel ((CComboBox*)GetDlgItem(Dlg_id[idx]))->SetCurSel(box_index); break; } } } } // set up asteroid subtype checkboxes ((CButton*)GetDlgItem(IDC_SUBTYPE1))->SetCheck(a_field[cur_field].field_debris_type[0] == 1); ((CButton*)GetDlgItem(IDC_SUBTYPE2))->SetCheck(a_field[cur_field].field_debris_type[1] == 1); ((CButton*)GetDlgItem(IDC_SUBTYPE3))->SetCheck(a_field[cur_field].field_debris_type[2] == 1); UpdateData(FALSE); OnEnableAsteroids(); last_field = cur_field; }
// ----------------------------------------------------------------------------------------------------------- //Simulate a physics object for this frame do_physics_sim(object *obj) { int ignore_obj_list[MAX_IGNORE_OBJS],n_ignore_objs; int iseg; int try_again; int fate; vms_vector frame_vec; //movement in this frame vms_vector new_pos,ipos; //position after this frame int count=0; int objnum; int WallHitSeg, WallHitSide; fvi_info hit_info; fvi_query fq; vms_vector save_pos; int save_seg; fix drag; fix sim_time; vms_vector start_pos; int obj_stopped=0; fix moved_time; //how long objected moved before hit something vms_vector save_p0,save_p1; physics_info *pi; int orig_segnum = obj->segnum; Assert(obj->type != OBJ_NONE); Assert(obj->movement_type == MT_PHYSICS); #ifndef NDEBUG if (Dont_move_ai_objects) if (obj->control_type == CT_AI) return; #endif pi = &obj->mtype.phys_info; do_physics_sim_rot(obj); if (!(pi->velocity.x || pi->velocity.y || pi->velocity.z || pi->thrust.x || pi->thrust.y || pi->thrust.z)) return; objnum = obj-Objects; n_phys_segs = 0; disable_new_fvi_stuff = (obj->type != OBJ_PLAYER); sim_time = FrameTime; //debug_obj = obj; #ifdef EXTRA_DEBUG if (obj == debug_obj) { printf("object %d:\n start pos = %x %x %x\n",objnum,XYZ(&obj->pos)); printf(" thrust = %x %x %x\n",XYZ(&obj->mtype.phys_info.thrust)); printf(" sim_time = %x\n",sim_time); } //check for correct object segment if(!get_seg_masks(&obj->pos,obj->segnum,0).centermask==0) { #ifndef NDEBUG mprintf((0,"Warning: object %d not in given seg!\n",objnum)); #endif //Int3(); Removed by Rob 10/5/94 if (!update_object_seg(obj)) { #ifndef NDEBUG mprintf((0,"Warning: can't find seg for object %d - moving\n",objnum)); #endif if (!(Game_mode & GM_MULTI)) Int3(); compute_segment_center(&obj->pos,&Segments[obj->segnum]); obj->pos.x += objnum; } } #endif start_pos = obj->pos; n_ignore_objs = 0; Assert(obj->mtype.phys_info.brakes==0); //brakes not used anymore? //if uses thrust, cannot have zero drag Assert(!(obj->mtype.phys_info.flags&PF_USES_THRUST) || obj->mtype.phys_info.drag!=0); //mprintf((0,"thrust=%x speed=%x\n",vm_vec_mag(&obj->mtype.phys_info.thrust),vm_vec_mag(&obj->mtype.phys_info.velocity))); //do thrust & drag if ((drag = obj->mtype.phys_info.drag) != 0) { int count; vms_vector accel; fix r,k; count = sim_time / FT; r = sim_time % FT; k = fixdiv(r,FT); if (obj->mtype.phys_info.flags & PF_USES_THRUST) { vm_vec_copy_scale(&accel,&obj->mtype.phys_info.thrust,fixdiv(f1_0,obj->mtype.phys_info.mass)); while (count--) { vm_vec_add2(&obj->mtype.phys_info.velocity,&accel); vm_vec_scale(&obj->mtype.phys_info.velocity,f1_0-drag); } //do linear scale on remaining bit of time vm_vec_scale_add2(&obj->mtype.phys_info.velocity,&accel,k); vm_vec_scale(&obj->mtype.phys_info.velocity,f1_0-fixmul(k,drag)); } else { fix total_drag=f1_0; while (count--) total_drag = fixmul(total_drag,f1_0-drag); //do linear scale on remaining bit of time total_drag = fixmul(total_drag,f1_0-fixmul(k,drag)); vm_vec_scale(&obj->mtype.phys_info.velocity,total_drag); } } #ifdef EXTRA_DEBUG if (obj == debug_obj) printf(" velocity = %x %x %x\n",XYZ(&obj->mtype.phys_info.velocity)); #endif do { try_again = 0; //Move the object vm_vec_copy_scale(&frame_vec, &obj->mtype.phys_info.velocity, sim_time); #ifdef EXTRA_DEBUG if (obj == debug_obj) printf(" pass %d, frame_vec = %x %x %x\n",count,XYZ(&frame_vec)); #endif if ( (frame_vec.x==0) && (frame_vec.y==0) && (frame_vec.z==0) ) break; count++; // If retry count is getting large, then we are trying to do something stupid. if ( count > 3) { if (obj->type == OBJ_PLAYER) { if (count > 8) break; } else break; } vm_vec_add(&new_pos,&obj->pos,&frame_vec); #ifdef EXTRA_DEBUG if (obj == debug_obj) printf(" desired_pos = %x %x %x\n",XYZ(&new_pos)); #endif ignore_obj_list[n_ignore_objs] = -1; #ifdef EXTRA_DEBUG if (obj == debug_obj) { printf(" FVI parms: p0 = %8x %8x %8x, segnum=%x, size=%x\n",XYZ(&obj->pos),obj->segnum,obj->size); printf(" p1 = %8x %8x %8x\n",XYZ(&new_pos)); } #endif fq.p0 = &obj->pos; fq.startseg = obj->segnum; fq.p1 = &new_pos; fq.rad = obj->size; fq.thisobjnum = objnum; fq.ignore_obj_list = ignore_obj_list; fq.flags = FQ_CHECK_OBJS; if (obj->type == OBJ_WEAPON) fq.flags |= FQ_TRANSPOINT; if (obj->type == OBJ_PLAYER) fq.flags |= FQ_GET_SEGLIST; //@@ if (get_seg_masks(&obj->pos,obj->segnum,0).centermask!=0) //@@ Int3(); save_p0 = *fq.p0; save_p1 = *fq.p1; fate = find_vector_intersection(&fq,&hit_info); // Matt: Mike's hack. if (fate == HIT_OBJECT) { object *objp = &Objects[hit_info.hit_object]; if ((objp->type == OBJ_WEAPON) && (objp->id == PROXIMITY_ID)) count--; } #ifndef NDEBUG if (fate == HIT_BAD_P0) { mprintf((0,"Warning: Bad p0 in physics! Object = %i, type = %i [%s]\n", obj-Objects, obj->type, Object_type_names[obj->type])); Int3(); } #endif if (obj->type == OBJ_PLAYER) { int i; if (n_phys_segs && phys_seglist[n_phys_segs-1]==hit_info.seglist[0]) n_phys_segs--; for (i=0;(i<hit_info.n_segs) && (n_phys_segs<MAX_FVI_SEGS-1); ) phys_seglist[n_phys_segs++] = hit_info.seglist[i++]; } #ifdef EXTRA_DEBUG if (obj == debug_obj) printf(" fate = %d, hit_pnt = %8x %8x %8x\n",fate,XYZ(&hit_info.hit_pnt));; #endif ipos = hit_info.hit_pnt; iseg = hit_info.hit_seg; WallHitSide = hit_info.hit_side; WallHitSeg = hit_info.hit_side_seg; if (iseg==-1) { //some sort of horrible error #ifndef NDEBUG mprintf((1,"iseg==-1 in physics! Object = %i, type = %i (%s)\n", obj-Objects, obj->type, Object_type_names[obj->type])); #endif //Int3(); //compute_segment_center(&ipos,&Segments[obj->segnum]); //ipos.x += objnum; //iseg = obj->segnum; //fate = HIT_NONE; if (obj->type == OBJ_WEAPON) obj->flags |= OF_SHOULD_BE_DEAD; break; } Assert(!((fate==HIT_WALL) && ((WallHitSeg == -1) || (WallHitSeg > Highest_segment_index)))); //if(!get_seg_masks(&hit_info.hit_pnt,hit_info.hit_seg,0).centermask==0) // Int3(); save_pos = obj->pos; //save the object's position save_seg = obj->segnum; // update object's position and segment number obj->pos = ipos; #ifdef EXTRA_DEBUG if (obj == debug_obj) printf(" new pos = %x %x %x\n",XYZ(&obj->pos)); #endif if ( iseg != obj->segnum ) obj_relink(objnum, iseg ); //if start point not in segment, move object to center of segment if (get_seg_masks(&obj->pos,obj->segnum,0).centermask!=0) { int n; if ((n=find_object_seg(obj))==-1) { //Int3(); if (obj->type==OBJ_PLAYER && (n=find_point_seg(&obj->last_pos,obj->segnum))!=-1) { obj->pos = obj->last_pos; obj_relink(objnum, n ); } else { compute_segment_center(&obj->pos,&Segments[obj->segnum]); obj->pos.x += objnum; } if (obj->type == OBJ_WEAPON) obj->flags |= OF_SHOULD_BE_DEAD; } return; } //calulate new sim time { //vms_vector moved_vec; vms_vector moved_vec_n; fix attempted_dist,actual_dist; actual_dist = vm_vec_normalized_dir(&moved_vec_n,&obj->pos,&save_pos); if (fate==HIT_WALL && vm_vec_dot(&moved_vec_n,&frame_vec) < 0) { //moved backwards //don't change position or sim_time //******* mprintf((0,"Obj %d moved backwards\n",obj-Objects)); #ifdef EXTRA_DEBUG if (obj == debug_obj) printf(" Warning: moved backwards!\n"); #endif obj->pos = save_pos; //iseg = obj->segnum; //don't change segment obj_relink(objnum, save_seg ); moved_time = 0; } else { fix old_sim_time; //if (obj == debug_obj) // printf(" moved_vec = %x %x %x\n",XYZ(&moved_vec)); attempted_dist = vm_vec_mag(&frame_vec); old_sim_time = sim_time; sim_time = fixmuldiv(sim_time,attempted_dist-actual_dist,attempted_dist); moved_time = old_sim_time - sim_time; if (sim_time < 0 || sim_time>old_sim_time) { #ifndef NDEBUG mprintf((0,"Bogus sim_time = %x, old = %x\n",sim_time,old_sim_time)); if (obj == debug_obj) printf(" Bogus sim_time = %x, old = %x, attempted_dist = %x, actual_dist = %x\n",sim_time,old_sim_time,attempted_dist,actual_dist); //Int3(); Removed by Rob #endif sim_time = old_sim_time; //WHY DOES THIS HAPPEN?? moved_time = 0; } } #ifdef EXTRA_DEBUG if (obj == debug_obj) printf(" new sim_time = %x\n",sim_time); #endif } switch( fate ) { case HIT_WALL: { vms_vector moved_v; //@@fix total_d,moved_d; fix hit_speed,wall_part; // Find hit speed vm_vec_sub(&moved_v,&obj->pos,&save_pos); wall_part = vm_vec_dot(&moved_v,&hit_info.hit_wallnorm); if (wall_part != 0 && moved_time>0 && (hit_speed=-fixdiv(wall_part,moved_time))>0) collide_object_with_wall( obj, hit_speed, WallHitSeg, WallHitSide, &hit_info.hit_pnt ); else scrape_object_on_wall(obj, WallHitSeg, WallHitSide, &hit_info.hit_pnt ); Assert( WallHitSeg > -1 ); Assert( WallHitSide > -1 ); if ( !(obj->flags&OF_SHOULD_BE_DEAD) ) { Assert(! (obj->mtype.phys_info.flags & PF_STICK && obj->mtype.phys_info.flags & PF_BOUNCE)); //can't be bounce and stick if (obj->mtype.phys_info.flags & PF_STICK) { //stop moving // mprintf((0, "Object %i stuck at %i:%i\n", obj-Objects, WallHitSeg, WallHitSide)); add_stuck_object(obj, WallHitSeg, WallHitSide); vm_vec_zero(&obj->mtype.phys_info.velocity); obj_stopped = 1; try_again = 0; } else { // Slide object along wall //We're constrained by wall, so subtract wall part from //velocity vector wall_part = vm_vec_dot(&hit_info.hit_wallnorm,&obj->mtype.phys_info.velocity); if (obj->mtype.phys_info.flags & PF_BOUNCE) //bounce off wall wall_part *= 2; //Subtract out wall part twice to achieve bounce vm_vec_scale_add2(&obj->mtype.phys_info.velocity,&hit_info.hit_wallnorm,-wall_part); #ifdef EXTRA_DEBUG if (obj == debug_obj) { printf(" sliding - wall_norm %x %x %x\n",wall_part,XYZ(&hit_info.hit_wallnorm)); printf(" wall_part %x, new velocity = %x %x %x\n",wall_part,XYZ(&obj->mtype.phys_info.velocity)); } #endif try_again = 1; } } break; } case HIT_OBJECT: { vms_vector old_vel; // Mark the hit object so that on a retry the fvi code // ignores this object. Assert(hit_info.hit_object != -1); // Calculcate the hit point between the two objects. { vms_vector *ppos0, *ppos1, pos_hit; fix size0, size1; ppos0 = &Objects[hit_info.hit_object].pos; ppos1 = &obj->pos; size0 = Objects[hit_info.hit_object].size; size1 = obj->size; Assert(size0+size1 != 0); // Error, both sizes are 0, so how did they collide, anyway?!? //vm_vec_scale(vm_vec_sub(&pos_hit, ppos1, ppos0), fixdiv(size0, size0 + size1)); //vm_vec_add2(&pos_hit, ppos0); vm_vec_sub(&pos_hit, ppos1, ppos0); vm_vec_scale_add(&pos_hit,ppos0,&pos_hit,fixdiv(size0, size0 + size1)); old_vel = obj->mtype.phys_info.velocity; collide_two_objects( obj, &Objects[hit_info.hit_object], &pos_hit); } // Let object continue its movement if ( !(obj->flags&OF_SHOULD_BE_DEAD) ) { //obj->pos = save_pos; if (obj->mtype.phys_info.flags&PF_PERSISTENT || (old_vel.x == obj->mtype.phys_info.velocity.x && old_vel.y == obj->mtype.phys_info.velocity.y && old_vel.z == obj->mtype.phys_info.velocity.z)) { //if (Objects[hit_info.hit_object].type == OBJ_POWERUP) ignore_obj_list[n_ignore_objs++] = hit_info.hit_object; try_again = 1; } } break; } case HIT_NONE: break; #ifndef NDEBUG case HIT_BAD_P0: Int3(); // Unexpected collision type: start point not in specified segment. mprintf((0,"Warning: Bad p0 in physics!!!\n")); break; default: // Unknown collision type returned from find_vector_intersection!! Int3(); break; #endif } } while ( try_again ); // Pass retry count info to AI. if (obj->control_type == CT_AI) { if (count > 0) { Ai_local_info[objnum].retry_count = count-1; Total_retries += count-1; Total_sims++; } } if (! obj_stopped) { //Set velocity from actual movement vms_vector moved_vec; vm_vec_sub(&moved_vec,&obj->pos,&start_pos); vm_vec_copy_scale(&obj->mtype.phys_info.velocity,&moved_vec,fixdiv(f1_0,FrameTime)); #ifdef BUMP_HACK if (obj==ConsoleObject && (obj->mtype.phys_info.velocity.x==0 && obj->mtype.phys_info.velocity.y==0 && obj->mtype.phys_info.velocity.z==0) && !(obj->mtype.phys_info.thrust.x==0 && obj->mtype.phys_info.thrust.y==0 && obj->mtype.phys_info.thrust.z==0)) { vms_vector center,bump_vec; //bump player a little towards center of segment to unstick compute_segment_center(¢er,&Segments[obj->segnum]); vm_vec_normalized_dir_quick(&bump_vec,¢er,&obj->pos); vm_vec_scale_add2(&obj->pos,&bump_vec,obj->size/5); } #endif } //Assert(check_point_in_seg(&obj->pos,obj->segnum,0).centermask==0); //if (obj->control_type == CT_FLYING) if (obj->mtype.phys_info.flags & PF_LEVELLING) do_physics_align_object( obj ); //hack to keep player from going through closed doors if (obj->type==OBJ_PLAYER && obj->segnum!=orig_segnum && (Physics_cheat_flag!=0xBADA55) ) { int sidenum; sidenum = find_connect_side(&Segments[obj->segnum],&Segments[orig_segnum]); if (sidenum != -1) { if (! (WALL_IS_DOORWAY(&Segments[orig_segnum],sidenum) & WID_FLY_FLAG)) { side *s; int vertnum,num_faces,i; fix dist; int vertex_list[6]; //bump object back s = &Segments[orig_segnum].sides[sidenum]; create_abs_vertex_lists( &num_faces, vertex_list, orig_segnum, sidenum); //let's pretend this wall is not triangulated vertnum = vertex_list[0]; for (i=1;i<4;i++) if (vertex_list[i] < vertnum) vertnum = vertex_list[i]; #ifdef COMPACT_SEGS { vms_vector _vn; get_side_normal(&Segments[orig_segnum], sidenum, 0, &_vn ); dist = vm_dist_to_plane(&start_pos, &_vn, &Vertices[vertnum]); vm_vec_scale_add(&obj->pos,&start_pos,&_vn,obj->size-dist); } #else dist = vm_dist_to_plane(&start_pos, &s->normals[0], &Vertices[vertnum]); vm_vec_scale_add(&obj->pos,&start_pos,&s->normals[0],obj->size-dist); #endif update_object_seg(obj); } } } //--WE ALWYS WANT THIS IN, MATT AND MIKE DECISION ON 12/10/94, TWO MONTHS AFTER FINAL #ifndef NDEBUG //if end point not in segment, move object to last pos, or segment center if (get_seg_masks(&obj->pos,obj->segnum,0).centermask!=0) { if (find_object_seg(obj)==-1) { int n; //Int3(); if (obj->type==OBJ_PLAYER && (n=find_point_seg(&obj->last_pos,obj->segnum))!=-1) { obj->pos = obj->last_pos; obj_relink(objnum, n ); } else { compute_segment_center(&obj->pos,&Segments[obj->segnum]); obj->pos.x += objnum; } if (obj->type == OBJ_WEAPON) obj->flags |= OF_SHOULD_BE_DEAD; } } //--WE ALWYS WANT THIS IN, MATT AND MIKE DECISION ON 12/10/94, TWO MONTHS AFTER FINAL #endif }
void asteroid_editor::update_init() { int num_asteroids, idx, cur_choice; UpdateData(TRUE); if (last_field >= 0) { // store into temp asteroid field num_asteroids = a_field[last_field].num_initial_asteroids; a_field[last_field].num_initial_asteroids = m_enable_asteroids ? m_density : 0; if (a_field[last_field].num_initial_asteroids < 0) a_field[last_field].num_initial_asteroids = 0; if (a_field[last_field].num_initial_asteroids > MAX_ASTEROIDS) a_field[last_field].num_initial_asteroids = MAX_ASTEROIDS; if (num_asteroids != a_field[last_field].num_initial_asteroids) set_modified(); vector vel_vec = {1.0f, 0.0f, 0.0f}; vm_vec_scale(&vel_vec, (float) m_avg_speed); MODIFY(a_field[last_field].vel.x, vel_vec.x); MODIFY(a_field[last_field].vel.y, vel_vec.y); MODIFY(a_field[last_field].vel.z, vel_vec.z); MODIFY(a_field[last_field].min_bound.x, (float) atof(m_min_x)); MODIFY(a_field[last_field].min_bound.y, (float) atof(m_min_y)); MODIFY(a_field[last_field].min_bound.z, (float) atof(m_min_z)); MODIFY(a_field[last_field].max_bound.x, (float) atof(m_max_x)); MODIFY(a_field[last_field].max_bound.y, (float) atof(m_max_y)); MODIFY(a_field[last_field].max_bound.z, (float) atof(m_max_z)); // type of field MODIFY(a_field[last_field].field_type, m_field_type); MODIFY(a_field[last_field].debris_genre, m_debris_genre); if ( (m_field_type == FT_PASSIVE) && (m_debris_genre == DG_SHIP) ) { // we should have ship debris for (idx=0; idx<3; idx++) { // loop over combo boxes, store the item data of the cur selection, -1 in no cur selection int cur_sel = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->GetCurSel(); if (cur_sel != CB_ERR) { cur_choice = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->GetItemData(cur_sel); } else { cur_choice = -1; } MODIFY(a_field[cur_field].field_debris_type[idx], cur_choice); } } if ( m_debris_genre == DG_ASTEROID ) { if ( ((CButton *)GetDlgItem(IDC_SUBTYPE1))->GetCheck() == 1) { cur_choice = 1; } else { cur_choice = -1; } MODIFY(a_field[cur_field].field_debris_type[0], cur_choice); if ( ((CButton *)GetDlgItem(IDC_SUBTYPE2))->GetCheck() == 1) { cur_choice = 1; } else { cur_choice = -1; } MODIFY(a_field[cur_field].field_debris_type[1], cur_choice); if ( ((CButton *)GetDlgItem(IDC_SUBTYPE3))->GetCheck() == 1) { cur_choice = 1; } else { cur_choice = -1; } MODIFY(a_field[cur_field].field_debris_type[2], cur_choice); } MODIFY(a_field[last_field].has_inner_bound, m_enable_inner_bounds); MODIFY(a_field[last_field].inner_min_bound.x, (float) atof(m_box_min_x)); MODIFY(a_field[last_field].inner_min_bound.y, (float) atof(m_box_min_y)); MODIFY(a_field[last_field].inner_min_bound.z, (float) atof(m_box_min_z)); MODIFY(a_field[last_field].inner_max_bound.x, (float) atof(m_box_max_x)); MODIFY(a_field[last_field].inner_max_bound.y, (float) atof(m_box_max_y)); MODIFY(a_field[last_field].inner_max_bound.z, (float) atof(m_box_max_z)); } Assert(cur_field >= 0); // get from temp asteroid field into class m_enable_asteroids = a_field[cur_field].num_initial_asteroids ? TRUE : FALSE; m_enable_inner_bounds = a_field[cur_field].has_inner_bound ? TRUE : FALSE; m_density = a_field[cur_field].num_initial_asteroids; if (!m_enable_asteroids) m_density = 10; // set field type m_field_type = a_field[cur_field].field_type; m_debris_genre = a_field[cur_field].debris_genre; // m_debris_species = a_field[cur_field].debris_species; m_avg_speed = (int) vm_vec_mag(&a_field[cur_field].vel); m_min_x.Format("%.1f", a_field[cur_field].min_bound.x); m_min_y.Format("%.1f", a_field[cur_field].min_bound.y); m_min_z.Format("%.1f", a_field[cur_field].min_bound.z); m_max_x.Format("%.1f", a_field[cur_field].max_bound.x); m_max_y.Format("%.1f", a_field[cur_field].max_bound.y); m_max_z.Format("%.1f", a_field[cur_field].max_bound.z); m_box_min_x.Format("%.1f", a_field[cur_field].inner_min_bound.x); m_box_min_y.Format("%.1f", a_field[cur_field].inner_min_bound.y); m_box_min_z.Format("%.1f", a_field[cur_field].inner_min_bound.z); m_box_max_x.Format("%.1f", a_field[cur_field].inner_max_bound.x); m_box_max_y.Format("%.1f", a_field[cur_field].inner_max_bound.y); m_box_max_z.Format("%.1f", a_field[cur_field].inner_max_bound.z); // set up combo boxes int box_index; int num_field_debris_info = MAX_DEBRIS_TYPES + 1; for (idx=0; idx<num_field_debris_info; idx++) { box_index = ((CComboBox*)GetDlgItem(IDC_SHIP_DEBRIS1))->AddString(Field_debris_info[idx].name); ((CComboBox*)GetDlgItem(IDC_SHIP_DEBRIS1))->SetItemData(box_index, Field_debris_info[idx].index); box_index = ((CComboBox*)GetDlgItem(IDC_SHIP_DEBRIS2))->AddString(Field_debris_info[idx].name); ((CComboBox*)GetDlgItem(IDC_SHIP_DEBRIS2))->SetItemData(box_index, Field_debris_info[idx].index); box_index = ((CComboBox*)GetDlgItem(IDC_SHIP_DEBRIS3))->AddString(Field_debris_info[idx].name); ((CComboBox*)GetDlgItem(IDC_SHIP_DEBRIS3))->SetItemData(box_index, Field_debris_info[idx].index); } // now delete asteroid data // search by string Field_debris_info[1-3].name for (idx=0; idx<3; idx++) { box_index = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->FindStringExact(0, Field_debris_info[1].name); // "Asteroid Small" if (box_index > 0) { ((CComboBox*)GetDlgItem(Dlg_id[idx]))->DeleteString(box_index); } box_index = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->FindStringExact(0, Field_debris_info[2].name); // "Asteroid Medium" if (box_index > 0) { ((CComboBox*)GetDlgItem(Dlg_id[idx]))->DeleteString(box_index); } box_index = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->FindStringExact(0, Field_debris_info[3].name); // "Asteroid Big" if (box_index > 0) { ((CComboBox*)GetDlgItem(Dlg_id[idx]))->DeleteString(box_index); } } // set active debris type for each combo box int box_count, cur_box_data; for (idx=0;idx<3; idx++) { // Only set selection if not "None" if (a_field[cur_field].field_debris_type[idx] != -1) { box_count = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->GetCount(); for (box_index=0; box_index<box_count; box_index++) { cur_box_data = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->GetItemData(box_index); if (cur_box_data == a_field[cur_field].field_debris_type[idx]) { // set cur sel ((CComboBox*)GetDlgItem(Dlg_id[idx]))->SetCurSel(box_index); break; } } } } // set up asteroid subtype checkboxes ((CButton*)GetDlgItem(IDC_SUBTYPE1))->SetCheck(a_field[cur_field].field_debris_type[0] == 1); ((CButton*)GetDlgItem(IDC_SUBTYPE2))->SetCheck(a_field[cur_field].field_debris_type[1] == 1); ((CButton*)GetDlgItem(IDC_SUBTYPE3))->SetCheck(a_field[cur_field].field_debris_type[2] == 1); UpdateData(FALSE); OnEnableAsteroids(); last_field = cur_field; }
// Returns TRUE if the weapon will never hit the other object. // If it can it predicts how long until these two objects need // to be checked and fills the time in in current_pair. int weapon_will_never_hit( object *obj_weapon, object *other, obj_pair * current_pair ) { Assert( obj_weapon->type == OBJ_WEAPON ); weapon *wp = &Weapons[obj_weapon->instance]; weapon_info *wip = &Weapon_info[wp->weapon_info_index]; // mprintf(( "Frame: %d, Weapon=%d, Other=%d, pair=$%08x\n", G3_frame_count, OBJ_INDEX(weapon), OBJ_INDEX(other), current_pair )); // Do some checks for weapons that don't turn if ( !(wip->wi_flags & WIF_TURNS) ) { // This first check is to see if a weapon is behind an object, and they // are heading in opposite directions. If so, we don't need to ever check // them again. This is only valid for weapons that don't turn. float vdot; if (wip->subtype == WP_LASER) { vec3d velocity_rel_weapon; vm_vec_sub(&velocity_rel_weapon, &obj_weapon->phys_info.vel, &other->phys_info.vel); vdot = -vm_vec_dot(&velocity_rel_weapon, &obj_weapon->orient.vec.fvec); } else { vdot = vm_vec_dot( &other->phys_info.vel, &obj_weapon->phys_info.vel); } if ( vdot <= 0.0f ) { // They're heading in opposite directions... // check their positions vec3d weapon2other; vm_vec_sub( &weapon2other, &other->pos, &obj_weapon->pos ); float pdot = vm_vec_dot( &obj_weapon->orient.vec.fvec, &weapon2other ); if ( pdot <= -other->radius ) { // The other object is behind the weapon by more than // its radius, so it will never hit... return 1; } } // FUTURE ENHANCEMENT IDEAS // Given a laser does it hit a slow or not moving object // in its life or the next n seconds? We'd actually need to check the // model for this. } // This check doesn't care about orient, only looks at the maximum speed // of the two objects, so it knows that in the next n seconds, they can't // go further than some distance, so don't bother checking collisions for // that time. This is very rough, but is so general that it works for // everything and immidiately gets rid of a lot of cases. if ( current_pair ) { // Find the time it will take before these get within each others distances. // tmp->next_check_time = timestamp(500); //vector max_vel; //maximum foward velocity in x,y,z float max_vel_weapon, max_vel_other; //SUSHI: Fix bug where additive weapon velocity screws up collisions //Assumes that weapons which don't home don't change speed, which is currently the case. if (!(wip->wi_flags & WIF_TURNS)) max_vel_weapon = obj_weapon->phys_info.speed; else if (wp->lssm_stage==5) max_vel_weapon = wip->lssm_stage5_vel; else max_vel_weapon = wp->weapon_max_vel; max_vel_other = other->phys_info.max_vel.xyz.z; if (max_vel_other < 10.0f) { if ( vm_vec_mag_squared( &other->phys_info.vel ) > 100 ) { // bump up velocity from collision max_vel_other = vm_vec_mag( &other->phys_info.vel ) + 10.0f; } else { max_vel_other = 10.0f; // object may move from collision } } // check weapon that does not turn against sphere expanding at ship maxvel // compare (weeapon) ray with expanding sphere (ship) to find earliest possible collision time // look for two time solutions to Xw = Xs, where Xw = Xw0 + Vwt*t Xs = Xs + Vs*(t+dt), where Vs*dt = radius of ship // Since direction of Vs is unknown, solve for (Vs*t) and find norm of both sides if ( !(wip->wi_flags & WIF_TURNS) ) { vec3d delta_x, laser_vel; float a,b,c, delta_x_dot_vl, delta_t; float root1, root2, root, earliest_time; if (max_vel_weapon == max_vel_other) { // this will give us NAN using the below formula, so check every frame current_pair->next_check_time = timestamp(0); return 0; } vm_vec_sub( &delta_x, &obj_weapon->pos, &other->pos ); laser_vel = obj_weapon->phys_info.vel; // vm_vec_copy_scale( &laser_vel, &weapon->orient.vec.fvec, max_vel_weapon ); delta_t = (other->radius + 10.0f) / max_vel_other; // time to get from center to radius of other obj delta_x_dot_vl = vm_vec_dotprod( &delta_x, &laser_vel ); a = max_vel_weapon*max_vel_weapon - max_vel_other*max_vel_other; b = 2.0f * (delta_x_dot_vl - max_vel_other*max_vel_other*delta_t); c = vm_vec_mag_squared( &delta_x ) - max_vel_other*max_vel_other*delta_t*delta_t; float discriminant = b*b - 4.0f*a*c; if ( discriminant < 0) { // never hit return 1; } else { root = fl_sqrt( discriminant ); root1 = (-b + root) / (2.0f * a) * 1000.0f; // get time in ms root2 = (-b - root) / (2.0f * a) * 1000.0f; // get time in ms } // standard algorithm if (max_vel_weapon > max_vel_other) { // find earliest positive time if ( root1 > root2 ) { float temp = root1; root1 = root2; root2 = temp; } if (root1 > 0) { earliest_time = root1; } else if (root2 > 0) { // root1 < 0 and root2 > 0, so we're inside sphere and next check should be next frame current_pair->next_check_time = timestamp(0); // check next time return 0; } else { // both times negative, so never collides return 1; } } // need to modify it for weapons that are slower than ships else { if (root2 > 0) { earliest_time = root2; } else { current_pair->next_check_time = timestamp(0); return 0; } } // check if possible collision occurs after weapon expires if ( earliest_time > 1000*wp->lifeleft ) return 1; // Allow one worst case frametime to elapse (~5 fps) earliest_time -= 200.0f; if (earliest_time > 100) { current_pair->next_check_time = timestamp( fl2i(earliest_time) ); return 0; } else { current_pair->next_check_time = timestamp(0); // check next time return 0; } } else { float dist, max_vel, time; max_vel = max_vel_weapon + max_vel_other; // suggest that fudge factor for other radius be changed to other_radius + const (~10) dist = vm_vec_dist( &other->pos, &obj_weapon->pos ) - (other->radius + 10.0f); if ( dist > 0.0f ) { time = (dist*1000.0f) / max_vel; int time_ms = fl2i(time); // check if possible collision occurs after weapon expires if ( time_ms > 1000*wp->lifeleft ) return 1; time_ms -= 200; // Allow at least one worst case frametime to elapse (~5 fps) if ( time_ms > 100 ) { // If it takes longer than 1/10th of a second, then delay it current_pair->next_check_time = timestamp(time_ms); //mprintf(( "Delaying %d ms\n", time_ms )); return 0; } } current_pair->next_check_time = timestamp(0); // check next time } } return 0; }
// ******************************************************************************************** // Checks for changes every NPS_TICKRATE milliseconds // Checks: // * if we've gotten close enough to a nav point for it to be counted as "Visited" // * If we're current AutoNavigating it checks if we need to autodisengage void NavSystem_Do() { static unsigned int last_update = 0; if (clock () - last_update > NPS_TICKRATE) { if (AutoPilotEngaged) { if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) { camera *cam = nav_get_set_camera(); if (CinematicStarted) { // update our cinematic and possibly perform warp //if (!CameraMoving) if(cam != NULL) cam->set_rotation_facing(&Player_obj->pos); if (timestamp() >= MoveCamera && !CameraMoving && vm_vec_mag(&cameraTarget) > 0.0f) { //Free_camera->set_position(&cameraTarget, float(camMovingTime), float(camMovingTime)/2.0f); //Free_camera->set_translation_velocity(&cameraTarget); CameraMoving = true; } if (timestamp() >= EndAPCinematic || !CanAutopilot()) { nav_warp(); EndAutoPilot(); } } else { // start cinematic if (UseCutsceneBars) { Cutscene_bar_flags |= CUB_CUTSCENE; Cutscene_bar_flags &= ~CUB_GRADUAL; } nav_warp(true); if(cam != NULL) { cam->set_position(&cameraPos); cam->set_rotation_facing(&Autopilot_flight_leader->pos); } CinematicStarted = true; } } else { if (!CanAutopilot()) EndAutoPilot(); } } // check if a NavPoints target has left, delete it if so int i; for (i = 0; i < MAX_NAVPOINTS; i++) { if ((Navs[i].flags & NP_SHIP) && (Navs[i].target_obj != NULL)) { if (((ship*)Navs[i].target_obj)->objnum == -1) { if (CurrentNav == i) CurrentNav = -1; DelNavPoint(i); } } } // check if we're reached a Node for (i = 0; i < MAX_NAVPOINTS; i++) { if (Navs[i].target_obj != NULL) { if (Navs[i].flags & NP_VALIDTYPE && DistanceTo(i) < 1000) Navs[i].flags |= NP_VISITED; } } } // ramp time compression - only if not using cinematics if (AutoPilotEngaged && !(The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS)) { int dstfrm_start = start_dist - DistanceTo(CurrentNav); // Ramp UP time compression if (dstfrm_start < (3500*ramp_bias)) { if (dstfrm_start >= (3000*ramp_bias) && DistanceTo(CurrentNav) > 30000) set_time_compression(64); else if (dstfrm_start >= (2000*ramp_bias)) set_time_compression(32); else if (dstfrm_start >= (1600*ramp_bias)) set_time_compression(16); else if (dstfrm_start >= (1200*ramp_bias)) set_time_compression(8); else if (dstfrm_start >= (800*ramp_bias)) set_time_compression(4); else if (dstfrm_start >= (400*ramp_bias)) set_time_compression(2); } // Ramp DOWN time compression if (DistanceTo(CurrentNav) <= (7000*ramp_bias)) { int dist = DistanceTo(CurrentNav); if (dist >= (5000*ramp_bias)) set_time_compression(32); else if (dist >= (4000*ramp_bias)) set_time_compression(16); else if (dist >= (3000*ramp_bias)) set_time_compression(8); else if (dist >= (2000*ramp_bias)) set_time_compression(4); else if (dist >= (1000*ramp_bias)) set_time_compression(2); } } /* Link in any ships that need to be linked in when the player gets close enough. Always done by the player because: "making sure the AI fighters are where they're supposed to be at all times is like herding cats" -Woolie Wool */ for (int i = 0; i < MAX_SHIPS; i++) { if (Ships[i].objnum != -1 && Ships[i].flags2 & SF2_NAVPOINT_NEEDSLINK) { object *other_objp = &Objects[Ships[i].objnum]; if (vm_vec_dist_quick(&Player_obj->pos, &other_objp->pos) < (NavLinkDistance + other_objp->radius)) { Ships[i].flags2 &= ~SF2_NAVPOINT_NEEDSLINK; Ships[i].flags2 |= SF2_NAVPOINT_CARRY; send_autopilot_msgID(NP_MSG_MISC_LINKED); } } } }