Advertisement
Guest User

code/playerman/playercontrol.cpp x-style-mouse

a guest
Apr 5th, 2015
421
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 68.87 KB | None | 0 0
  1. /*
  2.  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
  3.  *
  4.  * All source code herein is the property of Volition, Inc. You may not sell
  5.  * or otherwise commercially exploit the source or things you created based on the
  6.  * source.
  7.  *
  8. */
  9.  
  10.  
  11. #include "playerman/player.h"
  12. #include "io/joy.h"
  13. #include "io/joy_ff.h"
  14. #include "io/mouse.h"
  15. #include "io/timer.h"
  16. #include "external_dll/trackirpublic.h"
  17. #include "object/object.h"
  18. #include "hud/hud.h"
  19. #include "hud/hudtargetbox.h"
  20. #include "ship/ship.h"
  21. #include "ship/shipfx.h"
  22. #include "freespace2/freespace.h"
  23. #include "gamesnd/gamesnd.h"
  24. #include "gamesequence/gamesequence.h"
  25. #include "mission/missionmessage.h"
  26. #include "globalincs/linklist.h"
  27. #include "mission/missiongoals.h"
  28. #include "hud/hudsquadmsg.h"
  29. #include "hud/hudmessage.h"
  30. #include "observer/observer.h"
  31. #include "weapon/weapon.h"
  32. #include "object/objectdock.h"
  33. #include "camera/camera.h"
  34. #include "network/multiutil.h"
  35. #include "network/multi_obj.h"
  36. #include "parse/parselo.h"
  37. #include "debugconsole/console.h"
  38.  
  39. #ifndef NDEBUG
  40. #include "io/key.h"
  41. #endif
  42.  
  43. #include "autopilot/autopilot.h"
  44.  
  45.  
  46. ////////////////////////////////////////////////////////////
  47. // Global object and other interesting player type things
  48. ////////////////////////////////////////////////////////////
  49. player  Players[MAX_PLAYERS];
  50.  
  51. int     Player_num;
  52. player  *Player = NULL;
  53.  
  54. // Goober5000
  55. int     Player_use_ai = 0;
  56.  
  57. int     lua_game_control = 0;
  58.  
  59. physics_info Descent_physics;           // used when we want to control the player like the descent ship
  60.  
  61. angles chase_slew_angles;
  62. int view_centering = 0;
  63.  
  64. int toggle_glide = 0;
  65. int press_glide = 0;
  66.  
  67. ////////////////////////////////////////////////////////////
  68. // Module data
  69. ////////////////////////////////////////////////////////////
  70. static int Player_all_alone_msg_inited=0;   // flag used for initializing a player-specific voice msg
  71.  
  72. #ifndef NDEBUG
  73.     int Show_killer_weapon = 0;
  74.     DCF_BOOL( show_killer_weapon, Show_killer_weapon )
  75. #endif
  76.  
  77. void playercontrol_read_stick(int *axis, float frame_time);
  78. void player_set_padlock_state();
  79.  
  80. /**
  81.  * @brief Slew angles chase towards a value like they're on a spring.
  82.  * @details When furthest away, move fastest. Minimum speed set so that doesn't take too long. When gets close, clamps to the value.
  83.  */
  84. void chase_angles_to_value(angles *ap, angles *bp, int scale)
  85. {
  86.     float sk;
  87.     angles delta;
  88.  
  89.     //  Make sure we actually need to do all this math.
  90.     if ((ap->p == bp->p) && (ap->h == bp->h))
  91.         return;
  92.  
  93.     sk = 1.0f - scale*flRealframetime;
  94.  
  95.     CLAMP(sk, 0.0f, 1.0f);
  96.  
  97.     delta.p = ap->p - bp->p;
  98.     delta.h = ap->h - bp->h;
  99.  
  100.     ap->p = ap->p - delta.p * (1.0f - sk);
  101.     ap->h = ap->h - delta.h * (1.0f - sk);
  102.  
  103.     //  If we're very close, put ourselves at goal.
  104.     if ((fl_abs(delta.p) < 0.005f) && (fl_abs(delta.h) < 0.005f)) {
  105.         ap->p = bp->p;
  106.         ap->h = bp->h;
  107.     }
  108. }
  109.  
  110. angles  Viewer_slew_angles_delta;
  111. angles  Viewer_external_angles_delta;
  112.  
  113. void view_modify(angles *ma, angles *da, float max_p, float max_h, float frame_time)
  114. {
  115.     int axis[NUM_JOY_AXIS_ACTIONS];
  116.     float   t = 0;
  117.     float   u = 0;
  118.     vec3d trans = ZERO_VECTOR;
  119.  
  120.     if ( Viewer_mode & VM_EXTERNAL) {
  121.         if (! (Viewer_mode & VM_EXTERNAL_CAMERA_LOCKED) ) {
  122.             t = t + (check_control_timef(YAW_LEFT) - check_control_timef(YAW_RIGHT));
  123.             u = u + (check_control_timef(PITCH_BACK) - check_control_timef(PITCH_FORWARD));
  124.         } else {
  125.             return;
  126.         }
  127.     } else if ( gTirDll_TrackIR.Enabled( ) ) {
  128.         gTirDll_TrackIR.Query();
  129.         ma->h = -PI2*(gTirDll_TrackIR.GetYaw());
  130.         ma->p = PI2*(gTirDll_TrackIR.GetPitch());
  131.  
  132.         trans.xyz.x = -0.4f*gTirDll_TrackIR.GetX();
  133.         trans.xyz.y = 0.4f*gTirDll_TrackIR.GetY();
  134.         trans.xyz.z = -gTirDll_TrackIR.GetZ();
  135.  
  136.         if(trans.xyz.z < 0)
  137.             trans.xyz.z = 0.0f;
  138.  
  139.         vm_vec_unrotate(&leaning_position,&trans,&Eye_matrix);
  140.     } else {
  141.         // View slewing commands commented out until we can safely add more commands in the pilot code.
  142.     }
  143.  
  144.     if (t != 0.0f)
  145.         da->h += t;
  146.     else
  147.         da->h = 0.0f;
  148.  
  149.     if (u != 0.0f)
  150.         da->p += u;
  151.     else
  152.         da->p = 0.0f;
  153.            
  154.     da->b = 0.0f;
  155.  
  156.     playercontrol_read_stick(axis, frame_time);
  157.  
  158.     if (( Viewer_mode & VM_EXTERNAL ) && !(Viewer_mode & VM_EXTERNAL_CAMERA_LOCKED)) {
  159.         // check the heading on the x and y axes
  160.         da->h -= f2fl( axis[0] );
  161.         da->p -= f2fl( axis[1] );
  162.     }
  163.  
  164.     if (da->h > 1.0f)
  165.         da->h = 1.0f;
  166.     else if (da->h < -1.0f)
  167.         da->h = -1.0f;
  168.  
  169.     if (da->p > 1.0f)
  170.         da->p = 1.0f;
  171.     else if (da->p < -1.0f)
  172.         da->p = -1.0f;
  173.  
  174.     if ( (Game_time_compression >= F1_0) && !(Viewer_mode & VM_EXTERNAL) )
  175.     {
  176.         ma->p += 2*da->p * flFrametime;
  177.         ma->b += 2*da->b * flFrametime;
  178.         ma->h += 2*da->h * flFrametime;
  179.     }
  180.     else
  181.     {
  182.         //If time compression is less than normal, still move camera at same speed
  183.         //This gives a cool matrix effect
  184.         ma->p += da->p * flRealframetime;
  185.         ma->b += da->b * flRealframetime;
  186.         ma->h += da->h * flRealframetime;
  187.     }
  188.  
  189.     if (ma->p > max_p)
  190.         ma->p = max_p;
  191.     else if (ma->p < -max_p)
  192.         ma->p = -max_p;
  193.  
  194.     if (ma->h > max_h)
  195.         ma->h = max_h;
  196.     else if (ma->h < -max_h)
  197.         ma->h = -max_h;
  198. }
  199.  
  200. void do_view_track_target(float frame_time)
  201. {
  202.     vec3d view_vector;
  203.     vec3d targetpos_rotated;
  204.     vec3d playerpos_rotated;
  205.     vec3d forwardvec_rotated;
  206.     vec3d target_pos;
  207.     angles view_angles;
  208.     angles forward_angles;
  209.  
  210.     if ((Player_ai->target_objnum == -1) || (Viewer_mode & VM_OTHER_SHIP)) {
  211.      // If the object isn't targeted or we're viewing from the target's perspective, center the view and turn off target padlock
  212.      // because the target won't be at the angle we've calculated from the player's perspective.
  213.         Viewer_mode ^= VM_TRACK;
  214.         chase_slew_angles.p = 0.0f;
  215.         chase_slew_angles.h = 0.0f;
  216.         return;
  217.     }
  218.  
  219.     object * targetp = &Objects[Player_ai->target_objnum];
  220.  
  221.     // check to see if there is even a current target. if not, switch off the
  222.     // target padlock tracking flag, make the camera slew to the center,
  223.     // and exit the procedure
  224.     if ( targetp == &obj_used_list ) {
  225.         Viewer_mode ^= VM_TRACK;
  226.         chase_slew_angles.p = 0.0f;
  227.         chase_slew_angles.h = 0.0f;
  228.         return;
  229.     }
  230.  
  231.     // look at a subsystem if there is one.
  232.     if ( Player_ai->targeted_subsys != NULL ) {
  233.         get_subsystem_world_pos(targetp, Player_ai->targeted_subsys, &target_pos);
  234.  
  235.     } else {
  236.         target_pos = targetp->pos;
  237.     }
  238.  
  239.     vm_vec_rotate(&targetpos_rotated, &target_pos, &Player_obj->orient);
  240.     vm_vec_rotate(&playerpos_rotated, &Player_obj->pos, &Player_obj->orient);
  241.     vm_vec_rotate(&forwardvec_rotated, &Player_obj->orient.vec.fvec, &Player_obj->orient);
  242.  
  243.     vm_vec_normalized_dir(&view_vector,&targetpos_rotated,&playerpos_rotated);
  244.     vm_extract_angles_vector(&view_angles,&view_vector);
  245.     vm_extract_angles_vector(&forward_angles,&forwardvec_rotated);
  246.     chase_slew_angles.h = forward_angles.h - view_angles.h;
  247.     chase_slew_angles.p = -(forward_angles.p - view_angles.p);
  248.  
  249.     // the gimbal limits of the player's virtual neck.
  250.     // These nested ifs prevent the player from looking up and
  251.     // down beyond 90 degree angles.
  252.     if (chase_slew_angles.p > PI_2)
  253.         chase_slew_angles.p = PI_2;
  254.     else if (chase_slew_angles.p < -PI_2)
  255.         chase_slew_angles.p = -PI_2;
  256.  
  257.     // prevents the player from looking completely behind himself; just over his shoulder
  258.     if (chase_slew_angles.h > PI2/3)
  259.         chase_slew_angles.h = PI2/3;
  260.     else if (chase_slew_angles.h < -PI2/3)
  261.         chase_slew_angles.h = -PI2/3;
  262. }
  263.  
  264.  
  265. /**
  266.  * When PAD0 is pressed, keypad controls viewer direction slewing.
  267.  */
  268. void do_view_slew(float frame_time)
  269. {
  270.     view_modify(&chase_slew_angles, &Viewer_slew_angles_delta, PI_2, PI2/3, frame_time);
  271. }
  272.  
  273. void do_view_chase(float frame_time)
  274. {
  275.     float t;
  276.  
  277.     //  Process centering key.
  278.     if (check_control_timef(VIEW_CENTER)) {
  279.         Viewer_chase_info.distance = 0.0f;
  280.     }
  281.    
  282.     t = check_control_timef(VIEW_DIST_INCREASE) - check_control_timef(VIEW_DIST_DECREASE);
  283.     Viewer_chase_info.distance += t*4;
  284.     if (Viewer_chase_info.distance < 0.0f)
  285.         Viewer_chase_info.distance = 0.0f;
  286. }
  287.  
  288. float camera_zoom_scale = 1.0f;
  289.  
  290. DCF(camera_speed, "Sets the camera zoom scale")
  291. {
  292.     if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
  293.         dc_printf("Camera zoom scale is %f\n", camera_zoom_scale);
  294.         return;
  295.     }
  296.  
  297.     dc_stuff_float(&camera_zoom_scale);
  298.  
  299.     dc_printf("Camera zoom scale set to %f\n", camera_zoom_scale);
  300. }
  301.  
  302. void do_view_external(float frame_time)
  303. {
  304.     float   t;
  305.  
  306.     view_modify(&Viewer_external_info.angles, &Viewer_external_angles_delta, PI2, PI2, frame_time);
  307.  
  308.     //  Process centering key.
  309.     if (check_control_timef(VIEW_CENTER)) {
  310.         Viewer_external_info.angles.p = 0.0f;
  311.         Viewer_external_info.angles.h = 0.0f;
  312.         Viewer_external_info.distance = 0.0f;
  313.     }
  314.    
  315.     t = check_control_timef(VIEW_DIST_INCREASE) - check_control_timef(VIEW_DIST_DECREASE);
  316.     Viewer_external_info.distance += t*4*camera_zoom_scale;
  317.     if (Viewer_external_info.distance < 0.0f){
  318.         Viewer_external_info.distance = 0.0f;
  319.     }
  320.  
  321.     //  Do over-the-top correction.
  322.  
  323.     if (Viewer_external_info.angles.p > PI)
  324.         Viewer_external_info.angles.p = -PI2 + Viewer_external_info.angles.p;
  325.     else if (Viewer_external_info.angles.p < -PI)
  326.         Viewer_external_info.angles.p = PI2 + Viewer_external_info.angles.p;
  327.  
  328.     if (Viewer_external_info.angles.h > PI)
  329.         Viewer_external_info.angles.h = -PI2 + Viewer_external_info.angles.h;
  330.     else if (Viewer_external_info.angles.h < -PI)
  331.         Viewer_external_info.angles.h = PI2 + Viewer_external_info.angles.h;
  332. }
  333.  
  334. /**
  335.  * Separate out the reading of thrust keys, so we can call this from external view as well as from normal view
  336.  */
  337. void do_thrust_keys(control_info *ci)
  338. {
  339.     ci->forward = check_control_timef(FORWARD_THRUST) - check_control_timef(REVERSE_THRUST);
  340.     ci->sideways = (check_control_timef(RIGHT_SLIDE_THRUST) - check_control_timef(LEFT_SLIDE_THRUST));//for slideing-Bobboau
  341.     ci->vertical = (check_control_timef(UP_SLIDE_THRUST) - check_control_timef(DOWN_SLIDE_THRUST));//for slideing-Bobboau
  342. }
  343.  
  344. /**
  345.  * Called by single and multiplayer modes to reset information inside of control info structure
  346.  */
  347. void player_control_reset_ci( control_info *ci )
  348. {
  349.     float t1, t2, oldspeed;
  350.  
  351.     t1 = ci->heading;
  352.     t2 = ci->pitch;
  353.     oldspeed = ci->forward_cruise_percent;
  354.     memset( ci, 0, sizeof(control_info) );
  355.     ci->heading = t1;
  356.     ci->pitch = t2;
  357.     ci->forward_cruise_percent = oldspeed;
  358. }
  359.  
  360. // Read the 4 joystick axis.  This is its own function
  361. // because we only want to read it at a certain rate,
  362. // since it takes time.
  363.  
  364. static int Joystick_saved_reading[JOY_NUM_AXES];
  365. static int Joystick_last_reading = -1;
  366.  
  367. //=======================####### BLOCK BEGIN  by hanzo@150402
  368. #ifndef __hanzo_mouseJoy_disable__
  369.  
  370. int   g_nMjoyX = 0, g_nMjoyY = 0;               // mouse-joy cursor
  371. float g_fMjoyMaxRad__    = 425.0f;      // moving radius of the mouse control cursor
  372. float g_fMjoyDeadZn__    = 56.0f;       // deadzone radius of the mouse control
  373. float g_fMjoyFactor__    = 0.075f;       // sensitivity factor. Multiplied by Mouse_sensitivity to calculate mouse delta factor value.
  374. float g_fMjoyCriArea_    = (0.925f);    // must be greater than 0.5f and lesser than 0.98f
  375.  
  376. static int l_lastBankKeyStat = 0;
  377.  
  378. #endif
  379. //=======================####### BLOCK END
  380.  
  381. void playercontrol_read_stick(int *axis, float frame_time)
  382. {
  383.     int i;
  384.  
  385. #ifndef NDEBUG
  386.     // Make sure things get reset properly between missions.
  387.     if ( (Joystick_last_reading != -1) && (timestamp_until(Joystick_last_reading) > 1000) ) {
  388.         Int3();     // Get John!  John, the joystick last reading didn't get reset btwn levels.
  389.         Joystick_last_reading = -1;
  390.     }
  391. #endif
  392.  
  393.     if ( (Joystick_last_reading == -1)  || timestamp_elapsed(Joystick_last_reading) ) {
  394.         // Read the stick
  395.         control_get_axes_readings(&Joystick_saved_reading[0], &Joystick_saved_reading[1], &Joystick_saved_reading[2], &Joystick_saved_reading[3], &Joystick_saved_reading[4]);
  396.         Joystick_last_reading = timestamp( 1000/10 );   // Read 10x per second, like we did in Descent.
  397.     }
  398.  
  399.     for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
  400.         axis[i] = Joystick_saved_reading[i];
  401.     }
  402.  
  403.     if (Use_mouse_to_fly) {
  404.         int dx, dy, dz;
  405.         float factor;
  406.  
  407.         mouse_get_delta(&dx, &dy, &dz);
  408.  
  409. //=======================####### BLOCK BEGIN  by hanzo@150402
  410. #ifndef __hanzo_mouseJoy_disable__
  411.       float fDx, fDy, fLen, fLenEff, fMjoyFactor;
  412.  
  413.         factor = 1.77f;
  414.         factor = factor * factor / frame_time / 0.6f;
  415.  
  416.         //---- apply mouse move
  417.         g_nMjoyX += dx;
  418.         g_nMjoyY += dy;
  419.         fDx = (float)(g_nMjoyX);
  420.         fDy = (float)(g_nMjoyY);
  421.  
  422.         //---- check the boundary
  423.         fLen = sqrt( (fDx*fDx) + (fDy*fDy) );
  424.         if( fLen > g_fMjoyMaxRad__ ) {
  425.             fDx = (fDx*g_fMjoyMaxRad__) / fLen;
  426.             fDy = (fDy*g_fMjoyMaxRad__) / fLen;
  427.             fLen = g_fMjoyMaxRad__;
  428.         }
  429.  
  430.         i = check_control( BANK_WHEN_PRESSED );
  431.         if( i != l_lastBankKeyStat ) {
  432.             l_lastBankKeyStat = i;
  433.             fDx = fDy = 0.0f;
  434.             fLen = 0.0f;
  435.         }
  436.  
  437.         g_nMjoyX = (int)fDx;
  438.         g_nMjoyY = (int)fDy;
  439.  
  440.         //---- calc~ effective axis value
  441.         fLenEff = fLen - g_fMjoyDeadZn__;
  442.         if( fLenEff > 1.0f ) {
  443.             fMjoyFactor = (float)Mouse_sensitivity * g_fMjoyFactor__;
  444.             if( Mouse_sensitivity && (fLen >= (g_fMjoyMaxRad__*g_fMjoyCriArea_)) ) fMjoyFactor = 1.0f;
  445.             fDx = (fDx*fLenEff*fMjoyFactor)/fLen;
  446.             fDy = (fDy*fLenEff*fMjoyFactor)/fLen;
  447.         } else {
  448.             fDx = fDy = 0.0f;
  449.         }
  450.  
  451.         if ( Invert_axis[0] ) { fDx = -fDx; }
  452.         if ( Invert_axis[1] ) { fDy = -fDy; }
  453.         if ( Invert_axis[3] ) { dz = -dz; }
  454.  
  455.         axis[0] += (int) (fDx * factor);
  456.         axis[1] += (int) (fDy * factor);
  457.         axis[3] += (int) ((float) dz * factor);
  458. //=======================####### BLOCK END
  459. #else
  460.         factor = (float) Mouse_sensitivity + 1.77f;
  461.         factor = factor * factor / frame_time / 0.6f;
  462.  
  463.         if ( Invert_axis[0] ) {
  464.             dx = -dx;
  465.         }
  466.  
  467.         if ( Invert_axis[1] ) {
  468.             dy = -dy;
  469.         }
  470.  
  471.         if ( Invert_axis[3] ) {
  472.             dz = -dz;
  473.         }
  474.  
  475.         axis[0] += (int) ((float) dx * factor);
  476.         axis[1] += (int) ((float) dy * factor);
  477.         axis[3] += (int) ((float) dz * factor);
  478. #endif
  479.     }
  480. }
  481.  
  482. void read_keyboard_controls( control_info * ci, float frame_time, physics_info *pi )
  483. {
  484.     float kh=0.0f, scaled, newspeed, delta, oldspeed;
  485.     int axis[NUM_JOY_AXIS_ACTIONS], slew_active=1;
  486.     static int afterburner_last = 0;
  487.     static float analog_throttle_last = 9e9f;
  488.     static int override_analog_throttle = 0;
  489.     static float savedspeed = ci->forward_cruise_percent;   //Backslash
  490.     int ok_to_read_ci_pitch_yaw=1;
  491.     int centering_speed = 7; // the scale speed in which the camera will smoothly center when the player presses Center View
  492.  
  493.     oldspeed = ci->forward_cruise_percent;
  494.     player_control_reset_ci( ci );
  495.  
  496.     if ( Viewer_mode & VM_EXTERNAL ) {
  497.         control_used(VIEW_EXTERNAL);
  498.         if ( !(Viewer_mode & VM_EXTERNAL_CAMERA_LOCKED) ) {
  499.             ok_to_read_ci_pitch_yaw=0;
  500.         }
  501.  
  502.         do_view_external(frame_time);
  503.         do_thrust_keys(ci);
  504.         slew_active=0;
  505.     } else if ( Viewer_mode & VM_CHASE ) {
  506.         do_view_chase(frame_time);
  507.         slew_active=0;
  508.     } else { // We're in the cockpit.
  509.         if (view_centering) {
  510.             // If we're centering the view, check to see if we're actually centered and bypass any view modifications
  511.             // until the view has finally been centered.
  512.             if ((Viewer_slew_angles.h == 0.0f) && (Viewer_slew_angles.p == 0.0f)) {
  513.                 view_centering = 0; // if the view has been centered, allow the player to freelook again.
  514.             }
  515.             slew_active = 0;
  516.         } else if ( Viewer_mode & VM_TRACK ) { // Player's vision will track current target.
  517.             do_view_track_target(frame_time);
  518.         } else {
  519.             // The Center View command check is here because
  520.             // we don't want the player centering the view in target padlock mode
  521.             if (check_control_timef(VIEW_CENTER) && !view_centering) {
  522.                 view_centering = 1;
  523.                 slew_active = 0;
  524.             }
  525.             do_view_slew(frame_time);
  526.  
  527.             // Orthogonal padlock views moved here in order to get the springy chase effect when transitioning.
  528.             player_set_padlock_state();
  529.  
  530.         }
  531.     }
  532.    
  533.     if ( ok_to_read_ci_pitch_yaw ) {
  534.         // From keyboard...
  535.         do_thrust_keys(ci);
  536.         if ( check_control(BANK_WHEN_PRESSED) ) {
  537.             ci->bank = check_control_timef(BANK_LEFT) + check_control_timef(YAW_LEFT) - check_control_timef(YAW_RIGHT) - check_control_timef(BANK_RIGHT);
  538.             ci->heading = 0.0f;
  539.         } else {
  540.             kh = (check_control_timef(YAW_RIGHT) - check_control_timef(YAW_LEFT)) / 8.0f;
  541.             if (kh == 0.0f) {
  542.                 ci->heading = 0.0f;
  543.  
  544.             } else if (kh > 0.0f) {
  545.                 if (ci->heading < 0.0f)
  546.                     ci->heading = 0.0f;
  547.  
  548.             } else {  // kh < 0
  549.                 if (ci->heading > 0.0f)
  550.                     ci->heading = 0.0f;
  551.             }
  552.  
  553.             ci->bank = check_control_timef(BANK_LEFT) - check_control_timef(BANK_RIGHT);
  554.         }
  555.  
  556.         ci->heading += kh;
  557.  
  558.         kh = (check_control_timef(PITCH_FORWARD) - check_control_timef(PITCH_BACK)) / 8.0f;
  559.         if (kh == 0.0f) {
  560.             ci->pitch = 0.0f;
  561.         } else if (kh > 0.0f) {
  562.             if (ci->pitch < 0.0f)
  563.                 ci->pitch = 0.0f;
  564.  
  565.         } else {  // kh < 0
  566.             if (ci->pitch > 0.0f)
  567.                 ci->pitch = 0.0f;
  568.         }
  569.  
  570.         ci->pitch += kh;
  571.     }
  572.  
  573.     if ( !slew_active ) {
  574.         // If we're not in a view that slews (ie, not a cockpit view), make the viewer slew angles spring to the center.
  575.         chase_slew_angles.h = 0.0f;
  576.         chase_slew_angles.p = 0.0f;
  577.     }
  578.  
  579.     chase_angles_to_value(&Viewer_slew_angles, &chase_slew_angles, centering_speed);
  580.  
  581.     if (!(Game_mode & GM_DEAD)) {
  582.         if ( button_info_query(&Player->bi, ONE_THIRD_THROTTLE) ) {
  583.             control_used(ONE_THIRD_THROTTLE);
  584.             player_clear_speed_matching();
  585.             if ( Player->ci.forward_cruise_percent < 33.3f ) {
  586.                 snd_play( &Snds[ship_get_sound(Player_obj, SND_THROTTLE_UP)], 0.0f );
  587.  
  588.             } else if ( Player->ci.forward_cruise_percent > 33.3f ) {
  589.                 snd_play( &Snds[ship_get_sound(Player_obj, SND_THROTTLE_DOWN)], 0.0f );
  590.             }
  591.  
  592.             Player->ci.forward_cruise_percent = 33.3f;
  593.             override_analog_throttle = 1;
  594.         }
  595.  
  596.         if ( button_info_query(&Player->bi, TWO_THIRDS_THROTTLE) ) {
  597.             control_used(TWO_THIRDS_THROTTLE);
  598.             player_clear_speed_matching();
  599.             if ( Player->ci.forward_cruise_percent < 66.6f ) {
  600.                 snd_play( &Snds[ship_get_sound(Player_obj, SND_THROTTLE_UP)], 0.0f );
  601.  
  602.             } else if (Player->ci.forward_cruise_percent > 66.6f) {
  603.                 snd_play( &Snds[ship_get_sound(Player_obj, SND_THROTTLE_DOWN)], 0.0f );
  604.             }
  605.  
  606.             Player->ci.forward_cruise_percent = 66.6f;
  607.             override_analog_throttle = 1;
  608.         }
  609.  
  610.         if ( button_info_query(&Player->bi, PLUS_5_PERCENT_THROTTLE) ) {
  611.             control_used(PLUS_5_PERCENT_THROTTLE);
  612.             Player->ci.forward_cruise_percent += 5.0f;
  613.             if (Player->ci.forward_cruise_percent > 100.0f)
  614.                 Player->ci.forward_cruise_percent = 100.0f;
  615.         }
  616.  
  617.         if ( button_info_query(&Player->bi, MINUS_5_PERCENT_THROTTLE) ) {
  618.             control_used(MINUS_5_PERCENT_THROTTLE);
  619.             Player->ci.forward_cruise_percent -= 5.0f;
  620.             if (Player->ci.forward_cruise_percent < 0.0f)
  621.                 Player->ci.forward_cruise_percent = 0.0f;
  622.         }
  623.  
  624.         if ( button_info_query(&Player->bi, ZERO_THROTTLE) ) {
  625.             control_used(ZERO_THROTTLE);
  626.             player_clear_speed_matching();
  627.             if ( ci->forward_cruise_percent > 0.0f && Player_obj->phys_info.fspeed > 0.5) {
  628.                 snd_play( &Snds[ship_get_sound(Player_obj, SND_ZERO_THROTTLE)], 0.0f );
  629.             }
  630.  
  631.             ci->forward_cruise_percent = 0.0f;
  632.             override_analog_throttle = 1;
  633.         }
  634.  
  635.         if ( button_info_query(&Player->bi, MAX_THROTTLE) ) {
  636.             control_used(MAX_THROTTLE);
  637.             player_clear_speed_matching();
  638.             if ( ci->forward_cruise_percent < 100.0f ) {
  639.                 snd_play( &Snds[ship_get_sound(Player_obj, SND_FULL_THROTTLE)], 0.0f );
  640.             }
  641.  
  642.             ci->forward_cruise_percent = 100.0f;
  643.             override_analog_throttle = 1;
  644.         }
  645.  
  646.         // AL 12-29-97: If afterburner key is down, player should have full forward thrust (even if afterburners run out)
  647.         if ( check_control(AFTERBURNER) ) {
  648.             ci->forward = 1.0f;
  649.         }
  650.  
  651.         if ( check_control(REVERSE_THRUST) && check_control(AFTERBURNER) ) {
  652.             ci->forward = -pi->max_rear_vel * 1.0f;
  653.         }
  654.  
  655.         if ( Player->flags & PLAYER_FLAGS_MATCH_TARGET ) {
  656.             if ( (Player_ai->last_target == Player_ai->target_objnum) && (Player_ai->target_objnum != -1) && ( ci->forward_cruise_percent == oldspeed) ) {
  657.                 float tspeed, pmax_speed;
  658.                 object *targeted_objp = &Objects[Player_ai->target_objnum];
  659.  
  660.                 tspeed = targeted_objp->phys_info.speed;    //changed from fspeed. If target is reversing, sliding, or gliding we still want to keep up. -- Backslash
  661.  
  662.                 // maybe need to get speed from docked partner
  663.                 if ( tspeed < MATCH_SPEED_THRESHOLD ) {
  664.                     Assert(targeted_objp->type == OBJ_SHIP);
  665.  
  666.                     // Goober5000
  667.                     if (object_is_docked(targeted_objp))
  668.                     {
  669.                         tspeed = dock_calc_docked_speed(targeted_objp); //changed from fspeed
  670.                     }
  671.                 }
  672.  
  673.                 //  Note, if closer than 100 units, scale down speed a bit.  Prevents repeated collisions. -- MK, 12/17/97
  674.                 float dist = vm_vec_dist(&Player_obj->pos, &targeted_objp->pos);
  675.  
  676.                 if (dist < 100.0f) {
  677.                     tspeed = tspeed * (0.5f + dist/200.0f);
  678.                 }
  679.  
  680.                 //SUSHI: If gliding, don't do anything for speed matching
  681.                 if (!( (Objects[Player->objnum].phys_info.flags & PF_GLIDING) || (Objects[Player->objnum].phys_info.flags & PF_FORCE_GLIDE) )) {
  682.                     pmax_speed = Ships[Player_obj->instance].current_max_speed;
  683.                     if (pmax_speed > 0.0f) {
  684.                         ci->forward_cruise_percent = (tspeed / pmax_speed) * 100.0f;
  685.                     } else {
  686.                         ci->forward_cruise_percent = 0.0f;
  687.                     }
  688.                     override_analog_throttle = 1;
  689.                 }
  690.  
  691.             } else
  692.                 Player->flags &= ~PLAYER_FLAGS_MATCH_TARGET;
  693.         }
  694.  
  695.         // code to read joystick axis for pitch/heading.  Code to read joystick buttons
  696.         // for bank.
  697.         if ( !(Game_mode & GM_DEAD) )   {
  698.             playercontrol_read_stick(axis, frame_time);
  699.         } else {
  700.             axis[0] = axis[1] = axis[2] = axis[3] = axis[4] = 0;
  701.         }
  702.  
  703.         if (Axis_map_to[JOY_HEADING_AXIS] >= 0) {
  704.             // check the heading on the x axis
  705.             if ( check_control(BANK_WHEN_PRESSED) ) {
  706.                 delta = f2fl( axis[JOY_HEADING_AXIS] );
  707.                 if ( (delta > 0.05f) || (delta < -0.05f) ) {
  708.                     ci->bank -= delta;
  709.                 }
  710.             } else {
  711.                 ci->heading += f2fl( axis[JOY_HEADING_AXIS] );
  712.             }
  713.         }
  714.  
  715.         // check the pitch on the y axis
  716.         if (Axis_map_to[JOY_PITCH_AXIS] >= 0) {
  717.             ci->pitch -= f2fl( axis[JOY_PITCH_AXIS] );
  718.         }
  719.  
  720.         if (Axis_map_to[JOY_BANK_AXIS] >= 0) {
  721.             ci->bank -= f2fl( axis[JOY_BANK_AXIS] ) * 1.5f;
  722.         }
  723.  
  724.         // axis 2 is for throttle
  725.         if (Axis_map_to[JOY_ABS_THROTTLE_AXIS] >= 0) {
  726.             scaled = (float) axis[JOY_ABS_THROTTLE_AXIS] * 1.2f / (float) F1_0 - 0.1f;  // convert to -0.1 - 1.1 range
  727.             oldspeed = ci->forward_cruise_percent;
  728.  
  729.             newspeed = (1.0f - scaled) * 100.0f;
  730.  
  731.             delta = analog_throttle_last - newspeed;
  732.             if (!override_analog_throttle || (delta < -1.5f) || (delta > 1.5f)) {
  733.                 ci->forward_cruise_percent = newspeed;
  734.                 analog_throttle_last = newspeed;
  735.                 override_analog_throttle = 0;
  736.             }
  737.         }
  738.  
  739.         if (Axis_map_to[JOY_REL_THROTTLE_AXIS] >= 0)
  740.             ci->forward_cruise_percent += f2fl(axis[JOY_REL_THROTTLE_AXIS]) * 100.0f * frame_time;
  741.  
  742.         if ( ci->forward_cruise_percent > 100.0f )
  743.             ci->forward_cruise_percent = 100.0f;
  744.         if ( ci->forward_cruise_percent < 0.0f )
  745.             ci->forward_cruise_percent = 0.0f;
  746.  
  747.         // set up the firing stuff.  Read into control info ala Descent so that weapons will be
  748.         // created during the object simulation phase, and not immediately as was happening before.
  749.  
  750.         //keyboard: fire the current primary weapon
  751.         if (check_control(FIRE_PRIMARY)) {
  752.             ci->fire_primary_count++;
  753.         }
  754.  
  755.         // for debugging, check to see if the debug key is down -- if so, make fire the debug laser instead
  756. #ifndef NDEBUG
  757.         if ( keyd_pressed[KEY_DEBUG_KEY] ) {
  758.             ci->fire_debug_count = ci->fire_primary_count;
  759.             ci->fire_primary_count = 0;
  760.         }
  761. #endif
  762.  
  763.         // keyboard: fire the current secondary weapon
  764.         if (check_control(FIRE_SECONDARY)) {
  765.             ci->fire_secondary_count++;
  766.  
  767.             // if we're a multiplayer client, set our accum bits now
  768.             if( MULTIPLAYER_CLIENT && (Net_player != NULL)){
  769.                 Net_player->s_info.accum_buttons |= OOC_FIRE_SECONDARY;
  770.             }
  771.         }
  772.  
  773.         // keyboard: launch countermeasures, but not if AI controlling Player
  774.         if (button_info_query(&Player->bi, LAUNCH_COUNTERMEASURE) && !Player_use_ai) {
  775.             control_used(LAUNCH_COUNTERMEASURE);
  776.             ci->fire_countermeasure_count++;
  777.             hud_gauge_popup_start(HUD_CMEASURE_GAUGE);
  778.         }
  779.  
  780.         // see if the afterburner has been started (keyboard + joystick)
  781.         if (check_control(AFTERBURNER) && !Player_use_ai) {
  782.             if (!afterburner_last) {
  783.                 Assert(Player_ship);
  784.                 if ( !(Ship_info[Player_ship->ship_info_index].flags & SIF_AFTERBURNER) ) {
  785.                     gamesnd_play_error_beep();
  786.                 } else {
  787.                     ci->afterburner_start = 1;
  788.                 }
  789.             }
  790.  
  791.             afterburner_last = 1;
  792.  
  793.         } else {
  794.             if (afterburner_last)
  795.                 ci->afterburner_stop = 1;
  796.  
  797.             afterburner_last = 0;
  798.         }
  799.  
  800.         // new gliding systems combining code by Backslash, Turey, Kazan, and WMCoolmon
  801.  
  802.         // Check for toggle button pressed.
  803.         if ( button_info_query(&Player->bi, TOGGLE_GLIDING) ) {
  804.             control_used(TOGGLE_GLIDING);
  805.             if ( Player_obj != NULL && Ship_info[Player_ship->ship_info_index].can_glide ) {
  806.                 toggle_glide = !toggle_glide;
  807.             }
  808.         }
  809.         // This logic is a bit tricky. It checks to see if the glide_when_pressed button is in a different state
  810.         // than press_glide. Since it sets press_glide equal to glide_when_pressed inside of this if statement,
  811.         //  this only evaluates to true when the state of the button is different than it was last time.
  812.         if ( check_control(GLIDE_WHEN_PRESSED) != press_glide ) {
  813.             if ( Player_obj != NULL && Ship_info[Player_ship->ship_info_index].can_glide ) {
  814.                 // This only works if check_control returns only 1 or 0. Shouldn't be a problem,
  815.                 // but this comment's here just in case it is.
  816.                 press_glide = !press_glide;
  817.             }
  818.         }
  819.  
  820.         // if the player is warping out, cancel gliding
  821.         if (Player_ship->flags & SF_DEPART_WARP) {
  822.             toggle_glide = 0;
  823.             press_glide = 0;
  824.         }
  825.  
  826.         // Do we want to be gliding?
  827.         if ( toggle_glide || press_glide ) {
  828.             // Probably don't need to do this check, but just in case...
  829.             if ( Player_obj != NULL && Ship_info[Player_ship->ship_info_index].can_glide ) {
  830.                 // Only bother doing this if we need to.
  831.                 if ( toggle_glide && press_glide ) {
  832.                     // Overkill -- if gliding is toggled on and glide_when_pressed is pressed, turn glide off
  833.                     if ( object_get_gliding(Player_obj) && !object_glide_forced(Player_obj) ) {
  834.                         object_set_gliding(Player_obj, false);
  835.                         ci->forward_cruise_percent = savedspeed;
  836.                         press_glide = !press_glide;
  837.                         snd_play( &Snds[ship_get_sound(Player_obj, SND_THROTTLE_UP)], 0.0f );
  838.                     }
  839.                 } else if ( !object_get_gliding(Player_obj) ) {
  840.                     object_set_gliding(Player_obj, true);
  841.                     savedspeed = ci->forward_cruise_percent;
  842.                     ci->forward_cruise_percent = 0.0f;
  843.                     override_analog_throttle = 1;
  844.                     if (Ship_info[Player_ship->ship_info_index].glide_start_snd > 0) {
  845.                         //If a custom glide start sound was specified, play it
  846.                         snd_play( &Snds[Ship_info[Player_ship->ship_info_index].glide_start_snd], 0.0f );
  847.                     } else {
  848.                         //If glide_start_snd wasn't set (probably == 0), use the default throttle down sound
  849.                         snd_play( &Snds[ship_get_sound(Player_obj, SND_THROTTLE_DOWN)], 0.0f );
  850.                     }
  851.                 }
  852.             }
  853.         } else {
  854.             // Probably don't need to do the second half of this check, but just in case...
  855.             if ( Player_obj != NULL && Ship_info[Player_ship->ship_info_index].can_glide ) {
  856.                 // Only bother doing this if we need to.
  857.                 if ( object_get_gliding(Player_obj) && !object_glide_forced(Player_obj) ) {
  858.                     object_set_gliding(Player_obj, false);
  859.                     ci->forward_cruise_percent = savedspeed;
  860.                     if (Ship_info[Player_ship->ship_info_index].glide_end_snd > 0) {
  861.                         //If a custom glide end sound was specified, play it
  862.                         snd_play( &Snds[Ship_info[Player_ship->ship_info_index].glide_end_snd], 0.0f );
  863.                     } else {
  864.                         //If glide_end_snd wasn't set (probably == 0), use the default throttle up sound
  865.                         snd_play( &Snds[ship_get_sound(Player_obj, SND_THROTTLE_UP)], 0.0f );
  866.                     }
  867.                 }
  868.             }
  869.         }
  870.  
  871.     }
  872.  
  873.     if ( (Viewer_mode & VM_EXTERNAL) ) {
  874.         if ( !(Viewer_mode & VM_EXTERNAL_CAMERA_LOCKED) ) {
  875.             ci->heading=0.0f;
  876.             ci->pitch=0.0f;
  877.             ci->bank=0.0f;
  878.         }
  879.     }
  880. }
  881.  
  882. void copy_control_info(control_info *dest_ci, control_info *src_ci)
  883. {
  884.     if (dest_ci == NULL)
  885.         return;
  886.  
  887.     if (src_ci == NULL) {
  888.         dest_ci->pitch = 0.0f;
  889.         dest_ci->vertical = 0.0f;
  890.         dest_ci->heading = 0.0f;
  891.         dest_ci->sideways = 0.0f;
  892.         dest_ci->bank = 0.0f;
  893.         dest_ci->forward = 0.0f;
  894.         dest_ci->forward_cruise_percent = 0.0f;
  895.         dest_ci->fire_countermeasure_count = 0;
  896.         dest_ci->fire_secondary_count = 0;
  897.         dest_ci->fire_primary_count = 0;
  898.     } else {
  899.         dest_ci->pitch = src_ci->pitch;
  900.         dest_ci->vertical = src_ci->vertical;
  901.         dest_ci->heading = src_ci->heading;
  902.         dest_ci->sideways = src_ci->sideways;
  903.         dest_ci->bank = src_ci->bank;
  904.         dest_ci->forward = src_ci->forward;
  905.         dest_ci->forward_cruise_percent = src_ci->forward_cruise_percent;
  906.     }
  907. }
  908.  
  909. void read_player_controls(object *objp, float frametime)
  910. {
  911.     float diff;
  912.     int can_warp = 0, warp_failed = 0;
  913.     float target_warpout_speed;
  914.  
  915.     joy_ff_adjust_handling((int) objp->phys_info.speed);
  916.  
  917.     switch( Player->control_mode )
  918.     {
  919.         case PCM_SUPERNOVA:
  920.             break;
  921.  
  922.         case PCM_NORMAL:
  923.             read_keyboard_controls(&(Player->ci), frametime, &objp->phys_info );
  924.  
  925.             if ( lua_game_control & LGC_STEERING ) {
  926.                 // make sure to copy the control before reseting it
  927.                 Player->lua_ci = Player->ci;
  928.                 copy_control_info(&(Player->ci), NULL);
  929.             } else if ( lua_game_control & LGC_FULL ) {
  930.                 control_info temp;
  931.                 // first copy over the new values, then reset
  932.                 temp = Player->ci;
  933.                 copy_control_info(&(Player->ci), &(Player->lua_ci));
  934.                 Player->lua_ci = temp;
  935.             } else {
  936.                 // just copy the ci should that be needed in scripting
  937.                 Player->lua_ci = Player->ci;
  938.             }
  939.             break;
  940.  
  941.         case PCM_WARPOUT_STAGE1:    // Accelerate to 40 km/s
  942.         case PCM_WARPOUT_STAGE2:    // Go 40 km/s steady up to the effect
  943.         case PCM_WARPOUT_STAGE3:    // Go 40 km/s steady through the effect
  944.         {
  945.             memset(&(Player->ci), 0, sizeof(control_info) );        // set the controls to 0
  946.  
  947.             if ( (objp->type == OBJ_SHIP) && (!(Game_mode & GM_DEAD)) )
  948.             {
  949.                 Warpout_time += flFrametime;
  950.  
  951.                 target_warpout_speed = ship_get_warpout_speed(objp);
  952.  
  953.                 if ( Warpout_forced ) {
  954.                     Ships[objp->instance].current_max_speed = target_warpout_speed * 2.0f;
  955.  
  956.                     diff = target_warpout_speed-objp->phys_info.fspeed;
  957.  
  958.                     if ( diff < 0.0f )
  959.                         diff = 0.0f;
  960.  
  961.                     Player->ci.forward = ((target_warpout_speed+diff) / Ships[objp->instance].current_max_speed);
  962.                 } else {
  963.                     // check if warp ability has been disabled
  964.                     if ( Ships[objp->instance].flags & ( SF_WARP_BROKEN|SF_WARP_NEVER ) ) {
  965.                         HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Cannot warp out at this time.", 81));
  966.                         warp_failed = 1;
  967.                     } else {
  968.                         if ( (!warp_failed) && (Ships[objp->instance].current_max_speed >= target_warpout_speed) ) {
  969.                             can_warp = 1;
  970.                         } else {
  971.                             Ships[objp->instance].current_max_speed = target_warpout_speed + 5.0f;
  972.                             can_warp = 1;
  973.                         }
  974.  
  975.                         if (can_warp) {
  976.                             diff = target_warpout_speed - objp->phys_info.fspeed;
  977.  
  978.                             if ( diff < 0.0f )
  979.                                 diff = 0.0f;
  980.                        
  981.                             Player->ci.forward = ((target_warpout_speed + diff) / Ships[objp->instance].current_max_speed);
  982.                         }
  983.                     }
  984.  
  985.                     if ( warp_failed ) {
  986.                         snd_play(&Snds[SND_PLAYER_WARP_FAIL]);
  987.                         gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_STOP );
  988.                     }
  989.                 }
  990.            
  991.                 if ( Player->control_mode == PCM_WARPOUT_STAGE1 )
  992.                 {
  993.                     float warpout_delay;
  994.                     ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
  995.  
  996.                     if (sip->warpout_engage_time >= 0)
  997.                         warpout_delay = sip->warpout_engage_time / 1000.0f;
  998.                     else
  999.                         warpout_delay = MINIMUM_PLAYER_WARPOUT_TIME;
  1000.  
  1001.                     // Wait at least 3 seconds before making sure warp speed is set.
  1002.                     if ( Warpout_time > warpout_delay) {
  1003.                         // If we are going around 5% of the target speed, progress to next stage
  1004.                         float diffSpeed = objp->phys_info.fspeed;
  1005.                         if(target_warpout_speed != 0.0f) {
  1006.                             diffSpeed = fl_abs(objp->phys_info.fspeed - target_warpout_speed )/target_warpout_speed;
  1007.                         }
  1008.                         if ( diffSpeed < TARGET_WARPOUT_MATCH_PERCENT ) {
  1009.                             gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_DONE_STAGE1 );
  1010.                         }
  1011.                     }
  1012.                 }
  1013.             }
  1014.  
  1015.             break;
  1016.         }
  1017.     }
  1018.  
  1019.     // the ships maximum velocity now depends on the energy flowing to engines
  1020.     if(objp->type != OBJ_OBSERVER){
  1021.         objp->phys_info.max_vel.xyz.z = Ships[objp->instance].current_max_speed;
  1022.     }
  1023.     if(Player_obj->type == OBJ_SHIP && !Player_use_ai){
  1024.         // only read player control info if player ship is not dead
  1025.         // or if Player_use_ai is disabed
  1026.         if ( !(Ships[Player_obj->instance].flags & SF_DYING) ) {
  1027.             vec3d wash_rot;
  1028.             if ((Ships[objp->instance].wash_intensity > 0) && !((Player->control_mode == PCM_WARPOUT_STAGE1) || (Player->control_mode == PCM_WARPOUT_STAGE2) || (Player->control_mode == PCM_WARPOUT_STAGE3)) ) {
  1029.                 float intensity = 0.3f * MIN(Ships[objp->instance].wash_intensity, 1.0f);
  1030.                 vm_vec_copy_scale(&wash_rot, &Ships[objp->instance].wash_rot_axis, intensity);
  1031.                 physics_read_flying_controls( &objp->orient, &objp->phys_info, &(Player->ci), flFrametime, &wash_rot);
  1032.             } else {
  1033.                 physics_read_flying_controls( &objp->orient, &objp->phys_info, &(Player->ci), flFrametime);
  1034.             }
  1035.         }
  1036.     } else if(Player_obj->type == OBJ_OBSERVER){
  1037.         physics_read_flying_controls(&objp->orient,&objp->phys_info,&(Player->ci), flFrametime);
  1038.     }
  1039. }
  1040.  
  1041. void player_controls_init()
  1042. {
  1043.     static int initted = 0;
  1044.  
  1045.     if (initted)
  1046.         return;
  1047.  
  1048.     initted = 1;
  1049.     physics_init( &Descent_physics );
  1050.     Descent_physics.flags |= PF_ACCELERATES | PF_SLIDE_ENABLED;
  1051.  
  1052.     Viewer_slew_angles_delta.p = 0.0f;
  1053.     Viewer_slew_angles_delta.b = 0.0f;
  1054.     Viewer_slew_angles_delta.h = 0.0f;
  1055. }
  1056.  
  1057. /**
  1058.  * Clear current speed matching and auto-speed matching flags
  1059.  */
  1060. void player_clear_speed_matching()
  1061. {
  1062.     if ( !Player ) {
  1063.         Int3(); // why is Player NULL?
  1064.         return;
  1065.     }
  1066.  
  1067.     Player->flags &= ~PLAYER_FLAGS_MATCH_TARGET;
  1068.     Player->flags &= ~PLAYER_FLAGS_AUTO_MATCH_SPEED;
  1069. }
  1070.  
  1071. /**
  1072.  * Computes the forward_thrust_time needed for the player ship to match velocities with the currently selected target
  1073.  *
  1074.  * @param no_target_text Default parm (NULL), used to override HUD output when no target exists
  1075.  * @param match_off_text Default parm (NULL), used to overide HUD output when matching toggled off
  1076.  * @param match_on_text Default parm (NULL), used to overide HUD output when matching toggled on
  1077.  */
  1078. void player_match_target_speed(char *no_target_text, char *match_off_text, char *match_on_text)
  1079. {
  1080.     // multiplayer observers can't match target speed
  1081.     if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER)) ){
  1082.         return;
  1083.     }
  1084.  
  1085.     if ( Player_ai->target_objnum == -1) {
  1086.         if ( no_target_text ) {
  1087.             if ( no_target_text[0] ) {
  1088.                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, no_target_text );
  1089.             }
  1090.         } else {
  1091. //          HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR("No currently selected target.",-1) );
  1092.         }
  1093.         return;
  1094.     }
  1095.  
  1096.     object *targeted_objp = &Objects[Player_ai->target_objnum];
  1097.  
  1098.     if ( targeted_objp->type != OBJ_SHIP ) {
  1099.         return;
  1100.     }
  1101.  
  1102.     if ( Player->flags & PLAYER_FLAGS_MATCH_TARGET ) {
  1103.         Player->flags &= ~PLAYER_FLAGS_MATCH_TARGET;
  1104.         if ( match_off_text ) {
  1105.             if ( match_off_text[0] ) {
  1106.                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, match_off_text );
  1107.             }
  1108.         } else {
  1109. //          HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR("No longer matching speed with current target.",-1) );
  1110.         }
  1111.     } else {
  1112.         int can_match=0;
  1113.  
  1114.         if ( targeted_objp->phys_info.fspeed > MATCH_SPEED_THRESHOLD ) {
  1115.             can_match=1;
  1116.         } else {
  1117.             // account for case of matching speed with docked ship
  1118.             if (object_is_docked(targeted_objp))
  1119.             {
  1120.                 if (dock_calc_docked_fspeed(targeted_objp) > MATCH_SPEED_THRESHOLD)
  1121.                 {
  1122.                     can_match=1;
  1123.                 }
  1124.             }
  1125.         }
  1126.  
  1127.         if ( can_match ) {
  1128.             Player->flags |= PLAYER_FLAGS_MATCH_TARGET;
  1129.             if ( match_on_text ) {
  1130.                 if ( match_on_text[0] ) {
  1131.                     HUD_sourced_printf(HUD_SOURCE_HIDDEN, match_on_text );
  1132.                 }
  1133.             } else {
  1134. //              HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR("Matching speed with current target.",-1) );
  1135.             }
  1136.         }
  1137.     }
  1138. }
  1139.  
  1140. // toggle_player_object toggles between the player objects (i.e. the ship they are currently flying)
  1141. // and a descent style ship.
  1142.  
  1143. int use_descent = 0;
  1144. LOCAL physics_info phys_save;
  1145.  
  1146. void toggle_player_object()
  1147. {
  1148.     if ( use_descent ) {
  1149.         memcpy( &Player_obj->phys_info, &phys_save, sizeof(physics_info) );
  1150.     } else {
  1151.         memcpy( &phys_save, &Player_obj->phys_info, sizeof(physics_info) );
  1152.         memcpy( &Player_obj->phys_info, &Descent_physics, sizeof(physics_info) );
  1153.     }
  1154.     use_descent = !use_descent;
  1155.  
  1156.     HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("Using %s style physics for player ship."), use_descent ? NOX("DESCENT") : NOX("FreeSpace"));
  1157. }
  1158.  
  1159. /**
  1160.  * Initialise the data required for determining whether 'all alone' message should play
  1161.  */
  1162. void player_init_all_alone_msg()
  1163. {
  1164.     ship_obj    *so;
  1165.     object  *objp;
  1166.  
  1167.     Player->check_for_all_alone_msg=timestamp(0);
  1168.  
  1169.     // See if there are any friendly ships present, if so return without preventing msg
  1170.     for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
  1171.         objp = &Objects[so->objnum];
  1172.         if ( objp == Player_obj ) {
  1173.             continue;
  1174.         }
  1175.  
  1176.         if ( Ships[objp->instance].team == Player_ship->team ) {
  1177.             int ship_type = Ship_info[Ships[objp->instance].ship_info_index].class_type;
  1178.             if ( ship_type != -1 && (Ship_types[ship_type].message_bools & STI_MSG_COUNTS_FOR_ALONE) ) {
  1179.                 return;
  1180.             }
  1181.         }
  1182.     }
  1183.  
  1184.     // There must be no friendly ships present, so prevent the 'all alone' message from ever playing
  1185.     Player->flags |= PLAYER_FLAGS_NO_CHECK_ALL_ALONE_MSG;
  1186. }
  1187.  
  1188. /**
  1189.  * Called when a new pilot is created
  1190.  */
  1191. void player_set_pilot_defaults(player *p)
  1192. {
  1193.     // Enable auto-targeting by default for all new pilots
  1194.     p->flags |= PLAYER_FLAGS_AUTO_TARGETING;
  1195.     p->save_flags |= PLAYER_FLAGS_AUTO_TARGETING;
  1196.  
  1197.     p->auto_advance = 1;
  1198. }
  1199.  
  1200. /**
  1201.  * Store some player preferences to ::Player->save_flags
  1202.  */
  1203. void player_save_target_and_weapon_link_prefs()
  1204. {
  1205.     Player->save_flags = 0;
  1206.     if ( Player->flags & PLAYER_FLAGS_AUTO_TARGETING ) {
  1207.         Player->save_flags |= PLAYER_FLAGS_AUTO_TARGETING;
  1208.     }
  1209.  
  1210.  
  1211.     if ( Player->flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
  1212.         // multiplayer observers can't match target speed
  1213.         if(!((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER))) )
  1214.         {
  1215.             Player->save_flags |= PLAYER_FLAGS_AUTO_MATCH_SPEED;
  1216.         }      
  1217.     }
  1218.  
  1219.     // if we're in multiplayer mode don't do this because we will desync ourselves with the server
  1220.     if(!(Game_mode & GM_MULTIPLAYER)){
  1221.         if ( Player_ship->flags & SF_PRIMARY_LINKED ) {
  1222.             Player->save_flags |= PLAYER_FLAGS_LINK_PRIMARY;
  1223.         } else {
  1224.             Player->flags &= ~PLAYER_FLAGS_LINK_PRIMARY;
  1225.         }
  1226.         if ( Player_ship->flags & SF_SECONDARY_DUAL_FIRE ) {
  1227.             Player->save_flags |= PLAYER_FLAGS_LINK_SECONDARY;
  1228.         } else {
  1229.             Player->flags &= ~PLAYER_FLAGS_LINK_SECONDARY;
  1230.         }
  1231.     }
  1232. }
  1233.  
  1234. /**
  1235.  * Store some player preferences to ::Player->save_flags
  1236.  */
  1237. void player_restore_target_and_weapon_link_prefs()
  1238. {
  1239.     ship_info *player_sip;
  1240.     player_sip = &Ship_info[Player_ship->ship_info_index];
  1241.     polymodel *pm = model_get(player_sip->model_num);
  1242.  
  1243.     //  Don't restores the save flags in training, as we must ensure certain things are off, such as speed matching.
  1244.     if ( !(The_mission.game_type & MISSION_TYPE_TRAINING )) {
  1245.         Player->flags |= Player->save_flags;
  1246.     }
  1247.  
  1248.     if ( Player->flags & PLAYER_FLAGS_LINK_PRIMARY && !(player_sip->flags2 & SIF2_NO_PRIMARY_LINKING) ) {
  1249.         if ( Player_ship->weapons.num_primary_banks > 1 ) {
  1250.             Player_ship->flags |= SF_PRIMARY_LINKED;
  1251.         }
  1252.     }
  1253.  
  1254.     if ( Player->flags & PLAYER_FLAGS_LINK_SECONDARY && (pm->n_missiles > 0 && pm->missile_banks[0].num_slots > 1) ) {
  1255.         Player_ship->flags |= SF_SECONDARY_DUAL_FIRE;
  1256.     }
  1257. }
  1258.  
  1259. /**
  1260.  * Initialise player statistics on a per mission basis
  1261.  * @todo Don't use memset(0) approach to setting up Player->ci
  1262.  */
  1263. void player_level_init()
  1264. {
  1265.     toggle_glide = 0;
  1266.     press_glide = 0;
  1267.     memset(&(Player->ci), 0, sizeof(control_info) );        // set the controls to 0
  1268.  
  1269.     Viewer_slew_angles.p = 0.0f;    Viewer_slew_angles.b = 0.0f;    Viewer_slew_angles.h = 0.0f;
  1270.     Viewer_external_info.angles.p = 0.0f;
  1271.     Viewer_external_info.angles.b = 0.0f;
  1272.     Viewer_external_info.angles.h = 0.0f;
  1273.     Viewer_external_info.distance = 0.0f;
  1274.  
  1275.     Viewer_mode = 0;
  1276.  
  1277.     Player_obj = NULL;
  1278.     Player_ship = NULL;
  1279.     Player_ai = NULL;
  1280.  
  1281.     Player_use_ai = 0;  // Goober5000
  1282.  
  1283.     Joystick_last_reading = -1;             // Make the joystick read right away.
  1284.  
  1285.     if(Player == NULL)
  1286.         return;
  1287.  
  1288.     Player->flags = PLAYER_FLAGS_STRUCTURE_IN_USE;          // reset the player flags
  1289.     Player->flags |= Player->save_flags;
  1290.    
  1291.     //  Init variables for friendly fire monitoring.
  1292.     Player->friendly_last_hit_time = 0;
  1293.     Player->friendly_hits = 0;
  1294.     Player->friendly_damage = 0.0f;
  1295.     Player->last_warning_message_time = 0;
  1296.  
  1297.     Player->control_mode = PCM_NORMAL;
  1298.  
  1299.     Player->allow_warn_timestamp = 1;       // init timestamp that is used for managing attack warnings sent to player
  1300.     Player->check_warn_timestamp = 1;
  1301.     Player->warn_count = 0;                     // number of attack warnings player has received this mission
  1302.  
  1303.     Player->distance_warning_count = 0;     // Number of warning too far from origin
  1304.     Player->distance_warning_time = 0;      // Time at which last warning was given
  1305.  
  1306.     Player->praise_count = 0;                   // number of praises player has received this mission
  1307.     Player->allow_praise_timestamp = 1;     // timestamp until next praise is allowed
  1308.     Player->praise_delay_timestamp = 0;     // timstamp used to delay praises given to the player
  1309.  
  1310.     Player->ask_help_count = 0;             // number of times player has been asked for help by wingmen
  1311.     Player->allow_ask_help_timestamp = 1;   // timestamp until next ask_help is allowed
  1312.  
  1313.     Player->scream_count = 0;                   // number of times player has heard wingman screams this mission
  1314.     Player->allow_scream_timestamp = 1;     // timestamp until next wingman scream is allowed
  1315.    
  1316.     Player->low_ammo_complaint_count = 0;   // number of complaints about low ammo received in this mission
  1317.     Player->allow_ammo_timestamp = 1;       // timestamp until next 'Ammo low' message can be played
  1318.  
  1319.     Player->praise_self_count = 0;          // number of boasts about kills received in this mission
  1320.     Player->praise_self_timestamp = 1;      // timestamp marking time until next boast is allowed
  1321.  
  1322.     Player->request_repair_timestamp = 1;   // timestamp until next 'requesting repair sir' message can be played
  1323.  
  1324.     Player->repair_sound_loop = -1;
  1325.     Player->cargo_scan_loop = -1;
  1326.     Player->cargo_inspect_time = 0;         // time that current target's cargo has been inspected for
  1327.  
  1328.     Player->target_is_dying = -1;               // The player target is dying, set to -1 if no target
  1329.     Player->current_target_sx = -1;         // Screen x-pos of current target (or subsystem if applicable)
  1330.     Player->current_target_sy = -1;         // Screen y-pos of current target (or subsystem if applicable)
  1331.     Player->target_in_lock_cone = -1;       // Is the current target in secondary weapon lock cone?
  1332.     Player->locking_subsys=NULL;                // Subsystem pointer that missile lock is trying to seek
  1333.     Player->locking_on_center=0;                // boolean, whether missile lock is trying for center of ship or not
  1334.     Player->locking_subsys_parent=-1;
  1335.  
  1336.     Player->killer_objtype=-1;                  // type of object that killed player
  1337.     Player->killer_weapon_index = -1;           // weapon used to kill player (if applicable)
  1338.     Player->killer_parent_name[0]=0;            // name of parent object that killed the player
  1339.  
  1340.     Player_all_alone_msg_inited=0;
  1341.     Player->flags &= ~PLAYER_FLAGS_NO_CHECK_ALL_ALONE_MSG;
  1342.  
  1343.     Player->death_message = "";
  1344. }
  1345.  
  1346. /**
  1347.  * Initializes global variables once a game -- needed because of mallocing that
  1348.  * goes on in structures in the player file
  1349.  */
  1350. void player_init()
  1351. {
  1352.     Player_num = 0;
  1353.     Player = &Players[Player_num];
  1354.     Player->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE;
  1355.     Player->failures_this_session = 0;
  1356.     Player->show_skip_popup = (ubyte) 1;
  1357. }
  1358.  
  1359. /**
  1360.  * Stop any looping sounds associated with the Player, called from ::game_stop_looped_sounds().
  1361.  */
  1362. void player_stop_looped_sounds()
  1363. {
  1364.     Assert(Player);
  1365.     if ( Player->repair_sound_loop > -1 )   {
  1366.         snd_stop(Player->repair_sound_loop);
  1367.         Player->repair_sound_loop = -1;
  1368.     }
  1369.     if ( Player->cargo_scan_loop > -1 ) {
  1370.         snd_stop(Player->cargo_scan_loop);
  1371.         Player->cargo_scan_loop = -1;
  1372.     }
  1373. }
  1374.  
  1375. /**
  1376.  * Start the repair sound if it hasn't already been started.  Called when a player ship is being
  1377.  * repaired by a support ship
  1378.  */
  1379. void player_maybe_start_repair_sound()
  1380. {
  1381.     Assert(Player);
  1382.     if ( Player->repair_sound_loop == -1 ) {
  1383.         Player->repair_sound_loop = snd_play_looping( &Snds[SND_SHIP_REPAIR] );
  1384.     }
  1385. }
  1386.  
  1387. /**
  1388.  * Stop the player repair sound if it is already playing
  1389.  */
  1390. void player_stop_repair_sound()
  1391. {
  1392.     Assert(Player);
  1393.     if ( Player->repair_sound_loop != -1 ) {
  1394.         snd_stop(Player->repair_sound_loop);
  1395.         Player->repair_sound_loop  = -1;
  1396.     }
  1397. }
  1398.  
  1399. /**
  1400.  * Start the cargo scanning sound if it hasn't already been started
  1401.  */
  1402. void player_maybe_start_cargo_scan_sound()
  1403. {
  1404.     Assert(Player);
  1405.     if ( Player->cargo_scan_loop == -1 ) {
  1406.         Player->cargo_scan_loop = snd_play_looping( &Snds[ship_get_sound(Player_obj, SND_CARGO_SCAN)] );
  1407.     }
  1408. }
  1409.  
  1410. /**
  1411.  * Stop the player repair sound if it is already playing
  1412.  */
  1413. void player_stop_cargo_scan_sound()
  1414. {
  1415.     Assert(Player);
  1416.     if ( Player->cargo_scan_loop != -1 ) {
  1417.         snd_stop(Player->cargo_scan_loop);
  1418.         Player->cargo_scan_loop  = -1;
  1419.     }
  1420. }
  1421.  
  1422. /**
  1423.  * @brief See if there is a praise message to deliver to the player.  We want to delay the praise messages
  1424.  * a bit, to make them more realistic
  1425.  *
  1426.  * @return 1 if a praise message was delivered to the player, or a praise is pending; 0 if no praise is pending
  1427.  */
  1428. int player_process_pending_praise()
  1429. {
  1430.     // in multiplayer, never praise
  1431.     if(Game_mode & GM_MULTIPLAYER){
  1432.         return 0;
  1433.     }
  1434.  
  1435.     if ( timestamp_elapsed(Player->praise_delay_timestamp) ) {
  1436.         int ship_index;
  1437.  
  1438.         Player->praise_delay_timestamp = 0;
  1439.         ship_index = ship_get_random_player_wing_ship( SHIP_GET_UNSILENCED, 1000.0f );
  1440.         if ( ship_index >= 0 ) {
  1441.             // Only praise if above 50% integrity
  1442.             if ( get_hull_pct(&Objects[Ships[ship_index].objnum]) > 0.5f ) {
  1443.                 if (Player->stats.m_kill_count_ok > 10) {   // this number should probably be in the AI profile or mission file rather than hardcoded
  1444.                     message_send_builtin_to_player(MESSAGE_HIGH_PRAISE, &Ships[ship_index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, -1, -1);
  1445.                 }
  1446.                 else {
  1447.                     message_send_builtin_to_player(MESSAGE_PRAISE, &Ships[ship_index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, -1, -1);
  1448.                 }
  1449.                 Player->allow_praise_timestamp = timestamp(Builtin_messages[MESSAGE_PRAISE].min_delay * (Game_skill_level+1) );
  1450.                 Player->allow_scream_timestamp = timestamp(20000);      // prevent death scream following praise
  1451.                 Player->praise_count++;
  1452.                 return 1;
  1453.             }
  1454.         }
  1455.     }
  1456.  
  1457.     if ( Player->praise_delay_timestamp == 0 ) {
  1458.         return 0;
  1459.     }
  1460.  
  1461.     return 1;
  1462. }
  1463.  
  1464. int player_inspect_cap_subsys_cargo(float frametime, char *outstr);
  1465.  
  1466. /**
  1467.  * See if the player should be inspecting cargo, and update progress.
  1468.  *
  1469.  * @param frametime Time since last frame in seconds
  1470.  * @param outstr (output parm) holds string that HUD should display
  1471.  *
  1472.  * @return 1 if player should display outstr on HUD; 0if don't display cargo on HUD
  1473.  */
  1474. int player_inspect_cargo(float frametime, char *outstr)
  1475. {
  1476.     object      *cargo_objp;
  1477.     ship            *cargo_sp;
  1478.     ship_info   *cargo_sip;
  1479.     vec3d       vec_to_cargo;
  1480.     float           dot;
  1481.     int scan_subsys;
  1482.  
  1483.     outstr[0] = 0;
  1484.  
  1485.     if ( Player_ai->target_objnum < 0 ) {
  1486.         return 0;
  1487.     }
  1488.  
  1489.     cargo_objp = &Objects[Player_ai->target_objnum];
  1490.     Assert(cargo_objp->type == OBJ_SHIP);
  1491.     cargo_sp = &Ships[cargo_objp->instance];
  1492.     cargo_sip = &Ship_info[cargo_sp->ship_info_index];
  1493.  
  1494.     // Goober5000 - possibly swap cargo scan behavior
  1495.     scan_subsys = (cargo_sip->flags & SIF_HUGE_SHIP);
  1496.     if (cargo_sp->flags2 & SF2_TOGGLE_SUBSYSTEM_SCANNING)
  1497.         scan_subsys = !scan_subsys;
  1498.     if (scan_subsys)
  1499.         return player_inspect_cap_subsys_cargo(frametime, outstr);
  1500.  
  1501.     // check if target is ship class that can be inspected
  1502.     // MWA -- 1/27/98 -- added fighters/bombers to this list.  For multiplayer, we
  1503.     // want to show callsign of player
  1504.     // G5K -- 10/20/08 -- moved the callsign code into hud_stuff_ship_callsign, where
  1505.     // it makes more sense
  1506.  
  1507.     // scannable cargo behaves differently.  Scannable cargo is either "scanned" or "not scanned".  This flag
  1508.     // can be set on any ship.  Any ship with this set won't have "normal" cargo behavior
  1509.     if ( !(cargo_sp->flags & SF_SCANNABLE) ) {
  1510.         if ( !(cargo_sip->flags & (SIF_CARGO|SIF_TRANSPORT)) ) {
  1511.             return 0;
  1512.         }
  1513.     }
  1514.  
  1515.     // if cargo is already revealed
  1516.     if ( cargo_sp->flags & SF_CARGO_REVEALED ) {
  1517.         if ( !(cargo_sp->flags & SF_SCANNABLE) ) {
  1518.             char *cargo_name = Cargo_names[cargo_sp->cargo1 & CARGO_INDEX_MASK];
  1519.             Assert( cargo_sip->flags & (SIF_CARGO|SIF_TRANSPORT) );
  1520.  
  1521.             if ( cargo_name[0] == '#' ) {
  1522.                 sprintf(outstr, XSTR("passengers: %s", 83), cargo_name+1 );
  1523.             } else {
  1524.                 sprintf(outstr,XSTR("cargo: %s", 84), cargo_name );
  1525.             }
  1526.         } else {
  1527.             strcpy(outstr, XSTR( "Scanned", 85) );
  1528.         }
  1529.  
  1530.         // always bash cargo_inspect_time to 0 since AI ships can reveal cargo that we
  1531.         // are in the process of scanning
  1532.         Player->cargo_inspect_time = 0;
  1533.  
  1534.         return 1;
  1535.     }
  1536.  
  1537.     // see if player is within inspection range
  1538.     if ( Player_ai->current_target_distance < MAX(CARGO_REVEAL_MIN_DIST, (cargo_objp->radius+CARGO_RADIUS_DELTA)) ) {
  1539.  
  1540.         // check if player is facing cargo, do not proceed with inspection if not
  1541.         vm_vec_normalized_dir(&vec_to_cargo, &cargo_objp->pos, &Player_obj->pos);
  1542.         dot = vm_vec_dot(&vec_to_cargo, &Player_obj->orient.vec.fvec);
  1543.         if ( dot < CARGO_MIN_DOT_TO_REVEAL ) {
  1544.             if ( !(cargo_sp->flags & SF_SCANNABLE) )
  1545.                 strcpy(outstr,XSTR( "cargo: <unknown>", 86));
  1546.             else
  1547.                 strcpy(outstr,XSTR( "not scanned", 87));
  1548.             hud_targetbox_end_flash(TBOX_FLASH_CARGO);
  1549.             Player->cargo_inspect_time = 0;
  1550.             return 1;
  1551.         }
  1552.  
  1553.         // player is facing the cargo, and withing range, so proceed with inspection
  1554.         if ( hud_sensors_ok(Player_ship, 0) ) {
  1555.             Player->cargo_inspect_time += fl2i(frametime*1000+0.5f);
  1556.         }
  1557.  
  1558.         if ( !(cargo_sp->flags & SF_SCANNABLE) )
  1559.             strcpy(outstr,XSTR( "cargo: inspecting", 88));
  1560.         else
  1561.             strcpy(outstr,XSTR( "scanning", 89));
  1562.  
  1563.         if ( Player->cargo_inspect_time > cargo_sip->scan_time ) {
  1564.             ship_do_cargo_revealed( cargo_sp );
  1565.             snd_play( &Snds[SND_CARGO_REVEAL], 0.0f );
  1566.             Player->cargo_inspect_time = 0;
  1567.         }
  1568.     } else {
  1569.         if ( !(cargo_sp->flags & SF_SCANNABLE) )
  1570.             strcpy(outstr,XSTR( "cargo: <unknown>", 86));
  1571.         else
  1572.             strcpy(outstr,XSTR( "not scanned", 87));
  1573.     }
  1574.  
  1575.     return 1;
  1576. }
  1577.  
  1578. /**
  1579.  * @return 1 if player should display outstr on HUD; 0 if don't display cargo on HUD
  1580.  */
  1581. int player_inspect_cap_subsys_cargo(float frametime, char *outstr)
  1582. {
  1583.     object      *cargo_objp;
  1584.     ship            *cargo_sp;
  1585.     ship_info   *cargo_sip;
  1586.     vec3d       vec_to_cargo;
  1587.     float           dot;
  1588.     ship_subsys *subsys;
  1589.  
  1590.     outstr[0] = 0;
  1591.     subsys = Player_ai->targeted_subsys;
  1592.  
  1593.     if ( subsys == NULL ) {
  1594.         return 0;
  1595.     }
  1596.  
  1597.     cargo_objp = &Objects[Player_ai->target_objnum];
  1598.     Assert(cargo_objp->type == OBJ_SHIP);
  1599.     cargo_sp = &Ships[cargo_objp->instance];
  1600.     cargo_sip = &Ship_info[cargo_sp->ship_info_index];
  1601.  
  1602.     // don't do any sort of scanning thing unless capship has a non-"nothing" cargo
  1603.     // this compensates for changing the "no display" index from -1 to 0
  1604.     if (subsys->subsys_cargo_name == 0) {
  1605.         return 0;
  1606.     }
  1607.  
  1608.     // don't scan cargo on turrets, radar, etc.  only the majors: fighterbay, sensor, engines, weapons, nav, comm
  1609.     if (!valid_cap_subsys_cargo_list(subsys->system_info->subobj_name)) {
  1610.         return 0;
  1611.     }
  1612.  
  1613.     // if cargo is already revealed
  1614.     if (subsys->flags & SSF_CARGO_REVEALED) {
  1615.         if ( !(cargo_sp->flags & SF_SCANNABLE) ) {
  1616.             char *cargo_name = Cargo_names[subsys->subsys_cargo_name & CARGO_INDEX_MASK];
  1617.  
  1618.             if ( cargo_name[0] == '#' ) {
  1619.                 sprintf(outstr, XSTR("passengers: %s", 83), cargo_name+1 );
  1620.             } else {
  1621.                 sprintf(outstr,XSTR("cargo: %s", 84), cargo_name );
  1622.             }
  1623.         } else {
  1624.             strcpy(outstr, XSTR( "Scanned", 85) );
  1625.         }
  1626.    
  1627.         // always bash cargo_inspect_time to 0 since AI ships can reveal cargo that we
  1628.         // are in the process of scanning
  1629.         Player->cargo_inspect_time = 0;
  1630.  
  1631.         return 1;
  1632.     }
  1633.  
  1634.     // see if player is within inspection range [ok for subsys]
  1635.     vec3d   subsys_pos;
  1636.     float       subsys_rad;
  1637.     int     subsys_in_view, x, y;
  1638.     float scan_dist;
  1639.  
  1640.     get_subsystem_world_pos(cargo_objp, Player_ai->targeted_subsys, &subsys_pos);
  1641.     subsys_rad = subsys->system_info->radius;
  1642.  
  1643.     // Goober5000
  1644.     if (cargo_sip->flags & SIF_HUGE_SHIP) {
  1645.         scan_dist = MAX(CAP_CARGO_REVEAL_MIN_DIST, (subsys_rad + CAPITAL_CARGO_RADIUS_DELTA));
  1646.     } else {
  1647.         scan_dist = MAX(CARGO_REVEAL_MIN_DIST, (subsys_rad + CARGO_RADIUS_DELTA));
  1648.     }
  1649.  
  1650.     if ( Player_ai->current_target_distance < scan_dist ) {
  1651.  
  1652.         // check if player is facing cargo, do not proceed with inspection if not
  1653.         vm_vec_normalized_dir(&vec_to_cargo, &subsys_pos, &Player_obj->pos);
  1654.         dot = vm_vec_dot(&vec_to_cargo, &Player_obj->orient.vec.fvec);
  1655.         int hud_targetbox_subsystem_in_view(object *target_objp, int *sx, int *sy);
  1656.         subsys_in_view = hud_targetbox_subsystem_in_view(cargo_objp, &x, &y);
  1657.  
  1658.         if ( (dot < CARGO_MIN_DOT_TO_REVEAL) || (!subsys_in_view) ) {
  1659.             if ( !(cargo_sp->flags & SF_SCANNABLE) )
  1660.                 strcpy(outstr,XSTR( "cargo: <unknown>", 86));
  1661.             else
  1662.                 strcpy(outstr,XSTR( "not scanned", 87));
  1663.             hud_targetbox_end_flash(TBOX_FLASH_CARGO);
  1664.             Player->cargo_inspect_time = 0;
  1665.             return 1;
  1666.         }
  1667.  
  1668.         // player is facing the cargo, and within range, so proceed with inspection
  1669.         if ( hud_sensors_ok(Player_ship, 0) ) {
  1670.             Player->cargo_inspect_time += fl2i(frametime*1000+0.5f);
  1671.         }
  1672.  
  1673.         if ( !(cargo_sp->flags & SF_SCANNABLE) )
  1674.             strcpy(outstr,XSTR( "cargo: inspecting", 88));
  1675.         else
  1676.             strcpy(outstr,XSTR( "scanning", 89));
  1677.  
  1678.         if ( Player->cargo_inspect_time > cargo_sip->scan_time ) {
  1679.             ship_do_cap_subsys_cargo_revealed( cargo_sp, subsys, 0);
  1680.             snd_play( &Snds[SND_CARGO_REVEAL], 0.0f );
  1681.             Player->cargo_inspect_time = 0;
  1682.         }
  1683.     } else {
  1684.         if ( !(cargo_sp->flags & SF_SCANNABLE) )
  1685.             strcpy(outstr,XSTR( "cargo: <unknown>", 86));
  1686.         else
  1687.             strcpy(outstr,XSTR( "not scanned", 87));
  1688.     }
  1689.  
  1690.     return 1;
  1691. }
  1692.  
  1693.  
  1694. /**
  1695.  * Get the maximum weapon range for the player (of both primary and secondary)
  1696.  * @return Maximum weapon range
  1697.  */
  1698. float   player_farthest_weapon_range()
  1699. {
  1700.     float prange,srange;
  1701.  
  1702.     hud_get_best_primary_bank(&prange);
  1703.     srange=ship_get_secondary_weapon_range(Player_ship);
  1704.  
  1705.     return MAX(prange,srange);
  1706. }
  1707.  
  1708. /**
  1709.  * Determine text name for the weapon that killed the player.
  1710.  *
  1711.  * @param weapon_info_index Weapon type that killed the player (can be -1 if no weapon involved)
  1712.  * @param killer_species Species of ship that fired weapon
  1713.  * @param weapon_name (Output parameter) Stores weapon name generated in this function
  1714.  */
  1715. void player_generate_killer_weapon_name(int weapon_info_index, int killer_species, char *weapon_name)
  1716. {
  1717.     if ( weapon_info_index < 0 ) {
  1718.         return;
  1719.     }
  1720.  
  1721. #ifndef NDEBUG
  1722.     if ( Show_killer_weapon || (killer_species == Ship_info[Player_ship->ship_info_index].species) ) {
  1723. #else
  1724.     if (killer_species == Ship_info[Player_ship->ship_info_index].species) {
  1725. #endif
  1726.         strcpy(weapon_name, Weapon_info[weapon_info_index].name);
  1727.     } else {
  1728.         if ( Weapon_info[weapon_info_index].subtype == WP_MISSILE ) {
  1729.             strcpy(weapon_name, XSTR( "missile", 90));
  1730.         } else {
  1731.             strcpy(weapon_name, XSTR( "laser fire", 91));
  1732.         }
  1733.     }
  1734. }
  1735.  
  1736. /**
  1737.  * Generates the message for death of a player given the information stored in the player object.
  1738.  */
  1739. void player_generate_death_message(player *player_p)
  1740. {
  1741.     char weapon_name[NAME_LENGTH];
  1742.     weapon_name[0] = 0;
  1743.     SCP_string &msg = player_p->death_message;
  1744.     int ship_index;
  1745.  
  1746.     player_generate_killer_weapon_name(player_p->killer_weapon_index, player_p->killer_species, weapon_name);
  1747.  
  1748.     switch (player_p->killer_objtype)
  1749.     {
  1750.         case OBJ_SHOCKWAVE:
  1751.             if (weapon_name[0])
  1752.             {
  1753.                 sprintf(msg, XSTR( "%s was killed by a missile shockwave", 92), player_p->callsign);
  1754.             }
  1755.             else
  1756.             {
  1757.                 sprintf(msg, XSTR( "%s was killed by a shockwave from %s exploding", 93), player_p->callsign, player_p->killer_parent_name);
  1758.             }
  1759.             break;
  1760.  
  1761.         case OBJ_WEAPON:
  1762.             Assert(weapon_name[0]);
  1763.  
  1764.             // is this from a friendly ship?
  1765.             ship_index = ship_name_lookup(player_p->killer_parent_name, 1);
  1766.             if ((ship_index >= 0) && (Player_ship != NULL) && (Player_ship->team == Ships[ship_index].team))
  1767.             {
  1768.                 sprintf(msg, XSTR( "%s was killed by friendly fire from %s", 1338), player_p->callsign, player_p->killer_parent_name);
  1769.             }
  1770.             else
  1771.             {
  1772.                 sprintf(msg, XSTR( "%s was killed by %s", 94), player_p->callsign, player_p->killer_parent_name);
  1773.             }
  1774.             break;
  1775.  
  1776.         case OBJ_SHIP:
  1777.             if (player_p->flags & PLAYER_FLAGS_KILLED_BY_EXPLOSION)
  1778.             {
  1779.                 sprintf(msg, XSTR( "%s was killed by a blast from %s exploding", 95), player_p->callsign, player_p->killer_parent_name);
  1780.             }
  1781.             else if (player_p->flags & PLAYER_FLAGS_KILLED_BY_ENGINE_WASH)
  1782.             {
  1783.                 sprintf(msg, XSTR( "%s was killed by engine wash from %s", 1494), player_p->callsign, player_p->killer_parent_name);
  1784.             }
  1785.             else
  1786.             {
  1787.                 sprintf(msg, XSTR( "%s was killed by a collision with %s", 96), player_p->callsign, player_p->killer_parent_name);
  1788.             }
  1789.             break;
  1790.  
  1791.         case OBJ_DEBRIS:
  1792.             sprintf(msg, XSTR( "%s was killed by a collision with debris", 97), player_p->callsign);
  1793.             break;
  1794.  
  1795.         case OBJ_ASTEROID:
  1796.             sprintf(msg, XSTR( "%s was killed by a collision with an asteroid", 98), player_p->callsign);
  1797.             break;
  1798.  
  1799.         case OBJ_BEAM:
  1800.             if (strlen(player_p->killer_parent_name) <= 0)
  1801.             {
  1802.                 Warning(LOCATION, "Killer_parent_name not specified for beam!");
  1803.                 sprintf(msg, XSTR( "%s was killed by a beam from an unknown source", 1081), player_p->callsign);
  1804.             }
  1805.             else
  1806.             {
  1807.                 // is this from a friendly ship?
  1808.                 ship_index = ship_name_lookup(player_p->killer_parent_name, 1);
  1809.                 if ((ship_index >= 0) && (Player_ship != NULL) && (Player_ship->team == Ships[ship_index].team))
  1810.                 {
  1811.                     sprintf(msg, XSTR( "%s was destroyed by friendly beam fire from %s", 1339), player_p->callsign, player_p->killer_parent_name);
  1812.                 }
  1813.                 else
  1814.                 {
  1815.                     sprintf(msg, XSTR( "%s was destroyed by a beam from %s", 1082), player_p->callsign, player_p->killer_parent_name);
  1816.                 }          
  1817.             }
  1818.             break;
  1819.  
  1820.         default:
  1821.             sprintf(msg, XSTR( "%s was killed by unknown causes", 99), player_p->callsign);
  1822.             break;
  1823.     }
  1824. }
  1825.  
  1826. /**
  1827.  * Display what/who killed the player
  1828.  */
  1829. void player_show_death_message()
  1830. {
  1831.     SCP_string &msg = Player->death_message;
  1832.  
  1833.     // make sure we don't already have a death message
  1834.     if (msg.empty())
  1835.     {
  1836.         // check if player killed self
  1837.         if (Player->flags & PLAYER_KILLED_SELF)
  1838.         {
  1839.             // reasons he killed himself
  1840.             if (Player->flags & PLAYER_FLAGS_KILLED_SELF_SHOCKWAVE)
  1841.             {
  1842.                 msg = XSTR("You have killed yourself with a shockwave from your own weapon", 1421);
  1843.             }
  1844.             else if (Player->flags & PLAYER_FLAGS_KILLED_SELF_MISSILES)
  1845.             {
  1846.                 msg = XSTR("You have killed yourself with your own missiles", 1422);
  1847.             }
  1848.             else
  1849.             {
  1850.                 msg = XSTR("You have killed yourself", 100);
  1851.             }
  1852.    
  1853.             Player->flags &= ~PLAYER_KILLED_SELF;
  1854.         }
  1855.         else
  1856.         {
  1857.             player_generate_death_message(Player);
  1858.         }
  1859.     }
  1860.     color col;
  1861.     gr_init_color(&col, 255, 0, 0);
  1862.     // display the message
  1863.     HUD_fixed_printf(30.0f, col, const_cast<char *>(msg.c_str()));
  1864. }
  1865.  
  1866. void player_set_next_all_alone_msg_timestamp()
  1867. {
  1868.     Player->check_for_all_alone_msg=timestamp(30000);
  1869. }
  1870.  
  1871. /**
  1872.  * Maybe play message from Terran Command 'You're all alone now, pilot'
  1873.  */
  1874. void player_maybe_play_all_alone_msg()
  1875. {
  1876.     if ( Game_mode & GM_MULTIPLAYER ){
  1877.         return;
  1878.     }
  1879.  
  1880.     if ( !Player_all_alone_msg_inited ) {
  1881.         player_init_all_alone_msg();
  1882.         Player_all_alone_msg_inited=1;
  1883.         return;
  1884.     }
  1885.  
  1886.     if ( Player->flags & PLAYER_FLAGS_NO_CHECK_ALL_ALONE_MSG ) {
  1887.         return;
  1888.     }
  1889.  
  1890.     // only check every N seconds
  1891.     if ( !timestamp_elapsed(Player->check_for_all_alone_msg) ) {
  1892.         return;
  1893.     }
  1894.  
  1895.     player_set_next_all_alone_msg_timestamp();
  1896.    
  1897.     // at least one primary objective must be not complete (but not failed)
  1898.     if ( !mission_goals_incomplete(PRIMARY_GOAL) ) {
  1899.         Player->flags |= PLAYER_FLAGS_NO_CHECK_ALL_ALONE_MSG;
  1900.         return;
  1901.     }
  1902.  
  1903.     // there must be no reinforcements available, hold off on message
  1904.     if ( (Player_ship != NULL) && hud_squadmsg_reinforcements_available(Player_ship->team) ) {
  1905.         return;
  1906.     }
  1907.  
  1908.     // there must be no ships present that are on the same team as the player
  1909.     ship_obj *so;
  1910.     object  *objp;
  1911.  
  1912.     for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
  1913.         objp = &Objects[so->objnum];
  1914.  
  1915.         if ( objp == Player_obj ) {
  1916.             continue;
  1917.         }
  1918.  
  1919.         if ( Ships[objp->instance].team == Player_ship->team ) {
  1920.             int ship_type = Ship_info[Ships[objp->instance].ship_info_index].class_type;
  1921.             if ( ship_type != -1 && (Ship_types[ship_type].message_bools & STI_MSG_COUNTS_FOR_ALONE) ) {
  1922.                 return;
  1923.             }
  1924.         }
  1925.     }
  1926.  
  1927.     // met all the requirements, now only play 50% of the time :)
  1928.     if ( rand()&1 ) {
  1929.         message_send_builtin_to_player(MESSAGE_ALL_ALONE, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1);
  1930.     }
  1931.     Player->flags |= PLAYER_FLAGS_NO_CHECK_ALL_ALONE_MSG;
  1932. }
  1933.  
  1934.  
  1935. void player_set_padlock_state()
  1936. {
  1937.     if ( check_control(PADLOCK_UP) ) {
  1938.         chase_slew_angles.h = 0.0f;
  1939.         chase_slew_angles.p = -PI_2;
  1940.         Viewer_mode |= VM_PADLOCK_UP;
  1941.         return;
  1942.     }
  1943.     if ( check_control(PADLOCK_DOWN) ) {
  1944.         chase_slew_angles.h = -PI;
  1945.         chase_slew_angles.p = 0.0f;
  1946.         Viewer_mode |= VM_PADLOCK_REAR;
  1947.         return;
  1948.     }
  1949.  
  1950.     if ( check_control(PADLOCK_RIGHT) ) {
  1951.         chase_slew_angles.h = PI_2;
  1952.         chase_slew_angles.p = 0.0f;
  1953.         Viewer_mode |= VM_PADLOCK_RIGHT;
  1954.         return;
  1955.     }
  1956.  
  1957.     if ( check_control(PADLOCK_LEFT) ) {
  1958.         chase_slew_angles.h = -PI_2;
  1959.         chase_slew_angles.p = 0.0f;
  1960.         Viewer_mode |= VM_PADLOCK_LEFT;
  1961.         return;
  1962.     }
  1963.  
  1964.     if ( Viewer_mode & VM_PADLOCK_ANY ) {
  1965.         // clear padlock views and center the view once
  1966.         // the player lets go of an orthogonal padlock command
  1967.         Viewer_mode &= ~(VM_PADLOCK_ANY);
  1968.         chase_slew_angles.h = 0.0f;
  1969.         chase_slew_angles.p = 0.0f;
  1970.     }
  1971. }
  1972.  
  1973. void player_get_padlock_orient(matrix *eye_orient)
  1974. {
  1975.     Assert(Viewer_mode & VM_PADLOCK_ANY);
  1976.  
  1977.     matrix old_eye_orient;
  1978.     old_eye_orient = *eye_orient;
  1979.  
  1980.     if ( Viewer_mode & VM_PADLOCK_UP ) {
  1981.         eye_orient->vec.fvec = old_eye_orient.vec.uvec;
  1982.         vm_vec_copy_scale( &eye_orient->vec.uvec, &old_eye_orient.vec.fvec, -1.0f );
  1983.     } else if ( Viewer_mode & VM_PADLOCK_REAR ) {
  1984.         vm_vec_negate(&eye_orient->vec.fvec);
  1985.         vm_vec_negate(&eye_orient->vec.rvec);
  1986.     } else if ( Viewer_mode & VM_PADLOCK_LEFT ) {
  1987.         vm_vec_copy_scale( &eye_orient->vec.fvec, &old_eye_orient.vec.rvec, -1.0f );
  1988.         eye_orient->vec.rvec = old_eye_orient.vec.fvec;
  1989.     } else if ( Viewer_mode & VM_PADLOCK_RIGHT ) {
  1990.         eye_orient->vec.fvec = old_eye_orient.vec.rvec;
  1991.         vm_vec_copy_scale( &eye_orient->vec.rvec, &old_eye_orient.vec.fvec, -1.0f );
  1992.     } else {
  1993.         Int3();
  1994.     }
  1995. }
  1996.  
  1997. void player_display_padlock_view()
  1998. {
  1999.     int padlock_view_index=0;
  2000.  
  2001.     if ( Viewer_mode & VM_PADLOCK_UP ) {
  2002.         padlock_view_index = 0;
  2003.     } else if ( Viewer_mode & VM_PADLOCK_REAR ) {
  2004.         padlock_view_index = 1;
  2005.     } else if ( Viewer_mode & VM_PADLOCK_LEFT ) {
  2006.         padlock_view_index = 2;
  2007.     } else if ( Viewer_mode & VM_PADLOCK_RIGHT ) {
  2008.         padlock_view_index = 3;
  2009.     } else {
  2010.         Int3();
  2011.         return;
  2012.     }
  2013.  
  2014.     char str[128];
  2015.  
  2016.     if ( !(Viewer_mode & (VM_CHASE|VM_EXTERNAL)) ) {
  2017.         switch (padlock_view_index) {
  2018.         case 0:
  2019.             strcpy_s(str, XSTR( "top view", 101));  break;
  2020.         case 1:
  2021.             strcpy_s(str, XSTR( "rear view", 102)); break;
  2022.         case 2:
  2023.             strcpy_s(str, XSTR( "left view", 103)); break;
  2024.         case 3:
  2025.             strcpy_s(str, XSTR( "right view", 104));    break;
  2026.             }
  2027.         color col;
  2028.         gr_init_color(&col, 0, 255, 0);
  2029.         HUD_fixed_printf(0.01f, col, str);
  2030.     }
  2031. }
  2032.  
  2033. extern vec3d Dead_camera_pos;
  2034. extern vec3d Dead_player_last_vel;
  2035.  
  2036. #define MIN_DIST_TO_DEAD_CAMERA         50.0f
  2037. /**
  2038.  * Get the player's eye position and orient
  2039.  */
  2040. camid player_get_cam()
  2041. {
  2042.     static camid player_camera;
  2043.     if(!player_camera.isValid())
  2044.     {
  2045.         player_camera = cam_create("Player camera");
  2046.     }
  2047.  
  2048.     object *viewer_obj = NULL;
  2049.     vec3d eye_pos = vmd_zero_vector;
  2050.     matrix eye_orient = vmd_identity_matrix;
  2051.     vec3d tmp_dir;
  2052.  
  2053.     // if the player object is NULL, return
  2054.     if(Player_obj == NULL){
  2055.         return camid();
  2056.     }
  2057.  
  2058.     // standalone servers can bail here
  2059.     if(Game_mode & GM_STANDALONE_SERVER){
  2060.         return camid();
  2061.     }
  2062.  
  2063.     // if we're not in-mission, don't do this
  2064.     if(!(Game_mode & GM_IN_MISSION)){
  2065.         return camid();
  2066.     }
  2067.  
  2068.     if (Game_mode & GM_DEAD) {
  2069.         vec3d   vec_to_deader, view_pos;
  2070.         float       dist;      
  2071.         if (Player_ai->target_objnum != -1) {
  2072.             int view_from_player = 1;
  2073.  
  2074.             if (Viewer_mode & VM_OTHER_SHIP) {
  2075.                 //  View from target.
  2076.                 viewer_obj = &Objects[Player_ai->target_objnum];
  2077.                 if ( viewer_obj->type == OBJ_SHIP ) {
  2078.                     ship_get_eye( &eye_pos, &eye_orient, viewer_obj );
  2079.                     view_from_player = 0;
  2080.                 }
  2081.             }
  2082.  
  2083.             if ( view_from_player ) {
  2084.                 //  View target from player ship.
  2085.                 viewer_obj = NULL;
  2086.                 eye_pos = Player_obj->pos;
  2087.  
  2088.                 vm_vec_normalized_dir(&tmp_dir, &Objects[Player_ai->target_objnum].pos, &eye_pos);
  2089.                 vm_vector_2_matrix(&eye_orient, &tmp_dir, NULL, NULL);
  2090.             }
  2091.         } else {
  2092.             dist = vm_vec_normalized_dir(&vec_to_deader, &Player_obj->pos, &Dead_camera_pos);
  2093.            
  2094.             if (dist < MIN_DIST_TO_DEAD_CAMERA){
  2095.                 dist += flFrametime * 16.0f;
  2096.             }
  2097.  
  2098.             vm_vec_scale(&vec_to_deader, -dist);
  2099.             vm_vec_add(&Dead_camera_pos, &Player_obj->pos, &vec_to_deader);
  2100.            
  2101.             view_pos = Player_obj->pos;
  2102.  
  2103.             if (!(Game_mode & GM_DEAD_BLEW_UP)) {                              
  2104.             } else if (Player_ai->target_objnum != -1) {
  2105.                 view_pos = Objects[Player_ai->target_objnum].pos;
  2106.             } else {
  2107.                 //  Make camera follow explosion, but gradually slow down.
  2108.                 vm_vec_scale_add2(&Player_obj->pos, &Dead_player_last_vel, flFrametime);
  2109.                 view_pos = Player_obj->pos;            
  2110.             }
  2111.  
  2112.             eye_pos = Dead_camera_pos;
  2113.  
  2114.             vm_vec_normalized_dir(&tmp_dir, &Player_obj->pos, &eye_pos);
  2115.  
  2116.             vm_vector_2_matrix(&eye_orient, &tmp_dir, NULL, NULL);
  2117.             viewer_obj = NULL;
  2118.         }
  2119.     }
  2120.    
  2121.     //  If already blown up, these other modes can override.
  2122.     if (!(Game_mode & (GM_DEAD | GM_DEAD_BLEW_UP))) {
  2123.         if(!(Viewer_mode & VM_FREECAMERA))
  2124.                 viewer_obj = Player_obj;
  2125.  
  2126.         if (Viewer_mode & VM_OTHER_SHIP) {
  2127.             if (Player_ai->target_objnum != -1){
  2128.                 viewer_obj = &Objects[Player_ai->target_objnum];
  2129.             }
  2130.         }
  2131.         if(Viewer_mode & VM_FREECAMERA) {
  2132.                 Viewer_obj = NULL;
  2133.                 return cam_get_current();
  2134.         } else if (Viewer_mode & VM_EXTERNAL) {
  2135.             Assert(viewer_obj != NULL);
  2136.             matrix  tm, tm2;
  2137.  
  2138.             vm_angles_2_matrix(&tm2, &Viewer_external_info.angles);
  2139.             vm_matrix_x_matrix(&tm, &viewer_obj->orient, &tm2);
  2140.  
  2141.             vm_vec_scale_add(&eye_pos, &viewer_obj->pos, &tm.vec.fvec, 2.0f * viewer_obj->radius + Viewer_external_info.distance);
  2142.  
  2143.             vm_vec_sub(&tmp_dir, &viewer_obj->pos, &eye_pos);
  2144.             vm_vec_normalize(&tmp_dir);
  2145.             vm_vector_2_matrix(&eye_orient, &tmp_dir, &viewer_obj->orient.vec.uvec, NULL);
  2146.             viewer_obj = NULL;
  2147.  
  2148.             //  Modify the orientation based on head orientation.
  2149.             compute_slew_matrix(&eye_orient, &Viewer_slew_angles);
  2150.         } else if ( Viewer_mode & VM_CHASE ) {
  2151.             vec3d   move_dir;
  2152.  
  2153.             if ( viewer_obj->phys_info.speed < 0.1 ){
  2154.                 move_dir = viewer_obj->orient.vec.fvec;
  2155.             } else {
  2156.                 move_dir = viewer_obj->phys_info.vel;
  2157.                 vm_vec_normalize_safe(&move_dir);
  2158.             }
  2159.  
  2160.             vm_vec_scale_add(&eye_pos, &viewer_obj->pos, &move_dir, -3.0f * viewer_obj->radius - Viewer_chase_info.distance);
  2161.             vm_vec_scale_add2(&eye_pos, &viewer_obj->orient.vec.uvec, 0.75f * viewer_obj->radius);
  2162.             vm_vec_sub(&tmp_dir, &viewer_obj->pos, &eye_pos);
  2163.             vm_vec_normalize(&tmp_dir);
  2164.  
  2165.             // JAS: I added the following code because if you slew up using
  2166.             // Descent-style physics, eye_dir and Viewer_obj->orient.vec.uvec are
  2167.             // equal, which causes a zero-length vector in the vm_vector_2_matrix
  2168.             // call because the up and the forward vector are the same.   I fixed
  2169.             // it by adding in a fraction of the right vector all the time to the
  2170.             // up vector.
  2171.             vec3d tmp_up = viewer_obj->orient.vec.uvec;
  2172.             vm_vec_scale_add2( &tmp_up, &viewer_obj->orient.vec.rvec, 0.00001f );
  2173.  
  2174.             vm_vector_2_matrix(&eye_orient, &tmp_dir, &tmp_up, NULL);
  2175.             viewer_obj = NULL;
  2176.  
  2177.             //  Modify the orientation based on head orientation.
  2178.             compute_slew_matrix(&eye_orient, &Viewer_slew_angles);
  2179.         } else if ( Viewer_mode & VM_WARP_CHASE ) {
  2180.             Warp_camera.get_info(&eye_pos, NULL);
  2181.  
  2182.             ship * shipp = &Ships[Player_obj->instance];
  2183.  
  2184.  
  2185.             vec3d warp_pos = Player_obj->pos;
  2186.             shipp->warpout_effect->getWarpPosition(&warp_pos);
  2187.             vm_vec_sub(&tmp_dir, &warp_pos, &eye_pos);
  2188.             vm_vec_normalize(&tmp_dir);
  2189.             vm_vector_2_matrix(&eye_orient, &tmp_dir, &Player_obj->orient.vec.uvec, NULL);
  2190.             viewer_obj = NULL;
  2191.         } else {
  2192.             // get an eye position based upon the correct type of object
  2193.             switch(viewer_obj->type)
  2194.             {
  2195.                 case OBJ_SHIP:
  2196.                     // make a call to get the eye point for the player object
  2197.                     ship_get_eye( &eye_pos, &eye_orient, viewer_obj );
  2198.                     break;
  2199.                 case OBJ_OBSERVER:
  2200.                     // make a call to get the eye point for the player object
  2201.                     observer_get_eye( &eye_pos, &eye_orient, viewer_obj );             
  2202.                     break;
  2203.                 default :
  2204.                     Int3();
  2205.             }          
  2206.         }
  2207.     }
  2208.  
  2209.     player_camera.getCamera()->set_position(&eye_pos);
  2210.     player_camera.getCamera()->set_rotation(&eye_orient);
  2211.  
  2212.     return player_camera;
  2213. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement