do_endlevel_flythrough(int n) { object *obj; segment *pseg; int old_player_seg; flydata = &fly_objects[n]; obj = flydata->obj; old_player_seg = obj->segnum; //move the player for this frame if (!flydata->first_time) { vm_vec_scale_add2(&obj->pos,&flydata->step,FrameTime); angvec_add2_scale(&flydata->angles,&flydata->angstep,FrameTime); vm_angles_2_matrix(&obj->orient,&flydata->angles); } //check new player seg update_object_seg(obj); pseg = &Segments[obj->segnum]; if (flydata->first_time || obj->segnum != old_player_seg) { //moved into new seg vms_vector curcenter,nextcenter; fix step_size,seg_time; short entry_side,exit_side; //what sides we entry and leave through vms_vector dest_point; //where we are heading (center of exit_side) vms_angvec dest_angles; //where we want to be pointing vms_matrix dest_orient; int up_side; //find new exit side if (!flydata->first_time) { entry_side = matt_find_connect_side(obj->segnum,old_player_seg); exit_side = Side_opposite[entry_side]; } if (flydata->first_time || entry_side==-1 || pseg->children[exit_side]==-1) exit_side = find_exit_side(obj); { //find closest side to align to fix d,largest_d=-f1_0; int i; for (i=0;i<6;i++) { #ifdef COMPACT_SEGS vms_vector v1; get_side_normal(pseg, i, 0, &v1 ); d = vm_vec_dot(&v1,&flydata->obj->orient.uvec); #else d = vm_vec_dot(&pseg->sides[i].normals[0],&flydata->obj->orient.uvec); #endif if (d > largest_d) {largest_d = d; up_side=i;} } } //update target point & angles compute_center_point_on_side(&dest_point,pseg,exit_side); //update target point and movement points //offset object sideways if (flydata->offset_frac) { int s0=-1,s1,i; vms_vector s0p,s1p; fix dist; for (i=0;i<6;i++) if (i!=entry_side && i!=exit_side && i!=up_side && i!=Side_opposite[up_side]) if (s0==-1) s0 = i; else s1 = i; compute_center_point_on_side(&s0p,pseg,s0); compute_center_point_on_side(&s1p,pseg,s1); dist = fixmul(vm_vec_dist(&s0p,&s1p),flydata->offset_frac); if (dist-flydata->offset_dist > MAX_SLIDE_PER_SEGMENT) dist = flydata->offset_dist + MAX_SLIDE_PER_SEGMENT; flydata->offset_dist = dist; vm_vec_scale_add2(&dest_point,&obj->orient.rvec,dist); } vm_vec_sub(&flydata->step,&dest_point,&obj->pos); step_size = vm_vec_normalize_quick(&flydata->step); vm_vec_scale(&flydata->step,flydata->speed); compute_segment_center(&curcenter,pseg); compute_segment_center(&nextcenter,&Segments[pseg->children[exit_side]]); vm_vec_sub(&flydata->headvec,&nextcenter,&curcenter); #ifdef COMPACT_SEGS { vms_vector _v1; get_side_normal(pseg, up_side, 0, &_v1 ); vm_vector_2_matrix(&dest_orient,&flydata->headvec,&_v1,NULL); } #else vm_vector_2_matrix(&dest_orient,&flydata->headvec,&pseg->sides[up_side].normals[0],NULL); #endif vm_extract_angles_matrix(&dest_angles,&dest_orient); if (flydata->first_time) vm_extract_angles_matrix(&flydata->angles,&obj->orient); seg_time = fixdiv(step_size,flydata->speed); //how long through seg if (seg_time) { flydata->angstep.x = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.p,dest_angles.p),seg_time))); flydata->angstep.z = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.b,dest_angles.b),seg_time))); flydata->angstep.y = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.h,dest_angles.h),seg_time))); } else { flydata->angles = dest_angles; flydata->angstep.x = flydata->angstep.y = flydata->angstep.z = 0; } } flydata->first_time=0; }
// ----------------------------------------------------------------------------------------------------------- //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 }
void do_physics_align_object( object * obj ) { vms_vector desired_upvec; fixang delta_ang,roll_ang; //vms_vector forvec = {0,0,f1_0}; vms_matrix temp_matrix; fix d,largest_d=-f1_0; int i,best_side; best_side=0; // bank player according to segment orientation //find side of segment that player is most alligned with for (i=0;i<6;i++) { #ifdef COMPACT_SEGS vms_vector _tv1; get_side_normal( &Segments[obj->segnum], i, 0, &_tv1 ); d = vm_vec_dot(&_tv1,&obj->orient.uvec); #else d = vm_vec_dot(&Segments[obj->segnum].sides[i].normals[0],&obj->orient.uvec); #endif if (d > largest_d) {largest_d = d; best_side=i;} } if (floor_levelling) { // old way: used floor's normal as upvec #ifdef COMPACT_SEGS get_side_normal(&Segments[obj->segnum], 3, 0, &desired_upvec ); #else desired_upvec = Segments[obj->segnum].sides[3].normals[0]; #endif } else // new player leveling code: use normal of side closest to our up vec if (get_num_faces(&Segments[obj->segnum].sides[best_side])==2) { #ifdef COMPACT_SEGS vms_vector normals[2]; get_side_normals(&Segments[obj->segnum], best_side, &normals[0], &normals[1] ); desired_upvec.x = (normals[0].x + normals[1].x) / 2; desired_upvec.y = (normals[0].y + normals[1].y) / 2; desired_upvec.z = (normals[0].z + normals[1].z) / 2; vm_vec_normalize(&desired_upvec); #else side *s = &Segments[obj->segnum].sides[best_side]; desired_upvec.x = (s->normals[0].x + s->normals[1].x) / 2; desired_upvec.y = (s->normals[0].y + s->normals[1].y) / 2; desired_upvec.z = (s->normals[0].z + s->normals[1].z) / 2; vm_vec_normalize(&desired_upvec); #endif } else #ifdef COMPACT_SEGS get_side_normal(&Segments[obj->segnum], best_side, 0, &desired_upvec ); #else desired_upvec = Segments[obj->segnum].sides[best_side].normals[0]; #endif if (labs(vm_vec_dot(&desired_upvec,&obj->orient.fvec)) < f1_0/2) { vms_angvec tangles; 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); delta_ang += obj->mtype.phys_info.turnroll; if (abs(delta_ang) > DAMP_ANG) { vms_matrix rotmat, new_pm; 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; tangles.p = tangles.h = 0; tangles.b = roll_ang; vm_angles_2_matrix(&rotmat,&tangles); vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat); obj->orient = new_pm; } else floor_levelling=0; } }
// ----------------------------------------------------------------------------------------------------------- //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 draw_all_edges(automap *am) { g3s_codes cc; int i,j,nbright; ubyte nfacing,nnfacing; Edge_info *e; vms_vector *tv1; fix distance; fix min_distance = 0x7fffffff; g3s_point *p1, *p2; nbright=0; for (i=0; i<=am->highest_edge_index; i++ ) { //e = &am->edges[Edge_used_list[i]]; e = &am->edges[i]; if (!(e->flags & EF_USED)) continue; if ( e->flags & EF_TOO_FAR) continue; if (e->flags&EF_FRONTIER) { // A line that is between what we have seen and what we haven't if ( (!(e->flags&EF_SECRET))&&(e->color==am->wall_normal_color)) continue; // If a line isn't secret and is normal color, then don't draw it } cc=rotate_list(2,e->verts); distance = Segment_points[e->verts[1]].p3_z; if (min_distance>distance ) min_distance = distance; if (!cc.uand) { //all off screen? nfacing = nnfacing = 0; tv1 = &Vertices[e->verts[0]]; j = 0; while( j<e->num_faces && (nfacing==0 || nnfacing==0) ) { #ifdef COMPACT_SEGS vms_vector temp_v; get_side_normal(&Segments[e->segnum[j]], e->sides[j], 0, &temp_v ); if (!g3_check_normal_facing( tv1, &temp_v ) ) #else if (!g3_check_normal_facing( tv1, &Segments[e->segnum[j]].sides[e->sides[j]].normals[0] ) ) #endif nfacing++; else nnfacing++; j++; } if ( nfacing && nnfacing ) { // a contour line am->drawingListBright[nbright++] = e-am->edges; } else if ( e->flags&(EF_DEFINING|EF_GRATE) ) { if ( nfacing == 0 ) { if ( e->flags & EF_NO_FADE ) gr_setcolor( e->color ); else gr_setcolor( gr_fade_table[e->color+256*8] ); g3_draw_line( &Segment_points[e->verts[0]], &Segment_points[e->verts[1]] ); } else { am->drawingListBright[nbright++] = e-am->edges; } } } } if ( min_distance < 0 ) min_distance = 0; // Sort the bright ones using a shell sort { int t; int i, j, incr, v1, v2; incr = nbright / 2; while( incr > 0 ) { for (i=incr; i<nbright; i++ ) { j = i - incr; while (j>=0 ) { // compare element j and j+incr v1 = am->edges[am->drawingListBright[j]].verts[0]; v2 = am->edges[am->drawingListBright[j+incr]].verts[0]; if (Segment_points[v1].p3_z < Segment_points[v2].p3_z) { // If not in correct order, them swap 'em t=am->drawingListBright[j+incr]; am->drawingListBright[j+incr]=am->drawingListBright[j]; am->drawingListBright[j]=t; j -= incr; } else break; } } incr = incr / 2; } } // Draw the bright ones for (i=0; i<nbright; i++ ) { int color; fix dist; e = &am->edges[am->drawingListBright[i]]; p1 = &Segment_points[e->verts[0]]; p2 = &Segment_points[e->verts[1]]; dist = p1->p3_z - min_distance; // Make distance be 1.0 to 0.0, where 0.0 is 10 segments away; if ( dist < 0 ) dist=0; if ( dist >= am->farthest_dist ) continue; if ( e->flags & EF_NO_FADE ) { gr_setcolor( e->color ); } else { dist = F1_0 - fixdiv( dist, am->farthest_dist ); color = f2i( dist*31 ); gr_setcolor( gr_fade_table[e->color+color*256] ); } g3_draw_line( p1, p2 ); } }
void automap_build_edge_list(automap *am) { int i,e1,e2,s; Edge_info * e; // clear edge list for (i=0; i<am->max_edges; i++) { am->edges[i].num_faces = 0; am->edges[i].flags = 0; } am->num_edges = 0; am->highest_edge_index = -1; if (cheats.fullautomap || (Players[Player_num].flags & PLAYER_FLAGS_MAP_ALL) ) { // Cheating, add all edges as visited for (s=0; s<=Highest_segment_index; s++) #ifdef EDITOR if (Segments[s].segnum != -1) #endif { add_segment_edges(am, &Segments[s]); } } else { // Not cheating, add visited edges, and then unvisited edges for (s=0; s<=Highest_segment_index; s++) #ifdef EDITOR if (Segments[s].segnum != -1) #endif if (Automap_visited[s]) { add_segment_edges(am, &Segments[s]); } for (s=0; s<=Highest_segment_index; s++) #ifdef EDITOR if (Segments[s].segnum != -1) #endif if (!Automap_visited[s]) { add_unknown_segment_edges(am, &Segments[s]); } } // Find unnecessary lines (These are lines that don't have to be drawn because they have small curvature) for (i=0; i<=am->highest_edge_index; i++ ) { e = &am->edges[i]; if (!(e->flags&EF_USED)) continue; for (e1=0; e1<e->num_faces; e1++ ) { for (e2=1; e2<e->num_faces; e2++ ) { if ( (e1 != e2) && (e->segnum[e1] != e->segnum[e2]) ) { #ifdef COMPACT_SEGS vms_vector v1, v2; get_side_normal(&Segments[e->segnum[e1]], e->sides[e1], 0, &v1 ); get_side_normal(&Segments[e->segnum[e2]], e->sides[e2], 0, &v2 ); if ( vm_vec_dot(&v1,&v2) > (F1_0-(F1_0/10)) ) { #else if ( vm_vec_dot( &Segments[e->segnum[e1]].sides[e->sides[e1]].normals[0], &Segments[e->segnum[e2]].sides[e->sides[e2]].normals[0] ) > (F1_0-(F1_0/10)) ) { #endif e->flags &= (~EF_DEFINING); break; } } } if (!(e->flags & EF_DEFINING)) break; } } } char Marker_input [40]; int Marker_index=0; ubyte DefiningMarkerMessage=0; ubyte MarkerBeingDefined; ubyte LastMarkerDropped; void InitMarkerInput () { int maxdrop,i; //find free marker slot if (Game_mode & GM_MULTI) maxdrop=MAX_DROP_MULTI; else maxdrop=MAX_DROP_SINGLE; for (i=0;i<maxdrop;i++) if (MarkerObject[(Player_num*2)+i] == -1) //found free slot! break; if (i==maxdrop) //no free slot { if (Game_mode & GM_MULTI) i = !LastMarkerDropped; //in multi, replace older of two else { HUD_init_message_literal(HM_DEFAULT, "No free marker slots"); return; } } //got a free slot. start inputting marker message Marker_input[0]=0; Marker_index=0; DefiningMarkerMessage=1; MarkerBeingDefined = i; key_toggle_repeat(1); }
void automap_build_edge_list() { int i,e1,e2,s; Edge_info * e; Automap_cheat = 0; if ( Players[Player_num].flags & PLAYER_FLAGS_MAP_ALL_CHEAT ) Automap_cheat = 1; // Damn cheaters... // clear edge list for (i=0; i<Max_edges; i++) { Edges[i].num_faces = 0; Edges[i].flags = 0; } Num_edges = 0; Highest_edge_index = -1; if (Automap_cheat || (Players[Player_num].flags & PLAYER_FLAGS_MAP_ALL) ) { // Cheating, add all edges as visited for (s=0; s<=Highest_segment_index; s++) #ifdef EDITOR if (Segments[s].segnum != -1) #endif { add_segment_edges(&Segments[s]); } } else { // Not cheating, add visited edges, and then unvisited edges for (s=0; s<=Highest_segment_index; s++) #ifdef EDITOR if (Segments[s].segnum != -1) #endif if (Automap_visited[s]) { add_segment_edges(&Segments[s]); } for (s=0; s<=Highest_segment_index; s++) #ifdef EDITOR if (Segments[s].segnum != -1) #endif if (!Automap_visited[s]) { add_unknown_segment_edges(&Segments[s]); } } // Find unnecessary lines (These are lines that don't have to be drawn because they have small curvature) for (i=0; i<=Highest_edge_index; i++ ) { e = &Edges[i]; if (!(e->flags&EF_USED)) continue; for (e1=0; e1<e->num_faces; e1++ ) { for (e2=1; e2<e->num_faces; e2++ ) { if ( (e1 != e2) && (e->segnum[e1] != e->segnum[e2]) ) { #ifdef COMPACT_SEGS vms_vector v1, v2; get_side_normal(&Segments[e->segnum[e1]], e->sides[e1], 0, &v1 ); get_side_normal(&Segments[e->segnum[e2]], e->sides[e2], 0, &v2 ); if ( vm_vec_dot(&v1,&v2) > (F1_0-(F1_0/10)) ) { #else if ( vm_vec_dot( &Segments[e->segnum[e1]].sides[e->sides[e1]].normals[0], &Segments[e->segnum[e2]].sides[e->sides[e2]].normals[0] ) > (F1_0-(F1_0/10)) ) { #endif e->flags &= (~EF_DEFINING); break; } } } if (!(e->flags & EF_DEFINING)) break; } } mprintf( (0, "Automap used %d / %d edges\n", Num_edges, Max_edges )); }
void DoPhysicsSim (tObject *objP) { short ignoreObjList [MAX_IGNORE_OBJS], nIgnoreObjs; int iseg; int bRetry; int fate; vmsVector vFrame; //movement in this frame vmsVector vNewPos, ipos; //position after this frame int count=0; short nObject = OBJ_IDX (objP); short nWallHitSeg, nWallHitSide; fvi_info hi; fvi_query fq; vmsVector vSavePos; int nSaveSeg; fix xDrag; fix xSimTime, xOldSimTime, xTimeScale; vmsVector vStartPos; int bObjStopped=0; fix xMovedTime; //how long objected moved before hit something vmsVector vSaveP0, vSaveP1; tPhysicsInfo *pi; short nOrigSegment = objP->nSegment; int bBounced = 0; tSpeedBoostData sbd = gameData.objs.speedBoost [nObject]; int bDoSpeedBoost = sbd.bBoosted; // && (objP == gameData.objs.console); Assert (objP->nType != OBJ_NONE); Assert (objP->movementType == MT_PHYSICS); if (objP->nType == OBJ_PLAYER && Controls.headingTime) objP = objP; #ifdef _DEBUG if (bDontMoveAIObjects) if (objP->controlType == CT_AI) return; #endif pi = &objP->mType.physInfo; DoPhysicsSimRot (objP); #if 1 if (!(pi->velocity.x || pi->velocity.y || pi->velocity.z)) { UnstickObject (objP); if (objP == gameData.objs.console) gameData.objs.speedBoost [nObject].bBoosted = sbd.bBoosted = 0; if (!(pi->thrust.x || pi->thrust.y || pi->thrust.z)) return; } #endif nPhysSegs = 0; bSimpleFVI = (objP->nType != OBJ_PLAYER); xSimTime = gameData.time.xFrame; vStartPos = objP->pos; nIgnoreObjs = 0; Assert (objP->mType.physInfo.brakes==0); //brakes not used anymore? //if uses thrust, cannot have zero xDrag Assert (!(objP->mType.physInfo.flags&PF_USES_THRUST) || objP->mType.physInfo.drag!=0); //do thrust & xDrag if (xDrag = objP->mType.physInfo.drag) { int count; vmsVector accel, *vel = &objP->mType.physInfo.velocity; fix r, k, d, a; d = f1_0 - xDrag; count = xSimTime / FT; r = xSimTime % FT; k = FixDiv (r, FT); if (objP->mType.physInfo.flags & PF_USES_THRUST) { VmVecCopyScale (&accel, &objP->mType.physInfo.thrust, FixDiv (f1_0, objP->mType.physInfo.mass)); a = (accel.x || accel.y || accel.z); if (bDoSpeedBoost && !(a || gameStates.input.bControlsSkipFrame)) *vel = sbd.vVel; else { while (count--) { if (a) VmVecInc (vel, &accel); VmVecScale (vel, d); } //do linear scale on remaining bit of time VmVecScaleInc (vel, &accel, k); VmVecScale (vel, f1_0 - FixMul (k, xDrag)); if (bDoSpeedBoost) { if (vel->x < sbd.vMinVel.x) vel->x = sbd.vMinVel.x; else if (vel->x > sbd.vMaxVel.x) vel->x = sbd.vMaxVel.x; if (vel->y < sbd.vMinVel.y) vel->y = sbd.vMinVel.y; else if (vel->y > sbd.vMaxVel.y) vel->y = sbd.vMaxVel.y; if (vel->z < sbd.vMinVel.z) vel->z = sbd.vMinVel.z; else if (vel->z > sbd.vMaxVel.z) vel->z = sbd.vMaxVel.z; } } } else { fix xTotalDrag = f1_0; while (count--) xTotalDrag = FixMul (xTotalDrag, d); //do linear scale on remaining bit of time xTotalDrag = FixMul (xTotalDrag, f1_0-FixMul (k, xDrag)); VmVecScale (&objP->mType.physInfo.velocity, xTotalDrag); } } if (extraGameInfo [IsMultiGame].bFluidPhysics) { if (gameData.segs.segment2s [objP->nSegment].special == SEGMENT_IS_WATER) xTimeScale = 75; else if (gameData.segs.segment2s [objP->nSegment].special == SEGMENT_IS_LAVA) xTimeScale = 66; else xTimeScale = 100; } else xTimeScale = 100; do { bRetry = 0; //Move the tObject VmVecCopyScale ( &vFrame, &objP->mType.physInfo.velocity, FixMulDiv (xSimTime, xTimeScale, 100)); if ((vFrame.x == 0) && (vFrame.y == 0) && (vFrame.z == 0)) break; retryMove: count++; // If retry count is getting large, then we are trying to do something stupid. if (count > 3) { if (objP->nType == OBJ_PLAYER) { if (count > 8) { if (sbd.bBoosted) sbd.bBoosted = 0; break; } } else break; } VmVecAdd (&vNewPos, &objP->pos, &vFrame); ignoreObjList [nIgnoreObjs] = -1; fq.p0 = &objP->pos; fq.startSeg = objP->nSegment; fq.p1 = &vNewPos; fq.rad = objP->size; fq.thisObjNum = nObject; fq.ignoreObjList = ignoreObjList; fq.flags = FQ_CHECK_OBJS; if (objP->nType == OBJ_WEAPON) fq.flags |= FQ_TRANSPOINT; //if (objP->nType == OBJ_PLAYER) fq.flags |= FQ_GET_SEGLIST; vSaveP0 = *fq.p0; vSaveP1 = *fq.p1; fate = FindVectorIntersection (&fq, &hi); #ifdef _DEBUG if (fate == HIT_WALL) fate = FindVectorIntersection (&fq, &hi); #endif // Matt: Mike's hack. if (fate == HIT_OBJECT) { tObject *objP = gameData.objs.objects + hi.hit.nObject; if ((objP->nType == OBJ_WEAPON) && ((objP->id == PROXIMITY_ID) || (objP->id == SUPERPROX_ID))) count--; } #ifdef _DEBUG if (fate == HIT_BAD_P0) { #if 0 //TRACE con_printf (CON_DEBUG, "Warning: Bad p0 in physics! Object = %i, nType = %i [%s]\n", objP - gameData.objs.objects, objP->nType, ObjectType_names [objP->nType]); #endif Int3 (); } #endif //if ((objP->nType == OBJ_PLAYER) || (objP->nType == OBJ_ROBOT) || (objP->nType == OBJ_MONSTERBALL)) { int i; #ifdef RELEASE int j; #endif if (nPhysSegs && (physSegList [nPhysSegs-1] == hi.segList [0])) nPhysSegs--; #ifdef RELEASE j = MAX_FVI_SEGS - nPhysSegs - 1; if (j > hi.nSegments) j = hi.nSegments; memcpy (physSegList + nPhysSegs, hi.segList, j * sizeof (*physSegList)); nPhysSegs += j; #else for (i = 0; (i < hi.nSegments) && (nPhysSegs < MAX_FVI_SEGS-1); ) { if (hi.segList [i] > gameData.segs.nLastSegment) LogErr ("Invalid segment in segment list #1\n"); physSegList [nPhysSegs++] = hi.segList [i++]; } #endif } ipos = hi.hit.vPoint; iseg = hi.hit.nSegment; nWallHitSide = hi.hit.nSide; nWallHitSeg = hi.hit.nSideSegment; if (iseg==-1) { //some sort of horrible error if (objP->nType == OBJ_WEAPON) objP->flags |= OF_SHOULD_BE_DEAD; break; } Assert (!((fate==HIT_WALL) && ((nWallHitSeg == -1) || (nWallHitSeg > gameData.segs.nLastSegment)))); vSavePos = objP->pos; //save the tObject's position nSaveSeg = objP->nSegment; // update tObject's position and tSegment number objP->pos = ipos; if (iseg != objP->nSegment) RelinkObject (nObject, iseg); //if start point not in tSegment, move tObject to center of tSegment if (GetSegMasks (&objP->pos, objP->nSegment, 0).centerMask) { //tObject stuck vmsVector vCenter; int n = FindObjectSeg (objP); if (n == -1) { n = FindSegByPoint (&objP->last_pos, objP->nSegment); if (n == -1) { objP->flags |= OF_SHOULD_BE_DEAD; return; } } objP->pos = objP->last_pos; RelinkObject (nObject, n); COMPUTE_SEGMENT_CENTER_I (&vCenter, objP->nSegment); VmVecDec (&vCenter, &objP->pos); if (VmVecMag (&vCenter) > F1_0) { VmVecNormalize (&vCenter); VmVecScaleFrac (&vCenter, 1, 10); } VmVecDec (&objP->pos, &vCenter); //return; } //calulate new sim time { //vmsVector vMoved; vmsVector vMoveNormal; fix attemptedDist, actualDist; xOldSimTime = xSimTime; actualDist = VmVecNormalizedDir (&vMoveNormal, &objP->pos, &vSavePos); if ((fate == HIT_WALL) && (VmVecDot (&vMoveNormal, &vFrame) < 0)) { //moved backwards //don't change position or xSimTime objP->pos = vSavePos; //iseg = objP->nSegment; //don't change tSegment if (nSaveSeg != iseg) RelinkObject (nObject, nSaveSeg); if (bDoSpeedBoost) { // int h = FindSegByPoint (&vNewPos, -1); objP->pos = vStartPos; SetSpeedBoostVelocity (nObject, -1, -1, -1, -1, -1, &vStartPos, &sbd.vDest, 0); VmVecCopyScale (&vFrame, &sbd.vVel, xSimTime); goto retryMove; } xMovedTime = 0; } else { //retryMove2: attemptedDist = VmVecMag (&vFrame); xSimTime = FixMulDiv (xSimTime, attemptedDist-actualDist, attemptedDist); xMovedTime = xOldSimTime - xSimTime; if ((xSimTime < 0) || (xSimTime > xOldSimTime)) { xSimTime = xOldSimTime; xMovedTime = 0; } } } switch (fate) { case HIT_WALL: { vmsVector vMoved; fix xHitSpeed, xWallPart, xSideDist, xSideDists [6]; short nSegment; // Find hit speed #if 0//def _DEBUG if (objP->nType == OBJ_PLAYER) HUDMessage (0, "WALL CONTACT"); fate = FindVectorIntersection (&fq, &hi); #endif VmVecSub (&vMoved, &objP->pos, &vSavePos); xWallPart = VmVecDot (&vMoved, &hi.hit.vNormal); if (xWallPart && (xMovedTime > 0) && (xHitSpeed = -FixDiv (xWallPart, xMovedTime)) > 0) { CollideObjectWithWall (objP, xHitSpeed, nWallHitSeg, nWallHitSide, &hi.hit.vPoint); #if 0//def _DEBUG if (objP->nType == OBJ_PLAYER) HUDMessage (0, "BUMP!"); #endif } else { #if 0//def _DEBUG if (objP->nType == OBJ_PLAYER) HUDMessage (0, "SCREEEEEEEEEECH"); #endif ScrapeObjectOnWall (objP, nWallHitSeg, nWallHitSide, &hi.hit.vPoint); } Assert (nWallHitSeg > -1); Assert (nWallHitSide > -1); GetSideDistsAll (&objP->pos, nWallHitSeg, xSideDists); if ((xSideDist = xSideDists [nWallHitSide]) && (xSideDist < objP->size - objP->size / 100)) { #if 1 float r = 0.1f; #else float r; xSideDist = objP->size - xSideDist; r = ((float) xSideDist / (float) objP->size) * f2fl (objP->size); #endif objP->pos.x += (fix) ((float) hi.hit.vNormal.x * r); objP->pos.y += (fix) ((float) hi.hit.vNormal.y * r); objP->pos.z += (fix) ((float) hi.hit.vNormal.z * r); nSegment = FindSegByPoint (&objP->pos, objP->nSegment); if (nSegment != objP->nSegment) RelinkObject (OBJ_IDX (objP), nSegment); #if 0//def _DEBUG if (objP->nType == OBJ_PLAYER) HUDMessage (0, "PENETRATING WALL (%d, %1.4f)", objP->size - xSideDists [nWallHitSide], r); #endif bRetry = 1; } if (!(objP->flags & OF_SHOULD_BE_DEAD)) { int forcefield_bounce; //bounce off a forcefield Assert (gameStates.app.cheats.bBouncingWeapons || !(objP->mType.physInfo.flags & PF_STICK && objP->mType.physInfo.flags & PF_BOUNCE)); //can't be bounce and stick forcefield_bounce = (gameData.pig.tex.pTMapInfo [gameData.segs.segments [nWallHitSeg].sides [nWallHitSide].nBaseTex].flags & TMI_FORCE_FIELD); if (!forcefield_bounce && (objP->mType.physInfo.flags & PF_STICK)) { //stop moving AddStuckObject (objP, nWallHitSeg, nWallHitSide); VmVecZero (&objP->mType.physInfo.velocity); bObjStopped = 1; bRetry = 0; } else { // Slide tObject along wall int bCheckVel = 0; //We're constrained by wall, so subtract wall part from //velocity vector xWallPart = VmVecDot (&hi.hit.vNormal, &objP->mType.physInfo.velocity); if (forcefield_bounce || (objP->mType.physInfo.flags & PF_BOUNCE)) { //bounce off wall xWallPart *= 2; //Subtract out wall part twice to achieve bounce if (forcefield_bounce) { bCheckVel = 1; //check for max velocity if (objP->nType == OBJ_PLAYER) xWallPart *= 2; //player bounce twice as much } if (objP->mType.physInfo.flags & PF_BOUNCES_TWICE) { Assert (objP->mType.physInfo.flags & PF_BOUNCE); if (objP->mType.physInfo.flags & PF_BOUNCED_ONCE) objP->mType.physInfo.flags &= ~ (PF_BOUNCE+PF_BOUNCED_ONCE+PF_BOUNCES_TWICE); else objP->mType.physInfo.flags |= PF_BOUNCED_ONCE; } bBounced = 1; //this tObject bBounced } VmVecScaleInc (&objP->mType.physInfo.velocity, &hi.hit.vNormal, -xWallPart); if (bCheckVel) { fix vel = VmVecMag (&objP->mType.physInfo.velocity); if (vel > MAX_OBJECT_VEL) VmVecScale (&objP->mType.physInfo.velocity, FixDiv (MAX_OBJECT_VEL, vel)); } if (bBounced && (objP->nType == OBJ_WEAPON)) VmVector2Matrix (&objP->orient, &objP->mType.physInfo.velocity, &objP->orient.uVec, NULL); bRetry = 1; } } break; } case HIT_OBJECT: { vmsVector vOldVel; vmsVector *ppos0, *ppos1, vHitPos; fix size0, size1; // Mark the hit tObject so that on a retry the fvi code // ignores this tObject. //if (bSpeedBoost && (objP == gameData.objs.console)) // break; Assert (hi.hit.nObject != -1); ppos0 = &gameData.objs.objects [hi.hit.nObject].pos; ppos1 = &objP->pos; size0 = gameData.objs.objects [hi.hit.nObject].size; size1 = objP->size; // Calculcate the hit point between the two objects. Assert (size0+size1 != 0); // Error, both sizes are 0, so how did they collide, anyway?!? //VmVecScale (VmVecSub (&pos_hit, ppos1, ppos0), FixDiv (size0, size0 + size1); //VmVecInc (&pos_hit, ppos0); VmVecSub (&vHitPos, ppos1, ppos0); VmVecScaleAdd (&vHitPos, ppos0, &vHitPos, FixDiv (size0, size0 + size1)); vOldVel = objP->mType.physInfo.velocity; CollideTwoObjects (objP, gameData.objs.objects + hi.hit.nObject, &vHitPos); if (sbd.bBoosted && (objP == gameData.objs.console)) objP->mType.physInfo.velocity = vOldVel; // Let tObject continue its movement if (!(objP->flags&OF_SHOULD_BE_DEAD) ) { if (objP->mType.physInfo.flags&PF_PERSISTENT || (vOldVel.x == objP->mType.physInfo.velocity.x && vOldVel.y == objP->mType.physInfo.velocity.y && vOldVel.z == objP->mType.physInfo.velocity.z)) { //if (gameData.objs.objects [hi.hit.nObject].nType == OBJ_POWERUP) ignoreObjList [nIgnoreObjs++] = hi.hit.nObject; bRetry = 1; } } break; } case HIT_NONE: #ifdef TACTILE if (TactileStick && objP==gameData.objs.console && !(FrameCount & 15)) Tactile_Xvibrate_clear (); #endif break; #ifdef _DEBUG case HIT_BAD_P0: Int3 (); // Unexpected collision nType: start point not in specified tSegment. #if TRACE con_printf (CON_DEBUG, "Warning: Bad p0 in physics!!!\n"); #endif break; default: // Unknown collision nType returned from FindVectorIntersection!! Int3 (); break; #endif } } while (bRetry); // Pass retry count info to AI. if (objP->controlType == CT_AI) { if (count > 0) { gameData.ai.localInfo [nObject].nRetryCount = count-1; #ifdef _DEBUG Total_retries += count-1; Total_sims++; #endif } } //I'm not sure why we do this. I wish there were a comment that //explained it. I think maybe it only needs to be done if the tObject //is sliding, but I don't know if (!(sbd.bBoosted || bObjStopped || bBounced)) { //Set velocity from actual movement vmsVector vMoved; VmVecSub (&vMoved, &objP->pos, &vStartPos); VmVecCopyScale (&objP->mType.physInfo.velocity, &vMoved, FixMulDiv (FixDiv (f1_0, gameData.time.xFrame), 100, xTimeScale)); #ifdef BUMP_HACK if (objP == gameData.objs.console && objP->mType.physInfo.velocity.x==0 && objP->mType.physInfo.velocity.y==0 && objP->mType.physInfo.velocity.z==0 && (objP->mType.physInfo.thrust.x!=0 || objP->mType.physInfo.thrust.y!=0 || objP->mType.physInfo.thrust.z!=0)) { vmsVector vCenter, vBump; //bump player a little towards vCenter of tSegment to unstick COMPUTE_SEGMENT_CENTER_I (&vCenter, objP->nSegment); //HUDMessage (0, "BUMP! %d %d", d1, d2); //don't bump player toward vCenter of reactor tSegment if (gameData.segs.segment2s [objP->nSegment].special == SEGMENT_IS_CONTROLCEN) VmVecNegate (&vBump); VmVecScaleInc (&objP->pos, &vBump, objP->size/5); //if moving away from seg, might move out of seg, so update if (gameData.segs.segment2s [objP->nSegment].special == SEGMENT_IS_CONTROLCEN) UpdateObjectSeg (objP); } #endif } //Assert (check_point_in_seg (&objP->pos, objP->nSegment, 0).centerMask==0); //if (objP->controlType == CT_FLYING) if (objP->mType.physInfo.flags & PF_LEVELLING) DoPhysicsAlignObject (objP); //hack to keep player from going through closed doors if ((objP->nType == OBJ_PLAYER) && (objP->nSegment != nOrigSegment) && (gameStates.app.cheats.bPhysics != 0xBADA55)) { int nSide; nSide = FindConnectedSide (gameData.segs.segments + objP->nSegment, gameData.segs.segments + nOrigSegment); if (nSide != -1) { if (!(WALL_IS_DOORWAY (gameData.segs.segments + nOrigSegment, nSide, NULL) & WID_FLY_FLAG)) { tSide *sideP; int nVertex, nFaces; fix dist; int vertex_list [6]; //bump tObject back sideP = gameData.segs.segments [nOrigSegment].sides + nSide; if (nOrigSegment==-1) Error ("nOrigSegment == -1 in physics"); CreateAbsVertexLists (&nFaces, vertex_list, nOrigSegment, nSide); //let'sideP pretend this wall is not triangulated nVertex = vertex_list [0]; if (nVertex > vertex_list [1]) nVertex = vertex_list [1]; if (nVertex > vertex_list [2]) nVertex = vertex_list [2]; if (nVertex > vertex_list [3]) nVertex = vertex_list [3]; #ifdef COMPACT_SEGS { vmsVector _vn; get_side_normal (gameData.segs.segments + nOrigSegment, nSide, 0, &_vn); dist = VmDistToPlane (&vStartPos, &_vn, &gameData.segs.vertices [nVertex]); VmVecScaleAdd (&objP->pos, &vStartPos, &_vn, objP->size-dist); } #else dist = VmDistToPlane (&vStartPos, sideP->normals, gameData.segs.vertices + nVertex); VmVecScaleAdd (&objP->pos, &vStartPos, sideP->normals, objP->size-dist); #endif UpdateObjectSeg (objP); } } } //if end point not in tSegment, move tObject to last pos, or tSegment center if (GetSegMasks (&objP->pos, objP->nSegment, 0).centerMask) { if (FindObjectSeg (objP) == -1) { int n; if (objP->nType==OBJ_PLAYER && (n=FindSegByPoint (&objP->last_pos, objP->nSegment))!=-1) { objP->pos = objP->last_pos; RelinkObject (nObject, n); } else { COMPUTE_SEGMENT_CENTER_I (&objP->pos, objP->nSegment); objP->pos.x += nObject; } if (objP->nType == OBJ_WEAPON) objP->flags |= OF_SHOULD_BE_DEAD; } } UnstickObject (objP); }