naev 0.12.6
pilot.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include <math.h>
11#include <stdlib.h>
12
13#include "naev.h"
15
16#include "pilot.h"
17
18#include "ai.h"
19#include "array.h"
20#include "board.h"
21#include "camera.h"
22#include "damagetype.h"
23#include "debris.h"
24#include "debug.h"
25#include "escort.h"
26#include "explosion.h"
27#include "faction.h"
28#include "font.h"
29#include "gui.h"
30#include "hook.h"
31#include "log.h"
32#include "nlua_outfit.h"
33#include "nlua_pilotoutfit.h"
34#include "nlua_vec2.h"
35#include "ntime.h"
36#include "ntracing.h"
37#include "pilot_ship.h"
38#include "player.h"
39#include "player_autonav.h"
40#include "quadtree.h"
41#include "rng.h"
42#include "sound.h"
43
44#define PILOT_SIZE_MIN 128
45
46/* ID Generators. */
47static unsigned int pilot_id =
48 PLAYER_ID;
49
50/* stack of pilots */
52 NULL;
56static int qt_init = 0;
57/* A simple grid search procedure was used to determine the following
58 * parameters. */
59static int qt_max_elem = 2;
60static int qt_depth = 5;
61
62/* misc */
63static const double pilot_commTimeout =
64 15.;
65static const double pilot_commFade =
66 5.;
67
68/*
69 * Prototypes
70 */
71/* Create. */
72static void pilot_init( Pilot *dest, const Ship *ship, const char *name,
73 int faction, const double dir, const vec2 *pos,
74 const vec2 *vel, const PilotFlags flags,
75 unsigned int dockpilot, int dockslot );
76/* Update. */
77static void pilot_hyperspace( Pilot *pilot, double dt );
78static void pilot_refuel( Pilot *p, double dt );
79static void pilot_updateSolid( Pilot *p, double dt );
80/* Clean up. */
81static void pilot_erase( Pilot *p );
82/* Misc. */
83static void pilot_renderFramebufferBase( Pilot *p, GLuint fbo, double fw,
84 double fh, const Lighting *L );
85static int pilot_getStackPos( unsigned int id );
86static void pilot_init_trails( Pilot *p );
87static int pilot_trail_generated( Pilot *p, int generator );
88static void pilot_addQuadtree( const Pilot *p, int i );
89
93Pilot *const *pilot_getAll( void )
94{
95 return pilot_stack;
96}
97
101static int pilot_cmp( const void *ptr1, const void *ptr2 )
102{
103 const Pilot *p1, *p2;
104 p1 = *( (const Pilot **)ptr1 );
105 p2 = *( (const Pilot **)ptr2 );
106 return p1->id - p2->id;
107}
108
115static int pilot_getStackPos( unsigned int id )
116{
117 const Pilot pid = { .id = id };
118 const Pilot *pidptr = &pid;
119 /* binary search */
120 Pilot **pp = bsearch( &pidptr, pilot_stack, array_size( pilot_stack ),
121 sizeof( Pilot * ), pilot_cmp );
122 if ( pp == NULL )
123 return -1;
124 else
125 return pp - pilot_stack;
126}
127
135unsigned int pilot_getNextID( unsigned int id, int mode )
136{
137 int m, p;
138
139 /* Player must exist. */
140 if ( player.p == NULL )
141 return PLAYER_ID;
142
143 /* Get the pilot. */
144 m = pilot_getStackPos( id );
145
146 /* Unselect. */
147 if ( ( m == ( array_size( pilot_stack ) - 1 ) ) || ( m == -1 ) )
148 return PLAYER_ID;
149
150 /* Get first one in range. */
151 p = m + 1;
152 if ( mode == 0 ) {
153 while ( p < array_size( pilot_stack ) ) {
154 if ( ( !pilot_isWithPlayer( pilot_stack[p] ) ||
155 pilot_isDisabled( pilot_stack[p] ) ) &&
157 return pilot_stack[p]->id;
158 p++;
159 }
160 }
161 /* Get first hostile in range. */
162 if ( mode == 1 ) {
163 while ( p < array_size( pilot_stack ) ) {
164 if ( !pilot_isWithPlayer( pilot_stack[p] ) &&
167 return pilot_stack[p]->id;
168 p++;
169 }
170 }
171
172 /* None found. */
173 return PLAYER_ID;
174}
175
183unsigned int pilot_getPrevID( unsigned int id, int mode )
184{
185 int m, p;
186
187 /* Player must exist. */
188 if ( player.p == NULL )
189 return PLAYER_ID;
190
191 /* Get the pilot. */
192 m = pilot_getStackPos( id );
193
194 /* Check to see what position to try. */
195 if ( m == -1 )
196 return PLAYER_ID;
197 else if ( m == 0 )
198 p = array_size( pilot_stack ) - 1;
199 else
200 p = m - 1;
201
202 /* Get first one in range. */
203 if ( mode == 0 ) {
204 while ( p >= 0 ) {
205 if ( ( !pilot_isWithPlayer( pilot_stack[p] ) ||
206 ( pilot_isDisabled( pilot_stack[p] ) ) ) &&
208 return pilot_stack[p]->id;
209 p--;
210 }
211 }
212 /* Get first hostile in range. */
213 else if ( mode == 1 ) {
214 while ( p >= 0 ) {
215 if ( !pilot_isWithPlayer( pilot_stack[p] ) &&
216 !pilot_isFlag( pilot_stack[p], PILOT_HIDE ) &&
219 return pilot_stack[p]->id;
220 p--;
221 }
222 }
223
224 /* None found. */
225 return PLAYER_ID;
226}
227
235int pilot_validTarget( const Pilot *p, const Pilot *target )
236{
237 int inrange;
238 return pilot_validTargetRange( p, target, &inrange );
239}
240
250int pilot_validTargetRange( const Pilot *p, const Pilot *target, int *inrange )
251{
252 /* Must be targetable. */
253 if ( !pilot_canTarget( target ) ) {
254 *inrange = 0;
255 return 0;
256 }
257
258 /* Must be in range. */
259 *inrange = pilot_inRangePilot( p, target, NULL );
260 if ( !( *inrange ) )
261 return 0;
262
263 /* Pilot is a valid target. */
264 return 1;
265}
266
270int pilot_canTarget( const Pilot *p )
271{
272 /* Must not be dead. */
273 if ( pilot_isFlag( p, PILOT_DELETE ) || pilot_isFlag( p, PILOT_DEAD ) )
274 return 0;
275
276 /* Must not be hidden nor invisible. */
277 if ( pilot_isFlag( p, PILOT_HIDE ) || pilot_isFlag( p, PILOT_INVISIBLE ) )
278 return 0;
279
280 /* Pilot is a valid target. */
281 return 1;
282}
283
291int pilot_validEnemy( const Pilot *p, const Pilot *target )
292{
293 int inrange;
294
295 /* Shouldn't be disabled. */
296 if ( pilot_isDisabled( target ) )
297 return 0;
298
299 /* Shouldn't be invincible. */
300 if ( pilot_isFlag( target, PILOT_INVINCIBLE ) )
301 return 0;
302
303 /* Shouldn't be landing or taking off. */
304 if ( pilot_isFlag( target, PILOT_LANDING ) ||
305 pilot_isFlag( target, PILOT_TAKEOFF ) ||
306 pilot_isFlag( target, PILOT_NONTARGETABLE ) )
307 return 0;
308
309 /* Must be a valid target. */
310 if ( !pilot_validTargetRange( p, target, &inrange ) )
311 return 0;
312
313 /* Should either be hostile by faction or by player. */
314 if ( !pilot_areEnemies( p, target ) )
315 return 0;
316
317 /* Must not be fuzzy. */
318 if ( inrange != 1 )
319 return 0;
320
321 /* They're ok. */
322 return 1;
323}
324
331unsigned int pilot_getNearestEnemy( const Pilot *p )
332{
333 unsigned int tp = 0;
334 double d = 0.;
335
336 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
337 double td;
338
339 if ( !pilot_validEnemy( p, pilot_stack[i] ) )
340 continue;
341
342 /* Check distance. */
343 td = vec2_dist2( &pilot_stack[i]->solid.pos, &p->solid.pos );
344 if ( !tp || ( td < d ) ) {
345 d = td;
346 tp = pilot_stack[i]->id;
347 }
348 }
349 return tp;
350}
351
361unsigned int pilot_getNearestEnemy_size( const Pilot *p, double target_mass_LB,
362 double target_mass_UB )
363{
364 unsigned int tp = 0;
365 double d = 0.;
366
367 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
368 double td;
369
370 if ( !pilot_validEnemy( p, pilot_stack[i] ) )
371 continue;
372
373 if ( pilot_stack[i]->solid.mass < target_mass_LB ||
374 pilot_stack[i]->solid.mass > target_mass_UB )
375 continue;
376
377 /* Check distance. */
378 td = vec2_dist2( &pilot_stack[i]->solid.pos, &p->solid.pos );
379 if ( !tp || ( td < d ) ) {
380 d = td;
381 tp = pilot_stack[i]->id;
382 }
383 }
384
385 return tp;
386}
387
401 double mass_factor,
402 double health_factor,
403 double damage_factor,
404 double range_factor )
405{
406 unsigned int tp = 0;
407 double current_heuristic_value = 10e3;
408
409 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
410 double temp;
411 Pilot *target = pilot_stack[i];
412
413 if ( !pilot_validEnemy( p, target ) )
414 continue;
415
416 /* Check distance. */
417 temp = range_factor * vec2_dist2( &target->solid.pos, &p->solid.pos ) +
418 FABS( pilot_relsize( p, target ) - mass_factor ) +
419 FABS( pilot_relhp( p, target ) - health_factor ) +
420 FABS( pilot_reldps( p, target ) - damage_factor );
421
422 if ( ( tp == 0 ) || ( temp < current_heuristic_value ) ) {
423 current_heuristic_value = temp;
424 tp = target->id;
425 }
426 }
427
428 return tp;
429}
430
437unsigned int pilot_getNearestPilot( const Pilot *p )
438{
439 unsigned int t;
440 pilot_getNearestPos( p, &t, p->solid.pos.x, p->solid.pos.y, 0 );
441 return t;
442}
443
450unsigned int pilot_getBoss( const Pilot *p )
451{
452 unsigned int t;
453 double relpower, ppower;
454 /* TODO : all the parameters should be adjustable with arguments */
455
456 relpower = 0;
457 t = 0;
458
459 /* Initialized to 0.25 which would mean equivalent power. */
460 ppower = 0.5 * 0.5;
461
462 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
463 double dx, dy, td, curpower;
464
465 /* Must not be self. */
466 if ( pilot_stack[i] == p )
467 continue;
468
469 /* Shouldn't be disabled. */
470 if ( pilot_isDisabled( pilot_stack[i] ) )
471 continue;
472
473 /* Must be a valid target. */
474 if ( !pilot_validTarget( p, pilot_stack[i] ) )
475 continue;
476
477 /* Maximum distance in 2 seconds. */
478 dx = pilot_stack[i]->solid.pos.x + 2 * pilot_stack[i]->solid.vel.x -
479 p->solid.pos.x - 2 * p->solid.vel.x;
480 dy = pilot_stack[i]->solid.pos.y + 2 * pilot_stack[i]->solid.vel.y -
481 p->solid.pos.y - 2 * p->solid.vel.y;
482 td = sqrt( pow2( dx ) + pow2( dy ) );
483 if ( td > 5e3 )
484 continue;
485
486 /* Must have the same faction. */
487 if ( pilot_stack[i]->faction != p->faction )
488 continue;
489
490 /* Must be slower. */
491 if ( pilot_stack[i]->speed > p->speed )
492 continue;
493
494 /* Should not be weaker than the current pilot*/
495 curpower =
497 if ( ppower >= curpower )
498 continue;
499
500 if ( relpower < curpower ) {
501 relpower = curpower;
502 t = pilot_stack[i]->id;
503 }
504 }
505 return t;
506}
507
518double pilot_getNearestPosPilot( const Pilot *p, Pilot **tp, double x, double y,
519 int disabled )
520{
521 double d = 0.;
522 *tp = NULL;
523 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
524 double td;
525
526 /* Must not be self. */
527 if ( pilot_stack[i] == p )
528 continue;
529
530 /* Player doesn't select escorts (unless disabled is active). */
531 if ( !disabled && pilot_isPlayer( p ) &&
532 pilot_isWithPlayer( pilot_stack[i] ) )
533 continue;
534
535 /* Shouldn't be disabled. */
536 if ( !disabled && pilot_isDisabled( pilot_stack[i] ) )
537 continue;
538
539 /* Must be a valid target. */
540 if ( !pilot_validTarget( p, pilot_stack[i] ) )
541 continue;
542
543 /* Minimum distance. */
544 td = pow2( x - pilot_stack[i]->solid.pos.x ) +
545 pow2( y - pilot_stack[i]->solid.pos.y );
546 if ( ( ( *tp == NULL ) || ( td < d ) ) ) {
547 d = td;
548 *tp = pilot_stack[i];
549 }
550 }
551 return d;
552}
553
564double pilot_getNearestPos( const Pilot *p, unsigned int *tp, double x,
565 double y, int disabled )
566{
567 Pilot *pn;
568 double d = pilot_getNearestPosPilot( p, &pn, x, y, disabled );
569 if ( pn == NULL )
570 *tp = PLAYER_ID;
571 else
572 *tp = pn->id;
573 return d;
574}
575
585double pilot_getNearestAng( const Pilot *p, unsigned int *tp, double ang,
586 int disabled )
587{
588 double a = ang + M_PI;
589
590 *tp = PLAYER_ID;
591 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
592 double rx, ry, ta;
593 int inrange;
594
595 /* Must not be self. */
596 if ( pilot_stack[i] == p )
597 continue;
598
599 /* Player doesn't select escorts (unless disabled is active). */
600 if ( !disabled && pilot_isPlayer( p ) &&
601 pilot_isWithPlayer( pilot_stack[i] ) )
602 continue;
603
604 /* Shouldn't be disabled. */
605 if ( !disabled && pilot_isDisabled( pilot_stack[i] ) )
606 continue;
607
608 /* Must be a valid target. */
609 if ( !pilot_validTargetRange( p, pilot_stack[i], &inrange ) )
610 continue;
611
612 /* Must be in range. */
613 if ( !inrange )
614 continue;
615
616 /* Only allow selection if off-screen. */
617 if ( gui_onScreenPilot( &rx, &ry, pilot_stack[i] ) )
618 continue;
619
620 ta = atan2( p->solid.pos.y - pilot_stack[i]->solid.pos.y,
621 p->solid.pos.x - pilot_stack[i]->solid.pos.x );
622 if ( ABS( angle_diff( ang, ta ) ) < ABS( angle_diff( ang, a ) ) ) {
623 a = ta;
624 *tp = pilot_stack[i]->id;
625 }
626 }
627 return a;
628}
629
640Pilot *pilot_get( unsigned int id )
641{
642 const Pilot pid = { .id = id };
643 const Pilot *pidptr = &pid;
644 /* binary search */
645 Pilot **pp = bsearch( &pidptr, pilot_stack, array_size( pilot_stack ),
646 sizeof( Pilot * ), pilot_cmp );
647 if ( ( pp == NULL ) || ( pilot_isFlag( *pp, PILOT_DELETE ) ) )
648 return NULL;
649 return *pp;
650}
651
656{
657 Pilot *t;
658
659 /* Case no target. */
660 if ( p->target == p->id )
661 return NULL;
662
663 t = p->ptarget;
664 /* Return ptarget if exists and valid. */
665 if ( t != NULL ) {
666 if ( pilot_isFlag( t, PILOT_DELETE ) ) {
667 p->ptarget = NULL;
668 t = NULL;
669 }
670 return t;
671 }
672
673 p->ptarget = pilot_get( p->target );
674 return p->ptarget;
675}
676
680void pilot_setAccel( Pilot *p, double accel )
681{
682 p->solid.accel = p->accel * accel;
683}
684
688void pilot_setTurn( Pilot *p, double turn )
689{
690 p->solid.dir_vel = p->turn * turn;
691}
692
699int pilot_isHostile( const Pilot *p )
700{
701 if ( !pilot_isFriendly( p ) && !pilot_isFlag( p, PILOT_BRIBED ) &&
702 ( pilot_isFlag( p, PILOT_HOSTILE ) ||
703 areEnemiesSystem( FACTION_PLAYER, p->faction, cur_system ) ) )
704 return 1;
705
706 return 0;
707}
708
715int pilot_isNeutral( const Pilot *p )
716{
717 if ( !pilot_isHostile( p ) && !pilot_isFriendly( p ) )
718 return 1;
719 return 0;
720}
721
728int pilot_isFriendly( const Pilot *p )
729{
730 if ( pilot_isFlag( p, PILOT_FRIENDLY ) ||
731 ( areAlliesSystem( FACTION_PLAYER, p->faction, cur_system ) &&
732 !pilot_isFlag( p, PILOT_HOSTILE ) ) )
733 return 1;
734
735 return 0;
736}
737
741int pilot_areAllies( const Pilot *p, const Pilot *target )
742{
743 if ( pilot_isWithPlayer( p ) ) {
744 if ( pilot_isFriendly( target ) )
745 return 1;
746 else if ( pilot_isFlag( target, PILOT_HOSTILE ) )
747 return 0;
748 } else if ( pilot_isWithPlayer( target ) ) {
749 if ( pilot_isFriendly( p ) )
750 return 1;
751 else if ( pilot_isFlag( p, PILOT_HOSTILE ) )
752 return 0;
753 } else {
754 if ( areAlliesSystem( p->faction, target->faction, cur_system ) )
755 return 1;
756 }
757 return 0;
758}
759
763int pilot_areEnemies( const Pilot *p, const Pilot *target )
764{
765 if ( pilot_isWithPlayer( p ) ) {
766 if ( pilot_isHostile( target ) )
767 return 1;
768 else if ( pilot_isFlag( target, PILOT_FRIENDLY ) )
769 return 0;
770 else if ( pilot_isFlag( target, PILOT_BRIBED ) )
771 return 0;
772 }
773 if ( pilot_isWithPlayer( target ) ) {
774 if ( pilot_isHostile( p ) )
775 return 1;
776 else if ( pilot_isFlag( p, PILOT_FRIENDLY ) )
777 return 0;
778 else if ( pilot_isFlag( p, PILOT_BRIBED ) )
779 return 0;
780 } else {
781 if ( areEnemiesSystem( p->faction, target->faction, cur_system ) )
782 return 1;
783 }
784 return 0;
785}
786
794{
795 if ( ( p->dockpilot != 0 ) && ( p->dockslot != -1 ) ) {
796 Pilot *dockpilot = pilot_get( p->dockpilot );
797 if ( dockpilot != NULL )
798 return dockpilot->outfits[p->dockslot];
799 }
800 p->dockpilot = 0;
801 p->dockslot = -1;
802 return NULL;
803}
804
805const IntList *pilot_collideQuery( int x1, int y1, int x2, int y2 )
806{
807 qt_query( &pilot_quadtree, &pilot_qtquery, x1, y1, x2, y2 );
808 return &pilot_qtquery;
809}
810
811void pilot_collideQueryIL( IntList *il, int x1, int y1, int x2, int y2 )
812{
813 qt_query( &pilot_quadtree, il, x1, y1, x2, y2 );
814}
815
827double pilot_face( Pilot *p, double dir, double dt )
828{
829 double diff = angle_diff( p->solid.dir, dir );
830 double turn = CLAMP( -1., 1., diff / ( p->turn * dt ) );
831 pilot_setTurn( p, turn );
832 return diff;
833}
834
839{
840 if ( p->stats.misc_reverse_thrust ) {
841 double diff, btime, ftime, vel, t;
842 vel = MIN( VMOD( p->solid.vel ), p->speed );
843 t = vel / p->accel;
844
845 /* Calculate the time to face backward and apply forward thrust. */
846 diff = angle_diff( p->solid.dir, VANGLE( p->solid.vel ) + M_PI );
847 btime = ABS( diff ) / p->turn + t;
848
849 /* Calculate the time to face forward and apply reverse thrust. */
850 diff = angle_diff( p->solid.dir, VANGLE( p->solid.vel ) );
851 ftime = ABS( diff ) / p->turn + t / PILOT_REVERSE_THRUST;
852
853 if ( btime > ftime )
854 return 1;
855 }
856 return 0;
857}
858
862double pilot_minbrakedist( const Pilot *p, double dt, double *flytime )
863{
864 double vel = MIN( MIN( VMOD( p->solid.vel ), p->speed ),
865 solid_maxspeed( &p->solid, p->speed, p->accel ) );
866 double accel = p->accel;
867 double t = vel / accel; /* Try to improve accuracy. */
869 t /= PILOT_REVERSE_THRUST; /* Have to compensate slower accel. */
870 *flytime = t + dt;
871 return vel * ( t + dt ) -
872 0.5 * PILOT_REVERSE_THRUST * accel * pow2( t - dt );
873 }
874 /* Small compensation for current delta-tick. */
875 *flytime = t + M_PI / p->turn + dt;
876 return vel * ( *flytime ) - 0.5 * accel * pow2( t - dt );
877}
878
886int pilot_brake( Pilot *p, double dt )
887{
888 double dir, accel, diff;
889 int isstopped = pilot_isStopped( p );
890
891 if ( isstopped )
892 return 1;
893
895 dir = VANGLE( p->solid.vel );
896 accel =
897 -MIN( PILOT_REVERSE_THRUST, VMOD( p->solid.vel ) / ( p->accel * dt ) );
898 } else {
899 dir = VANGLE( p->solid.vel ) + M_PI;
900 accel = MIN( 1., VMOD( p->solid.vel ) / ( p->accel * dt ) );
901 }
902
903 diff = pilot_face( p, dir, dt );
904 if ( ABS( diff ) < MIN_DIR_ERR )
905 pilot_setAccel( p, accel );
906 else
907 pilot_setAccel( p, 0. );
908 return 0;
909}
910
918void pilot_cooldown( Pilot *p, int dochecks )
919{
920 double heat_capacity, heat_mean;
921
922 /* Brake if necessary. */
923 if ( dochecks && !pilot_isStopped( p ) ) {
924 pilot_setFlag( p, PILOT_BRAKING );
925 pilot_setFlag( p, PILOT_COOLDOWN_BRAKE );
926 return;
927 } else {
928 pilot_rmFlag( p, PILOT_BRAKING );
929 pilot_rmFlag( p, PILOT_COOLDOWN_BRAKE );
930 }
931
932 if ( p->id == PLAYER_ID )
933 player_message( _( "#oActive cooldown engaged." ) );
934
935 /* Turn off all weapon sets. */
938
939 /* Disable active outfits. */
940 pilotoutfit_modified = 0;
941 if ( ( pilot_outfitOffAll( p ) > 0 ) || pilotoutfit_modified )
942 pilot_calcStats( p );
943
944 /* Calculate the ship's overall heat. */
945 heat_capacity = p->heat_C;
946 heat_mean = p->heat_T * p->heat_C;
947 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
948 PilotOutfitSlot *o = p->outfits[i];
949 o->heat_start = o->heat_T;
950 heat_capacity += p->outfits[i]->heat_C;
951 heat_mean += o->heat_T * o->heat_C;
952 }
953
954 /* Paranoia - a negative mean heat will result in NaN cdelay. */
955 heat_mean = MAX( heat_mean, CONST_SPACE_STAR_TEMP );
956
957 heat_mean /= heat_capacity;
958
959 /*
960 * Base delay of about 9.5s for a Lancelot, 32.8s for a Peacemaker.
961 *
962 * Super heat penalty table:
963 * 300K: 13.4%
964 * 350K: 31.8%
965 * 400K: 52.8%
966 * 450K: 75.6%
967 * 500K: 100.0%
968 */
969 p->cdelay =
970 ( 5. + sqrt( p->base_mass ) / 2. ) *
971 ( 1. + pow( MAX( heat_mean / CONST_SPACE_STAR_TEMP - 1., 0. ), 1.25 ) );
972 p->ctimer = p->cdelay * p->stats.cooldown_time;
973 p->heat_start = p->heat_T;
974 pilot_setFlag( p, PILOT_COOLDOWN );
975
976 /* Run outfit cooldown start hook. */
977 pilot_outfitLCooldown( p, 0, 0, p->ctimer );
978}
979
986void pilot_cooldownEnd( Pilot *p, const char *reason )
987{
988 if ( pilot_isFlag( p, PILOT_COOLDOWN_BRAKE ) ) {
989 pilot_rmFlag( p, PILOT_COOLDOWN_BRAKE );
990 return;
991 }
992
993 /* Send message to player. */
994 if ( p->id == PLAYER_ID ) {
995 if ( p->ctimer < 0. )
996 player_message( "#o%s", _( "Active cooldown completed." ) );
997 else {
998 if ( reason != NULL )
999 player_message( _( "#rActive cooldown aborted: %s!" ), reason );
1000 else
1001 player_message( "#r%s", _( "Active cooldown aborted!" ) );
1002 }
1004 }
1005
1006 pilot_rmFlag( p, PILOT_COOLDOWN );
1007
1008 /* Cooldown finished naturally, reset heat just in case. */
1009 if ( p->ctimer < 0. ) {
1010 pilot_heatReset( p );
1011 pilot_fillAmmo( p );
1012 pilot_outfitLCooldown( p, 1, 1, 0. );
1013 } else {
1014 pilot_outfitLCooldown( p, 1, 0, 0. );
1015 }
1016}
1017
1025double pilot_aimAngle( Pilot *p, const vec2 *pos, const vec2 *vel )
1026{
1027 double x, y;
1028 double t;
1029 vec2 tv, approach_vector, relative_location, orthoradial_vector;
1030 double dist;
1031 double speed;
1032 double radial_speed;
1033 double orthoradial_speed;
1034
1035 /* Get the distance */
1036 dist = vec2_dist( &p->solid.pos, pos );
1037
1038 /* Check if should recalculate weapon speed with secondary weapon. */
1039 speed = pilot_weapSetSpeed( p, 0 ); /* primary. */
1040
1041 /* determine the radial, or approach speed */
1042 /*
1043 *approach_vector (denote Va) is the relative velocites of the pilot and
1044 *target relative_location (denote Vr) is the vector that points from the
1045 *target to the pilot
1046 *
1047 *Va dot Vr is the rate of approach between the target and the pilot.
1048 *If this is greater than 0, the target is approaching the pilot, if less
1049 *than 0, the target is fleeing.
1050 *
1051 *Va dot Vr + ShotSpeed is the net closing velocity for the shot, and is used
1052 *to compute the time of flight for the shot.
1053 */
1054 vec2_cset( &approach_vector, VX( p->solid.vel ) - VX( *vel ),
1055 VY( p->solid.vel ) - VY( *vel ) );
1056 vec2_cset( &relative_location, VX( *pos ) - VX( p->solid.pos ),
1057 VY( *pos ) - VY( p->solid.pos ) );
1058 vec2_cset( &orthoradial_vector, VY( p->solid.pos ) - VY( *pos ),
1059 VX( *pos ) - VX( p->solid.pos ) );
1060
1061 radial_speed = vec2_dot( &approach_vector, &relative_location );
1062 radial_speed = radial_speed / VMOD( relative_location );
1063
1064 orthoradial_speed = vec2_dot( &approach_vector, &orthoradial_vector );
1065 orthoradial_speed = orthoradial_speed / VMOD( relative_location );
1066
1067 /* Time for shots to reach that distance */
1068 /* t is the real positive solution of a 2nd order equation*/
1069 /* if the target is not hittable (i.e., fleeing faster than our shots can
1070 * fly, determinant <= 0), just face the target */
1071 if ( ( ( speed * speed -
1072 VMOD( approach_vector ) * VMOD( approach_vector ) ) != 0 ) &&
1073 ( speed * speed - orthoradial_speed * orthoradial_speed ) > 0 )
1074 t = dist *
1075 ( sqrt( speed * speed - orthoradial_speed * orthoradial_speed ) -
1076 radial_speed ) /
1077 ( speed * speed - VMOD( approach_vector ) * VMOD( approach_vector ) );
1078 else
1079 t = 0;
1080
1081 /* if t < 0, try the other solution*/
1082 if ( t < 0 )
1083 t = -dist *
1084 ( sqrt( speed * speed - orthoradial_speed * orthoradial_speed ) +
1085 radial_speed ) /
1086 ( speed * speed - VMOD( approach_vector ) * VMOD( approach_vector ) );
1087
1088 /* if t still < 0, no solution*/
1089 if ( t < 0 )
1090 t = 0;
1091
1092 /* Position is calculated on where it should be */
1093 x = pos->x + vel->x * t - ( p->solid.pos.x + p->solid.vel.x * t );
1094 y = pos->y + vel->y * t - ( p->solid.pos.y + p->solid.vel.y * t );
1095 vec2_cset( &tv, x, y );
1096
1097 return VANGLE( tv );
1098}
1099
1106{
1107 /* Don't let player ships be hostile to player. */
1108 if ( pilot_isWithPlayer( p ) )
1109 return;
1110 pilot_setFlag( p, PILOT_HOSTILE );
1111 pilot_rmFriendly( p );
1112 pilot_rmFlag( p, PILOT_BRIBED );
1113}
1114
1122{
1123 if ( pilot_isDisabled( p ) )
1124 return 'I';
1125 else if ( pilot_isFriendly( p ) )
1126 return 'F';
1127 else if ( pilot_isHostile( p ) )
1128 return 'H';
1129 else if ( pilot_isFlag( p, PILOT_BRIBED ) )
1130 return 'N';
1131 return faction_reputationColourCharSystem( p->faction, cur_system );
1132}
1133
1137void pilot_setCommMsg( Pilot *p, const char *s )
1138{
1139 free( p->comm_msg );
1140 p->comm_msg = strdup( s );
1141 p->comm_msgWidth = gl_printWidthRaw( NULL, s );
1142 p->comm_msgTimer = pilot_commTimeout;
1143}
1144
1152void pilot_broadcast( Pilot *p, const char *msg, int ignore_int )
1153{
1154 char c;
1155
1156 /* Only display if player.p exists and is in range. */
1157 if ( player.p == NULL )
1158 return;
1159
1160 /* Check if should ignore interference. */
1161 if ( !ignore_int && !pilot_inRangePilot( player.p, p, NULL ) )
1162 return;
1163
1165 player_message( _( "#%cBroadcast %s>#0 \"%s\"" ), c, p->name, msg );
1166
1167 /* Set comm message. */
1168 pilot_setCommMsg( p, msg );
1169}
1170
1180void pilot_distress( Pilot *p, Pilot *attacker, const char *msg )
1181{
1182 int r;
1183 double d;
1184
1185 /* Broadcast the message. */
1186 if ( ( msg != NULL ) && ( msg[0] != '\0' ) )
1187 pilot_broadcast( p, msg, 0 );
1188
1189 /* Use the victim's target if the attacker is unknown. */
1190 if ( attacker == NULL )
1191 attacker = pilot_getTarget( p );
1192
1193 /* Now proceed to see if player.p should incur faction loss because
1194 * of the broadcast signal. */
1195
1196 /* Consider not in range at first. */
1197 r = 0;
1198
1199 if ( !pilot_isFlag( p, PILOT_DISTRESSED ) ) {
1200 /* Check if spob is in range. */
1201 for ( int i = 0; i < array_size( cur_system->spobs ); i++ ) {
1202 Spob *spb = cur_system->spobs[i];
1203 if ( spob_hasService( spb, SPOB_SERVICE_INHABITED ) &&
1204 pilot_inRangeSpob( p, i ) ) {
1205 spob_distress( spb, p, attacker );
1206 if ( !areEnemies( p->faction, spb->presence.faction ) ) {
1207 r = 1;
1208 }
1209 }
1210 }
1211 }
1212
1213 /* Now we must check to see if a pilot is in range. */
1214 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
1215 Pilot *pi = pilot_stack[i];
1216
1217 /* Skip if unsuitable. */
1218 if ( ( pi->ai == NULL ) || ( pi->id == p->id ) ||
1219 ( pilot_isFlag( pi, PILOT_DEAD ) ) ||
1220 ( pilot_isFlag( pi, PILOT_DELETE ) ) )
1221 continue;
1222
1223 if ( !pilot_inRangePilot( p, pi, NULL ) ) {
1224 /*
1225 * If the pilots are within sensor range of each other, send the
1226 * distress signal, regardless of electronic warfare hide values.
1227 */
1228 d = vec2_dist2( &p->solid.pos, &pi->solid.pos );
1229 if ( d > pilot_sensorRange() )
1230 continue;
1231 }
1232
1233 /* Send AI the distress signal. */
1234 ai_getDistress( pi, p, attacker );
1235
1236 /* Check if should take faction hit. */
1237 if ( !pilot_isFlag( p, PILOT_DISTRESSED ) &&
1238 !areEnemies( p->faction, pi->faction ) )
1239 r = 1;
1240 }
1241
1242 /* Player only gets one faction hit per pilot. */
1243 if ( !pilot_isFlag( p, PILOT_DISTRESSED ) ) {
1244
1245 /* Modify faction, about 1 for a llama, 4.2 for a hawking */
1246 if ( r && ( attacker != NULL ) && ( pilot_isWithPlayer( attacker ) ) ) {
1247 if ( p->ai == NULL )
1248 WARN( _( "Pilot '%s' does not have an AI!" ), p->name );
1249 else {
1250 double hit;
1251 lua_rawgeti( naevL, LUA_REGISTRYINDEX, p->lua_mem ); /* m */
1252 lua_getfield( naevL, -1, "distress_hit" ); /* m, v */
1253 if ( lua_isnil( naevL, -1 ) )
1254 hit = MAX( 0., pow( p->ship->points, 0.37 ) - 2. );
1255 else if ( lua_isnumber( naevL, -1 ) )
1256 hit = lua_tonumber( naevL, -1 );
1257 else {
1258 WARN( _( "Pilot '%s' has non-number mem.distress_hit!" ),
1259 p->name );
1260 hit = 0.;
1261 }
1262 lua_pop( naevL, 2 );
1263 faction_hit( p->faction, cur_system, -hit, "distress", 0 );
1264 }
1265 }
1266
1267 /* Set flag to avoid a second faction hit. */
1268 pilot_setFlag( p, PILOT_DISTRESSED );
1269 }
1270}
1271
1278{
1279 if ( !pilot_isHostile( p ) )
1280 return;
1281
1282 if ( pilot_isFlag( p, PILOT_HOSTILE ) )
1283 pilot_rmFlag( p, PILOT_HOSTILE );
1284
1285 /* Set "bribed" flag if faction has poor reputation */
1286 if ( areEnemies( FACTION_PLAYER, p->faction ) )
1287 pilot_setFlag( p, PILOT_BRIBED );
1288}
1289
1296{
1297 pilot_rmHostile( p );
1298 pilot_setFlag( p, PILOT_FRIENDLY );
1299}
1300
1307{
1308 pilot_rmFlag( p, PILOT_FRIENDLY );
1309}
1310
1317int pilot_getJumps( const Pilot *p )
1318{
1319 return p->fuel / p->fuel_consumption;
1320}
1321
1328const glColour *pilot_getColour( const Pilot *p )
1329{
1330 const glColour *col;
1331
1332 if ( pilot_inRangePilot( player.p, p, NULL ) == -1 )
1333 col = &cNeutral;
1334 else if ( pilot_isDisabled( p ) || pilot_isFlag( p, PILOT_DEAD ) )
1335 col = &cInert;
1336 else if ( pilot_isFriendly( p ) )
1337 col = &cFriend;
1338 else if ( pilot_isHostile( p ) )
1339 col = &cHostile;
1340 else
1341 col = &cNeutral;
1342
1343 return col;
1344}
1345
1352void pilot_setTarget( Pilot *p, unsigned int id )
1353{
1354 /* Case no change. */
1355 if ( p->target == id )
1356 return;
1357
1358 p->target = id;
1359 p->ptarget = NULL; /* Gets recomputed later in pilot_getTarget. */
1360 pilot_lockClear( p );
1361
1362 /* Set the scan timer. */
1363 pilot_ewScanStart( p );
1364
1365 /* Untarget asteroid (if any). */
1366 p->nav_anchor = -1;
1367 p->nav_asteroid = -1;
1368}
1369
1383double pilot_hit( Pilot *p, const Solid *w, const Pilot *pshooter,
1384 const Damage *dmg, const Outfit *outfit, int lua_mem,
1385 int reset )
1386{
1387 int shooter;
1388 double damage_shield, damage_armour, disable, knockback, dam_mod, ddmg, ddis,
1389 absorb, dmod, start;
1390 double tdshield, tdarmour;
1391
1392 /* Invincible means no damage. */
1393 if ( pilot_isFlag( p, PILOT_INVINCIBLE ) || pilot_isFlag( p, PILOT_HIDE ) ||
1394 ( ( pshooter != NULL ) && pilot_isWithPlayer( pshooter ) &&
1395 pilot_isFlag( p, PILOT_INVINC_PLAYER ) ) )
1396 return 0.;
1397
1398 /* Defaults. */
1399 dam_mod = 0.;
1400 ddmg = 0.;
1401 ddis = 0.;
1402 shooter = ( pshooter == NULL ) ? 0 : pshooter->id;
1403
1404 /* Calculate the damage. */
1405 absorb = 1. - CLAMP( 0., 1., p->dmg_absorb - dmg->penetration );
1406 disable = dmg->disable;
1407 dtype_calcDamage( &damage_shield, &damage_armour, absorb, &knockback, dmg,
1408 &p->stats );
1409
1410 /*
1411 * Delay undisable if necessary. Amount varies with damage, as e.g. a
1412 * single Laser Cannon shot should not reset a Peacemaker's timer.
1413 */
1414 if ( !pilot_isFlag( p, PILOT_DEAD ) && ( p->dtimer_accum > 0. ) )
1415 p->dtimer_accum -= MIN( pow( disable, 0.8 ), p->dtimer_accum );
1416
1417 /* Ships that can not be disabled take raw armour damage instead of getting
1418 * disabled. */
1419 if ( pilot_isFlag( p, PILOT_NODISABLE ) ) {
1420 damage_armour += disable * absorb;
1421 disable = 0.;
1422 } else
1423 disable *= absorb;
1424 tdshield = 0.;
1425 tdarmour = 0.;
1426
1427 /*
1428 * Shields take entire blow.
1429 */
1430 if ( p->shield - damage_shield > 0. ) {
1431 start = p->shield;
1432 ddmg = damage_shield;
1433 p->shield -= damage_shield;
1434 dam_mod = damage_shield / p->shield_max;
1435
1436 /*
1437 * Disabling damage leaks accordingly:
1438 * 50% + (50% - mean shield % / 2)
1439 *
1440 * 50% leaks at 100% shield, scales to 100% by 0% shield.
1441 *
1442 * The damage is adjusted based on the mean of the starting and ending
1443 * shield percentages. Using the starting percentage biases towards
1444 * low-damage, high-ROF weapons, while using the ending percentage
1445 * biases towards high-damage, low-ROF weapons.
1446 */
1447 ddis = disable *
1448 ( 0.5 + ( 0.5 - ( ( start + p->shield ) / p->shield_max ) / 4. ) );
1449 p->stress += ddis;
1450
1451 /* True damage. */
1452 tdshield = damage_shield;
1453 }
1454 /*
1455 * Shields take part of the blow.
1456 */
1457 else if ( p->shield > 0. ) {
1458 start = p->shield;
1459 dmod = ( 1. - p->shield / damage_shield );
1460 ddmg = p->shield + dmod * damage_armour;
1461 tdshield = p->shield;
1462 p->shield = 0.;
1463
1464 /* Leak some disabling damage through the remaining bit of shields. */
1465 ddis = disable * ( 1. - dmod ) *
1466 ( 0.5 + ( 0.5 - ( start / p->shield_max / 4. ) ) );
1467 p->stress += ddis;
1468
1469 /* Reduce stress as armour is eaten away. */
1470 // p->stress *= (p->armour - dmod * damage_armour) / p->armour;
1471 tdarmour = dmod * damage_armour;
1472 p->armour -= tdarmour;
1473 p->stress += dmod * disable;
1474 dam_mod = ( damage_shield + damage_armour ) /
1475 ( ( p->shield_max + p->armour_max ) / 2. );
1476
1477 /* Increment shield timer or time before shield regeneration kicks in. */
1478 if ( reset ) {
1479 p->stimer = 5. * p->stats.shielddown_mod;
1480 p->sbonus = 3.;
1481 }
1482 }
1483 /*
1484 * Armour takes the entire blow.
1485 */
1486 else if ( p->armour > 0. ) {
1487 ddmg = damage_armour;
1488 /* Reduce stress as armour is eaten away. */
1489 // p->stress *= (p->armour - damage_armour) / p->armour;
1490 p->armour -= damage_armour;
1491 ddis = disable;
1492 p->stress += ddis;
1493
1494 /* Increment shield timer or time before shield regeneration kicks in. */
1495 if ( reset ) {
1496 p->stimer = 5. * p->stats.shielddown_mod;
1497 p->sbonus = 3.;
1498 }
1499
1500 /* True damage. */
1501 tdarmour = ddmg;
1502 }
1503
1504 /* Do not let pilot die. */
1505 if ( ( p->armour <= 0. ) && pilot_isFlag( p, PILOT_NODEATH ) ) {
1506 p->armour = 1.;
1507 p->stress = 1.;
1508 }
1509
1510 /* Can't undisable permanently disabled pilots through shooting. */
1511 if ( pilot_isFlag( p, PILOT_DISABLED_PERM ) )
1512 p->stress = p->armour;
1513
1514 /* Some minor effects and stuff. */
1515 if ( p->shield <= 0. ) {
1516 if ( p->id == PLAYER_ID ) { /* a bit of shaking */
1517 double spfx_mod = tdarmour / p->armour_max;
1518 spfx_shake( 0.5 * spfx_mod );
1519 spfx_damage( spfx_mod );
1520 }
1521 }
1522
1523 /* Update player meta-data if applicable. */
1524 if ( p->id == PLAYER_ID ) {
1525 player.dmg_taken_shield += tdshield;
1526 player.dmg_taken_armour += tdarmour;
1527 player.ps.dmg_taken_shield += tdshield;
1528 player.ps.dmg_taken_armour += tdarmour;
1529 }
1530 /* See if shooter is with player. */
1531 else if ( ( pshooter != NULL ) && pilot_isWithPlayer( pshooter ) ) {
1532 player.dmg_done_shield += tdshield;
1533 player.dmg_done_armour += tdarmour;
1534 player.ps.dmg_done_shield += tdshield;
1535 player.ps.dmg_done_armour += tdarmour;
1536 }
1537
1538 if ( w != NULL )
1539 /* knock back effect is dependent on both damage and mass of the weapon
1540 * should probably get turned into a partial conservative collision */
1541 vec2_cadd(
1542 &p->solid.vel,
1543 knockback *
1544 ( w->vel.x * ( dam_mod / 9. + w->mass / p->solid.mass / 6. ) ),
1545 knockback *
1546 ( w->vel.y * ( dam_mod / 9. + w->mass / p->solid.mass / 6. ) ) );
1547
1548 /* On hit weapon effects. */
1549 if ( ( outfit != NULL ) && ( outfit->lua_onimpact != LUA_NOREF ) ) {
1550 lua_rawgeti( naevL, LUA_REGISTRYINDEX, lua_mem ); /* mem */
1551 nlua_setenv( naevL, outfit->lua_env, "mem" ); /* */
1552
1553 /* Set up the function: onimpact( pshooter, p ) */
1554 lua_rawgeti( naevL, LUA_REGISTRYINDEX, outfit->lua_onimpact ); /* f */
1555 lua_pushpilot( naevL, shooter ); /* f, p */
1556 lua_pushpilot( naevL, p->id ); /* f, p, p */
1557 lua_pushvector( naevL, w->pos ); /* f, p, p, x */
1558 lua_pushvector( naevL, w->vel ); /* f, p, p, x, v */
1559 lua_pushoutfit( naevL, outfit ); /* f, p, p, x, v, o */
1560 if ( nlua_pcall( outfit->lua_env, 5, 0 ) ) { /* */
1561 WARN( _( "Pilot '%s''s outfit '%s' -> '%s':\n%s" ), p->name,
1562 outfit->name, "onimpact", lua_tostring( naevL, -1 ) );
1563 lua_pop( naevL, 1 );
1564 }
1565 }
1566
1567 /* On hit Lua outfits activate. */
1568 pilot_outfitLOnhit( p, tdarmour, tdshield, shooter );
1569
1570 /* Run disabled before death. Should be run after on hit effects in case of
1571 * regen. */
1572 pilot_updateDisable( p, shooter );
1573
1574 /* Officially dead, run after in case they are regenerated by outfit. */
1575 if ( p->armour <= 0. ) {
1576 p->armour = 0.;
1577
1578 if ( !pilot_isFlag( p, PILOT_DEAD ) ) {
1579 pilot_dead( p, shooter );
1580
1581 /* adjust the combat rating based on pilot mass and ditto faction */
1582 if ( ( p->armour <= 0. ) && ( pshooter != NULL ) &&
1583 pilot_isWithPlayer( pshooter ) ) {
1584 /* Modify faction for him and friends. */
1585 faction_hit( p->faction, cur_system, -p->ship->points, "destroy",
1586 0 );
1587
1588 /* Note that player destroyed the ship. */
1589 player.ships_destroyed[p->ship->class]++;
1590 player.ps.ships_destroyed[p->ship->class]++;
1591 }
1592 }
1593 }
1594
1595 return ddmg + ddis;
1596}
1597
1605void pilot_updateDisable( Pilot *p, unsigned int shooter )
1606{
1607 if ( ( !pilot_isFlag( p, PILOT_DISABLED ) ) &&
1608 ( !pilot_isFlag( p, PILOT_NODISABLE ) || ( p->armour <= 0. ) ) &&
1609 ( p->armour <= p->stress ) ) { /* Pilot should be disabled. */
1610 HookParam hparam;
1611
1612 /* Cooldown is an active process, so cancel it. */
1613 if ( pilot_isFlag( p, PILOT_COOLDOWN ) )
1614 pilot_cooldownEnd( p, NULL );
1615
1616 /* Clear other active states. */
1617 pilot_rmFlag( p, PILOT_COOLDOWN_BRAKE );
1618 pilot_rmFlag( p, PILOT_BRAKING );
1619 pilot_rmFlag( p, PILOT_STEALTH );
1620
1621 /* Clear hyperspace flags. */
1622 pilot_rmFlag( p, PILOT_HYP_PREP );
1623 pilot_rmFlag( p, PILOT_HYP_BEGIN );
1624 pilot_rmFlag( p, PILOT_HYP_BRAKE );
1625 pilot_rmFlag( p, PILOT_HYPERSPACE );
1626
1627 /* Disabled ships don't use up presence. */
1628 if ( p->presence > 0 ) {
1629 system_rmCurrentPresence( cur_system, p->faction_spawn, p->presence );
1630 p->presence = 0;
1631 }
1632
1633 /* Set disable timer. This is the time the pilot will remain disabled. */
1634 /* 200 mass llama => 46.78 s
1635 * 8000 mass peacemaker => 156 s
1636 */
1637 p->dtimer = 8. * pow( p->solid.mass, 1. / 3. );
1638 p->dtimer_accum = 0.;
1639
1640 /* Disable active outfits. */
1641 pilotoutfit_modified = 0;
1642 if ( ( pilot_outfitOffAll( p ) > 0 ) || pilotoutfit_modified )
1643 pilot_calcStats( p );
1644
1645 pilot_setFlag( p, PILOT_DISABLED ); /* set as disabled */
1646 if ( pilot_isPlayer( p ) )
1647 player_message( "#r%s", _( "You have been disabled!" ) );
1648
1649 /* Run hook */
1650 if ( shooter > 0 ) {
1651 hparam.type = HOOK_PARAM_PILOT;
1652 hparam.u.lp = shooter;
1653 } else {
1654 hparam.type = HOOK_PARAM_NIL;
1655 }
1656 pilot_runHookParam( p, PILOT_HOOK_DISABLE, &hparam,
1657 1 ); /* Already disabled. */
1658 } else if ( pilot_isFlag( p, PILOT_DISABLED ) &&
1659 ( p->armour >
1660 p->stress ) ) { /* Pilot is disabled, but shouldn't be. */
1661 pilot_rmFlag( p, PILOT_DISABLED ); /* Undisable. */
1662 pilot_rmFlag(
1663 p, PILOT_DISABLED_PERM ); /* Clear perma-disable flag if necessary. */
1664 pilot_rmFlag( p, PILOT_BOARDING ); /* Can get boarded again. */
1665
1666 /* Reset the accumulated disable time. */
1667 p->dtimer_accum = 0.;
1668
1669 /* TODO: Make undisabled pilot use up presence again. */
1670 pilot_runHook( p, PILOT_HOOK_UNDISABLE );
1671
1672 /* This is sort of a hack to make sure it gets reset... */
1673 if ( pilot_isPlayer( p ) ) {
1675 player_message( "#g%s",
1676 _( "You have recovered control of your ship!" ) );
1677 }
1678 }
1679}
1680
1687void pilot_dead( Pilot *p, unsigned int killer )
1688{
1689 HookParam hparam;
1690
1691 if ( pilot_isFlag( p, PILOT_DEAD ) )
1692 return; /* he's already dead */
1693
1694 /* basically just set timers */
1695 if ( p->id == PLAYER_ID ) {
1696 pilot_setFlag( p, PILOT_DISABLED );
1697 player_dead();
1698 }
1699 p->timer[0] = 0.; /* no need for AI anymore */
1700 p->ptimer = MIN( 1. + sqrt( p->armour_max * p->shield_max ) / 650.,
1701 3 + pow( p->armour_max * p->shield_max, 0.4 ) / 500 );
1702 p->timer[1] = 0.; /* explosion timer */
1703
1704 /* flag cleanup - fixes some issues */
1705 pilot_rmFlag( p, PILOT_HYP_PREP );
1706 pilot_rmFlag( p, PILOT_HYP_BEGIN );
1707 pilot_rmFlag( p, PILOT_HYP_BRAKE );
1708 pilot_rmFlag( p, PILOT_HYPERSPACE );
1709
1710 /* Turn off all outfits, should disable Lua stuff as necessary. */
1711 pilot_outfitOffAll( p );
1712 /* More aggressive force-turning off so that stats can be properly computed
1713 * at the equipment place. */
1714 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
1715 PilotOutfitSlot *po = p->outfits[i];
1716 po->state = PILOT_OUTFIT_OFF;
1717 po->flags &= ~( PILOTOUTFIT_DYNAMIC_FLAGS | PILOTOUTFIT_ISON );
1718 }
1719
1720 /* Pilot must die before setting death flag and probably messing with other
1721 * flags. */
1722 if ( killer > 0 ) {
1723 hparam.type = HOOK_PARAM_PILOT;
1724 hparam.u.lp = killer;
1725 } else
1726 hparam.type = HOOK_PARAM_NIL;
1727 pilot_runHookParam( p, PILOT_HOOK_DEATH, &hparam, 1 );
1728
1729 /* Need a check here in case the hook "regenerates" the pilot. */
1730 if ( p->armour > 0. )
1731 return;
1732
1733 if ( p->parent == PLAYER_ID )
1734 player_message( _( "#rShip under command '%s' was destroyed!#0" ),
1735 p->name );
1736 /* PILOT R OFFICIALLY DEADZ0R */
1737 pilot_setFlag( p, PILOT_DEAD );
1738
1739 /* Run Lua if applicable. */
1741
1742 /* Kill deployed ships. */
1743 for ( int i = 0; i < array_size( p->escorts ); i++ ) {
1744 /* Make sure the followers are valid. */
1745 Pilot *pe = pilot_get( p->escorts[i].id );
1746 if ( ( pe == NULL ) || pilot_isFlag( pe, PILOT_DEAD ) ||
1747 pilot_isFlag( pe, PILOT_HIDE ) )
1748 continue;
1749
1750 if ( !pilot_isFlag( pe, PILOT_CARRIED ) )
1751 continue;
1752
1753 /* Update stats, they will include damage over time now. */
1754 pilot_setFlag( pe, PILOT_CARRIER_DIED );
1755 pilot_calcStats( pe );
1756 }
1757}
1758
1767void pilot_explode( double x, double y, double radius, const Damage *dmg,
1768 const Pilot *parent )
1769{
1770 double rad2;
1771 Solid s; /* Only need to manipulate mass and vel. */
1772 Damage ddmg;
1773 const IntList *qt;
1774 int qx, qy, qr;
1775
1776 rad2 = radius * radius;
1777 ddmg = *dmg;
1778
1779 qx = round( x );
1780 qy = round( y );
1781 qr = ceil( radius );
1782 qt = pilot_collideQuery( qx - qr, qy - qr, qx + qr, qy + qr );
1783 for ( int i = 0; i < il_size( qt ); i++ ) {
1784 Pilot *p = pilot_stack[il_get( qt, i, 0 )];
1785 double rx, ry, dist;
1786
1787 /* Calculate a bit. */
1788 rx = p->solid.pos.x - x;
1789 ry = p->solid.pos.y - y;
1790 dist = pow2( rx ) + pow2( ry );
1791 /* Take into account ship size. */
1792 dist -= pow2( p->ship->size );
1793 dist = MAX( 0, dist );
1794
1795 /* Pilot is not hit. */
1796 if ( dist > rad2 )
1797 continue;
1798
1799 /* Adjust damage based on distance. */
1800 ddmg.damage = dmg->damage * ( 1. - sqrt( dist / rad2 ) );
1801
1802 /* Impact settings. */
1803 s.mass = pow2( dmg->damage ) / 30.;
1804 s.vel.x = rx;
1805 s.vel.y = ry;
1806
1807 /* Actual damage calculations. */
1808 pilot_hit( p, &s, parent, &ddmg, NULL, LUA_NOREF, 1 );
1809
1810 /* Shock wave from the explosion. */
1811 if ( p->id == PILOT_PLAYER )
1812 spfx_shake( pow2( ddmg.damage ) / pow2( 100. ) );
1813 }
1814}
1815
1825static void pilot_renderFramebufferBase( Pilot *p, GLuint fbo, double fw,
1826 double fh, const Lighting *L )
1827{
1828 glColour c = { 1., 1., 1., 1. };
1829
1830 /* Add some transparency if stealthed. TODO better effect */
1831 if ( !pilot_isPlayer( p ) && pilot_isFlag( p, PILOT_STEALTH ) )
1832 c.a = 0.5;
1833
1834 ship_renderFramebuffer( p->ship, fbo, fw, fh, p->solid.dir, p->engine_glow,
1835 p->tilt, p->r, p->tsx, p->tsy, &c, L );
1836}
1837
1849void pilot_renderFramebuffer( Pilot *p, GLuint fbo, double fw, double fh,
1850 const Lighting *L )
1851{
1852 double x, y, w, h;
1853 double timeleft, elapsed;
1854 const Effect *e = NULL;
1855
1856 /* Transform coordinates. */
1857 w = p->ship->size;
1858 h = p->ship->size;
1859 gl_gameToScreenCoords( &x, &y, p->solid.pos.x - w / 2.,
1860 p->solid.pos.y - h / 2. );
1861
1862 /* Render effects - already sorted by priority and then timer. */
1863 for ( int i = 0; i < array_size( p->effects ); i++ ) {
1864 // for (int i=array_size(p->effects)-1; i>=0; i--) {
1865 const Effect *eiter = &p->effects[i];
1866 if ( eiter->data->program == 0 )
1867 continue;
1868
1869 if ( e == NULL ) {
1870 e = eiter;
1871 timeleft = e->timer;
1872 elapsed = e->elapsed;
1873 } else if ( eiter->data == e->data ) {
1874 timeleft = MAX( e->timer, eiter->timer );
1875 elapsed = MAX( e->elapsed, eiter->elapsed );
1876 }
1877 }
1878
1879 /* Render normally. */
1880 if ( e == NULL )
1881 pilot_renderFramebufferBase( p, fbo, fw, fh, L );
1882 /* Render effect single effect. */
1883 else {
1884 mat4 projection, tex_mat;
1885 const EffectData *ed = e->data;
1886
1887 /* Have to scissors a bit more in case of custom vertex effects. */
1888 if ( ed->flags & EFFECT_VERTEX ) {
1889 double s = ceil( 2.0 * p->ship->size / gl_screen.scale );
1890 glBindFramebuffer( GL_FRAMEBUFFER, gl_screen.fbo[2] );
1891 glEnable( GL_SCISSOR_TEST );
1892 glScissor( 0, 0, s, s );
1893 glClear( GL_COLOR_BUFFER_BIT );
1894 glDisable( GL_SCISSOR_TEST );
1895 }
1896
1897 /* Render onto framebuffer. */
1899 gl_screen.nh, L );
1900
1901 glBindFramebuffer( GL_FRAMEBUFFER, fbo );
1902
1903 glClear( GL_COLOR_BUFFER_BIT );
1904
1905 glUseProgram( ed->program );
1906
1907 /* Has an image to use. */
1908 if ( ed->img != NULL ) {
1909 glActiveTexture( GL_TEXTURE1 );
1910 glBindTexture( GL_TEXTURE_2D, ed->img->texture );
1911 glUniform1i( ed->u_img, 1 );
1912 }
1913
1914 glActiveTexture( GL_TEXTURE0 );
1915 glBindTexture( GL_TEXTURE_2D, gl_screen.fbo_tex[2] );
1916 glUniform1i( ed->u_tex, 0 );
1917
1918 glEnableVertexAttribArray( ed->vertex );
1919 gl_vboActivateAttribOffset( gl_squareVBO, ed->vertex, 0, 2, GL_FLOAT, 0 );
1920
1921 projection = mat4_ortho( 0., fw, 0, fh, -1., 1. );
1922 mat4_scale( &projection, w, h, 1. );
1923 gl_uniformMat4( ed->projection, &projection );
1924
1925 tex_mat = mat4_identity();
1926 mat4_scale( &tex_mat, w / (double)gl_screen.nw, h / (double)gl_screen.nh,
1927 1. );
1928 gl_uniformMat4( ed->tex_mat, &tex_mat );
1929
1930 glUniform3f( ed->dimensions, SCREEN_W, SCREEN_H, 1. );
1931 glUniform1f( ed->u_timer, timeleft );
1932 glUniform1f( ed->u_elapsed, elapsed );
1933 glUniform1f( ed->u_r, e->r );
1934 glUniform1f( ed->u_dir, p->solid.dir );
1935
1936 /* Draw. */
1937 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
1938
1939 /* Clean up texture. */
1940 if ( ed->img != NULL ) {
1941 glActiveTexture( GL_TEXTURE1 );
1942 glBindTexture( GL_TEXTURE_2D, 0 );
1943 glActiveTexture( GL_TEXTURE0 );
1944 }
1945
1946 /* Clear state. */
1947 glDisableVertexAttribArray( ed->vertex );
1948
1949 glBindFramebuffer( GL_FRAMEBUFFER, gl_screen.current_fbo );
1950 }
1951}
1952
1959{
1960 double scale, x, y, w, h, z;
1961 double timeleft, elapsed;
1962 int inbounds = 1;
1963 Effect *e = NULL;
1964 glColour c = { .r = 1., .g = 1., .b = 1., .a = 1. };
1965
1966 /* Don't render the pilot. */
1967 if ( pilot_isFlag( p, PILOT_NORENDER ) )
1968 return;
1969
1970 /* Transform coordinates. */
1971 z = cam_getZoom();
1972 w = p->ship->size;
1973 h = p->ship->size;
1974 gl_gameToScreenCoords( &x, &y, p->solid.pos.x - w / 2.,
1975 p->solid.pos.y - h / 2. );
1976
1977 /* Check if inbounds */
1978 if ( ( x < -w ) || ( x > SCREEN_W + w ) || ( y < -h ) ||
1979 ( y > SCREEN_H + h ) )
1980 inbounds = 0;
1981
1982 if ( inbounds ) {
1983 /* Check if needs scaling. */
1984 if ( pilot_isFlag( p, PILOT_LANDING ) )
1985 scale = CLAMP( 0., 1., p->ptimer / p->landing_delay );
1986 else if ( pilot_isFlag( p, PILOT_TAKEOFF ) )
1987 scale = CLAMP( 0., 1., 1. - p->ptimer / p->landing_delay );
1988 else
1989 scale = 1.;
1990
1991 /* Render effects. */
1992 for ( int i = 0; i < array_size( p->effects ); i++ ) {
1993 // for (int i=array_size(p->effects)-1; i>=0; i--) {
1994 Effect *eiter = &p->effects[i];
1995 if ( eiter->data->program == 0 )
1996 continue;
1997
1998 if ( e == NULL ) {
1999 e = eiter;
2000 timeleft = e->timer;
2001 elapsed = e->elapsed;
2002 } else if ( eiter->data == e->data ) {
2003 timeleft = MAX( e->timer, eiter->timer );
2004 elapsed = MAX( e->elapsed, eiter->elapsed );
2005 }
2006 }
2007
2008 /* Add some transparency if stealthed. TODO better effect */
2009 if ( !pilot_isPlayer( p ) && pilot_isFlag( p, PILOT_STEALTH ) )
2010 c.a = 0.5;
2011
2012 /* Render normally. */
2013 if ( e == NULL ) {
2014 if ( p->ship->gfx_3d != NULL ) {
2015 /* Render to framebuffer first. */
2017 gl_screen.nh, NULL );
2018
2019 /* Draw framebuffer with depth on screen. */
2020 gl_renderTextureDepthRaw(
2021 gl_screen.fbo_tex[2], gl_screen.fbo_depth_tex[2], 0,
2022 x + ( 1. - scale ) * z * w * 0.5,
2023 y + ( 1. - scale ) * z * h * 0.5, w * scale * z, h * scale * z,
2024 0, 0, w / (double)gl_screen.nw, h / (double)gl_screen.nh, NULL,
2025 0. ); /* Colour should already be applied. */
2026 } else {
2028 p->ship->gfx_space, p->ship->gfx_engine, 1. - p->engine_glow,
2029 p->solid.pos.x, p->solid.pos.y, scale, scale, p->tsx, p->tsy,
2030 &c );
2031 }
2032 }
2033 /* Render effect single effect. */
2034 else {
2035 mat4 projection, tex_mat;
2036 const EffectData *ed = e->data;
2037
2038 /* Have to scissors a bit more in case of custom vertex effects. */
2039 if ( ed->flags & EFFECT_VERTEX ) {
2040 double s = ceil( 2.0 * p->ship->size / gl_screen.scale );
2041 glBindFramebuffer( GL_FRAMEBUFFER, gl_screen.fbo[2] );
2042 glEnable( GL_SCISSOR_TEST );
2043 glScissor( 0, 0, s, s );
2044 glClear( GL_COLOR_BUFFER_BIT );
2045 glDisable( GL_SCISSOR_TEST );
2046 }
2047
2048 /* Render onto framebuffer. */
2050 gl_screen.nh, NULL );
2051 /* gl_screen.current_fbo is bound. */
2052
2053 /* Draw the depth. */
2054 gl_renderDepthRaw(
2055 gl_screen.fbo_depth_tex[2], 0, x + ( 1. - scale ) * z * w * 0.5,
2056 y + ( 1. - scale ) * z * h * 0.5, w * scale * z, h * scale * z, 0,
2057 0, w / (double)gl_screen.nw, h / (double)gl_screen.nh, 0. );
2058
2059 /* Go to the shader now. */
2060 glUseProgram( ed->program );
2061
2062 /* Has an image to use. */
2063 if ( ed->img != NULL ) {
2064 glActiveTexture( GL_TEXTURE1 );
2065 glBindTexture( GL_TEXTURE_2D, ed->img->texture );
2066 glUniform1i( ed->u_img, 1 );
2067 }
2068
2069 glActiveTexture( GL_TEXTURE0 );
2070 glBindTexture( GL_TEXTURE_2D, gl_screen.fbo_tex[2] );
2071 glUniform1i( ed->u_tex, 0 );
2072
2073 glEnableVertexAttribArray( ed->vertex );
2074 gl_vboActivateAttribOffset( gl_squareVBO, ed->vertex, 0, 2, GL_FLOAT,
2075 0 );
2076
2077 projection = gl_view_matrix;
2078 mat4_translate_scale_xy( &projection, x + ( 1. - scale ) * z * w * 0.5,
2079 y + ( 1. - scale ) * z * h * 0.5,
2080 scale * z * w, scale * z * h );
2081 gl_uniformMat4( ed->projection, &projection );
2082
2083 tex_mat = mat4_identity();
2084 mat4_scale( &tex_mat, w / (double)gl_screen.nw,
2085 h / (double)gl_screen.nh, 1. );
2086 gl_uniformMat4( ed->tex_mat, &tex_mat );
2087
2088 glUniform3f( ed->dimensions, SCREEN_W, SCREEN_H, cam_getZoom() );
2089 glUniform1f( ed->u_timer, timeleft );
2090 glUniform1f( ed->u_elapsed, elapsed );
2091 glUniform1f( ed->u_r, e->r );
2092 glUniform1f( ed->u_dir, p->solid.dir );
2093
2094 /* Draw. */
2095 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
2096
2097 /* Clean up texture. */
2098 if ( ed->img != NULL ) {
2099 glActiveTexture( GL_TEXTURE1 );
2100 glBindTexture( GL_TEXTURE_2D, 0 );
2101 glActiveTexture( GL_TEXTURE0 );
2102 }
2103
2104 /* Clear state. */
2105 glDisableVertexAttribArray( ed->vertex );
2106 }
2107 }
2108
2109 /* Re-draw backwards trails. */
2110 if ( inbounds ) {
2111 glEnable( GL_DEPTH_TEST );
2112 glDepthMask( GL_FALSE ); /* Don't overwrite depth. */
2113 }
2114 for ( int i = 0, g = 0; g < array_size( p->ship->trail_emitters ); g++ ) {
2115 if ( pilot_trail_generated( p, g ) ) {
2116 if ( p->trail[i]->ontop )
2117 spfx_trail_draw( p->trail[i] );
2118 i++;
2119 }
2120 }
2121 if ( inbounds ) {
2122 glDepthMask( GL_TRUE );
2123 glDisable( GL_DEPTH_TEST );
2124 }
2125
2126 /* Erase the depth so it doesn't affect trails of other pilots. */
2127 if ( inbounds && ( p->ship->gfx_3d != NULL ) ) {
2128 gl_clipRect( x + ( 1. - scale ) * z * w * 0.5,
2129 y + ( 1. - scale ) * z * h * 0.5, w * scale * z,
2130 h * scale * z );
2131 glClear( GL_DEPTH_BUFFER_BIT );
2132 gl_unclipRect();
2133 }
2134
2135 /* Useful debug stuff below. */
2136#ifdef DEBUGGING
2137 if ( inbounds && debug_isFlag( DEBUG_MARK_COLLISION ) ) {
2138 static gl_vbo *poly_vbo = NULL;
2139 GLfloat data[1024];
2140 const CollPolyView *poly = poly_view( &p->ship->polygon, p->solid.dir );
2141 size_t n =
2142 MIN( sizeof( data ) / ( sizeof( GLfloat ) * 2 ), (size_t)poly->npt );
2143 size_t ndata = n * sizeof( GLfloat ) * 2;
2144 mat4 projection = gl_view_matrix;
2145
2146 /* Set up the vector data. */
2147 for ( size_t i = 0; i < n; i++ ) {
2148 data[i * 2 + 0] = poly->x[i];
2149 data[i * 2 + 1] = poly->y[i];
2150 }
2151
2152 /* Upload to VBO, creating as necessary. */
2153 if ( poly_vbo == NULL )
2154 poly_vbo = gl_vboCreateDynamic( ndata, data );
2155 else
2156 gl_vboData( poly_vbo, ndata, data );
2157
2158 /* Draw. */
2159 glUseProgram( shaders.lines.program );
2160 glEnableVertexAttribArray( shaders.lines.vertex );
2161 gl_vboActivateAttribOffset( poly_vbo, shaders.lines.vertex, 0, 2,
2162 GL_FLOAT, 0 );
2163
2164 /* Do projection. */
2165 gl_gameToScreenCoords( &x, &y, p->solid.pos.x, p->solid.pos.y );
2166 mat4_translate_scale_xy( &projection, x, y, z, z );
2167 gl_uniformMat4( shaders.lines.projection, &projection );
2168
2169 /* Do colour. */
2170 gl_uniformColour( shaders.lines.colour, &cWhite );
2171
2172 /* Draw. */
2173 glDrawArrays( GL_LINE_LOOP, 0, n );
2174
2175 /* Clear state. */
2176 glDisableVertexAttribArray( shaders.lines.vertex );
2177
2178 gl_checkErr();
2179 glUseProgram( 0 );
2180 }
2181
2182 /* Draw trail emitters on top in debug mode. */
2183 if ( inbounds && debug_isFlag( DEBUG_MARK_EMITTER ) ) {
2184 double dircos, dirsin;
2185 mat4 H;
2186 int use_3d = ship_isFlag( p->ship, SHIP_3DTRAILS );
2187
2188 /* Use 3D to compute trail stuff. */
2189 if ( use_3d ) {
2190 H = pilot_local_transform( p );
2191 } else {
2192 dircos = cos( p->solid.dir );
2193 dirsin = sin( p->solid.dir );
2194 }
2195
2196 /* Draw the trails emitters. */
2197 for ( int i = 0; i < array_size( p->ship->trail_emitters ); i++ ) {
2198 const ShipTrailEmitter *trail = &p->ship->trail_emitters[i];
2199 vec2 v;
2200
2201 /* Visualize the trail emitters. */
2202 if ( use_3d ) {
2203 vec3 v2;
2204 mat4_mul_vec( &v2, &H, &trail->pos );
2205 v.x = v2.v[0];
2206 v.y = v2.v[1];
2207 } else {
2208 v.x = trail->pos.v[0] * dircos - trail->pos.v[1] * dirsin;
2209 v.y = trail->pos.v[0] * dirsin + trail->pos.v[1] * dircos +
2210 trail->pos.v[2];
2211 v.y *= M_SQRT1_2;
2212 }
2213
2214 /* Scale if necessary. */
2215 v.x *= scale;
2216 v.y *= scale;
2217
2218 /* Draw. */
2219 gl_gameToScreenCoords( &x, &y, p->solid.pos.x + v.x,
2220 p->solid.pos.y + v.y );
2221 if ( trail->trail_spec->nebula )
2222 gl_renderCross( x, y, 2, &cFontBlue );
2223 else
2224 gl_renderCross( x, y, 4, &cInert );
2225 }
2226 }
2227#endif /* DEBUGGING */
2228}
2229
2236{
2237 int playerdead;
2238 double sw, sh;
2239
2240 /* Don't render the pilot. */
2241 if ( pilot_isFlag( p, PILOT_NORENDER ) )
2242 return;
2243
2244 sw = p->ship->size;
2245 sh = p->ship->size;
2246
2247 playerdead = ( player_isFlag( PLAYER_DESTROYED ) || ( player.p == NULL ) );
2248
2249 /* Render the hailing graphic if needed. */
2250 if ( !playerdead && pilot_isFlag( p, PILOT_HAILING ) ) {
2251 glTexture *ico_hail = gui_hailIcon();
2252 if ( ico_hail != NULL ) {
2253 int sx = (int)ico_hail->sx;
2254
2255 /* Render. */
2257 ico_hail,
2258 p->solid.pos.x + PILOT_SIZE_APPROX * sw / 2. + ico_hail->sw / 4.,
2259 p->solid.pos.y + PILOT_SIZE_APPROX * sh / 2. + ico_hail->sh / 4.,
2260 p->hail_pos % sx, p->hail_pos / sx, NULL );
2261 }
2262 }
2263
2264 /* Text ontop if needed. */
2265 if ( p->comm_msg != NULL ) {
2266 double x, y, dx, dy;
2267
2268 /* Coordinate translation. */
2269 gl_gameToScreenCoords( &x, &y, p->solid.pos.x, p->solid.pos.y );
2270
2271 /* Display the text. */
2272 glColour c = { 1., 1., 1., 1. };
2273
2274 /* Colour. */
2275 if ( p->comm_msgTimer - pilot_commFade < 0. )
2276 c.a = p->comm_msgTimer / pilot_commFade;
2277
2278 /* Position to render at. */
2279 dx = x - p->comm_msgWidth / 2.;
2280 dy = y + PILOT_SIZE_APPROX * sh / 2.;
2281
2282 /* Background. */
2283 gl_renderRect( dx - 2., dy - 2., p->comm_msgWidth + 4., gl_defFont.h + 4.,
2284 &cBlackHilight );
2285
2286 /* Display text. */
2287 gl_printRaw( NULL, dx, dy, &c, -1., p->comm_msg );
2288 }
2289
2290 /* Show health / friendlyness */
2291 if ( conf.healthbars && !playerdead && !pilot_isPlayer( p ) &&
2292 !pilot_isFlag( p, PILOT_DEAD ) && !pilot_isDisabled( p ) &&
2293 ( pilot_isFlag( p, PILOT_COMBAT ) || ( p->shield < p->shield_max ) ) ) {
2294 double x, y, w, h;
2295
2296 /* Coordinate translation. */
2297 gl_gameToScreenCoords( &x, &y, p->solid.pos.x, p->solid.pos.y );
2298
2299 w = sw + 4.;
2300 h = sh + 4.;
2301
2302 /* Can do an inbounds check now. */
2303 if ( ( x < -w ) || ( x > SCREEN_W + w ) || ( y < -h ) ||
2304 ( y > SCREEN_H + h ) )
2305 return;
2306
2307 w = PILOT_SIZE_APPROX * sw;
2308 h = PILOT_SIZE_APPROX * sh / 3.;
2309
2310 glUseProgram( shaders.healthbar.program );
2311 glUniform2f( shaders.healthbar.dimensions, 5., h );
2312 glUniform1f( shaders.healthbar.paramf,
2313 ( p->armour + p->shield ) /
2314 ( p->armour_max + p->shield_max ) );
2315 gl_uniformColour( shaders.healthbar.paramv,
2316 ( p->shield > 0. ) ? &cShield : &cArmour );
2317 gl_renderShader( x + w / 2. + 2.5, y, 5., h, 0., &shaders.healthbar,
2318 pilot_getColour( p ), 1 );
2319 }
2320}
2321
2328void pilot_update( Pilot *pilot, double dt )
2329{
2330 int cooling, nchg;
2331 Pilot *target;
2332 double a, px, py, vx, vy, Q;
2333 Target wt;
2334
2335 /* Modify the dt with speedup. */
2336 dt *= pilot->stats.time_speedup;
2337
2338 /* Check target validity. */
2339 target = pilot_weaponTarget( pilot, &wt );
2340 cooling = pilot_isFlag( pilot, PILOT_COOLDOWN );
2341
2342 /*
2343 * Update timers.
2344 */
2345 pilot->ptimer -= dt;
2346 pilot->tcontrol -= dt;
2347 if ( cooling ) {
2348 pilot->ctimer -= dt;
2349 if ( pilot->ctimer < 0. ) {
2350 pilot_cooldownEnd( pilot, NULL );
2351 cooling = 0;
2352 }
2353 }
2354 pilot->stimer -= dt;
2355 if ( pilot->stimer <= 0. )
2356 pilot->sbonus -= dt;
2357 for ( int i = 0; i < MAX_AI_TIMERS; i++ )
2358 if ( pilot->timer[i] > 0. )
2359 pilot->timer[i] -= dt;
2360 if ( pilot->comm_msg != NULL ) {
2361 pilot->comm_msgTimer -= dt;
2362 if ( pilot->comm_msgTimer < 0. ) {
2363 free( pilot->comm_msg );
2364 pilot->comm_msg = NULL;
2365 }
2366 }
2367 if ( pilot_isFlag( pilot, PILOT_HAILING ) ) {
2368 glTexture *ico_hail = gui_hailIcon();
2369 if ( ico_hail != NULL ) {
2370 int sx, sy;
2371 pilot->htimer -= dt;
2372 sx = (int)ico_hail->sx;
2373 sy = (int)ico_hail->sy;
2374 if ( pilot->htimer < 0. ) {
2375 pilot->htimer = 0.1;
2376 pilot->hail_pos++;
2377 pilot->hail_pos %= sx * sy;
2378 }
2379 }
2380 }
2381 /* Update heat. */
2382 pilotoutfit_modified = 0;
2383 a = -1.;
2384 Q = 0.;
2385 nchg = 0; /* Number of outfits that change state, processed at the end. */
2386 for ( int i = 0; i < array_size( pilot->outfits ); i++ ) {
2387 PilotOutfitSlot *pos = pilot->outfits[i];
2388
2389 /* Picky about our outfits. */
2390 if ( pos->outfit == NULL )
2391 continue;
2392 if ( !( pos->flags & PILOTOUTFIT_ACTIVE ) )
2393 continue;
2394
2395 /* Handle firerate timer. */
2396 if ( pos->timer > 0. )
2397 pos->timer -= dt * pilot_heatFireRateMod( pos->heat_T );
2398
2399 /* Handle reload timer. (Note: this works backwards compared to
2400 * other timers. This helps to simplify code resetting the timer
2401 * elsewhere.)
2402 */
2403 if ( outfit_isLauncher( pos->outfit ) ||
2404 outfit_isFighterBay( pos->outfit ) ) {
2405 double ammo_threshold, reload_time;
2406
2407 /* Initial (raw) ammo threshold */
2408 if ( outfit_isLauncher( pos->outfit ) ) {
2409 ammo_threshold = pos->outfit->u.lau.amount;
2410 ammo_threshold =
2411 round( (double)ammo_threshold * pilot->stats.ammo_capacity );
2412 reload_time =
2413 pos->outfit->u.lau.reload_time / pilot->stats.launch_reload;
2414 } else {
2415 /* if (outfit_isFighterBay( pos->outfit)) { */ /* Commented to shut
2416 up warning. */
2417 ammo_threshold = pos->outfit->u.bay.amount;
2418 ammo_threshold =
2419 round( (double)ammo_threshold * pilot->stats.fbay_capacity );
2420 /* Adjust for deployed fighters if needed */
2421 ammo_threshold -= pos->u.ammo.deployed;
2422 reload_time =
2423 pos->outfit->u.bay.reload_time / pilot->stats.fbay_reload;
2424 }
2425
2426 /* Add to timer. */
2427 if ( pos->rtimer < reload_time )
2428 pos->rtimer += dt;
2429
2430 /* Don't allow accumulation of the timer before reload allowed */
2431 if ( pos->u.ammo.quantity >= ammo_threshold )
2432 pos->rtimer = 0;
2433
2434 while ( ( pos->rtimer >= reload_time ) &&
2435 ( pos->u.ammo.quantity < ammo_threshold ) ) {
2436 pos->rtimer -= reload_time;
2437 pilot_addAmmo( pilot, pos, 1 );
2438 }
2439
2440 pos->rtimer = MIN( pos->rtimer, reload_time );
2441 }
2442
2443 /* Handle state timer. */
2444 if ( pos->stimer >= 0. ) {
2445 pos->stimer -= dt;
2446 if ( pos->stimer < 0. ) {
2447 if ( pos->state == PILOT_OUTFIT_ON ) {
2448 pilot_outfitOff( pilot, pos, 0 );
2449 nchg++;
2450 } else if ( pos->state == PILOT_OUTFIT_COOLDOWN ) {
2451 pos->state = PILOT_OUTFIT_OFF;
2452 nchg++;
2453 }
2454 }
2455 }
2456
2457 /* Handle heat. */
2458 if ( !cooling )
2459 Q += pilot_heatUpdateSlot( pilot, pos, dt );
2460
2461 /* Handle lockons. */
2462 pilot_lockUpdateSlot( pilot, pos, target, &wt, &a, dt );
2463 }
2464
2465 /* Global heat. */
2466 if ( !cooling )
2467 pilot_heatUpdateShip( pilot, Q, dt );
2468 else
2469 pilot_heatUpdateCooldown( pilot );
2470
2471 /* Update electronic warfare. */
2472 pilot_ewUpdateDynamic( pilot, dt );
2473
2474 /* Update stress. */
2475 if ( !pilot_isFlag( pilot,
2476 PILOT_DISABLED ) ) { /* Case pilot is not disabled. */
2477 double stress_falloff =
2478 0.3 *
2479 sqrt( pilot->solid.mass ); /* Should be about 38 seconds for a 300 mass
2480 ship with 200 armour, and 172 seconds for
2481 a 6000 mass ship with 4000 armour. */
2482 pilot->stress -= stress_falloff * pilot->stats.stress_dissipation * dt;
2483 pilot->stress = MAX( pilot->stress, 0 );
2484 } else if ( !pilot_isFlag(
2485 pilot,
2486 PILOT_DISABLED_PERM ) ) { /* Case pilot is disabled (but not
2487 permanently so). */
2488 pilot->dtimer_accum += dt;
2489 if ( pilot->dtimer_accum >= pilot->dtimer ) {
2490 pilot->stress = 0.;
2491 pilot->dtimer_accum = 0;
2492 pilot_updateDisable( pilot, 0 );
2493 }
2494 }
2495
2496 /* Damage effect. */
2497 if ( ( pilot->stats.damage > 0. ) || ( pilot->stats.disable > 0. ) ) {
2498 Damage dmg;
2499 dmg.type = dtype_get( "raw" );
2500 dmg.damage = pilot->stats.damage * dt;
2501 dmg.penetration = 1.; /* Full penetration. */
2502 dmg.disable = pilot->stats.disable * dt;
2503 pilot_hit( pilot, NULL, NULL, &dmg, NULL, LUA_NOREF, 0 );
2504 }
2505
2506 /* Handle takeoff/landing. */
2507 if ( pilot_isFlag( pilot, PILOT_TAKEOFF ) ) {
2508 if ( pilot->ptimer < 0. ) {
2509 pilot_rmFlag( pilot, PILOT_TAKEOFF );
2510 if ( pilot_isFlag( pilot, PILOT_PLAYER ) ) {
2511 pilot_setFlag( pilot, PILOT_NONTARGETABLE );
2512 pilot->itimer = PILOT_PLAYER_NONTARGETABLE_TAKEOFF_DELAY;
2513 }
2514 return;
2515 }
2516 } else if ( pilot_isFlag( pilot, PILOT_LANDING ) ) {
2517 if ( pilot->ptimer < 0. ) {
2518 if ( pilot_isPlayer( pilot ) ) {
2519 player_setFlag( PLAYER_HOOK_LAND );
2520 pilot->ptimer = 0.;
2521 } else
2522 pilot_delete( pilot );
2523 return;
2524 }
2525 }
2526 /* he's dead jim */
2527 else if ( pilot_isFlag( pilot, PILOT_DEAD ) ) {
2528
2529 if ( pilot->ship->lua_explode_update != LUA_NOREF ) {
2530 /* Run Lua if applicable. */
2531 pilot_shipLExplodeUpdate( pilot, dt );
2532 } else {
2533 /* pilot death sound */
2534 if ( !pilot_isFlag( pilot, PILOT_DEATH_SOUND ) &&
2535 ( pilot->ptimer < 0.050 ) ) {
2536 char buf[16];
2537
2538 /* Play random explosion sound. */
2539 snprintf( buf, sizeof( buf ), "explosion%d", RNG( 0, 2 ) );
2540 sound_playPos( sound_get( buf ), pilot->solid.pos.x,
2541 pilot->solid.pos.y, pilot->solid.vel.x,
2542 pilot->solid.vel.y );
2543
2544 pilot_setFlag( pilot, PILOT_DEATH_SOUND );
2545 }
2546 /* final explosion */
2547 else if ( !pilot_isFlag( pilot, PILOT_EXPLODED ) &&
2548 ( pilot->ptimer < 0.200 ) ) {
2549 Damage dmg;
2550
2551 /* Damage from explosion. */
2552 a = sqrt( pilot->solid.mass );
2553 dmg.type = dtype_get( "explosion_splash" );
2554 dmg.damage =
2555 MAX( 0., 2. * ( a * ( 1. + sqrt( pilot->fuel + 1. ) / 28. ) ) );
2556 dmg.penetration = 1.; /* Full penetration. */
2557 dmg.disable = 0.;
2558 expl_explode( pilot->solid.pos.x, pilot->solid.pos.y,
2559 pilot->solid.vel.x, pilot->solid.vel.y,
2560 pilot->ship->size / 2. / PILOT_SIZE_APPROX + a, &dmg,
2561 NULL, EXPL_MODE_SHIP );
2562 debris_add( pilot->solid.mass, pilot->ship->size / 2.,
2563 pilot->solid.pos.x, pilot->solid.pos.y,
2564 pilot->solid.vel.x, pilot->solid.vel.y );
2565 pilot_setFlag( pilot, PILOT_EXPLODED );
2566 pilot_runHook( pilot, PILOT_HOOK_EXPLODED );
2567
2568 /* We do a check here in case the pilot was regenerated. */
2569 if ( pilot_isFlag( pilot, PILOT_EXPLODED ) ) {
2570 /* Release cargo */
2571 for ( int i = 0; i < array_size( pilot->commodities ); i++ )
2572 pilot_cargoJet( pilot, pilot->commodities[i].commodity,
2573 pilot->commodities[i].quantity, 1 );
2574 }
2575 }
2576 /* reset random explosion timer */
2577 else if ( pilot->timer[1] <= 0. ) {
2578 unsigned int l;
2579
2580 pilot->timer[1] =
2581 0.08 * ( pilot->ptimer - pilot->timer[1] ) / pilot->ptimer;
2582
2583 /* random position on ship */
2584 a = RNGF() * 2. * M_PI;
2585 px = VX( pilot->solid.pos ) +
2586 cos( a ) * RNGF() * pilot->ship->size / 2.;
2587 py = VY( pilot->solid.pos ) +
2588 sin( a ) * RNGF() * pilot->ship->size / 2.;
2589 vx = VX( pilot->solid.vel );
2590 vy = VY( pilot->solid.vel );
2591
2592 /* set explosions */
2593 l = ( pilot->id == PLAYER_ID ) ? SPFX_LAYER_FRONT
2594 : SPFX_LAYER_MIDDLE;
2595 if ( RNGF() > 0.8 )
2596 spfx_add( spfx_get( "ExpM" ), px, py, vx, vy, l );
2597 else
2598 spfx_add( spfx_get( "ExpS" ), px, py, vx, vy, l );
2599 }
2600
2601 /* completely destroyed with final explosion */
2602 if ( pilot_isFlag( pilot, PILOT_DEAD ) && ( pilot->ptimer < 0. ) ) {
2603 if ( pilot->id == PLAYER_ID ) /* player.p handled differently */
2605 pilot_delete( pilot );
2606 return;
2607 }
2608 }
2609 } else if ( pilot_isFlag( pilot, PILOT_NONTARGETABLE ) ) {
2610 pilot->itimer -= dt;
2611 if ( pilot->itimer < 0. )
2612 pilot_rmFlag( pilot, PILOT_NONTARGETABLE );
2613 } else if ( pilot->armour <= 0. ) { /* PWNED */
2614 if ( pilot_isFlag( pilot, PILOT_NODEATH ) )
2615 pilot->armour = 1.;
2616 else
2617 pilot_dead( pilot, 0 ); /* start death stuff - dunno who killed. */
2618 }
2619
2620 /* Special handling for braking. */
2621 if ( pilot_isFlag( pilot, PILOT_BRAKING ) ) {
2622 if ( pilot_brake( pilot, dt ) ) {
2623 if ( pilot_isFlag( pilot, PILOT_COOLDOWN_BRAKE ) )
2624 pilot_cooldown( pilot, 1 );
2625 else {
2626 /* Normal braking is done (we're below MIN_VEL_ERR), now sidestep
2627 * normal physics and bring the ship to a near-complete stop. */
2628 pilot->solid.speed_max = 0.;
2629 pilot->solid.update( &pilot->solid, dt );
2630
2631 if ( VMOD( pilot->solid.vel ) < 1e-1 ) {
2632 vectnull( &pilot->solid.vel ); /* Forcibly zero velocity. */
2633 pilot_rmFlag( pilot, PILOT_BRAKING );
2634 }
2635 }
2636 }
2637 }
2638
2639 /* Healing and energy usage is only done if not disabled. */
2640 if ( !pilot_isDisabled( pilot ) ) {
2641 pilot_ewUpdateStealth( pilot, dt );
2642
2643 /* Pilot is still alive */
2644 pilot->armour += pilot->armour_regen * dt;
2645 if ( pilot->armour > pilot->armour_max )
2646 pilot->armour = pilot->armour_max;
2647
2648 /* Regen shield */
2649 if ( pilot->stimer <= 0. ) {
2650 pilot->shield += pilot->shield_regen * dt;
2651 if ( pilot->sbonus > 0. )
2652 pilot->shield +=
2653 dt * ( pilot->shield_regen * ( pilot->sbonus / 1.5 ) );
2654 pilot->shield = CLAMP( 0., pilot->shield_max, pilot->shield );
2655 }
2656
2657 /* Regen fuel. */
2658 pilot->fuel =
2659 MIN( pilot->fuel_max, pilot->fuel + pilot->stats.fuel_regen * dt );
2660
2661 /*
2662 * Recharge rate used to be proportional to the current state of the
2663 * battery: i.e. if the battery was 0% charged, the recharge rate would be
2664 * at its maximum, but if it was 70% charge, the battery would only
2665 * recharge at 30% of its normal rate.
2666 */
2667 pilot->energy += pilot->energy_regen * dt;
2668 if ( pilot->energy > pilot->energy_max )
2669 pilot->energy = pilot->energy_max;
2670 else if ( pilot->energy < 0. ) {
2671 pilot->energy = 0.;
2672 /* Stop all on outfits. */
2673 nchg += pilot_outfitOffAll( pilot );
2674 /* Run Lua stuff. */
2676 }
2677 }
2678
2679 /* Update effects. */
2680 nchg += effect_update( &pilot->effects, dt );
2681 if ( pilot_isFlag( pilot, PILOT_DELETE ) )
2682 return; /* It's possible for effects to remove the pilot causing future
2683 Lua to be unhappy. */
2684
2685 /* Must recalculate stats because something changed state. */
2686 if ( ( nchg > 0 ) || pilotoutfit_modified )
2687 pilot_calcStats( pilot );
2688
2689 /* purpose fallthrough to get the movement like disabled */
2690 if ( pilot_isDisabled( pilot ) || cooling ) {
2691 /* Do the slow brake thing */
2692 pilot->solid.speed_max = 0.;
2693 pilot_setAccel( pilot, 0. );
2694 pilot_setTurn( pilot, 0. );
2695
2696 /* Update the solid */
2697 pilot_updateSolid( pilot, dt );
2698
2699 /* Engine glow decay. */
2700 if ( pilot->engine_glow > 0. ) {
2701 pilot->engine_glow -= pilot->speed / pilot->accel * dt;
2702 if ( pilot->engine_glow < 0. )
2703 pilot->engine_glow = 0.;
2704 }
2705
2706 /* Update the trail. */
2707 pilot_sample_trails( pilot, 0 );
2708
2709 /* Cooldown still updates outfits. */
2710 pilot_shipLUpdate( pilot, dt );
2711
2712 /* Update outfits if necessary. */
2713 pilot->otimer += dt;
2714 while ( pilot->otimer >= PILOT_OUTFIT_LUA_UPDATE_DT ) {
2715 pilot_outfitLUpdate( pilot, PILOT_OUTFIT_LUA_UPDATE_DT );
2716 if ( pilot_isFlag( pilot, PILOT_DELETE ) )
2717 return; /* Same as with the effects, it is theoretically possible
2718 for the outfit to remove the pilot. */
2719 pilot->otimer -= PILOT_OUTFIT_LUA_UPDATE_DT;
2720 }
2721 return;
2722 }
2723
2724 /* Player damage decay. */
2725 if ( pilot->player_damage > 0. )
2726 pilot->player_damage -= dt * PILOT_HOSTILE_DECAY;
2727 else
2728 pilot->player_damage = 0.;
2729
2730 /* Pilot is board/refueling. Hack to match speeds. */
2731 if ( pilot_isFlag( pilot, PILOT_REFUELBOARDING ) )
2732 pilot_refuel( pilot, dt );
2733
2734 /* Pilot is boarding its target. Hack to match speeds. */
2735 if ( pilot_isFlag( pilot, PILOT_BOARDING ) ) {
2736 if ( target == NULL )
2737 pilot_rmFlag( pilot, PILOT_BOARDING );
2738 else {
2739 /* Match speeds. */
2740 pilot->solid.vel = target->solid.vel;
2741
2742 /* See if boarding is finished. */
2743 if ( pilot->ptimer < 0. )
2744 pilot_boardComplete( pilot );
2745 }
2746 }
2747
2748 /* Update weapons. */
2749 pilot_weapSetUpdate( pilot );
2750
2751 if ( !pilot_isFlag( pilot, PILOT_HYPERSPACE ) ) { /* limit the speed */
2752
2753 /* pilot is afterburning */
2754 if ( pilot_isFlag( pilot, PILOT_AFTERBURNER ) ) {
2755 const Outfit *afb = pilot->afterburner->outfit;
2756
2757 /* Heat up the afterburner. */
2758 pilot_heatAddSlotTime( pilot, pilot->afterburner, dt );
2759
2760 /* If the afterburner's efficiency is reduced to 0, shut it off. */
2762 afb->overheat_min,
2763 afb->overheat_max ) <= 0. )
2764 pilot_afterburnOver( pilot );
2765 else {
2766 double efficiency =
2768 afb->overheat_min, afb->overheat_max );
2769 efficiency = MIN( 1., afb->u.afb.mass_limit / pilot->solid.mass ) *
2770 efficiency;
2771
2772 if ( pilot->id == PLAYER_ID )
2773 spfx_shake( 0.75 * SPFX_SHAKE_DECAY *
2774 dt ); /* shake goes down at quarter speed */
2775
2776 /* Adjust speed. Speed bonus falls as heat rises. */
2777 pilot->solid.speed_max =
2778 pilot->speed * ( 1. + afb->u.afb.speed * efficiency );
2779
2780 /* Adjust accel. Thrust bonus falls as heat rises. */
2781 pilot_setAccel( pilot, 1. + afb->u.afb.accel * efficiency );
2782 }
2783 } else
2784 pilot->solid.speed_max = pilot->speed;
2785 } else
2786 pilot->solid.speed_max = -1.; /* Disables max speed. */
2787
2788 /* Set engine glow. */
2789 if ( pilot->solid.accel > 0. ) {
2790 /*pilot->engine_glow += pilot->accel / pilot->speed * dt;*/
2791 pilot->engine_glow += pilot->speed / pilot->accel * dt;
2792 if ( pilot->engine_glow > 1. )
2793 pilot->engine_glow = 1.;
2794 } else if ( pilot->engine_glow > 0. ) {
2795 pilot->engine_glow -= pilot->speed / pilot->accel * dt;
2796 if ( pilot->engine_glow < 0. )
2797 pilot->engine_glow = 0.;
2798 }
2799
2800 /* Set tilt. */
2801 const double tilt_max = M_PI / 8.;
2802 const double tilt_mod = tilt_max * 8.;
2803 const double tilt_threshold = 0.05; /* A bit under 5 degrees/sec. */
2804 if ( pilot->solid.dir_vel > tilt_threshold ) {
2805 pilot->tilt += pilot->solid.dir_vel / tilt_mod * dt;
2806 pilot->tilt = MIN( pilot->tilt, tilt_max );
2807 } else if ( pilot->solid.dir_vel < -tilt_threshold ) {
2808 /* Already negative. */
2809 pilot->tilt += pilot->solid.dir_vel / tilt_mod * dt;
2810 pilot->tilt = MAX( pilot->tilt, -tilt_max );
2811 } else if ( pilot->tilt > 0. ) {
2812 pilot->tilt -= pilot->turn / tilt_mod * dt;
2813 pilot->tilt = MAX( pilot->tilt, 0. );
2814 } else if ( pilot->tilt < 0. ) {
2815 pilot->tilt += pilot->turn / tilt_mod * dt;
2816 pilot->tilt = MIN( pilot->tilt, 0. );
2817 }
2818
2819 /* Update the solid, must be run after limit_speed. */
2820 pilot_updateSolid( pilot, dt );
2821
2822 /* Update the trail. */
2823 pilot_sample_trails( pilot, 0 );
2824
2825 /* Update pilot Lua. */
2826 pilot_shipLUpdate( pilot, dt );
2827
2828 /* Update outfits if necessary. */
2829 pilot->otimer += dt;
2830 while ( pilot->otimer >= PILOT_OUTFIT_LUA_UPDATE_DT ) {
2831 pilot_outfitLUpdate( pilot, PILOT_OUTFIT_LUA_UPDATE_DT );
2832 if ( pilot_isFlag( pilot, PILOT_DELETE ) )
2833 return; /* Same as with the effects, it is theoretically possible for
2834 the outfit to remove the pilot. */
2835 pilot->otimer -= PILOT_OUTFIT_LUA_UPDATE_DT;
2836 }
2837}
2838
2846void pilot_sample_trails( Pilot *p, int none )
2847{
2848 double d2, cx, cy, dircos, dirsin;
2849 TrailMode mode;
2850 int use_3d;
2851 mat4 H;
2852
2853 /* Ignore for simulation. */
2854 if ( !space_needsEffects() )
2855 return;
2856
2857 /* No trails to sample. */
2858 if ( p->trail == NULL )
2859 return;
2860
2861 /* Skip if far away (pretty heuristic-based but seems to work). */
2862 cam_getPos( &cx, &cy );
2863 d2 = pow2( cx - p->solid.pos.x ) + pow2( cy - p->solid.pos.y );
2864 if ( d2 > pow2( MAX( SCREEN_W, SCREEN_H ) / conf.zoom_far * 2. ) )
2865 return;
2866
2867 /* Identify the emission type. */
2868 if ( none )
2869 mode = MODE_NONE;
2870 else {
2871 if ( pilot_isFlag( p, PILOT_HYPERSPACE ) ||
2872 pilot_isFlag( p, PILOT_HYP_END ) )
2873 mode = MODE_JUMPING;
2874 else if ( pilot_isFlag( p, PILOT_AFTERBURNER ) )
2875 mode = MODE_AFTERBURN;
2876 else if ( p->solid.accel > 0.2 )
2877 mode = MODE_GLOW;
2878 else
2879 mode = MODE_IDLE;
2880 }
2881
2882 use_3d = ship_isFlag( p->ship, SHIP_3DTRAILS );
2883 if ( use_3d ) {
2884 H = pilot_local_transform( p );
2885 }
2886
2887 dircos = cos( p->solid.dir );
2888 dirsin = sin( p->solid.dir );
2889
2890 /* Compute the engine offset and decide where to draw the trail. */
2891 for ( int i = 0, g = 0; g < array_size( p->ship->trail_emitters ); g++ ) {
2892 const ShipTrailEmitter *trail = &p->ship->trail_emitters[g];
2893 double dx, dy, dz, amod, ax, ay, scale;
2894
2895 if ( !pilot_trail_generated( p, g ) )
2896 continue;
2897
2898 if ( use_3d ) {
2899 vec3 v;
2900 mat4_mul_vec( &v, &H, &trail->pos );
2901 dx = v.v[0];
2902 dy = v.v[1];
2903 dz = v.v[2];
2904 /* Have to correct z so it's a valid depth for GLSL, by converting dz
2905 * to [-1,1] range. */
2906 dz /= p->ship->size * 0.5; /* Already scaled by radius. */
2907 p->trail[i]->ontop = 1; /* Since we use shaders to mess with it. */
2908 } else {
2909 p->trail[i]->ontop = 0;
2910 if ( !( trail->flags & SHIP_TRAIL_ALWAYS_UNDER ) && ( dirsin > 0 ) ) {
2911 /* See if the trail's front (tail) is in front of the ship. */
2912 double prod =
2913 ( trail_front( p->trail[i] ).x - p->solid.pos.x ) * dircos +
2914 ( trail_front( p->trail[i] ).y - p->solid.pos.y ) * dirsin;
2915
2916 p->trail[i]->ontop = ( prod < 0 );
2917 }
2918
2919 /* Figure our relative position. */
2920 dx = trail->pos.v[0] * dircos - trail->pos.v[1] * dirsin;
2921 dy = trail->pos.v[0] * dirsin + trail->pos.v[1] * dircos +
2922 trail->pos.v[2];
2923 dy *= M_SQRT1_2;
2924 dz = 1.; /* Will always be "above" */
2925 }
2926
2927 /* Check if needs scaling. */
2928 if ( pilot_isFlag( p, PILOT_LANDING ) )
2929 scale = CLAMP( 0., 1., p->ptimer / p->landing_delay );
2930 else if ( pilot_isFlag( p, PILOT_TAKEOFF ) )
2931 scale = CLAMP( 0., 1., 1. - p->ptimer / p->landing_delay );
2932 else
2933 scale = 1.;
2934 dx *= scale;
2935 dy *= scale;
2936 dz *= scale;
2937
2938 amod = p->solid.accel * p->engine_glow;
2939 ax = amod * -dircos;
2940 ay = amod * -dirsin;
2941
2942 /* Sample. */
2943 spfx_trail_sample( p->trail[i++], p->solid.pos.x + dx,
2944 p->solid.pos.y + dy, dz, ax, ay, mode,
2945 mode == MODE_NONE );
2946 }
2947}
2948
2953static int pilot_trail_generated( Pilot *p, int generator )
2954{
2955 return !p->ship->trail_emitters[generator].trail_spec->nebula ||
2956 ( cur_system->nebu_density > 0 ) ||
2957 ( sys_isFlag( cur_system, SYSTEM_NEBULATRAIL ) );
2958}
2959
2966{
2967 PilotOutfitSlot *dockslot;
2968
2969 /* Don't double delete, just in case. */
2970 if ( pilot_isFlag( p, PILOT_DELETE ) )
2971 return;
2972
2973 /* Stop ship stuff. */
2974 pilot_shipLCleanup( p );
2975
2976 /* Handle Lua outfits. */
2977 pilot_outfitOffAll( p );
2979
2980 /* Remove from parent's escort list */
2981 if ( p->parent != 0 ) {
2982 Pilot *leader = pilot_get( p->parent );
2983 if ( leader != NULL )
2984 escort_rmList( leader, p->id );
2985 }
2986
2987 /* Remove faction if necessary. */
2988 if ( p->presence > 0 ) {
2989 system_rmCurrentPresence( cur_system, p->faction_spawn, p->presence );
2990 p->presence = 0;
2991 }
2992
2993 /* Unmark as deployed if necessary */
2994 dockslot = pilot_getDockSlot( p );
2995 if ( dockslot != NULL ) {
2996 dockslot->u.ammo.deployed--;
2997 p->dockpilot = 0;
2998 p->dockslot = -1;
2999 }
3000
3001 /* Set flag to mark for deletion. */
3002 pilot_setFlag( p, PILOT_DELETE );
3003}
3004
3011static void pilot_hyperspace( Pilot *p, double dt )
3012{
3013 StarSystem *sys;
3014 double a, diff;
3015 int can_hyp;
3016 HookParam hparam;
3017
3018 /* pilot is actually in hyperspace */
3019 if ( pilot_isFlag( p, PILOT_HYPERSPACE ) ) {
3020
3021 /* Time to play sound. */
3022 if ( ( p->id == PLAYER_ID ) &&
3023 ( p->ptimer < sound_getLength( snd_hypPowUpJump ) ) &&
3024 ( p->timer[0] == -1. ) ) {
3025 p->timer[0] = -2.;
3027 }
3028
3029 /* has jump happened? */
3030 if ( p->ptimer < 0. ) {
3031 pilot_setFlag( p, PILOT_HYP_END );
3032 pilot_setAccel( p, 0. );
3033 if ( p->id == PLAYER_ID ) /* player.p just broke hyperspace */
3034 player_setFlag( PLAYER_HOOK_HYPER );
3035 else {
3036 hparam.type = HOOK_PARAM_JUMP;
3037 hparam.u.lj.srcid = cur_system->id;
3038 hparam.u.lj.destid = cur_system->jumps[p->nav_hyperspace].targetid;
3039
3040 /* Should be run before messing with delete flag. */
3041 pilot_runHookParam( p, PILOT_HOOK_JUMP, &hparam, 1 );
3042
3043 pilot_delete( p );
3044 }
3045 return;
3046 }
3047
3048 /* keep acceling - hyperspace uses much bigger accel */
3049 pilot_setAccel( p, HYPERSPACE_ACCEL / p->accel );
3050 }
3051 /* engines getting ready for the jump */
3052 else if ( pilot_isFlag( p, PILOT_HYP_BEGIN ) ) {
3053
3054 /* Make sure still within range. */
3055 can_hyp = space_canHyperspace( p );
3056 if ( !can_hyp ) {
3058
3059 if ( pilot_isPlayer( p ) )
3060 if ( !player_isFlag( PLAYER_AUTONAV ) )
3062 "#r%s",
3063 _( "Strayed too far from jump point: jump aborted." ) );
3064 } else if ( pilot_isFlag( p, PILOT_AFTERBURNER ) ) {
3066
3067 if ( pilot_isPlayer( p ) )
3068 if ( !player_isFlag( PLAYER_AUTONAV ) )
3069 player_message( "#r%s",
3070 _( "Afterburner active: jump aborted." ) );
3071 } else {
3072 if ( p->ptimer < 0. ) { /* engines ready */
3073 p->ptimer = HYPERSPACE_FLY_DELAY * p->stats.jump_delay;
3074 pilot_setFlag( p, PILOT_HYPERSPACE );
3075 if ( p->id == PLAYER_ID )
3076 p->timer[0] = -1.;
3077 }
3078 }
3079 }
3080 /* pilot is getting ready for hyperspace */
3081 else {
3082 /* Make sure still within range. */
3083 can_hyp = space_canHyperspace( p );
3084 if ( !can_hyp ) {
3086
3087 if ( pilot_isPlayer( p ) )
3088 if ( !player_isFlag( PLAYER_AUTONAV ) )
3090 "#r%s",
3091 _( "Strayed too far from jump point: jump aborted." ) );
3092 } else {
3093 /* If the ship needs to charge up its hyperdrive, brake. */
3094 if ( !p->stats.misc_instant_jump &&
3095 !pilot_isFlag( p, PILOT_HYP_BRAKE ) && !pilot_isStopped( p ) )
3096 pilot_brake( p, dt );
3097 /* face target */
3098 else {
3099 /* Done braking or no braking required. */
3100 pilot_setFlag( p, PILOT_HYP_BRAKE );
3101 pilot_setAccel( p, 0. );
3102
3103 /* Face system headed to. */
3104 sys = cur_system->jumps[p->nav_hyperspace].target;
3105 a = ANGLE( sys->pos.x - cur_system->pos.x,
3106 sys->pos.y - cur_system->pos.y );
3107 diff = pilot_face( p, a, dt );
3108
3109 if ( ABS( diff ) < MIN_DIR_ERR ) { /* we can now prepare the jump */
3110 if ( jp_isFlag( &cur_system->jumps[p->nav_hyperspace],
3111 JP_EXITONLY ) ) {
3112 WARN( _( "Pilot '%s' trying to jump through exit-only jump "
3113 "from '%s' to '%s'" ),
3114 p->name, cur_system->name, sys->name );
3115 } else {
3116 pilot_setTurn( p, 0. );
3117 p->ptimer = HYPERSPACE_ENGINE_DELAY * p->stats.jump_warmup *
3118 !p->stats.misc_instant_jump;
3119 pilot_setFlag( p, PILOT_HYP_BEGIN );
3120 /* Player plays sound. */
3121 if ( ( p->id == PLAYER_ID ) && !p->stats.misc_instant_jump )
3123 }
3124 }
3125 }
3126 }
3127 }
3128
3129 if ( pilot_isPlayer( p ) )
3130 player_updateSpecific( p, dt );
3131}
3132
3141{
3142 if ( pilot_isFlag( p, PILOT_HYPERSPACE ) )
3143 return;
3144
3145 if ( pilot_isFlag( p, PILOT_HYP_BEGIN ) ) {
3146 /* Player plays sound. */
3147 if ( p->id == PLAYER_ID ) {
3150 }
3151 }
3152 pilot_rmFlag( p, PILOT_HYP_BEGIN );
3153 pilot_rmFlag( p, PILOT_HYP_BRAKE );
3154 pilot_rmFlag( p, PILOT_HYP_PREP );
3155
3156 /* Try to inform followers. */
3157 for ( int i = 0; i < array_size( p->escorts ); i++ ) {
3158 const Pilot *e = pilot_get( p->escorts[i].id );
3159 if ( e == NULL ) /* Most likely died. */
3160 continue;
3161 pilot_msg( p, e, "hyperspace_abort", 0 );
3162 }
3163}
3164
3171{
3172 Pilot *target = pilot_getTarget( p );
3173 /* Check to see if target exists, remove flag if not. */
3174 if ( target == NULL ) {
3175 pilot_rmFlag( p, PILOT_REFUELING );
3176 return 0;
3177 }
3178
3179 /* Conditions are the same as boarding, except disabled. */
3180 if ( vec2_dist( &p->solid.pos, &target->solid.pos ) >
3181 target->ship->size * PILOT_SIZE_APPROX )
3182 return 0;
3183 else if ( vec2_dist2( &p->solid.vel, &target->solid.vel ) >
3184 pow2( MAX_HYPERSPACE_VEL ) )
3185 return 0;
3186
3187 /* Now start the boarding to refuel. */
3188 pilot_setFlag( p, PILOT_REFUELBOARDING );
3189 p->ptimer = PILOT_REFUEL_TIME; /* Use timer to handle refueling. */
3190 return 1;
3191}
3192
3196static void pilot_updateSolid( Pilot *p, double dt )
3197{
3198 p->solid.update( &p->solid, dt );
3199 /* TODO remove this from below when moving more towards 3D ships. */
3200 gl_getSpriteFromDir( &p->tsx, &p->tsy, p->ship->sx, p->ship->sy,
3201 p->solid.dir );
3202}
3203
3210static void pilot_refuel( Pilot *p, double dt )
3211{
3212 (void)dt;
3213 /* Check to see if target exists, remove flag if not. */
3214 Pilot *target = pilot_getTarget( p );
3215 if ( target == NULL ) {
3216 pilot_rmFlag( p, PILOT_REFUELBOARDING );
3217 pilot_rmFlag( p, PILOT_REFUELING );
3218 return;
3219 }
3220
3221 /* Match speeds. */
3222 p->solid.vel = target->solid.vel;
3223
3224 /* Check to see if done. */
3225 if ( p->ptimer < 0. ) {
3226 /* Move fuel. */
3227 double amount = MIN( p->fuel, p->refuel_amount );
3228 amount = MIN( amount, target->fuel_max - target->fuel );
3229 p->fuel -= amount;
3230 target->fuel += amount;
3231
3232 pilot_rmFlag( p, PILOT_REFUELBOARDING );
3233 pilot_rmFlag( p, PILOT_REFUELING );
3234 }
3235}
3236
3243ntime_t pilot_hyperspaceDelay( const Pilot *p )
3244{
3245 int stu = (int)( NT_PERIOD_SECONDS * p->stats.jump_delay );
3246 return ntime_create( 0, 0, stu );
3247}
3248
3255void pilot_untargetAsteroid( int anchor, int asteroid )
3256{
3257 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
3258 if ( ( pilot_stack[i]->nav_asteroid == asteroid ) &&
3259 ( pilot_stack[i]->nav_anchor == anchor ) ) {
3260 pilot_stack[i]->nav_asteroid = -1;
3261 pilot_stack[i]->nav_anchor = -1;
3262 }
3263 }
3264}
3265
3269int pilot_numOutfit( const Pilot *p, const Outfit *o )
3270{
3271 int q = 0;
3272 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
3273 if ( p->outfits[i]->outfit == o )
3274 q++;
3275 }
3276 return q;
3277}
3278
3286int pilot_hasCredits( const Pilot *p, credits_t amount )
3287{
3288 if ( amount < 0 )
3289 return 1;
3290 return ( amount <= p->credits );
3291}
3292
3300credits_t pilot_modCredits( Pilot *p, credits_t amount )
3301{
3302 if ( amount > 0 ) {
3303 if ( CREDITS_MAX - p->credits <= amount )
3304 p->credits = CREDITS_MAX;
3305 else
3306 p->credits += amount;
3307 } else if ( amount < 0 ) {
3308 /* ABS(CREDITS_MIN) doesn't work properly because it might be
3309 * -2,147,483,648, which ABS will try to convert to 2,147,483,648.
3310 * Problem is, that value would be represented like this in
3311 * binary:
3312 *
3313 * 10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
3314 *
3315 * Which is actually -2,147,483,648, causing the condition
3316 * ABS(amount) >= p->credits to return false (since -2,147,483,648
3317 * is less than any amount of credits the player could have). */
3318 if ( ( amount <= CREDITS_MIN ) || ( ABS( amount ) >= p->credits ) )
3319 p->credits = 0;
3320 else
3321 p->credits += amount;
3322 }
3323 return p->credits;
3324}
3325
3340static void pilot_init( Pilot *pilot, const Ship *ship, const char *name,
3341 int faction, double dir, const vec2 *pos,
3342 const vec2 *vel, const PilotFlags flags,
3343 unsigned int dockpilot, int dockslot )
3344{
3345 PilotOutfitSlot *dslot;
3346 PilotOutfitSlot **pilot_list_ptr[] = {
3347 &pilot->outfit_structure, &pilot->outfit_utility, &pilot->outfit_weapon };
3348 ShipOutfitSlot *ship_list[] = { ship->outfit_structure, ship->outfit_utility,
3349 ship->outfit_weapon };
3350
3351 /* Randomness. */
3352 pilot->r = RNGF();
3353
3354 /* Defaults. */
3355 pilot->lua_mem = LUA_NOREF;
3356 pilot->lua_ship_mem = LUA_NOREF;
3357 pilot->autoweap = 1;
3358 pilot->aimLines = 0;
3359 pilot->dockpilot = dockpilot;
3360 pilot->parent = dockpilot; /* leader will default to mothership if exists. */
3361 pilot->dockslot = dockslot;
3362
3363 /* Basic information. */
3364 pilot->ship = ship;
3365 pilot->name = strdup( ( name == NULL ) ? _( ship->name ) : name );
3366
3367 /* faction */
3368 pilot->faction = faction;
3369 pilot->faction_spawn = faction; /* Just copy for now, but can be overwritten
3370 by spawn scheduler. */
3371
3372 /* solid */
3373 solid_init( &pilot->solid, ship->mass, dir, pos, vel, SOLID_UPDATE_RK4 );
3374
3375 /* First pass to make sure requirements make sense. */
3376 pilot->armour = pilot->armour_max = 1.; /* hack to have full armour */
3377 pilot->shield = pilot->shield_max = 1.; /* ditto shield */
3378 pilot->energy = pilot->energy_max = 1.; /* ditto energy */
3379 pilot->fuel = pilot->fuel_max = 1.; /* ditto fuel */
3380 pilot_calcStats( pilot );
3381 pilot->stress = 0.; /* No stress. */
3382
3383 /* Allocate outfit memory. */
3384 pilot->outfits = array_create( PilotOutfitSlot * );
3385 /* First pass copy data. */
3386 for ( int i = 0; i < 3; i++ ) {
3387 *pilot_list_ptr[i] =
3388 array_create_size( PilotOutfitSlot, array_size( ship_list[i] ) );
3389 for ( int j = 0; j < array_size( ship_list[i] ); j++ ) {
3390 PilotOutfitSlot *slot = &array_grow( pilot_list_ptr[i] );
3391 memset( slot, 0, sizeof( PilotOutfitSlot ) );
3392 slot->id = array_size( pilot->outfits );
3393 slot->sslot = &ship_list[i][j];
3394 slot->weapset = -1;
3395 array_push_back( &pilot->outfits, slot );
3396 /* We'll ignore non-required outfits if NO_OUTFITS is set. */
3397 if ( ( !pilot_isFlagRaw( flags, PILOT_NO_OUTFITS ) ||
3398 slot->sslot->required || slot->sslot->locked ) &&
3399 slot->sslot->data != NULL )
3400 pilot_addOutfitRaw( pilot, slot->sslot->data, slot );
3401 }
3402 }
3403 array_shrink( &pilot->outfits );
3404
3405 /* Add intrinsics if applicable. */
3406 if ( !pilot_isFlagRaw( flags, PILOT_NO_OUTFITS ) ) {
3407 for ( int i = 0; i < array_size( ship->outfit_intrinsic ); i++ )
3409 }
3410
3411 /* We must set the weapon auto in case some of the outfits had a default
3412 * weapon equipped. */
3413 // pilot_weaponAuto( pilot );
3414
3415 /* cargo - must be set before calcStats */
3416 pilot->cargo_free =
3417 pilot->ship->cap_cargo; /* should get redone with calcCargo */
3418
3419 /* Initialize heat. */
3420 pilot_heatReset( pilot );
3421
3422 /* Set the pilot stats based on their ship and outfits */
3423 pilot_calcStats( pilot );
3424
3425 /* Update dynamic electronic warfare (static should have already been done).
3426 */
3427 pilot_ewUpdateDynamic( pilot, 0. );
3428
3429 /* Heal up the ship. */
3430 pilot->armour = pilot->armour_max;
3431 pilot->shield = pilot->shield_max;
3432 pilot->energy = pilot->energy_max;
3433 pilot->fuel = pilot->fuel_max;
3434
3435 /* Mark as deployed if needed */
3436 dslot = pilot_getDockSlot( pilot );
3437 if ( dslot != NULL )
3438 dslot->u.ammo.deployed++;
3439
3440 /* Safety check. */
3441#ifdef DEBUGGING
3442 if ( !pilot_isFlagRaw( flags, PILOT_NO_OUTFITS ) ) {
3443 char message[STRMAX_SHORT];
3444 int notworthy =
3445 pilot_reportSpaceworthy( pilot, message, sizeof( message ) );
3446 if ( notworthy ) {
3447 DEBUG( _( "Pilot '%s' failed safety check: %s" ), pilot->name,
3448 message );
3449 for ( int i = 0; i < array_size( pilot->outfits ); i++ ) {
3450 if ( pilot->outfits[i]->outfit != NULL )
3451 DEBUG( _( " [%d] %s" ), i,
3452 _( pilot->outfits[i]->outfit->name ) );
3453 }
3454 }
3455 }
3456#endif /* DEBUGGING */
3457
3458 /* Copy pilot flags. */
3459 pilot_copyFlagsRaw( pilot->flags, flags );
3460
3461 /* Clear timers. */
3462 pilot_clearTimers( pilot );
3463
3464 /* Update the solid as necessary. */
3465 pilot_updateSolid( pilot, 0. );
3466
3467 /* Targets. */
3468 pilot_setTarget( pilot, pilot->id ); /* No target. */
3469 pilot->nav_spob = -1;
3470 pilot->nav_hyperspace = -1;
3471 pilot->nav_anchor = -1;
3472 pilot->nav_asteroid = -1;
3473 pilot->shoot_indicator = 0;
3474
3475 /* Check takeoff. */
3476 if ( pilot_isFlagRaw( flags, PILOT_TAKEOFF ) ) {
3477 pilot->landing_delay = PILOT_TAKEOFF_DELAY * pilot->ship->dt_default;
3478 pilot->ptimer = pilot->landing_delay;
3479 }
3480
3481 /* Create empty table for messages. */
3482 lua_newtable( naevL );
3483 pilot->messages = luaL_ref( naevL, LUA_REGISTRYINDEX );
3484}
3485
3491void pilot_reset( Pilot *pilot )
3492{
3493 /* Clean up flags. */
3494 for ( int i = PILOT_NOCLEAR + 1; i < PILOT_FLAGS_MAX; i++ )
3495 pilot->flags[i] = 0;
3496
3497 /* Initialize heat. */
3498 pilot_heatReset( pilot );
3499
3500 /* Set the pilot stats based on their ship and outfits */
3501 pilot_calcStats( pilot );
3502
3503 /* Update dynamic electronic warfare (static should have already been done).
3504 */
3505 pilot_ewUpdateDynamic( pilot, 0. );
3506
3507 /* Clear timers. */
3508 pilot_clearTimers( pilot );
3509
3510 /* Update the solid as necessary. */
3511 pilot_updateSolid( pilot, 0. );
3512
3513 /* Heal up. */
3514 pilot_healLanded( pilot );
3515
3516 /* Targets. */
3517 pilot_setTarget( pilot, pilot->id ); /* No target. */
3518 pilot->nav_spob = -1;
3519 pilot->nav_hyperspace = -1;
3520 pilot->nav_anchor = -1;
3521 pilot->nav_asteroid = -1;
3522
3523 /* AI */
3524 pilot->shoot_indicator = 0;
3525
3526 /* Run Lua stuff. */
3527 pilot_shipLInit( pilot );
3528 pilot_outfitLInitAll( pilot );
3529}
3530
3535static void pilot_init_trails( Pilot *p )
3536{
3537 int n = array_size( p->ship->trail_emitters );
3538 if ( p->trail == NULL )
3539 p->trail = array_create_size( Trail_spfx *, n );
3540
3541 for ( int g = 0; g < n; g++ )
3542 if ( pilot_trail_generated( p, g ) )
3544 &p->trail,
3545 spfx_trail_create( p->ship->trail_emitters[g].trail_spec ) );
3546}
3547
3557Pilot *pilot_create( const Ship *ship, const char *name, int faction,
3558 const char *ai, const double dir, const vec2 *pos,
3559 const vec2 *vel, const PilotFlags flags,
3560 unsigned int dockpilot, int dockslot )
3561{
3562 /* Allocate pilot memory. */
3563 Pilot *p = nmalloc( sizeof( Pilot ) );
3564 if ( p == NULL ) {
3565 WARN( _( "Unable to allocate memory" ) );
3566 return 0;
3567 }
3568
3569 NTracingZone( _ctx, 1 );
3570
3571 /* Set the pilot in the stack -- must be there before initializing */
3573 memset( p, 0, sizeof( Pilot ) );
3574
3575 /* Load ship graphics. */
3576 ship_gfxLoad( (Ship *)ship ); /* TODO no casting. */
3577
3578 /* Set the ID, has to be set before pilot_init as escort stuff may do
3579 * pilot_get in pilot_init. */
3580 if ( pilot_isFlagRaw(
3581 flags, PILOT_PLAYER ) ) { /* Set player ID. TODO should probably be
3582 fixed to something better someday. */
3583 p->id = PLAYER_ID;
3584 qsort( pilot_stack, array_size( pilot_stack ), sizeof( Pilot * ),
3585 pilot_cmp );
3586 } else
3587 p->id =
3588 ++pilot_id; /* new unique pilot id based on pilot_id, can't be 0 */
3589
3590 /* Initialize the pilot. */
3591 pilot_init( p, ship, name, faction, dir, pos, vel, flags, dockpilot,
3592 dockslot );
3593
3594 /* Initialize AI if applicable. */
3595 if ( ai == NULL )
3596 ai = faction_default_ai( faction );
3597 if ( ai != NULL )
3598 ai_pinit( p, ai ); /* Must run before ai_create */
3599
3600 /* Run Lua stuff. */
3601 pilot_shipLInit( p );
3603
3604 /* Animated trail. */
3605 pilot_init_trails( p );
3606
3607 /* Pilot creation hook. */
3608 pilot_runHook( p, PILOT_HOOK_CREATION );
3609
3610 /* Add to quadtree. */
3611 pilot_addQuadtree( p, array_size( pilot_stack ) - 1 );
3612
3613 NTracingZoneEnd( _ctx );
3614
3615 return p;
3616}
3617
3627Pilot *pilot_createEmpty( const Ship *ship, const char *name, int faction,
3628 PilotFlags flags )
3629{
3630 Pilot *dyn = nmalloc( sizeof( Pilot ) );
3631 if ( dyn == NULL ) {
3632 WARN( _( "Unable to allocate memory" ) );
3633 return 0;
3634 }
3635 memset( dyn, 0, sizeof( Pilot ) );
3636 pilot_init( dyn, ship, name, faction, 0., NULL, NULL, flags, 0, 0 );
3637 return dyn;
3638}
3639
3646unsigned int pilot_clone( const Pilot *ref )
3647{
3648 Pilot *dyn, **p;
3649 PilotFlags pf;
3650
3651 pilot_clearFlagsRaw( &pf );
3652 pilot_setFlagRaw( pf, PILOT_NO_OUTFITS );
3653
3654 /* Allocate pilot memory. */
3655 dyn = nmalloc( sizeof( Pilot ) );
3656 if ( dyn == NULL ) {
3657 WARN( _( "Unable to allocate memory" ) );
3658 return 0;
3659 }
3660
3661 /* Set the pilot in the stack -- must be there before initializing */
3662 p = &array_grow( &pilot_stack );
3663 *p = dyn;
3664 memset( dyn, 0, sizeof( Pilot ) );
3665 dyn->id = ++pilot_id; /* new unique pilot id. */
3666
3667 /* Initialize the pilot. */
3668 pilot_init( dyn, ref->ship, ref->name, ref->faction, ref->solid.dir,
3669 &ref->solid.pos, &ref->solid.vel, pf, 0, 0 );
3670
3671 /* Add outfits over. */
3672 for ( int i = 0; i < array_size( ref->outfits ); i++ )
3673 if ( ref->outfits[i]->outfit != NULL )
3674 pilot_addOutfitRaw( dyn, ref->outfits[i]->outfit, dyn->outfits[i] );
3675 for ( int i = 0; i < array_size( ref->outfit_intrinsic ); i++ )
3677
3678 /* Reset the pilot. */
3679 pilot_reset( dyn );
3680
3681 /* Add to quadtree. */
3682 pilot_addQuadtree( dyn, array_size( pilot_stack ) - 1 );
3683
3684 return dyn->id;
3685}
3686
3690unsigned int pilot_addStack( Pilot *p )
3691{
3692 p->id = ++pilot_id; /* new unique pilot id based on pilot_id, can't be 0 */
3693 pilot_setFlag( p, PILOT_NOFREE );
3694
3696
3697 /* Load ship graphics. */
3698 ship_gfxLoad( (Ship *)p->ship ); /* TODO no casting. */
3699
3700 /* Have to reset after adding to stack, as some Lua functions will run code
3701 * on the pilot. */
3702 pilot_reset( p );
3703
3704 /* Animated trail. */
3705 pilot_init_trails( p );
3706
3707#if DEBUGGING
3708 for ( int i = 1; i < array_size( pilot_stack ); i++ )
3709 if ( pilot_stack[i] == pilot_stack[i - 1] )
3710 WARN( _( "Duplicate pilots on stack!" ) );
3711#endif /* DEBUGGING */
3712
3713 /* Add to quadtree. */
3714 pilot_addQuadtree( p, array_size( pilot_stack ) - 1 );
3715
3716 return p->id;
3717}
3718
3723{
3724 for ( int j = 0; j < array_size( p->trail ); j++ )
3725 spfx_trail_remove( p->trail[j] );
3726 array_erase( &p->trail, array_begin( p->trail ), array_end( p->trail ) );
3727 pilot_init_trails( p );
3728}
3729
3736{
3737 int i = pilot_getStackPos( PLAYER_ID );
3738 int l = pilot_getStackPos( after->id );
3739
3740 if ( i < 0 ) { /* No existing player ID. */
3741 if ( l < 0 ) /* No existing pilot, have to create. */
3742 array_push_back( &pilot_stack, after );
3743 } else { /* Player pilot already exists. */
3744 if ( l >= 0 )
3745 pilot_delete( pilot_stack[i] ); /* Both player and after are on stack.
3746 Remove player. */
3747 else
3748 pilot_stack[i] = after; /* after overwrites player. */
3749 }
3750 after->id = PLAYER_ID;
3751 qsort( pilot_stack, array_size( pilot_stack ), sizeof( Pilot * ),
3752 pilot_cmp );
3753
3754 /* Load graphics if necessary. */
3755 ship_gfxLoad( (Ship *)after->ship );
3756
3757 /* Set up stuff. */
3758 player.p = after;
3759 pilot_clearTrails( after );
3760
3761 /* Initialize AI as necessary. */
3762 ai_pinit( after, "player" );
3763
3764 /* Reset pilot. */
3765 pilot_reset( after );
3766
3767 /* Set player flag. */
3768 pilot_setFlag( after, PILOT_PLAYER );
3769 pilot_setFlag( after, PILOT_NOFREE );
3770
3771 return after;
3772}
3773
3784void pilot_choosePoint( vec2 *vp, Spob **spob, JumpPoint **jump, int lf,
3785 int ignore_rules, int guerilla )
3786{
3787 int *ind;
3788 JumpPoint **validJumpPoints;
3789
3790 /* Initialize. */
3791 *spob = NULL;
3792 *jump = NULL;
3793 vectnull( vp );
3794
3795 /* Build landable spob table. */
3796 ind = array_create_size( int, array_size( cur_system->spobs ) );
3797 for ( int i = 0; i < array_size( cur_system->spobs ); i++ ) {
3798 const Spob *pnt = cur_system->spobs[i];
3799 if ( spob_hasService( pnt, SPOB_SERVICE_INHABITED ) &&
3800 !areEnemies( lf, pnt->presence.faction ) )
3801 array_push_back( &ind, i );
3802 }
3803
3804 /* Build jumpable jump table. */
3805 validJumpPoints =
3806 array_create_size( JumpPoint *, array_size( cur_system->jumps ) );
3807 if ( array_size( cur_system->jumps ) > 0 ) {
3808 for ( int i = 0; i < array_size( cur_system->jumps ); i++ ) {
3809 /* The jump into the system must not be exit-only, and unless
3810 * ignore_rules is set, must also be non-hidden
3811 * (excepted if the pilot is guerilla) and have faction
3812 * presence matching the pilot's on the remote side. */
3813 const JumpPoint *jmp = &cur_system->jumps[i];
3814 JumpPoint *target = jmp->returnJump;
3815 double limit, pres;
3816 const int *fact;
3817
3818 /* Can't use exit only from the other-side. */
3819 if ( jp_isFlag( target, JP_EXITONLY ) )
3820 continue;
3821
3822 if ( ignore_rules ) {
3823 array_push_back( &validJumpPoints, target );
3824 continue;
3825 }
3826
3827 /* Only guerrila entrances can use hidden jumps. */
3828 if ( jp_isFlag( jmp, JP_HIDDEN ) && !guerilla )
3829 continue;
3830
3831 /* Test presence on the other side, making sure there is presence. */
3832 pres = system_getPresence( jmp->target, lf );
3833 if ( pres <= 0. )
3834 continue;
3835
3836 /* See if the remote system isn't dominantly controlled by enemies. */
3837 limit = 0.;
3838 fact = faction_getEnemies( lf );
3839 for ( int j = 0; j < array_size( fact ); j++ )
3840 limit += system_getPresence( jmp->target, fact[j] );
3841 if ( pres > limit )
3842 array_push_back( &validJumpPoints, target );
3843 }
3844 }
3845
3846 /* Unusual case no landable nor presence, we'll just jump in randomly if
3847 * possible. */
3848 if ( array_size( ind ) == 0 && array_size( validJumpPoints ) == 0 ) {
3849 if ( guerilla ) /* Guerilla ships are created far away in deep space. */
3850 vec2_pset( vp, 1.5 * cur_system->radius, RNGF() * 2 * M_PI );
3851 else if ( array_size( cur_system->jumps ) > 0 ) {
3852 for ( int i = 0; i < array_size( cur_system->jumps ); i++ ) {
3853 JumpPoint *jp = &cur_system->jumps[i];
3854 if ( jp_isFlag( jp->returnJump, JP_EXITONLY ) )
3855 continue;
3856 /* Ignore hidden jumps for now. */
3857 if ( jp_isFlag( jp, JP_HIDDEN ) )
3858 continue;
3859 array_push_back( &validJumpPoints, jp->returnJump );
3860 }
3861 /* Now add hidden jumps as a last resort - only for non guerillas as
3862 * they should be added otherwise. */
3863 if ( !guerilla && array_size( validJumpPoints ) <= 0 ) {
3864 for ( int i = 0; i < array_size( cur_system->jumps ); i++ ) {
3865 JumpPoint *jp = &cur_system->jumps[i];
3866 if ( jp_isFlag( jp->returnJump, JP_EXITONLY ) )
3867 continue;
3868 array_push_back( &validJumpPoints, jp->returnJump );
3869 }
3870 }
3871 } else {
3872 WARN( _( "Creating pilot in system with no jumps nor spobs to take "
3873 "off from!" ) );
3874 vectnull( vp );
3875 }
3876 }
3877
3878 /* Calculate jump chance. */
3879 if ( array_size( ind ) > 0 || array_size( validJumpPoints ) > 0 ) {
3880 double chance = array_size( validJumpPoints );
3881 chance = chance / ( chance + array_size( ind ) );
3882
3883 /* Random jump in. */
3884 if ( ( RNGF() <= chance ) && ( validJumpPoints != NULL ) )
3885 *jump =
3886 validJumpPoints[RNG_BASE( 0, array_size( validJumpPoints ) - 1 )];
3887 /* Random take off. */
3888 else if ( array_size( ind ) != 0 )
3889 *spob = cur_system->spobs[ind[RNG_BASE( 0, array_size( ind ) - 1 )]];
3890 }
3891
3892 /* Free memory allocated. */
3893 array_free( ind );
3894 array_free( validJumpPoints );
3895}
3896
3903{
3904 NTracingZone( _ctx, 1 );
3905
3906 /* Clear some useful things. */
3907 pilot_clearHooks( p );
3908 effect_cleanup( p->effects );
3909 p->effects = NULL;
3910 escort_freeList( p );
3911
3912 /* If hostile, must remove counter. */
3913 pilot_rmHostile( p );
3914
3915 /* Free animated trail. */
3916 for ( int i = 0; i < array_size( p->trail ); i++ )
3917 spfx_trail_remove( p->trail[i] );
3918 array_free( p->trail );
3919 p->trail = NULL;
3920
3921 /* We don't actually free internals of the pilot once we cleaned up stuff. */
3922 if ( pilot_isFlag( p, PILOT_NOFREE ) ) {
3923 p->id = 0; /* Invalidate ID. */
3924 NTracingZoneEnd( _ctx );
3925 return;
3926 }
3927
3928 /* Only remove cargo at the end. */
3929 pilot_cargoRmAll( p, 1 );
3930
3931 /* Clean up stats. */
3932 ss_free( p->ship_stats );
3933 ss_free( p->intrinsic_stats );
3934
3935 lvar_freeArray( p->shipvar );
3936
3937 pilot_weapSetFree( p );
3938
3939 /* Clean up outfit slots. */
3940 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
3941 ss_free( p->outfits[i]->lua_stats );
3942 }
3943 array_free( p->outfits );
3944 array_free( p->outfit_structure );
3945 array_free( p->outfit_utility );
3946 array_free( p->outfit_weapon );
3947 array_free( p->outfit_intrinsic );
3948
3949 /* Clean up data. */
3950 ai_destroy( p ); /* Must be destroyed first if applicable. */
3951
3952 free( p->name );
3953 /* Case if pilot is the player. */
3954 if ( player.p == p ) {
3955 player.p = NULL;
3956 player.ps.p = NULL;
3957 }
3958 // solid_free(p->solid);
3959 free( p->mounted );
3960
3961 escort_freeList( p );
3962
3963 free( p->comm_msg );
3964
3965 /* Free messages. */
3966 luaL_unref( naevL, p->messages, LUA_REGISTRYINDEX );
3967
3968#ifdef DEBUGGING
3969 memset( p, 0, sizeof( Pilot ) );
3970#endif /* DEBUGGING */
3971
3972 nfree( p );
3973
3974 NTracingZoneEnd( _ctx );
3975}
3976
3982static void pilot_erase( Pilot *p )
3983{
3984 int i = pilot_getStackPos( p->id );
3985 pilot_free( p );
3987}
3988
3993{
3994 int i = pilot_getStackPos( p->id );
3995#ifdef DEBUGGING
3996 if ( i < 0 ) {
3997 WARN( _( "Trying to remove non-existent pilot '%s' from stack!" ),
3998 p->name );
3999 p->id = 0;
4000 return;
4001 }
4002#endif /* DEBUGGING */
4003 p->id = 0;
4005}
4006
4010void pilots_init( void )
4011{
4013 il_create( &pilot_qtquery, 1 );
4014}
4015
4019void pilots_free( void )
4020{
4022
4023 /* First pass to stop outfits. */
4024 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
4025 /* Stop ship stuff. */
4027 /* Stop all outfits. */
4029 /* Handle Lua outfits. */
4031 }
4032
4033 /* Free pilots. */
4034 for ( int i = 0; i < array_size( pilot_stack ); i++ )
4035 pilot_free( pilot_stack[i] );
4037 pilot_stack = NULL;
4038 player.p = NULL;
4039 free( player.ps.acquired );
4040 memset( &player.ps, 0, sizeof( PlayerShip_t ) );
4041
4042 /* Clean up quadtree. */
4043 qt_destroy( &pilot_quadtree );
4044 il_destroy( &pilot_qtquery );
4045}
4046
4052void pilots_clean( int persist )
4053{
4054 int persist_count = 0;
4055 NTracingZone( _ctx, 1 );
4056
4057 /* First pass to stop outfits without clearing stuff - this can call all
4058 * sorts of Lua stuff. */
4059 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
4060 Pilot *p = pilot_stack[i];
4061 if ( p == player.p && ( persist && pilot_isFlag( p, PILOT_PERSIST ) ) )
4062 continue;
4063 /* Stop all outfits. */
4064 pilot_outfitOffAll( p );
4065 /* Handle Lua outfits. */
4067 }
4068
4069 /* Here we actually clean up stuff. */
4070 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
4071 /* move player and persisted pilots to start */
4072 if ( !pilot_isFlag( pilot_stack[i], PILOT_DELETE ) &&
4073 ( pilot_stack[i] == player.p ||
4074 ( persist && pilot_isFlag( pilot_stack[i], PILOT_PERSIST ) ) ) ) {
4075 /* Have to swap the pilots so it gets properly freed. */
4076 Pilot *p = pilot_stack[persist_count];
4077 pilot_stack[persist_count] = pilot_stack[i];
4078 pilot_stack[i] = p;
4079 p = pilot_stack[persist_count];
4080 /* Misc clean up. */
4081 p->lockons = 0; /* Clear lockons. */
4082 p->projectiles = 0; /* Clear projectiles. */
4083 pilot_clearTimers( p ); /* Reset timers. */
4084 /* Reset trails */
4085 for ( int g = 0; g < array_size( p->trail ); g++ )
4086 spfx_trail_remove( p->trail[g] );
4087 array_erase( &p->trail, array_begin( p->trail ),
4088 array_end( p->trail ) );
4089 /* All done. */
4090 persist_count++;
4091 } else /* rest get killed */
4092 pilot_free( pilot_stack[i] );
4093 }
4094 array_erase( &pilot_stack, &pilot_stack[persist_count],
4096
4097 /* Init AI on the remaining pilots, has to be done here so the pilot_stack is
4098 * consistent. */
4099 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
4100 Pilot *p = pilot_stack[i];
4101 pilot_clearHooks( p );
4102 ai_cleartasks( p );
4103 ai_init( p );
4104 }
4105
4106 /* Clear global hooks. */
4108
4109 NTracingZoneEnd( _ctx );
4110}
4111
4117{
4118 double r = cur_system->radius * 1.1;
4119
4120 NTracingZone( _ctx, 1 );
4121
4123 for ( int i = 0; i < array_size( pilot_stack ); i++ )
4125
4126 if ( qt_init )
4127 qt_destroy( &pilot_quadtree );
4128 qt_create( &pilot_quadtree, -r, -r, r, r, qt_max_elem, qt_depth );
4129 qt_init = 1;
4130
4131 NTracingZoneEnd( _ctx );
4132}
4133
4137void pilots_clear( void )
4138{
4139 for ( int i = 0; i < array_size( pilot_stack ); i++ )
4140 if ( !pilot_isPlayer( pilot_stack[i] ) &&
4141 !pilot_isFlag( pilot_stack[i], PILOT_NOCLEAR ) )
4143}
4144
4149{
4150 pilots_clean( 0 );
4151 if ( player.p != NULL ) {
4152 player_rmPlayerShip( &player.ps );
4153 player.p = NULL;
4154 memset( &player.ps, 0, sizeof( PlayerShip_t ) );
4155 }
4158}
4159
4160static void pilot_addQuadtree( const Pilot *p, int i )
4161{
4162 int x, y, w2, h2, px, py;
4163 x = round( p->solid.pos.x );
4164 y = round( p->solid.pos.y );
4165 px = round( p->solid.pre.x );
4166 py = round( p->solid.pre.y );
4167 w2 = ceil( p->ship->size * 0.5 );
4168 h2 = ceil( p->ship->size * 0.5 );
4169 qt_insert( &pilot_quadtree, i, MIN( x, px ) - w2, MIN( y, py ) - h2,
4170 MAX( x, px ) + w2, MAX( y, py ) + h2 );
4171}
4172
4177{
4178 NTracingZone( _ctx, 1 );
4179
4180 /* Delete loop - this should be atomic or we get hook fuckery! */
4181 for ( int i = array_size( pilot_stack ) - 1; i >= 0; i-- ) {
4182 Pilot *p = pilot_stack[i];
4183
4184 /* Clear target. */
4185 p->ptarget = NULL;
4186
4187 /* Destroy pilot and go on. */
4188 if ( pilot_isFlag( p, PILOT_DELETE ) )
4189 pilot_erase( p );
4190 }
4191
4192 /* Second loop sets up quadtrees. */
4193 qt_clear( &pilot_quadtree ); /* Empty it. */
4194 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
4195 const Pilot *p = pilot_stack[i];
4196
4197 /* Ignore pilots being deleted. */
4198 if ( pilot_isFlag( p, PILOT_DELETE ) )
4199 continue;
4200
4201 /* Ignore hidden pilots. */
4202 if ( pilot_isFlag( p, PILOT_HIDE ) )
4203 continue;
4204
4205 pilot_addQuadtree( p, i );
4206 }
4207
4208 NTracingZoneEnd( _ctx );
4209}
4210
4216void pilots_update( double dt )
4217{
4218 NTracingZone( _ctx, 1 );
4219 NTracingPlotI( "pilots", array_size( pilot_stack ) );
4220
4221 /* Have all the pilots think. */
4222 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
4223 Pilot *p = pilot_stack[i];
4224
4225 /* Invisible, not doing anything. */
4226 if ( pilot_isFlag( p, PILOT_HIDE ) )
4227 continue;
4228
4229 /* See if should think. */
4230 if ( pilot_isDisabled( p ) )
4231 continue;
4232 if ( pilot_isFlag( p, PILOT_DEAD ) || pilot_isFlag( p, PILOT_DELETE ) )
4233 continue;
4234
4235 /* Ignore persisting pilots during simulation since they don't get
4236 * cleared. */
4237 if ( space_isSimulation() && ( pilot_isFlag( p, PILOT_PERSIST ) ) )
4238 continue;
4239
4240 /* Hyperspace gets special treatment */
4241 if ( pilot_isFlag( p, PILOT_HYP_PREP ) ) {
4242 if ( !pilot_isFlag( p, PILOT_HYPERSPACE ) )
4243 ai_think( p, dt, 0 );
4244 pilot_hyperspace( p, dt );
4245 }
4246 /* Entering hyperspace. */
4247 else if ( pilot_isFlag( p, PILOT_HYP_END ) ) {
4248 if ( ( VMOD( p->solid.vel ) <
4249 2 * solid_maxspeed( &p->solid, p->speed, p->accel ) ) &&
4250 ( p->ptimer < 0. ) )
4251 pilot_rmFlag( p, PILOT_HYP_END );
4252 }
4253 /* Must not be boarding to think. */
4254 else if ( !pilot_isFlag( p, PILOT_BOARDING ) &&
4255 !pilot_isFlag( p, PILOT_REFUELBOARDING ) &&
4256 /* Must not be landing nor taking off. */
4257 !pilot_isFlag( p, PILOT_LANDING ) &&
4258 !pilot_isFlag( p, PILOT_TAKEOFF ) &&
4259 /* Must not be jumping in. */
4260 !pilot_isFlag( p, PILOT_HYP_END ) ) {
4261 if ( pilot_isFlag( p, PILOT_PLAYER ) )
4262 player_think( p, dt );
4263 else
4264 ai_think( p, dt, 1 );
4265 }
4266 }
4267
4268 /* Now update all the pilots. */
4269 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
4270 Pilot *p = pilot_stack[i];
4271
4272 /* Ignore. */
4273 if ( pilot_isFlag( p, PILOT_DELETE ) )
4274 continue;
4275
4276 /* Invisible, not doing anything. */
4277 if ( pilot_isFlag( p, PILOT_HIDE ) )
4278 continue;
4279
4280 /* Just update the pilot. */
4281 if ( pilot_isFlag( p, PILOT_PLAYER ) )
4282 player_update( p, dt );
4283 else
4284 pilot_update( p, dt );
4285 }
4286
4287 NTracingZoneEnd( _ctx );
4288}
4289
4293void pilots_render( void )
4294{
4295 NTracingZone( _ctx, 1 );
4296
4297 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
4298 Pilot *p = pilot_stack[i];
4299
4300 /* Invisible, not doing anything. */
4301 if ( pilot_isFlag( p, PILOT_HIDE ) || pilot_isFlag( p, PILOT_DELETE ) )
4302 continue;
4303
4304 if ( !pilot_isFlag( p, PILOT_PLAYER ) )
4305 pilot_render( p );
4306 }
4307
4308 NTracingZoneEnd( _ctx );
4309}
4310
4315{
4316 NTracingZone( _ctx, 1 );
4317
4318 for ( int i = 0; i < array_size( pilot_stack ); i++ ) {
4319 Pilot *p = pilot_stack[i];
4320
4321 /* Invisible, not doing anything. */
4322 if ( pilot_isFlag( p, PILOT_HIDE ) || pilot_isFlag( p, PILOT_DELETE ) )
4323 continue;
4324
4325 if ( !pilot_isFlag( p, PILOT_PLAYER ) )
4327 }
4328
4329 NTracingZoneEnd( _ctx );
4330}
4331
4338{
4339 /* Clear outfits first to not leave some outfits in dangling states. */
4340 pilot_outfitOffAll( pilot );
4341
4342 pilot->ptimer = 0.; /* Pilot timer. */
4343 pilot->tcontrol = 0.; /* AI control timer. */
4344 pilot->stimer = 0.; /* Shield timer. */
4345 pilot->dtimer = 0.; /* Disable timer. */
4346 pilot->otimer = 0.; /* Outfit timer. */
4347 for ( int i = 0; i < MAX_AI_TIMERS; i++ )
4348 pilot->timer[i] = 0.; /* Specific AI timers. */
4349 for ( int i = 0; i < array_size( pilot->outfits ); i++ ) {
4350 PilotOutfitSlot *o = pilot->outfits[i];
4351 o->timer = 0.; /* Last used timer. */
4352 o->stimer = 0.; /* State timer. */
4353 }
4354}
4355
4364double pilot_relsize( const Pilot *cur_pilot, const Pilot *p )
4365{
4366 return ( 1. - 1. / ( 1. + ( (double)cur_pilot->solid.mass /
4367 (double)p->solid.mass ) ) );
4368}
4369
4377void pilot_dpseps( const Pilot *p, double *pdps, double *peps )
4378{
4379 double shots, dps = 0., eps = 0.;
4380 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
4381 const Damage *dmg;
4382 double mod_energy, mod_damage, mod_shots;
4383 const Outfit *o = p->outfits[i]->outfit;
4384 if ( o == NULL )
4385 continue;
4386 switch ( o->type ) {
4387 case OUTFIT_TYPE_BOLT:
4388 mod_energy = p->stats.fwd_energy;
4389 mod_damage = p->stats.fwd_damage;
4390 mod_shots = 1. / p->stats.fwd_firerate * (double)o->u.blt.shots;
4391 break;
4392 case OUTFIT_TYPE_TURRET_BOLT:
4393 mod_energy = p->stats.tur_energy;
4394 mod_damage = p->stats.tur_damage;
4395 mod_shots = 1. / p->stats.tur_firerate * (double)o->u.blt.shots;
4396 break;
4397 case OUTFIT_TYPE_LAUNCHER:
4398 case OUTFIT_TYPE_TURRET_LAUNCHER:
4399 mod_energy = 1.;
4400 mod_damage = p->stats.launch_damage;
4401 mod_shots = 1. / p->stats.launch_rate * (double)o->u.lau.shots;
4402 break;
4403 case OUTFIT_TYPE_BEAM:
4404 case OUTFIT_TYPE_TURRET_BEAM:
4405 /* Special case due to continuous fire. */
4406 if ( o->type == OUTFIT_TYPE_BEAM ) {
4407 mod_energy = p->stats.fwd_energy;
4408 mod_damage = p->stats.fwd_damage;
4409 mod_shots = 1. / p->stats.fwd_firerate;
4410 } else {
4411 mod_energy = p->stats.tur_energy;
4412 mod_damage = p->stats.tur_damage;
4413 mod_shots = 1. / p->stats.tur_firerate;
4414 }
4415 shots = o->u.bem.duration;
4416 mod_shots = shots / ( shots + mod_shots * outfit_delay( o ) );
4417 dps += mod_shots * mod_damage * outfit_damage( o )->damage;
4418 eps += mod_shots * mod_energy * outfit_energy( o );
4419 continue;
4420
4421 default:
4422 continue;
4423 }
4424 shots = 1. / ( mod_shots * outfit_delay( o ) );
4425
4426 dmg = outfit_damage( o );
4427 dps += shots * mod_damage * dmg->damage;
4428 eps += shots * mod_energy * MAX( outfit_energy( o ), 0. );
4429 }
4430 if ( pdps != NULL )
4431 *pdps = dps;
4432 if ( peps != NULL )
4433 *peps = eps;
4434}
4435
4445double pilot_reldps( const Pilot *cur_pilot, const Pilot *p )
4446{
4447 double DPSaccum_target, DPSaccum_pilot;
4448
4449 pilot_dpseps( p, &DPSaccum_target, NULL );
4450 pilot_dpseps( cur_pilot, &DPSaccum_pilot, NULL );
4451
4452 if ( ( DPSaccum_target > DOUBLE_TOL ) && ( DPSaccum_pilot > DOUBLE_TOL ) )
4453 return DPSaccum_pilot / ( DPSaccum_target + DPSaccum_pilot );
4454 else if ( DPSaccum_pilot > 0. )
4455 return 1.;
4456 return 0.;
4457}
4458
4468double pilot_relhp( const Pilot *cur_pilot, const Pilot *p )
4469{
4470 double c_hp = cur_pilot->armour_max + cur_pilot->shield_max;
4471 double p_hp = p->armour_max + p->shield_max;
4472 return c_hp / ( p_hp + c_hp );
4473}
4474
4482credits_t pilot_worth( const Pilot *p, int count_unique )
4483{
4484 /* Ship price is base price + outfit prices. */
4485 credits_t price = ship_basePrice( p->ship );
4486 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
4487 if ( p->outfits[i]->outfit == NULL )
4488 continue;
4489 /* Don't count unique outfits. */
4490 if ( !count_unique &&
4491 outfit_isProp( p->outfits[i]->outfit, OUTFIT_PROP_UNIQUE ) )
4492 continue;
4493 price += p->outfits[i]->outfit->price;
4494 }
4495
4496 return price;
4497}
4498
4508{
4509 mat4 H = { .m = { { -1., 0., 0., 0. },
4510 { 0., 1., 0., 0. },
4511 { 0., 0., 1., 0. },
4512 { 0., 0., 0., 1. } } };
4513 if ( fabs( p->tilt ) > DOUBLE_TOL ) {
4514 mat4_rotate( &H, M_PI_2, 0.0, 1.0, 0.0 );
4515 mat4_rotate( &H, p->tilt, 1.0, 0.0, 0.0 );
4516 mat4_rotate( &H, -p->solid.dir, 0.0, 1.0, 0.0 );
4517 } else
4518 mat4_rotate( &H, -p->solid.dir + M_PI_2, 0.0, 1.0, 0.0 );
4519 mat4_rotate( &H, -M_PI / 4.0, 1., 0., 0. );
4520 return H;
4521}
4522
4531void pilot_msg( const Pilot *p, const Pilot *receiver, const char *type,
4532 unsigned int idx )
4533{
4534 if ( idx != 0 )
4535 lua_pushvalue( naevL, idx ); /* data */
4536 else
4537 lua_pushnil( naevL ); /* data */
4538
4539 lua_newtable( naevL ); /* data, msg */
4540
4541 if ( p != NULL ) {
4542 lua_pushpilot( naevL, p->id ); /* data, msg, sender */
4543 lua_rawseti( naevL, -2, 1 ); /* data, msg */
4544 }
4545
4546 lua_pushstring( naevL, type ); /* data, msg, type */
4547 lua_rawseti( naevL, -2, 2 ); /* data, msg */
4548
4549 lua_pushvalue( naevL, -2 ); /* data, msg, data */
4550 lua_rawseti( naevL, -2, 3 ); /* data, msg */
4551
4552 lua_rawgeti( naevL, LUA_REGISTRYINDEX,
4553 receiver->messages ); /* data, msg, messages */
4554 lua_pushvalue( naevL, -2 ); /* data, msg, messages, msg */
4555 lua_rawseti( naevL, -2,
4556 lua_objlen( naevL, -2 ) + 1 ); /* data, msg, messages */
4557 lua_pop( naevL, 3 ); /* */
4558}
4559
4567int pilot_hasIllegal( const Pilot *p, int faction )
4568{
4569 /* Check commodities. */
4570 for ( int i = 0; i < array_size( p->commodities ); i++ ) {
4571 const Commodity *c = p->commodities[i].commodity;
4572 if ( commodity_checkIllegal( c, faction ) )
4573 return 1;
4574 }
4575 /* Check outfits. */
4576 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
4577 const Outfit *o = p->outfits[i]->outfit;
4578 if ( ( o != NULL ) && outfit_checkIllegal( o, faction ) )
4579 return 1;
4580 }
4581 /* Nothing to see here sir. */
4582 return 0;
4583}
4584
4592void pilot_quadtreeParams( int max_elem, int depth )
4593{
4594 qt_max_elem = max_elem;
4595 qt_depth = depth;
4596}
void ai_cleartasks(Pilot *p)
Clears the pilot's tasks.
Definition ai.c:548
void ai_think(Pilot *pilot, double dt, int dotask)
Heart of the AI, brains of the pilot.
Definition ai.c:812
void ai_getDistress(const Pilot *p, const Pilot *distressed, const Pilot *attacker)
Sends a distress signal to a pilot.
Definition ai.c:1094
void ai_destroy(Pilot *p)
Destroys the ai part of the pilot.
Definition ai.c:561
Pilot * cur_pilot
Definition ai.c:340
void ai_init(Pilot *p)
Initializes the AI.
Definition ai.c:929
int ai_pinit(Pilot *p, const char *ai)
Initializes the pilot in the ai.
Definition ai.c:494
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
#define array_end(array)
Returns a pointer to the end of the reserved memory space.
Definition array.h:214
#define array_create_size(basic_type, capacity)
Creates a new dynamic array of ‘basic_type’ with an initial capacity.
Definition array.h:102
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition array.h:148
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:179
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:122
#define array_shrink(ptr_array)
Shrinks memory to fit only ‘size’ elements.
Definition array.h:160
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:134
#define array_begin(array)
Returns a pointer to the beginning of the reserved memory space.
Definition array.h:206
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
void pilot_boardComplete(Pilot *p)
Finishes the boarding.
Definition board.c:274
void cam_getPos(double *x, double *y)
Gets the camera position.
Definition camera.c:122
double cam_getZoom(void)
Gets the camera zoom.
Definition camera.c:101
int commodity_checkIllegal(const Commodity *com, int faction)
Checks to see if a commodity is illegal to a faction.
Definition commodity.c:422
void dtype_calcDamage(double *dshield, double *darmour, double absorb, double *knockback, const Damage *dmg, const ShipStats *s)
Gives the real shield damage, armour damage and knockback modifier.
Definition damagetype.c:275
int dtype_get(const char *name)
Gets the id of a dtype based on name.
Definition damagetype.c:157
void debris_add(double mass, double r, double px, double py, double vx, double vy)
Creates a cloud of debris.
Definition debris.c:80
void effect_cleanup(Effect *efxlist)
Cleans up an effect list freeing it.
Definition effect.c:561
int effect_update(Effect **efxlist, double dt)
Updates an effect list.
Definition effect.c:282
void escort_rmList(Pilot *p, unsigned int id)
Remove from escorts list.
Definition escort.c:82
void escort_freeList(Pilot *p)
Remove all escorts from a pilot.
Definition escort.c:57
void expl_explode(double x, double y, double vx, double vy, double radius, const Damage *dmg, const Pilot *parent, int mode)
Does explosion in a radius (damage and graphics).
Definition explosion.c:42
const char * faction_default_ai(int f)
Gets the name of the default AI profile for the faction's pilots.
Definition faction.c:424
const int * faction_getEnemies(int f)
Gets the list of enemies of a faction.
Definition faction.c:509
int areEnemies(int a, int b)
Checks whether two factions are enemies.
Definition faction.c:1450
double faction_hit(int f, const StarSystem *sys, double mod, const char *source, int single)
Handles a faction hit against a faction and how to apply it.
Definition faction.c:871
void gl_printRaw(const glFont *ft_font, double x, double y, const glColour *c, double outlineR, const char *text)
Prints text on screen.
Definition font.c:646
int gl_printWidthRaw(const glFont *ft_font, const char *text)
Gets the width that it would take to print some text.
Definition font.c:984
glFont gl_defFont
Definition font.c:158
glTexture * gui_hailIcon(void)
Gets the hail icon texture.
Definition gui.c:2168
int gui_onScreenPilot(double *rx, double *ry, const Pilot *pilot)
Takes a pilot and returns whether it's on screen, plus its relative position.
Definition gui.c:651
void gui_cooldownEnd(void)
Notifies GUI scripts that the player broke out of cooldown.
Definition gui.c:898
void player_message(const char *fmt,...)
Adds a mesg to the queue to be displayed on screen.
Definition gui.c:353
void lvar_freeArray(lvar *arr)
Frees a variable array.
Definition lvar.c:145
mat4 mat4_identity(void)
Creates an identity matrix.
Definition mat4.c:335
void mat4_rotate(mat4 *m, double angle, double x, double y, double z)
Multiplies the given matrix by a rotation. (Follows the right-hand rule.)
Definition mat4.c:216
void mat4_mul_vec(vec3 *out, const mat4 *m, const vec3 *v)
Multiplies a matrix with a vector (out = m * v);.
Definition mat4.c:67
void mat4_scale(mat4 *m, double x, double y, double z)
Scales a homogeneous transformation matrix.
Definition mat4.c:113
mat4 mat4_ortho(double left, double right, double bottom, double top, double nearVal, double farVal)
Creates an orthographic projection matrix.
Definition mat4.c:347
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:39
#define CLAMP(a, b, x)
Definition naev.h:41
#define ABS(x)
Definition naev.h:32
#define pow2(x)
Definition naev.h:53
#define FABS(x)
Definition naev.h:34
#define MAX(x, y)
Definition naev.h:37
lua_State * naevL
Definition nlua.c:54
const Outfit ** lua_pushoutfit(lua_State *L, const Outfit *outfit)
Pushes a outfit on the stack.
LuaPilot * lua_pushpilot(lua_State *L, LuaPilot pilot)
Pushes a pilot on the stack.
Definition nlua_pilot.c:576
vec2 * lua_pushvector(lua_State *L, vec2 vec)
Pushes a vector on the stack.
Definition nlua_vec2.c:145
ntime_t ntime_create(int scu, int stp, int stu)
Creates a time structure.
Definition ntime.c:99
glInfo gl_screen
Definition opengl.c:47
void gl_renderShader(double x, double y, double w, double h, double r, const SimpleShader *shd, const glColour *c, int center)
Renders a simple shader.
void gl_renderRect(double x, double y, double w, double h, const glColour *c)
Renders a rectangle.
void gl_gameToScreenCoords(double *nx, double *ny, double bx, double by)
Converts in-game coordinates to screen coordinates.
void gl_unclipRect(void)
Clears the 2d clipping planes.
void gl_renderSprite(const glTexture *sprite, double bx, double by, int sx, int sy, const glColour *c)
Blits a sprite, position is relative to the player.
void gl_renderCross(double x, double y, double r, const glColour *c)
Renders a cross at a given position.
void gl_renderSpriteInterpolateScale(const glTexture *sa, const glTexture *sb, double inter, double bx, double by, double scalew, double scaleh, int sx, int sy, const glColour *c)
Blits a sprite interpolating, position is relative to the player.
void gl_clipRect(int x, int y, int w, int h)
Sets up 2d clipping planes around a rectangle.
void gl_getSpriteFromDir(int *x, int *y, int sx, int sy, double dir)
Sets x and y to be the appropriate sprite for glTexture using dir.
Definition opengl_tex.c:977
gl_vbo * gl_vboCreateDynamic(GLsizei size, const void *data)
Creates a dynamic vbo.
Definition opengl_vbo.c:160
void gl_vboActivateAttribOffset(gl_vbo *vbo, GLuint index, GLuint offset, GLint size, GLenum type, GLsizei stride)
Activates a VBO's offset.
Definition opengl_vbo.c:224
void gl_vboData(gl_vbo *vbo, GLsizei size, const void *data)
Reloads new data or grows the size of the vbo.
Definition opengl_vbo.c:96
int outfit_isLauncher(const Outfit *o)
Checks if outfit is a weapon launcher.
Definition outfit.c:649
int outfit_checkIllegal(const Outfit *o, int fct)
Checks illegality of an outfit to a faction.
Definition outfit.c:3309
int outfit_isFighterBay(const Outfit *o)
Checks if outfit is a fighter bay.
Definition outfit.c:701
const Damage * outfit_damage(const Outfit *o)
Gets the outfit's damage.
Definition outfit.c:808
double outfit_energy(const Outfit *o)
Gets the outfit's energy usage.
Definition outfit.c:863
double outfit_delay(const Outfit *o)
Gets the outfit's delay.
Definition outfit.c:834
char pilot_getFactionColourChar(const Pilot *p)
Gets the faction colour char, works like faction_reputationColourChar but for a pilot.
Definition pilot.c:1121
void pilot_free(Pilot *p)
Frees and cleans up a pilot.
Definition pilot.c:3902
void pilot_stackRemove(Pilot *p)
Tries to remove a pilot from the stack.
Definition pilot.c:3992
static void pilot_hyperspace(Pilot *pilot, double dt)
Handles pilot's hyperspace states.
Definition pilot.c:3011
unsigned int pilot_getNearestEnemy_size(const Pilot *p, double target_mass_LB, double target_mass_UB)
Gets the nearest enemy to the pilot closest to the pilot whose mass is between LB and UB.
Definition pilot.c:361
static int pilot_trail_generated(Pilot *p, int generator)
Return true if the given trail_emitters index has a corresponding generated trail.
Definition pilot.c:2953
void pilot_choosePoint(vec2 *vp, Spob **spob, JumpPoint **jump, int lf, int ignore_rules, int guerilla)
Finds a spawn point for a pilot.
Definition pilot.c:3784
int pilot_isHostile(const Pilot *p)
Checks to see if pilot is hostile to the player.
Definition pilot.c:699
void pilot_updateDisable(Pilot *p, unsigned int shooter)
Handles pilot disabling. Set or unset the disable status depending on health and stress values.
Definition pilot.c:1605
void pilot_cooldown(Pilot *p, int dochecks)
Begins active cooldown, reducing hull and outfit temperatures.
Definition pilot.c:918
static IntList pilot_qtquery
Definition pilot.c:55
int pilot_brakeCheckReverseThrusters(const Pilot *p)
See if the pilot wants to use their reverse thrusters to brake.
Definition pilot.c:838
void pilot_renderFramebuffer(Pilot *p, GLuint fbo, double fw, double fh, const Lighting *L)
Renders a pilot to a framebuffer.
Definition pilot.c:1849
void pilot_rmHostile(Pilot *p)
Unmarks a pilot as hostile to player.
Definition pilot.c:1277
unsigned int pilot_addStack(Pilot *p)
Adds a pilot to the stack.
Definition pilot.c:3690
void pilots_updatePurge(void)
Purges pilots set for deletion.
Definition pilot.c:4176
void pilot_quadtreeParams(int max_elem, int depth)
Sets the quad tree parameters. Can have significant impact on performance.
Definition pilot.c:4592
double pilot_relhp(const Pilot *cur_pilot, const Pilot *p)
Gets the relative hp(combined shields and armour) between the current pilot and the specified target.
Definition pilot.c:4468
int pilot_validEnemy(const Pilot *p, const Pilot *target)
Checks to see if a pilot is a valid enemy for another pilot.
Definition pilot.c:291
double pilot_relsize(const Pilot *cur_pilot, const Pilot *p)
Gets the relative size(shipmass) between the current pilot and the specified target.
Definition pilot.c:4364
int pilot_areEnemies(const Pilot *p, const Pilot *target)
Like areEnemies but for pilots.
Definition pilot.c:763
void pilots_clear(void)
Clears all the pilots except the player and clear-exempt pilots.
Definition pilot.c:4137
static const double pilot_commFade
Definition pilot.c:65
unsigned int pilot_getNearestPilot(const Pilot *p)
Get the nearest pilot to a pilot.
Definition pilot.c:437
void pilots_clean(int persist)
Cleans up the pilot stack - leaves the player.
Definition pilot.c:4052
Pilot * pilot_getTarget(Pilot *p)
Gets the target of a pilot using a fancy caching system.
Definition pilot.c:655
Pilot * pilot_createEmpty(const Ship *ship, const char *name, int faction, PilotFlags flags)
Creates a pilot without adding it to the stack.
Definition pilot.c:3627
double pilot_face(Pilot *p, double dir, double dt)
Tries to turn the pilot to face dir.
Definition pilot.c:827
mat4 pilot_local_transform(const Pilot *p)
Gets the local transformation matrix of a pilot.
Definition pilot.c:4507
double pilot_getNearestPos(const Pilot *p, unsigned int *tp, double x, double y, int disabled)
Get the nearest pilot to a pilot from a certain position.
Definition pilot.c:564
void pilot_clearTimers(Pilot *pilot)
Clears the pilot's timers.
Definition pilot.c:4337
double pilot_reldps(const Pilot *cur_pilot, const Pilot *p)
Gets the relative damage output(total DPS) between the current pilot and the specified target.
Definition pilot.c:4445
void pilot_msg(const Pilot *p, const Pilot *receiver, const char *type, unsigned int idx)
Sends a message.
Definition pilot.c:4531
void pilot_dead(Pilot *p, unsigned int killer)
Pilot is dead, now will slowly explode.
Definition pilot.c:1687
static int pilot_cmp(const void *ptr1, const void *ptr2)
Compare id (for use with bsearch)
Definition pilot.c:101
Pilot * pilot_setPlayer(Pilot *after)
Replaces the player's pilot with an alternate ship with the same ID.
Definition pilot.c:3735
static void pilot_refuel(Pilot *p, double dt)
Has the pilot refuel its target.
Definition pilot.c:3210
void pilot_renderOverlay(Pilot *p)
Renders the pilot overlay.
Definition pilot.c:2235
void pilot_setAccel(Pilot *p, double accel)
Sets the pilot's accel.
Definition pilot.c:680
static void pilot_erase(Pilot *p)
Destroys pilot from stack.
Definition pilot.c:3982
void pilot_cooldownEnd(Pilot *p, const char *reason)
Terminates active cooldown.
Definition pilot.c:986
static void pilot_init(Pilot *dest, const Ship *ship, const char *name, int faction, const double dir, const vec2 *pos, const vec2 *vel, const PilotFlags flags, unsigned int dockpilot, int dockslot)
Initialize pilot.
Definition pilot.c:3340
static const double pilot_commTimeout
Definition pilot.c:63
credits_t pilot_modCredits(Pilot *p, credits_t amount)
Modifies the amount of credits the pilot has.
Definition pilot.c:3300
unsigned int pilot_getNearestEnemy(const Pilot *p)
Gets the nearest enemy to the pilot.
Definition pilot.c:331
void pilot_setFriendly(Pilot *p)
Marks pilot as friendly to player.
Definition pilot.c:1295
void pilot_hyperspaceAbort(Pilot *p)
Stops the pilot from hyperspacing.
Definition pilot.c:3140
void pilot_setTurn(Pilot *p, double turn)
Sets the pilot's turn.
Definition pilot.c:688
int pilot_validTarget(const Pilot *p, const Pilot *target)
Checks to see if a pilot is a valid target for another pilot.
Definition pilot.c:235
void pilot_explode(double x, double y, double radius, const Damage *dmg, const Pilot *parent)
Makes the pilot explosion.
Definition pilot.c:1767
void pilot_reset(Pilot *pilot)
Resets a pilot.
Definition pilot.c:3491
int pilot_isFriendly(const Pilot *p)
Checks to see if pilot is friendly to the player.
Definition pilot.c:728
int pilot_refuelStart(Pilot *p)
Attempts to start refueling the pilot's target.
Definition pilot.c:3170
void pilots_renderOverlay(void)
Renders all the pilots overlays.
Definition pilot.c:4314
int pilot_isNeutral(const Pilot *p)
Checks to see if pilot is neutral to the player.
Definition pilot.c:715
PilotOutfitSlot * pilot_getDockSlot(Pilot *p)
Gets the dock slot of the pilot.
Definition pilot.c:793
void pilots_init(void)
Initializes pilot stuff.
Definition pilot.c:4010
int pilot_hasIllegal(const Pilot *p, int faction)
Checks to see if the pilot has illegal stuf to a faction.
Definition pilot.c:4567
void pilot_setCommMsg(Pilot *p, const char *s)
Sets the overhead communication message of the pilot.
Definition pilot.c:1137
Pilot * pilot_get(unsigned int id)
Pulls a pilot out of the pilot_stack based on ID.
Definition pilot.c:640
ntime_t pilot_hyperspaceDelay(const Pilot *p)
Calculates the hyperspace delay for a pilot.
Definition pilot.c:3243
void pilot_broadcast(Pilot *p, const char *msg, int ignore_int)
Has the pilot broadcast a message.
Definition pilot.c:1152
double pilot_minbrakedist(const Pilot *p, double dt, double *flytime)
Gets the minimum braking distance for the pilot.
Definition pilot.c:862
void pilot_setHostile(Pilot *p)
Marks pilot as hostile to player.
Definition pilot.c:1105
double pilot_getNearestPosPilot(const Pilot *p, Pilot **tp, double x, double y, int disabled)
Get the nearest pilot to a pilot from a certain position.
Definition pilot.c:518
void pilot_dpseps(const Pilot *p, double *pdps, double *peps)
Calculates the dps and eps of a pilot.
Definition pilot.c:4377
const glColour * pilot_getColour(const Pilot *p)
Gets a pilot's colour.
Definition pilot.c:1328
static Pilot ** pilot_stack
Definition pilot.c:51
void pilot_distress(Pilot *p, Pilot *attacker, const char *msg)
Has the pilot broadcast a distress signal.
Definition pilot.c:1180
void pilot_render(Pilot *p)
Renders the pilot.
Definition pilot.c:1958
#define PILOT_SIZE_MIN
Definition pilot.c:44
Pilot *const * pilot_getAll(void)
Gets the pilot stack.
Definition pilot.c:93
int pilot_canTarget(const Pilot *p)
Same as pilot_validTarget but without the range check.
Definition pilot.c:270
credits_t pilot_worth(const Pilot *p, int count_unique)
Gets the price or worth of a pilot in credits.
Definition pilot.c:4482
unsigned int pilot_getNearestEnemy_heuristic(const Pilot *p, double mass_factor, double health_factor, double damage_factor, double range_factor)
Gets the nearest enemy to the pilot closest to the pilot whose mass is between LB and UB.
Definition pilot.c:400
static unsigned int pilot_id
Definition pilot.c:47
double pilot_aimAngle(Pilot *p, const vec2 *pos, const vec2 *vel)
Returns the angle for a pilot to aim at another pilot.
Definition pilot.c:1025
void pilots_newSystem(void)
Updates pilot state which depends on the system (sensor range, nebula trails...)
Definition pilot.c:4116
unsigned int pilot_getNextID(unsigned int id, int mode)
Gets the next pilot based on id.
Definition pilot.c:135
void pilot_rmFriendly(Pilot *p)
Unmarks a pilot as friendly to player.
Definition pilot.c:1306
void pilots_render(void)
Renders all the pilots.
Definition pilot.c:4293
int pilot_areAllies(const Pilot *p, const Pilot *target)
Like areAllies but for pilots.
Definition pilot.c:741
int pilot_validTargetRange(const Pilot *p, const Pilot *target, int *inrange)
Checks to see if a pilot is a valid target for another pilot while storing the result of pilot_inRang...
Definition pilot.c:250
double pilot_getNearestAng(const Pilot *p, unsigned int *tp, double ang, int disabled)
Get the pilot closest to an angle extending from another pilot.
Definition pilot.c:585
void pilot_untargetAsteroid(int anchor, int asteroid)
Loops over pilot stack to remove an asteroid as target.
Definition pilot.c:3255
int pilot_getJumps(const Pilot *p)
Gets the amount of jumps the pilot has left.
Definition pilot.c:1317
void pilot_delete(Pilot *p)
Deletes a pilot.
Definition pilot.c:2965
void pilots_update(double dt)
Updates all the pilots.
Definition pilot.c:4216
void pilots_cleanAll(void)
Even cleans up the player.
Definition pilot.c:4148
void pilot_clearTrails(Pilot *p)
Resets the trails for a pilot.
Definition pilot.c:3722
unsigned int pilot_getBoss(const Pilot *p)
Get the strongest ally in a given range.
Definition pilot.c:450
static void pilot_init_trails(Pilot *p)
Initialize pilot's trails according to the ship type and current system characteristics.
Definition pilot.c:3535
void pilots_free(void)
Frees the pilot stack.
Definition pilot.c:4019
static Quadtree pilot_quadtree
Definition pilot.c:54
double pilot_hit(Pilot *p, const Solid *w, const Pilot *pshooter, const Damage *dmg, const Outfit *outfit, int lua_mem, int reset)
Damages the pilot.
Definition pilot.c:1383
static int pilot_getStackPos(unsigned int id)
Gets the pilot's position in the stack.
Definition pilot.c:115
void pilot_update(Pilot *pilot, double dt)
Updates the pilot.
Definition pilot.c:2328
Pilot * pilot_create(const Ship *ship, const char *name, int faction, const char *ai, const double dir, const vec2 *pos, const vec2 *vel, const PilotFlags flags, unsigned int dockpilot, int dockslot)
Creates a new pilot.
Definition pilot.c:3557
static void pilot_renderFramebufferBase(Pilot *p, GLuint fbo, double fw, double fh, const Lighting *L)
Renders a pilot to a framebuffer without effects.
Definition pilot.c:1825
unsigned int pilot_getPrevID(unsigned int id, int mode)
Gets the previous pilot based on ID.
Definition pilot.c:183
int pilot_numOutfit(const Pilot *p, const Outfit *o)
Checks to see how many of an outfit a pilot has.
Definition pilot.c:3269
static void pilot_updateSolid(Pilot *p, double dt)
Updates the pilot solid.
Definition pilot.c:3196
void pilot_sample_trails(Pilot *p, int none)
Updates the given pilot's trail emissions.
Definition pilot.c:2846
int pilot_hasCredits(const Pilot *p, credits_t amount)
Checks to see if the pilot has at least a certain amount of credits.
Definition pilot.c:3286
int pilot_brake(Pilot *p, double dt)
Causes the pilot to turn around and brake.
Definition pilot.c:886
void pilot_setTarget(Pilot *p, unsigned int id)
Sets the target of the pilot.
Definition pilot.c:1352
unsigned int pilot_clone(const Pilot *ref)
Clones an existing pilot.
Definition pilot.c:3646
int pilot_cargoRmAll(Pilot *pilot, int cleanup)
Gets rid of all cargo from pilot. Can remove mission cargo.
int pilot_cargoJet(Pilot *p, const Commodity *cargo, int quantity, int simulate)
Tries to get rid of quantity cargo from pilot, jetting it into space.
void pilot_ewUpdateStealth(Pilot *p, double dt)
Updates the stealth mode and checks to see if it is getting broken.
Definition pilot_ew.c:497
int pilot_inRangePilot(const Pilot *p, const Pilot *target, double *dist2)
Check to see if a pilot is in sensor range of another.
Definition pilot_ew.c:256
void pilot_ewUpdateDynamic(Pilot *p, double dt)
Updates the pilot's dynamic electronic warfare properties.
Definition pilot_ew.c:113
int pilot_inRangeSpob(const Pilot *p, int target)
Check to see if a spob is in sensor range of the pilot.
Definition pilot_ew.c:293
void pilot_updateSensorRange(void)
Updates the system's base sensor range.
Definition pilot_ew.c:208
void pilot_ewScanStart(Pilot *p)
Initializes the scan timer for a pilot.
Definition pilot_ew.c:54
double pilot_sensorRange(void)
Returns the default sensor range for the current system.
Definition pilot_ew.c:218
double pilot_heatEfficiencyMod(double T, double Tb, double Tc)
Returns a 0:1 modifier representing efficiency (1. being normal).
Definition pilot_heat.c:237
void pilot_heatUpdateCooldown(Pilot *p)
Overrides the usual heat model during active cooldown.
Definition pilot_heat.c:247
double pilot_heatFireRateMod(double T)
Returns a 0:1 modifier representing fire rate (1. being normal).
Definition pilot_heat.c:289
void pilot_heatReset(Pilot *p)
Resets a pilot's heat.
Definition pilot_heat.c:103
void pilot_heatUpdateShip(Pilot *p, double Q_cond, double dt)
Heats the pilot's ship.
Definition pilot_heat.c:215
double pilot_heatUpdateSlot(const Pilot *p, PilotOutfitSlot *o, double dt)
Heats the pilot's slot.
Definition pilot_heat.c:182
void pilot_heatAddSlotTime(const Pilot *p, PilotOutfitSlot *o, double dt)
Adds heat to an outfit slot over a period of time.
Definition pilot_heat.c:154
void pilots_clearGlobalHooks(void)
Removes all the pilot global hooks.
Definition pilot_hook.c:170
void pilot_clearHooks(Pilot *p)
Clears the pilots hooks.
Definition pilot_hook.c:212
void pilot_freeGlobalHooks(void)
Clears global pilot hooks.
Definition pilot_hook.c:227
int pilot_runHook(Pilot *p, int hook_type)
Tries to run a pilot hook if he has it.
Definition pilot_hook.c:104
void pilot_lockUpdateSlot(Pilot *p, PilotOutfitSlot *o, Pilot *t, Target *wt, double *a, double dt)
Updates the lockons on the pilot's launchers.
int pilot_addOutfitIntrinsicRaw(Pilot *pilot, const Outfit *outfit)
Adds an outfit as an intrinsic slot.
void pilot_outfitLCooldown(Pilot *pilot, int done, int success, double timer)
Handle cooldown hooks for outfits.
void pilot_outfitLOutfofenergy(Pilot *pilot)
Handles when the pilot runs out of energy.
void pilot_healLanded(Pilot *pilot)
Cures the pilot as if he was landed.
void pilot_calcStats(Pilot *pilot)
Recalculates the pilot's stats based on his outfits.
void pilot_fillAmmo(Pilot *pilot)
Fills pilot's ammo completely.
int pilot_addAmmo(Pilot *pilot, PilotOutfitSlot *s, int quantity)
Adds some ammo to the pilot stock.
void pilot_lockClear(Pilot *p)
Clears pilot's missile lockon timers.
void pilot_outfitLCleanup(Pilot *pilot)
Handle cleanup hooks for outfits.
int pilot_reportSpaceworthy(const Pilot *p, char *buf, int bufSize)
Pilot safety report - makes sure stats are safe.
void pilot_outfitLUpdate(Pilot *pilot, double dt)
Runs the pilot's Lua outfits update script.
int pilot_addOutfitRaw(Pilot *pilot, const Outfit *outfit, PilotOutfitSlot *s)
Adds an outfit to the pilot, ignoring CPU or other limits.
int pilot_addOutfitIntrinsic(Pilot *pilot, const Outfit *outfit)
Adds an outfit as an intrinsic slot.
void pilot_outfitLOnhit(Pilot *pilot, double armour, double shield, unsigned int attacker)
Runs the pilot's Lua outfits onhit script.
void pilot_outfitLInitAll(Pilot *pilot)
Runs the pilot's Lua outfits init script.
int pilot_shipLInit(Pilot *p)
Initializes the pilot ship Lua.
Definition pilot_ship.c:41
int pilot_shipLExplodeUpdate(Pilot *p, double dt)
Updates the pilot explosion Lua stuff.
Definition pilot_ship.c:168
int pilot_shipLUpdate(Pilot *p, double dt)
Updates the pilot Lua stuff.
Definition pilot_ship.c:105
int pilot_shipLCleanup(Pilot *p)
Cleans up the pilot ship Lua.
Definition pilot_ship.c:73
int pilot_shipLExplodeInit(Pilot *p)
Initializes the pilot explosion stuff.
Definition pilot_ship.c:141
void pilot_afterburnOver(Pilot *p)
Deactivates the afterburner.
void pilot_weapSetAIClear(Pilot *p)
Useful function for AI, clears activeness of all weapon sets.
Pilot * pilot_weaponTarget(Pilot *p, Target *wt)
Gets the weapon target of a pilot.
void pilot_weapSetUpdate(Pilot *p)
Updates the pilot's weapon sets.
int pilot_outfitOffAll(Pilot *p)
Disables all active outfits for a pilot.
double pilot_weapSetSpeed(Pilot *p, int id)
Gets the speed of the current pilot weapon set.
int pilot_outfitOff(Pilot *p, PilotOutfitSlot *o, int natural)
Disables a given active outfit.
void pilot_weapSetUpdateOutfitState(Pilot *p)
Updates the local state of all the pilot's outfits based on the weapon sets.
void pilot_weapSetFree(Pilot *p)
Frees a pilot's weapon sets.
void player_updateSpecific(Pilot *pplayer, const double dt)
Does a player specific update.
Definition player.c:1395
void player_dead(void)
Player got pwned.
Definition player.c:2684
void player_soundPlay(int sound, int once)
Plays a sound at the player.
Definition player.c:958
int snd_hypPowDown
Definition player.c:110
void player_update(Pilot *pplayer, const double dt)
Player update function.
Definition player.c:1379
void player_soundStop(void)
Stops playing player sounds.
Definition player.c:966
int snd_hypPowUpJump
Definition player.c:111
int snd_hypPowUp
Definition player.c:108
void player_think(Pilot *pplayer, const double dt)
Basically uses keyboard input instead of AI input. Used in pilot.c.
Definition player.c:1252
Player_t player
Definition player.c:77
void player_destroyed(void)
Player blew up in a fireball.
Definition player.c:2698
void player_autonavResetSpeed(void)
Resets the game speed.
static const double c[]
Definition rng.c:256
static const double d[]
Definition rng.c:263
credits_t ship_basePrice(const Ship *s)
Gets the ship's base price (no outfits).
Definition ship.c:295
void ship_renderFramebuffer(const Ship *s, GLuint fbo, double fw, double fh, double dir, double engine_glow, double tilt, double r, int sx, int sy, const glColour *c, const Lighting *L)
Renders a ship to a framebuffer.
Definition ship.c:1489
int ship_gfxLoad(Ship *s)
Loads the graphics for a ship if necessary.
Definition ship.c:628
void ss_free(ShipStatList *ll)
Frees a list of ship stats.
Definition shipstats.c:934
double sound_getLength(int sound)
Gets the length of the sound buffer.
Definition sound.c:780
int sound_playPos(int sound, double px, double py, double vx, double vy)
Plays a sound based on position.
Definition sound.c:832
int sound_get(const char *name)
Gets the buffer to sound of name.
Definition sound.c:761
void spob_distress(Spob *spb, const Pilot *p, const Pilot *attacker)
Spob is receiving distress from a pilot about an attacker.
Definition space.c:2081
void system_rmCurrentPresence(StarSystem *sys, int faction, double amount)
Removes active presence.
Definition space.c:4678
int space_canHyperspace(const Pilot *p)
Checks to make sure if pilot is far enough away to hyperspace.
Definition space.c:494
StarSystem * cur_system
Definition space.c:110
double system_getPresence(const StarSystem *sys, int faction)
Get the presence of a faction in a system.
Definition space.c:4537
int space_needsEffects(void)
returns whether or not we're simulating with effects.
Definition space.c:1609
int space_isSimulation(void)
returns whether we're just simulating.
Definition space.c:1601
void spfx_trail_sample(Trail_spfx *trail, double x, double y, double z, double dx, double dy, TrailMode mode, int force)
Makes a trail grow.
Definition spfx.c:811
void spfx_shake(double mod)
Increases the current rumble level.
Definition spfx.c:966
int spfx_get(const char *name)
Gets the id of an spfx based on name.
Definition spfx.c:355
void spfx_trail_remove(Trail_spfx *trail)
Removes a trail.
Definition spfx.c:857
void spfx_add(int effect, const double px, const double py, const double vx, const double vy, int layer)
Creates a new special effect.
Definition spfx.c:504
void spfx_damage(double mod)
Increases the current damage level.
Definition spfx.c:988
Trail_spfx * spfx_trail_create(const TrailSpec *spec)
Initalizes a trail.
Definition spfx.c:735
void spfx_trail_draw(const Trail_spfx *trail)
Draws a trail on screen.
Definition spfx.c:878
Represents a polygon used for collision detection.
Definition collision.h:13
float * x
Definition collision.h:14
float * y
Definition collision.h:15
Represents a commodity.
Definition commodity.h:57
Core damage that an outfit does.
Definition outfit.h:168
int type
Definition outfit.h:169
double disable
Definition outfit.h:174
double penetration
Definition outfit.h:171
double damage
Definition outfit.h:173
Pilot ship effect data.
Definition effect.h:16
glTexture * img
Definition effect.h:38
unsigned int flags
Definition effect.h:23
Pilot ship effect.
Definition effect.h:49
double r
Definition effect.h:55
const EffectData * data
Definition effect.h:50
double elapsed
Definition effect.h:56
double timer
Definition effect.h:52
The actual hook parameter.
Definition hook.h:40
LuaPilot lp
Definition hook.h:46
union HookParam::@114305201244257020071001257133270030317263020235 u
HookParamType type
Definition hook.h:41
LuaJump lj
Definition hook.h:53
int destid
Definition nlua_jump.h:16
int srcid
Definition nlua_jump.h:15
double duration
Definition outfit.h:218
double reload_time
Definition outfit.h:252
A ship outfit, depends radically on the type.
Definition outfit.h:372
OutfitLauncherData lau
Definition outfit.h:456
OutfitBeamData bem
Definition outfit.h:455
OutfitBoltData blt
Definition outfit.h:454
double overheat_max
Definition outfit.h:404
OutfitType type
Definition outfit.h:452
int lua_onimpact
Definition outfit.h:443
OutfitAfterburnerData afb
Definition outfit.h:458
OutfitFighterBayData bay
Definition outfit.h:459
union Outfit::@125206365173064275134261143013340144375262037177 u
nlua_env lua_env
Definition outfit.h:418
double overheat_min
Definition outfit.h:402
char * name
Definition outfit.h:373
const Commodity * commodity
Definition pilot.h:224
Stores an outfit the pilot has.
Definition pilot.h:145
PilotOutfitAmmo ammo
Definition pilot.h:174
double heat_C
Definition pilot.h:155
double stimer
Definition pilot.h:161
double rtimer
Definition pilot.h:163
double heat_start
Definition pilot.h:157
double heat_T
Definition pilot.h:154
PilotOutfitState state
Definition pilot.h:160
double timer
Definition pilot.h:162
ShipOutfitSlot * sslot
Definition pilot.h:151
const Outfit * outfit
Definition pilot.h:149
The representation of an in-game pilot.
Definition pilot.h:263
double tilt
Definition pilot.h:443
double engine_glow
Definition pilot.h:442
double dtimer
Definition pilot.h:432
double dtimer_accum
Definition pilot.h:433
double itimer
Definition pilot.h:428
ShipStats stats
Definition pilot.h:348
double accel
Definition pilot.h:291
double shield
Definition pilot.h:303
double otimer
Definition pilot.h:434
AI_Profile * ai
Definition pilot.h:408
unsigned int id
Definition pilot.h:264
PilotCommodity * commodities
Definition pilot.h:383
int aimLines
Definition pilot.h:379
PilotOutfitSlot * outfit_structure
Definition pilot.h:356
unsigned int parent
Definition pilot.h:390
int nav_hyperspace
Definition pilot.h:403
double energy_regen
Definition pilot.h:316
PilotOutfitSlot ** outfits
Definition pilot.h:354
PilotOutfitSlot * outfit_utility
Definition pilot.h:357
PilotOutfitSlot * outfit_intrinsic
Definition pilot.h:360
int messages
Definition pilot.h:444
double armour_max
Definition pilot.h:304
double speed
Definition pilot.h:293
double timer[MAX_AI_TIMERS]
Definition pilot.h:411
double htimer
Definition pilot.h:429
const Ship * ship
Definition pilot.h:274
double sbonus
Definition pilot.h:431
double fuel_max
Definition pilot.h:309
double energy
Definition pilot.h:314
int nav_anchor
Definition pilot.h:404
double stress
Definition pilot.h:302
int hail_pos
Definition pilot.h:436
double ptimer
Definition pilot.h:427
int faction
Definition pilot.h:269
int faction_spawn
Definition pilot.h:270
int autoweap
Definition pilot.h:377
double comm_msgTimer
Definition pilot.h:421
double stimer
Definition pilot.h:430
double energy_max
Definition pilot.h:315
double landing_delay
Definition pilot.h:425
unsigned int shoot_indicator
Definition pilot.h:413
Solid solid
Definition pilot.h:275
double fuel
Definition pilot.h:310
PilotOutfitSlot * afterburner
Definition pilot.h:372
char * name
Definition pilot.h:265
void * ptarget
Definition pilot.h:401
double tcontrol
Definition pilot.h:410
double r
Definition pilot.h:266
PilotFlags flags
Definition pilot.h:424
unsigned int dockpilot
Definition pilot.h:392
int lua_ship_mem
Definition pilot.h:417
double ctimer
Definition pilot.h:339
double shield_regen
Definition pilot.h:307
int cargo_free
Definition pilot.h:384
int nav_spob
Definition pilot.h:402
int dockslot
Definition pilot.h:396
int lua_mem
Definition pilot.h:409
double player_damage
Definition pilot.h:440
Effect * effects
Definition pilot.h:351
double turn
Definition pilot.h:297
char * comm_msg
Definition pilot.h:423
PilotOutfitSlot * outfit_weapon
Definition pilot.h:358
double armour
Definition pilot.h:301
double shield_max
Definition pilot.h:305
int nav_asteroid
Definition pilot.h:405
double armour_regen
Definition pilot.h:306
Player ship.
Definition player.h:72
Ship outfit slot.
Definition ship.h:71
int required
Definition ship.h:75
const Outfit * data
Definition ship.h:78
int locked
Definition ship.h:76
double time_speedup
Definition shipstats.h:357
double ammo_capacity
Definition shipstats.h:301
double damage
Definition shipstats.h:254
double disable
Definition shipstats.h:255
double fuel_regen
Definition shipstats.h:352
double launch_reload
Definition shipstats.h:304
double fbay_capacity
Definition shipstats.h:314
double stress_dissipation
Definition shipstats.h:286
double fbay_reload
Definition shipstats.h:316
Ship trail emitter.
Definition ship.h:88
const TrailSpec * trail_spec
Definition ship.h:91
unsigned int flags
Definition ship.h:90
Represents a space ship.
Definition ship.h:97
double dt_default
Definition ship.h:136
double cap_cargo
Definition ship.h:135
char * name
Definition ship.h:100
ShipOutfitSlot * outfit_utility
Definition ship.h:174
Outfit const ** outfit_intrinsic
Definition ship.h:176
ShipOutfitSlot * outfit_weapon
Definition ship.h:175
double size
Definition ship.h:149
double mass
Definition ship.h:131
ShipOutfitSlot * outfit_structure
Definition ship.h:172
int lua_explode_update
Definition ship.h:197
Represents a solid in the game.
Definition physics.h:44
double dir_vel
Definition physics.h:47
double speed_max
Definition physics.h:53
void(* update)(struct Solid_ *, double)
Definition physics.h:54
vec2 vel
Definition physics.h:48
double mass
Definition physics.h:45
vec2 pos
Definition physics.h:49
double accel
Definition physics.h:51
int faction
Definition space.h:79
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition space.h:102
SpobPresence presence
Definition space.h:121
Represents a weapon target.
Definition target.h:19
int nebula
Definition spfx.h:60
A trail generated by a ship or an ammo.
Definition spfx.h:93
Abstraction for rendering sprite sheets.
Definition opengl_tex.h:43
double sw
Definition opengl_tex.h:53
double sh
Definition opengl_tex.h:54
double sx
Definition opengl_tex.h:51
GLuint texture
Definition opengl_tex.h:59
double sy
Definition opengl_tex.h:52
Definition mat4.h:12
Represents a 2d vector.
Definition vec2.h:45
double y
Definition vec2.h:47
double x
Definition vec2.h:46
Definition vec3.h:6