/* THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. */ /* * * Code for flying through the mines * */ #include #include #include "joy.h" #include "error.h" #include "inferno.h" #include "segment.h" #include "object.h" #include "physics.h" #include "key.h" #include "game.h" #include "collide.h" #include "fvi.h" #include "newdemo.h" #include "timer.h" #include "ai.h" #include "wall.h" #include "laser.h" #include "bm.h" #include "player.h" //Global variables for physics system #define ROLL_RATE 0x2000 #define DAMP_ANG 0x400 //min angle to bank #define TURNROLL_SCALE (0x4ec4/2) #define MAX_OBJECT_VEL i2f(100) //check point against each side of segment. return bitmask, where bit //set means behind that side int floor_levelling=0; //make sure matrix is orthogonal void check_and_fix_matrix(vms_matrix *m) { vms_matrix tempm; vm_vector_2_matrix(&tempm,&m->fvec,&m->uvec,NULL); *m = tempm; } 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) { fixang save_delta_ang; vms_angvec tangles; vm_vector_2_matrix(&temp_matrix,&obj->orient.fvec,&desired_upvec,NULL); save_delta_ang = 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; } } void set_object_turnroll(object *obj) { fixang desired_bank; desired_bank = -fixmul(obj->mtype.phys_info.rotvel.y,TURNROLL_SCALE); if (obj->mtype.phys_info.turnroll != desired_bank) { fixang delta_ang,max_roll; max_roll = fixmul(ROLL_RATE,FrameTime); delta_ang = desired_bank - obj->mtype.phys_info.turnroll; if (labs(delta_ang) < max_roll) max_roll = delta_ang; else if (delta_ang < 0) max_roll = -max_roll; obj->mtype.phys_info.turnroll += max_roll; } } //list of segments went through int phys_seglist[MAX_FVI_SEGS],n_phys_segs; #define MAX_IGNORE_OBJS 100 #ifndef NDEBUG #define EXTRA_DEBUG 1 //no extra debug when NDEBUG is on #endif #ifdef EXTRA_DEBUG object *debug_obj=NULL; #endif #define XYZ(v) (v)->x,(v)->y,(v)->z #ifndef NDEBUG int Total_retries=0, Total_sims=0; int Dont_move_ai_objects=0; #endif #define FT (f1_0/64) // ----------------------------------------------------------------------------------------------------------- // add rotational velocity & acceleration void do_physics_sim_rot(object *obj) { vms_angvec tangles; vms_matrix rotmat,new_orient; //fix rotdrag_scale; physics_info *pi; Assert(FrameTime > 0); //Get MATT if hit this! pi = &obj->mtype.phys_info; if (!(pi->rotvel.x || pi->rotvel.y || pi->rotvel.z || pi->rotthrust.x || pi->rotthrust.y || pi->rotthrust.z)) return; if (obj->mtype.phys_info.drag) { int count; vms_vector accel; fix drag,r,k; count = FrameTime / FT; r = FrameTime % FT; k = fixdiv(r,FT); drag = (obj->mtype.phys_info.drag*5)/2; if (obj->mtype.phys_info.flags & PF_USES_THRUST) { vm_vec_copy_scale(&accel,&obj->mtype.phys_info.rotthrust,fixdiv(f1_0,obj->mtype.phys_info.mass)); while (count--) { vm_vec_add2(&obj->mtype.phys_info.rotvel,&accel); vm_vec_scale(&obj->mtype.phys_info.rotvel,f1_0-drag); } //do linear scale on remaining bit of time vm_vec_scale_add2(&obj->mtype.phys_info.rotvel,&accel,k); vm_vec_scale(&obj->mtype.phys_info.rotvel,f1_0-fixmul(k,drag)); } else if (! (obj->mtype.phys_info.flags & PF_FREE_SPINNING)) { 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.rotvel,total_drag); } } //now rotate object //unrotate object for bank caused by turn if (obj->mtype.phys_info.turnroll) { vms_matrix new_pm; tangles.p = tangles.h = 0; tangles.b = -obj->mtype.phys_info.turnroll; vm_angles_2_matrix(&rotmat,&tangles); vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat); obj->orient = new_pm; } tangles.p = fixmul(obj->mtype.phys_info.rotvel.x,FrameTime); tangles.h = fixmul(obj->mtype.phys_info.rotvel.y,FrameTime); tangles.b = fixmul(obj->mtype.phys_info.rotvel.z,FrameTime); vm_angles_2_matrix(&rotmat,&tangles); vm_matrix_x_matrix(&new_orient,&obj->orient,&rotmat); obj->orient = new_orient; if (obj->mtype.phys_info.flags & PF_TURNROLL) set_object_turnroll(obj); //re-rotate object for bank caused by turn if (obj->mtype.phys_info.turnroll) { vms_matrix new_pm; tangles.p = tangles.h = 0; tangles.b = obj->mtype.phys_info.turnroll; vm_angles_2_matrix(&rotmat,&tangles); vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat); obj->orient = new_pm; } check_and_fix_matrix(&obj->orient); } // ----------------------------------------------------------------------------------------------------------- //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,bumpcount=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,old_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; int bounced=0; fix PhysTime = (FrameTimemovement_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) { 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; 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) || (objp->id == SUPERPROX_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;(itype == 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, __FILE__, __LINE__).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 ); /* * On joining edges fvi tends to get inaccurate as hell due to object size. And I have no means to fix that - shame on me (that whole code should be rewritten). Approach is to check if the object interects with the wall and if so, move it out towards segment center. Two things to improve: * 1) object_intersects_wall() does not say how far we went inside the wall which is why we use while to move out until we do not intersect anymore. This should be improved so we only move one time. * 2) we move the object towards segment center. Depending on the level architecture this is safe. However this will heavily influence velocity and reduce sliding speed near joining edges. Also depending on velocity it's still possible we might not pass an endge leading into another segment since we are pulled back to the old one. */ if (obj->type == OBJ_PLAYER || obj->type == OBJ_ROBOT) while (object_intersects_wall(obj) && bumpcount++ < 64) { vms_vector center,bump_vec; //bump player a little towards center of segment to unstick compute_segment_center(¢er,&Segments[iseg]); vm_vec_normalized_dir_quick(&bump_vec,¢er,&obj->pos); vm_vec_scale_add2(&obj->pos,&bump_vec,F0_1); } //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; old_sim_time = sim_time; 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 { attempted_dist = vm_vec_mag(&frame_vec); 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 total_d,moved_d; 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) 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) ) { int forcefield_bounce; //bounce off a forcefield Assert(cheats.bouncyfire || !(obj->mtype.phys_info.flags & PF_STICK && obj->mtype.phys_info.flags & PF_BOUNCE)); //can't be bounce and stick forcefield_bounce = (TmapInfo[Segments[WallHitSeg].sides[WallHitSide].tmap_num].flags & TMI_FORCE_FIELD); if (!forcefield_bounce && (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 int check_vel=0; wall_part = vm_vec_dot(&hit_info.hit_wallnorm,&obj->mtype.phys_info.velocity); if (forcefield_bounce || (obj->mtype.phys_info.flags & PF_BOUNCE)) { //bounce off wall wall_part *= 2; //Subtract out wall part twice to achieve bounce if (forcefield_bounce) { check_vel = 1; //check for max velocity if (obj->type == OBJ_PLAYER) wall_part *= 2; //player bounce twice as much } if ( obj->mtype.phys_info.flags & PF_BOUNCES_TWICE) { Assert(obj->mtype.phys_info.flags & PF_BOUNCE); if (obj->mtype.phys_info.flags & PF_BOUNCED_ONCE) obj->mtype.phys_info.flags &= ~(PF_BOUNCE+PF_BOUNCED_ONCE+PF_BOUNCES_TWICE); else obj->mtype.phys_info.flags |= PF_BOUNCED_ONCE; } bounced = 1; //this object bounced } vm_vec_scale_add2(&obj->mtype.phys_info.velocity,&hit_info.hit_wallnorm,-wall_part); if (check_vel) { fix vel = vm_vec_mag_quick(&obj->mtype.phys_info.velocity); if (vel > MAX_OBJECT_VEL) vm_vec_scale(&obj->mtype.phys_info.velocity,fixdiv(MAX_OBJECT_VEL,vel)); } if (bounced && obj->type == OBJ_WEAPON) vm_vector_2_matrix(&obj->orient,&obj->mtype.phys_info.velocity,&obj->orient.uvec,NULL); 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; #ifndef NDEBUG Total_retries += count-1; Total_sims++; #endif } } // As sim_time may not base on FrameTime, scale actual object position to get accurate movement if (PhysTime/FrameTime > 0) { obj->pos.x = start_pos.x + ((obj->pos.x - start_pos.x) / ((float)PhysTime/FrameTime)); obj->pos.y = start_pos.y + ((obj->pos.y - start_pos.y) / ((float)PhysTime/FrameTime)); obj->pos.z = start_pos.z + ((obj->pos.z - start_pos.z) / ((float)PhysTime/FrameTime)); //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 && !bounced && ((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)); } //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]; if (orig_segnum==-1) Error("orig_segnum == -1 in physics"); 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 } //Applies an instantaneous force on an object, resulting in an instantaneous //change in velocity. void phys_apply_force(object *obj,vms_vector *force_vec) { // Put in by MK on 2/13/96 for force getting applied to Omega blobs, which have 0 mass, // in collision with crazy reactor robot thing on d2levf-s. if (obj->mtype.phys_info.mass == 0) return; if (obj->movement_type != MT_PHYSICS) return; //Add in acceleration due to force vm_vec_scale_add2(&obj->mtype.phys_info.velocity,force_vec,fixdiv(f1_0,obj->mtype.phys_info.mass)); } // ---------------------------------------------------------------- // Do *dest = *delta unless: // *delta is pretty small // and they are of different signs. void physics_set_rotvel_and_saturate(fix *dest, fix delta) { if ((delta ^ *dest) < 0) { if (abs(delta) < F1_0/8) { *dest = delta/4; } else *dest = delta; } else { *dest = delta; } } // ------------------------------------------------------------------------------------------------------ // Note: This is the old ai_turn_towards_vector code. // phys_apply_rot used to call ai_turn_towards_vector until I fixed it, which broke phys_apply_rot. void physics_turn_towards_vector(vms_vector *goal_vector, object *obj, fix rate) { vms_angvec dest_angles, cur_angles; fix delta_p, delta_h; vms_vector *rotvel_ptr = &obj->mtype.phys_info.rotvel; // Make this object turn towards the goal_vector. Changes orientation, doesn't change direction of movement. // If no one moves, will be facing goal_vector in 1 second. // Detect null vector. if ((goal_vector->x == 0) && (goal_vector->y == 0) && (goal_vector->z == 0)) return; // Make morph objects turn more slowly. if (obj->control_type == CT_MORPH) rate *= 2; vm_extract_angles_vector(&dest_angles, goal_vector); vm_extract_angles_vector(&cur_angles, &obj->orient.fvec); delta_p = (dest_angles.p - cur_angles.p); delta_h = (dest_angles.h - cur_angles.h); if (delta_p > F1_0/2) delta_p = dest_angles.p - cur_angles.p - F1_0; if (delta_p < -F1_0/2) delta_p = dest_angles.p - cur_angles.p + F1_0; if (delta_h > F1_0/2) delta_h = dest_angles.h - cur_angles.h - F1_0; if (delta_h < -F1_0/2) delta_h = dest_angles.h - cur_angles.h + F1_0; delta_p = fixdiv(delta_p, rate); delta_h = fixdiv(delta_h, rate); if (abs(delta_p) < F1_0/16) delta_p *= 4; if (abs(delta_h) < F1_0/16) delta_h *= 4; #ifdef WORDS_NEED_ALIGNMENT if ((delta_p ^ rotvel_ptr->x) < 0) { if (abs(delta_p) < F1_0/8) rotvel_ptr->x = delta_p/4; else rotvel_ptr->x = delta_p; } else rotvel_ptr->x = delta_p; if ((delta_h ^ rotvel_ptr->y) < 0) { if (abs(delta_h) < F1_0/8) rotvel_ptr->y = delta_h/4; else rotvel_ptr->y = delta_h; } else rotvel_ptr->y = delta_h; #else physics_set_rotvel_and_saturate(&rotvel_ptr->x, delta_p); physics_set_rotvel_and_saturate(&rotvel_ptr->y, delta_h); #endif rotvel_ptr->z = 0; } // ----------------------------------------------------------------------------- // 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; } } } 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); } //this routine will set the thrust for an object to a value that will //(hopefully) maintain the object's current velocity void set_thrust_from_velocity(object *obj) { fix k; Assert(obj->movement_type == MT_PHYSICS); k = fixmuldiv(obj->mtype.phys_info.mass,obj->mtype.phys_info.drag,(f1_0-obj->mtype.phys_info.drag)); vm_vec_copy_scale(&obj->mtype.phys_info.thrust,&obj->mtype.phys_info.velocity,k); }