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