naev 0.10.4
pilot.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include <limits.h>
11#include <math.h>
12#include <stdlib.h>
13
14#include "naev.h"
17#include "pilot.h"
18
19#include "ai.h"
20#include "array.h"
21#include "board.h"
22#include "camera.h"
23#include "damagetype.h"
24#include "debris.h"
25#include "debug.h"
26#include "escort.h"
27#include "explosion.h"
28#include "faction.h"
29#include "font.h"
30#include "gatherable.h"
31#include "gui.h"
32#include "hook.h"
33#include "land.h"
34#include "land_outfits.h"
35#include "land_shipyard.h"
36#include "log.h"
37#include "map.h"
38#include "music.h"
39#include "nlua_pilotoutfit.h"
40#include "nlua_vec2.h"
41#include "ndata.h"
42#include "nstring.h"
43#include "ntime.h"
44#include "nxml.h"
45#include "pause.h"
46#include "player.h"
47#include "player_autonav.h"
48#include "rng.h"
49#include "weapon.h"
50
51#define PILOT_SIZE_MIN 128
53/* ID Generators. */
54static unsigned int pilot_id = PLAYER_ID;
56/* stack of pilots */
57static Pilot** pilot_stack = NULL;
59/* misc */
60static const double pilot_commTimeout = 15.;
61static const double pilot_commFade = 5.;
63/*
64 * Prototypes
65 */
66/* Create. */
67static void pilot_init( Pilot* dest, const Ship* ship, const char* name, int faction,
68 const double dir, const vec2* pos, const vec2* vel,
69 const PilotFlags flags, unsigned int dockpilot, int dockslot );
70/* Update. */
71static void pilot_hyperspace( Pilot* pilot, double dt );
72static void pilot_refuel( Pilot *p, double dt );
73/* Clean up. */
74static void pilot_erase( Pilot *p );
75/* Misc. */
76static int pilot_getStackPos( unsigned int id );
77static void pilot_init_trails( Pilot* p );
78static int pilot_trail_generated( Pilot* p, int generator );
79
83Pilot*const* pilot_getAll (void)
84{
85 return pilot_stack;
86}
87
91static int pilot_cmp( const void *ptr1, const void *ptr2 )
92{
93 const Pilot *p1, *p2;
94 p1 = *((const Pilot**) ptr1);
95 p2 = *((const Pilot**) ptr2);
96 return p1->id - p2->id;
97}
98
105static int pilot_getStackPos( unsigned int id )
106{
107 const Pilot pid = { .id = id };
108 const Pilot *pidptr = &pid;
109 /* binary search */
110 Pilot **pp = bsearch(&pidptr, pilot_stack, array_size(pilot_stack), sizeof(Pilot*), pilot_cmp);
111 if (pp == NULL)
112 return -1;
113 else
114 return pp - pilot_stack;
115}
116
124unsigned int pilot_getNextID( unsigned int id, int mode )
125{
126 int m, p;
127
128 /* Player must exist. */
129 if (player.p == NULL)
130 return PLAYER_ID;
131
132 /* Get the pilot. */
133 m = pilot_getStackPos(id);
134
135 /* Unselect. */
136 if ((m == (array_size(pilot_stack)-1)) || (m == -1))
137 return PLAYER_ID;
138
139 /* Get first one in range. */
140 p = m+1;
141 if (mode == 0) {
142 while (p < array_size(pilot_stack)) {
143 if ((!pilot_isWithPlayer(pilot_stack[p]) ||
144 pilot_isDisabled(pilot_stack[p])) &&
146 return pilot_stack[p]->id;
147 p++;
148 }
149 }
150 /* Get first hostile in range. */
151 if (mode == 1) {
152 while (p < array_size(pilot_stack)) {
153 if (!pilot_isWithPlayer(pilot_stack[p]) &&
156 return pilot_stack[p]->id;
157 p++;
158 }
159 }
160
161 /* None found. */
162 return PLAYER_ID;
163}
164
172unsigned int pilot_getPrevID( unsigned int id, int mode )
173{
174 int m, p;
175
176 /* Player must exist. */
177 if (player.p == NULL)
178 return PLAYER_ID;
179
180 /* Get the pilot. */
181 m = pilot_getStackPos(id);
182
183 /* Check to see what position to try. */
184 if (m == -1)
185 return PLAYER_ID;
186 else if (m == 0)
187 p = array_size(pilot_stack)-1;
188 else
189 p = m-1;
190
191 /* Get first one in range. */
192 if (mode == 0) {
193 while (p >= 0) {
194 if ((!pilot_isWithPlayer(pilot_stack[p]) ||
195 (pilot_isDisabled(pilot_stack[p]))) &&
197 return pilot_stack[p]->id;
198 p--;
199 }
200 }
201 /* Get first hostile in range. */
202 else if (mode == 1) {
203 while (p >= 0) {
204 if (!pilot_isWithPlayer(pilot_stack[p]) &&
205 !pilot_isFlag( pilot_stack[p], PILOT_HIDE ) &&
208 return pilot_stack[p]->id;
209 p--;
210 }
211 }
212
213 /* None found. */
214 return PLAYER_ID;
215}
216
226int pilot_validTarget( const Pilot* p, const Pilot* target )
227{
228 /* Must be targetable. */
229 if (!pilot_canTarget( target ))
230 return 0;
231
232 /* Must be in range. */
233 if (!pilot_inRangePilot( p, target, NULL ))
234 return 0;
235
236 /* Pilot is a valid target. */
237 return 1;
238}
239
243int pilot_canTarget( const Pilot* p )
244{
245 /* Must not be dead. */
246 if (pilot_isFlag( p, PILOT_DELETE ) ||
247 pilot_isFlag( p, PILOT_DEAD ))
248 return 0;
249
250 /* Must not be hidden nor invisible. */
251 if (pilot_isFlag( p, PILOT_HIDE ) ||
252 pilot_isFlag( p, PILOT_INVISIBLE))
253 return 0;
254
255 /* Pilot is a valid target. */
256 return 1;
257}
258
266int pilot_validEnemy( const Pilot* p, const Pilot* target )
267{
268 return pilot_validEnemyDist( p, target, NULL );
269}
270
274int pilot_validEnemyDist( const Pilot* p, const Pilot* target, double *dist )
275{
276 /* Shouldn't be disabled. */
277 if (pilot_isDisabled(target))
278 return 0;
279
280 /* Shouldn't be invincible. */
281 if (pilot_isFlag( target, PILOT_INVINCIBLE ))
282 return 0;
283
284 /* Shouldn't be landing or taking off. */
285 if (pilot_isFlag( target, PILOT_LANDING) ||
286 pilot_isFlag( target, PILOT_TAKEOFF ) ||
287 pilot_isFlag( target, PILOT_NONTARGETABLE))
288 return 0;
289
290 /* Must be a valid target. */
291 if (!pilot_validTarget( p, target ))
292 return 0;
293
294 /* Should either be hostile by faction or by player. */
295 if (!pilot_areEnemies( p, target ))
296 return 0;
297
298 /* Must not be fuzzy. */
299 if (pilot_inRangePilot( p, target, dist ) != 1)
300 return 0;
301
302 /* They're ok. */
303 return 1;
304}
305
312unsigned int pilot_getNearestEnemy( const Pilot* p )
313{
314 unsigned int tp = 0;
315 double d = 0.;
316
317 for (int i=0; i<array_size(pilot_stack); i++) {
318 double td;
319
320 if (!pilot_validEnemy( p, pilot_stack[i] ))
321 continue;
322
323 /* Check distance. */
324 td = vec2_dist2(&pilot_stack[i]->solid->pos, &p->solid->pos);
325 if (!tp || (td < d)) {
326 d = td;
327 tp = pilot_stack[i]->id;
328 }
329 }
330 return tp;
331}
332
341unsigned int pilot_getNearestEnemy_size( const Pilot* p, double target_mass_LB, double target_mass_UB )
342{
343 unsigned int tp = 0;
344 double d = 0.;
345
346 for (int i=0; i<array_size(pilot_stack); i++) {
347 double td;
348
349 if (!pilot_validEnemy( p, pilot_stack[i] ))
350 continue;
351
352 if (pilot_stack[i]->solid->mass < target_mass_LB || pilot_stack[i]->solid->mass > target_mass_UB)
353 continue;
354
355 /* Check distance. */
356 td = vec2_dist2(&pilot_stack[i]->solid->pos, &p->solid->pos);
357 if (!tp || (td < d)) {
358 d = td;
359 tp = pilot_stack[i]->id;
360 }
361 }
362
363 return tp;
364}
365
377 double mass_factor, double health_factor,
378 double damage_factor, double range_factor )
379{
380 unsigned int tp = 0;
381 double current_heuristic_value = 10e3;
382
383 for (int i=0; i<array_size(pilot_stack); i++) {
384 double temp;
385 Pilot *target = pilot_stack[i];
386
387 if (!pilot_validEnemy( p, target ))
388 continue;
389
390 /* Check distance. */
391 temp = range_factor *
392 vec2_dist2( &target->solid->pos, &p->solid->pos )
393 + FABS( pilot_relsize( p, target ) - mass_factor )
394 + FABS( pilot_relhp( p, target ) - health_factor )
395 + FABS( pilot_reldps( p, target ) - damage_factor );
396
397 if ((tp == 0) || (temp < current_heuristic_value)) {
398 current_heuristic_value = temp;
399 tp = target->id;
400 }
401 }
402
403 return tp;
404}
405
412unsigned int pilot_getNearestPilot( const Pilot* p )
413{
414 unsigned int t;
415 pilot_getNearestPos( p, &t, p->solid->pos.x, p->solid->pos.y, 0 );
416 return t;
417}
418
425unsigned int pilot_getBoss( const Pilot* p )
426{
427 unsigned int t;
428 double relpower, ppower;
429 /* TODO : all the parameters should be adjustable with arguments */
430
431 relpower = 0;
432 t = 0;
433
434 /* Initialized to 0.25 which would mean equivalent power. */
435 ppower = 0.5*0.5;
436
437 for (int i=0; i<array_size(pilot_stack); i++) {
438 double dx, dy, td, curpower;
439
440 /* Must not be self. */
441 if (pilot_stack[i] == p)
442 continue;
443
444 /* Shouldn't be disabled. */
445 if (pilot_isDisabled(pilot_stack[i]))
446 continue;
447
448 /* Must be a valid target. */
449 if (!pilot_validTarget( p, pilot_stack[i] ))
450 continue;
451
452 /* Maximum distance in 2 seconds. */
453 dx = pilot_stack[i]->solid->pos.x + 2*pilot_stack[i]->solid->vel.x -
454 p->solid->pos.x - 2*p->solid->vel.x;
455 dy = pilot_stack[i]->solid->pos.y + 2*pilot_stack[i]->solid->vel.y -
456 p->solid->pos.y - 2*p->solid->vel.y;
457 td = sqrt( pow2(dx) + pow2(dy) );
458 if (td > 5e3)
459 continue;
460
461 /* Must have the same faction. */
462 if (pilot_stack[i]->faction != p->faction)
463 continue;
464
465 /* Must be slower. */
466 if (pilot_stack[i]->speed > p->speed)
467 continue;
468
469 /* Should not be weaker than the current pilot*/
470 curpower = pilot_reldps( pilot_stack[i], p ) * pilot_relhp( pilot_stack[i], p );
471 if (ppower >= curpower )
472 continue;
473
474 if (relpower < curpower ) {
475 relpower = curpower;
476 t = pilot_stack[i]->id;
477 }
478 }
479 return t;
480}
481
492double pilot_getNearestPos( const Pilot *p, unsigned int *tp, double x, double y, int disabled )
493{
494 double d = 0.;
495
496 *tp = PLAYER_ID;
497 for (int i=0; i<array_size(pilot_stack); i++) {
498 double td;
499
500 /* Must not be self. */
501 if (pilot_stack[i] == p)
502 continue;
503
504 /* Player doesn't select escorts (unless disabled is active). */
505 if (!disabled && pilot_isPlayer(p) &&
506 pilot_isWithPlayer(pilot_stack[i]))
507 continue;
508
509 /* Shouldn't be disabled. */
510 if (!disabled && pilot_isDisabled(pilot_stack[i]))
511 continue;
512
513 /* Must be a valid target. */
514 if (!pilot_validTarget( p, pilot_stack[i] ))
515 continue;
516
517 /* Minimum distance. */
518 td = pow2(x-pilot_stack[i]->solid->pos.x) + pow2(y-pilot_stack[i]->solid->pos.y);
519 if (((*tp==PLAYER_ID) || (td < d))) {
520 d = td;
521 *tp = pilot_stack[i]->id;
522 }
523 }
524 return d;
525}
526
536double pilot_getNearestAng( const Pilot *p, unsigned int *tp, double ang, int disabled )
537{
538 double a = ang + M_PI;
539
540 *tp = PLAYER_ID;
541 for (int i=0; i<array_size(pilot_stack); i++) {
542 double rx, ry, ta;
543
544 /* Must not be self. */
545 if (pilot_stack[i] == p)
546 continue;
547
548 /* Player doesn't select escorts (unless disabled is active). */
549 if (!disabled && pilot_isPlayer(p) &&
550 pilot_isWithPlayer(pilot_stack[i]))
551 continue;
552
553 /* Shouldn't be disabled. */
554 if (!disabled && pilot_isDisabled(pilot_stack[i]))
555 continue;
556
557 /* Must be a valid target. */
558 if (!pilot_validTarget( p, pilot_stack[i] ))
559 continue;
560
561 /* Must be in range. */
562 if (!pilot_inRangePilot( p, pilot_stack[i], NULL ))
563 continue;
564
565 /* Only allow selection if off-screen. */
566 if (gui_onScreenPilot( &rx, &ry, pilot_stack[i] ))
567 continue;
568
569 ta = atan2( p->solid->pos.y - pilot_stack[i]->solid->pos.y,
570 p->solid->pos.x - pilot_stack[i]->solid->pos.x );
571 if (ABS(angle_diff(ang, ta)) < ABS(angle_diff(ang, a))) {
572 a = ta;
573 *tp = pilot_stack[i]->id;
574 }
575 }
576 return a;
577}
578
589Pilot* pilot_get( unsigned int id )
590{
591 int m;
592
593 if (id==PLAYER_ID)
594 return player.p; /* special case player.p */
595
596 m = pilot_getStackPos(id);
597
598 if ((m==-1) || (pilot_isFlag(pilot_stack[m], PILOT_DELETE)))
599 return NULL;
600 else
601 return pilot_stack[m];
602}
603
608{
609 Pilot *t;
610
611 /* Case no target. */
612 if (p->target==p->id)
613 return NULL;
614
615 t = p->ptarget;
616 /* Return ptarget if exists and valid. */
617 if (t != NULL) {
618 if (pilot_isFlag( t, PILOT_DELETE )) {
619 p->ptarget = NULL;
620 t = NULL;
621 }
622 return t;
623 }
624
625 p->ptarget = pilot_get( p->target );
626 return p->ptarget;
627}
628
632void pilot_setThrust( Pilot *p, double thrust )
633{
634 p->solid->thrust = p->thrust * thrust;
635}
636
640void pilot_setTurn( Pilot *p, double turn )
641{
642 p->solid->dir_vel = p->turn * turn;
643}
644
651int pilot_isHostile( const Pilot *p )
652{
653 if (!pilot_isFriendly( p )
654 && !pilot_isFlag( p, PILOT_BRIBED )
655 && (pilot_isFlag( p, PILOT_HOSTILE ) ||
656 areEnemies( FACTION_PLAYER, p->faction )))
657 return 1;
658
659 return 0;
660}
661
668int pilot_isNeutral( const Pilot *p )
669{
670 if (!pilot_isHostile(p) && !pilot_isFriendly(p))
671 return 1;
672 return 0;
673}
674
681int pilot_isFriendly( const Pilot *p )
682{
683 if (pilot_isFlag( p, PILOT_FRIENDLY) ||
684 (areAllies( FACTION_PLAYER,p->faction) &&
685 !pilot_isFlag( p, PILOT_HOSTILE ) ) )
686 return 1;
687
688 return 0;
689}
690
694int pilot_areAllies( const Pilot *p, const Pilot *target )
695{
696 if (pilot_isWithPlayer(p)) {
697 if (pilot_isFriendly(target))
698 return 1;
699 else if (pilot_isFlag( target, PILOT_HOSTILE ))
700 return 0;
701 }
702 else if (pilot_isWithPlayer(target)) {
703 if (pilot_isFriendly(p))
704 return 1;
705 else if (pilot_isFlag( p, PILOT_HOSTILE ))
706 return 0;
707 }
708 else {
709 if (areAllies( p->faction, target->faction ))
710 return 1;
711 }
712 return 0;
713}
714
718int pilot_areEnemies( const Pilot *p, const Pilot *target )
719{
720 if (pilot_isWithPlayer(p)) {
721 if (pilot_isHostile(target))
722 return 1;
723 else if (pilot_isFlag( target, PILOT_FRIENDLY ))
724 return 0;
725 else if (pilot_isFlag(target, PILOT_BRIBED))
726 return 0;
727 }
728 if (pilot_isWithPlayer(target)) {
729 if (pilot_isHostile(p))
730 return 1;
731 else if (pilot_isFlag( p, PILOT_FRIENDLY ))
732 return 0;
733 else if (pilot_isFlag(p, PILOT_BRIBED))
734 return 0;
735 }
736 else {
737 if (areEnemies( p->faction, target->faction ))
738 return 1;
739 }
740 return 0;
741}
742
750{
751 Pilot* dockpilot;
752
753 if ((p->dockpilot != 0) && (p->dockslot != -1)) {
754 dockpilot = pilot_get(p->dockpilot);
755 if (dockpilot != NULL)
756 return dockpilot->outfits[p->dockslot];
757 }
758 p->dockpilot = 0;
759 p->dockslot = -1;
760 return NULL;
761}
762
773double pilot_face( Pilot* p, const double dir )
774{
775 double diff = angle_diff( p->solid->dir, dir );
776 double turn = CLAMP( -1., 1., -10.*diff );
777 pilot_setTurn( p, -turn );
778
779 return diff;
780}
781
789{
790 double dir, thrust, diff;
791 int isstopped = pilot_isStopped(p);
792
793 if (isstopped)
794 return 1;
795
796 /* Face backwards by default. */
797 dir = VANGLE(p->solid->vel) + M_PI;
798 thrust = 1.;
799
800 if (p->stats.misc_reverse_thrust) {
801 double btime, ftime;
802 /* Calculate the time to face backward and apply forward thrust. */
803 diff = angle_diff(p->solid->dir, VANGLE(p->solid->vel) + M_PI);
804 btime = ABS(diff) / p->turn + MIN( VMOD(p->solid->vel), p->speed ) /
805 (p->thrust / p->solid->mass);
806
807 /* Calculate the time to face forward and apply reverse thrust. */
808 diff = angle_diff(p->solid->dir, VANGLE(p->solid->vel));
809 ftime = ABS(diff) / p->turn + MIN( VMOD(p->solid->vel), p->speed ) /
810 (p->thrust / p->solid->mass * PILOT_REVERSE_THRUST);
811
812 if (btime > ftime) {
813 dir = VANGLE(p->solid->vel);
814 thrust = -PILOT_REVERSE_THRUST;
815 }
816 }
817
818 diff = pilot_face(p, dir);
819 if (ABS(diff) < MAX_DIR_ERR && !isstopped)
820 pilot_setThrust(p, thrust);
821 else {
822 pilot_setThrust(p, 0.);
823 if (isstopped)
824 return 1;
825 }
826
827 return 0;
828}
829
837double pilot_brakeDist( Pilot *p, vec2 *pos )
838{
839 double fdiff, bdiff, ftime, btime;
840 double vang, speed, dist;
841
842 if (pilot_isStopped(p)) {
843 if (pos != NULL)
844 *pos = p->solid->pos;
845
846 return 0;
847 }
848
849 vang = VANGLE(p->solid->vel);
850 speed = MIN( VMOD(p->solid->vel), p->speed );
851
852 /* Calculate the time to face backward and apply forward thrust. */
853 bdiff = angle_diff(p->solid->dir, vang + M_PI);
854 btime = ABS(bdiff) / p->turn + speed / (p->thrust / p->solid->mass);
855 dist = (ABS(bdiff) / p->turn) * speed +
856 (speed / (p->thrust / p->solid->mass)) * (speed / 2.);
857
858 if (p->stats.misc_reverse_thrust) {
859 /* Calculate the time to face forward and apply reverse thrust. */
860 fdiff = angle_diff(p->solid->dir, vang);
861 ftime = ABS(fdiff) / p->turn + speed /
862 (p->thrust / p->solid->mass * PILOT_REVERSE_THRUST);
863
864 /* Faster to use reverse thrust. */
865 if (ftime < btime)
866 dist = (ABS(fdiff) / p->turn) * speed + (speed /
867 (p->thrust / p->solid->mass * PILOT_REVERSE_THRUST)) * (speed / 2.);
868 }
869
870 if (pos != NULL)
871 vec2_cset( pos,
872 p->solid->pos.x + cos(vang) * dist,
873 p->solid->pos.y + sin(vang) * dist);
874
875 return dist;
876}
877
888int pilot_interceptPos( Pilot *p, double x, double y )
889{
890 double px, py, target, face, diff, fdiff;
891
892 px = p->solid->pos.x;
893 py = p->solid->pos.y;
894
895 /* Target angle for the pilot's vel */
896 target = atan2( y - py, x - px );
897
898 /* Current angle error. */
899 diff = angle_diff( VANGLE(p->solid->vel), target );
900
901 if (ABS(diff) < MIN_DIR_ERR) {
902 pilot_setThrust(p, 0.);
903 return 1;
904 }
905 else if (ABS(diff) > M_PI / 1.5) {
906 face = target;
907 fdiff = pilot_face(p, face);
908
909 /* Apply thrust if within 180 degrees. */
910 if (FABS(fdiff) < M_PI)
911 pilot_setThrust(p, 1.);
912 else
913 pilot_setThrust(p, 0.);
914
915 return 0;
916 }
917 else if (diff > M_PI_4)
918 face = target + M_PI_4;
919 else if (diff < -M_PI_4)
920 face = target - M_PI_4;
921 else
922 face = target + diff;
923
924 fdiff = pilot_face(p, face);
925
926 /* Must be in proper quadrant, +/- 45 degrees. */
927 if (fdiff < M_PI_4)
928 pilot_setThrust(p, 1.);
929 else
930 pilot_setThrust(p, 0.);
931
932 return 0;
933}
934
941void pilot_cooldown( Pilot *p, int dochecks )
942{
943 double heat_capacity, heat_mean;
944
945 /* Brake if necessary. */
946 if (dochecks && !pilot_isStopped(p)) {
947 pilot_setFlag(p, PILOT_BRAKING);
948 pilot_setFlag(p, PILOT_COOLDOWN_BRAKE);
949 return;
950 }
951 else {
952 pilot_rmFlag(p, PILOT_BRAKING);
953 pilot_rmFlag(p, PILOT_COOLDOWN_BRAKE);
954 }
955
956 if (p->id == PLAYER_ID)
957 player_message(_("#oActive cooldown engaged."));
958
959 /* Disable active outfits. */
960 if (pilot_outfitOffAll( p ) > 0)
961 pilot_calcStats( p );
962
963 /* Calculate the ship's overall heat. */
964 heat_capacity = p->heat_C;
965 heat_mean = p->heat_T * p->heat_C;
966 for (int i=0; i<array_size(p->outfits); i++) {
967 PilotOutfitSlot *o = p->outfits[i];
968 o->heat_start = o->heat_T;
969 heat_capacity += p->outfits[i]->heat_C;
970 heat_mean += o->heat_T * o->heat_C;
971 }
972
973 /* Paranoia - a negative mean heat will result in NaN cdelay. */
974 heat_mean = MAX( heat_mean, CONST_SPACE_STAR_TEMP );
975
976 heat_mean /= heat_capacity;
977
978 /*
979 * Base delay of about 9.5s for a Lancelot, 32.8s for a Peacemaker.
980 *
981 * Super heat penalty table:
982 * 300K: 13.4%
983 * 350K: 31.8%
984 * 400K: 52.8%
985 * 450K: 75.6%
986 * 500K: 100.0%
987 */
988 p->cdelay = (5. + sqrt(p->base_mass) / 2.) *
989 (1. + pow(MAX(heat_mean / CONST_SPACE_STAR_TEMP - 1.,0.), 1.25));
990 p->ctimer = p->cdelay * p->stats.cooldown_time;
991 p->heat_start = p->heat_T;
992 pilot_setFlag(p, PILOT_COOLDOWN);
993
994 /* Run outfit cooldown start hook. */
995 pilot_outfitLCooldown(p, 0, 0, p->ctimer);
996}
997
1004void pilot_cooldownEnd( Pilot *p, const char *reason )
1005{
1006 if (pilot_isFlag(p, PILOT_COOLDOWN_BRAKE)) {
1007 pilot_rmFlag(p, PILOT_COOLDOWN_BRAKE);
1008 return;
1009 }
1010
1011 /* Send message to player. */
1012 if (p->id == PLAYER_ID) {
1013 if (p->ctimer < 0.)
1014 player_message("#o%s",_("Active cooldown completed."));
1015 else {
1016 if (reason != NULL)
1017 player_message(_("#rActive cooldown aborted: %s!"), reason);
1018 else
1019 player_message("#r%s",_("Active cooldown aborted!"));
1020 }
1022 }
1023
1024 pilot_rmFlag(p, PILOT_COOLDOWN);
1025
1026 /* Cooldown finished naturally, reset heat just in case. */
1027 if (p->ctimer < 0.) {
1028 pilot_heatReset( p );
1029 pilot_fillAmmo( p );
1030 pilot_outfitLCooldown(p, 1, 1, 0.);
1031 }
1032 else {
1033 pilot_outfitLCooldown(p, 1, 0, 0.);
1034 }
1035}
1036
1044double pilot_aimAngle( Pilot *p, const vec2* pos, const vec2* vel )
1045{
1046 double x, y;
1047 double t;
1048 vec2 tv, approach_vector, relative_location, orthoradial_vector;
1049 double dist;
1050 double speed;
1051 double radial_speed;
1052 double orthoradial_speed;
1053
1054 /* Get the distance */
1055 dist = vec2_dist( &p->solid->pos, pos );
1056
1057 /* Check if should recalculate weapon speed with secondary weapon. */
1058 speed = pilot_weapSetSpeed( p, p->active_set, -1 );
1059
1060 /* determine the radial, or approach speed */
1061 /*
1062 *approach_vector (denote Va) is the relative velocites of the pilot and target
1063 *relative_location (denote Vr) is the vector that points from the target to the pilot
1064 *
1065 *Va dot Vr is the rate of approach between the target and the pilot.
1066 *If this is greater than 0, the target is approaching the pilot, if less than 0, the target is fleeing.
1067 *
1068 *Va dot Vr + ShotSpeed is the net closing velocity for the shot, and is used to compute the time of flight for the shot.
1069 */
1070 vec2_cset(&approach_vector, VX(p->solid->vel) - VX(*vel), VY(p->solid->vel) - VY(*vel) );
1071 vec2_cset(&relative_location, VX(*pos) - VX(p->solid->pos), VY(*pos) - VY(p->solid->pos) );
1072 vec2_cset(&orthoradial_vector, VY(p->solid->pos) - VY(*pos), VX(*pos) - VX(p->solid->pos) );
1073
1074 radial_speed = vec2_dot(&approach_vector, &relative_location);
1075 radial_speed = radial_speed / VMOD(relative_location);
1076
1077 orthoradial_speed = vec2_dot(&approach_vector, &orthoradial_vector);
1078 orthoradial_speed = orthoradial_speed / VMOD(relative_location);
1079
1080 /* Time for shots to reach that distance */
1081 /* t is the real positive solution of a 2nd order equation*/
1082 /* if the target is not hittable (i.e., fleeing faster than our shots can fly, determinant <= 0), just face the target */
1083 if ( ((speed*speed - VMOD(approach_vector)*VMOD(approach_vector)) != 0) && (speed*speed - orthoradial_speed*orthoradial_speed) > 0)
1084 t = dist * (sqrt( speed*speed - orthoradial_speed*orthoradial_speed ) - radial_speed) /
1085 (speed*speed - VMOD(approach_vector)*VMOD(approach_vector));
1086 else
1087 t = 0;
1088
1089 /* if t < 0, try the other solution*/
1090 if (t < 0)
1091 t = - dist * (sqrt( speed*speed - orthoradial_speed*orthoradial_speed ) + radial_speed) /
1092 (speed*speed - VMOD(approach_vector)*VMOD(approach_vector));
1093
1094 /* if t still < 0, no solution*/
1095 if (t < 0)
1096 t = 0;
1097
1098 /* Position is calculated on where it should be */
1099 x = pos->x + vel->x*t - (p->solid->pos.x + p->solid->vel.x*t);
1100 y = pos->y + vel->y*t - (p->solid->pos.y + p->solid->vel.y*t);
1101 vec2_cset( &tv, x, y );
1102
1103 return VANGLE(tv);
1104}
1105
1112{
1113 if (pilot_isFriendly( p ) || pilot_isFlag( p, PILOT_BRIBED )
1114 || !pilot_isFlag( p, PILOT_HOSTILE ))
1115 pilot_setFlag( p, PILOT_HOSTILE );
1116 pilot_rmFriendly( p );
1117 pilot_rmFlag( p, PILOT_BRIBED );
1118}
1119
1126{
1127 if (pilot_isDisabled(p))
1128 return 'I';
1129 else if (pilot_isFriendly(p))
1130 return 'F';
1131 else if (pilot_isHostile(p))
1132 return 'H';
1133 return faction_getColourChar(p->faction);
1134}
1135
1139void pilot_setCommMsg( Pilot *p, const char *s )
1140{
1141 free( p->comm_msg );
1142 p->comm_msg = strdup( s );
1143 p->comm_msgWidth = gl_printWidthRaw( NULL, s );
1144 p->comm_msgTimer = pilot_commTimeout;
1145}
1146
1154void pilot_broadcast( Pilot *p, const char *msg, int ignore_int )
1155{
1156 char c;
1157
1158 /* Only display if player.p exists and is in range. */
1159 if (player.p==NULL)
1160 return;
1161
1162 /* Check if should ignore interference. */
1163 if (!ignore_int && !pilot_inRangePilot( player.p, p, NULL ))
1164 return;
1165
1167 player_message( _("#%cBroadcast %s>#0 \"%s\""), c, p->name, msg );
1168
1169 /* Set comm message. */
1170 pilot_setCommMsg( p, msg );
1171}
1172
1182void pilot_distress( Pilot *p, Pilot *attacker, const char *msg )
1183{
1184 int r;
1185 double d;
1186
1187 /* Broadcast the message. */
1188 if (msg[0] != '\0')
1189 pilot_broadcast( p, msg, 0 );
1190
1191 /* Use the victim's target if the attacker is unknown. */
1192 if (attacker == NULL)
1193 attacker = pilot_getTarget( p );
1194
1195 /* Now proceed to see if player.p should incur faction loss because
1196 * of the broadcast signal. */
1197
1198 /* Consider not in range at first. */
1199 r = 0;
1200
1201 if (!pilot_isFlag(p, PILOT_DISTRESSED)) {
1202 /* Check if spob is in range. */
1203 for (int i=0; i<array_size(cur_system->spobs); i++) {
1204 if (spob_hasService(cur_system->spobs[i], SPOB_SERVICE_INHABITED) &&
1205 pilot_inRangeSpob(p, i) &&
1206 !areEnemies(p->faction, cur_system->spobs[i]->presence.faction)) {
1207 r = 1;
1208 break;
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 = (pow(p->base_mass, 0.2) - 1.);
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!"),p->name);
1259 hit = 0.;
1260 }
1261 lua_pop(naevL,2);
1262 faction_modPlayer( p->faction, -hit, "distress" );
1263 }
1264 }
1265
1266 /* Set flag to avoid a second faction hit. */
1267 pilot_setFlag(p, PILOT_DISTRESSED);
1268 }
1269}
1270
1277{
1278 if (!pilot_isHostile(p))
1279 return;
1280
1281 if (pilot_isFlag(p, PILOT_HOSTILE))
1282 pilot_rmFlag(p, PILOT_HOSTILE);
1283
1284 /* Set "bribed" flag if faction has poor reputation */
1285 if (areEnemies( FACTION_PLAYER, p->faction ))
1286 pilot_setFlag(p, PILOT_BRIBED);
1287}
1288
1295{
1296 pilot_rmHostile(p);
1297 pilot_setFlag(p, PILOT_FRIENDLY);
1298}
1299
1306{
1307 pilot_rmFlag(p, PILOT_FRIENDLY);
1308}
1309
1316int pilot_getJumps( const Pilot* p )
1317{
1318 return p->fuel / p->fuel_consumption;
1319}
1320
1327const glColour* pilot_getColour( const Pilot* p )
1328{
1329 const glColour *col;
1330
1331 if (pilot_inRangePilot(player.p, p, NULL) == -1)
1332 col = &cNeutral;
1333 else if (pilot_isDisabled(p) || pilot_isFlag(p,PILOT_DEAD))
1334 col = &cInert;
1335 else if (pilot_isFriendly(p))
1336 col = &cFriend;
1337 else if (pilot_isHostile(p))
1338 col = &cHostile;
1339 else
1340 col = &cNeutral;
1341
1342 return col;
1343}
1344
1351void pilot_setTarget( Pilot* p, unsigned int id )
1352{
1353 /* Case no change. */
1354 if (p->target == id)
1355 return;
1356
1357 p->target = id;
1358 p->ptarget = NULL; /* Gets recomputed later in pilot_getTarget. */
1359 pilot_lockClear( p );
1360
1361 /* Set the scan timer. */
1362 pilot_ewScanStart( p );
1363
1364 /* Untarget asteroid (if any). */
1365 p->nav_anchor = -1;
1366 p->nav_asteroid = -1;
1367}
1368
1381double pilot_hit( Pilot* p, const Solid* w, const Pilot *pshooter,
1382 const Damage *dmg, const Outfit *outfit, int lua_mem, int reset )
1383{
1384 int mod, shooter;
1385 double damage_shield, damage_armour, disable, knockback, dam_mod, ddmg, ddis, absorb, dmod, start;
1386 double tdshield, tdarmour;
1387
1388 /* Invincible means no damage. */
1389 if (pilot_isFlag( p, PILOT_INVINCIBLE ) ||
1390 pilot_isFlag( p, PILOT_HIDE ) ||
1391 ((pshooter!=NULL) && pilot_isWithPlayer(pshooter) && pilot_isFlag( p, PILOT_INVINC_PLAYER )))
1392 return 0.;
1393
1394 /* Defaults. */
1395 dam_mod = 0.;
1396 ddmg = 0.;
1397 ddis = 0.;
1398 shooter = (pshooter==NULL) ? 0 : pshooter->id;
1399
1400 /* Calculate the damage. */
1401 absorb = 1. - CLAMP( 0., 1., p->dmg_absorb - dmg->penetration );
1402 disable = dmg->disable;
1403 dtype_calcDamage( &damage_shield, &damage_armour, absorb, &knockback, dmg, &p->stats );
1404
1405 /*
1406 * Delay undisable if necessary. Amount varies with damage, as e.g. a
1407 * single Laser Cannon shot should not reset a Peacemaker's timer.
1408 */
1409 if (!pilot_isFlag(p, PILOT_DEAD) && (p->dtimer_accum > 0.))
1410 p->dtimer_accum -= MIN( pow(disable, 0.8), p->dtimer_accum );
1411
1412 /* Ships that can not be disabled take raw armour damage instead of getting disabled. */
1413 if (pilot_isFlag( p, PILOT_NODISABLE )) {
1414 damage_armour += disable * absorb;
1415 disable = 0.;
1416 }
1417 else
1418 disable *= absorb;
1419 tdshield = 0.;
1420 tdarmour = 0.;
1421
1422 /*
1423 * Shields take entire blow.
1424 */
1425 if (p->shield - damage_shield > 0.) {
1426 start = p->shield;
1427 ddmg = damage_shield;
1428 p->shield -= damage_shield;
1429 dam_mod = damage_shield/p->shield_max;
1430
1431 /*
1432 * Disabling damage leaks accordingly:
1433 * 50% + (50% - mean shield % / 2)
1434 *
1435 * 50% leaks at 100% shield, scales to 100% by 0% shield.
1436 *
1437 * The damage is adjusted based on the mean of the starting and ending
1438 * shield percentages. Using the starting percentage biases towards
1439 * low-damage, high-ROF weapons, while using the ending percentage
1440 * biases towards high-damage, low-ROF weapons.
1441 */
1442 ddis = disable * (0.5 + (0.5 - ((start+p->shield) / p->shield_max) / 4.));
1443 p->stress += ddis;
1444
1445 /* True damage. */
1446 tdshield = damage_shield;
1447 }
1448 /*
1449 * Shields take part of the blow.
1450 */
1451 else if (p->shield > 0.) {
1452 start = p->shield;
1453 dmod = (1. - p->shield/damage_shield);
1454 ddmg = p->shield + dmod * damage_armour;
1455 tdshield = p->shield;
1456 p->shield = 0.;
1457
1458 /* Leak some disabling damage through the remaining bit of shields. */
1459 ddis = disable * (1. - dmod) * (0.5 + (0.5 - (start / p->shield_max / 4.)));
1460 p->stress += ddis;
1461
1462 /* Reduce stress as armour is eaten away. */
1463 //p->stress *= (p->armour - dmod * damage_armour) / p->armour;
1464 tdarmour = dmod * damage_armour;
1465 p->armour -= tdarmour;
1466 p->stress += dmod * disable;
1467 dam_mod = (damage_shield + damage_armour) /
1468 ((p->shield_max + p->armour_max) / 2.);
1469
1470 /* Increment shield timer or time before shield regeneration kicks in. */
1471 if (reset) {
1472 p->stimer = 3.;
1473 p->sbonus = 3.;
1474 }
1475 }
1476 /*
1477 * Armour takes the entire blow.
1478 */
1479 else if (p->armour > 0.) {
1480 ddmg = damage_armour;
1481 /* Reduce stress as armour is eaten away. */
1482 //p->stress *= (p->armour - damage_armour) / p->armour;
1483 p->armour -= damage_armour;
1484 ddis = disable;
1485 p->stress += ddis;
1486
1487 /* Increment shield timer or time before shield regeneration kicks in. */
1488 if (reset) {
1489 p->stimer = 3.;
1490 p->sbonus = 3.;
1491 }
1492
1493 /* True damage. */
1494 tdarmour = ddmg;
1495 }
1496
1497 /* Ensure stress never exceeds remaining armour. */
1498 if (p->stress > p->armour)
1499 p->stress = p->armour;
1500
1501 /* Do not let pilot die. */
1502 if ((p->armour <= 0.) && pilot_isFlag( p, PILOT_NODEATH )) {
1503 p->armour = 1.;
1504 p->stress = 1.;
1505 }
1506
1507 /* Can't undisable permanently disabled pilots through shooting. */
1508 if (pilot_isFlag( p, PILOT_DISABLED_PERM ))
1509 p->stress = p->armour;
1510
1511 /* Some minor effects and stuff. */
1512 if (p->shield <= 0.) {
1513 if (p->id == PLAYER_ID) { /* a bit of shaking */
1514 double spfx_mod = tdarmour/p->armour_max;
1515 spfx_shake( 0.5 * spfx_mod );
1516 spfx_damage( spfx_mod );
1517 }
1518 }
1519
1520 /* Update player meta-data if applicable. */
1521 if (p->id == PLAYER_ID) {
1522 player.dmg_taken_shield += tdshield;
1523 player.dmg_taken_armour += tdarmour;
1524 player.ps.dmg_taken_shield += tdshield;
1525 player.ps.dmg_taken_armour += tdarmour;
1526 }
1527 /* TODO we might want to actually resolve shooter and check for
1528 * FACTION_PLAYER so that escorts also get counted... */
1529 else if (shooter == PLAYER_ID) {
1530 player.dmg_done_shield += tdshield;
1531 player.dmg_done_armour += tdarmour;
1532 player.ps.dmg_done_shield += tdshield;
1533 player.ps.dmg_done_armour += tdarmour;
1534 }
1535
1536 if (w != NULL)
1537 /* knock back effect is dependent on both damage and mass of the weapon
1538 * should probably get turned into a partial conservative collision */
1539 vec2_cadd( &p->solid->vel,
1540 knockback * (w->vel.x * (dam_mod/9. + w->mass/p->solid->mass/6.)),
1541 knockback * (w->vel.y * (dam_mod/9. + w->mass/p->solid->mass/6.)) );
1542
1543 /* On hit weapon effects. */
1544 if ((outfit!=NULL) && (outfit->lua_onimpact != LUA_NOREF)) {
1545 lua_rawgeti(naevL, LUA_REGISTRYINDEX, lua_mem); /* mem */
1546 nlua_setenv(naevL, outfit->lua_env, "mem"); /* */
1547
1548 /* Set up the function: onimpact( pshooter, p ) */
1549 lua_rawgeti(naevL, LUA_REGISTRYINDEX, outfit->lua_onimpact); /* f */
1550 lua_pushpilot(naevL, shooter); /* f, p */
1551 lua_pushpilot(naevL, p->id); /* f, p, p */
1552 lua_pushvector(naevL, w->pos); /* f, p, p, x */
1553 lua_pushvector(naevL, w->vel); /* f, p, p, x, v */
1554 if (nlua_pcall( outfit->lua_env, 4, 0 )) { /* */
1555 WARN( _("Pilot '%s''s outfit '%s' -> '%s':\n%s"), p->name, outfit->name, "onimpact", lua_tostring(naevL,-1) );
1556 lua_pop(naevL, 1);
1557 }
1558 }
1559
1560 /* On hit Lua outfits activate. */
1561 pilot_outfitLOnhit( p, tdarmour, tdshield, shooter );
1562
1563 /* Run disabled before death. Should be run after on hit effects in case of regen. */
1564 pilot_updateDisable(p, shooter);
1565
1566 /* Officially dead, run after in case they are regenerated by outfit. */
1567 if (p->armour <= 0.) {
1568 p->armour = 0.;
1569
1570 if (!pilot_isFlag(p, PILOT_DEAD)) {
1571 pilot_dead( p, shooter );
1572
1573 /* adjust the combat rating based on pilot mass and ditto faction */
1574 if ((pshooter != NULL) && pilot_isWithPlayer(pshooter)) {
1575
1576 /* About 6 for a llama, 52 for hawking. */
1577 mod = 2. * (pow(p->base_mass, 0.4) - 1.);
1578
1579 /* Modify faction for him and friends. */
1580 faction_modPlayer( p->faction, -mod, "kill" );
1581
1582 /* Note that player destroyed the ship. */
1583 player.ships_destroyed[p->ship->class]++;
1584 player.ps.ships_destroyed[p->ship->class]++;
1585 }
1586 }
1587 }
1588
1589 return ddmg + ddis;
1590}
1591
1598void pilot_updateDisable( Pilot* p, unsigned int shooter )
1599{
1600 if ((!pilot_isFlag(p, PILOT_DISABLED)) &&
1601 (!pilot_isFlag(p, PILOT_NODISABLE) || (p->armour <= 0.)) &&
1602 (p->armour <= p->stress)) { /* Pilot should be disabled. */
1603 HookParam hparam;
1604
1605 /* Cooldown is an active process, so cancel it. */
1606 if (pilot_isFlag(p, PILOT_COOLDOWN))
1607 pilot_cooldownEnd(p, NULL);
1608
1609 /* Clear other active states. */
1610 pilot_rmFlag(p, PILOT_COOLDOWN_BRAKE);
1611 pilot_rmFlag(p, PILOT_BRAKING);
1612 pilot_rmFlag(p, PILOT_STEALTH);
1613
1614 /* Clear hyperspace flags. */
1615 pilot_rmFlag(p, PILOT_HYP_PREP);
1616 pilot_rmFlag(p, PILOT_HYP_BEGIN);
1617 pilot_rmFlag(p, PILOT_HYP_BRAKE);
1618 pilot_rmFlag(p, PILOT_HYPERSPACE);
1619
1620 /* Disabled ships don't use up presence. */
1621 if (p->presence > 0) {
1622 system_rmCurrentPresence( cur_system, p->faction, p->presence );
1623 p->presence = 0;
1624 }
1625
1626 /* Set disable timer. This is the time the pilot will remain disabled. */
1627 /* 200 mass llama => 46.78 s
1628 * 8000 mass peacemaker => 156 s
1629 */
1630 p->dtimer = 8. * pow( p->solid->mass, 1./3. );
1631 p->dtimer_accum = 0.;
1632
1633 /* Disable active outfits. */
1634 if (pilot_outfitOffAll( p ) > 0)
1635 pilot_calcStats( p );
1636
1637 pilot_setFlag( p, PILOT_DISABLED ); /* set as disabled */
1638 if (pilot_isPlayer( p ))
1639 player_message("#r%s",_("You have been disabled!"));
1640
1641 /* Run hook */
1642 if (shooter > 0) {
1643 hparam.type = HOOK_PARAM_PILOT;
1644 hparam.u.lp = shooter;
1645 }
1646 else {
1647 hparam.type = HOOK_PARAM_NIL;
1648 }
1649 pilot_runHookParam( p, PILOT_HOOK_DISABLE, &hparam, 1 ); /* Already disabled. */
1650 }
1651 else if (pilot_isFlag(p, PILOT_DISABLED) && (p->armour > p->stress)) { /* Pilot is disabled, but shouldn't be. */
1652 pilot_rmFlag( p, PILOT_DISABLED ); /* Undisable. */
1653 pilot_rmFlag( p, PILOT_DISABLED_PERM ); /* Clear perma-disable flag if necessary. */
1654 pilot_rmFlag( p, PILOT_BOARDING ); /* Can get boarded again. */
1655
1656 /* Reset the accumulated disable time. */
1657 p->dtimer_accum = 0.;
1658
1659 /* TODO: Make undisabled pilot use up presence again. */
1660 pilot_runHook( p, PILOT_HOOK_UNDISABLE );
1661
1662 /* This is sort of a hack to make sure it gets reset... */
1663 if (pilot_isPlayer(p)) {
1665 player_message("#g%s",_("You have recovered control of your ship!"));
1666 }
1667 }
1668}
1669
1676void pilot_dead( Pilot* p, unsigned int killer )
1677{
1678 HookParam hparam;
1679
1680 if (pilot_isFlag(p,PILOT_DEAD))
1681 return; /* he's already dead */
1682
1683 /* basically just set timers */
1684 if (p->id==PLAYER_ID) {
1685 pilot_setFlag(p, PILOT_DISABLED );
1686 player_dead();
1687 }
1688 p->timer[0] = 0.; /* no need for AI anymore */
1689 p->ptimer = MIN( 1. + sqrt(p->armour_max * p->shield_max) / 650.,
1690 3 + pow(p->armour_max * p->shield_max, 0.4) / 500);
1691 p->timer[1] = 0.; /* explosion timer */
1692
1693 /* flag cleanup - fixes some issues */
1694 pilot_rmFlag(p,PILOT_HYP_PREP);
1695 pilot_rmFlag(p,PILOT_HYP_BEGIN);
1696 pilot_rmFlag(p,PILOT_HYP_BRAKE);
1697 pilot_rmFlag(p,PILOT_HYPERSPACE);
1698
1699 /* Turn off all outfits, should disable Lua stuff as necessary. */
1700 pilot_outfitOffAll( p );
1701
1702 /* Pilot must die before setting death flag and probably messing with other flags. */
1703 if (killer > 0) {
1704 hparam.type = HOOK_PARAM_PILOT;
1705 hparam.u.lp = killer;
1706 }
1707 else
1708 hparam.type = HOOK_PARAM_NIL;
1709 pilot_runHookParam( p, PILOT_HOOK_DEATH, &hparam, 1 );
1710
1711 /* Need a check here in case the hook "regenerates" the pilot. */
1712 if (p->armour <= 0.) {
1713 if (p->parent == PLAYER_ID)
1714 player_message( _("#rShip under command '%s' was destroyed!#0"), p->name );
1715 /* PILOT R OFFICIALLY DEADZ0R */
1716 pilot_setFlag( p, PILOT_DEAD );
1717 }
1718}
1719
1728void pilot_explode( double x, double y, double radius, const Damage *dmg, const Pilot *parent )
1729{
1730 double dist, rad2;
1731 Solid s; /* Only need to manipulate mass and vel. */
1732 Damage ddmg;
1733
1734 rad2 = radius*radius;
1735 ddmg = *dmg;
1736
1737 for (int i=0; i<array_size(pilot_stack); i++) {
1738 double rx, ry;
1739 Pilot *p = pilot_stack[i];
1740
1741 /* Calculate a bit. */
1742 rx = p->solid->pos.x - x;
1743 ry = p->solid->pos.y - y;
1744 dist = pow2(rx) + pow2(ry);
1745 /* Take into account ship size. */
1746 dist -= pow2(p->ship->gfx_space->sw);
1747 dist = MAX(0,dist);
1748
1749 /* Pilot is not hit. */
1750 if (dist > rad2)
1751 continue;
1752
1753 /* Adjust damage based on distance. */
1754 ddmg.damage = dmg->damage * (1. - sqrt(dist / rad2));
1755
1756 /* Impact settings. */
1757 s.mass = pow2(dmg->damage) / 30.;
1758 s.vel.x = rx;
1759 s.vel.y = ry;
1760
1761 /* Actual damage calculations. */
1762 pilot_hit( p, &s, parent, &ddmg, NULL, LUA_NOREF, 1 );
1763
1764 /* Shock wave from the explosion. */
1765 if (p->id == PILOT_PLAYER)
1766 spfx_shake( pow2(ddmg.damage) / pow2(100.) );
1767 }
1768}
1769
1778void pilot_renderFramebuffer( Pilot *p, GLuint fbo, double fw, double fh )
1779{
1780 glColour c = { 1., 1., 1., 1. };
1781
1782 /* Add some transparency if stealthed. TODO better effect */
1783 if (pilot_isFlag(p, PILOT_STEALTH))
1784 c.a = 0.5;
1785
1786 glBindFramebuffer( GL_FRAMEBUFFER, fbo );
1787 glClearColor( 0., 0., 0., 0. );
1788
1789 if (p->ship->gfx_3d != NULL) {
1790 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1791 /* TODO fix 3D rendering. */
1792 }
1793 else {
1794 double tx,ty;
1795 const glTexture *sa, *sb;
1796 mat4 tmpm;
1797
1798 glClear( GL_COLOR_BUFFER_BIT );
1799
1800 sa = p->ship->gfx_space;
1801 sb = p->ship->gfx_engine;
1802
1803 /* texture coords */
1804 tx = sa->sw*(double)(p->tsx)/sa->w;
1805 ty = sa->sh*(sa->sy-(double)p->tsy-1)/sa->h;
1806
1807 tmpm = gl_view_matrix;
1808 gl_view_matrix = mat4_ortho( 0., fw, 0, fh, -1., 1. );
1809
1811 1.-p->engine_glow, 0., 0., sa->sw, sa->sh,
1812 tx, ty, sa->srw, sa->srh, &c );
1813
1814 gl_view_matrix = tmpm;
1815 }
1816
1817 glBindFramebuffer(GL_FRAMEBUFFER, gl_screen.current_fbo);
1818 glClearColor( 0., 0., 0., 1. );
1819}
1820
1827{
1828 double scale, x,y, w,h, z;
1829 int inbounds = 1;
1830 Effect *e = NULL;
1831 glColour c = {.r=1., .g=1., .b=1., .a=1.};
1832
1833 /* Don't render the pilot. */
1834 if (pilot_isFlag( p, PILOT_NORENDER ))
1835 return;
1836
1837 /* Transform coordinates. */
1838 z = cam_getZoom();
1839 w = p->ship->gfx_space->sw;
1840 h = p->ship->gfx_space->sh;
1841 gl_gameToScreenCoords( &x, &y, p->solid->pos.x-w/2., p->solid->pos.y-h/2. );
1842
1843 /* Check if inbounds */
1844 if ((x < -w) || (x > SCREEN_W+w) ||
1845 (y < -h) || (y > SCREEN_H+h))
1846 inbounds = 0;
1847
1848 if (inbounds) {
1849 /* Render effects. */
1850 for (int i=0; i<array_size(p->effects); i++) {
1851 //for (int i=array_size(p->effects)-1; i>=0; i--) {
1852 Effect *eiter = &p->effects[i];
1853 if (eiter->data->program==0)
1854 continue;
1855
1856 /* Only render one effect for now. */
1857 e = eiter;
1858 break;
1859 }
1860
1861 /* Check if needs scaling. */
1862 if (pilot_isFlag( p, PILOT_LANDING ))
1863 scale = CLAMP( 0., 1., p->ptimer / p->landing_delay );
1864 else if (pilot_isFlag( p, PILOT_TAKEOFF ))
1865 scale = CLAMP( 0., 1., 1. - p->ptimer / p->landing_delay );
1866 else
1867 scale = 1.;
1868
1869 /* Add some transparency if stealthed. TODO better effect */
1870 if (pilot_isFlag(p, PILOT_STEALTH))
1871 c.a = 0.5;
1872
1873 /* Render normally. */
1874 if (e==NULL) {
1875 if (p->ship->gfx_3d != NULL) {
1876 /* 3d */
1877 object_renderSolidPart(p->ship->gfx_3d, p->solid, "body", c.a, p->ship->gfx_3d_scale * scale);
1878 object_renderSolidPart(p->ship->gfx_3d, p->solid, "engine", c.a * p->engine_glow, p->ship->gfx_3d_scale * scale);
1879 }
1880 else {
1881 gl_renderSpriteInterpolateScale( p->ship->gfx_space, p->ship->gfx_engine,
1882 1.-p->engine_glow, p->solid->pos.x, p->solid->pos.y,
1883 scale, scale, p->tsx, p->tsy, &c );
1884 }
1885 }
1886 /* Render effect single effect. */
1887 else {
1888 mat4 projection, tex_mat;
1889 const EffectData *ed = e->data;
1890
1891 /* Render onto framebuffer. */
1893
1894 glUseProgram( ed->program );
1895
1896 glActiveTexture( GL_TEXTURE0 );
1897 glBindTexture( GL_TEXTURE_2D, gl_screen.fbo_tex[2] );
1898 glUniform1i( ed->u_tex, 0 );
1899
1900 glEnableVertexAttribArray( ed->vertex );
1901 gl_vboActivateAttribOffset( gl_squareVBO, ed->vertex, 0, 2, GL_FLOAT, 0 );
1902
1903 projection = gl_view_matrix;
1904 mat4_translate( &projection, x + (1.-scale)*z*w/2., y + (1.-scale)*z*h/2., 0. );
1905 mat4_scale( &projection, scale*z*w, scale*z*h, 1. );
1906 gl_uniformMat4(ed->projection, &projection);
1907
1908 tex_mat = mat4_identity();
1909 mat4_scale( &tex_mat, w/(double)gl_screen.nw, h/(double)gl_screen.nh, 1. );
1910 gl_uniformMat4(ed->tex_mat, &tex_mat);
1911
1912 glUniform3f( ed->dimensions, SCREEN_W, SCREEN_H, cam_getZoom() );
1913 glUniform1f( ed->u_timer, e->timer );
1914 glUniform1f( ed->u_elapsed, e->elapsed );
1915 glUniform1f( ed->u_r, e->r );
1916
1917 /* Draw. */
1918 glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
1919
1920 /* Clear state. */
1921 glDisableVertexAttribArray( ed->vertex );
1922 }
1923 }
1924
1925#ifdef DEBUGGING
1926 double dircos, dirsin;
1927 int debug_mark_emitter = debug_isFlag(DEBUG_MARK_EMITTER);
1928 vec2 v;
1929 if (debug_mark_emitter) {
1930 dircos = cos(p->solid->dir);
1931 dirsin = sin(p->solid->dir);
1932 }
1933#endif /* DEBUGGING */
1934
1935 /* Re-draw backwards trails. */
1936 for (int i=0,g=0; g<array_size(p->ship->trail_emitters); g++) {
1937#ifdef DEBUGGING
1938 if (debug_mark_emitter) {
1939 /* Visualize the trail emitters. */
1940 v.x = p->ship->trail_emitters[g].x_engine * dircos -
1941 p->ship->trail_emitters[g].y_engine * dirsin;
1942 v.y = p->ship->trail_emitters[g].x_engine * dirsin +
1943 p->ship->trail_emitters[g].y_engine * dircos +
1944 p->ship->trail_emitters[g].h_engine;
1945
1946 gl_gameToScreenCoords( &x, &y, p->solid->pos.x + v.x,
1947 p->solid->pos.y + v.y*M_SQRT1_2 );
1948 if (p->ship->trail_emitters[i].trail_spec->nebula)
1949 gl_renderCross(x, y, 2, &cFontBlue);
1950 else
1951 gl_renderCross(x, y, 4, &cInert);
1952 }
1953#endif /* DEBUGGING */
1954
1955 if (pilot_trail_generated( p, g )) {
1956 if (p->trail[i]->ontop)
1957 spfx_trail_draw( p->trail[i] );
1958 i++;
1959 }
1960 }
1961}
1962
1969{
1970 int playerdead;
1971
1972 /* Don't render the pilot. */
1973 if (pilot_isFlag( p, PILOT_NORENDER ))
1974 return;
1975
1976 playerdead = (player_isFlag(PLAYER_DESTROYED) || (player.p==NULL));
1977
1978 /* Render the hailing graphic if needed. */
1979 if (!playerdead && pilot_isFlag( p, PILOT_HAILING )) {
1980 glTexture *ico_hail = gui_hailIcon();
1981 if (ico_hail != NULL) {
1982 int sx = (int)ico_hail->sx;
1983
1984 /* Render. */
1985 gl_renderSprite( ico_hail,
1986 p->solid->pos.x + PILOT_SIZE_APPROX*p->ship->gfx_space->sw/2. + ico_hail->sw/4.,
1987 p->solid->pos.y + PILOT_SIZE_APPROX*p->ship->gfx_space->sh/2. + ico_hail->sh/4.,
1988 p->hail_pos % sx, p->hail_pos / sx, NULL );
1989 }
1990 }
1991
1992 /* Text ontop if needed. */
1993 if (p->comm_msg != NULL) {
1994 double x, y, dx, dy;
1995
1996 /* Coordinate translation. */
1997 gl_gameToScreenCoords( &x, &y, p->solid->pos.x, p->solid->pos.y );
1998
1999 /* Display the text. */
2000 glColour c = {1., 1., 1., 1.};
2001
2002 /* Colour. */
2003 if (p->comm_msgTimer - pilot_commFade < 0.)
2004 c.a = p->comm_msgTimer / pilot_commFade;
2005
2006 /* Position to render at. */
2007 dx = x - p->comm_msgWidth/2.;
2008 dy = y + PILOT_SIZE_APPROX*p->ship->gfx_space->sh/2.;
2009
2010 /* Background. */
2011 gl_renderRect( dx-2., dy-2., p->comm_msgWidth+4., gl_defFont.h+4., &cBlackHilight );
2012
2013 /* Display text. */
2014 gl_printRaw( NULL, dx, dy, &c, -1., p->comm_msg );
2015 }
2016
2017 /* Show health / friendlyness */
2018 if (conf.healthbars && !playerdead && !pilot_isPlayer(p) && !pilot_isFlag(p, PILOT_DEAD) &&
2019 (pilot_isFlag(p, PILOT_COMBAT) || (p->shield < p->shield_max))) {
2020 double x, y, w, h;
2021
2022 /* Coordinate translation. */
2023 gl_gameToScreenCoords( &x, &y, p->solid->pos.x, p->solid->pos.y );
2024
2025 w = p->ship->gfx_space->sw + 4.;
2026 h = p->ship->gfx_space->sh + 4.;
2027
2028 /* Can do an inbounds check now. */
2029 if ((x < -w) || (x > SCREEN_W+w) ||
2030 (y < -h) || (y > SCREEN_H+h))
2031 return;
2032
2033 w = PILOT_SIZE_APPROX * p->ship->gfx_space->sw;
2034 h = PILOT_SIZE_APPROX * p->ship->gfx_space->sh / 3.;
2035
2036 glUseProgram( shaders.healthbar.program );
2037 glUniform2f( shaders.healthbar.dimensions, 5., h );
2038 glUniform1f( shaders.healthbar.paramf, (p->armour + p->shield) / (p->armour_max + p->shield_max) );
2039 gl_uniformColor( shaders.healthbar.paramv, (p->shield > 0.) ? &cShield : &cArmour );
2040 gl_renderShader( x + w/2. + 2.5, y, 5., h, 0., &shaders.healthbar, pilot_getColour(p), 1 );
2041 }
2042}
2043
2050void pilot_update( Pilot* pilot, double dt )
2051{
2052 int cooling, nchg;
2053 Pilot *target;
2054 double a, px,py, vx,vy;
2055 double Q;
2056
2057 /* Modify the dt with speedup. */
2058 dt *= pilot->stats.time_speedup;
2059
2060 /* Check target validity. */
2061 target = pilot_getTarget( pilot );
2062
2063 cooling = pilot_isFlag(pilot, PILOT_COOLDOWN);
2064
2065 /*
2066 * Update timers.
2067 */
2068 pilot->ptimer -= dt;
2069 pilot->tcontrol -= dt;
2070 if (cooling) {
2071 pilot->ctimer -= dt;
2072 if (pilot->ctimer < 0.) {
2073 pilot_cooldownEnd(pilot, NULL);
2074 cooling = 0;
2075 }
2076 }
2077 pilot->stimer -= dt;
2078 if (pilot->stimer <= 0.)
2079 pilot->sbonus -= dt;
2080 for (int i=0; i<MAX_AI_TIMERS; i++)
2081 if (pilot->timer[i] > 0.)
2082 pilot->timer[i] -= dt;
2083 if (pilot->comm_msg != NULL) {
2084 pilot->comm_msgTimer -= dt;
2085 if (pilot->comm_msgTimer < 0.) {
2086 free(pilot->comm_msg);
2087 pilot->comm_msg = NULL;
2088 }
2089 }
2090 if (pilot_isFlag( pilot, PILOT_HAILING )) {
2091 glTexture *ico_hail = gui_hailIcon();
2092 if (ico_hail != NULL) {
2093 int sx, sy;
2094 pilot->htimer -= dt;
2095 sx = (int)ico_hail->sx;
2096 sy = (int)ico_hail->sy;
2097 if (pilot->htimer < 0.) {
2098 pilot->htimer = 0.1;
2099 pilot->hail_pos++;
2100 pilot->hail_pos %= sx*sy;
2101 }
2102 }
2103 }
2104 /* Update heat. */
2105 a = -1.;
2106 Q = 0.;
2107 nchg = 0; /* Number of outfits that change state, processed at the end. */
2108 for (int i=0; i<array_size(pilot->outfits); i++) {
2109 PilotOutfitSlot *o = pilot->outfits[i];
2110
2111 /* Picky about our outfits. */
2112 if (o->outfit == NULL)
2113 continue;
2114 if (!o->active)
2115 continue;
2116
2117 /* Handle firerate timer. */
2118 if (o->timer > 0.)
2119 o->timer -= dt * pilot_heatFireRateMod( o->heat_T );
2120
2121 /* Handle reload timer. (Note: this works backwards compared to
2122 * other timers. This helps to simplify code resetting the timer
2123 * elsewhere.)
2124 */
2126 double ammo_threshold, reload_time;
2127
2128 /* Initial (raw) ammo threshold */
2129 if (outfit_isLauncher(o->outfit)) {
2130 ammo_threshold = o->outfit->u.lau.amount;
2131 ammo_threshold = round( (double)ammo_threshold * pilot->stats.ammo_capacity );
2132 reload_time = o->outfit->u.lau.reload_time / pilot->stats.launch_reload;
2133 }
2134 else { /* if (outfit_isFighterBay( o->outfit)) { */ /* Commented to shut up warning. */
2135 ammo_threshold = o->outfit->u.bay.amount;
2136 ammo_threshold = round( (double)ammo_threshold * pilot->stats.fbay_capacity );
2137 /* Adjust for deployed fighters if needed */
2138 ammo_threshold -= o->u.ammo.deployed;
2139 reload_time = o->outfit->u.bay.reload_time / pilot->stats.fbay_reload;
2140 }
2141
2142 /* Add to timer. */
2143 if (o->rtimer < reload_time)
2144 o->rtimer += dt;
2145
2146 /* Don't allow accumulation of the timer before reload allowed */
2147 if ( o->u.ammo.quantity >= ammo_threshold )
2148 o->rtimer = 0;
2149
2150 while ((o->rtimer >= reload_time) &&
2151 (o->u.ammo.quantity < ammo_threshold)) {
2152 o->rtimer -= reload_time;
2153 pilot_addAmmo( pilot, o, 1 );
2154 }
2155
2156 o->rtimer = MIN( o->rtimer, reload_time );
2157 }
2158
2159 /* Handle state timer. */
2160 if (o->stimer >= 0.) {
2161 o->stimer -= dt;
2162 if (o->stimer < 0.) {
2163 if (o->state == PILOT_OUTFIT_ON) {
2164 pilot_outfitOff( pilot, o );
2165 nchg++;
2166 }
2167 else if (o->state == PILOT_OUTFIT_COOLDOWN) {
2168 o->state = PILOT_OUTFIT_OFF;
2169 nchg++;
2170 }
2171 }
2172 }
2173
2174 /* Handle heat. */
2175 if (!cooling)
2176 Q += pilot_heatUpdateSlot( pilot, o, dt );
2177
2178 /* Handle lockons. */
2179 pilot_lockUpdateSlot( pilot, o, target, &a, dt );
2180 }
2181
2182 /* Global heat. */
2183 if (!cooling)
2184 pilot_heatUpdateShip( pilot, Q, dt );
2185 else
2186 pilot_heatUpdateCooldown( pilot );
2187
2188 /* Update electronic warfare. */
2189 pilot_ewUpdateDynamic( pilot, dt );
2190
2191 /* Update stress. */
2192 if (!pilot_isFlag(pilot, PILOT_DISABLED)) { /* Case pilot is not disabled. */
2193 double stress_falloff = 0.3*sqrt(pilot->solid->mass); /* Should be about 38 seconds for a 300 mass ship with 200 armour, and 172 seconds for a 6000 mass ship with 4000 armour. */
2194 pilot->stress -= stress_falloff * pilot->stats.stress_dissipation * dt;
2195 pilot->stress = MAX(pilot->stress, 0);
2196 }
2197 else if (!pilot_isFlag(pilot, PILOT_DISABLED_PERM)) { /* Case pilot is disabled (but not permanently so). */
2198 pilot->dtimer_accum += dt;
2199 if (pilot->dtimer_accum >= pilot->dtimer) {
2200 pilot->stress = 0.;
2201 pilot->dtimer_accum = 0;
2202 pilot_updateDisable(pilot, 0);
2203 }
2204 }
2205
2206 /* Damage effect. */
2207 if ((pilot->stats.damage > 0.) || (pilot->stats.disable > 0.)) {
2208 Damage dmg;
2209 dmg.type = dtype_get("normal");
2210 dmg.damage = pilot->stats.damage * dt;
2211 dmg.penetration = 1.; /* Full penetration. */
2212 dmg.disable = pilot->stats.disable * dt;
2213 pilot_hit( pilot, NULL, NULL, &dmg, NULL, LUA_NOREF, 0 );
2214 }
2215
2216 /* Handle takeoff/landing. */
2217 if (pilot_isFlag(pilot,PILOT_TAKEOFF)) {
2218 if (pilot->ptimer < 0.) {
2219 pilot_rmFlag(pilot,PILOT_TAKEOFF);
2220 if (pilot_isFlag(pilot, PILOT_PLAYER)) {
2221 pilot_setFlag(pilot, PILOT_NONTARGETABLE);
2222 pilot->itimer = PILOT_PLAYER_NONTARGETABLE_TAKEOFF_DELAY;
2223 }
2224 return;
2225 }
2226 }
2227 else if (pilot_isFlag(pilot,PILOT_LANDING)) {
2228 if (pilot->ptimer < 0.) {
2229 if (pilot_isPlayer(pilot)) {
2230 player_setFlag( PLAYER_HOOK_LAND );
2231 pilot->ptimer = 0.;
2232 }
2233 else
2234 pilot_delete(pilot);
2235 return;
2236 }
2237 }
2238 /* he's dead jim */
2239 else if (pilot_isFlag(pilot,PILOT_DEAD)) {
2240
2241 /* pilot death sound */
2242 if (!pilot_isFlag(pilot,PILOT_DEATH_SOUND) &&
2243 (pilot->ptimer < 0.050)) {
2244 char buf[16];
2245
2246 /* Play random explosion sound. */
2247 snprintf(buf, sizeof(buf), "explosion%d", RNG(0,2));
2248 sound_playPos( sound_get(buf), pilot->solid->pos.x, pilot->solid->pos.y,
2249 pilot->solid->vel.x, pilot->solid->vel.y );
2250
2251 pilot_setFlag(pilot,PILOT_DEATH_SOUND);
2252 }
2253 /* final explosion */
2254 else if (!pilot_isFlag(pilot,PILOT_EXPLODED) &&
2255 (pilot->ptimer < 0.200)) {
2256 Damage dmg;
2257
2258 /* Damage from explosion. */
2259 a = sqrt(pilot->solid->mass);
2260 dmg.type = dtype_get("explosion_splash");
2261 dmg.damage = MAX(0., 2. * (a * (1. + sqrt(pilot->fuel + 1.) / 28.)));
2262 dmg.penetration = 1.; /* Full penetration. */
2263 dmg.disable = 0.;
2264 expl_explode( pilot->solid->pos.x, pilot->solid->pos.y,
2265 pilot->solid->vel.x, pilot->solid->vel.y,
2266 pilot->ship->gfx_space->sw/2./PILOT_SIZE_APPROX + a, &dmg, NULL, EXPL_MODE_SHIP );
2267 debris_add( pilot->solid->mass, pilot->ship->gfx_space->sw/2.,
2268 pilot->solid->pos.x, pilot->solid->pos.y,
2269 pilot->solid->vel.x, pilot->solid->vel.y );
2270 pilot_setFlag(pilot,PILOT_EXPLODED);
2271 pilot_runHook( pilot, PILOT_HOOK_EXPLODED );
2272
2273 /* We do a check here in case the pilot was regenerated. */
2274 if (pilot_isFlag(pilot, PILOT_EXPLODED)) {
2275 /* Release cargo */
2276 for (int i=0; i<array_size(pilot->commodities); i++)
2277 pilot_cargoJet( pilot, pilot->commodities[i].commodity,
2278 pilot->commodities[i].quantity, 1 );
2279 }
2280 }
2281 /* reset random explosion timer */
2282 else if (pilot->timer[1] <= 0.) {
2283 unsigned int l;
2284
2285 pilot->timer[1] = 0.08 * (pilot->ptimer - pilot->timer[1]) /
2286 pilot->ptimer;
2287
2288 /* random position on ship */
2289 a = RNGF()*2.*M_PI;
2290 px = VX(pilot->solid->pos) + cos(a)*RNGF()*pilot->ship->gfx_space->sw/2.;
2291 py = VY(pilot->solid->pos) + sin(a)*RNGF()*pilot->ship->gfx_space->sh/2.;
2292 vx = VX(pilot->solid->vel);
2293 vy = VY(pilot->solid->vel);
2294
2295 /* set explosions */
2296 l = (pilot->id==PLAYER_ID) ? SPFX_LAYER_FRONT : SPFX_LAYER_MIDDLE;
2297 if (RNGF() > 0.8)
2298 spfx_add( spfx_get("ExpM"), px, py, vx, vy, l );
2299 else
2300 spfx_add( spfx_get("ExpS"), px, py, vx, vy, l );
2301 }
2302
2303 /* completely destroyed with final explosion */
2304 if (pilot_isFlag(pilot,PILOT_DEAD) && (pilot->ptimer < 0.)) {
2305 if (pilot->id==PLAYER_ID) /* player.p handled differently */
2307 pilot_delete(pilot);
2308 return;
2309 }
2310 }
2311 else if (pilot_isFlag(pilot, PILOT_NONTARGETABLE)) {
2312 pilot->itimer -= dt;
2313 if (pilot->itimer < 0.)
2314 pilot_rmFlag(pilot, PILOT_NONTARGETABLE);
2315 }
2316 else if (pilot->armour <= 0.) { /* PWNED */
2317 if (pilot_isFlag( pilot, PILOT_NODEATH ))
2318 pilot->armour = 1.;
2319 else
2320 pilot_dead( pilot, 0 ); /* start death stuff - dunno who killed. */
2321 }
2322
2323 /* Special handling for braking. */
2324 if (pilot_isFlag(pilot, PILOT_BRAKING )) {
2325 if (pilot_brake( pilot )) {
2326 if (pilot_isFlag(pilot, PILOT_COOLDOWN_BRAKE))
2327 pilot_cooldown( pilot, 1 );
2328 else {
2329 /* Normal braking is done (we're below MIN_VEL_ERR), now sidestep
2330 * normal physics and bring the ship to a near-complete stop.
2331 */
2332 pilot->solid->speed_max = 0.;
2333 pilot->solid->update( pilot->solid, dt );
2334
2335 if (VMOD(pilot->solid->vel) < 1e-1) {
2336 vectnull( &pilot->solid->vel ); /* Forcibly zero velocity. */
2337 pilot_rmFlag(pilot, PILOT_BRAKING);
2338 }
2339 }
2340 }
2341 }
2342
2343 /* Healing and energy usage is only done if not disabled. */
2344 if (!pilot_isDisabled(pilot)) {
2345 pilot_ewUpdateStealth(pilot, dt);
2346
2347 /* Pilot is still alive */
2348 pilot->armour += pilot->armour_regen * dt;
2349 if (pilot->armour > pilot->armour_max)
2350 pilot->armour = pilot->armour_max;
2351
2352 /* Regen shield */
2353 if (pilot->stimer <= 0.) {
2354 pilot->shield += pilot->shield_regen * dt;
2355 if (pilot->sbonus > 0.)
2356 pilot->shield += dt * (pilot->shield_regen * (pilot->sbonus / 1.5));
2357 pilot->shield = CLAMP( 0., pilot->shield_max, pilot->shield );
2358 }
2359
2360 /* Regen fuel. */
2361 pilot->fuel = MIN( pilot->fuel_max, pilot->fuel + pilot->stats.fuel_regen * dt );
2362
2363 /*
2364 * Using RC circuit energy loading.
2365 *
2366 * Calculations (using y = [0:1])
2367 *
2368 * \
2369 * y = 1 - exp( -x / tau ) |
2370 * y + dy = 1 - exp( -( x + dx ) / tau ) | ==>
2371 * /
2372 *
2373 * ==> dy = exp( -x / tau ) * ( 1 - exp( -dx / tau ) ==>
2374 * ==> [ dy = (1 - y) * ( 1 - exp( -dx / tau ) ) ]
2375 */
2376 pilot->energy += (pilot->energy_max - pilot->energy) *
2377 (1. - exp( -dt / pilot->energy_tau));
2378 pilot->energy -= pilot->energy_loss * dt;
2379 if (pilot->energy > pilot->energy_max)
2380 pilot->energy = pilot->energy_max;
2381 else if (pilot->energy < 0.) {
2382 pilot->energy = 0.;
2383 /* Stop all on outfits. */
2384 nchg += pilot_outfitOffAll( pilot );
2385 /* Run Lua stuff. */
2387 }
2388 }
2389
2390 /* Update effects. */
2391 nchg += effect_update( &pilot->effects, dt );
2392 if (pilot_isFlag( pilot, PILOT_DELETE ))
2393 return; /* It's possible for effects to remove the pilot causing future Lua to be unhappy. */
2394
2395 /* Must recalculate stats because something changed state. */
2396 if (nchg > 0)
2397 pilot_calcStats( pilot );
2398
2399 /* purpose fallthrough to get the movement like disabled */
2400 if (pilot_isDisabled(pilot) || pilot_isFlag(pilot, PILOT_COOLDOWN)) {
2401 /* Do the slow brake thing */
2402 pilot->solid->speed_max = 0.;
2403 pilot_setThrust( pilot, 0. );
2404 pilot_setTurn( pilot, 0. );
2405
2406 /* update the solid */
2407 pilot->solid->update( pilot->solid, dt );
2408
2409 gl_getSpriteFromDir( &pilot->tsx, &pilot->tsy,
2410 pilot->ship->gfx_space, pilot->solid->dir );
2411
2412 /* Engine glow decay. */
2413 if (pilot->engine_glow > 0.) {
2414 pilot->engine_glow -= pilot->speed / pilot->thrust * dt * pilot->solid->mass;
2415 if (pilot->engine_glow < 0.)
2416 pilot->engine_glow = 0.;
2417 }
2418
2419 /* Update the trail. */
2420 pilot_sample_trails( pilot, 0 );
2421
2422 return;
2423 }
2424
2425 /* Player damage decay. */
2426 if (pilot->player_damage > 0.)
2427 pilot->player_damage -= dt * PILOT_HOSTILE_DECAY;
2428 else
2429 pilot->player_damage = 0.;
2430
2431 /* Pilot is board/refueling. Hack to match speeds. */
2432 if (pilot_isFlag(pilot, PILOT_REFUELBOARDING))
2433 pilot_refuel(pilot, dt);
2434
2435 /* Pilot is boarding its target. Hack to match speeds. */
2436 if (pilot_isFlag(pilot, PILOT_BOARDING)) {
2437 if (target==NULL)
2438 pilot_rmFlag(pilot, PILOT_BOARDING);
2439 else {
2440 /* Match speeds. */
2441 pilot->solid->vel = target->solid->vel;
2442
2443 /* See if boarding is finished. */
2444 if (pilot->ptimer < 0.)
2445 pilot_boardComplete(pilot);
2446 }
2447 }
2448
2449 /* Update weapons. */
2450 pilot_weapSetUpdate( pilot );
2451
2452 if (!pilot_isFlag(pilot, PILOT_HYPERSPACE)) { /* limit the speed */
2453
2454 /* pilot is afterburning */
2455 if (pilot_isFlag(pilot, PILOT_AFTERBURNER)) {
2456 /* Heat up the afterburner. */
2457 pilot_heatAddSlotTime(pilot, pilot->afterburner, dt);
2458
2459 /* If the afterburner's efficiency is reduced to 0, shut it off. */
2461 pilot->afterburner->outfit->u.afb.heat_base,
2462 pilot->afterburner->outfit->u.afb.heat_cap)==0)
2463 pilot_afterburnOver(pilot);
2464 else {
2465 double efficiency, thrust;
2466
2467 if (pilot->id == PLAYER_ID)
2468 spfx_shake( 0.75*SPFX_SHAKE_DECAY * dt); /* shake goes down at quarter speed */
2469 efficiency = pilot_heatEfficiencyMod( pilot->afterburner->heat_T,
2470 pilot->afterburner->outfit->u.afb.heat_base,
2471 pilot->afterburner->outfit->u.afb.heat_cap );
2472 thrust = MIN( 1., pilot->afterburner->outfit->u.afb.mass_limit / pilot->solid->mass ) * efficiency;
2473
2474 /* Adjust speed. Speed bonus falls as heat rises. */
2475 pilot->solid->speed_max = pilot->speed * (1. +
2476 pilot->afterburner->outfit->u.afb.speed * thrust);
2477
2478 /* Adjust thrust. Thrust bonus falls as heat rises. */
2479 pilot_setThrust(pilot, 1. + pilot->afterburner->outfit->u.afb.thrust * thrust);
2480 }
2481 }
2482 else
2483 pilot->solid->speed_max = pilot->speed;
2484 }
2485 else
2486 pilot->solid->speed_max = -1.; /* Disables max speed. */
2487
2488 /* Set engine glow. */
2489 if (pilot->solid->thrust > 0.) {
2490 /*pilot->engine_glow += pilot->thrust / pilot->speed * dt;*/
2491 pilot->engine_glow += pilot->speed / pilot->thrust * dt * pilot->solid->mass;
2492 if (pilot->engine_glow > 1.)
2493 pilot->engine_glow = 1.;
2494 }
2495 else if (pilot->engine_glow > 0.) {
2496 pilot->engine_glow -= pilot->speed / pilot->thrust * dt * pilot->solid->mass;
2497 if (pilot->engine_glow < 0.)
2498 pilot->engine_glow = 0.;
2499 }
2500
2501 /* Update the solid, must be run after limit_speed. */
2502 pilot->solid->update( pilot->solid, dt );
2503 gl_getSpriteFromDir( &pilot->tsx, &pilot->tsy,
2504 pilot->ship->gfx_space, pilot->solid->dir );
2505
2506 /* See if there is commodities to gather. */
2507 if (!pilot_isDisabled(pilot))
2508 gatherable_gather( pilot );
2509
2510 /* Update the trail. */
2511 pilot_sample_trails( pilot, 0 );
2512
2513 /* Update outfits if necessary. */
2514 pilot->otimer += dt;
2515 while (pilot->otimer >= PILOT_OUTFIT_LUA_UPDATE_DT) {
2516 pilot_outfitLUpdate( pilot, PILOT_OUTFIT_LUA_UPDATE_DT );
2517 if (pilot_isFlag( pilot, PILOT_DELETE ))
2518 return; /* Same as with the effects, it is theoretically possible for the outfit to remove the pilot. */
2519 pilot->otimer -= PILOT_OUTFIT_LUA_UPDATE_DT;
2520 }
2521}
2522
2529void pilot_sample_trails( Pilot* p, int none )
2530{
2531 double d2, cx, cy, dircos, dirsin;
2532 TrailMode mode;
2533
2534 /* Ignore for simulation. */
2536 return;
2537
2538 /* No trails to sample. */
2539 if (p->trail == NULL)
2540 return;
2541
2542 /* Skip if far away (pretty heuristic-based but seems to work). */
2543 cam_getPos( &cx, &cy );
2544 d2 = pow2(cx-p->solid->pos.x) + pow2(cy-p->solid->pos.y);
2545 if (d2 > pow2( MAX(SCREEN_W,SCREEN_H) / conf.zoom_far * 2. ))
2546 return;
2547
2548 dircos = cos(p->solid->dir);
2549 dirsin = sin(p->solid->dir);
2550
2551 /* Identify the emission type. */
2552 if (none)
2553 mode = MODE_NONE;
2554 else {
2555 if (pilot_isFlag(p, PILOT_HYPERSPACE) || pilot_isFlag(p, PILOT_HYP_END))
2556 mode = MODE_JUMPING;
2557 else if (pilot_isFlag(p, PILOT_AFTERBURNER))
2558 mode = MODE_AFTERBURN;
2559 else if (p->engine_glow > 0.)
2560 mode = MODE_GLOW;
2561 else
2562 mode = MODE_IDLE;
2563 }
2564
2565 /* Compute the engine offset and decide where to draw the trail. */
2566 for (int i=0, g=0; g<array_size(p->ship->trail_emitters); g++) {
2567 double dx, dy, prod;
2568
2569 if (!pilot_trail_generated( p, g ))
2570 continue;
2571
2572 p->trail[i]->ontop = 0;
2573 if (!(p->ship->trail_emitters[g].always_under) && (dirsin > 0)) {
2574 /* See if the trail's front (tail) is in front of the ship. */
2575 prod = (trail_front( p->trail[i] ).x - p->solid->pos.x) * dircos +
2576 (trail_front( p->trail[i] ).y - p->solid->pos.y) * dirsin;
2577
2578 p->trail[i]->ontop = (prod < 0);
2579 }
2580
2581 dx = p->ship->trail_emitters[g].x_engine * dircos -
2582 p->ship->trail_emitters[g].y_engine * dirsin;
2583 dy = p->ship->trail_emitters[g].x_engine * dirsin +
2584 p->ship->trail_emitters[g].y_engine * dircos +
2585 p->ship->trail_emitters[g].h_engine;
2586 spfx_trail_sample( p->trail[i++], p->solid->pos.x + dx, p->solid->pos.y + dy*M_SQRT1_2, mode, mode==MODE_NONE );
2587 }
2588}
2589
2593static int pilot_trail_generated( Pilot* p, int generator )
2594{
2595 return !p->ship->trail_emitters[generator].trail_spec->nebula || cur_system->nebu_density>0;
2596}
2597
2604{
2605 PilotOutfitSlot* dockslot;
2606
2607 /* Don't double delete, just in case. */
2608 if (pilot_isFlag( p, PILOT_DELETE ))
2609 return;
2610
2611 /* Stop all outfits. */
2613
2614 /* Handle Lua outfits. */
2616
2617 /* Remove from parent's escort list */
2618 if (p->parent != 0) {
2619 Pilot *leader = pilot_get(p->parent);
2620 if (leader != NULL)
2621 escort_rmList(leader, p->id);
2622 }
2623
2624 /* Remove faction if necessary. */
2625 if (p->presence > 0) {
2626 system_rmCurrentPresence( cur_system, p->faction, p->presence );
2627 p->presence = 0;
2628 }
2629
2630 /* Unmark as deployed if necessary */
2631 dockslot = pilot_getDockSlot( p );
2632 if (dockslot != NULL) {
2633 dockslot->u.ammo.deployed--;
2634 p->dockpilot = 0;
2635 p->dockslot = -1;
2636 }
2637
2638 /* Set flag to mark for deletion. */
2639 pilot_setFlag(p, PILOT_DELETE);
2640}
2641
2648static void pilot_hyperspace( Pilot* p, double dt )
2649{
2650 StarSystem *sys;
2651 double a, diff;
2652 int can_hyp;
2653 HookParam hparam;
2654
2655 /* pilot is actually in hyperspace */
2656 if (pilot_isFlag(p, PILOT_HYPERSPACE)) {
2657
2658 /* Time to play sound. */
2659 if ((p->id == PLAYER_ID) &&
2660 (p->ptimer < sound_getLength(snd_hypPowUpJump)) &&
2661 (p->timer[0] == -1.)) {
2662 p->timer[0] = -2.;
2664 }
2665
2666 /* has jump happened? */
2667 if (p->ptimer < 0.) {
2668 pilot_setFlag( p, PILOT_HYP_END );
2669 pilot_setThrust( p, 0. );
2670 if (p->id == PLAYER_ID) /* player.p just broke hyperspace */
2671 player_setFlag( PLAYER_HOOK_HYPER );
2672 else {
2673 hparam.type = HOOK_PARAM_JUMP;
2674 hparam.u.lj.srcid = cur_system->id;
2675 hparam.u.lj.destid = cur_system->jumps[ p->nav_hyperspace ].targetid;
2676
2677 /* Should be run before messing with delete flag. */
2678 pilot_runHookParam( p, PILOT_HOOK_JUMP, &hparam, 1 );
2679
2680 pilot_delete(p);
2681 }
2682 return;
2683 }
2684
2685 /* keep acceling - hyperspace uses much bigger accel */
2686 pilot_setThrust( p, HYPERSPACE_THRUST*p->solid->mass/p->thrust );
2687 }
2688 /* engines getting ready for the jump */
2689 else if (pilot_isFlag(p, PILOT_HYP_BEGIN)) {
2690
2691 /* Make sure still within range. */
2692 can_hyp = space_canHyperspace( p );
2693 if (!can_hyp) {
2695
2696 if (pilot_isPlayer(p))
2697 if (!player_isFlag(PLAYER_AUTONAV))
2698 player_message( "#r%s", _("Strayed too far from jump point: jump aborted.") );
2699 }
2700 else if (pilot_isFlag(p,PILOT_AFTERBURNER)) {
2702
2703 if (pilot_isPlayer(p))
2704 if (!player_isFlag(PLAYER_AUTONAV))
2705 player_message( "#r%s", _("Afterburner active: jump aborted.") );
2706 }
2707 else {
2708 if (p->ptimer < 0.) { /* engines ready */
2709 p->ptimer = HYPERSPACE_FLY_DELAY * p->stats.jump_delay;
2710 pilot_setFlag(p, PILOT_HYPERSPACE);
2711 if (p->id == PLAYER_ID)
2712 p->timer[0] = -1.;
2713 }
2714 }
2715 }
2716 /* pilot is getting ready for hyperspace */
2717 else {
2718 /* Make sure still within range. */
2719 can_hyp = space_canHyperspace( p );
2720 if (!can_hyp) {
2722
2723 if (pilot_isPlayer(p))
2724 if (!player_isFlag(PLAYER_AUTONAV))
2725 player_message( "#r%s", _("Strayed too far from jump point: jump aborted.") );
2726 }
2727 else {
2728 /* If the ship needs to charge up its hyperdrive, brake. */
2729 if (!p->stats.misc_instant_jump &&
2730 !pilot_isFlag(p, PILOT_HYP_BRAKE) && !pilot_isStopped(p))
2731 pilot_brake(p);
2732 /* face target */
2733 else {
2734 /* Done braking or no braking required. */
2735 pilot_setFlag( p, PILOT_HYP_BRAKE );
2736 pilot_setThrust( p, 0. );
2737
2738 /* Face system headed to. */
2739 sys = cur_system->jumps[p->nav_hyperspace].target;
2740 a = ANGLE( sys->pos.x - cur_system->pos.x, sys->pos.y - cur_system->pos.y );
2741 diff = pilot_face( p, a );
2742
2743 if (ABS(diff) < MAX_DIR_ERR) { /* we can now prepare the jump */
2744 if (jp_isFlag( &cur_system->jumps[p->nav_hyperspace], JP_EXITONLY )) {
2745 WARN( _("Pilot '%s' trying to jump through exit-only jump from '%s' to '%s'"),
2746 p->name, cur_system->name, sys->name );
2747 }
2748 else {
2749 pilot_setTurn( p, 0. );
2750 p->ptimer = HYPERSPACE_ENGINE_DELAY * p->stats.jump_warmup * !p->stats.misc_instant_jump;
2751 pilot_setFlag(p, PILOT_HYP_BEGIN);
2752 /* Player plays sound. */
2753 if ((p->id == PLAYER_ID) && !p->stats.misc_instant_jump)
2755 }
2756 }
2757 }
2758 }
2759 }
2760
2761 if (pilot_isPlayer(p))
2762 player_updateSpecific( p, dt );
2763}
2764
2773{
2774 if (pilot_isFlag(p, PILOT_HYPERSPACE))
2775 return;
2776
2777 if (pilot_isFlag(p, PILOT_HYP_BEGIN)) {
2778 /* Player plays sound. */
2779 if (p->id == PLAYER_ID) {
2782 }
2783 }
2784 pilot_rmFlag(p, PILOT_HYP_BEGIN);
2785 pilot_rmFlag(p, PILOT_HYP_BRAKE);
2786 pilot_rmFlag(p, PILOT_HYP_PREP);
2787}
2788
2795{
2796 Pilot *target = pilot_getTarget( p );
2797 /* Check to see if target exists, remove flag if not. */
2798 if (target == NULL) {
2799 pilot_rmFlag(p, PILOT_REFUELING);
2800 return 0;
2801 }
2802
2803 /* Conditions are the same as boarding, except disabled. */
2804 if (vec2_dist(&p->solid->pos, &target->solid->pos) >
2805 target->ship->gfx_space->sw * PILOT_SIZE_APPROX )
2806 return 0;
2807 else if (vec2_dist2( &p->solid->vel, &target->solid->vel ) > pow2(MAX_HYPERSPACE_VEL))
2808 return 0;
2809
2810 /* Now start the boarding to refuel. */
2811 pilot_setFlag(p, PILOT_REFUELBOARDING);
2812 p->ptimer = PILOT_REFUEL_TIME; /* Use timer to handle refueling. */
2813 return 1;
2814}
2815
2822static void pilot_refuel( Pilot *p, double dt )
2823{
2824 (void) dt;
2825 /* Check to see if target exists, remove flag if not. */
2826 Pilot *target = pilot_getTarget( p );
2827 if (target == NULL) {
2828 pilot_rmFlag(p, PILOT_REFUELBOARDING);
2829 pilot_rmFlag(p, PILOT_REFUELING);
2830 return;
2831 }
2832
2833 /* Match speeds. */
2834 p->solid->vel = target->solid->vel;
2835
2836 /* Check to see if done. */
2837 if (p->ptimer < 0.) {
2838 /* Move fuel. */
2839 double amount = MIN( p->fuel, p->refuel_amount );
2840 amount = MIN( amount, target->fuel_max-target->fuel );
2841 p->fuel -= amount;
2842 target->fuel += amount;
2843
2844 pilot_rmFlag(p, PILOT_REFUELBOARDING);
2845 pilot_rmFlag(p, PILOT_REFUELING);
2846 }
2847}
2848
2856{
2857 int stu = (int)(NT_PERIOD_SECONDS * p->stats.jump_delay);
2858 return ntime_create( 0, 0, stu );
2859}
2860
2867void pilot_untargetAsteroid( int anchor, int asteroid )
2868{
2869 for (int i=0; i < array_size(pilot_stack); i++) {
2870 if ((pilot_stack[i]->nav_asteroid == asteroid) && (pilot_stack[i]->nav_anchor == anchor)) {
2871 pilot_stack[i]->nav_asteroid = -1;
2872 pilot_stack[i]->nav_anchor = -1;
2873 }
2874 }
2875}
2876
2880int pilot_numOutfit( const Pilot *p, const Outfit *o )
2881{
2882 int q = 0;
2883 for (int i=0; i<array_size(p->outfits); i++) {
2884 if (p->outfits[i]->outfit == o)
2885 q++;
2886 }
2887 return q;
2888}
2889
2897int pilot_hasCredits( Pilot *p, credits_t amount )
2898{
2899 if (amount < 0)
2900 return 1;
2901 return (amount <= p->credits);
2902}
2903
2911credits_t pilot_modCredits( Pilot *p, credits_t amount )
2912{
2913 if (amount > 0) {
2914 if (CREDITS_MAX - p->credits <= amount)
2915 p->credits = CREDITS_MAX;
2916 else
2917 p->credits += amount;
2918 }
2919 else if (amount < 0) {
2920 /* ABS(CREDITS_MIN) doesn't work properly because it might be
2921 * -2,147,483,648, which ABS will try to convert to 2,147,483,648.
2922 * Problem is, that value would be represented like this in
2923 * binary:
2924 *
2925 * 10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
2926 *
2927 * Which is actually -2,147,483,648, causing the condition
2928 * ABS(amount) >= p->credits to return false (since -2,147,483,648
2929 * is less than any amount of credits the player could have). */
2930 if ( (amount <= CREDITS_MIN) || (ABS(amount) >= p->credits) )
2931 p->credits = 0;
2932 else
2933 p->credits += amount;
2934 }
2935 return p->credits;
2936}
2937
2952static void pilot_init( Pilot* pilot, const Ship* ship, const char* name, int faction,
2953 const double dir, const vec2* pos, const vec2* vel,
2954 const PilotFlags flags, unsigned int dockpilot, int dockslot )
2955{
2956 PilotOutfitSlot *dslot;
2957 PilotOutfitSlot **pilot_list_ptr[] = { &pilot->outfit_structure, &pilot->outfit_utility, &pilot->outfit_weapon };
2958 ShipOutfitSlot *ship_list[] = { ship->outfit_structure, ship->outfit_utility, ship->outfit_weapon };
2959
2960 /* Clear memory. */
2961 memset(pilot, 0, sizeof(Pilot));
2962
2963 /* Defaults. */
2964 pilot->lua_mem = LUA_NOREF;
2965 pilot->autoweap = 1;
2966 pilot->aimLines = 0;
2967 pilot->dockpilot = dockpilot;
2968 pilot->parent = dockpilot; /* leader will default to mothership if exists. */
2969 pilot->dockslot = dockslot;
2970 ss_statsInit( &pilot->intrinsic_stats );
2971
2972 /* Basic information. */
2973 pilot->ship = ship;
2974 pilot->name = strdup( (name==NULL) ? _(ship->name) : name );
2975
2976 /* faction */
2977 pilot->faction = faction;
2978
2979 /* solid */
2980 pilot->solid = solid_create(ship->mass, dir, pos, vel, SOLID_UPDATE_RK4);
2981
2982 /* First pass to make sure requirements make sense. */
2983 pilot->armour = pilot->armour_max = 1.; /* hack to have full armour */
2984 pilot->shield = pilot->shield_max = 1.; /* ditto shield */
2985 pilot->energy = pilot->energy_max = 1.; /* ditto energy */
2986 pilot->fuel = pilot->fuel_max = 1.; /* ditto fuel */
2987 pilot_calcStats(pilot);
2988 pilot->stress = 0.; /* No stress. */
2989
2990 /* Allocate outfit memory. */
2992 /* First pass copy data. */
2993 for (int i=0; i<3; i++) {
2994 *pilot_list_ptr[i] = array_create_size( PilotOutfitSlot, array_size(ship_list[i]) );
2995 for (int j=0; j<array_size(ship_list[i]); j++) {
2996 PilotOutfitSlot *slot = &array_grow( pilot_list_ptr[i] );
2997 memset( slot, 0, sizeof(PilotOutfitSlot) );
2998 slot->id = array_size(pilot->outfits);
2999 slot->sslot = &ship_list[i][j];
3000 array_push_back( &pilot->outfits, slot );
3001 if (pilot_list_ptr[i] != &pilot->outfit_weapon)
3002 slot->weapset = -1;
3003 if (slot->sslot->data != NULL)
3004 pilot_addOutfitRaw( pilot, slot->sslot->data, slot );
3005 }
3006 }
3007 array_shrink( &pilot->outfits );
3008
3009 /* We must set the weapon auto in case some of the outfits had a default
3010 * weapon equipped. */
3011 if (!pilot_isFlagRaw(flags, PILOT_PLAYER))
3012 pilot_weaponAuto(pilot);
3013
3014 /* cargo - must be set before calcStats */
3015 pilot->cargo_free = pilot->ship->cap_cargo; /* should get redone with calcCargo */
3016
3017 /* Initialize heat. */
3018 pilot_heatReset( pilot );
3019
3020 /* Set the pilot stats based on their ship and outfits */
3021 pilot_calcStats( pilot );
3022
3023 /* Update dynamic electronic warfare (static should have already been done). */
3024 pilot_ewUpdateDynamic( pilot, 0. );
3025
3026 /* Heal up the ship. */
3027 pilot->armour = pilot->armour_max;
3028 pilot->shield = pilot->shield_max;
3029 pilot->energy = pilot->energy_max;
3030 pilot->fuel = pilot->fuel_max;
3031
3032 /* Mark as deployed if needed */
3033 dslot = pilot_getDockSlot( pilot );
3034 if (dslot != NULL)
3035 dslot->u.ammo.deployed++;
3036
3037 /* Safety check. */
3038#ifdef DEBUGGING
3039 char message[STRMAX_SHORT];
3040 int notworthy = pilot_reportSpaceworthy( pilot, message, sizeof(message) );
3041 if (notworthy) {
3042 DEBUG( _("Pilot '%s' failed safety check: %s"), pilot->name, message );
3043 for (int i=0; i<array_size(pilot->outfits); i++) {
3044 if (pilot->outfits[i]->outfit != NULL)
3045 DEBUG(_(" [%d] %s"), i, _(pilot->outfits[i]->outfit->name) );
3046 }
3047 }
3048#endif /* DEBUGGING */
3049
3050 /* Copy pilot flags. */
3051 pilot_copyFlagsRaw(pilot->flags, flags);
3052
3053 /* Clear timers. */
3054 pilot_clearTimers(pilot);
3055
3056 /* Update the x and y sprite positions. */
3057 gl_getSpriteFromDir( &pilot->tsx, &pilot->tsy,
3058 pilot->ship->gfx_space, pilot->solid->dir );
3059
3060 /* Fill ammo. */
3061 pilot_fillAmmo( pilot );
3062
3063 /* Targets. */
3064 pilot_setTarget( pilot, pilot->id ); /* No target. */
3065 pilot->nav_spob = -1;
3066 pilot->nav_hyperspace = -1;
3067 pilot->nav_anchor = -1;
3068 pilot->nav_asteroid = -1;
3069
3070 /* Check takeoff. */
3071 if (pilot_isFlagRaw( flags, PILOT_TAKEOFF )) {
3072 pilot->landing_delay = PILOT_TAKEOFF_DELAY * pilot->ship->dt_default;
3073 pilot->ptimer = pilot->landing_delay;
3074 }
3075
3076 /* Create empty table for messages. */
3077 lua_newtable(naevL);
3078 pilot->messages = luaL_ref(naevL, LUA_REGISTRYINDEX);
3079
3080 pilot->shoot_indicator = 0;
3081}
3082
3088void pilot_reset( Pilot* pilot )
3089{
3090 /* Clean up flag.s */
3091 for (int i=PILOT_NOCLEAR+1; i<PILOT_FLAGS_MAX; i++)
3092 pilot->flags[i] = 0;
3093
3094 /* Initialize heat. */
3095 pilot_heatReset( pilot );
3096
3097 /* Set the pilot stats based on their ship and outfits */
3098 pilot_calcStats( pilot );
3099
3100 /* Update dynamic electronic warfare (static should have already been done). */
3101 pilot_ewUpdateDynamic( pilot, 0. );
3102
3103 /* Clear timers. */
3104 pilot_clearTimers(pilot);
3105
3106 /* Update the x and y sprite positions. */
3107 gl_getSpriteFromDir( &pilot->tsx, &pilot->tsy,
3108 pilot->ship->gfx_space, pilot->solid->dir );
3109
3110 /* Heal up. */
3111 pilot_healLanded( pilot );
3112
3113 /* Targets. */
3114 pilot_setTarget( pilot, pilot->id ); /* No target. */
3115 pilot->nav_spob = -1;
3116 pilot->nav_hyperspace = -1;
3117 pilot->nav_anchor = -1;
3118 pilot->nav_asteroid = -1;
3119
3120 /* AI */
3121 pilot->shoot_indicator = 0;
3122
3123 /* Run Lua stuff. */
3124 pilot_outfitLInitAll( pilot );
3125}
3126
3130static void pilot_init_trails( Pilot* p )
3131{
3132 int n = array_size(p->ship->trail_emitters);
3133 if (p->trail == NULL)
3134 p->trail = array_create_size( Trail_spfx*, n );
3135
3136 for (int g=0; g<n; g++)
3137 if (pilot_trail_generated( p, g ))
3138 array_push_back( &p->trail, spfx_trail_create( p->ship->trail_emitters[g].trail_spec ) );
3139 array_shrink( &p->trail );
3140}
3141
3151unsigned int pilot_create( const Ship* ship, const char* name, int faction, const char *ai,
3152 const double dir, const vec2* pos, const vec2* vel,
3153 const PilotFlags flags, unsigned int dockpilot, int dockslot )
3154{
3155 Pilot *p;
3156
3157 /* Allocate pilot memory. */
3158 p = malloc(sizeof(Pilot));
3159 if (p == NULL) {
3160 WARN(_("Unable to allocate memory"));
3161 return 0;
3162 }
3163
3164 /* Set the pilot in the stack -- must be there before initializing */
3166
3167 /* Initialize the pilot. */
3168 pilot_init( p, ship, name, faction, dir, pos, vel, flags, dockpilot, dockslot );
3169
3170 /* Set the ID. */
3171 if (pilot_isFlagRaw(flags, PILOT_PLAYER)) { /* Set player ID. TODO should probably be fixed to something better someday. */
3172 p->id = PLAYER_ID;
3173 qsort( pilot_stack, array_size(pilot_stack), sizeof(Pilot*), pilot_cmp );
3174 }
3175 else
3176 p->id = ++pilot_id; /* new unique pilot id based on pilot_id, can't be 0 */
3177
3178 /* Initialize AI if applicable. */
3179 if (ai == NULL)
3180 ai = faction_default_ai( faction );
3181 if (ai != NULL)
3182 ai_pinit( p, ai ); /* Must run before ai_create */
3183
3184 /* Animated trail. */
3185 pilot_init_trails( p );
3186
3187 /* Run Lua stuff. */
3189
3190 /* Pilot creation hook. */
3191 pilot_runHook( p, PILOT_HOOK_CREATION );
3192
3193 return p->id;
3194}
3195
3205Pilot* pilot_createEmpty( const Ship* ship, const char* name,
3206 int faction, PilotFlags flags )
3207{
3208 Pilot *dyn = malloc(sizeof(Pilot));
3209 if (dyn == NULL) {
3210 WARN(_("Unable to allocate memory"));
3211 return 0;
3212 }
3213 pilot_init( dyn, ship, name, faction, 0., NULL, NULL, flags, 0, 0 );
3214 return dyn;
3215}
3216
3223unsigned int pilot_clone( const Pilot *ref )
3224{
3225 Pilot *dyn, **p;
3226 PilotFlags pf;
3227
3228 pilot_clearFlagsRaw( &pf );
3229 pilot_setFlagRaw( pf, PILOT_NO_OUTFITS );
3230
3231 /* Allocate pilot memory. */
3232 dyn = malloc(sizeof(Pilot));
3233 if (dyn == NULL) {
3234 WARN(_("Unable to allocate memory"));
3235 return 0;
3236 }
3237
3238 /* Set the pilot in the stack -- must be there before initializing */
3239 p = &array_grow( &pilot_stack );
3240 *p = dyn;
3241
3242 /* Initialize the pilot. */
3243 pilot_init( dyn, ref->ship, ref->name, ref->faction,
3244 ref->solid->dir, &ref->solid->pos, &ref->solid->vel, pf, 0, 0 );
3245
3246 /* Add outfits over. */
3247 for (int i=0; i<array_size(ref->outfits); i++)
3248 if (ref->outfits[i]->outfit != NULL)
3249 pilot_addOutfitRaw( dyn, ref->outfits[i]->outfit, dyn->outfits[i] );
3250 for (int i=0; i<array_size(ref->outfit_intrinsic); i++)
3252
3253 /* Reset the pilot. */
3254 pilot_reset( dyn );
3255
3256 return dyn->id;
3257}
3258
3262unsigned int pilot_addStack( Pilot *p )
3263{
3264 p->id = ++pilot_id; /* new unique pilot id based on pilot_id, can't be 0 */
3265 pilot_setFlag( p, PILOT_NOFREE );
3266
3268
3269 /* Have to reset after adding to stack, as some Lua functions will run code on the pilot. */
3270 pilot_reset( p );
3271
3272 /* Animated trail. */
3273 pilot_init_trails( p );
3274
3275#if DEBUGGING
3276 for (int i=1; i<array_size(pilot_stack); i++)
3277 if (pilot_stack[i]==pilot_stack[i-1])
3278 WARN(_("Duplicate pilots on stack!"));
3279#endif /* DEBUGGING */
3280
3281 return p->id;
3282}
3283
3288{
3289 for (int j=0; j<array_size(p->trail); j++)
3290 spfx_trail_remove( p->trail[j] );
3291 array_erase( &p->trail, array_begin(p->trail), array_end(p->trail) );
3292 pilot_init_trails( p );
3293}
3294
3301{
3302 int i = pilot_getStackPos( PLAYER_ID);
3303 int l = pilot_getStackPos( after->id );
3304
3305 if (i < 0) { /* No existing player ID. */
3306 if (l < 0) /* No existing pilot, have to create. */
3307 array_push_back( &pilot_stack, after );
3308 }
3309 else { /* Player pilot already exists. */
3310 if (l >= 0)
3311 pilot_delete( pilot_stack[i] ); /* Both player and after are on stack. Remove player. */
3312 else
3313 pilot_stack[i] = after; /* after overwrites player. */
3314 }
3315 after->id = PLAYER_ID;
3316 qsort( pilot_stack, array_size(pilot_stack), sizeof(Pilot*), pilot_cmp );
3317
3318 /* Set up stuff. */
3319 player.p = after;
3320 pilot_clearTrails( after );
3321
3322 /* Initialize AI as necessary. */
3323 ai_pinit( after, "player" );
3324
3325 /* Set player flag. */
3326 pilot_setFlag( after, PILOT_PLAYER );
3327 pilot_setFlag( after, PILOT_NOFREE );
3328
3329 /* Run Lua stuff. */
3330 pilot_outfitLInitAll( after );
3331
3332 return after;
3333}
3334
3345void pilot_choosePoint( vec2 *vp, Spob **spob, JumpPoint **jump, int lf, int ignore_rules, int guerilla )
3346{
3347 int *ind;
3348 JumpPoint **validJumpPoints;
3349
3350 /* Initialize. */
3351 *spob = NULL;
3352 *jump = NULL;
3353 vectnull( vp );
3354
3355 /* Build landable spob table. */
3356 ind = array_create_size( int, array_size(cur_system->spobs) );
3357 for (int i=0; i<array_size(cur_system->spobs); i++) {
3358 Spob *pnt = cur_system->spobs[i];
3359 if (spob_hasService( pnt, SPOB_SERVICE_INHABITED ) &&
3360 !areEnemies( lf, pnt->presence.faction ))
3361 array_push_back( &ind, i );
3362 }
3363
3364 /* Build jumpable jump table. */
3365 validJumpPoints = array_create_size( JumpPoint*, array_size(cur_system->jumps) );
3366 if (array_size(cur_system->jumps) > 0) {
3367 for (int i=0; i<array_size(cur_system->jumps); i++) {
3368 /* The jump into the system must not be exit-only, and unless
3369 * ignore_rules is set, must also be non-hidden
3370 * (excepted if the pilot is guerilla) and have faction
3371 * presence matching the pilot's on the remote side.
3372 */
3373 JumpPoint *target = cur_system->jumps[i].returnJump;
3374 double limit = 0.;
3375 if (guerilla) {/* Test enemy presence on the other side. */
3376 const int *fact = faction_getEnemies( lf );
3377 for (int j=0; j<array_size(fact); j++)
3378 limit += system_getPresence( cur_system->jumps[i].target, fact[j] );
3379 }
3380
3381 if (!jp_isFlag( target, JP_EXITONLY ) && (ignore_rules ||
3382 ((!jp_isFlag( &cur_system->jumps[i], JP_HIDDEN ) || guerilla) &&
3383 (system_getPresence( cur_system->jumps[i].target, lf ) > limit))))
3384 array_push_back(&validJumpPoints, target);
3385 }
3386 }
3387
3388 /* Unusual case no landable nor presence, we'll just jump in randomly if possible. */
3389 if (array_size(ind)==0 && array_size(validJumpPoints)==0) {
3390 if (guerilla) /* Guerilla ships are created far away in deep space. */
3391 vec2_pset ( vp, 1.5*cur_system->radius, RNGF()*2*M_PI );
3392 else if (array_size(cur_system->jumps) > 0) {
3393 for (int i=0; i<array_size(cur_system->jumps); i++) {
3394 JumpPoint *jp = &cur_system->jumps[i];
3395 if (!jp_isFlag( jp->returnJump, JP_EXITONLY ))
3396 array_push_back(&validJumpPoints, jp->returnJump);
3397 }
3398 }
3399 else {
3400 WARN(_("Creating pilot in system with no jumps nor spobs to take off from!"));
3401 vectnull( vp );
3402 }
3403 }
3404
3405 /* Calculate jump chance. */
3406 if (array_size(ind)>0 || array_size(validJumpPoints)>0) {
3407 double chance = array_size(validJumpPoints);
3408 chance = chance / (chance + array_size(ind));
3409
3410 /* Random jump in. */
3411 if ((RNGF() <= chance) && (validJumpPoints != NULL))
3412 *jump = validJumpPoints[ RNG_BASE(0,array_size(validJumpPoints)-1) ];
3413 /* Random take off. */
3414 else if (array_size(ind) != 0)
3415 *spob = cur_system->spobs[ ind[ RNG_BASE(0, array_size(ind)-1) ] ];
3416 }
3417
3418 /* Free memory allocated. */
3419 array_free( ind );
3420 array_free(validJumpPoints);
3421}
3422
3429{
3430 /* Clear some useful things. */
3432 effect_cleanup( p->effects );
3433 p->effects = NULL;
3434 pilot_cargoRmAll( p, 1 );
3435 escort_freeList(p);
3436
3437 /* If hostile, must remove counter. */
3438 pilot_rmHostile(p);
3439
3440 /* Free animated trail. */
3441 for (int i=0; i<array_size(p->trail); i++) {
3442 p->trail[i]->ontop = 0;
3443 spfx_trail_remove( p->trail[i] );
3444 }
3445 array_free(p->trail);
3446 p->trail = NULL;
3447
3448 /* We don't actually free internals of the pilot once we cleaned up stuff. */
3449 if (pilot_isFlag( p, PILOT_NOFREE )) {
3450 p->id = 0; /* Invalidate ID. */
3451 return;
3452 }
3453
3454 lvar_freeArray( p->shipvar );
3455
3457
3458 array_free(p->outfits);
3459 array_free(p->outfit_structure);
3460 array_free(p->outfit_utility);
3461 array_free(p->outfit_weapon);
3462 array_free(p->outfit_intrinsic);
3463
3464 /* Clean up data. */
3465 if (p->ai != NULL)
3466 ai_destroy(p); /* Must be destroyed first if applicable. */
3467
3468 free(p->name);
3469 /* Case if pilot is the player. */
3470 if (player.p==p) {
3471 player.p = NULL;
3472 player.ps.p = NULL;
3473 }
3474 solid_free(p->solid);
3475 free(p->mounted);
3476
3477 escort_freeList(p);
3478
3479 free(p->comm_msg);
3480
3481 /* Free messages. */
3482 luaL_unref(naevL, p->messages, LUA_REGISTRYINDEX);
3483
3484#ifdef DEBUGGING
3485 memset( p, 0, sizeof(Pilot) );
3486#endif /* DEBUGGING */
3487
3488 free(p);
3489}
3490
3496static void pilot_erase( Pilot *p )
3497{
3498 int i = pilot_getStackPos( p->id );
3499 pilot_free(p);
3501}
3502
3507{
3508 int i = pilot_getStackPos( p->id );
3509#ifdef DEBUGGING
3510 if (i < 0)
3511 WARN(_("Trying to remove non-existent pilot '%s' from stack!"), p->name);
3512#endif /* DEBUGGING */
3513 p->id = 0;
3515}
3516
3520void pilots_init (void)
3521{
3523}
3524
3528void pilots_free (void)
3529{
3531
3532 /* First pass to stop outfits. */
3533 for (int i=0; i < array_size(pilot_stack); i++) {
3534 /* Stop all outfits. */
3536 /* Handle Lua outfits. */
3538 }
3539
3540 /* Free pilots. */
3541 for (int i=0; i < array_size(pilot_stack); i++)
3544 pilot_stack = NULL;
3545 player.p = NULL;
3546 free( player.ps.acquired );
3547 memset( &player.ps, 0, sizeof(PlayerShip_t) );
3548}
3549
3555void pilots_clean( int persist )
3556{
3557 int persist_count = 0;
3558
3559 /* First pass to stop outfits without clearing stuff - this can call all
3560 * sorts of Lua stuff. */
3561 for (int i=0; i<array_size(pilot_stack); i++) {
3562 Pilot *p = pilot_stack[i];
3563 if (p == player.p &&
3564 (persist && pilot_isFlag(p, PILOT_PERSIST)))
3565 continue;
3566 /* Stop all outfits. */
3567 pilot_outfitOffAll( p );
3568 /* Handle Lua outfits. */
3570 }
3571
3572 /* Here we actually clean up stuff. */
3573 for (int i=0; i<array_size(pilot_stack); i++) {
3574 /* move player and persisted pilots to start */
3575 if (!pilot_isFlag(pilot_stack[i], PILOT_DELETE) &&
3576 (pilot_stack[i] == player.p ||
3577 (persist && pilot_isFlag(pilot_stack[i], PILOT_PERSIST)))) {
3578 /* Have to swap the pilots so it gets properly freed. */
3579 Pilot *p = pilot_stack[persist_count];
3580 pilot_stack[persist_count] = pilot_stack[i];
3581 pilot_stack[i] = p;
3582 p = pilot_stack[persist_count];
3583 /* Misc clean up. */
3584 p->lockons = 0; /* Clear lockons. */
3585 p->projectiles = 0; /* Clear projectiles. */
3586 pilot_clearTimers( p ); /* Reset timers. */
3587 /* Reset trails */
3588 for (int g=0; g<array_size(p->trail); g++)
3589 spfx_trail_remove( p->trail[g] );
3590 array_erase( &p->trail, array_begin(p->trail), array_end(p->trail) );
3591 /* All done. */
3592 persist_count++;
3593 }
3594 else /* rest get killed */
3596 }
3598
3599 /* Init AI on the remaining pilots, has to be done here so the pilot_stack is consistent. */
3600 for (int i=0; i<array_size(pilot_stack); i++) {
3601 Pilot *p = pilot_stack[i];
3603 ai_cleartasks(p);
3604 ai_init(p);
3605 }
3606
3607 /* Clear global hooks. */
3609}
3610
3615{
3617 for (int i=0; i < array_size(pilot_stack); i++)
3619}
3620
3624void pilots_clear (void)
3625{
3626 for (int i=0; i < array_size(pilot_stack); i++)
3627 if (!pilot_isPlayer(pilot_stack[i])
3628 && !pilot_isFlag(pilot_stack[i], PILOT_NOCLEAR))
3630}
3631
3636{
3637 pilots_clean(0);
3638 if (player.p != NULL) {
3639 pilot_rmFlag( player.p, PILOT_NOFREE );
3641 player.p = NULL;
3642 free( player.ps.acquired );
3643 memset( &player.ps, 0, sizeof(PlayerShip_t) );
3644 }
3646}
3647
3653void pilots_update( double dt )
3654{
3655 /* Delete loop - this should be atomic or we get hook fuckery! */
3656 for (int i=array_size(pilot_stack)-1; i>=0; i--) {
3657 Pilot *p = pilot_stack[i];
3658
3659 /* Clear target. */
3660 p->ptarget = NULL;
3661
3662 /* Destroy pilot and go on. */
3663 if (pilot_isFlag(p, PILOT_DELETE))
3664 pilot_erase( p );
3665 }
3666
3667 /* Have all the pilots think. */
3668 for (int i=0; i<array_size(pilot_stack); i++) {
3669 Pilot *p = pilot_stack[i];
3670
3671 /* Invisible, not doing anything. */
3672 if (pilot_isFlag(p, PILOT_HIDE))
3673 continue;
3674
3675 /* See if should think. */
3676 if (pilot_isDisabled(p))
3677 continue;
3678 if (pilot_isFlag(p,PILOT_DEAD))
3679 continue;
3680
3681 /* Ignore persisting pilots during simulation since they don't get cleared. */
3682 if (space_isSimulation() && (pilot_isFlag(p,PILOT_PERSIST)))
3683 continue;
3684
3685 /* Hyperspace gets special treatment */
3686 if (pilot_isFlag(p, PILOT_HYP_PREP))
3687 pilot_hyperspace(p, dt);
3688 /* Entering hyperspace. */
3689 else if (pilot_isFlag(p, PILOT_HYP_END)) {
3690 if ((VMOD(p->solid->vel) < 2*solid_maxspeed( p->solid, p->speed, p->thrust) ) && (p->ptimer < 0.))
3691 pilot_rmFlag(p, PILOT_HYP_END);
3692 }
3693 /* Must not be boarding to think. */
3694 else if (!pilot_isFlag(p, PILOT_BOARDING) &&
3695 !pilot_isFlag(p, PILOT_REFUELBOARDING) &&
3696 /* Must not be landing nor taking off. */
3697 !pilot_isFlag(p, PILOT_LANDING) &&
3698 !pilot_isFlag(p, PILOT_TAKEOFF) &&
3699 /* Must not be jumping in. */
3700 !pilot_isFlag(p, PILOT_HYP_END)) {
3701 if (pilot_isFlag(p, PILOT_PLAYER))
3702 player_think( p, dt );
3703 else
3704 ai_think( p, dt );
3705 }
3706 }
3707
3708 /* Now update all the pilots. */
3709 for (int i=0; i<array_size(pilot_stack); i++) {
3710 Pilot *p = pilot_stack[i];
3711
3712 /* Ignore. */
3713 if (pilot_isFlag(p, PILOT_DELETE))
3714 continue;
3715
3716 /* Invisible, not doing anything. */
3717 if (pilot_isFlag(p, PILOT_HIDE))
3718 continue;
3719
3720 /* Just update the pilot. */
3721 if (pilot_isFlag( p, PILOT_PLAYER ))
3722 player_update( p, dt );
3723 else
3724 pilot_update( p, dt );
3725 }
3726}
3727
3731void pilots_render (void)
3732{
3733 for (int i=0; i<array_size(pilot_stack); i++) {
3734 Pilot *p = pilot_stack[i];
3735
3736 /* Invisible, not doing anything. */
3737 if (pilot_isFlag(p, PILOT_HIDE) || pilot_isFlag(p, PILOT_DELETE))
3738 continue;
3739
3740 if (!pilot_isFlag( p, PILOT_PLAYER ))
3741 pilot_render( p );
3742 }
3743}
3744
3749{
3750 for (int i=0; i<array_size(pilot_stack); i++) {
3751 Pilot *p = pilot_stack[i];
3752
3753 /* Invisible, not doing anything. */
3754 if (pilot_isFlag(p, PILOT_HIDE) || pilot_isFlag(p, PILOT_DELETE))
3755 continue;
3756
3757 if (!pilot_isFlag( p, PILOT_PLAYER ))
3759 }
3760}
3761
3768{
3769 int n;
3770
3771 /* Clear outfits first to not leave some outfits in dangling states. */
3772 pilot_outfitOffAll(pilot);
3773
3774 pilot->ptimer = 0.; /* Pilot timer. */
3775 pilot->tcontrol = 0.; /* AI control timer. */
3776 pilot->stimer = 0.; /* Shield timer. */
3777 pilot->dtimer = 0.; /* Disable timer. */
3778 pilot->otimer = 0.; /* Outfit timer. */
3779 for (int i=0; i<MAX_AI_TIMERS; i++)
3780 pilot->timer[i] = 0.; /* Specific AI timers. */
3781 n = 0;
3782 for (int i=0; i<array_size(pilot->outfits); i++) {
3783 PilotOutfitSlot *o = pilot->outfits[i];
3784 o->timer = 0.; /* Last used timer. */
3785 o->stimer = 0.; /* State timer. */
3786 if (o->state != PILOT_OUTFIT_OFF) {
3787 o->state = PILOT_OUTFIT_OFF; /* Set off. */
3788 n++;
3789 }
3790 }
3791
3792 /* Must recalculate stats. */
3793 if (n > 0)
3794 pilot_calcStats( pilot );
3795}
3796
3804double pilot_relsize( const Pilot* cur_pilot, const Pilot* p )
3805{
3806 return (1. - 1./(1. + ((double)cur_pilot->solid->mass / (double)p->solid->mass)));
3807}
3808
3816void pilot_dpseps( const Pilot *p, double *pdps, double *peps )
3817{
3818 double shots, dps=0., eps=0.;
3819 for (int i=0; i<array_size(p->outfits); i++) {
3820 const Damage *dmg;
3821 double mod_energy, mod_damage, mod_shots;
3822 const Outfit *o = p->outfits[i]->outfit;
3823 if (o==NULL)
3824 continue;
3825 switch (o->type) {
3826 case OUTFIT_TYPE_BOLT:
3827 mod_energy = p->stats.fwd_energy;
3828 mod_damage = p->stats.fwd_damage;
3829 mod_shots = 1. / p->stats.fwd_firerate * (double)o->u.blt.shots;
3830 break;
3831 case OUTFIT_TYPE_TURRET_BOLT:
3832 mod_energy = p->stats.tur_energy;
3833 mod_damage = p->stats.tur_damage;
3834 mod_shots = 1. / p->stats.tur_firerate * (double)o->u.blt.shots;
3835 break;
3836 case OUTFIT_TYPE_LAUNCHER:
3837 case OUTFIT_TYPE_TURRET_LAUNCHER:
3838 mod_energy = 1.;
3839 mod_damage = p->stats.launch_damage;
3840 mod_shots = 1. / p->stats.launch_rate * (double)o->u.lau.shots;
3841 break;
3842 case OUTFIT_TYPE_BEAM:
3843 case OUTFIT_TYPE_TURRET_BEAM:
3844 /* Special case due to continuous fire. */
3845 if (o->type == OUTFIT_TYPE_BEAM) {
3846 mod_energy = p->stats.fwd_energy;
3847 mod_damage = p->stats.fwd_damage;
3848 mod_shots = 1. / p->stats.fwd_firerate;
3849 }
3850 else {
3851 mod_energy = p->stats.tur_energy;
3852 mod_damage = p->stats.tur_damage;
3853 mod_shots = 1. / p->stats.tur_firerate;
3854 }
3855 shots = outfit_duration(o);
3856 mod_shots = shots / (shots + mod_shots * outfit_delay(o));
3857 dps += mod_shots * mod_damage * outfit_damage(o)->damage;
3858 eps += mod_shots * mod_energy * outfit_energy(o);
3859 continue;
3860
3861 default:
3862 continue;
3863 }
3864 shots = 1. / (mod_shots * outfit_delay(o));
3865
3866 dmg = outfit_damage(o);
3867 dps += shots * mod_damage * dmg->damage;
3868 eps += shots * mod_energy * MAX( outfit_energy(o), 0. );
3869 }
3870 if (pdps != NULL)
3871 *pdps = dps;
3872 if (peps != NULL)
3873 *peps = eps;
3874}
3875
3883double pilot_reldps( const Pilot* cur_pilot, const Pilot* p )
3884{
3885 double DPSaccum_target, DPSaccum_pilot;
3886
3887 pilot_dpseps( p, &DPSaccum_target, NULL );
3888 pilot_dpseps( cur_pilot, &DPSaccum_pilot, NULL );
3889
3890 if ((DPSaccum_target > 1e-6) && (DPSaccum_pilot > 1e-6))
3891 return DPSaccum_pilot / (DPSaccum_target + DPSaccum_pilot);
3892 else if (DPSaccum_pilot > 0.)
3893 return 1.;
3894 return 0.;
3895}
3896
3904double pilot_relhp( const Pilot* cur_pilot, const Pilot* p )
3905{
3906 double c_hp = cur_pilot -> armour_max + cur_pilot -> shield_max;
3907 double p_hp = p -> armour_max + p -> shield_max;
3908 return c_hp / (p_hp + c_hp);
3909}
3910
3917credits_t pilot_worth( const Pilot *p )
3918{
3919 /* Ship price is base price + outfit prices. */
3920 credits_t price = ship_basePrice( p->ship );
3921 for (int i=0; i<array_size(p->outfits); i++) {
3922 if (p->outfits[i]->outfit == NULL)
3923 continue;
3924 /* Don't count unique outfits. */
3925 if (outfit_isProp(p->outfits[i]->outfit, OUTFIT_PROP_UNIQUE))
3926 continue;
3927 price += p->outfits[i]->outfit->price;
3928 }
3929
3930 return price;
3931}
3932
3941void pilot_msg( Pilot *p, Pilot *receiver, const char *type, unsigned int idx )
3942{
3943 if (idx != 0)
3944 lua_pushvalue(naevL, idx); /* data */
3945 else
3946 lua_pushnil(naevL); /* data */
3947
3948 lua_newtable(naevL); /* data, msg */
3949
3950 if (p != NULL) {
3951 lua_pushpilot(naevL, p->id); /* data, msg, sender */
3952 lua_rawseti(naevL, -2, 1); /* data, msg */
3953 }
3954
3955 lua_pushstring(naevL, type); /* data, msg, type */
3956 lua_rawseti(naevL, -2, 2); /* data, msg */
3957
3958 lua_pushvalue(naevL, -2); /* data, msg, data */
3959 lua_rawseti(naevL, -2, 3); /* data, msg */
3960
3961 lua_rawgeti(naevL, LUA_REGISTRYINDEX, receiver->messages); /* data, msg, messages */
3962 lua_pushvalue(naevL, -2); /* data, msg, messages, msg */
3963 lua_rawseti(naevL, -2, lua_objlen(naevL, -2)+1); /* data, msg, messages */
3964 lua_pop(naevL, 3); /* */
3965}
3966
3974int pilot_hasIllegal( const Pilot *p, int faction )
3975{
3976 /* Check commodities. */
3977 for (int i=0; i<array_size(p->commodities); i++) {
3978 const Commodity *c = p->commodities[i].commodity;
3979 if (commodity_checkIllegal( c, faction ))
3980 return 1;
3981 }
3982 /* Check outfits. */
3983 for (int i=0; i<array_size(p->outfits); i++) {
3984 const Outfit *o = p->outfits[i]->outfit;
3985 if ((o != NULL) && outfit_checkIllegal( o, faction ))
3986 return 1;
3987 }
3988 /* Nothing to see here sir. */
3989 return 0;
3990}
void ai_getDistress(Pilot *p, const Pilot *distressed, const Pilot *attacker)
Sends a distress signal to a pilot.
Definition: ai.c:968
void ai_cleartasks(Pilot *p)
Clears the pilot's tasks.
Definition: ai.c:485
void ai_destroy(Pilot *p)
Destroys the ai part of the pilot.
Definition: ai.c:498
Pilot * cur_pilot
Definition: ai.c:336
void ai_think(Pilot *pilot, const double dt)
Heart of the AI, brains of the pilot.
Definition: ai.c:715
void ai_init(Pilot *p)
Initializes the AI.
Definition: ai.c:823
int ai_pinit(Pilot *p, const char *ai)
Initializes the pilot in the ai.
Definition: ai.c:434
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition: array.h:158
#define array_end(array)
Returns a pointer to the end of the reserved memory space.
Definition: array.h:202
#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:140
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition: array.h:168
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition: array.h:119
#define array_shrink(ptr_array)
Shrinks memory to fit only ‘size’ elements.
Definition: array.h:149
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition: array.h:129
#define array_begin(array)
Returns a pointer to the beginning of the reserved memory space.
Definition: array.h:194
#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:257
void cam_getPos(double *x, double *y)
Gets the camera position.
Definition: camera.c:118
double cam_getZoom(void)
Gets the camera zoom.
Definition: camera.c:97
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:257
int dtype_get(const char *name)
Gets the id of a dtype based on name.
Definition: damagetype.c:144
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:423
int effect_update(Effect **efxlist, double dt)
Updates an effect list.
Definition: effect.c:238
void escort_rmList(Pilot *p, unsigned int id)
Remove from escorts list.
Definition: escort.c:88
void escort_freeList(Pilot *p)
Remove all escorts from a pilot.
Definition: escort.c:60
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:397
const int * faction_getEnemies(int f)
Gets the list of enemies of a faction.
Definition: faction.c:483
char faction_getColourChar(int f)
Gets the faction character associated to its standing with the player.
Definition: faction.c:1040
int areEnemies(int a, int b)
Checks whether two factions are enemies.
Definition: faction.c:1197
void faction_modPlayer(int f, double mod, const char *source)
Modifies the player's standing with a faction.
Definition: faction.c:839
int areAllies(int a, int b)
Checks whether two factions are allies or not.
Definition: faction.c:1222
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:616
int gl_printWidthRaw(const glFont *ft_font, const char *text)
Gets the width that it would take to print some text.
Definition: font.c:960
glFont gl_defFont
Definition: font.c:153
void gatherable_gather(Pilot *p)
See if the pilot can gather anything.
Definition: gatherable.c:168
glTexture * gui_hailIcon(void)
Gets the hail icon texture.
Definition: gui.c:2030
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:619
void gui_cooldownEnd(void)
Notifies GUI scripts that the player broke out of cooldown.
Definition: gui.c:851
void player_message(const char *fmt,...)
Adds a mesg to the queue to be displayed on screen.
Definition: gui.c:330
void lvar_freeArray(lvar *arr)
Frees a variable array.
Definition: lvar.c:145
void mat4_translate(mat4 *m, double x, double y, double z)
Translates a homogenous transformation matrix.
Definition: mat4.c:99
mat4 mat4_identity(void)
Creates an identity matrix.
Definition: mat4.c:195
void mat4_scale(mat4 *m, double x, double y, double z)
Scales a homogeneous transformation matrix.
Definition: mat4.c:82
mat4 mat4_ortho(double left, double right, double bottom, double top, double nearVal, double farVal)
Creates an orthographic projection matrix.
Definition: mat4.c:209
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition: naev.h:40
#define CLAMP(a, b, x)
Definition: naev.h:41
#define ABS(x)
Definition: naev.h:36
#define pow2(x)
Definition: naev.h:46
#define FABS(x)
Definition: naev.h:37
#define MAX(x, y)
Definition: naev.h:39
LuaPilot * lua_pushpilot(lua_State *L, LuaPilot pilot)
Pushes a pilot on the stack.
Definition: nlua_pilot.c:495
vec2 * lua_pushvector(lua_State *L, vec2 vec)
Pushes a vector on the stack.
Definition: nlua_vec2.c:139
ntime_t ntime_create(int scu, int stp, int stu)
Creates a time structure.
Definition: ntime.c:94
glInfo gl_screen
Definition: opengl.c:51
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.
Definition: opengl_render.c:90
void gl_gameToScreenCoords(double *nx, double *ny, double bx, double by)
Converts in-game coordinates to screen coordinates.
void gl_renderTextureInterpolate(const glTexture *ta, const glTexture *tb, double inter, double x, double y, double w, double h, double tx, double ty, double tw, double th, const glColour *c)
Texture blitting backend for interpolated texture.
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_getSpriteFromDir(int *x, int *y, const glTexture *t, const double dir)
Sets x and y to be the appropriate sprite for glTexture using dir.
Definition: opengl_tex.c:857
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:228
int outfit_isLauncher(const Outfit *o)
Checks if outfit is a weapon launcher.
Definition: outfit.c:498
int outfit_checkIllegal(const Outfit *o, int fct)
Checks illegality of an outfit to a faction.
Definition: outfit.c:2736
int outfit_isFighterBay(const Outfit *o)
Checks if outfit is a fighter bay.
Definition: outfit.c:550
const Damage * outfit_damage(const Outfit *o)
Gets the outfit's damage.
Definition: outfit.c:647
double outfit_duration(const Outfit *o)
Gets the outfit's duration.
Definition: outfit.c:851
double outfit_energy(const Outfit *o)
Gets the outfit's energy usage.
Definition: outfit.c:681
double outfit_delay(const Outfit *o)
Gets the outfit's delay.
Definition: outfit.c:658
char pilot_getFactionColourChar(const Pilot *p)
Gets the faction colour char, works like faction_getColourChar but for a pilot.
Definition: pilot.c:1125
void pilot_free(Pilot *p)
Frees and cleans up a pilot.
Definition: pilot.c:3428
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:2952
void pilot_stackRemove(Pilot *p)
Tries to remove a pilot from the stack.
Definition: pilot.c:3506
static void pilot_hyperspace(Pilot *pilot, double dt)
Handles pilot's hyperspace states.
Definition: pilot.c:2648
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:341
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:2593
credits_t pilot_worth(const Pilot *p)
Gets the price or worth of a pilot in credits.
Definition: pilot.c:3917
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:3345
void pilot_setThrust(Pilot *p, double thrust)
Sets the pilot's thrust.
Definition: pilot.c:632
int pilot_isHostile(const Pilot *p)
Checks to see if pilot is hostile to the player.
Definition: pilot.c:651
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:1598
void pilot_cooldown(Pilot *p, int dochecks)
Begins active cooldown, reducing hull and outfit temperatures.
Definition: pilot.c:941
unsigned int 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:3151
void pilot_rmHostile(Pilot *p)
Unmarks a pilot as hostile to player.
Definition: pilot.c:1276
unsigned int pilot_addStack(Pilot *p)
Adds a pilot to the stack.
Definition: pilot.c:3262
void pilot_msg(Pilot *p, Pilot *receiver, const char *type, unsigned int idx)
Sends a message.
Definition: pilot.c:3941
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:3904
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:266
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:3804
int pilot_areEnemies(const Pilot *p, const Pilot *target)
Like areEnemies but for pilots.
Definition: pilot.c:718
void pilots_clear(void)
Clears all the pilots except the player and clear-exempt pilots.
Definition: pilot.c:3624
static const double pilot_commFade
Definition: pilot.c:61
unsigned int pilot_getNearestPilot(const Pilot *p)
Get the nearest pilot to a pilot.
Definition: pilot.c:412
void pilots_clean(int persist)
Cleans up the pilot stack - leaves the player.
Definition: pilot.c:3555
Pilot * pilot_getTarget(Pilot *p)
Gets the target of a pilot using a fancy caching system.
Definition: pilot.c:607
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:3205
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:492
ntime_t pilot_hyperspaceDelay(Pilot *p)
Calculates the hyperspace delay for a pilot.
Definition: pilot.c:2855
double pilot_face(Pilot *p, const double dir)
Tries to turn the pilot to face dir.
Definition: pilot.c:773
void pilot_clearTimers(Pilot *pilot)
Clears the pilot's timers.
Definition: pilot.c:3767
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:3883
void pilot_dead(Pilot *p, unsigned int killer)
Pilot is dead, now will slowly explode.
Definition: pilot.c:1676
static int pilot_cmp(const void *ptr1, const void *ptr2)
Compare id (for use with bsearch)
Definition: pilot.c:91
Pilot * pilot_setPlayer(Pilot *after)
Replaces the player's pilot with an alternate ship with the same ID.
Definition: pilot.c:3300
int pilot_validEnemyDist(const Pilot *p, const Pilot *target, double *dist)
Same as pilot_validEnemy, but able to store the distance too.
Definition: pilot.c:274
static void pilot_refuel(Pilot *p, double dt)
Has the pilot refuel its target.
Definition: pilot.c:2822
void pilot_renderOverlay(Pilot *p)
Renders the pilot overlay.
Definition: pilot.c:1968
static void pilot_erase(Pilot *p)
Destroys pilot from stack.
Definition: pilot.c:3496
void pilot_cooldownEnd(Pilot *p, const char *reason)
Terminates active cooldown.
Definition: pilot.c:1004
static const double pilot_commTimeout
Definition: pilot.c:60
credits_t pilot_modCredits(Pilot *p, credits_t amount)
Modifies the amount of credits the pilot has.
Definition: pilot.c:2911
unsigned int pilot_getNearestEnemy(const Pilot *p)
Gets the nearest enemy to the pilot.
Definition: pilot.c:312
void pilot_setFriendly(Pilot *p)
Marks pilot as friendly to player.
Definition: pilot.c:1294
void pilot_hyperspaceAbort(Pilot *p)
Stops the pilot from hyperspacing.
Definition: pilot.c:2772
void pilot_setTurn(Pilot *p, double turn)
Sets the pilot's turn.
Definition: pilot.c:640
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:226
void pilot_explode(double x, double y, double radius, const Damage *dmg, const Pilot *parent)
Makes the pilot explosion.
Definition: pilot.c:1728
void pilot_reset(Pilot *pilot)
Resets a pilot.
Definition: pilot.c:3088
int pilot_isFriendly(const Pilot *p)
Checks to see if pilot is friendly to the player.
Definition: pilot.c:681
int pilot_refuelStart(Pilot *p)
Attempts to start refueling the pilot's target.
Definition: pilot.c:2794
void pilots_renderOverlay(void)
Renders all the pilots overlays.
Definition: pilot.c:3748
int pilot_isNeutral(const Pilot *p)
Checks to see if pilot is neutral to the player.
Definition: pilot.c:668
PilotOutfitSlot * pilot_getDockSlot(Pilot *p)
Gets the dock slot of the pilot.
Definition: pilot.c:749
void pilots_init(void)
Initializes pilot stuff.
Definition: pilot.c:3520
int pilot_hasIllegal(const Pilot *p, int faction)
Checks to see if the pilot has illegal stuf to a faction.
Definition: pilot.c:3974
void pilot_setCommMsg(Pilot *p, const char *s)
Sets the overhead communication message of the pilot.
Definition: pilot.c:1139
Pilot * pilot_get(unsigned int id)
Pulls a pilot out of the pilot_stack based on ID.
Definition: pilot.c:589
void pilot_broadcast(Pilot *p, const char *msg, int ignore_int)
Has the pilot broadcast a message.
Definition: pilot.c:1154
double pilot_brakeDist(Pilot *p, vec2 *pos)
Gets the braking distance for a pilot.
Definition: pilot.c:837
void pilot_setHostile(Pilot *p)
Marks pilot as hostile to player.
Definition: pilot.c:1111
void pilot_dpseps(const Pilot *p, double *pdps, double *peps)
Calculates the dps and eps of a pilot.
Definition: pilot.c:3816
int pilot_hasCredits(Pilot *p, credits_t amount)
Checks to see if the pilot has at least a certain amount of credits.
Definition: pilot.c:2897
const glColour * pilot_getColour(const Pilot *p)
Gets a pilot's colour.
Definition: pilot.c:1327
static Pilot ** pilot_stack
Definition: pilot.c:57
void pilot_distress(Pilot *p, Pilot *attacker, const char *msg)
Has the pilot broadcast a distress signal.
Definition: pilot.c:1182
void pilot_render(Pilot *p)
Renders the pilot.
Definition: pilot.c:1826
#define PILOT_SIZE_MIN
Definition: pilot.c:51
Pilot *const * pilot_getAll(void)
Gets the pilot stack.
Definition: pilot.c:83
int pilot_canTarget(const Pilot *p)
Same as pilot_validTarget but without the range check.
Definition: pilot.c:243
int pilot_interceptPos(Pilot *p, double x, double y)
Attempts to make the pilot pass through a given point.
Definition: pilot.c:888
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:376
static unsigned int pilot_id
Definition: pilot.c:54
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:1044
void pilots_newSystem(void)
Updates pilot state which depends on the system (sensor range, nebula trails...)
Definition: pilot.c:3614
unsigned int pilot_getNextID(unsigned int id, int mode)
Gets the next pilot based on id.
Definition: pilot.c:124
void pilot_rmFriendly(Pilot *p)
Unmarks a pilot as friendly to player.
Definition: pilot.c:1305
void pilots_render(void)
Renders all the pilots.
Definition: pilot.c:3731
int pilot_areAllies(const Pilot *p, const Pilot *target)
Like areAllies but for pilots.
Definition: pilot.c:694
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:536
void pilot_untargetAsteroid(int anchor, int asteroid)
Loops over pilot stack to remove an asteroid as target.
Definition: pilot.c:2867
int pilot_getJumps(const Pilot *p)
Gets the amount of jumps the pilot has left.
Definition: pilot.c:1316
int pilot_brake(Pilot *p)
Causes the pilot to turn around and brake.
Definition: pilot.c:788
void pilot_delete(Pilot *p)
Deletes a pilot.
Definition: pilot.c:2603
void pilots_update(double dt)
Updates all the pilots.
Definition: pilot.c:3653
void pilots_cleanAll(void)
Even cleans up the player.
Definition: pilot.c:3635
void pilot_clearTrails(Pilot *p)
Resets the trails for a pilot.
Definition: pilot.c:3287
unsigned int pilot_getBoss(const Pilot *p)
Get the strongest ally in a given range.
Definition: pilot.c:425
static void pilot_init_trails(Pilot *p)
Initialize pilot's trails according to the ship type and current system characteristics.
Definition: pilot.c:3130
void pilots_free(void)
Frees the pilot stack.
Definition: pilot.c:3528
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:1381
static int pilot_getStackPos(unsigned int id)
Gets the pilot's position in the stack.
Definition: pilot.c:105
void pilot_renderFramebuffer(Pilot *p, GLuint fbo, double fw, double fh)
Renders a pilot to a framebuffer.
Definition: pilot.c:1778
void pilot_update(Pilot *pilot, double dt)
Updates the pilot.
Definition: pilot.c:2050
unsigned int pilot_getPrevID(unsigned int id, int mode)
Gets the previous pilot based on ID.
Definition: pilot.c:172
int pilot_numOutfit(const Pilot *p, const Outfit *o)
Checks to see how many of an outfit a pilot has.
Definition: pilot.c:2880
void pilot_sample_trails(Pilot *p, int none)
Updates the given pilot's trail emissions.
Definition: pilot.c:2529
void pilot_setTarget(Pilot *p, unsigned int id)
Sets the target of the pilot.
Definition: pilot.c:1351
unsigned int pilot_clone(const Pilot *ref)
Clones an existing pilot.
Definition: pilot.c:3223
int pilot_cargoRmAll(Pilot *pilot, int cleanup)
Gets rid of all cargo from pilot. Can remove mission cargo.
Definition: pilot_cargo.c:348
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.
Definition: pilot_cargo.c:404
void pilot_ewUpdateStealth(Pilot *p, double dt)
Updates the stealth mode and checks to see if it is getting broken.
Definition: pilot_ew.c:468
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:242
void pilot_ewUpdateDynamic(Pilot *p, double dt)
Updates the pilot's dynamic electronic warfare properties.
Definition: pilot_ew.c:110
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:277
void pilot_updateSensorRange(void)
Updates the system's base sensor range.
Definition: pilot_ew.c:202
void pilot_ewScanStart(Pilot *p)
Initializes the scan timer for a pilot.
Definition: pilot_ew.c:55
double pilot_sensorRange(void)
Returns the default sensor range for the current system.
Definition: pilot_ew.c:212
double pilot_heatEfficiencyMod(double T, double Tb, double Tc)
Returns a 0:1 modifier representing efficiency (1. being normal).
Definition: pilot_heat.c:252
void pilot_heatUpdateCooldown(Pilot *p)
Overrides the usual heat model during active cooldown.
Definition: pilot_heat.c:262
double pilot_heatFireRateMod(double T)
Returns a 0:1 modifier representing fire rate (1. being normal).
Definition: pilot_heat.c:305
void pilot_heatReset(Pilot *p)
Resets a pilot's heat.
Definition: pilot_heat.c:110
void pilot_heatUpdateShip(Pilot *p, double Q_cond, double dt)
Heats the pilot's ship.
Definition: pilot_heat.c:229
double pilot_heatUpdateSlot(const Pilot *p, PilotOutfitSlot *o, double dt)
Heats the pilot's slot.
Definition: pilot_heat.c:193
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:164
void pilots_clearGlobalHooks(void)
Removes all the pilot global hooks.
Definition: pilot_hook.c:165
void pilot_clearHooks(Pilot *p)
Clears the pilots hooks.
Definition: pilot_hook.c:206
int pilot_runHookParam(Pilot *p, int hook_type, const HookParam *param, int nparam)
Tries to run a pilot hook if he has it.
Definition: pilot_hook.c:37
void pilot_freeGlobalHooks(void)
Clears global pilot hooks.
Definition: pilot_hook.c:221
int pilot_runHook(Pilot *p, int hook_type)
Tries to run a pilot hook if he has it.
Definition: pilot_hook.c:106
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.
Definition: pilot_outfit.c:877
void pilot_fillAmmo(Pilot *pilot)
Fills pilot's ammo completely.
Definition: pilot_outfit.c:786
int pilot_addAmmo(Pilot *pilot, PilotOutfitSlot *s, int quantity)
Adds some ammo to the pilot stock.
Definition: pilot_outfit.c:661
void pilot_lockClear(Pilot *p)
Clears pilot's missile lockon timers.
Definition: pilot_outfit.c:121
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.
Definition: pilot_outfit.c:541
void pilot_lockUpdateSlot(Pilot *p, PilotOutfitSlot *o, Pilot *t, double *a, double dt)
Updates the lockons on the pilot's launchers.
Definition: pilot_outfit.c:46
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.
Definition: pilot_outfit.c:273
int pilot_addOutfitIntrinsic(Pilot *pilot, const Outfit *outfit)
Adds an outfit as an intrinsic slot.
Definition: pilot_outfit.c:385
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.
void pilot_afterburnOver(Pilot *p)
Deactivates the afterburner.
void pilot_weapSetUpdate(Pilot *p)
Updates the pilot's weapon sets.
Definition: pilot_weapon.c:264
int pilot_outfitOffAll(Pilot *p)
Disables all active outfits for a pilot.
void pilot_weaponAuto(Pilot *p)
Tries to automatically set and create the pilot's weapon set.
void pilot_weapSetFree(Pilot *p)
Frees a pilot's weapon sets.
Definition: pilot_weapon.c:718
double pilot_weapSetSpeed(Pilot *p, int id, int level)
Gets the speed of the current pilot weapon set.
Definition: pilot_weapon.c:654
int pilot_outfitOff(Pilot *p, PilotOutfitSlot *o)
Disables a given active outfit.
void player_updateSpecific(Pilot *pplayer, const double dt)
Does a player specific update.
Definition: player.c:1304
void player_dead(void)
Player got pwned.
Definition: player.c:2515
void player_soundPlay(int sound, int once)
Plays a sound at the player.
Definition: player.c:858
int snd_hypPowDown
Definition: player.c:103
void player_update(Pilot *pplayer, const double dt)
Player update function.
Definition: player.c:1288
void player_soundStop(void)
Stops playing player sounds.
Definition: player.c:866
int snd_hypPowUpJump
Definition: player.c:104
int snd_hypPowUp
Definition: player.c:101
void player_think(Pilot *pplayer, const double dt)
Basically uses keyboard input instead of AI input. Used in pilot.c.
Definition: player.c:1142
Player_t player
Definition: player.c:73
void player_destroyed(void)
Player blew up in a fireball.
Definition: player.c:2528
void player_autonavResetSpeed(void)
Resets the game speed.
static const double c[]
Definition: rng.c:264
static const double a[]
Definition: rng.c:247
static const double d[]
Definition: rng.c:273
credits_t ship_basePrice(const Ship *s)
Gets the ship's base price (no outfits).
Definition: ship.c:252
int ss_statsInit(ShipStats *stats)
Initializes a stat structure.
Definition: shipstats.c:347
double sound_getLength(int sound)
Gets the length of the sound buffer.
Definition: sound.c:783
int sound_playPos(int sound, double px, double py, double vx, double vy)
Plays a sound based on position.
Definition: sound.c:835
int sound_get(const char *name)
Gets the buffer to sound of name.
Definition: sound.c:764
void system_rmCurrentPresence(StarSystem *sys, int faction, double amount)
Removes active presence.
Definition: space.c:4269
int space_canHyperspace(const Pilot *p)
Checks to make sure if pilot is far enough away to hyperspace.
Definition: space.c:437
int space_isSimulationEffects(void)
returns whether or not we're simulating with effects.
Definition: space.c:1490
StarSystem * cur_system
Definition: space.c:105
double system_getPresence(const StarSystem *sys, int faction)
Get the presence of a faction in a system.
Definition: space.c:4138
int space_isSimulation(void)
returns whether we're just simulating.
Definition: space.c:1482
int spfx_get(char *name)
Gets the id of an spfx based on name.
Definition: spfx.c:333
void spfx_trail_sample(Trail_spfx *trail, double x, double y, TrailMode mode, int force)
Makes a trail grow.
Definition: spfx.c:743
void spfx_shake(double mod)
Increases the current rumble level.
Definition: spfx.c:883
void spfx_trail_remove(Trail_spfx *trail)
Removes a trail.
Definition: spfx.c:779
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:462
void spfx_damage(double mod)
Increases the current damage level.
Definition: spfx.c:903
Trail_spfx * spfx_trail_create(const TrailSpec *spec)
Initalizes a trail.
Definition: spfx.c:675
void spfx_trail_draw(const Trail_spfx *trail)
Draws a trail on screen.
Definition: spfx.c:798
Represents a commodity.
Definition: commodity.h:43
Core damage that an outfit does.
Definition: outfit.h:111
int type
Definition: outfit.h:112
double disable
Definition: outfit.h:115
double penetration
Definition: outfit.h:113
double damage
Definition: outfit.h:114
Pilot ship effect data.
Definition: effect.h:13
Pilot ship effect.
Definition: effect.h:41
double r
Definition: effect.h:47
const EffectData * data
Definition: effect.h:42
double elapsed
Definition: effect.h:48
double timer
Definition: effect.h:44
The actual hook parameter.
Definition: hook.h:35
LuaPilot lp
Definition: hook.h:41
union HookParam::@4 u
HookParamType type
Definition: hook.h:36
LuaJump lj
Definition: hook.h:44
int destid
Definition: nlua_jump.h:16
int srcid
Definition: nlua_jump.h:15
double reload_time
Definition: outfit.h:271
double reload_time
Definition: outfit.h:190
A ship outfit, depends radically on the type.
Definition: outfit.h:304
OutfitLauncherData lau
Definition: outfit.h:373
OutfitBoltData blt
Definition: outfit.h:371
OutfitType type
Definition: outfit.h:369
int lua_onimpact
Definition: outfit.h:361
OutfitAfterburnerData afb
Definition: outfit.h:375
OutfitFighterBayData bay
Definition: outfit.h:376
nlua_env lua_env
Definition: outfit.h:342
union Outfit::@22 u
char * name
Definition: outfit.h:305
const Commodity * commodity
Definition: pilot.h:173
int quantity
Definition: pilot.h:174
int deployed
Definition: pilot.h:100
int quantity
Definition: pilot.h:99
Stores an outfit the pilot has.
Definition: pilot.h:108
PilotOutfitAmmo ammo
Definition: pilot.h:135
double heat_C
Definition: pilot.h:118
double stimer
Definition: pilot.h:124
double rtimer
Definition: pilot.h:126
double heat_start
Definition: pilot.h:120
double heat_T
Definition: pilot.h:117
PilotOutfitState state
Definition: pilot.h:123
double timer
Definition: pilot.h:125
ShipOutfitSlot * sslot
Definition: pilot.h:114
const Outfit * outfit
Definition: pilot.h:112
The representation of an in-game pilot.
Definition: pilot.h:210
double engine_glow
Definition: pilot.h:371
Solid * solid
Definition: pilot.h:220
double dtimer
Definition: pilot.h:361
double dtimer_accum
Definition: pilot.h:362
double itimer
Definition: pilot.h:357
ShipStats stats
Definition: pilot.h:286
double shield
Definition: pilot.h:246
double otimer
Definition: pilot.h:363
AI_Profile * ai
Definition: pilot.h:342
unsigned int id
Definition: pilot.h:211
PilotCommodity * commodities
Definition: pilot.h:318
double thrust
Definition: pilot.h:235
int aimLines
Definition: pilot.h:314
PilotOutfitSlot * outfit_structure
Definition: pilot.h:293
unsigned int parent
Definition: pilot.h:325
int nav_hyperspace
Definition: pilot.h:337
PilotOutfitSlot ** outfits
Definition: pilot.h:292
PilotOutfitSlot * outfit_utility
Definition: pilot.h:294
PilotOutfitSlot * outfit_intrinsic
Definition: pilot.h:296
int messages
Definition: pilot.h:372
double armour_max
Definition: pilot.h:247
double speed
Definition: pilot.h:237
int tsy
Definition: pilot.h:225
ShipStats intrinsic_stats
Definition: pilot.h:285
int tsx
Definition: pilot.h:224
double timer[MAX_AI_TIMERS]
Definition: pilot.h:345
double htimer
Definition: pilot.h:358
const Ship * ship
Definition: pilot.h:219
double sbonus
Definition: pilot.h:360
double fuel_max
Definition: pilot.h:252
double energy
Definition: pilot.h:257
int nav_anchor
Definition: pilot.h:338
double stress
Definition: pilot.h:245
int hail_pos
Definition: pilot.h:365
double ptimer
Definition: pilot.h:356
int faction
Definition: pilot.h:215
int autoweap
Definition: pilot.h:313
double comm_msgTimer
Definition: pilot.h:350
double stimer
Definition: pilot.h:359
double energy_max
Definition: pilot.h:258
double landing_delay
Definition: pilot.h:354
unsigned int shoot_indicator
Definition: pilot.h:347
double fuel
Definition: pilot.h:253
double energy_loss
Definition: pilot.h:261
PilotOutfitSlot * afterburner
Definition: pilot.h:308
char * name
Definition: pilot.h:212
double tcontrol
Definition: pilot.h:344
PilotFlags flags
Definition: pilot.h:353
unsigned int dockpilot
Definition: pilot.h:327
double ctimer
Definition: pilot.h:281
double energy_tau
Definition: pilot.h:260
double shield_regen
Definition: pilot.h:250
int cargo_free
Definition: pilot.h:319
int nav_spob
Definition: pilot.h:336
int dockslot
Definition: pilot.h:331
int lua_mem
Definition: pilot.h:343
double player_damage
Definition: pilot.h:369
Effect * effects
Definition: pilot.h:289
char * comm_msg
Definition: pilot.h:352
PilotOutfitSlot * outfit_weapon
Definition: pilot.h:295
double armour
Definition: pilot.h:244
double shield_max
Definition: pilot.h:248
int nav_asteroid
Definition: pilot.h:339
double armour_regen
Definition: pilot.h:249
int healthbars
Definition: conf.h:98
double zoom_far
Definition: conf.h:135
Player ship.
Definition: player.h:73
double dmg_done_armour
Definition: player.h:87
double dmg_taken_armour
Definition: player.h:89
double dmg_taken_shield
Definition: player.h:88
Pilot * p
Definition: player.h:74
unsigned int ships_destroyed[SHIP_CLASS_TOTAL]
Definition: player.h:90
char * acquired
Definition: player.h:84
double dmg_done_shield
Definition: player.h:86
Pilot * p
Definition: player.h:101
double dmg_done_shield
Definition: player.h:137
PlayerShip_t ps
Definition: player.h:102
double dmg_done_armour
Definition: player.h:138
unsigned int ships_destroyed[SHIP_CLASS_TOTAL]
Definition: player.h:141
double dmg_taken_armour
Definition: player.h:140
double dmg_taken_shield
Definition: player.h:139
Ship outfit slot.
Definition: ship.h:70
const Outfit * data
Definition: ship.h:76
double time_speedup
Definition: shipstats.h:309
double ammo_capacity
Definition: shipstats.h:260
double damage
Definition: shipstats.h:224
double disable
Definition: shipstats.h:225
double fuel_regen
Definition: shipstats.h:304
double launch_reload
Definition: shipstats.h:263
double fbay_capacity
Definition: shipstats.h:269
double stress_dissipation
Definition: shipstats.h:252
double fbay_reload
Definition: shipstats.h:271
Represents a space ship.
Definition: ship.h:94
double dt_default
Definition: ship.h:123
double cap_cargo
Definition: ship.h:122
glTexture * gfx_space
Definition: ship.h:137
char * name
Definition: ship.h:95
ShipOutfitSlot * outfit_utility
Definition: ship.h:157
ShipOutfitSlot * outfit_weapon
Definition: ship.h:158
double mass
Definition: ship.h:118
ShipOutfitSlot * outfit_structure
Definition: ship.h:156
Represents a solid in the game.
Definition: physics.h:17
double speed_max
Definition: physics.h:24
void(* update)(struct Solid_ *, double)
Definition: physics.h:25
vec2 vel
Definition: physics.h:21
double thrust
Definition: physics.h:23
double dir
Definition: physics.h:19
double mass
Definition: physics.h:18
vec2 pos
Definition: physics.h:22
int faction
Definition: space.h:66
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition: space.h:88
SpobPresence presence
Definition: space.h:102
A trail generated by a ship or an ammo.
Definition: spfx.h:64
int h
Definition: font.h:18
GLuint fbo_tex[OPENGL_NUM_FBOS]
Definition: opengl.h:68
GLuint fbo[OPENGL_NUM_FBOS]
Definition: opengl.h:67
int nw
Definition: opengl.h:43
int nh
Definition: opengl.h:44
GLuint current_fbo
Definition: opengl.h:66
Abstraction for rendering sprite sheets.
Definition: opengl_tex.h:34
double sw
Definition: opengl_tex.h:44
double sh
Definition: opengl_tex.h:45
double w
Definition: opengl_tex.h:38
double sx
Definition: opengl_tex.h:42
double srh
Definition: opengl_tex.h:47
double sy
Definition: opengl_tex.h:43
double h
Definition: opengl_tex.h:39
double srw
Definition: opengl_tex.h:46
Definition: mat4.h:10
Represents a 2d vector.
Definition: vec2.h:32
double y
Definition: vec2.h:34
double x
Definition: vec2.h:33