]> icculus.org git repositories - taylor/freespace2.git/blob - src/playerman/playercontrol.cpp
SDL2 port - stage 2
[taylor/freespace2.git] / src / playerman / playercontrol.cpp
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
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Playerman/PlayerControl.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Routines to deal with player ship movement
16  *
17  * $Log$
18  * Revision 1.6  2004/07/04 11:39:06  taylor
19  * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
20  *
21  * Revision 1.5  2003/05/25 02:30:43  taylor
22  * Freespace 1 support
23  *
24  * Revision 1.4  2002/06/17 06:33:10  relnev
25  * ryan's struct patch for gcc 2.95
26  *
27  * Revision 1.3  2002/06/09 04:41:25  relnev
28  * added copyright header
29  *
30  * Revision 1.2  2002/05/07 03:16:51  theoddone33
31  * The Great Newline Fix
32  *
33  * Revision 1.1.1.1  2002/05/03 03:28:11  root
34  * Initial import.
35  *
36  * 
37  * 33    10/13/99 3:42p Jefff
38  * fixed unnumbered XSTRs
39  * 
40  * 32    9/06/99 9:43p Jefff
41  * skip mission support
42  * 
43  * 31    8/30/99 10:39a Mikeb
44  * Fixed can't-warpout problem.
45  * 
46  * 30    8/29/99 4:52p Andsager
47  * Don't rotate from engine wash when warping out.
48  * 
49  * 29    8/26/99 8:51p Dave
50  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
51  * 
52  * 28    8/03/99 12:06p Dave
53  * Fixed player death messages when a player has killed himself in
54  * multiplayer.
55  * 
56  * 27    7/28/99 1:36p Andsager
57  * Modify cargo1 to include flag CARGO_NO_DEPLETE.  Add sexp
58  * cargo-no-deplete (only for BIG / HUGE).  Modify ship struct to pack
59  * better.
60  * 
61  * 26    7/21/99 8:10p Dave
62  * First run of supernova effect.
63  * 
64  * 25    7/19/99 7:20p Dave
65  * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula
66  * pre-rendering.
67  * 
68  * 24    7/08/99 10:53a Dave
69  * New multiplayer interpolation scheme. Not 100% done yet, but still
70  * better than the old way.
71  * 
72  * 23    7/02/99 3:48p Mikeb
73  * Remove semi-bogus assert. Handle properly.
74  * 
75  * 22    6/16/99 10:21a Dave
76  * Added send-message-list sexpression.
77  * 
78  * 21    6/14/99 5:19p Dave
79  * 
80  * 20    6/09/99 3:26p Dave
81  * Changed zing message for beam weapon deaths.
82  * 
83  * 19    6/04/99 4:13p Johne
84  * Made a potentially unsafe normalize safe.
85  * 
86  * 18    5/21/99 5:03p Andsager
87  * Add code to display engine wash death.  Modify ship_kill_packet
88  * 
89  * 17    5/18/99 10:08a Andsager
90  * Modified single maximum range before blown up to also be multi
91  * friendly.
92  * 
93  * 16    5/17/99 6:03p Dave
94  * Added new timing code. Put in code to allow camera speed zooming.
95  * 
96  * 15    5/11/99 10:16p Andsager
97  * First pass on engine wash effect.  Rotation (control input), damage,
98  * shake.  
99  * 
100  * 14    4/23/99 12:01p Johnson
101  * Added SIF_HUGE_SHIP
102  * 
103  * 13    3/30/99 5:40p Dave
104  * Fixed reinforcements for TvT in multiplayer.
105  * 
106  * 12    3/28/99 5:58p Dave
107  * Added early demo code. Make objects move. Nice and framerate
108  * independant, but not much else. Don't use yet unless you're me :)
109  * 
110  * 11    3/10/99 6:50p Dave
111  * Changed the way we buffer packets for all clients. Optimized turret
112  * fired packets. Did some weapon firing optimizations.
113  * 
114  * 10    2/26/99 6:01p Andsager
115  * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay
116  * 
117  * 9     2/21/99 6:02p Dave
118  * Fixed standalone WSS packets. 
119  * 
120  * 8     1/30/99 1:29a Dave
121  * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot
122  * screen.  Fixed beam weapon death messages.
123  * 
124  * 7     1/14/99 6:06p Dave
125  * 100% full squad logo support for single player and multiplayer.
126  * 
127  * 6     1/12/99 5:45p Dave
128  * Moved weapon pipeline in multiplayer to almost exclusively client side.
129  * Very good results. Bandwidth goes down, playability goes up for crappy
130  * connections. Fixed object update problem for ship subsystems.
131  * 
132  * 5     12/10/98 10:54a Dave
133  * Fixed problem where a mission ended in multiplayer and a player was
134  * dead. There was an Int3() when getting player eye-point.
135  * 
136  * 4     11/12/98 12:13a Dave
137  * Tidied code up for multiplayer test. Put in network support for flak
138  * guns.
139  * 
140  * 3     10/13/98 9:29a Dave
141  * Started neatening up freespace.h. Many variables renamed and
142  * reorganized. Added AlphaColors.[h,cpp]
143  * 
144  * 2     10/07/98 10:53a Dave
145  * Initial checkin.
146  * 
147  * 1     10/07/98 10:50a Dave
148  * 
149  * 219   6/30/98 2:16p Dave
150  * Revised object update system. Removed updates for all weapons. Put
151  * button info back into control info packet.
152  * 
153  * 218   6/09/98 5:17p Lawrance
154  * French/German localization
155  * 
156  * 217   6/09/98 10:31a Hoffoss
157  * Created index numbers for all xstr() references.  Any new xstr() stuff
158  * added from here on out should be added to the end if the list.  The
159  * current list count can be found in FreeSpace.cpp (search for
160  * XSTR_SIZE).
161  * 
162  * 216   6/01/98 11:43a John
163  * JAS & MK:  Classified all strings for localization.
164  * 
165  * 215   5/22/98 11:27a Hoffoss
166  * Fixed the bank when pressed bug where you continue to turn while using
167  * it.
168  * 
169  * 214   5/19/98 8:19p Lawrance
170  * Make mouse invert work
171  * 
172  * 213   5/19/98 12:19p Mike
173  * Cheat codes!
174  * 
175  * 212   5/18/98 2:50p Lawrance
176  * Change text for when warp drive is inoperable
177  * 
178  * 211   5/18/98 1:58a Mike
179  * Make Phoenix not be fired at fighters (but yes bombers).
180  * Improve positioning of ships in guard mode.
181  * Make turrets on player ship not fire near end of support ship docking.
182  * 
183  * 210   5/17/98 1:43a Dave
184  * Eradicated chatbox problems. Remove speed match for observers. Put in
185  * help screens for PXO. Fix messaging and end mission privelges. Fixed
186  * team select screen bugs. Misc UI fixes.
187  * 
188  * 209   5/15/98 2:41p Hoffoss
189  * Made mouse default to off (for flying ship) and fixed some new pilot
190  * init bugs.
191  * 
192  * 208   5/15/98 1:14p Hoffoss
193  * Added a deadzone to ends of abs throttle range.
194  * 
195  * 207   5/14/98 5:32p Hoffoss
196  * Improved axis binding code some more.
197  * 
198  * 206   5/13/98 11:55a Frank
199  * Fixed bug with axis 2 and 3 not working correctly.
200  * 
201  * 205   5/13/98 1:17a Hoffoss
202  * Added joystick axes configurability.
203  * 
204  * 204   5/11/98 5:29p Hoffoss
205  * Added mouse button mapped to joystick button support.
206  * 
207  * 203   5/11/98 12:01p Hoffoss
208  * Changed mouse sensitivity code to approx. double top limit while
209  * keeping low limit the same.
210  * 
211  * 202   5/09/98 4:52p Lawrance
212  * Implement padlock view (up/rear/left/right)
213  * 
214  * 201   5/08/98 5:35p Lawrance
215  * only allow cargo inspection if sensors are ok
216  * 
217  * 200   5/08/98 5:31p Hoffoss
218  * Isolated the joystick force feedback code more from dependence on other
219  * libraries.
220  * 
221  * 199   5/07/98 6:58p Hoffoss
222  * Made changes to mouse code to fix a number of problems.
223  * 
224  * 198   5/06/98 12:02a Hoffoss
225  * Fixed throttle problems with joysticks.
226  * 
227  * 197   5/05/98 8:38p Hoffoss
228  * Added sensitivity adjustment to options menu and made it save to pilot
229  * file.
230  * 
231  * 196   5/05/98 1:33p Hoffoss
232  * Changed mouse formula around a little.
233  * 
234  * 195   5/04/98 11:08p Hoffoss
235  * Expanded on Force Feedback code, and moved it all into Joy_ff.cpp.
236  * Updated references everywhere to it.
237  * 
238  * 194   5/01/98 5:45p Hoffoss
239  * Made further improvements to the mouse code.
240  * 
241  * 193   5/01/98 4:24p Lawrance
242  * Play error sound if player tries to engage afterburner when none are on
243  * ship
244  * 
245  * 192   5/01/98 1:14p Hoffoss
246  * Changed mouse usage so directInput is only used for release version.
247  * 
248  * 191   4/30/98 9:12p Hoffoss
249  * Fixed a questionable precidence issue to be known.
250  * 
251  * 190   4/30/98 5:40p Hoffoss
252  * Added mouse as a supported control to fly the ship.
253  * 
254  * 189   4/29/98 1:44p Lawrance
255  * Fix bug that would cause primaries to be linked when returning from a
256  * menu in-game (if primaries were linked at mission start)
257  * 
258  * 188   4/25/98 4:36p Lawrance
259  * init auto_advance field when pilot is created
260  * 
261  * 187   4/25/98 3:49p Lawrance
262  * Save briefing auto-advance pref
263  * 
264  * 186   4/18/98 9:52p Mike
265  * Don't restore player flags from save file in training mission, as it
266  * enables auto-target to persist.
267  * 
268  * 185   4/17/98 11:16a Allender
269  * use short callsign when showing player name in cargo area of target
270  * info box
271  * 
272  * 184   4/09/98 12:32a Lawrance
273  * Fix bugs related to multiple screams from same ship, builtin messages
274  * playing after screams, or praising while severly damaged.
275  * 
276  * 183   4/08/98 7:11p Dave
277  * Fix auto-targeting and auto-matching reset after player respawn.
278  * Removed incorrect call to furball update if in a non-furball game.
279  * 
280  * 182   4/08/98 11:11a Hoffoss
281  * Fixed some bugs that showed up due to fixing other bugs the other day
282  * with controls.
283  * 
284  * 181   4/08/98 10:46a Lawrance
285  * fix uninitialized data warning
286  * 
287  * 180   4/06/98 11:00a Hoffoss
288  * Fixed bank when pressed key to also be usable with keyboard yaw
289  * controls.
290  * 
291  * 179   4/05/98 7:43p Lawrance
292  * fix up saving/restoring of link status and auto-target/match-speed.
293  * 
294  * 178   4/01/98 7:42p Lawrance
295  * Enable auto-targeting by default when a new pilot is created.
296  * 
297  * 177   4/01/98 1:31p Allender
298  * don't play all alone message in multiplayer
299  * 
300  * 176   3/31/98 11:47p Lawrance
301  * 
302  * 175   3/31/98 5:18p John
303  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
304  * bunch of debug stuff out of player file.  Made model code be able to
305  * unload models and malloc out only however many models are needed.
306  *  
307  * 
308  * 174   3/30/98 12:19a Lawrance
309  * Make scan time a property of a ship
310  * 
311  * 173   3/24/98 4:25p Lawrance
312  * Make finding out if player killed self easier and more reliable
313  * 
314  * 172   3/23/98 12:28p Hoffoss
315  * Fixed +/-5% throttle controls to actually do 5%, rather than whatever
316  * the heck it was doing before.
317  * 
318  * 171   3/21/98 3:35p Lawrance
319  * Fix joystick turning in external view
320  * 
321  * 170   3/19/98 5:35p Lawrance
322  * Correctly inform player if killed by ship explosion.
323  * 
324  * 169   3/18/98 6:04p Lawrance
325  * change text for warp destroyed message
326  * 
327  * 168   3/18/98 12:03p John
328  * Marked all the new strings as externalized or not.
329  * 
330  * 167   3/16/98 5:54p Lawrance
331  * Play cargo scanning sound
332  * 
333  * 166   3/14/98 4:58p Lawrance
334  * surpress HUD messages for auto-match speed
335  * 
336  * 165   3/11/98 12:11p Mike
337  * Increase max speed to enable warpout even if ship has little energy
338  * directed towards engines.
339  * 
340  * 164   3/11/98 11:19a Mike
341  * Quick fix since I completely broke warping out.
342  * 
343  * 163   3/11/98 9:08a Mike
344  * Intermediate checkin to resolve link errors.  Working on forcing
345  * player's engine energy high enough to enable warpout.
346  * 
347  * 162   3/10/98 5:08p Allender
348  * fixed up multiplayer death messages (I hope).  changes in object update
349  * packets
350  * 
351  * 161   3/09/98 4:30p Allender
352  * multiplayer secondary weapon changes.  red-alert and cargo-known-delay
353  * sexpressions.  Add time cargo revealed to ship structure
354  * 
355  * 160   3/07/98 3:49p Lawrance
356  * store killer species in player struct
357  * 
358  * 159   3/05/98 10:16p Lawrance
359  * Add 'save_flags' to player struct to save/restore certain player flags
360  * cleanly.
361  * 
362  * 158   2/28/98 7:03p Lawrance
363  * get slew working in any view
364  * 
365  * 157   2/26/98 12:33a Lawrance
366  * Added back slew mode,  lots of changes to external and chase views.
367  * 
368  * 156   2/23/98 6:54p Lawrance
369  * Make interface to real-time voice more generic and useful.
370  * 
371  * 155   2/22/98 4:30p John
372  * More string externalization classification
373  * 
374  * 154   2/22/98 3:56p Lawrance
375  * Make external view a toggle like chase view.  Allow extenal view to
376  * override chase view.
377  * 
378  * 153   2/22/98 2:48p John
379  * More String Externalization Classification
380  * 
381  * 152   2/20/98 8:30p Lawrance
382  * Play 'all alone' message when player is last ship left and primary
383  * objective is incomplete.
384  * 
385  * 151   2/19/98 5:30p Lawrance
386  * Allow player turrets to fire automatically.
387  * 
388  * 150   2/16/98 7:33p Lawrance
389  * add function to determine range of current secondary weapon
390  * 
391  * 149   2/15/98 9:51p Sandeep
392  * AL: Check for 'Player 1' as a ship name when displaying the death
393  * message
394  * 
395  * 148   2/14/98 4:41p Lawrance
396  * Add some new fields to keep track of how the player died.
397  * 
398  * 147   2/11/98 9:53a John
399  * Put in a real fix to the bug with not resetting joystick time between
400  * missions
401  * 
402  * 146   2/10/98 9:29p Mike
403  * Hack-fix bug with joystick reading not being allowed in 2nd playthrough
404  * of a mission until time exceeds duration of previous mission.
405  * 
406  * 145   2/10/98 1:15p John
407  * Made the joystick only read at 10Hz max
408  * 
409  * 144   2/06/98 4:31p Lawrance
410  * revamp radar code, allow dim radar dots
411  * 
412  * 143   2/06/98 3:48p Allender
413  * added code to clear out control info for multiplayer clients so they
414  * don't accidentally start firing all the time
415  * 
416  * 142   2/06/98 12:13a Lawrance
417  * Don't play throttle sounds when using analog throttle.
418  * 
419  * 141   2/05/98 10:41a Lawrance
420  * Scale frequency of 'good kill' messages by skill level.
421  * 
422  * 140   2/02/98 4:24p Lawrance
423  * Only inspect cargo and transport in single player.
424  * 
425  * 139   2/02/98 4:12p Allender
426  * fixed bug I introduced with multiplayer callsigns displaying in cargo
427  * spot
428  * 
429  * $NoKeywords: $
430 */
431
432 #include "pstypes.h"
433 #include "physics.h"
434 #include "key.h"
435 #include "joy.h"
436 #include "joy_ff.h"
437 #include "mouse.h"
438 #include "fix.h"
439 #include "floating.h"
440 #include "object.h"
441 #include "player.h"
442 #include "hud.h"
443 #include "hudtarget.h"
444 #include "hudtargetbox.h"
445 #include "ship.h"
446 #include "freespace.h"
447 #include "controlsconfig.h"
448 #include "sound.h"
449 #include "gamesnd.h"
450 #include "missionshipchoice.h"
451 #include "afterburner.h"
452 #include "timer.h"
453 #include "gamesequence.h"
454 #include "missionmessage.h"
455 #include "multiutil.h"
456 #include "linklist.h"
457 #include "missiongoals.h"
458 #include "hudsquadmsg.h"
459 #include "multi_obj.h"
460 #include "observer.h"
461
462 ////////////////////////////////////////////////////////////
463 // Global object and other interesting player type things
464 ////////////////////////////////////////////////////////////
465 player  Players[MAX_PLAYERS];
466
467 int             Player_num;
468 player  *Player = NULL;
469
470 physics_info Descent_physics;                   // used when we want to control the player like the descent ship
471
472 ////////////////////////////////////////////////////////////
473 // Module data
474 ////////////////////////////////////////////////////////////
475 static int Player_all_alone_msg_inited=0;       // flag used for initalizing a player-specific voice msg
476
477 #ifndef NDEBUG
478         int Show_killer_weapon = 0;
479         DCF_BOOL( show_killer_weapon, Show_killer_weapon );
480 #endif
481
482 void playercontrol_read_stick(int *axis, float frame_time);
483 void player_set_padlock_state();
484
485 //      Slew angles chase towards zero like they're on a spring.
486 //      When furthest away, move fastest.
487 //      Minimum speed set so that doesn't take too long.
488 //      When gets close, clamps to zero.
489 void chase_angles_to_zero(angles *ap)
490 {
491         float   k1, k2;
492         float   sk;
493
494         //      Make sure we actually need to do all this math.
495         if ((ap->p == 0.0f) && (ap->h == 0.0f))
496                 return;
497
498         //      This is what we'll scale each value by.
499         sk = 1.0f - 2*flFrametime;
500
501         //      These are the amounts that will be subtracted from pitch and heading.
502         //      They are only needed to make sure we aren't moving too slowly.
503         k1 = fl_abs(ap->p * (1.0f - sk));
504         k2 = fl_abs(ap->h * (1.0f - sk));
505
506         //      See if the larger dimension of movement is too small.
507         // If so, boost amount of movement in both dimensions.
508         if (k1 >= k2) {
509                 if (k1 < flFrametime)
510                         sk = 1.0f - (1.0f - sk) * flFrametime/k1;
511         } else if (k2 > k1) {
512                 if (k2 < flFrametime)
513                         sk = 1.0f - (1.0f - sk) * flFrametime/k2;
514         }
515
516         //      It's possible we made the scale factor negative above.
517         if (sk < 0.0f)
518                 sk = 0.0f;
519
520         ap->p *= sk;
521         ap->h *= sk;
522
523         //      If we're very close, put ourselves at goal.
524         if ((fl_abs(ap->p) < 0.005f) && (fl_abs(ap->h) < 0.005f)) {
525                 ap->p = 0.0f;
526                 ap->h = 0.0f;
527         }
528
529         //      Update Viewer_mode based on whether we're looking dead ahead.   
530         if ((ap->p == 0.0f) && (ap->b == 0.0f) && (ap->h == 0.0f))
531                 Viewer_mode &= ~VM_SLEWED;
532         else
533                 Viewer_mode |= VM_SLEWED;
534
535 }
536
537 angles  Viewer_slew_angles_delta;
538 angles  Viewer_external_angles_delta;
539
540 void view_modify(angles *ma, angles *da, float minv, float maxv, int slew, float frame_time)
541 {
542         int axis[JOY_NUM_AXES];
543         float   t;
544
545         if ( (!slew) && (Viewer_mode & VM_EXTERNAL) && (Viewer_mode & VM_EXTERNAL_CAMERA_LOCKED) ) {
546                 return;
547         }
548
549         if ( Viewer_mode & VM_EXTERNAL ) {
550                 t = (check_control_timef(YAW_LEFT) - check_control_timef(YAW_RIGHT)) / 16.0f;
551         } else {
552                 t = (check_control_timef(YAW_RIGHT) - check_control_timef(YAW_LEFT)) / 16.0f;
553         }
554
555         if (t != 0.0f)
556                 da->h += t;
557         else
558                 da->h = 0.0f;
559
560         t = (check_control_timef(PITCH_FORWARD) - check_control_timef(PITCH_BACK)) / 16.0f;
561         if (t != 0.0f)
562                 da->p += t;
563         else
564                 da->p = 0.0f;
565                         
566         da->b = 0.0f;
567
568         playercontrol_read_stick(axis, frame_time);
569
570         if ( Viewer_mode & VM_EXTERNAL ) {
571                 // check the heading on the x axis
572                 da->h -= f2fl( axis[0] );
573
574         } else {
575                 // check the heading on the x axis
576                 da->h += f2fl( axis[0] );
577         }
578
579         // check the pitch on the y axis
580         da->p -= f2fl( axis[1] );
581
582         if (da->h > 1.0f)
583                 da->h = 1.0f;
584         else if (da->h < -1.0f)
585                 da->h = -1.0f;
586
587         if (da->p > 1.0f)
588                 da->p = 1.0f;
589         else if (da->p < -1.0f)
590                 da->p = -1.0f;
591
592         ma->p += da->p * flFrametime;
593         ma->b += da->b * flFrametime;
594         ma->h += da->h * flFrametime;
595
596         if (ma->p > maxv)
597                 ma->p = maxv;
598         else if (ma->p < minv)
599                 ma->p = minv;
600
601         if (ma->h > maxv)
602                 ma->h = maxv;
603         else if (ma->h < minv)
604                 ma->h = minv;
605 }
606
607 //      When PAD0 is pressed, keypad controls viewer direction slewing.
608 void do_view_slew(float frame_time)
609 {
610         view_modify(&Viewer_slew_angles, &Viewer_slew_angles_delta, -PI/3, PI/3, 1, frame_time);
611
612         if ((Viewer_slew_angles.p == 0.0f) && (Viewer_slew_angles.b == 0.0f) && (Viewer_slew_angles.h == 0.0f))
613                 Viewer_mode &= ~VM_SLEWED;
614         else
615                 Viewer_mode |= VM_SLEWED;
616 }
617
618 void do_view_chase(float frame_time)
619 {
620         float t;
621
622         //      Process centering key.
623         if (check_control_timef(VIEW_CENTER)) {
624                 Viewer_chase_info.distance = 0.0f;
625         }
626         
627         t = check_control_timef(VIEW_DIST_INCREASE) - check_control_timef(VIEW_DIST_DECREASE);
628         Viewer_chase_info.distance += t*4;
629         if (Viewer_chase_info.distance < 0.0f)
630                 Viewer_chase_info.distance = 0.0f;
631 }
632
633 float camera_zoom_scale = 1.0f;
634
635 DCF(camera_speed, "")
636 {
637         dc_get_arg(ARG_FLOAT);
638         camera_zoom_scale = Dc_arg_float;
639 }
640
641 void do_view_external(float frame_time)
642 {
643         float   t;
644
645         view_modify(&Viewer_external_info.angles, &Viewer_external_angles_delta, -2*PI, 2*PI, 0, frame_time);
646
647         //      Process centering key.
648         if (check_control_timef(VIEW_CENTER)) {
649                 Viewer_external_info.angles.p = 0.0f;
650                 Viewer_external_info.angles.h = 0.0f;
651                 Viewer_external_info.distance = 0.0f;
652         }
653         
654         t = check_control_timef(VIEW_DIST_INCREASE) - check_control_timef(VIEW_DIST_DECREASE);
655         Viewer_external_info.distance += t*4*camera_zoom_scale;
656         if (Viewer_external_info.distance < 0.0f){
657                 Viewer_external_info.distance = 0.0f;
658         }
659
660         //      Do over-the-top correction.
661
662         if (Viewer_external_info.angles.p > PI)
663                 Viewer_external_info.angles.p = -2*PI + Viewer_external_info.angles.p;
664         else if (Viewer_external_info.angles.p < -PI)
665                 Viewer_external_info.angles.p = 2*PI + Viewer_external_info.angles.p;
666
667         if (Viewer_external_info.angles.h > PI)
668                 Viewer_external_info.angles.h = -2*PI + Viewer_external_info.angles.h;
669         else if (Viewer_external_info.angles.h < -PI)
670                 Viewer_external_info.angles.h = 2*PI + Viewer_external_info.angles.h;
671 }
672
673 // separate out the reading of thrust keys, so we can call this from external
674 // view as well as from normal view
675 void do_thrust_keys(control_info *ci)
676 {
677         ci->forward = check_control_timef(FORWARD_THRUST) - check_control_timef(REVERSE_THRUST);
678 }
679
680 // called by single and multiplayer modes to reset information inside of control info structure
681 void player_control_reset_ci( control_info *ci )
682 {
683         float t1, t2, oldspeed;
684
685         t1 = ci->heading;
686         t2 = ci->pitch;
687         oldspeed = ci->forward_cruise_percent;
688         memset( ci, 0, sizeof(control_info) );
689         ci->heading = t1;
690         ci->pitch = t2;
691         ci->forward_cruise_percent = oldspeed;
692 }
693
694 // Read the 4 joystick axis.  This is its own function
695 // because we only want to read it at a certain rate,
696 // since it takes time.
697
698 static int Joystick_saved_reading[JOY_NUM_AXES];
699 static int Joystick_last_reading = -1;
700
701 void playercontrol_read_stick(int *axis, float frame_time)
702 {
703         int i;
704
705 #ifndef NDEBUG
706         // Make sure things get reset properly between missions.
707         if ( (Joystick_last_reading != -1) && (timestamp_until(Joystick_last_reading) > 1000) ) {
708                 Int3();         // Get John!  John, the joystick last reading didn't get reset btwn levels.
709                 Joystick_last_reading = -1;
710         }
711 #endif
712
713         if ( (Joystick_last_reading == -1)  || timestamp_elapsed(Joystick_last_reading) ) {
714                 // Read the stick
715                 control_get_axes_readings(&Joystick_saved_reading[0], &Joystick_saved_reading[1], &Joystick_saved_reading[2], &Joystick_saved_reading[3], &Joystick_saved_reading[4]);
716                 Joystick_last_reading = timestamp( 1000/10 );   // Read 10x per second, like we did in Descent.
717         }
718
719         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
720                 axis[i] = Joystick_saved_reading[i];
721         }
722
723         if (Use_mouse_to_fly) {
724                 int dx, dy, dz;
725                 float factor;
726
727 //              factor = (float) Mouse_sensitivity + 2.5f;
728 //              factor = factor * factor / frame_time / 1.2f;
729                 factor = (float) Mouse_sensitivity + 1.77f;
730                 factor = factor * factor / frame_time / 0.6f;
731
732                 mouse_get_delta(&dx, &dy, &dz);
733
734                 if ( Invert_axis[0] ) {
735                         dx = -dx;
736                 }
737
738                 if ( Invert_axis[1] ) {
739                         dy = -dy;
740                 }
741
742                 if ( Invert_axis[3] ) {
743                         dz = -dz;
744                 }
745
746                 axis[0] += (int) ((float) dx * factor);
747                 axis[1] += (int) ((float) dy * factor);
748                 axis[3] += (int) ((float) dz * factor);
749         }
750 }
751
752 void read_keyboard_controls( control_info * ci, float frame_time, physics_info *pi )
753 {
754         float kh=0.0f, scaled, newspeed, delta, oldspeed;
755         int axis[JOY_NUM_AXES], ignore_pitch, slew_active=0;
756         static int afterburner_last = 0;
757         static float analog_throttle_last = 9e9f;
758         static int override_analog_throttle = 0; 
759         int ok_to_read_ci_pitch_yaw=1;
760
761         oldspeed = ci->forward_cruise_percent;
762         player_control_reset_ci( ci );
763
764         if ( check_control(VIEW_SLEW) ) {
765                 do_view_slew(frame_time);
766                 slew_active=1;
767         }
768
769         if ( Viewer_mode & VM_EXTERNAL ) {
770                 control_used(VIEW_EXTERNAL);
771                 if ( !(Viewer_mode & VM_EXTERNAL_CAMERA_LOCKED) ) {
772                         ok_to_read_ci_pitch_yaw=0;
773                         slew_active=0;
774                 }
775
776                 do_view_external(frame_time);
777                 do_thrust_keys(ci);
778         }
779
780         if ( !slew_active ) {
781                 if ( Viewer_mode & VM_CHASE ) {
782                         do_view_chase(frame_time);
783                 }
784         }
785         
786         if ( ok_to_read_ci_pitch_yaw ) {
787                 // From keyboard...
788                 if ( check_control(BANK_WHEN_PRESSED) ) {
789                         ci->bank = check_control_timef(BANK_LEFT) + check_control_timef(YAW_LEFT) - check_control_timef(YAW_RIGHT) - check_control_timef(BANK_RIGHT);
790                         ci->heading = 0.0f;
791
792                 } else {
793                         kh = (check_control_timef(YAW_RIGHT) - check_control_timef(YAW_LEFT)) / 8.0f;
794                         if (kh == 0.0f) {
795                                 ci->heading = 0.0f;
796
797                         } else if (kh > 0.0f) {
798                                 if (ci->heading < 0.0f)
799                                         ci->heading = 0.0f;
800
801                         } else {  // kh < 0
802                                 if (ci->heading > 0.0f)
803                                         ci->heading = 0.0f;
804                         }
805
806                         ci->bank = check_control_timef(BANK_LEFT) - check_control_timef(BANK_RIGHT);
807                 }
808
809                 ci->heading += kh;
810
811                 kh = (check_control_timef(PITCH_FORWARD) - check_control_timef(PITCH_BACK)) / 8.0f;
812                 if (kh == 0.0f) {
813                         ci->pitch = 0.0f;
814
815                 } else if (kh > 0.0f) {
816                         if (ci->pitch < 0.0f)
817                                 ci->pitch = 0.0f;
818
819                 } else {  // kh < 0
820                         if (ci->pitch > 0.0f)
821                                 ci->pitch = 0.0f;
822                 }
823
824                 ci->pitch += kh;
825
826                 ci->sideways = (key_down_timef(SDLK_KP_3) - key_down_timef(SDLK_KP_1));
827                 ci->vertical = (key_down_timef(SDLK_KP_PLUS) - key_down_timef(SDLK_KP_ENTER));
828
829                 do_thrust_keys(ci);
830         }
831
832         if ( !slew_active ) {
833                 chase_angles_to_zero(&Viewer_slew_angles);
834         }
835
836         player_set_padlock_state();
837
838         if (!(Game_mode & GM_DEAD)) {
839                 if ( button_info_query(&Player->bi, ONE_THIRD_THROTTLE) ) {
840                         control_used(ONE_THIRD_THROTTLE);
841                         player_clear_speed_matching();
842                         if ( Player->ci.forward_cruise_percent < 33.3f ) {
843                                 snd_play( &Snds[SND_THROTTLE_UP], 0.0f );
844
845                         } else if ( Player->ci.forward_cruise_percent > 33.3f ) {
846                                 snd_play( &Snds[SND_THROTTLE_DOWN], 0.0f );
847                         }
848
849                         Player->ci.forward_cruise_percent = 33.3f;
850                         override_analog_throttle = 1;
851                 }
852
853                 if ( button_info_query(&Player->bi, TWO_THIRDS_THROTTLE) ) {
854                         control_used(TWO_THIRDS_THROTTLE);
855                         player_clear_speed_matching();
856                         if ( Player->ci.forward_cruise_percent < 66.6f ) {
857                                 snd_play( &Snds[SND_THROTTLE_UP], 0.0f );
858
859                         } else if (Player->ci.forward_cruise_percent > 66.6f) {
860                                 snd_play( &Snds[SND_THROTTLE_DOWN], 0.0f );
861                         }
862
863                         Player->ci.forward_cruise_percent = 66.6f;
864                         override_analog_throttle = 1;
865                 }
866
867 //              if ( button_info_query(&Player->bi, PLUS_5_PERCENT_THROTTLE) ) {
868 //                      control_used(PLUS_5_PERCENT_THROTTLE);
869 //                      Player->ci.forward_cruise_percent += (100.0f/Player_ship->current_max_speed);
870 //              }
871
872                 if ( button_info_query(&Player->bi, PLUS_5_PERCENT_THROTTLE) ) {
873                         control_used(PLUS_5_PERCENT_THROTTLE);
874                         Player->ci.forward_cruise_percent += 5.0f;
875                         if (Player->ci.forward_cruise_percent > 100.0f)
876                                 Player->ci.forward_cruise_percent = 100.0f;
877                 }
878
879 //              if ( button_info_query(&Player->bi, MINUS_5_PERCENT_THROTTLE) ) {
880 //                      control_used(MINUS_5_PERCENT_THROTTLE);
881 //                      Player->ci.forward_cruise_percent -= (100.0f/Player_ship->current_max_speed);
882 //              }
883
884                 if ( button_info_query(&Player->bi, MINUS_5_PERCENT_THROTTLE) ) {
885                         control_used(MINUS_5_PERCENT_THROTTLE);
886                         Player->ci.forward_cruise_percent -= 5.0f;
887                         if (Player->ci.forward_cruise_percent < 0.0f)
888                                 Player->ci.forward_cruise_percent = 0.0f;
889                 }
890
891                 if ( button_info_query(&Player->bi, ZERO_THROTTLE) ) {
892                         control_used(ZERO_THROTTLE);
893                         player_clear_speed_matching();
894                         if ( ci->forward_cruise_percent > 0.0f && Player_obj->phys_info.fspeed > 0.5) {
895                                 snd_play( &Snds[SND_ZERO_THROTTLE], 0.0f );
896                         }
897
898                         ci->forward_cruise_percent = 0.0f;
899                         override_analog_throttle = 1;
900                 }
901
902                 if ( button_info_query(&Player->bi, MAX_THROTTLE) ) {
903                         control_used(MAX_THROTTLE);
904                         player_clear_speed_matching();
905                         if ( ci->forward_cruise_percent < 100.0f ) {
906                                 snd_play( &Snds[SND_FULL_THROTTLE], 0.0f );
907                         }
908
909                         ci->forward_cruise_percent = 100.0f;
910                         override_analog_throttle = 1;
911                 }
912
913                 // AL 12-29-97: If afterburner key is down, player should have full forward thrust (even if afterburners run out)
914                 if ( check_control(AFTERBURNER) ) {
915                         ci->forward = 1.0f;
916                 }
917
918                 if ( Player->flags & PLAYER_FLAGS_MATCH_TARGET ) {
919                         if ( (Player_ai->last_target == Player_ai->target_objnum) && (Player_ai->target_objnum != -1) && ( ci->forward_cruise_percent == oldspeed) ) {
920                                 float tspeed, pmax_speed;
921
922                                 tspeed = Objects[Player_ai->target_objnum].phys_info.fspeed;
923
924                                 // maybe need to get speed from docked partner
925                                 if ( tspeed < MATCH_SPEED_THRESHOLD ) {
926                                         ai_info *aip;
927
928                                         Assert(Objects[Player_ai->target_objnum].type == OBJ_SHIP);
929
930                                         aip = &Ai_info[Ships[Objects[Player_ai->target_objnum].instance].ai_index];
931                                         if ( aip->ai_flags & AIF_DOCKED ) {
932                                                 Assert( aip->dock_objnum != -1 );
933                                                 tspeed = Objects[aip->dock_objnum].phys_info.fspeed;
934                                         }
935                                 }
936
937                                 //      Note, if closer than 100 units, scale down speed a bit.  Prevents repeated collisions. -- MK, 12/17/97
938                                 float dist = vm_vec_dist(&Player_obj->pos, &Objects[Player_ai->target_objnum].pos);
939
940                                 if (dist < 100.0f) {
941                                         tspeed = tspeed * (0.5f + dist/200.0f);
942                                 }
943
944                                 //pmax_speed = Ship_info[Ships[Player_obj->instance].ship_info_index].max_speed;
945                                 pmax_speed = Ships[Player_obj->instance].current_max_speed;
946                                 ci->forward_cruise_percent = (tspeed / pmax_speed) * 100.0f;
947                                 override_analog_throttle = 1;
948         //                      if ( ci->forward_cruise_percent > 100.0f )
949         //                              HUD_printf ("Cannot travel that fast.  Setting throttle to full.");
950                                 // mprintf(("forward -- %7.3f\n", ci->forward_cruise_percent));
951
952                         } else
953                                 Player->flags &= ~PLAYER_FLAGS_MATCH_TARGET;
954                 }
955
956 //                      player_read_joystick();
957                 // code to read joystick axis for pitch/heading.  Code to read joystick buttons
958                 // fo1r bank.
959                 if ( !(Game_mode & GM_DEAD) )   {
960                         playercontrol_read_stick(axis, frame_time);
961                 } else {
962                         axis[0] = axis[1] = axis[2] = axis[3] = axis[4] = 0;
963                 }
964
965                 ignore_pitch = FALSE;
966
967                 if (Axis_map_to[JOY_HEADING_AXIS] >= 0) {
968                         // check the heading on the x axis
969                         if ( check_control(BANK_WHEN_PRESSED) ) {
970                                 delta = f2fl( axis[JOY_HEADING_AXIS] );
971                                 if ( (delta > 0.05f) || (delta < -0.05f) ) {
972                                         ci->bank -= delta;
973                                         ignore_pitch = TRUE;
974                                 }
975
976                         } else {
977                                 ci->heading += f2fl( axis[JOY_HEADING_AXIS] );
978                         }
979                 }
980
981                 // check the pitch on the y axis
982                 if (Axis_map_to[JOY_PITCH_AXIS] >= 0)
983                         ci->pitch -= f2fl( axis[JOY_PITCH_AXIS] );
984
985                 if (Axis_map_to[JOY_BANK_AXIS] >= 0) {
986                         ci->bank -= f2fl( axis[JOY_BANK_AXIS] ) * 1.5f;
987                 }
988
989                 // axis 2 is for throttle
990                 if (Axis_map_to[JOY_ABS_THROTTLE_AXIS] >= 0) {
991                         scaled = (float) axis[JOY_ABS_THROTTLE_AXIS] * 1.2f / (float) F1_0 - 0.1f;  // convert to -0.1 - 1.1 range
992                         oldspeed = ci->forward_cruise_percent;
993
994 //                      scaled = (scaled + 1.0f) / 1.85f;
995                         newspeed = (1.0f - scaled) * 100.0f;
996
997                         delta = analog_throttle_last - newspeed;
998                         if (!override_analog_throttle || (delta < -1.5f) || (delta > 1.5f)) {
999                                 ci->forward_cruise_percent = newspeed;
1000                                 analog_throttle_last = newspeed;
1001                                 override_analog_throttle = 0;
1002 /*
1003                                 // AL 1-5-98: don't play throttle sounds when using analog control
1004
1005                                 if ( (oldspeed < 1.0f) && (newspeed >= 1.0f) )
1006                                         snd_play( &Snds[SND_THROTTLE_UP], 0.0f );
1007                                 else if ( (oldspeed < 66.6f) && (newspeed >= 66.6f) )
1008                                         snd_play( &Snds[SND_THROTTLE_UP], 0.0f );
1009                                 else if ( (oldspeed < 33.3f) && (newspeed >= 33.3f) )
1010                                         snd_play( &Snds[SND_THROTTLE_UP], 0.0f );
1011                                 else if ( (oldspeed > 99.0f) && (newspeed <= 99.0f) )
1012                                         snd_play( &Snds[SND_THROTTLE_DOWN], 0.0f );
1013                                 else if ( (oldspeed > 33.3f) && (newspeed <= 33.3f) )
1014                                         snd_play( &Snds[SND_THROTTLE_DOWN], 0.0f );
1015                                 else if ( (oldspeed > 66.6f) && (newspeed <= 66.6f) )
1016                                         snd_play( &Snds[SND_THROTTLE_DOWN], 0.0f );
1017 */
1018                         }
1019                 }
1020
1021                 if (Axis_map_to[JOY_REL_THROTTLE_AXIS] >= 0)
1022                         ci->forward_cruise_percent += f2fl(axis[JOY_REL_THROTTLE_AXIS]) * 100.0f * frame_time;
1023
1024                 if ( ci->forward_cruise_percent > 100.0f )
1025                         ci->forward_cruise_percent = 100.0f;
1026                 if ( ci->forward_cruise_percent < 0.0f )
1027                         ci->forward_cruise_percent = 0.0f;
1028
1029                 // set up the firing stuff.  Read into control info ala Descent so that weapons will be
1030                 // created during the object simulation phase, and not immediately as was happening before.
1031
1032                 //keyboard: fire the current primary weapon
1033                 if (check_control(FIRE_PRIMARY)) {
1034                         ci->fire_primary_count++;
1035
1036                         // if we're a multiplayer client, set our accum bits now
1037                         // if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && !(Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING)){
1038                         // if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && !(Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING)){
1039                                 // Net_player->s_info.accum_buttons |= OOC_FIRE_PRIMARY;
1040                         // }
1041                 }
1042
1043                 // mouse: fire the current primary weapon
1044 //              ci->fire_primary_count += mouse_down(1);
1045
1046                 // for debugging, check to see if the debug key is down -- if so, make fire the debug laser instead
1047 #ifndef NDEBUG
1048                 if ( key_pressed(KEY_DEBUG_KEY) ) {
1049                         ci->fire_debug_count = ci->fire_primary_count;
1050                         ci->fire_primary_count = 0;
1051                 }
1052 #endif
1053
1054                 // keyboard: fire the current secondary weapon
1055                 if (check_control(FIRE_SECONDARY)) {
1056                         ci->fire_secondary_count++;
1057
1058                         // if we're a multiplayer client, set our accum bits now
1059                         if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1060                                 Net_player->s_info.accum_buttons |= OOC_FIRE_SECONDARY;
1061                         }
1062                 }
1063
1064                 // keyboard: launch countermeasures
1065                 if ( button_info_query(&Player->bi, LAUNCH_COUNTERMEASURE) ) {
1066                         control_used(LAUNCH_COUNTERMEASURE);
1067                         ci->fire_countermeasure_count++;
1068                         hud_gauge_popup_start(HUD_CMEASURE_GAUGE);
1069
1070                         // if we're a multiplayer client, set our accum bits now
1071                         // if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && !(Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING)){
1072                                 // Net_player->s_info.accum_buttons |= OOC_FIRE_COUNTERMEASURE;
1073                         // }
1074                 }
1075
1076                 // see if the afterburner has been started (keyboard + joystick)
1077                 if (check_control(AFTERBURNER)) {
1078                         if (!afterburner_last) {
1079                                 Assert(Player_ship);
1080                                 if ( !(Ship_info[Player_ship->ship_info_index].flags & SIF_AFTERBURNER) ) {
1081                                         gamesnd_play_error_beep();
1082                                 } else {
1083                                         ci->afterburner_start = 1;
1084                                 }
1085                         }
1086
1087                         afterburner_last = 1;
1088
1089                 } else {
1090                         if (afterburner_last)
1091                                 ci->afterburner_stop = 1;
1092
1093                         afterburner_last = 0;
1094                 }
1095         }
1096
1097         if ( (Viewer_mode & VM_EXTERNAL) || slew_active ) {
1098                 if ( !(Viewer_mode & VM_EXTERNAL_CAMERA_LOCKED) || slew_active ) {
1099                         ci->heading=0.0f;
1100                         ci->pitch=0.0f;
1101                         ci->bank=0.0f;
1102                 }
1103         }
1104 }
1105
1106 void read_player_controls(object *objp, float frametime)
1107 {
1108 //      if (Game_mode & GM_DEAD)
1109 //              return;
1110
1111         joy_ff_adjust_handling((int) objp->phys_info.speed);
1112
1113         {
1114                 switch( Player->control_mode )  {
1115                 case PCM_SUPERNOVA:
1116                         break;
1117
1118                 case PCM_NORMAL:
1119               read_keyboard_controls(&(Player->ci), frametime, &objp->phys_info );
1120                         break;
1121                 case PCM_WARPOUT_STAGE1:        // Accelerate to 40 km/s
1122                 case PCM_WARPOUT_STAGE2:        // Go 40 km/s steady up to the effect
1123                 case PCM_WARPOUT_STAGE3:        // Go 40 km/s steady through the effect
1124
1125                         memset(&(Player->ci), 0, sizeof(control_info) );                // set the controls to 0
1126
1127                         if ( (objp->type == OBJ_SHIP) && (!(Game_mode & GM_DEAD)) )     {
1128
1129                                 Warpout_time += flFrametime;
1130
1131                                 if ( Warpout_forced )   {
1132
1133                                         Ships[objp->instance].current_max_speed = TARGET_WARPOUT_SPEED*2.0f;
1134
1135                                         float diff = TARGET_WARPOUT_SPEED-objp->phys_info.fspeed;                               
1136                                         if ( diff < 0.0f ) diff = 0.0f;
1137                                         
1138                                         Player->ci.forward = ((TARGET_WARPOUT_SPEED+diff) / Ships[objp->instance].current_max_speed);
1139
1140                                 } else {
1141                                         int warp_failed=0;
1142                                         // check if warp ability has been disabled
1143                                         if ( Ships[objp->instance].flags & ( SF_WARP_BROKEN|SF_WARP_NEVER ) ) {
1144                                                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Cannot warp out at this time.", 81));
1145                                                 warp_failed=1;
1146                                         } else {
1147                                                 int     can_warp = 0;
1148                                                 if ( (!warp_failed) && (Ships[objp->instance].current_max_speed >= TARGET_WARPOUT_SPEED) )      {
1149                                                         can_warp = 1;
1150                                                 } else {
1151                                                         if (Ship_info[Ships[objp->instance].ship_info_index].max_overclocked_speed < TARGET_WARPOUT_SPEED) {
1152                                                                 // Cannot go fast enough, so abort sequence.
1153                                                                 warp_failed=1;
1154                                                                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Unable to engage warp... ship must be able to reach %.1f km/s", 82), TARGET_WARPOUT_SPEED );
1155                                                                 can_warp = 0;
1156                                                         } else {
1157                                                                 Ships[objp->instance].current_max_speed = TARGET_WARPOUT_SPEED + 5.0f;
1158                                                                 //Ships[objp->instance].current_max_speed = Ship_info[Ships[objp->instance].ship_info_index].max_overclocked_speed;
1159                                                                 can_warp = 1;
1160                                                         }
1161                                                 }
1162                                                 if (can_warp) {
1163                                                         float diff = TARGET_WARPOUT_SPEED-objp->phys_info.fspeed;                               
1164                                                         if ( diff < 0.0f ) 
1165                                                                 diff = 0.0f;
1166                                                 
1167                                                         Player->ci.forward = ((TARGET_WARPOUT_SPEED+diff) / Ships[objp->instance].current_max_speed);
1168                                                 }
1169                                         }
1170
1171                                         if ( warp_failed ) {
1172                                                 snd_play(&Snds[SND_PLAYER_WARP_FAIL]);
1173                                                 gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_STOP );
1174                                         }
1175                                 }
1176                         
1177                                 if ( Player->control_mode == PCM_WARPOUT_STAGE1 )       {
1178
1179
1180                                         // Wait at least 3 seconds before making sure warp speed is set.
1181                                         if ( Warpout_time>MINIMUM_PLAYER_WARPOUT_TIME ) {
1182                                                 // If we are going around 5% of the target speed, progress to next stage
1183                                                 float diff = fl_abs(objp->phys_info.fspeed - TARGET_WARPOUT_SPEED )/TARGET_WARPOUT_SPEED;
1184                                                 if ( diff < TARGET_WARPOUT_MATCH_PERCENT )      {
1185                                                         gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_DONE_STAGE1 );
1186                                                 }
1187                                         }
1188                                 }
1189                         }
1190                         break;
1191                 }
1192         }
1193
1194         // the ships maximum velocity now depends on the energy flowing to engines
1195         if(objp->type != OBJ_OBSERVER){
1196                 objp->phys_info.max_vel.xyz.z = Ships[objp->instance].current_max_speed;
1197         } 
1198         if(Player_obj->type == OBJ_SHIP){       
1199                 // only read player control info if player ship is not dead
1200                 if ( !(Ships[Player_obj->instance].flags & SF_DYING) ) {
1201                         vector wash_rot;
1202                         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)) ) {
1203                                 float intensity = 0.3f * min(Ships[objp->instance].wash_intensity, 1.0f);
1204                                 vm_vec_copy_scale(&wash_rot, &Ships[objp->instance].wash_rot_axis, intensity);
1205                                 physics_read_flying_controls( &objp->orient, &objp->phys_info, &(Player->ci), flFrametime, &wash_rot);
1206                         } else {
1207                                 physics_read_flying_controls( &objp->orient, &objp->phys_info, &(Player->ci), flFrametime);
1208                         }
1209                 }
1210         } else if(Player_obj->type == OBJ_OBSERVER){
1211                 physics_read_flying_controls(&objp->orient,&objp->phys_info,&(Player->ci), flFrametime);
1212         }
1213 }
1214
1215 void player_controls_init()
1216 {
1217         static int initted = 0;
1218
1219         if (initted)
1220                 return;
1221
1222         initted = 1;
1223         physics_init( &Descent_physics );
1224         Descent_physics.flags |= PF_ACCELERATES | PF_SLIDE_ENABLED;
1225
1226         Viewer_slew_angles_delta.p = 0.0f;
1227         Viewer_slew_angles_delta.b = 0.0f;
1228         Viewer_slew_angles_delta.h = 0.0f;
1229 }
1230
1231 // Clear current speed matching and auto-speed matching flags
1232 void player_clear_speed_matching()
1233 {
1234         if ( !Player ) {
1235                 Int3(); // why is Player NULL?
1236                 return;
1237         }
1238
1239         Player->flags &= ~PLAYER_FLAGS_MATCH_TARGET;
1240         Player->flags &= ~PLAYER_FLAGS_AUTO_MATCH_SPEED;
1241 }
1242
1243 // function which computes the forward_thrust_time needed for the player ship to match
1244 // velocities with the currently selected target
1245 // input:       no_target_text  => default parm (NULL), used to override HUD output when no target exists
1246 //                              match_off_text  =>      default parm (NULL), used to overide HUD output when matching toggled off
1247 //                              match_on_text   =>      default parm (NULL), used to overide HUD output when matching toggled on
1248 void player_match_target_speed(char *no_target_text, char *match_off_text, char *match_on_text)
1249 {
1250         if ( Objects[Player_ai->target_objnum].type != OBJ_SHIP ) {
1251                 return;
1252         }
1253
1254         // multiplayer observers can't match target speed
1255         if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER)) ){
1256                 return;
1257         }
1258
1259         if ( Player_ai->target_objnum == -1) {
1260                 if ( no_target_text ) {
1261                         if ( no_target_text[0] ) {
1262                                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, no_target_text );
1263                         }
1264                 } else {
1265 //                      HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR("No currently selected target.",-1) );
1266                 }
1267                 return;
1268         }
1269
1270         if ( Player->flags & PLAYER_FLAGS_MATCH_TARGET ) {
1271                 Player->flags &= ~PLAYER_FLAGS_MATCH_TARGET;
1272                 if ( match_off_text ) {
1273                         if ( match_off_text[0] ) {
1274                                 HUD_sourced_printf(HUD_SOURCE_HIDDEN, match_off_text );
1275                         }
1276                 } else {
1277 //                      HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR("No longer matching speed with current target.",-1) );
1278                 }
1279         } else {
1280                 int can_match=0;
1281
1282                 if ( Objects[Player_ai->target_objnum].phys_info.speed > MATCH_SPEED_THRESHOLD ) {
1283                         can_match=1;
1284                 } else {
1285                         // account for case of matching speed with docked ship 
1286                         ai_info *aip;
1287                         aip = &Ai_info[Ships[Objects[Player_ai->target_objnum].instance].ai_index];
1288                         if ( aip->ai_flags & AIF_DOCKED ) {
1289                                 Assert( aip->dock_objnum != -1 );
1290                                 if ( Objects[aip->dock_objnum].phys_info.fspeed > MATCH_SPEED_THRESHOLD ) {
1291                                         can_match=1;
1292                                 }
1293                         }
1294                 }
1295
1296                 if ( can_match ) {
1297                         Player->flags |= PLAYER_FLAGS_MATCH_TARGET;
1298                         if ( match_on_text ) {
1299                                 if ( match_on_text[0] ) {
1300                                         HUD_sourced_printf(HUD_SOURCE_HIDDEN, match_on_text );
1301                                 }
1302                         } else {
1303 //                              HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR("Matching speed with current target.",-1) );
1304                         }
1305                 }
1306         }
1307 }
1308
1309
1310 //#ifndef NDEBUG
1311
1312 // toggle_player_object toggles between the player objects (i.e. the ship they are currently flying)
1313 // and a descent style ship.
1314
1315 int use_descent = 0;
1316 LOCAL physics_info phys_save;
1317
1318 void toggle_player_object()
1319 {
1320         if ( use_descent ) {
1321                 memcpy( &Player_obj->phys_info, &phys_save, sizeof(physics_info) );
1322         } else {
1323                 memcpy( &phys_save, &Player_obj->phys_info, sizeof(physics_info) );
1324                 memcpy( &Player_obj->phys_info, &Descent_physics, sizeof(physics_info) );
1325         }
1326         use_descent = !use_descent;
1327
1328         HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("Using %s style physics for player ship."), use_descent ? NOX("DESCENT") : NOX("FreeSpace"));
1329 }
1330
1331 //#endif                // ifndef NDEBUG
1332
1333 // Init the data required for determining whether 'all alone' message should play
1334 void player_init_all_alone_msg()
1335 {
1336         ship_obj        *so;
1337         object  *objp;
1338
1339         Player->check_for_all_alone_msg=timestamp(0);
1340
1341         // See if there are any friendly ships present, if so return without preventing msg
1342         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1343                 objp = &Objects[so->objnum];
1344                 if ( objp == Player_obj ) {
1345                         continue;
1346                 }
1347
1348                 if ( Ships[objp->instance].team == Player_ship->team ) {
1349                         if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HARMLESS) ) {
1350                                 return;
1351                         }
1352                 }
1353         }
1354
1355         // There must be no friendly ships present, so prevent the 'all alone' message from ever playing
1356         Player->flags |= PLAYER_FLAGS_NO_CHECK_ALL_ALONE_MSG;
1357 }
1358
1359 // Called when a new pilot is created
1360 void player_set_pilot_defaults(player *p)
1361 {
1362         // Enable auto-targeting by default for all new pilots
1363         p->flags |= PLAYER_FLAGS_AUTO_TARGETING;
1364         p->save_flags |= PLAYER_FLAGS_AUTO_TARGETING;
1365
1366         p->auto_advance = 1;
1367 }
1368
1369 // Store some player preferences to Player->save_flags
1370 void player_save_target_and_weapon_link_prefs()
1371 {
1372         Player->save_flags = 0;
1373         if ( Player->flags & PLAYER_FLAGS_AUTO_TARGETING ) {
1374                 Player->save_flags |= PLAYER_FLAGS_AUTO_TARGETING;
1375         }
1376
1377
1378         if ( Player->flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
1379                 // multiplayer observers can't match target speed
1380                 if(!((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER))) ){                            
1381                         Player->save_flags |= PLAYER_FLAGS_AUTO_MATCH_SPEED;
1382                 }               
1383         }
1384
1385         // if we're in multiplayer mode don't do this because we will desync ourselves with the server
1386         if(!(Game_mode & GM_MULTIPLAYER)){
1387                 if ( Player_ship->flags & SF_PRIMARY_LINKED ) {
1388                         Player->save_flags |= PLAYER_FLAGS_LINK_PRIMARY;
1389                 } else {
1390                         Player->flags &= ~PLAYER_FLAGS_LINK_PRIMARY;
1391                 }
1392                 if ( Player_ship->flags & SF_SECONDARY_DUAL_FIRE ) {
1393                         Player->save_flags |= PLAYER_FLAGS_LINK_SECONDARY;
1394                 } else {
1395                         Player->flags &= ~PLAYER_FLAGS_LINK_SECONDARY;
1396                 }
1397         }
1398 }
1399
1400 // Store some player preferences to Player->save_flags
1401 void player_restore_target_and_weapon_link_prefs()
1402 {
1403         //      Don't restores the save flags in training, as we must ensure certain things are off, such as speed matching.
1404         if ( !(The_mission.game_type & MISSION_TYPE_TRAINING )) {
1405                 Player->flags |= Player->save_flags;
1406         }
1407
1408         if ( Player->flags & PLAYER_FLAGS_LINK_PRIMARY ) {
1409                 if ( Player_ship->weapons.num_primary_banks > 1 ) {
1410                         Player_ship->flags |= SF_PRIMARY_LINKED;
1411                 }
1412         }
1413
1414         if ( Player->flags & PLAYER_FLAGS_LINK_SECONDARY ) {
1415                 Player_ship->flags |= SF_SECONDARY_DUAL_FIRE;
1416         }
1417 }
1418
1419 // initialize player statistics on a per mission basis
1420 void player_level_init()
1421 {       
1422         Player->flags = PLAYER_FLAGS_STRUCTURE_IN_USE;                  // reset the player flags
1423         Player->flags |= Player->save_flags;
1424
1425         memset(&(Player->ci), 0, sizeof(control_info) );                // set the controls to 0
1426
1427         Viewer_slew_angles.p = 0.0f;    Viewer_slew_angles.b = 0.0f;    Viewer_slew_angles.h = 0.0f;
1428         Viewer_external_info.angles.p = 0.0f;
1429         Viewer_external_info.angles.b = 0.0f;
1430         Viewer_external_info.angles.h = 0.0f;
1431         Viewer_external_info.distance = 0.0f;
1432
1433         Viewer_mode = 0;
1434  
1435         Player_obj = NULL;
1436         Player_ship = NULL;
1437         Player_ai = NULL;
1438         
1439         //      Init variables for friendly fire monitoring.
1440         Player->friendly_last_hit_time = 0;
1441         Player->friendly_hits = 0;
1442         Player->friendly_damage = 0.0f;
1443         Player->last_warning_message_time = 0;
1444
1445         Player->control_mode = PCM_NORMAL;
1446
1447         Player->allow_warn_timestamp = 1;               // init timestamp that is used for managing attack warnings sent to player
1448         Player->check_warn_timestamp = 1;
1449         Player->warn_count = 0;                                         // number of attack warnings player has received this mission
1450
1451         Player->distance_warning_count = 0;             // Number of warning too far from origin
1452         Player->distance_warning_time = 0;              // Time at which last warning was given
1453
1454         Player->praise_count = 0;                                       // number of praises player has received this mission
1455         Player->allow_praise_timestamp = 1;             // timestamp until next praise is allowed
1456         Player->praise_delay_timestamp = 0;             // timstamp used to delay praises given to the player
1457
1458         Player->ask_help_count = 0;                             // number of times player has been asked for help by wingmen
1459         Player->allow_ask_help_timestamp = 1;   // timestamp until next ask_help is allowed
1460
1461         Player->scream_count = 0;                                       // number of times player has heard wingman screams this mission
1462         Player->allow_scream_timestamp = 1;             // timestamp until next wingman scream is allowed
1463
1464         Player->request_repair_timestamp = 1;   // timestamp until next 'requesting repair sir' message can be played
1465
1466         Player->repair_sound_loop = -1;
1467         Player->cargo_scan_loop = -1;
1468         Player->cargo_inspect_time = 0;                 // time that current target's cargo has been inspected for
1469
1470         Player->target_is_dying = -1;                           // The player target is dying, set to -1 if no target
1471         Player->current_target_sx = -1;                 // Screen x-pos of current target (or subsystem if applicable)
1472         Player->current_target_sy = -1;                 // Screen y-pos of current target (or subsystem if applicable)
1473         Player->target_in_lock_cone = -1;               // Is the current target in secondary weapon lock cone?
1474         Player->locking_subsys=NULL;                            // Subsystem pointer that missile lock is trying to seek
1475         Player->locking_on_center=0;                            // boolean, whether missile lock is trying for center of ship or not
1476         Player->locking_subsys_parent=-1;
1477
1478         Player->killer_objtype=-1;                                      // type of object that killed player
1479         Player->killer_weapon_index=-1;                         // weapon used to kill player (if applicable)
1480         Player->killer_parent_name[0]=0;                        // name of parent object that killed the player
1481
1482         Player_all_alone_msg_inited=0;
1483         Player->flags &= ~PLAYER_FLAGS_NO_CHECK_ALL_ALONE_MSG;
1484
1485         // Player->insignia_bitmap = -1;
1486
1487         Joystick_last_reading = -1;                             // Make the joystick read right away.
1488 }
1489
1490 // player_init() initializes global veriables once a game -- needed because of mallocing that
1491 // goes on in structures in the player file
1492 void player_init()
1493 {
1494         Player_num = 0;
1495         Player = &Players[Player_num];
1496         Player->num_campaigns = 0;
1497         Player->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE;
1498         Player->failures_this_session = 0;
1499         Player->show_skip_popup = (ubyte) 1;
1500 }
1501
1502 // stop any looping sounds associated with the Player, called from game_stop_looped_sounds().
1503 void player_stop_looped_sounds()
1504 {
1505         Assert(Player);
1506         if ( Player->repair_sound_loop > -1 )   {
1507                 snd_stop(Player->repair_sound_loop);
1508                 Player->repair_sound_loop = -1;
1509         }
1510         if ( Player->cargo_scan_loop > -1 )     {
1511                 snd_stop(Player->cargo_scan_loop);
1512                 Player->cargo_scan_loop = -1;
1513         }
1514 }
1515
1516 // Start the repair sound if it hasn't already been started.  Called when a player ship is being
1517 // repaired by a support ship
1518 void player_maybe_start_repair_sound()
1519 {
1520         Assert(Player);
1521         if ( Player->repair_sound_loop == -1 ) {
1522                 Player->repair_sound_loop = snd_play_looping( &Snds[SND_SHIP_REPAIR] );
1523         }
1524 }
1525
1526 // stop the player repair sound if it is already playing
1527 void player_stop_repair_sound()
1528 {
1529         Assert(Player);
1530         if ( Player->repair_sound_loop != -1 ) {
1531                 snd_stop(Player->repair_sound_loop);
1532                 Player->repair_sound_loop  = -1;
1533         }
1534 }
1535
1536 // start the cargo scanning sound if it hasn't already been started
1537 void player_maybe_start_cargo_scan_sound()
1538 {
1539         Assert(Player);
1540         if ( Player->cargo_scan_loop == -1 ) {
1541                 Player->cargo_scan_loop = snd_play_looping( &Snds[SND_CARGO_SCAN] );
1542         }
1543 }
1544
1545 // stop the player repair sound if it is already playing
1546 void player_stop_cargo_scan_sound()
1547 {
1548         Assert(Player);
1549         if ( Player->cargo_scan_loop != -1 ) {
1550                 snd_stop(Player->cargo_scan_loop);
1551                 Player->cargo_scan_loop  = -1;
1552         }
1553 }
1554
1555 // See if there is a praise message to deliver to the player.  We want to delay the praise messages
1556 // a bit, to make them more realistic
1557 //
1558 // exit:        1       =>      a praise message was delivered to the player, or a praise is pending
1559 //                      0       => no praise is pending
1560
1561 #define PLAYER_ALLOW_PRAISE_INTERVAL    60000           // minimum time between praises
1562
1563 int player_process_pending_praise()
1564 {
1565         // in multiplayer, never praise
1566         if(Game_mode & GM_MULTIPLAYER){
1567                 return 0;
1568         }
1569
1570         if ( timestamp_elapsed(Player->praise_delay_timestamp) ) {
1571                 int ship_index;
1572
1573                 Player->praise_delay_timestamp = 0;
1574                 ship_index = ship_get_random_player_wing_ship( SHIP_GET_NO_PLAYERS, 1000.0f );
1575                 if ( ship_index >= 0 ) {
1576                         // Only praise if above 50% integrity
1577                         if ( Objects[Ships[ship_index].objnum].hull_strength/Ship_info[Ships[ship_index].ship_info_index].initial_hull_strength > 0.5f ) {
1578                                 message_send_builtin_to_player(MESSAGE_PRAISE, &Ships[ship_index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, -1, -1);
1579                                 Player->allow_praise_timestamp = timestamp(PLAYER_ALLOW_PRAISE_INTERVAL*(Game_skill_level+1) );
1580                                 Player->allow_scream_timestamp = timestamp(20000);              // prevent death scream following praise
1581                                 Player->praise_count++;
1582                                 return 1;
1583                         }
1584                 }
1585         }
1586
1587         if ( Player->praise_delay_timestamp == 0 ) {
1588                 return 0;
1589         }
1590
1591         return 1;
1592 }
1593
1594 int player_inspect_cap_subsys_cargo(float frametime, char *outstr);
1595 // See if the player should be inspecting cargo, and update progress.
1596 // input:       frametime       =>              time since last frame in seconds
1597 // input:       outstr          =>              (output parm) holds string that HUD should display
1598 //
1599 //      exit:           1                               =>              player should display outstr on HUD
1600 //                              0                               =>              don't display cargo on HUD
1601 int player_inspect_cargo(float frametime, char *outstr)
1602 {
1603         object          *cargo_objp;
1604         ship                    *cargo_sp;
1605         ship_info       *cargo_sip;
1606         vector          vec_to_cargo;
1607         float                   dot;
1608
1609         outstr[0] = 0;
1610
1611         if ( Player_ai->target_objnum < 0 ) {
1612                 return 0;
1613         }
1614
1615         cargo_objp = &Objects[Player_ai->target_objnum];
1616         Assert(cargo_objp->type == OBJ_SHIP);
1617         cargo_sp = &Ships[cargo_objp->instance];
1618         cargo_sip = &Ship_info[cargo_sp->ship_info_index];
1619
1620 #ifndef MAKE_FS1
1621         // causes a FS1 mission not to finish since the subsytems and not
1622         // the ship are scanned
1623         if (cargo_sip->flags & SIF_HUGE_SHIP) {
1624                 return player_inspect_cap_subsys_cargo(frametime, outstr);
1625         }
1626 #endif
1627
1628         // check if target is ship class that can be inspected
1629         // MWA -- 1/27/98 -- added fighters/bombers to this list.  For multiplayer, we
1630         // want to show callsign of player
1631
1632         // scannable cargo behaves differently.  Scannable cargo is either "scanned" or "not scanned".  This flag
1633         // can be set on any ship.  Any ship with this set won't have "normal" cargo behavior
1634         if ( !(cargo_sp->flags & SF_SCANNABLE) ) {
1635                 if ( Game_mode & GM_NORMAL ) {
1636                         if ( !(cargo_sip->flags & (SIF_CARGO|SIF_TRANSPORT)) ) {
1637                                 return 0;
1638                         }
1639                 } else {
1640                         if ( !(cargo_sip->flags & (SIF_CARGO|SIF_TRANSPORT|SIF_FIGHTER|SIF_BOMBER)) ) {
1641                                 return 0;
1642                         }
1643                 }
1644
1645                 // won't show callsign information for single player games
1646                 if ( (Game_mode & GM_MULTIPLAYER) && !((cargo_sip->flags & (SIF_FIGHTER|SIF_BOMBER)) && (cargo_objp->flags & OF_PLAYER_SHIP)) )
1647                         return 0;
1648         }
1649
1650         // if cargo is already revealed
1651         if ( cargo_sp->flags & SF_CARGO_REVEALED ) {
1652                 if ( !(cargo_sp->flags & SF_SCANNABLE) ) {
1653                         char *cargo_name;
1654                         cargo_name = Cargo_names[cargo_sp->cargo1 & CARGO_INDEX_MASK];
1655                         Assert ( cargo_name );
1656
1657                         if ( cargo_sip->flags & (SIF_CARGO|SIF_TRANSPORT) ) {
1658                                 if ( cargo_name[0] == '#' )
1659                                         sprintf(outstr, XSTR( "passengers:\n   %s", 83), cargo_name+1 );
1660                                 else
1661                                         sprintf(outstr,XSTR( "cargo: %s", 84), cargo_name );
1662                         } else {
1663                                 int pn;
1664
1665                                 Assert( Game_mode & GM_MULTIPLAYER );
1666
1667                                 // get a player num from the object, then get a callsign from the player structure.
1668                                 pn = multi_find_player_by_object( cargo_objp );
1669                                 // Assert( pn != -1 );
1670                                 if(pn == -1){
1671                                         strcpy(outstr, "");
1672                                 } else {
1673                                         sprintf(outstr, "%s", Net_players[pn].player->short_callsign );
1674                                 }
1675                         }
1676                 } else {
1677                         sprintf(outstr, XSTR( "Scanned", 85) );
1678                 }
1679
1680                 // always bash cargo_inspect_time to 0 since AI ships can reveal cargo that we
1681                 // are in the process of scanning
1682                 Player->cargo_inspect_time = 0;
1683
1684                 return 1;
1685         }
1686
1687         // see if player is within inspection range
1688         if ( Player_ai->current_target_distance < max(CARGO_REVEAL_MIN_DIST, (cargo_objp->radius+CARGO_RADIUS_DELTA)) ) {
1689
1690                 // check if player is facing cargo, do not proceed with inspection if not
1691                 vm_vec_normalized_dir(&vec_to_cargo, &cargo_objp->pos, &Player_obj->pos);
1692                 dot = vm_vec_dot(&vec_to_cargo, &Player_obj->orient.v.fvec);
1693                 if ( dot < CARGO_MIN_DOT_TO_REVEAL ) {
1694                         if ( !(cargo_sp->flags & SF_SCANNABLE) )
1695                                 sprintf(outstr,XSTR( "cargo: <unknown>", 86));
1696                         else
1697                                 sprintf(outstr,XSTR( "not scanned", 87));
1698                         hud_targetbox_end_flash(TBOX_FLASH_CARGO);
1699                         Player->cargo_inspect_time = 0;
1700                         return 1;
1701                 }
1702
1703                 // player is facing the cargo, and withing range, so proceed with inspection
1704                 if ( hud_sensors_ok(Player_ship, 0) ) {
1705                         Player->cargo_inspect_time += fl2i(frametime*1000+0.5f);
1706                 }
1707
1708                 if ( !(cargo_sp->flags & SF_SCANNABLE) )
1709                         sprintf(outstr,XSTR( "cargo: inspecting", 88));
1710                 else
1711                         sprintf(outstr,XSTR( "scanning", 89));
1712
1713                 if ( Player->cargo_inspect_time > cargo_sip->scan_time ) {
1714                         ship_do_cargo_revealed( cargo_sp );
1715                         snd_play( &Snds[SND_CARGO_REVEAL], 0.0f );
1716                         Player->cargo_inspect_time = 0;
1717                 }
1718         } else {
1719                 if ( !(cargo_sp->flags & SF_SCANNABLE) )
1720                         sprintf(outstr,XSTR( "cargo: <unknown>", 86));
1721                 else
1722                         sprintf(outstr,XSTR( "not scanned", 87));
1723         }
1724
1725         return 1;
1726 }
1727
1728 //      exit:           1                               =>              player should display outstr on HUD
1729 //                              0                               =>              don't display cargo on HUD
1730 int player_inspect_cap_subsys_cargo(float frametime, char *outstr)
1731 {
1732         object          *cargo_objp;
1733         ship                    *cargo_sp;
1734         ship_info       *cargo_sip;
1735         vector          vec_to_cargo;
1736         float                   dot;
1737         ship_subsys     *subsys;
1738
1739         outstr[0] = 0;
1740         subsys = Player_ai->targeted_subsys;
1741
1742         if ( subsys == NULL ) {
1743                 return 0;
1744         } 
1745
1746         cargo_objp = &Objects[Player_ai->target_objnum];
1747         Assert(cargo_objp->type == OBJ_SHIP);
1748         cargo_sp = &Ships[cargo_objp->instance];
1749         cargo_sip = &Ship_info[cargo_sp->ship_info_index];
1750
1751         Assert(cargo_sip->flags & SIF_HUGE_SHIP);
1752
1753         if ( !(cargo_sp->flags & SF_SCANNABLE) ) {
1754                 return 0;
1755         }
1756
1757         // dont scan cargo on turrets, radar, etc.  only the majors: fighterbay, sensor, engines, weapons, nav, comm
1758         if (!valid_cap_subsys_cargo_list(subsys->system_info->name)) {
1759                 return 0;
1760         }
1761
1762         // if cargo is already revealed
1763         if ( subsys->subsys_cargo_revealed ) {
1764                 const char *cargo_name;
1765                 if (subsys->subsys_cargo_name == -1) {
1766                         cargo_name = XSTR("Nothing", 1493);
1767                 } else {
1768                         cargo_name = Cargo_names[subsys->subsys_cargo_name];
1769                 }
1770                 Assert ( cargo_name );
1771
1772                 sprintf(outstr,XSTR( "cargo: %s", 84), cargo_name );
1773         
1774                 // always bash cargo_inspect_time to 0 since AI ships can reveal cargo that we
1775                 // are in the process of scanning
1776                 Player->cargo_inspect_time = 0;
1777
1778                 return 1;
1779         }
1780
1781         // see if player is within inspection range [ok for subsys]
1782         vector  subsys_pos;
1783         float           subsys_rad;
1784         int             subsys_in_view, x, y;
1785
1786         get_subsystem_world_pos(cargo_objp, Player_ai->targeted_subsys, &subsys_pos);
1787         subsys_rad = subsys->system_info->radius;
1788
1789         if ( Player_ai->current_target_distance < max(CAP_CARGO_REVEAL_MIN_DIST, (subsys_rad + CAPITAL_CARGO_RADIUS_DELTA)) ) {
1790
1791                 // check if player is facing cargo, do not proceed with inspection if not
1792                 vm_vec_normalized_dir(&vec_to_cargo, &subsys_pos, &Player_obj->pos);
1793                 dot = vm_vec_dot(&vec_to_cargo, &Player_obj->orient.v.fvec);
1794                 int hud_targetbox_subsystem_in_view(object *target_objp, int *sx, int *sy);
1795                 subsys_in_view = hud_targetbox_subsystem_in_view(cargo_objp, &x, &y);
1796
1797                 if ( (dot < CARGO_MIN_DOT_TO_REVEAL) || (!subsys_in_view) ) {
1798                         sprintf(outstr,XSTR( "cargo: <unknown>", 86));
1799                         hud_targetbox_end_flash(TBOX_FLASH_CARGO);
1800                         Player->cargo_inspect_time = 0;
1801                         return 1;
1802                 }
1803
1804                 // player is facing the cargo, and withing range, so proceed with inspection
1805                 if ( hud_sensors_ok(Player_ship, 0) ) {
1806                         Player->cargo_inspect_time += fl2i(frametime*1000+0.5f);
1807                 }
1808
1809                 sprintf(outstr,XSTR( "cargo: inspecting", 88));
1810
1811                 if ( Player->cargo_inspect_time > cargo_sip->scan_time ) {
1812                         void ship_do_cap_subsys_cargo_revealed( ship *shipp, ship_subsys *subsys, int from_network );
1813                         ship_do_cap_subsys_cargo_revealed( cargo_sp, subsys, 0);
1814                         snd_play( &Snds[SND_CARGO_REVEAL], 0.0f );
1815                         Player->cargo_inspect_time = 0;
1816                 }
1817         } else {
1818                 sprintf(outstr,XSTR( "cargo: <unknown>", 86));
1819         }
1820
1821         return 1;
1822 }
1823
1824
1825 // get the maximum weapon range for the player (of both primary and secondary)
1826 float   player_farthest_weapon_range()
1827 {
1828         float prange,srange;
1829
1830         hud_get_best_primary_bank(&prange);
1831         srange=ship_get_secondary_weapon_range(Player_ship);
1832
1833         return max(prange,srange);
1834 }
1835
1836 // Determine text name for the weapon that killed the player.
1837 // input:       weapon_info_index       =>              weapon type that killed the player (can be -1 if no weapon involved)
1838 //                              killer_species          =>              species of ship that fired weapon
1839 //                              weapon_name                     =>              output parameter... stores weapon name generated in this function       
1840 void player_generate_killer_weapon_name(int weapon_info_index, int killer_species, char *weapon_name)
1841 {
1842         if ( weapon_info_index < 0 ) {
1843                 return;
1844         }
1845
1846         #ifndef NDEBUG
1847         if ( Show_killer_weapon ) {
1848                 killer_species = SPECIES_TERRAN;
1849         }
1850         #endif
1851
1852         switch ( killer_species ) {
1853         case SPECIES_TERRAN:
1854                 strcpy(weapon_name, Weapon_info[weapon_info_index].name);
1855                 break;
1856         default:
1857                 if ( Weapon_info[weapon_info_index].subtype == WP_MISSILE ) {
1858                         strcpy(weapon_name, XSTR( "missile", 90));
1859                 } else {
1860                         strcpy(weapon_name, XSTR( "laser fire", 91));
1861                 }
1862                 break;
1863         }
1864 }
1865
1866 // function to generate the text for death of a player given the information stored in the player object.
1867 // a pointer to the text is returned
1868 char *player_generate_death_text( player *player_p, char *death_text )
1869 {
1870         char weapon_name[NAME_LENGTH];
1871         weapon_name[0] = 0;     
1872
1873         player_generate_killer_weapon_name(player_p->killer_weapon_index, player_p->killer_species, weapon_name);
1874
1875         switch ( player_p->killer_objtype ) {
1876         case OBJ_SHOCKWAVE:
1877                 if ( weapon_name[0] ) {
1878 //                      sprintf(death_text, XSTR("%s was killed by a shockwave from a %s, fired by %s",-1), player_p->callsign, weapon_name, player_p->killer_parent_name);
1879                         sprintf(death_text, XSTR( "%s was killed by a missile shockwave", 92), player_p->callsign);
1880                 } else {
1881                         sprintf(death_text, XSTR( "%s was killed by a shockwave from %s exploding", 93), player_p->callsign, player_p->killer_parent_name);
1882                 }
1883                 break;
1884         case OBJ_WEAPON:
1885                 Assert(weapon_name[0]);
1886
1887                 // is this from a friendly ship?
1888                 int ship_index;
1889                 ship_index = ship_name_lookup(player_p->killer_parent_name, 1);
1890                 if((ship_index >= 0) && (Player_ship != NULL) && (Player_ship->team == Ships[ship_index].team)){
1891                         sprintf(death_text, XSTR( "%s was killed by friendly fire from %s", 1338), player_p->callsign, player_p->killer_parent_name);
1892                 } else {
1893                         sprintf(death_text, XSTR( "%s was killed by %s", 94), player_p->callsign, player_p->killer_parent_name);
1894                 }
1895                 break;
1896         case OBJ_SHIP:
1897                 if ( player_p->flags & PLAYER_FLAGS_KILLED_BY_EXPLOSION ) {
1898                         sprintf(death_text, XSTR( "%s was killed by a blast from %s exploding", 95), player_p->callsign, player_p->killer_parent_name);
1899                 } else if (player_p->flags & PLAYER_FLAGS_KILLED_BY_ENGINE_WASH) {
1900                         sprintf(death_text, XSTR( "%s was killed by engine wash from %s", 1494), player_p->callsign, player_p->killer_parent_name);
1901                 } else {
1902                         sprintf(death_text, XSTR( "%s was killed by a collision with %s", 96), player_p->callsign, player_p->killer_parent_name);
1903                 }
1904                 break;
1905         case OBJ_DEBRIS:
1906                 sprintf(death_text, XSTR( "%s was killed by a collision with debris", 97), player_p->callsign);
1907                 break;
1908         case OBJ_ASTEROID:
1909                 sprintf(death_text, XSTR( "%s was killed by a collision with an asteroid", 98), player_p->callsign);
1910                 break;
1911         case OBJ_BEAM:
1912                 if(strlen(player_p->killer_parent_name) <= 0){                  
1913                         Int3();
1914                         sprintf(death_text, XSTR( "%s was killed by a beam from an unknown source", 1081), player_p->callsign);
1915                 } else {                                        
1916                         // is this from a friendly ship?
1917                         int ship_index;
1918                         ship_index = ship_name_lookup(player_p->killer_parent_name, 1);
1919                         if((ship_index >= 0) && (Player_ship != NULL) && (Player_ship->team == Ships[ship_index].team)){
1920                                 sprintf(death_text, XSTR( "%s was destroyed by friendly beam fire from %s", 1339), player_p->callsign, player_p->killer_parent_name);
1921                         } else {
1922                                 sprintf(death_text, XSTR( "%s was destroyed by a beam from %s", 1082), player_p->callsign, player_p->killer_parent_name);
1923                         }                       
1924                 }
1925                 break;
1926         default:
1927                 sprintf(death_text, XSTR( "%s was killed", 99), player_p->callsign);
1928                 break;
1929         }
1930
1931         return death_text;
1932 }
1933
1934 // display what/who killed the player
1935 void player_show_death_message()
1936 {
1937         char death_text[256];
1938
1939         // check if player killed self
1940         if ( Player->flags & PLAYER_KILLED_SELF ) {
1941                 // reasons he killed himself
1942                 if(Player->flags & PLAYER_FLAGS_KILLED_SELF_SHOCKWAVE){
1943                         sprintf(death_text, XSTR( "You have killed yourself with a shockwave from your own weapon", 1421));                     
1944                 }
1945                 else if(Player->flags & PLAYER_FLAGS_KILLED_SELF_MISSILES){
1946                         sprintf(death_text, XSTR( "You have killed yourself with your own missiles", 1422));                    
1947                 } else {
1948                         sprintf(death_text, XSTR( "You have killed yourself", 100));
1949                 }
1950
1951                 Player->flags &= ~(PLAYER_FLAGS_KILLED_SELF_MISSILES | PLAYER_FLAGS_KILLED_SELF_SHOCKWAVE);
1952         } else {
1953                 player_generate_death_text( Player, death_text );
1954         }
1955
1956         HUD_fixed_printf(30.0f, death_text);
1957 }
1958
1959
1960 extern void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum);
1961
1962 // maybe fire a turret that is on a player ship (single or multi)
1963 void player_maybe_fire_turret(object *objp)
1964 {
1965         model_subsystem *psub;
1966         ship_subsys             *pss;
1967
1968         ship                    *shipp = &Ships[objp->instance];
1969         ai_info                 *aip = &Ai_info[shipp->ai_index];
1970         ship_info               *sip = &Ship_info[shipp->ship_info_index];
1971
1972         // do a quick out if this isn't a bomber
1973         if ( !(sip->flags & SIF_BOMBER) ) {
1974                 return;
1975         }
1976
1977         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)) {
1978                 if (aip->dock_objnum > -1) {
1979                         if (vm_vec_dist_quick(&objp->pos, &Objects[aip->dock_objnum].pos) < (objp->radius + Objects[aip->dock_objnum].radius) * 1.25f)
1980                                 return;
1981                 }
1982         }
1983
1984         // See if there are any turrets on the ship, if so see if they should fire
1985         for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
1986
1987                 if ( pss->current_hits <= 0.0f ) 
1988                         continue;
1989
1990                 psub = pss->system_info;
1991
1992                 if ( psub->type == SUBSYSTEM_TURRET ) {
1993                         if ( psub->turret_num_firing_points > 0 ) {
1994                                 ai_fire_from_turret(shipp, pss, OBJ_INDEX(objp));
1995                         }
1996                 }
1997         }
1998 }
1999
2000 void player_set_next_all_alone_msg_timestamp()
2001 {
2002         Player->check_for_all_alone_msg=timestamp(30000);
2003 }
2004
2005 // maybe play message from Terran Command 'You're all alone now Alpha 1'
2006 void player_maybe_play_all_alone_msg()
2007 {
2008         if ( Game_mode & GM_MULTIPLAYER ){
2009                 return;
2010         }
2011
2012         if ( !Player_all_alone_msg_inited ) {
2013                 player_init_all_alone_msg();
2014                 Player_all_alone_msg_inited=1;
2015                 return;
2016         }
2017
2018         if ( Player->flags & PLAYER_FLAGS_NO_CHECK_ALL_ALONE_MSG ) {
2019                 return;
2020         }
2021
2022         // only check every N seconds
2023         if ( !timestamp_elapsed(Player->check_for_all_alone_msg) ) {
2024                 return;
2025         }
2026
2027         player_set_next_all_alone_msg_timestamp();
2028         
2029         // at least one primary objective must be not complete (but not failed)
2030         if ( !mission_goals_incomplete(PRIMARY_GOAL) ) {
2031                 Player->flags |= PLAYER_FLAGS_NO_CHECK_ALL_ALONE_MSG;
2032                 return;
2033         }
2034
2035         // there must be no reinforcements available, hold off on message
2036         if ( (Player_ship != NULL) && hud_squadmsg_reinforcements_available(Player_ship->team) ) {
2037                 return;
2038         }
2039
2040         // there must be no ships present that are on the same team as the player
2041         ship_obj *so;
2042         object  *objp;
2043
2044         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2045                 objp = &Objects[so->objnum];
2046
2047                 if ( objp == Player_obj ) {
2048                         continue;
2049                 }
2050
2051                 if ( Ships[objp->instance].team == Player_ship->team ) {
2052                         if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HARMLESS) ) {
2053                                 return;
2054                         }
2055                 }
2056         }
2057
2058         // met all the requirements, now only play 50% of the time :)
2059         if ( rand()&1 ) {
2060                 message_send_builtin_to_player(MESSAGE_ALL_ALONE, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1);
2061         }
2062         Player->flags |= PLAYER_FLAGS_NO_CHECK_ALL_ALONE_MSG;
2063
2064
2065
2066 void player_set_padlock_state()
2067 {
2068         // clear padlock views
2069         Viewer_mode &= ~(VM_PADLOCK_ANY);
2070
2071         if ( check_control(PADLOCK_UP) ) {
2072                 Viewer_mode |= VM_PADLOCK_UP;
2073                 return;
2074         }
2075
2076         if ( check_control(PADLOCK_DOWN) ) {
2077                 Viewer_mode |= VM_PADLOCK_REAR;
2078                 return;
2079         }
2080
2081         if ( check_control(PADLOCK_RIGHT) ) {
2082                 Viewer_mode |= VM_PADLOCK_RIGHT;
2083                 return;
2084         }
2085
2086         if ( check_control(PADLOCK_LEFT) ) {
2087                 Viewer_mode |= VM_PADLOCK_LEFT;
2088                 return;
2089         }
2090 }
2091
2092 void player_get_padlock_orient(matrix *eye_orient)
2093 {
2094         Assert(Viewer_mode & VM_PADLOCK_ANY);
2095
2096         matrix old_eye_orient;
2097         old_eye_orient = *eye_orient;
2098
2099         if ( Viewer_mode & VM_PADLOCK_UP ) {
2100                 eye_orient->v.fvec = old_eye_orient.v.uvec;
2101                 vm_vec_copy_scale( &eye_orient->v.uvec, &old_eye_orient.v.fvec, -1.0f );
2102         } else if ( Viewer_mode & VM_PADLOCK_REAR ) {
2103                 vm_vec_negate(&eye_orient->v.fvec);
2104                 vm_vec_negate(&eye_orient->v.rvec);
2105         } else if ( Viewer_mode & VM_PADLOCK_LEFT ) {
2106                 vm_vec_copy_scale( &eye_orient->v.fvec, &old_eye_orient.v.rvec, -1.0f );
2107                 eye_orient->v.rvec = old_eye_orient.v.fvec;
2108         } else if ( Viewer_mode & VM_PADLOCK_RIGHT ) {
2109                 eye_orient->v.fvec = old_eye_orient.v.rvec;
2110                 vm_vec_copy_scale( &eye_orient->v.rvec, &old_eye_orient.v.fvec, -1.0f );
2111         } else {
2112                 Int3();
2113         }
2114 }
2115
2116 void player_display_packlock_view()
2117 {
2118         int padlock_view_index=0;
2119
2120         if ( Viewer_mode & VM_PADLOCK_UP ) {
2121                 padlock_view_index = 0;
2122         } else if ( Viewer_mode & VM_PADLOCK_REAR ) {
2123                 padlock_view_index = 1;
2124         } else if ( Viewer_mode & VM_PADLOCK_LEFT ) {
2125                 padlock_view_index = 2;
2126         } else if ( Viewer_mode & VM_PADLOCK_RIGHT ) {
2127                 padlock_view_index = 3;
2128         } else {
2129                 Int3();
2130                 return;
2131         }
2132
2133         char    str[128];
2134
2135         if ( !(Viewer_mode & (VM_CHASE|VM_EXTERNAL|VM_SLEWED)) ) {
2136                 switch (padlock_view_index) {
2137                 case 0:
2138                         strcpy(str, XSTR( "top view", 101));    break;
2139                 case 1:
2140                         strcpy(str, XSTR( "rear view", 102));   break;
2141                 case 2:
2142                         strcpy(str, XSTR( "left view", 103));   break;
2143                 case 3:
2144                         strcpy(str, XSTR( "right view", 104));  break;
2145                         }
2146
2147                 HUD_fixed_printf(0.01f, str);
2148         }
2149 }
2150
2151 // get the player's eye position and orient
2152 // NOTE : this is mostly just copied from game_render_frame_setup()
2153 extern vector Dead_player_last_vel;
2154 extern vector Camera_pos;
2155 extern void compute_slew_matrix(matrix *orient, angles *a);
2156 #define MIN_DIST_TO_DEAD_CAMERA                 50.0f
2157 void player_get_eye(vector *eye_pos, matrix *eye_orient)
2158 {
2159         object *viewer_obj;
2160         vector eye_dir;
2161
2162         // if the player object is NULL, return
2163         if(Player_obj == NULL){
2164                 return;
2165         }
2166
2167         // standalone servers can bail here
2168         if(Game_mode & GM_STANDALONE_SERVER){
2169                 return;
2170         }
2171
2172         // if we're not in-mission, don't do this
2173         if(!(Game_mode & GM_IN_MISSION)){
2174                 return;
2175         }
2176
2177         Assert(eye_pos != NULL);
2178         Assert(eye_orient != NULL);
2179
2180         if (Game_mode & GM_DEAD) {
2181                 vector  vec_to_deader, view_pos;
2182                 float           dist;           
2183                 if (Player_ai->target_objnum != -1) {
2184                         int view_from_player = 1;
2185
2186                         if (Viewer_mode & VM_OTHER_SHIP) {
2187                                 //      View from target.
2188                                 viewer_obj = &Objects[Player_ai->target_objnum];
2189                                 if ( viewer_obj->type == OBJ_SHIP ) {
2190                                         ship_get_eye( eye_pos, eye_orient, viewer_obj );
2191                                         view_from_player = 0;
2192                                 }
2193                         }
2194
2195                         if ( view_from_player ) {
2196                                 //      View target from player ship.
2197                                 viewer_obj = NULL;
2198                                 *eye_pos = Player_obj->pos;
2199                                 vm_vec_normalized_dir(&eye_dir, &Objects[Player_ai->target_objnum].pos, eye_pos);
2200                                 vm_vector_2_matrix(eye_orient, &eye_dir, NULL, NULL);
2201                         }
2202                 } else {
2203                         dist = vm_vec_normalized_dir(&vec_to_deader, &Player_obj->pos, &Dead_camera_pos);
2204                         
2205                         if (dist < MIN_DIST_TO_DEAD_CAMERA){
2206                                 dist += flFrametime * 16.0f;
2207                         }
2208
2209                         vm_vec_scale(&vec_to_deader, -dist);
2210                         vm_vec_add(&Dead_camera_pos, &Player_obj->pos, &vec_to_deader);
2211                         
2212                         view_pos = Player_obj->pos;
2213
2214                         if (!(Game_mode & GM_DEAD_BLEW_UP)) {                                                           
2215                         } else if (Player_ai->target_objnum != -1) {
2216                                 view_pos = Objects[Player_ai->target_objnum].pos;
2217                         } else {
2218                                 //      Make camera follow explosion, but gradually slow down.
2219                                 vm_vec_scale_add2(&Player_obj->pos, &Dead_player_last_vel, flFrametime);
2220                                 view_pos = Player_obj->pos;                             
2221                         }
2222
2223                         *eye_pos = Dead_camera_pos;
2224
2225                         vm_vec_normalized_dir(&eye_dir, &Player_obj->pos, eye_pos);
2226
2227                         vm_vector_2_matrix(eye_orient, &eye_dir, NULL, NULL);
2228                         viewer_obj = NULL;
2229                 }
2230         } 
2231         
2232         //      If already blown up, these other modes can override.
2233         if (!(Game_mode & (GM_DEAD | GM_DEAD_BLEW_UP))) {
2234                 viewer_obj = Player_obj;
2235  
2236                 if (Viewer_mode & VM_OTHER_SHIP) {
2237                         if (Player_ai->target_objnum != -1){
2238                                 viewer_obj = &Objects[Player_ai->target_objnum];
2239                         } 
2240                 }
2241
2242                 if (Viewer_mode & VM_EXTERNAL) {
2243                         matrix  tm, tm2;
2244
2245                         vm_angles_2_matrix(&tm2, &Viewer_external_info.angles);
2246                         vm_matrix_x_matrix(&tm, &viewer_obj->orient, &tm2);
2247
2248                         vm_vec_scale_add(eye_pos, &viewer_obj->pos, &tm.v.fvec, 2.0f * viewer_obj->radius + Viewer_external_info.distance);
2249
2250                         vm_vec_sub(&eye_dir, &viewer_obj->pos, eye_pos);
2251                         vm_vec_normalize(&eye_dir);
2252                         vm_vector_2_matrix(eye_orient, &eye_dir, &viewer_obj->orient.v.uvec, NULL);
2253                         viewer_obj = NULL;
2254
2255                         //      Modify the orientation based on head orientation.
2256                         compute_slew_matrix(eye_orient, &Viewer_slew_angles);
2257                 } else if ( Viewer_mode & VM_CHASE ) {
2258                         vector  move_dir;
2259
2260                         if ( viewer_obj->phys_info.speed < 0.1 ){
2261                                 move_dir = viewer_obj->orient.v.fvec;
2262                         } else {
2263                                 move_dir = viewer_obj->phys_info.vel;
2264                                 vm_vec_normalize_safe(&move_dir);
2265                         }
2266
2267                         vm_vec_scale_add(eye_pos, &viewer_obj->pos, &move_dir, -3.0f * viewer_obj->radius - Viewer_chase_info.distance);
2268                         vm_vec_scale_add2(eye_pos, &viewer_obj->orient.v.uvec, 0.75f * viewer_obj->radius);
2269                         vm_vec_sub(&eye_dir, &viewer_obj->pos, eye_pos);
2270                         vm_vec_normalize(&eye_dir);
2271
2272                         // JAS: I added the following code because if you slew up using
2273                         // Descent-style physics, eye_dir and Viewer_obj->orient.v.uvec are
2274                         // equal, which causes a zero-length vector in the vm_vector_2_matrix
2275                         // call because the up and the forward vector are the same.   I fixed
2276                         // it by adding in a fraction of the right vector all the time to the
2277                         // up vector.
2278                         vector tmp_up = viewer_obj->orient.v.uvec;
2279                         vm_vec_scale_add2( &tmp_up, &viewer_obj->orient.v.rvec, 0.00001f );
2280
2281                         vm_vector_2_matrix(eye_orient, &eye_dir, &tmp_up, NULL);
2282                         viewer_obj = NULL;
2283
2284                         //      Modify the orientation based on head orientation.
2285                         compute_slew_matrix(eye_orient, &Viewer_slew_angles);
2286                 } else if ( Viewer_mode & VM_WARP_CHASE ) {
2287                         *eye_pos = Camera_pos;
2288
2289                         ship * shipp = &Ships[Player_obj->instance];
2290
2291                         vm_vec_sub(&eye_dir, &shipp->warp_effect_pos, eye_pos);
2292                         vm_vec_normalize(&eye_dir);
2293                         vm_vector_2_matrix(eye_orient, &eye_dir, &Player_obj->orient.v.uvec, NULL);
2294                         viewer_obj = NULL;
2295                 } else {
2296                         // get an eye position based upon the correct type of object
2297                         switch(viewer_obj->type){
2298                         case OBJ_SHIP:
2299                                 // make a call to get the eye point for the player object
2300                                 ship_get_eye( eye_pos, eye_orient, viewer_obj );
2301                                 break;
2302                         case OBJ_OBSERVER:
2303                                 // make a call to get the eye point for the player object
2304                                 observer_get_eye( eye_pos, eye_orient, viewer_obj );                            
2305                                 break;
2306                         default :
2307                                 Int3();
2308                         }                       
2309                 }
2310         }
2311 }