- /* 
-  * Portions of this file are copyright Rebirth contributors and licensed as 
-  * described in COPYING.txt. 
-  * Portions of this file are copyright Parallax Software and licensed 
-  * according to the Parallax license below. 
-  * See COPYING.txt for license details. 
-   
- 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 <stdio.h> 
- #include <stdlib.h> 
-   
- #include "joy.h" 
- #include "dxxerror.h" 
-   
- #include "inferno.h" 
- #include "segment.h" 
- #include "object.h" 
- #include "physics.h" 
- #include "robot.h" 
- #include "key.h" 
- #include "game.h" 
- #include "collide.h" 
- #include "fvi.h" 
- #include "newdemo.h" 
- #include "gameseg.h" 
- #include "timer.h" 
- #include "ai.h" 
- #include "wall.h" 
- #include "laser.h" 
- #if defined(DXX_BUILD_DESCENT_II) 
- #include "bm.h" 
- #include "player.h" 
- #define MAX_OBJECT_VEL  i2f(100) 
- #endif 
-   
- #include "dxxsconf.h" 
- #include "compiler-range_for.h" 
-   
- //Global variables for physics system 
-   
- #define ROLL_RATE       0x2000 
- #define DAMP_ANG        0x400 //min angle to bank 
- #define TURNROLL_SCALE  (0x4ec4/2) 
-   
- //check point against each side of segment. return bitmask, where bit 
- //set means behind that side 
-   
- //make sure matrix is orthogonal 
- void check_and_fix_matrix(vms_matrix &m) 
- { 
-         m = vm_vector_2_matrix(m.fvec,&m.uvec,nullptr); 
- } 
-   
-   
- static void do_physics_align_object(object_base &obj) 
- { 
-         vms_vector desired_upvec; 
-         fixang delta_ang,roll_ang; 
-         fix largest_d = INT32_MIN; 
-         const shared_side *best_side = nullptr; 
-         // bank player according to segment orientation 
-   
-         //find side of segment that player is most alligned with 
-   
-         range_for (auto &i, vcsegptr(obj.segnum)->shared_segment::sides) 
-         { 
-                 const auto d = vm_vec_dot(i.normals[0], obj.orient.uvec); 
-   
-                 if (largest_d < d) 
-                 { 
-                         largest_d = d; 
-                         best_side = &i; 
-                 } 
-         } 
-   
-         // new player leveling code: use normal of side closest to our up vec 
-         if (!best_side) 
-                 return; 
-         if (!get_side_is_quad(*best_side)) 
-         { 
-                 desired_upvec = vm_vec_avg(best_side->normals[0], best_side->normals[1]); 
-                 vm_vec_normalize(desired_upvec); 
-         } 
-         else 
-                 desired_upvec = best_side->normals[0]; 
-   
-         if (labs(vm_vec_dot(desired_upvec, obj.orient.fvec)) < f1_0 / 2) 
-         { 
-                 vms_angvec tangles; 
-                  
-                 const auto temp_matrix = vm_vector_2_matrix(obj.orient.fvec, &desired_upvec, nullptr); 
-   
-                 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) { 
-                         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; 
-                         const auto &&rotmat = vm_angles_2_matrix(tangles); 
-                         obj.orient = vm_matrix_x_matrix(obj.orient, rotmat); 
-                 } 
-         } 
-   
- } 
-   
- static void set_object_turnroll(object_base &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; 
-         } 
-   
- } 
-   
-   
- #define MAX_IGNORE_OBJS 100 
-   
- #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 
- namespace dsx { 
- static void do_physics_sim_rot(object_base &obj) 
- { 
-         vms_angvec      tangles; 
-         //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; 
-                 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) 
-                 { 
-                         const auto accel = vm_vec_copy_scale(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 defined(DXX_BUILD_DESCENT_II) 
-                         if (! (obj.mtype.phys_info.flags & PF_FREE_SPINNING)) 
- #endif 
-                 { 
-                         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) 
-         { 
-                 tangles.p = tangles.h = 0; 
-                 tangles.b = -obj.mtype.phys_info.turnroll; 
-                 const auto &&rotmat = vm_angles_2_matrix(tangles); 
-                 obj.orient = vm_matrix_x_matrix(obj.orient, rotmat); 
-         } 
-   
-         const auto frametime = FrameTime; 
-         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); 
-   
-         obj.orient = vm_matrix_x_matrix(obj.orient, vm_angles_2_matrix(tangles)); 
-   
-         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) 
-         { 
-                 tangles.p = tangles.h = 0; 
-                 tangles.b = obj.mtype.phys_info.turnroll; 
-                 obj.orient = vm_matrix_x_matrix(obj.orient, vm_angles_2_matrix(tangles)); 
-         } 
-   
-         check_and_fix_matrix(obj.orient); 
- } 
- } 
-   
- // On joining edges fvi tends to get inaccurate as hell. Approach is to check if the object interects with the wall and if so, move away from it. 
- static void fix_illegal_wall_intersection(const vmobjptridx_t obj) 
- { 
-         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state(); 
-         auto &Objects = LevelUniqueObjectState.Objects; 
-         auto &Vertices = LevelSharedVertexState.get_vertices(); 
-         auto &vmobjptr = Objects.vmptr; 
-         if (!(obj->type == OBJ_PLAYER || obj->type == OBJ_ROBOT)) 
-                 return; 
-   
-         auto &vcvertptr = Vertices.vcptr; 
-         const auto &&hresult = sphere_intersects_wall(vcvertptr, obj->pos, vcsegptridx(obj->segnum), obj->size); 
-         if (hresult.seg) 
-         { 
-                 vm_vec_scale_add2(obj->pos, hresult.seg->sides[hresult.side].normals[0], FrameTime*10); 
-                 update_object_seg(vmobjptr, LevelSharedSegmentState, LevelUniqueSegmentState, obj); 
-         } 
- } 
-   
- namespace { 
-   
- class ignore_objects_array_t 
- { 
-         using array_t = std::array<vcobjidx_t, MAX_IGNORE_OBJS>; 
-         array_t::iterator e; 
-         union { 
-                 array_t a; 
-         }; 
- public: 
-         /* The iterator should be initialized in a 
-          * member-initialization-list.  However, clang complains that the 
-          * union is uninitialized during the member-initialization-list, but 
-          * accepts the still-uninitialized union member once the constructor 
-          * body starts.  Assign the iterator in the body to silence this 
-          * useless clang warning. 
-          * 
-          * Known bad: 
-          *      clang-5 
-          *      clang-7 
-          */ 
-         ignore_objects_array_t() 
-         { 
-                 e = a.begin(); 
-         } 
-         bool push_back(const vcobjidx_t o) 
-         { 
-                 if (unlikely(e == a.end())) 
-                         return false; 
-                 std::uninitialized_fill_n(e, 1, o); 
-                 ++e; 
-                 return true; 
-         } 
-         operator std::pair<const vcobjidx_t *, const vcobjidx_t *>() const 
-         { 
-                 return {a.begin(), e}; 
-         } 
- }; 
-   
- } 
-   
- //      ----------------------------------------------------------------------------------------------------------- 
- //Simulate a physics object for this frame 
- namespace dsx { 
- window_event_result do_physics_sim(const vmobjptridx_t obj, const vms_vector &obj_previous_position, phys_visited_seglist *const phys_segs) 
- { 
-         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state(); 
-         auto &Objects = LevelUniqueObjectState.Objects; 
-         auto &Vertices = LevelSharedVertexState.get_vertices(); 
-         auto &vcobjptr = Objects.vcptr; 
-         auto &vmobjptr = Objects.vmptr; 
-         ignore_objects_array_t ignore_obj_list; 
-         int try_again; 
-         int fate=0; 
-         vms_vector ipos;                //position after this frame 
-         segnum_t WallHitSeg; 
-         int WallHitSide; 
-         fvi_info hit_info; 
-         fvi_query fq; 
-         vms_vector save_pos; 
-         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; 
-         auto orig_segnum = obj->segnum; 
-         int bounced=0; 
-         bool Player_ScrapeFrame=false; 
-         auto result = window_event_result::handled; 
- #if defined(DXX_BUILD_DESCENT_II) 
-         auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo; 
- #endif 
-   
-         Assert(obj->movement_type == MT_PHYSICS); 
-   
- #ifndef NDEBUG 
-         if (Dont_move_ai_objects) 
-                 if (obj->control_type == CT_AI) 
-                         return window_event_result::ignored; 
- #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 window_event_result::ignored; 
-   
-         sim_time = FrameTime; 
-   
-         start_pos = obj->pos; 
-   
-                 //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 
-         if ((drag = obj->mtype.phys_info.drag) != 0) { 
-   
-                 int count; 
-                 fix r,k,have_accel; 
-   
-                 count = FrameTime / FT; 
-                 r = FrameTime % FT; 
-                 k = fixdiv(r,FT); 
-   
-                 if (obj->mtype.phys_info.flags & PF_USES_THRUST) { 
-   
-                         const auto accel = vm_vec_copy_scale(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); 
-                 } 
-         } 
-   
-         int count = 0; 
-         auto &vcvertptr = Vertices.vcptr; 
-         auto &Walls = LevelUniqueWallSubsystemState.Walls; 
-         auto &vcwallptr = Walls.vcptr; 
-         do { 
-                 try_again = 0; 
-   
-                 //Move the object 
-                 const auto frame_vec = vm_vec_copy_scale(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. 
-   
-                 const auto new_pos = vm_vec_add(obj->pos,frame_vec); 
-                 fq.p0                                           = &obj->pos; 
-                 fq.startseg                             = obj->segnum; 
-                 fq.p1                                           = &new_pos; 
-                 fq.rad                                  = obj->size; 
-                 fq.thisobjnum                   = obj; 
-                 fq.ignore_obj_list      = ignore_obj_list; 
-                 fq.flags                                        = FQ_CHECK_OBJS; 
-   
-                 if (obj->type == OBJ_WEAPON) 
-                         fq.flags |= FQ_TRANSPOINT; 
-   
-                 if (phys_segs) 
-                         fq.flags |= FQ_GET_SEGLIST; 
-   
-                 fate = find_vector_intersection(fq, hit_info); 
-                 //      Matt: Mike's hack. 
-                 if (fate == HIT_OBJECT) { 
-                         auto &objp = *vcobjptr(hit_info.hit_object); 
-   
-                         if ((objp.type == OBJ_WEAPON && is_proximity_bomb_or_player_smart_mine(get_weapon_id(objp))) || 
-                                 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 (phys_segs && !hit_info.seglist.empty()) 
-                 { 
-                         auto n_phys_segs = phys_segs->nsegs; 
-                         if (n_phys_segs && phys_segs->seglist[n_phys_segs - 1] == hit_info.seglist[0]) 
-                                 n_phys_segs--; 
-                         range_for (const auto &hs, hit_info.seglist) 
-                         { 
-                                 if (!(n_phys_segs < phys_segs->seglist.size() - 1)) 
-                                         break; 
-                                 phys_segs->seglist[n_phys_segs++] = hs; 
-                         } 
-                         phys_segs->nsegs = n_phys_segs; 
-                 } 
-   
-                 ipos = hit_info.hit_pnt; 
-                 auto iseg = hit_info.hit_seg; 
-                 WallHitSide = hit_info.hit_side; 
-                 WallHitSeg = hit_info.hit_side_seg; 
-   
-                 if (iseg==segment_none) {               //some sort of horrible error 
-                         if (obj->type == OBJ_WEAPON) 
-                                 obj->flags |= OF_SHOULD_BE_DEAD; 
-                         break; 
-                 } 
-   
-                 Assert(!((fate==HIT_WALL) && ((WallHitSeg == segment_none) || (WallHitSeg > Highest_segment_index)))); 
-   
-                 save_pos = obj->pos;                    //save the object's position 
-                 auto save_seg = obj->segnum; 
-   
-                 // update object's position and segment number 
-                 obj->pos = ipos; 
-   
-                 const auto &&obj_segp = Segments.vmptridx(iseg); 
-                 if ( iseg != obj->segnum ) 
-                         obj_relink(vmobjptr, Segments.vmptr, obj, obj_segp); 
-   
-                 //if start point not in segment, move object to center of segment 
-                 if (get_seg_masks(vcvertptr, obj->pos, Segments.vcptr(obj->segnum), 0).centermask != 0) 
-                 { 
-                         auto n = find_object_seg(LevelSharedSegmentState, LevelUniqueSegmentState, obj); 
-                         if (n == segment_none) 
-                         { 
-                                 //Int3(); 
-                                 if (obj->type == OBJ_PLAYER && (n = find_point_seg(LevelSharedSegmentState, LevelUniqueSegmentState, obj_previous_position, obj_segp)) != segment_none) 
-                                 { 
-                                         obj->pos = obj_previous_position; 
-                                         obj_relink(vmobjptr, Segments.vmptr, obj, n); 
-                                 } 
-                                 else { 
-                                         compute_segment_center(vcvertptr, obj->pos, obj_segp); 
-                                         obj->pos.x += obj; 
-                                 } 
-                                 if (obj->type == OBJ_WEAPON) 
-                                         obj->flags |= OF_SHOULD_BE_DEAD; 
-                         } 
-                         return window_event_result::ignored; 
-                 } 
-   
-                 //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(vmobjptr, Segments.vmptr, obj, Segments.vmptridx(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:          { 
-                                 fix hit_speed=0,wall_part=0; 
-   
-                                 // Find hit speed        
-   
-                                 const auto moved_v = vm_vec_sub(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) 
-                                         result = collide_object_with_wall( 
- #if defined(DXX_BUILD_DESCENT_II) 
-                                                 LevelSharedSegmentState.DestructibleLights, 
- #endif 
-                                                 obj, hit_speed, Segments.vmptridx(WallHitSeg), WallHitSide, hit_info.hit_pnt); 
-                                 /* 
-                                  * Due to the nature of this loop, it's possible that a local player may receive scrape damage multiple times in one frame. 
-                                  * Check if we received damage and do not apply more damage (nor produce damage sounds/flashes/bumps, etc) for the rest of the loop. 
-                                  * It's possible that other walls later in the loop would still be valid for scraping but due to the generalized outcome, this should be negligible (practical wall sliding is handled below). 
-                                  * NOTE: Remote players will return false and never receive damage. But since we handle only one object (remote or local) per loop, this is no problem.  
-                                  */ 
-                                 if (obj->type == OBJ_PLAYER && Player_ScrapeFrame == false) 
-                                         Player_ScrapeFrame = scrape_player_on_wall(obj, Segments.vmptridx(WallHitSeg), WallHitSide, hit_info.hit_pnt); 
-   
-                                 Assert( WallHitSeg != segment_none ); 
-                                 Assert( WallHitSide > -1 ); 
-   
-                                 if ( !(obj->flags&OF_SHOULD_BE_DEAD) )  { 
-                                         int forcefield_bounce;          //bounce off a forcefield 
-   
- #if defined(DXX_BUILD_DESCENT_II) 
-                                         if (!cheats.bouncyfire) 
- #endif 
-                                         Assert(!(obj->mtype.phys_info.flags & PF_STICK && obj->mtype.phys_info.flags & PF_BOUNCE));     //can't be bounce and stick 
-   
- #if defined(DXX_BUILD_DESCENT_I) 
-                                         /* 
-                                          * Force fields are not supported in Descent 1.  Use 
-                                          * this as a placeholder to make the code match the 
-                                          * force field handling in Descent 2. 
-                                          */ 
-                                         forcefield_bounce = 0; 
- #elif defined(DXX_BUILD_DESCENT_II) 
-                                         forcefield_bounce = (TmapInfo[Segments[WallHitSeg].unique_segment::sides[WallHitSide].tmap_num].flags & TMI_FORCE_FIELD); 
-                                         int check_vel=0; 
- #endif 
-   
-                                         if (!forcefield_bounce && (obj->mtype.phys_info.flags & PF_STICK)) {            //stop moving 
-   
-                                                 LevelUniqueStuckObjectState.add_stuck_object(vcwallptr, obj, Segments.vmptr(WallHitSeg), WallHitSide); 
-   
-                                                 vm_vec_zero(obj->mtype.phys_info.velocity); 
-                                                 obj_stopped = 1; 
-                                                 try_again = 0; 
-                                         } 
-                                         else {                                  // Slide object along wall 
-   
-                                                 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 (forcefield_bounce || (obj->mtype.phys_info.flags & PF_BOUNCE)) {            //bounce off wall 
-                                                         wall_part *= 2; //Subtract out wall part twice to achieve bounce 
-   
- #if defined(DXX_BUILD_DESCENT_II) 
-                                                         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 
- #endif 
-                                                 } 
-   
-                                                 vm_vec_scale_add2(obj->mtype.phys_info.velocity,hit_info.hit_wallnorm,-wall_part); 
-   
- #if defined(DXX_BUILD_DESCENT_II) 
-                                                 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,nullptr); 
- #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 != object_none); 
-                                 //      Calculcate the hit point between the two objects. 
-                                 { 
-                                         fix                     size0, size1; 
-                                         const auto &&hit = obj.absolute_sibling(hit_info.hit_object); 
-                                         const auto &ppos0 = hit->pos; 
-                                         const auto &ppos1 = obj->pos; 
-                                         size0 = hit->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); 
-                                         auto pos_hit = vm_vec_sub(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, hit, 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) 
-                                                 if (ignore_obj_list.push_back(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) { 
-                         obj->ctype.ai_info.ail.retry_count = count-1; 
- #ifndef NDEBUG 
-                         Total_retries += count-1; 
-                         Total_sims++; 
- #endif 
-                 } 
-         } 
-   
-         // 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)) 
-                 ) 
-         {        
-                 const auto moved_vec = vm_vec_sub(obj->pos,start_pos); 
-                 vm_vec_copy_scale(obj->mtype.phys_info.velocity,moved_vec,fixdiv(f1_0,FrameTime)); 
-         } 
-   
-         fix_illegal_wall_intersection(obj); 
-   
-         //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) ) { 
-   
-                 const cscusegment orig_segp = vcsegptr(orig_segnum); 
-                 const auto &&sidenum = find_connect_side(vcsegptridx(obj->segnum), orig_segp); 
-                 if (sidenum != side_none) 
-                 { 
-   
-                         if (! (WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, orig_segp, sidenum) & WID_FLY_FLAG)) 
-                         { 
-                                 fix dist; 
-   
-                                 //bump object back 
-   
-                                 auto &s = orig_segp.s.sides[sidenum]; 
-   
-                                 const auto v = create_abs_vertex_lists(orig_segp, s, sidenum); 
-                                 const auto &vertex_list = v.second; 
-   
-                                 //let's pretend this wall is not triangulated 
-                                 const auto b = begin(vertex_list); 
-                                 const auto vertnum = *std::min_element(b, std::next(b, 4)); 
-   
-                                 dist = vm_dist_to_plane(start_pos, s.normals[0], vcvertptr(vertnum)); 
-                                 vm_vec_scale_add(obj->pos, start_pos, s.normals[0], obj->size-dist); 
-                                 update_object_seg(vmobjptr, LevelSharedSegmentState, LevelUniqueSegmentState, 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(vcvertptr, obj->pos, vcsegptr(obj->segnum), 0).centermask != 0) 
-         { 
-                 if (find_object_seg(LevelSharedSegmentState, LevelUniqueSegmentState, obj) == segment_none) 
-                 { 
-                         segnum_t n; 
-   
-                         //Int3(); 
-                         const auto &&obj_segp = Segments.vmptridx(obj->segnum); 
-                         if (obj->type == OBJ_PLAYER && (n = find_point_seg(LevelSharedSegmentState, LevelUniqueSegmentState, obj_previous_position, obj_segp)) != segment_none) 
-                         { 
-                                 obj->pos = obj_previous_position; 
-                                 obj_relink(vmobjptr, Segments.vmptr, obj, Segments.vmptridx(n)); 
-                         } 
-                         else { 
-                                 compute_segment_center(vcvertptr, obj->pos, obj_segp); 
-                                 obj->pos.x += obj; 
-                         } 
-                         if (obj->type == OBJ_WEAPON) 
-                                 obj->flags |= OF_SHOULD_BE_DEAD; 
-                 } 
-         } 
-   
-         return result; 
- //--WE ALWYS WANT THIS IN, MATT AND MIKE DECISION ON 12/10/94, TWO MONTHS AFTER FINAL   #endif 
- } 
- } 
-   
- namespace dcx { 
-   
- //Applies an instantaneous force on an object, resulting in an instantaneous 
- //change in velocity. 
- void phys_apply_force(object_base &obj, const vms_vector &force_vec) 
- { 
-         if (obj.movement_type != MT_PHYSICS) 
-                 return; 
-   
-         //      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; 
-   
-         //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. 
- static 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; 
-         } 
- } 
-   
- static inline vms_angvec vm_extract_angles_vector(const vms_vector &v) 
- { 
-         vms_angvec a; 
-         return vm_extract_angles_vector(a, v), a; 
- } 
-   
- //      ------------------------------------------------------------------------------------------------------ 
- //      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(const vms_vector &goal_vector, object_base &obj, fix rate) 
- { 
-         fix                     delta_p, delta_h; 
-   
-         // 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; 
-   
-         const auto dest_angles = vm_extract_angles_vector(goal_vector); 
-         const auto cur_angles = vm_extract_angles_vector(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; 
-   
-         auto &rotvel_ptr = obj.mtype.phys_info.rotvel; 
-         physics_set_rotvel_and_saturate(rotvel_ptr.x, delta_p); 
-         physics_set_rotvel_and_saturate(rotvel_ptr.y, delta_h); 
-         rotvel_ptr.z = 0; 
- } 
-   
- } 
-   
- namespace dsx { 
-   
- //      ----------------------------------------------------------------------------- 
- //      Applies an instantaneous whack on an object, resulting in an instantaneous 
- //      change in orientation. 
- void phys_apply_rot(object &obj, const vms_vector &force_vec) 
- { 
-         fix     rate; 
-   
-         if (obj.movement_type != MT_PHYSICS) 
-                 return; 
-   
- #if defined(DXX_BUILD_DESCENT_II) 
-         auto &Robot_info = LevelSharedRobotInfoState.Robot_info; 
- #endif 
-         auto vecmag = vm_vec_mag(force_vec); 
-         if (vecmag < F1_0/32) 
-                 rate = 4*F1_0; 
-         else if (vecmag < obj.mtype.phys_info.mass >> 11) 
-                 rate = 4*F1_0; 
-         else { 
-                 rate = fixdiv(obj.mtype.phys_info.mass, vecmag / 8); 
-                 if (obj.type == OBJ_ROBOT) { 
-                         if (rate < F1_0/4) 
-                                 rate = F1_0/4; 
- #if defined(DXX_BUILD_DESCENT_I) 
-                         obj.ctype.ai_info.SKIP_AI_COUNT = 2; 
- #elif defined(DXX_BUILD_DESCENT_II) 
-                         //      Changed by mk, 10/24/95, claw guys should not slow down when attacking! 
-                         if (!Robot_info[get_robot_id(obj)].thief && !Robot_info[get_robot_id(obj)].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; 
-                                 } 
-                         } 
- #endif 
-                 } 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); 
- } 
-   
- } 
-   
- namespace dcx { 
-   
- //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_base &obj) 
- { 
-         Assert(obj.movement_type == MT_PHYSICS); 
-         auto &phys_info = obj.mtype.phys_info; 
-         vm_vec_copy_scale(phys_info.thrust, phys_info.velocity, 
-                 fixmuldiv(phys_info.mass, phys_info.drag, F1_0 - phys_info.drag) 
-         ); 
- } 
-   
- } 
-