1 /*
2  * See Licensing and Copyright notice in naev.h
3  */
4 
5 
6 /**
7  * @file pilot.c
8  *
9  * @brief Handles the pilot stuff.
10  */
11 
12 
13 #include "pilot.h"
14 
15 #include "naev.h"
16 
17 #include <math.h>
18 #include <stdlib.h>
19 #include <limits.h>
20 
21 #include "nxml.h"
22 #include "nstring.h"
23 #include "log.h"
24 #include "weapon.h"
25 #include "ndata.h"
26 #include "spfx.h"
27 #include "rng.h"
28 #include "hook.h"
29 #include "map.h"
30 #include "explosion.h"
31 #include "escort.h"
32 #include "music.h"
33 #include "player.h"
34 #include "gui.h"
35 #include "board.h"
36 #include "debris.h"
37 #include "ntime.h"
38 #include "ai.h"
39 #include "faction.h"
40 #include "font.h"
41 #include "land.h"
42 #include "land_outfits.h"
43 #include "land_shipyard.h"
44 #include "camera.h"
45 #include "damagetype.h"
46 #include "pause.h"
47 
48 
49 #define PILOT_CHUNK_MIN 128 /**< Minimum chunks to increment pilot_stack by */
50 #define PILOT_CHUNK_MAX 2048 /**< Maximum chunks to increment pilot_stack by */
51 #define CHUNK_SIZE      32 /**< Size to allocate memory by. */
52 
53 /* ID Generators. */
54 static unsigned int pilot_id = PLAYER_ID; /**< Stack of pilot ids to assure uniqueness */
55 
56 
57 /* stack of pilot_nstack */
58 Pilot** pilot_stack = NULL; /**< Not static, used in player.c, weapon.c, pause.c, space.c and ai.c */
59 int pilot_nstack = 0; /**< same */
60 static int pilot_mstack = 0; /**< Memory allocated for pilot_stack. */
61 
62 
63 /* misc */
64 static double pilot_commTimeout  = 15.; /**< Time for text above pilot to time out. */
65 static double pilot_commFade     = 5.; /**< Time for text above pilot to fade out. */
66 
67 
68 
69 /*
70  * Prototypes
71  */
72 /* Update. */
73 static void pilot_hyperspace( Pilot* pilot, double dt );
74 static void pilot_refuel( Pilot *p, double dt );
75 /* Clean up. */
76 static void pilot_dead( Pilot* p, unsigned int killer );
77 /* Targetting. */
78 static int pilot_validEnemy( const Pilot* p, const Pilot* target );
79 /* Misc. */
80 static void pilot_setCommMsg( Pilot *p, const char *s );
81 static int pilot_getStackPos( const unsigned int id );
82 
83 
84 /**
85  * @brief Gets the pilot stack.
86  */
pilot_getAll(int * n)87 Pilot** pilot_getAll( int *n )
88 {
89    *n = pilot_nstack;
90    return pilot_stack;
91 }
92 
93 
94 /**
95  * @brief Compare id (for use with bsearch)
96  */
compid(const void * id,const void * p)97 static int compid(const void *id, const void *p) {
98     return *((const unsigned int*)id) - (*((Pilot**)p))->id;
99 }
100 
101 
102 /**
103  * @brief Gets the pilot's position in the stack.
104  *
105  *    @param id ID of the pilot to get.
106  *    @return Position of pilot in stack or -1 if not found.
107  */
pilot_getStackPos(const unsigned int id)108 static int pilot_getStackPos( const unsigned int id )
109 {
110    /* binary search */
111    Pilot **pp = bsearch(&id, pilot_stack, pilot_nstack, sizeof(Pilot*), compid);
112    if (pp == NULL)
113       return -1;
114    else
115       return pp - pilot_stack;
116 }
117 
118 
119 /**
120  * @brief Gets the next pilot based on id.
121  *
122  *    @param id ID of current pilot.
123  *    @param mode Method to use when cycling.  0 is normal, 1 is hostiles.
124  *    @return ID of next pilot or PLAYER_ID if no next pilot.
125  */
pilot_getNextID(const unsigned int id,int mode)126 unsigned int pilot_getNextID( const unsigned int id, int mode )
127 {
128    int m, p;
129 
130    /* Player must exist. */
131    if (player.p == NULL)
132       return PLAYER_ID;
133 
134    /* Get the pilot. */
135    m = pilot_getStackPos(id);
136 
137    /* Unselect. */
138    if ((m == (pilot_nstack-1)) || (m == -1))
139       return PLAYER_ID;
140 
141    /* Get first one in range. */
142    p = m+1;
143    if (mode == 0) {
144       while (p < pilot_nstack) {
145          if (((pilot_stack[p]->faction != FACTION_PLAYER) ||
146                   pilot_isDisabled(pilot_stack[p])) &&
147                !pilot_isFlag( pilot_stack[p], PILOT_INVISIBLE ) &&
148                pilot_inRangePilot( player.p, pilot_stack[p] ))
149             return pilot_stack[p]->id;
150          p++;
151       }
152    }
153    /* Get first hostile in range. */
154    if (mode == 1) {
155       while (p < pilot_nstack) {
156          if ((pilot_stack[p]->faction != FACTION_PLAYER) &&
157                !pilot_isFlag( pilot_stack[p], PILOT_INVISIBLE ) &&
158                pilot_inRangePilot( player.p, pilot_stack[p] ) &&
159                (pilot_isFlag(pilot_stack[p],PILOT_HOSTILE) ||
160                   areEnemies( FACTION_PLAYER, pilot_stack[p]->faction)))
161             return pilot_stack[p]->id;
162          p++;
163       }
164    }
165 
166    /* None found. */
167    return PLAYER_ID;
168 }
169 
170 
171 /**
172  * @brief Gets the previous pilot based on ID.
173  *
174  *    @param id ID of the current pilot.
175  *    @return ID of previous pilot or PLAYER_ID if no previous pilot.
176  */
pilot_getPrevID(const unsigned int id,int mode)177 unsigned int pilot_getPrevID( const unsigned int id, int mode )
178 {
179    int m, p;
180 
181    /* Player must exist. */
182    if (player.p == NULL)
183       return PLAYER_ID;
184 
185    /* Get the pilot. */
186    m = pilot_getStackPos(id);
187 
188    /* Check to see what position to try. */
189    if (m == -1)
190       return PLAYER_ID;
191    else if (m == 0)
192       p = pilot_nstack-1;
193    else
194       p = m-1;
195 
196    /* Get first one in range. */
197    if (mode == 0) {
198       while (p >= 0) {
199          if (((pilot_stack[p]->faction != FACTION_PLAYER) ||
200                   (pilot_isDisabled(pilot_stack[p]))) &&
201                !pilot_isFlag( pilot_stack[p], PILOT_INVISIBLE ) &&
202                pilot_inRangePilot( player.p, pilot_stack[p] ))
203             return pilot_stack[p]->id;
204          p--;
205       }
206    }
207    /* Get first hostile in range. */
208    else if (mode == 1) {
209       while (p >= 0) {
210          if ((pilot_stack[p]->faction != FACTION_PLAYER) &&
211                !pilot_isFlag( pilot_stack[p], PILOT_INVISIBLE ) &&
212                pilot_inRangePilot( player.p, pilot_stack[p] ) &&
213                (pilot_isFlag(pilot_stack[p],PILOT_HOSTILE) ||
214                   areEnemies( FACTION_PLAYER, pilot_stack[p]->faction)))
215             return pilot_stack[p]->id;
216          p--;
217       }
218    }
219 
220    /* None found. */
221    return PLAYER_ID;
222 }
223 
224 
225 /**
226  * @brief Checks to see if a pilot is a valid target for another pilot.
227  *
228  *    @param p Reference pilot.
229  *    @param target Pilot to see if is a valid target of the reference.
230  *    @return 1 if it is valid, 0 otherwise.
231  */
pilot_validTarget(const Pilot * p,const Pilot * target)232 int pilot_validTarget( const Pilot* p, const Pilot* target )
233 {
234    /* Must not be dead. */
235    if (pilot_isFlag( target, PILOT_DELETE ) ||
236          pilot_isFlag( target, PILOT_DEAD))
237       return 0;
238 
239    /* Must not be invisible. */
240    if (pilot_isFlag( target, PILOT_INVISIBLE ))
241       return 0;
242 
243    /* Must be in range. */
244    if (!pilot_inRangePilot( p, target ))
245       return 0;
246 
247    /* Pilot is a valid target. */
248    return 1;
249 }
250 
251 
252 /**
253  * @brief Checks to see if a pilot is a valid enemy for another pilot.
254  *
255  *    @param p Reference pilot.
256  *    @param target Pilot to see if is a valid enemy of the reference.
257  *    @return 1 if it is valid, 0 otherwise.
258  */
pilot_validEnemy(const Pilot * p,const Pilot * target)259 static int pilot_validEnemy( const Pilot* p, const Pilot* target )
260 {
261    /* Must not be bribed. */
262    if ((target->faction == FACTION_PLAYER) && pilot_isFlag(p,PILOT_BRIBED))
263       return 0;
264 
265    /* Should either be hostile by faction or by player. */
266    if (!(areEnemies( p->faction, target->faction) ||
267             ((target->id == PLAYER_ID) &&
268              pilot_isFlag(p,PILOT_HOSTILE))))
269       return 0;
270 
271    /* Shouldn't be disabled. */
272    if (pilot_isDisabled(target))
273       return 0;
274 
275    /* Must be a valid target. */
276    if (!pilot_validTarget( p, target ))
277       return 0;
278 
279    /* He's ok. */
280    return 1;
281 }
282 
283 
284 /**
285  * @brief Gets the nearest enemy to the pilot.
286  *
287  *    @param p Pilot to get the nearest enemy of.
288  *    @return ID of their nearest enemy.
289  */
pilot_getNearestEnemy(const Pilot * p)290 unsigned int pilot_getNearestEnemy( const Pilot* p )
291 {
292    unsigned int tp;
293    int i;
294    double d, td;
295 
296    tp = 0;
297    d  = 0.;
298    for (i=0; i<pilot_nstack; i++) {
299 
300       if (!pilot_validEnemy( p, pilot_stack[i] ))
301          continue;
302 
303       /* Check distance. */
304       td = vect_dist2(&pilot_stack[i]->solid->pos, &p->solid->pos);
305       if (!tp || (td < d)) {
306          d  = td;
307          tp = pilot_stack[i]->id;
308       }
309    }
310    return tp;
311 }
312 
313 /**
314  * @brief Gets the nearest enemy to the pilot closest to the pilot whose mass is between LB and UB.
315  *
316  *    @param p Pilot to get the nearest enemy of.
317  *    @param target_mass_LB the lower bound for target mass
318  *    @param target_mass_UB the upper bound for target mass
319  *    @return ID of their nearest enemy.
320  */
pilot_getNearestEnemy_size(const Pilot * p,double target_mass_LB,double target_mass_UB)321 unsigned int pilot_getNearestEnemy_size( const Pilot* p, double target_mass_LB, double target_mass_UB)
322 {
323    unsigned int tp;
324    int i;
325    double d, td;
326 
327    tp = 0;
328    d  = 0.;
329    for (i=0; i<pilot_nstack; i++) {
330 
331       if (!pilot_validEnemy( p, pilot_stack[i] ))
332          continue;
333 
334       if (pilot_stack[i]->solid->mass < target_mass_LB || pilot_stack[i]->solid->mass > target_mass_UB)
335          continue;
336 
337       /* Check distance. */
338       td = vect_dist2(&pilot_stack[i]->solid->pos, &p->solid->pos);
339       if (!tp || (td < d)) {
340          d = td;
341          tp = pilot_stack[i]->id;
342       }
343    }
344 
345    return tp;
346 }
347 
348 /**
349  * @brief Gets the nearest enemy to the pilot closest to the pilot whose mass is between LB and UB.
350  *
351  *    @param p Pilot to get the nearest enemy of.
352  *    @param mass_factor parameter for target mass (0-1, 0.5 = current mass)
353  *    @param health_factor parameter for target shields/armour (0-1, 0.5 = current health)
354  *    @param dps_factor parameter for target dps (0-1, 0.5 = current dps)
355  *    @param range_factor weighting for range (typically >> 1)
356  *    @return ID of their nearest enemy.
357  */
pilot_getNearestEnemy_heuristic(const Pilot * p,double mass_factor,double health_factor,double damage_factor,double range_factor)358 unsigned int pilot_getNearestEnemy_heuristic( const Pilot* p,
359       double mass_factor, double health_factor,
360       double damage_factor, double range_factor )
361 {
362    unsigned int tp;
363    int i;
364    double temp, current_heuristic_value;
365    Pilot *target;
366 
367    current_heuristic_value = 10000.;
368 
369    tp = 0;
370    for (i=0; i<pilot_nstack; i++) {
371       target = pilot_stack[i];
372 
373       if (!pilot_validEnemy( p, target ))
374          continue;
375 
376       /* Check distance. */
377       temp = range_factor *
378                vect_dist2( &target->solid->pos, &p->solid->pos )
379             + fabs( pilot_relsize( p, target ) - mass_factor)
380             + fabs( pilot_relhp(   p, target ) - health_factor)
381             + fabs( pilot_reldps(  p, target ) - damage_factor);
382 
383       if ((tp == 0) || (temp < current_heuristic_value)) {
384          current_heuristic_value = temp;
385          tp = target->id;
386       }
387    }
388 
389    return tp;
390 }
391 
392 /**
393  * @brief Get the nearest pilot to a pilot.
394  *
395  *    @param p Pilot to get the nearest pilot of.
396  *    @return The nearest pilot.
397  */
pilot_getNearestPilot(const Pilot * p)398 unsigned int pilot_getNearestPilot( const Pilot* p )
399 {
400    unsigned int t;
401    pilot_getNearestPos( p, &t, p->solid->pos.x, p->solid->pos.y, 0 );
402    return t;
403 }
404 
405 
406 /**
407  * @brief Get the strongest ally in a given range.
408  *
409  *    @param p Pilot to get the boss of.
410  *    @return The boss.
411  */
pilot_getBoss(const Pilot * p)412 unsigned int pilot_getBoss( const Pilot* p )
413 {
414    unsigned int t;
415    int i;
416    double td, dx, dy;
417    double relpower, ppower, curpower;
418    /* TODO : all the parameters should be adjustable with arguments */
419 
420    t = 0;
421 
422    /* Initialized to 0.25 which would mean equivalent power. */
423    ppower = 0.5*0.5;
424 
425    for (i=0; i<pilot_nstack; i++) {
426 
427       /* Must be in range. */
428       if (!pilot_inRangePilot( p, pilot_stack[i] ))
429          continue;
430 
431       /* Must not be self. */
432       if (pilot_stack[i] == p)
433          continue;
434 
435       /* Shouldn't be disabled. */
436       if (pilot_isDisabled(pilot_stack[i]))
437          continue;
438 
439       /* Must be a valid target. */
440       if (!pilot_validTarget( p, pilot_stack[i] ))
441          continue;
442 
443       /* Maximum distance in 2 seconds. */
444       dx = pilot_stack[i]->solid->pos.x + 2*pilot_stack[i]->solid->vel.x -
445            p->solid->pos.x - 2*p->solid->vel.x;
446       dy = pilot_stack[i]->solid->pos.y + 2*pilot_stack[i]->solid->vel.y -
447            p->solid->pos.y - 2*p->solid->vel.y;
448       td = sqrt( pow2(dx) + pow2(dy) );
449       if (td > 5000)
450          continue;
451 
452       /* Must have the same faction. */
453       if (pilot_stack[i]->faction != p->faction)
454          continue;
455 
456       /* Must be slower. */
457       if (pilot_stack[i]->speed > p->speed)
458          continue;
459 
460       /* Should not be weaker than the current pilot*/
461       curpower = pilot_reldps(  pilot_stack[i], p ) * pilot_relhp(  pilot_stack[i], p );
462       if (ppower >= curpower )
463          continue;
464 
465       if (relpower < curpower ){
466          relpower = curpower;
467          t = pilot_stack[i]->id;
468       }
469    }
470    return t;
471 }
472 
473 /**
474  * @brief Get the nearest pilot to a pilot from a certain position.
475  *
476  *    @param p Pilot to get the nearest pilot of.
477  *    @param[out] tp The nearest pilot.
478  *    @param x X position to calculate from.
479  *    @param y Y position to calculate from.
480  *    @param disabled Whether to return disabled pilots.
481  *    @return The distance to the nearest pilot.
482  */
pilot_getNearestPos(const Pilot * p,unsigned int * tp,double x,double y,int disabled)483 double pilot_getNearestPos( const Pilot *p, unsigned int *tp, double x, double y, int disabled )
484 {
485    int i;
486    double d, td;
487 
488    *tp = PLAYER_ID;
489    d  = 0;
490    for (i=0; i<pilot_nstack; i++) {
491 
492       /* Must not be self. */
493       if (pilot_stack[i] == p)
494          continue;
495 
496       /* Player doesn't select escorts (unless disabled is active). */
497       if (!disabled && (p->faction == FACTION_PLAYER) &&
498             (pilot_stack[i]->faction == FACTION_PLAYER))
499          continue;
500 
501       /* Shouldn't be disabled. */
502       if (!disabled && pilot_isDisabled(pilot_stack[i]))
503          continue;
504 
505       /* Must be a valid target. */
506       if (!pilot_validTarget( p, pilot_stack[i] ))
507          continue;
508 
509       /* Minimum distance. */
510       td = pow2(x-pilot_stack[i]->solid->pos.x) + pow2(y-pilot_stack[i]->solid->pos.y);
511       if (((*tp==PLAYER_ID) || (td < d))) {
512          d = td;
513          *tp = pilot_stack[i]->id;
514       }
515    }
516    return d;
517 }
518 
519 
520 /**
521  * @brief Get the pilot closest to an angle extending from another pilot.
522  *
523  *    @param p Pilot to get the nearest pilot of.
524  *    @param[out] tp The nearest pilot.
525  *    @param ang Angle to compare against.
526  *    @param disabled Whether to return disabled pilots.
527  *    @return Angle between the pilot and the nearest pilot.
528  */
pilot_getNearestAng(const Pilot * p,unsigned int * tp,double ang,int disabled)529 double pilot_getNearestAng( const Pilot *p, unsigned int *tp, double ang, int disabled )
530 {
531    int i;
532    double a, ta;
533    double rx, ry;
534 
535    *tp = PLAYER_ID;
536    a   = ang + M_PI;
537    for (i=0; i<pilot_nstack; i++) {
538 
539       /* Must not be self. */
540       if (pilot_stack[i] == p)
541          continue;
542 
543       /* Player doesn't select escorts (unless disabled is active). */
544       if (!disabled && (p->faction == FACTION_PLAYER) &&
545             (pilot_stack[i]->faction == FACTION_PLAYER))
546          continue;
547 
548       /* Shouldn't be disabled. */
549       if (!disabled && pilot_isDisabled(pilot_stack[i]))
550          continue;
551 
552       /* Must be a valid target. */
553       if (!pilot_validTarget( p, pilot_stack[i] ))
554          continue;
555 
556       /* Must be in range. */
557       if (!pilot_inRangePilot( p, pilot_stack[i] ))
558          continue;
559 
560       /* Only allow selection if off-screen. */
561       if (gui_onScreenPilot( &rx, &ry, pilot_stack[i] ))
562          continue;
563 
564       ta = atan2( p->solid->pos.y - pilot_stack[i]->solid->pos.y,
565             p->solid->pos.x - pilot_stack[i]->solid->pos.x );
566       if ( ABS(angle_diff(ang, ta)) < ABS(angle_diff(ang, a))) {
567          a = ta;
568          *tp = pilot_stack[i]->id;
569       }
570    }
571    return a;
572 }
573 
574 
575 /**
576  * @brief Pulls a pilot out of the pilot_stack based on ID.
577  *
578  * It's a binary search ( O(logn) ) therefore it's pretty fast and can be
579  *  abused all the time.  Maximum iterations is 32 on a platform with 32 bit
580  *  unsigned ints.
581  *
582  *    @param id ID of the pilot to get.
583  *    @return The actual pilot who has matching ID or NULL if not found.
584  */
pilot_get(const unsigned int id)585 Pilot* pilot_get( const unsigned int id )
586 {
587    int m;
588 
589    if (id==PLAYER_ID)
590       return player.p; /* special case player.p */
591 
592    m = pilot_getStackPos(id);
593 
594    if ((m==-1) || (pilot_isFlag(pilot_stack[m], PILOT_DELETE)))
595       return NULL;
596    else
597       return pilot_stack[m];
598 }
599 
600 
601 /**
602  * @brief Sets the pilot's thrust.
603  */
pilot_setThrust(Pilot * p,double thrust)604 void pilot_setThrust( Pilot *p, double thrust )
605 {
606    p->solid->thrust = p->thrust * thrust;
607 }
608 
609 
610 /**
611  * @brief Sets the pilot's turn.
612  */
pilot_setTurn(Pilot * p,double turn)613 void pilot_setTurn( Pilot *p, double turn )
614 {
615    p->solid->dir_vel = p->turn * turn;
616 }
617 
618 
619 /**
620  * @brief Checks to see if pilot is hostile to the player.
621  *
622  *    @param p Player to see if is hostile.
623  *    @return 1 if pilot is hostile to the player.
624  */
pilot_isHostile(const Pilot * p)625 int pilot_isHostile( const Pilot *p )
626 {
627    if (pilot_isFlag(p, PILOT_FRIENDLY))
628       return 0;
629    if (pilot_isFlag(p, PILOT_HOSTILE) ||
630          areEnemies(FACTION_PLAYER,p->faction))
631       return 1;
632    return 0;
633 }
634 
635 
636 /**
637  * @brief Checks to see if pilot is neutral to the player.
638  *
639  *    @param p Player to see if is neutral.
640  *    @return 1 if pilot is neutral to the player.
641  */
pilot_isNeutral(const Pilot * p)642 int pilot_isNeutral( const Pilot *p )
643 {
644    if (!pilot_isHostile(p) && !pilot_isFriendly(p))
645       return 1;
646    return 0;
647 }
648 
649 
650 /**
651  * @brief Checks to see if pilot is friendly to the player.
652  *
653  *    @param p Player to see if is friendly.
654  *    @return 1 if pilot is friendly to the player.
655  */
pilot_isFriendly(const Pilot * p)656 int pilot_isFriendly( const Pilot *p )
657 {
658    if (pilot_isFlag(p, PILOT_HOSTILE))
659       return 0;
660    if (pilot_isFlag(p, PILOT_FRIENDLY) ||
661          areAllies(FACTION_PLAYER,p->faction))
662       return 1;
663    return 0;
664 }
665 
666 
667 /**
668  * @brief Tries to turn the pilot to face dir.
669  *
670  * Sets the direction velocity property of the pilot's solid, does not
671  *  directly manipulate the direction.
672  *
673  *    @param p Pilot to turn.
674  *    @param dir Direction to attempt to face.
675  *    @return The distance left to turn to match dir.
676  */
pilot_face(Pilot * p,const double dir)677 double pilot_face( Pilot* p, const double dir )
678 {
679    double diff, turn;
680 
681    diff = angle_diff( p->solid->dir, dir );
682    turn = CLAMP( -1., 1., -10.*diff );
683    pilot_setTurn( p, -turn );
684 
685    return diff;
686 }
687 
688 
689 /**
690  * @brief Causes the pilot to turn around and brake.
691  *
692  *    @param Pilot to brake.
693  *    @return 1 when braking has finished.
694  */
pilot_brake(Pilot * p)695 int pilot_brake( Pilot *p )
696 {
697    double dir, thrust, diff, ftime, btime;
698 
699    /* Face backwards by default. */
700    dir    = VANGLE(p->solid->vel) + M_PI;
701    thrust = 1.;
702 
703    if (p->stats.misc_reverse_thrust) {
704       /* Calculate the time to face backward and apply forward thrust. */
705       diff = angle_diff(p->solid->dir, VANGLE(p->solid->vel) + M_PI);
706       btime = ABS(diff) / p->turn + MIN( VMOD(p->solid->vel), p->speed ) /
707             (p->thrust / p->solid->mass);
708 
709       /* Calculate the time to face forward and apply reverse thrust. */
710       diff = angle_diff(p->solid->dir, VANGLE(p->solid->vel));
711       ftime = ABS(diff) / p->turn + MIN( VMOD(p->solid->vel), p->speed ) /
712             (p->thrust / p->solid->mass * PILOT_REVERSE_THRUST);
713 
714       if (btime > ftime) {
715          dir    = VANGLE(p->solid->vel);
716          thrust = -PILOT_REVERSE_THRUST;
717       }
718    }
719 
720    diff = pilot_face(p, dir);
721    if (ABS(diff) < MAX_DIR_ERR && !pilot_isStopped(p))
722       pilot_setThrust(p, thrust);
723    else {
724       pilot_setThrust(p, 0.);
725       if (pilot_isStopped(p))
726          return 1;
727    }
728 
729    return 0;
730 }
731 
732 
733 /**
734  * @brief Gets the braking distance for a pilot.
735  *
736  *    @param p Pilot to get the braking distance of.
737  *    @param[out] pos Estimated final position once braked.
738  *    @return Estimated Braking distance based on current speed.
739  */
pilot_brakeDist(Pilot * p,Vector2d * pos)740 double pilot_brakeDist( Pilot *p, Vector2d *pos )
741 {
742    double fdiff, bdiff, ftime, btime;
743    double vang, speed, dist;
744 
745    if (pilot_isStopped(p)) {
746       if (pos != NULL)
747          *pos = p->solid->pos;
748 
749       return 0;
750    }
751 
752    vang  = VANGLE(p->solid->vel);
753    speed = MIN( VMOD(p->solid->vel), p->speed );
754 
755    /* Calculate the time to face backward and apply forward thrust. */
756    bdiff = angle_diff(p->solid->dir, vang + M_PI);
757    btime = ABS(bdiff) / p->turn + speed / (p->thrust / p->solid->mass);
758    dist  = (ABS(bdiff) / p->turn) * speed +
759          (speed / (p->thrust / p->solid->mass)) * (speed / 2.);
760 
761    if (p->stats.misc_reverse_thrust) {
762       /* Calculate the time to face forward and apply reverse thrust. */
763       fdiff = angle_diff(p->solid->dir, vang);
764       ftime = ABS(fdiff) / p->turn + speed /
765             (p->thrust / p->solid->mass * PILOT_REVERSE_THRUST);
766 
767       /* Faster to use reverse thrust. */
768       if (ftime < btime)
769          dist = (ABS(fdiff) / p->turn) * speed + (speed /
770                (p->thrust / p->solid->mass * PILOT_REVERSE_THRUST)) * (speed / 2.);
771    }
772 
773    if (pos != NULL)
774       vect_cset( pos,
775             p->solid->pos.x + cos(vang) * dist,
776             p->solid->pos.y + sin(vang) * dist);
777 
778    return dist;
779 }
780 
781 
782 /**
783  * @brief Attempts to make the pilot pass through a given point.
784  *
785  * @todo Rewrite this using a superior method.
786  *
787  *    @param p Pilot to control.
788  *    @param x Destination X position.
789  *    @param y Destination Y position.
790  *    @return 1 if pilot will pass through the point, 0 otherwise.
791  */
pilot_interceptPos(Pilot * p,double x,double y)792 int pilot_interceptPos( Pilot *p, double x, double y )
793 {
794    double px, py, target, face, diff, fdiff;
795 
796    px = p->solid->pos.x;
797    py = p->solid->pos.y;
798 
799    /* Target angle for the pilot's vel */
800    target = atan2( y - py, x - px );
801 
802    /* Current angle error. */
803    diff = angle_diff( VANGLE(p->solid->vel), target );
804 
805    if (ABS(diff) < MIN_DIR_ERR) {
806       pilot_setThrust(p, 0.);
807       return 1;
808    }
809    else if (ABS(diff) > M_PI / 1.5) {
810       face = target;
811       fdiff = pilot_face(p, face);
812 
813       /* Apply thrust if within 180 degrees. */
814       if (FABS(fdiff) < M_PI)
815          pilot_setThrust(p, 1.);
816       else
817          pilot_setThrust(p, 0.);
818 
819       return 0;
820    }
821    else if (diff > M_PI_4)
822       face = target + M_PI_4;
823    else if (diff < -M_PI_4)
824       face = target - M_PI_4;
825    else
826       face = target + diff;
827 
828    fdiff = pilot_face(p, face);
829 
830    /* Must be in proper quadrant, +/- 45 degrees. */
831    if (fdiff < M_PI_4)
832       pilot_setThrust(p, 1.);
833    else
834       pilot_setThrust(p, 0.);
835 
836    return 0;
837 }
838 
839 
840 /**
841  * @brief Begins active cooldown, reducing hull and outfit temperatures.
842  *
843  *    @param Pilot that should cool down.
844  */
pilot_cooldown(Pilot * p)845 void pilot_cooldown( Pilot *p )
846 {
847    int i;
848    double heat_capacity, heat_mean;
849    PilotOutfitSlot *o;
850 
851    /* Brake if necessary. */
852    if (!pilot_isStopped(p)) {
853       pilot_setFlag(p, PILOT_BRAKING);
854       pilot_setFlag(p, PILOT_COOLDOWN_BRAKE);
855       return;
856    }
857    else {
858       pilot_rmFlag(p, PILOT_BRAKING);
859       pilot_rmFlag(p, PILOT_COOLDOWN_BRAKE);
860    }
861 
862    if (p->id == PLAYER_ID)
863       player_message("\epActive cooldown engaged.");
864 
865    /* Disable active outfits. */
866    if (pilot_outfitOffAll( p ) > 0)
867       pilot_calcStats( p );
868 
869    /* Calculate the ship's overall heat. */
870    heat_capacity = p->heat_C;
871    heat_mean = p->heat_T * p->heat_C;
872    for (i=0; i<p->noutfits; i++) {
873       o = p->outfits[i];
874       o->heat_start = o->heat_T;
875       heat_capacity += p->outfits[i]->heat_C;
876       heat_mean += o->heat_T * o->heat_C;
877    }
878 
879    /* Paranoia - a negative mean heat will result in NaN cdelay. */
880    heat_mean = MAX( heat_mean, CONST_SPACE_STAR_TEMP );
881 
882    heat_mean /= heat_capacity;
883 
884    /*
885     * Base delay of about 9.5s for a Lancelot, 32.8s for a Peacemaker.
886     *
887     * Super heat penalty table:
888     *    300K:  13.4%
889     *    350K:  31.8%
890     *    400K:  52.8%
891     *    450K:  75.6%
892     *    500K: 100.0%
893     */
894    p->cdelay = (5. + sqrt(p->base_mass) / 2.) *
895          (1. + pow(heat_mean / CONST_SPACE_STAR_TEMP - 1., 1.25));
896    p->ctimer = p->cdelay;
897    p->heat_start = p->heat_T;
898    pilot_setFlag(p, PILOT_COOLDOWN);
899 }
900 
901 
902 /**
903  * @brief Terminates active cooldown.
904  *
905  *    @param Pilot to stop cooling.
906  *    @param Reason for the termination.
907  */
pilot_cooldownEnd(Pilot * p,const char * reason)908 void pilot_cooldownEnd( Pilot *p, const char *reason )
909 {
910    if (pilot_isFlag(p, PILOT_COOLDOWN_BRAKE)) {
911       pilot_rmFlag(p, PILOT_COOLDOWN_BRAKE);
912       return;
913    }
914 
915    /* Send message to player. */
916    if (p->id == PLAYER_ID) {
917       if (p->ctimer < 0.)
918          player_message("\epActive cooldown completed.");
919       else {
920          if (reason != NULL)
921             player_message("\erActive cooldown aborted: %s!", reason);
922          else
923             player_message("\erActive cooldown aborted!");
924       }
925    }
926 
927    pilot_rmFlag(p, PILOT_COOLDOWN);
928 
929    /* Cooldown finished naturally, reset heat just in case. */
930    if (p->ctimer < 0.)
931       pilot_heatReset( p );
932 }
933 
934 
935 /**
936  * @brief Marks pilot as hostile to player.
937  *
938  *    @param p Pilot to mark as hostile to player.
939  */
pilot_setHostile(Pilot * p)940 void pilot_setHostile( Pilot* p )
941 {
942    if (!pilot_isFlag(p, PILOT_HOSTILE)) {
943       /* Time to play combat music. */
944       if (player.enemies == 0)
945          music_choose("combat");
946 
947       player.enemies++;
948       pilot_setFlag(p, PILOT_HOSTILE);
949    }
950 }
951 
952 
953 /**
954  * @brief Gets the faction colour char, works like faction_getColourChar but for a pilot.
955  *
956  * @sa faction_getColourChar
957  */
pilot_getFactionColourChar(const Pilot * p)958 char pilot_getFactionColourChar( const Pilot *p )
959 {
960    if (pilot_isDisabled(p))
961       return 'I';
962    else if (pilot_isFlag(p, PILOT_BRIBED))
963       return 'N';
964    else if (pilot_isFlag(p, PILOT_HOSTILE))
965       return 'H';
966    return faction_getColourChar(p->faction);
967 }
968 
969 
970 /**
971  * @brief Sets the overhead communication message of the pilot.
972  */
pilot_setCommMsg(Pilot * p,const char * s)973 static void pilot_setCommMsg( Pilot *p, const char *s )
974 {
975    /* Free previous message. */
976    if (p->comm_msg != NULL)
977       free(p->comm_msg);
978 
979    /* Duplicate the message. */
980    p->comm_msg       = strdup(s);
981    p->comm_msgWidth  = gl_printWidthRaw( NULL, s );
982    p->comm_msgTimer  = pilot_commTimeout;
983 }
984 
985 
986 /**
987  * @brief Have pilot send a message to another.
988  *
989  *    @param p Pilot sending message.
990  *    @param target Target of the message.
991  *    @param msg The message.
992  *    @param ignore_int Whether or not should ignore interference.
993  */
pilot_message(Pilot * p,unsigned int target,const char * msg,int ignore_int)994 void pilot_message( Pilot *p, unsigned int target, const char *msg, int ignore_int )
995 {
996    Pilot *t;
997    char c;
998 
999    /* Makes no sense with no player.p atm. */
1000    if (player.p==NULL)
1001       return;
1002 
1003    /* Get the target. */
1004    t = pilot_get(target);
1005    if (t == NULL)
1006       return;
1007 
1008    /* Must be in range. */
1009    if (!ignore_int && !pilot_inRangePilot( player.p, p ))
1010       return;
1011 
1012    /* Only really affects player.p atm. */
1013    if (target == PLAYER_ID) {
1014       c = pilot_getFactionColourChar( p );
1015       player_message( "\e%cComm %s>\e0 \"%s\"", c, p->name, msg );
1016 
1017       /* Set comm message. */
1018       pilot_setCommMsg( p, msg );
1019    }
1020 }
1021 
1022 
1023 /**
1024  * @brief Has the pilot broadcast a message.
1025  *
1026  *    @param p Pilot to broadcast the message.
1027  *    @param msg Message to broadcast.
1028  *    @param ignore_int Whether or not should ignore interference.
1029  */
pilot_broadcast(Pilot * p,const char * msg,int ignore_int)1030 void pilot_broadcast( Pilot *p, const char *msg, int ignore_int )
1031 {
1032    char c;
1033 
1034    /* Only display if player.p exists and is in range. */
1035    if (player.p==NULL)
1036       return;
1037 
1038    /* Check if should ignore interference. */
1039    if (!ignore_int && !pilot_inRangePilot( player.p, p ))
1040       return;
1041 
1042    c = pilot_getFactionColourChar( p );
1043    player_message( "\e%cBroadcast %s>\e0 \"%s\"", c, p->name, msg );
1044 
1045    /* Set comm message. */
1046    pilot_setCommMsg( p, msg );
1047 }
1048 
1049 
1050 /**
1051  * @brief Has the pilot broadcast a distress signal.
1052  *
1053  * Can do a faction hit on the player.
1054  *
1055  *    @param p Pilot sending the distress signal.
1056  *    @param attacker Attacking pilot.
1057  *    @param msg Message in distress signal.
1058  *    @param ignore_int Whether or not should ignore interference.
1059  */
pilot_distress(Pilot * p,Pilot * attacker,const char * msg,int ignore_int)1060 void pilot_distress( Pilot *p, Pilot *attacker, const char *msg, int ignore_int )
1061 {
1062    int i, r;
1063    double d;
1064 
1065    /* Broadcast the message. */
1066    if (msg[0] != '\0')
1067       pilot_broadcast( p, msg, ignore_int );
1068 
1069    /* Use the victim's target if the attacker is unknown. */
1070    if (attacker == NULL)
1071       attacker = pilot_get( p->target );
1072 
1073    /* Now proceed to see if player.p should incur faction loss because
1074     * of the broadcast signal. */
1075 
1076    /* Consider not in range at first. */
1077    r = 0;
1078 
1079    if ((attacker == player.p) && !pilot_isFlag(p, PILOT_DISTRESSED)) {
1080       /* Check if planet is in range. */
1081       for (i=0; i<cur_system->nplanets; i++) {
1082          if (planet_hasService(cur_system->planets[i], PLANET_SERVICE_INHABITED) &&
1083                (!ignore_int && pilot_inRangePlanet(p, i)) &&
1084                !areEnemies(p->faction, cur_system->planets[i]->faction)) {
1085             r = 1;
1086             break;
1087          }
1088       }
1089    }
1090 
1091    /* Now we must check to see if a pilot is in range. */
1092    for (i=0; i<pilot_nstack; i++) {
1093       /* Skip if unsuitable. */
1094       if ((pilot_stack[i]->ai == NULL) || (pilot_stack[i]->id == p->id) ||
1095             (pilot_isFlag(pilot_stack[i], PILOT_DEAD)) ||
1096             (pilot_isFlag(pilot_stack[i], PILOT_DELETE)))
1097          continue;
1098 
1099       if (!ignore_int) {
1100          if (!pilot_inRangePilot(p, pilot_stack[i])) {
1101             /*
1102              * If the pilots are within sensor range of each other, send the
1103              * distress signal, regardless of electronic warfare hide values.
1104              */
1105             d = vect_dist2( &p->solid->pos, &pilot_stack[i]->solid->pos );
1106             if (d > pilot_sensorRange())
1107                continue;
1108          }
1109 
1110          /* Send AI the distress signal. */
1111          ai_getDistress( pilot_stack[i], p, attacker );
1112 
1113          /* Check if should take faction hit. */
1114          if ((attacker == player.p) && !pilot_isFlag(p, PILOT_DISTRESSED) &&
1115                !areEnemies(p->faction, pilot_stack[i]->faction))
1116             r = 1;
1117       }
1118    }
1119 
1120    /* Player only gets one faction hit per pilot. */
1121    if (!pilot_isFlag(p, PILOT_DISTRESSED)) {
1122 
1123       /* Modify faction, about 1 for a llama, 4.2 for a hawking */
1124       if ((attacker != NULL) && (attacker->faction == FACTION_PLAYER) && r)
1125          faction_modPlayer( p->faction, -(pow(p->base_mass, 0.2) - 1.), "distress" );
1126 
1127       /* Set flag to avoid a second faction hit. */
1128       pilot_setFlag(p, PILOT_DISTRESSED);
1129    }
1130 }
1131 
1132 
1133 /**
1134  * @brief Unmarks a pilot as hostile to player.
1135  *
1136  *    @param p Pilot to mark as hostile to player.
1137  */
pilot_rmHostile(Pilot * p)1138 void pilot_rmHostile( Pilot* p )
1139 {
1140    if (pilot_isFlag(p, PILOT_HOSTILE)) {
1141       if (!pilot_isDisabled(p))
1142          player.enemies--;
1143       pilot_rmFlag(p, PILOT_HOSTILE);
1144 
1145       /* Change music back to ambient if no more enemies. */
1146       if (player.enemies <= 0) {
1147          music_choose("ambient");
1148          player.enemies = 0;
1149       }
1150    }
1151 }
1152 
1153 
1154 /**
1155  * @brief Marks pilot as friendly to player.
1156  *
1157  *    @param p Pilot to mark as friendly to player.
1158  */
pilot_setFriendly(Pilot * p)1159 void pilot_setFriendly( Pilot* p )
1160 {
1161    pilot_rmHostile(p);
1162    pilot_setFlag(p, PILOT_FRIENDLY);
1163 }
1164 
1165 
1166 /**
1167  * @brief Unmarks a pilot as friendly to player.
1168  *
1169  *    @param p Pilot to mark as friendly to player.
1170  */
pilot_rmFriendly(Pilot * p)1171 void pilot_rmFriendly( Pilot* p )
1172 {
1173    pilot_rmFlag(p, PILOT_FRIENDLY);
1174 }
1175 
1176 
1177 /**
1178  * @brief Gets the amount of jumps the pilot has left.
1179  *
1180  *    @param p Pilot to get the jumps left.
1181  *    @return Number of jumps the pilot has left.
1182  */
pilot_getJumps(const Pilot * p)1183 int pilot_getJumps( const Pilot* p )
1184 {
1185    return (int)floor(p->fuel / p->fuel_consumption);
1186 }
1187 
1188 
1189 /**
1190  * @brief Gets a pilot's colour.
1191  *
1192  *    @param p Pilot to get colour of.
1193  *    @return The colour of the pilot.
1194  */
pilot_getColour(const Pilot * p)1195 const glColour* pilot_getColour( const Pilot* p )
1196 {
1197    const glColour *col;
1198 
1199    if (pilot_inRangePilot(player.p, p) == -1) col = &cMapNeutral;
1200    else if (pilot_isDisabled(p) || pilot_isFlag(p,PILOT_DEAD)) col = &cInert;
1201    else if (pilot_isFlag(p,PILOT_BRIBED)) col = &cNeutral;
1202    else if (pilot_isHostile(p)) col = &cHostile;
1203    else if (pilot_isFriendly(p)) col = &cFriend;
1204    else col = faction_getColour(p->faction);
1205 
1206    return col;
1207 }
1208 
1209 
1210 /**
1211  * @brief Sets the target of the pilot.
1212  *
1213  *    @param p Pilot to set target of.
1214  *    @param id ID of the target (set to p->id for none).
1215  */
pilot_setTarget(Pilot * p,unsigned int id)1216 void pilot_setTarget( Pilot* p, unsigned int id )
1217 {
1218    /* Case no change. */
1219    if (p->target == id)
1220       return;
1221 
1222    p->target = id;
1223    pilot_lockClear( p );
1224 }
1225 
1226 
1227 /**
1228  * @brief Damages the pilot.
1229  *
1230  *    @param p Pilot that is taking damage.
1231  *    @param w Solid that is hitting pilot.
1232  *    @param shooter Attacker that shot the pilot.
1233  *    @param dmg Damage being done.
1234  *    @param reset Whether the shield timer should be reset.
1235  *    @return The real damage done.
1236  */
pilot_hit(Pilot * p,const Solid * w,const unsigned int shooter,const Damage * dmg,int reset)1237 double pilot_hit( Pilot* p, const Solid* w, const unsigned int shooter,
1238       const Damage *dmg, int reset )
1239 {
1240    int mod;
1241    double damage_shield, damage_armour, disable, knockback, dam_mod, ddmg, absorb, dmod, start;
1242    Pilot *pshooter;
1243 
1244    /* Invincible means no damage. */
1245    if (pilot_isFlag( p, PILOT_INVINCIBLE) ||
1246          pilot_isFlag( p, PILOT_INVISIBLE))
1247       return 0.;
1248 
1249    /* Defaults. */
1250    pshooter       = NULL;
1251    dam_mod        = 0.;
1252    ddmg           = 0.;
1253 
1254    /* Calculate the damage. */
1255    absorb         = 1. - CLAMP( 0., 1., p->dmg_absorb - dmg->penetration );
1256    disable        = dmg->disable;
1257    dtype_calcDamage( &damage_shield, &damage_armour, absorb, &knockback, dmg, &p->stats );
1258 
1259    /*
1260     * Delay undisable if necessary. Amount varies with damage, as e.g. a
1261     * single Laser Cannon shot should not reset a Peacemaker's timer.
1262     */
1263    if (!pilot_isFlag(p, PILOT_DEAD) && (p->dtimer_accum > 0.))
1264       p->dtimer_accum -= MIN( pow(disable, .8), p->dtimer_accum );
1265 
1266    /* Ships that can not be disabled take raw armour damage instead of getting disabled. */
1267    if (pilot_isFlag( p, PILOT_NODISABLE )) {
1268       damage_armour += disable * absorb;
1269       disable        = 0.;
1270    }
1271    else
1272       disable       *= absorb;
1273 
1274    /*
1275     * Shields take entire blow.
1276     */
1277    if (p->shield - damage_shield > 0.) {
1278       start      = p->shield;
1279       ddmg       = damage_shield;
1280       p->shield -= damage_shield;
1281       dam_mod    = damage_shield/p->shield_max;
1282 
1283       /*
1284        * Disabling damage leaks accordingly:
1285        *    50% + (50% - mean shield % / 2)
1286        *
1287        * 50% leaks at 100% shield, scales to 100% by 0% shield.
1288        *
1289        * The damage is adjusted based on the mean of the starting and ending
1290        * shield percentages. Using the starting percentage biases towards
1291        * low-damage, high-ROF weapons, while using the ending percentage
1292        * biases towards high-damage, low-ROF weapons.
1293        */
1294       p->stress += disable * (.5 + (.5 - ((start+p->shield) / p->shield_max) / 4.));
1295    }
1296    /*
1297     * Shields take part of the blow.
1298     */
1299    else if (p->shield > 0.) {
1300       start      = p->shield;
1301       dmod        = (1. - p->shield/damage_shield);
1302       ddmg        = p->shield + dmod * damage_armour;
1303       p->shield   = 0.;
1304 
1305       /* Leak some disabling damage through the remaining bit of shields. */
1306       p->stress += disable * (1. - dmod) * (.5 + (.5 - (start / p->shield_max / 4.)));
1307 
1308       /* Reduce stress as armour is eaten away. */
1309       p->stress  *= (p->armour - dmod * damage_armour) / p->armour;
1310       p->armour  -= dmod * damage_armour;
1311       p->stress  += dmod * disable;
1312       dam_mod     = (damage_shield + damage_armour) /
1313                    ((p->shield_max + p->armour_max) / 2.);
1314 
1315       /* Increment shield timer or time before shield regeneration kicks in. */
1316       if (reset) {
1317          p->stimer   = 3.;
1318          p->sbonus   = 3.;
1319       }
1320    }
1321    /*
1322     * Armour takes the entire blow.
1323     */
1324    else if (p->armour > 0.) {
1325       ddmg        = damage_armour;
1326       /* Reduce stress as armour is eaten away. */
1327       p->stress  *= (p->armour - damage_armour) / p->armour;
1328       p->armour  -= damage_armour;
1329       p->stress  += disable;
1330 
1331       /* Increment shield timer or time before shield regeneration kicks in. */
1332       if (reset) {
1333          p->stimer  = 3.;
1334          p->sbonus  = 3.;
1335       }
1336    }
1337 
1338    /* Ensure stress never exceeds remaining armour. */
1339    if (p->stress > p->armour)
1340       p->stress = p->armour;
1341 
1342    /* Disabled always run before dead to ensure combat rating boost. */
1343    pilot_updateDisable(p, shooter);
1344 
1345    /* Do not let pilot die. */
1346    if (pilot_isFlag( p, PILOT_NODEATH ))
1347       p->armour = 1.;
1348 
1349    /* Officially dead. */
1350    if (p->armour <= 0.) {
1351       p->armour = 0.;
1352       dam_mod   = 0.;
1353 
1354       if (!pilot_isFlag(p, PILOT_DEAD)) {
1355          pilot_dead( p, shooter );
1356 
1357          /* adjust the combat rating based on pilot mass and ditto faction */
1358          if (pshooter == NULL)
1359             pshooter = pilot_get(shooter);
1360          if ((pshooter != NULL) && (pshooter->faction == FACTION_PLAYER)) {
1361 
1362             /* About 6 for a llama, 52 for hawking. */
1363             mod = 2 * (pow(p->base_mass, 0.4) - 1.);
1364 
1365             /* Modify faction for him and friends. */
1366             faction_modPlayer( p->faction, -mod, "kill" );
1367          }
1368       }
1369    }
1370 
1371    /* Some minor effects and stuff. */
1372    else if (p->shield <= 0.) {
1373       dam_mod = damage_armour/p->armour_max;
1374 
1375       if (p->id == PLAYER_ID) /* a bit of shaking */
1376          spfx_shake( SHAKE_MAX*dam_mod );
1377    }
1378 
1379 
1380    if (w != NULL)
1381       /* knock back effect is dependent on both damage and mass of the weapon
1382        * should probably get turned into a partial conservative collision */
1383       vect_cadd( &p->solid->vel,
1384             knockback * (w->vel.x * (dam_mod/9. + w->mass/p->solid->mass/6.)),
1385             knockback * (w->vel.y * (dam_mod/9. + w->mass/p->solid->mass/6.)) );
1386 
1387    return ddmg;
1388 }
1389 
1390 
1391 /**
1392  * @brief Handles pilot disabling. Set or unset the disable status depending on health and stress values.
1393  *
1394  *    @param p The pilot in question.
1395  *    @param shooter Attacker that shot the pilot.
1396  */
pilot_updateDisable(Pilot * p,const unsigned int shooter)1397 void pilot_updateDisable( Pilot* p, const unsigned int shooter )
1398 {
1399    int mod, h;
1400    Pilot *pshooter;
1401    HookParam hparam;
1402 
1403    if ((!pilot_isFlag(p, PILOT_DISABLED)) &&
1404        (!pilot_isFlag(p, PILOT_NODISABLE) || (p->armour <= 0.)) &&
1405        (p->armour <= p->stress)) { /* Pilot should be disabled. */
1406 
1407       /* Cooldown is an active process, so cancel it. */
1408       if (pilot_isFlag(p, PILOT_COOLDOWN))
1409          pilot_cooldownEnd(p, NULL);
1410 
1411       /* Clear other active states. */
1412       pilot_rmFlag(p, PILOT_COOLDOWN_BRAKE);
1413       pilot_rmFlag(p, PILOT_BRAKING);
1414 
1415       /* Clear hyperspace flags. */
1416       pilot_rmFlag(p, PILOT_HYP_PREP);
1417       pilot_rmFlag(p, PILOT_HYP_BEGIN);
1418       pilot_rmFlag(p, PILOT_HYP_BRAKE);
1419       pilot_rmFlag(p, PILOT_HYPERSPACE);
1420 
1421       /* If hostile, must remove counter. */
1422       h = (pilot_isHostile(p)) ? 1 : 0;
1423       pilot_rmHostile(p);
1424       if (h == 1) /* Horrible hack to make sure player.p can hit it if it was hostile. */
1425          /* Do not use pilot_setHostile here or music will change again. */
1426          pilot_setFlag(p,PILOT_HOSTILE);
1427 
1428       /* Modify player combat rating if applicable. */
1429       /* TODO: Base off something more sensible than mass. */
1430       pshooter = pilot_get(shooter);
1431       if ((pshooter != NULL) && (pshooter->faction == FACTION_PLAYER)) {
1432          /* About 3 for a llama, 26 for hawking. */
1433          mod = pow(p->base_mass, 0.4) - 1.;
1434 
1435          /* Modify combat rating. */
1436          player.crating += 2*mod;
1437       }
1438 
1439       /* Disabled ships don't use up presence. */
1440       if (p->presence > 0) {
1441          system_rmCurrentPresence( cur_system, p->faction, p->presence );
1442          p->presence = 0;
1443       }
1444 
1445       /* Set disable timer. This is the time the pilot will remain disabled. */
1446       /* 50 armour llama        => 53.18s
1447        * 5000 armour peacemaker => 168.18s
1448        */
1449       p->dtimer = 20. * pow( p->armour, 0.25 );
1450       p->dtimer_accum = 0.;
1451 
1452       /* Disable active outfits. */
1453       if (pilot_outfitOffAll( p ) > 0)
1454          pilot_calcStats( p );
1455 
1456       pilot_setFlag( p,PILOT_DISABLED ); /* set as disabled */
1457       /* Run hook */
1458       if (shooter > 0) {
1459          hparam.type       = HOOK_PARAM_PILOT;
1460          hparam.u.lp       = shooter;
1461       }
1462       else {
1463          hparam.type       = HOOK_PARAM_NIL;
1464          pilot_setFlag(p, PILOT_DISABLED_PERM ); /* Set as permanently disabled, since the disable was script-induced. */
1465       }
1466       pilot_runHookParam( p, PILOT_HOOK_DISABLE, &hparam, 1 ); /* Already disabled. */
1467    }
1468    else if (pilot_isFlag(p, PILOT_DISABLED) && (p->armour > p->stress)) { /* Pilot is disabled, but shouldn't be. */
1469       pilot_rmFlag( p, PILOT_DISABLED ); /* Undisable. */
1470       pilot_rmFlag( p, PILOT_DISABLED_PERM ); /* Clear perma-disable flag if necessary. */
1471       pilot_rmFlag( p, PILOT_BOARDING ); /* Can get boarded again. */
1472 
1473       /* Reset the accumulated disable time. */
1474       p->dtimer_accum = 0.;
1475 
1476       /* TODO: Make undisabled pilot use up presence again. */
1477       pilot_runHook( p, PILOT_HOOK_UNDISABLE );
1478 
1479       /* This is sort of a hack to make sure it gets reset... */
1480       if (p->id==PLAYER_ID)
1481          pause_setSpeed( player_isFlag(PLAYER_DOUBLESPEED) ? 2. : 1. );
1482    }
1483 }
1484 
1485 /**
1486  * @brief Pilot is dead, now will slowly explode.
1487  *
1488  *    @param p Pilot that just died.
1489  *    @param killer Pilot killer or 0 if invalid.
1490  */
pilot_dead(Pilot * p,unsigned int killer)1491 static void pilot_dead( Pilot* p, unsigned int killer )
1492 {
1493    HookParam hparam;
1494 
1495    if (pilot_isFlag(p,PILOT_DEAD))
1496       return; /* he's already dead */
1497 
1498    /* basically just set timers */
1499    if (p->id==PLAYER_ID) {
1500       pilot_setFlag(p, PILOT_DISABLED );
1501       player_dead();
1502    }
1503    p->timer[0] = 0.; /* no need for AI anymore */
1504    p->ptimer = MIN( 1. + sqrt(p->armour_max * p->shield_max) / 650.,
1505          3 + pow(p->armour_max * p->shield_max, 0.4) / 500);
1506    p->timer[1] = 0.; /* explosion timer */
1507 
1508    /* flag cleanup - fixes some issues */
1509    pilot_rmFlag(p,PILOT_HYP_PREP);
1510    pilot_rmFlag(p,PILOT_HYP_BEGIN);
1511    pilot_rmFlag(p,PILOT_HYP_BRAKE);
1512    pilot_rmFlag(p,PILOT_HYPERSPACE);
1513 
1514    /* Pilot must die before setting death flag and probably messing with other flags. */
1515    if (killer > 0) {
1516       hparam.type       = HOOK_PARAM_PILOT;
1517       hparam.u.lp       = killer;
1518    }
1519    else
1520       hparam.type       = HOOK_PARAM_NIL;
1521    pilot_runHookParam( p, PILOT_HOOK_DEATH, &hparam, 1 );
1522 
1523    /* PILOT R OFFICIALLY DEADZ0R */
1524    pilot_setFlag( p, PILOT_DEAD );
1525 }
1526 
1527 
1528 /**
1529  * @brief Makes the pilot explosion.
1530  *    @param x X position of the pilot.
1531  *    @param y Y position of the pilot.
1532  *    @param radius Radius of the explosion.
1533  *    @param dmg Damage of the explosion.
1534  *    @param parent The exploding pilot.
1535  */
pilot_explode(double x,double y,double radius,const Damage * dmg,const Pilot * parent)1536 void pilot_explode( double x, double y, double radius, const Damage *dmg, const Pilot *parent )
1537 {
1538    int i;
1539    double rx, ry;
1540    double dist, rad2;
1541    Pilot *p;
1542    Solid s; /* Only need to manipulate mass and vel. */
1543    Damage ddmg;
1544 
1545    rad2 = radius*radius;
1546    ddmg = *dmg;
1547 
1548    for (i=0; i<pilot_nstack; i++) {
1549       p = pilot_stack[i];
1550 
1551       /* Calculate a bit. */
1552       rx = p->solid->pos.x - x;
1553       ry = p->solid->pos.y - y;
1554       dist = pow2(rx) + pow2(ry);
1555       /* Take into account ship size. */
1556       dist -= pow2(p->ship->gfx_space->sw);
1557       dist = MAX(0,dist);
1558 
1559       /* Pilot is hit. */
1560       if (dist < rad2) {
1561 
1562          /* Adjust damage based on distance. */
1563          ddmg.damage = dmg->damage * (1. - sqrt(dist / rad2));
1564 
1565          /* Impact settings. */
1566          s.mass =  pow2(dmg->damage) / 30.;
1567          s.vel.x = rx;
1568          s.vel.y = ry;
1569 
1570          /* Actual damage calculations. */
1571          pilot_hit( p, &s, (parent!=NULL) ? parent->id : 0, &ddmg, 1 );
1572 
1573          /* Shock wave from the explosion. */
1574          if (p->id == PILOT_PLAYER)
1575             spfx_shake( pow2(ddmg.damage) / pow2(100.) * SHAKE_MAX );
1576       }
1577    }
1578 }
1579 
1580 
1581 /**
1582  * @brief Renders the pilot.
1583  *
1584  *    @param p Pilot to render.
1585  *    @param dt Current deltatick.
1586  */
pilot_render(Pilot * p,const double dt)1587 void pilot_render( Pilot* p, const double dt )
1588 {
1589    (void) dt;
1590    double scalew, scaleh;
1591 
1592    /* Check if needs scaling. */
1593    if (pilot_isFlag( p, PILOT_LANDING )) {
1594       scalew = CLAMP( 0., 1., p->ptimer / PILOT_LANDING_DELAY );
1595       scaleh = scalew;
1596    }
1597    else if (pilot_isFlag( p, PILOT_TAKEOFF )) {
1598       scalew = CLAMP( 0., 1., 1. - p->ptimer / PILOT_TAKEOFF_DELAY );
1599       scaleh = scalew;
1600    }
1601    else {
1602       scalew = 1.;
1603       scaleh = 1.;
1604    }
1605 
1606    /* Base ship. */
1607    gl_blitSpriteInterpolateScale( p->ship->gfx_space, p->ship->gfx_engine,
1608          1.-p->engine_glow, p->solid->pos.x, p->solid->pos.y,
1609          scalew, scaleh,
1610          p->tsx, p->tsy, NULL );
1611 }
1612 
1613 
1614 /**
1615  * @brief Renders the pilot overlay.
1616  *
1617  *    @param p Pilot to render.
1618  *    @param dt Current deltatick.
1619  */
pilot_renderOverlay(Pilot * p,const double dt)1620 void pilot_renderOverlay( Pilot* p, const double dt )
1621 {
1622    glTexture *ico_hail;
1623    double x, y;
1624    double dx, dy;
1625    int sx, sy;
1626    glColour c;
1627 
1628    /* Render the hailing graphic if needed. */
1629    if (pilot_isFlag( p, PILOT_HAILING )) {
1630       ico_hail = gui_hailIcon();
1631       if (ico_hail != NULL) {
1632          /* Handle animation. */
1633          p->htimer -= dt;
1634          sx = (int)ico_hail->sx;
1635          sy = (int)ico_hail->sy;
1636          if (p->htimer < 0.) {
1637             p->htimer = .1;
1638             p->hail_pos++;
1639             p->hail_pos %= sx*sy;
1640          }
1641          /* Render. */
1642          gl_blitSprite( ico_hail,
1643                p->solid->pos.x + PILOT_SIZE_APROX*p->ship->gfx_space->sw/2. + ico_hail->sw/4.,
1644                p->solid->pos.y + PILOT_SIZE_APROX*p->ship->gfx_space->sh/2. + ico_hail->sh/4.,
1645                p->hail_pos % sx, p->hail_pos / sx, NULL );
1646       }
1647    }
1648 
1649    /* Text ontop if needed. */
1650    if (p->comm_msg != NULL) {
1651 
1652       /* Coordinate translation. */
1653       gl_gameToScreenCoords( &x, &y, p->solid->pos.x, p->solid->pos.y );
1654 
1655       /* Display the text. */
1656       p->comm_msgTimer -= dt;
1657       if (p->comm_msgTimer < 0.) {
1658          free(p->comm_msg);
1659          p->comm_msg = NULL;
1660       }
1661       else {
1662          /* Colour. */
1663          c.r = 1.;
1664          c.g = 1.;
1665          c.b = 1.;
1666          if (p->comm_msgTimer - pilot_commFade < 0.)
1667             c.a = p->comm_msgTimer / pilot_commFade;
1668          else
1669             c.a = 1.;
1670 
1671          /* Position to render at. */
1672          dx = x - p->comm_msgWidth/2.;
1673          dy = y + PILOT_SIZE_APROX*p->ship->gfx_space->sh/2.;
1674 
1675          /* Background. */
1676          gl_renderRect( dx-2., dy-2., p->comm_msgWidth+4., gl_defFont.h+4., &cBlackHilight );
1677 
1678          /* Display text. */
1679          gl_printRaw( NULL, dx, dy, &c, p->comm_msg );
1680       }
1681    }
1682 }
1683 
1684 
1685 /**
1686  * @brief Updates the pilot.
1687  *
1688  *    @param pilot Pilot to update.
1689  *    @param dt Current delta tick.
1690  */
pilot_update(Pilot * pilot,const double dt)1691 void pilot_update( Pilot* pilot, const double dt )
1692 {
1693    int i, cooling, nchg;
1694    unsigned int l;
1695    Pilot *target;
1696    double a, px,py, vx,vy;
1697    char buf[16];
1698    PilotOutfitSlot *o;
1699    double Q;
1700    Damage dmg;
1701    double stress_falloff;
1702    double efficiency, thrust;
1703 
1704    /* Check target sanity. */
1705    if (pilot->target != pilot->id) {
1706       target = pilot_get(pilot->target);
1707       if (target == NULL)
1708          pilot_setTarget( pilot, pilot->id );
1709    }
1710    else
1711       target = NULL;
1712 
1713    cooling = pilot_isFlag(pilot, PILOT_COOLDOWN);
1714 
1715    /*
1716     * Update timers.
1717     */
1718    pilot->ptimer   -= dt;
1719    pilot->tcontrol -= dt;
1720    if (cooling) {
1721       pilot->ctimer   -= dt;
1722       if (pilot->ctimer < 0.) {
1723          pilot_cooldownEnd(pilot, NULL);
1724          cooling = 0;
1725       }
1726    }
1727    pilot->stimer   -= dt;
1728    if (pilot->stimer <= 0.)
1729       pilot->sbonus   -= dt;
1730    for (i=0; i<MAX_AI_TIMERS; i++)
1731       if (pilot->timer[i] > 0.)
1732          pilot->timer[i] -= dt;
1733    /* Update heat. */
1734    a = -1.;
1735    Q = 0.;
1736    nchg = 0; /* Number of outfits that change state, processed at the end. */
1737    for (i=0; i<pilot->noutfits; i++) {
1738       o = pilot->outfits[i];
1739 
1740       /* Picky about our outfits. */
1741       if (o->outfit == NULL)
1742          continue;
1743       if (!o->active)
1744          continue;
1745 
1746       /* Handle firerate timer. */
1747       if (o->timer > 0.)
1748          o->timer -= dt * pilot_heatFireRateMod( o->heat_T );
1749 
1750       /* Handle state timer. */
1751       if (o->stimer >= 0.) {
1752          o->stimer -= dt;
1753          if (o->stimer < 0.) {
1754             if (o->state == PILOT_OUTFIT_ON) {
1755                pilot_outfitOff( pilot, o );
1756                nchg++;
1757             }
1758             else if (o->state == PILOT_OUTFIT_COOLDOWN) {
1759                o->state  = PILOT_OUTFIT_OFF;
1760                nchg++;
1761             }
1762          }
1763       }
1764 
1765       /* Handle heat. */
1766       if (!cooling)
1767          Q  += pilot_heatUpdateSlot( pilot, o, dt );
1768 
1769       /* Handle lockons. */
1770       pilot_lockUpdateSlot( pilot, o, target, &a, dt );
1771    }
1772 
1773    /* Global heat. */
1774    if (!cooling)
1775       pilot_heatUpdateShip( pilot, Q, dt );
1776    else
1777       pilot_heatUpdateCooldown( pilot );
1778 
1779    /* Update electronic warfare. */
1780    pilot_ewUpdateDynamic( pilot );
1781 
1782    /* Update stress. */
1783    if (!pilot_isFlag(pilot, PILOT_DISABLED)) { /* Case pilot is not disabled. */
1784       stress_falloff = 4.; /* TODO: make a function of the pilot's ship and/or its outfits. */
1785       pilot->stress -= stress_falloff * pilot->stats.stress_dissipation * dt;
1786       pilot->stress = MAX(pilot->stress, 0);
1787    }
1788    else if (!pilot_isFlag(pilot, PILOT_DISABLED_PERM)) { /* Case pilot is disabled (but not permanently so). */
1789       pilot->dtimer_accum += dt;
1790       if (pilot->dtimer_accum >= pilot->dtimer) {
1791          pilot->stress       = 0.;
1792          pilot->dtimer_accum = 0;
1793          pilot_updateDisable(pilot, 0);
1794       }
1795    }
1796 
1797    /* Handle takeoff/landing. */
1798    if (pilot_isFlag(pilot,PILOT_TAKEOFF)) {
1799       if (pilot->ptimer < 0.) {
1800          pilot_rmFlag(pilot,PILOT_TAKEOFF);
1801          return;
1802       }
1803    }
1804    else if (pilot_isFlag(pilot,PILOT_LANDING)) {
1805       if (pilot->ptimer < 0.) {
1806          if (pilot_isPlayer(pilot)) {
1807             player_setFlag( PLAYER_HOOK_LAND );
1808             pilot->ptimer = 0.;
1809          }
1810          else
1811             pilot_delete(pilot);
1812          return;
1813       }
1814    }
1815    /* he's dead jim */
1816    else if (pilot_isFlag(pilot,PILOT_DEAD)) {
1817 
1818       /* pilot death sound */
1819       if (!pilot_isFlag(pilot,PILOT_DEATH_SOUND) &&
1820             (pilot->ptimer < 0.050)) {
1821 
1822          /* Play random explosion sound. */
1823          nsnprintf(buf, sizeof(buf), "explosion%d", RNG(0,2));
1824          sound_playPos( sound_get(buf), pilot->solid->pos.x, pilot->solid->pos.y,
1825                pilot->solid->vel.x, pilot->solid->vel.y );
1826 
1827          pilot_setFlag(pilot,PILOT_DEATH_SOUND);
1828       }
1829       /* final explosion */
1830       else if (!pilot_isFlag(pilot,PILOT_EXPLODED) &&
1831             (pilot->ptimer < 0.200)) {
1832 
1833          /* Damage from explosion. */
1834          a                 = sqrt(pilot->solid->mass);
1835          dmg.type          = dtype_get("explosion_splash");
1836          dmg.damage        = MAX(0., 2. * (a * (1. + sqrt(pilot->fuel + 1.) / 28.)));
1837          dmg.penetration   = 1.; /* Full penetration. */
1838          dmg.disable       = 0.;
1839          expl_explode( pilot->solid->pos.x, pilot->solid->pos.y,
1840                pilot->solid->vel.x, pilot->solid->vel.y,
1841                pilot->ship->gfx_space->sw/2. + a, &dmg, NULL, EXPL_MODE_SHIP );
1842          debris_add( pilot->solid->mass, pilot->ship->gfx_space->sw/2.,
1843                pilot->solid->pos.x, pilot->solid->pos.y,
1844                pilot->solid->vel.x, pilot->solid->vel.y );
1845          pilot_setFlag(pilot,PILOT_EXPLODED);
1846          pilot_runHook( pilot, PILOT_HOOK_EXPLODED );
1847 
1848          /* Release cargo */
1849          for (i=0; i<pilot->ncommodities; i++)
1850             commodity_Jettison( pilot->id, pilot->commodities[i].commodity,
1851                   pilot->commodities[i].quantity );
1852       }
1853       /* reset random explosion timer */
1854       else if (pilot->timer[1] <= 0.) {
1855          pilot->timer[1] = 0.08 * (pilot->ptimer - pilot->timer[1]) /
1856                pilot->ptimer;
1857 
1858          /* random position on ship */
1859          a = RNGF()*2.*M_PI;
1860          px = VX(pilot->solid->pos) +  cos(a)*RNGF()*pilot->ship->gfx_space->sw/2.;
1861          py = VY(pilot->solid->pos) +  sin(a)*RNGF()*pilot->ship->gfx_space->sh/2.;
1862          vx = VX(pilot->solid->vel);
1863          vy = VY(pilot->solid->vel);
1864 
1865          /* set explosions */
1866          l = (pilot->id==PLAYER_ID) ? SPFX_LAYER_FRONT : SPFX_LAYER_BACK;
1867          if (RNGF() > 0.8)
1868             spfx_add( spfx_get("ExpM"), px, py, vx, vy, l );
1869          else
1870             spfx_add( spfx_get("ExpS"), px, py, vx, vy, l );
1871       }
1872 
1873       /* completely destroyed with final explosion */
1874       if (pilot->ptimer < 0.) {
1875          if (pilot->id==PLAYER_ID) /* player.p handled differently */
1876             player_destroyed();
1877          pilot_delete(pilot);
1878          return;
1879       }
1880    }
1881    else if (pilot->armour <= 0.) { /* PWNED */
1882       if (pilot_isFlag( pilot, PILOT_NODEATH ))
1883          pilot->armour = 1.;
1884       else
1885          pilot_dead( pilot, 0 ); /* start death stuff - dunno who killed. */
1886    }
1887 
1888    /* Special handling for braking. */
1889    if (pilot_isFlag(pilot, PILOT_BRAKING )) {
1890       if (pilot_brake( pilot )) {
1891          if (pilot_isFlag(pilot, PILOT_COOLDOWN_BRAKE))
1892             pilot_cooldown( pilot );
1893          else {
1894             /* Normal braking is done (we're below MIN_VEL_ERR), now sidestep
1895              * normal physics and bring the ship to a near-complete stop.
1896              */
1897             pilot->solid->speed_max = 0.;
1898             pilot->solid->update( pilot->solid, dt );
1899 
1900             if (VMOD(pilot->solid->vel) < 1e-1) {
1901                vectnull( &pilot->solid->vel ); /* Forcibly zero velocity. */
1902                pilot_rmFlag(pilot, PILOT_BRAKING);
1903             }
1904          }
1905       }
1906    }
1907 
1908    /* purpose fallthrough to get the movement like disabled */
1909    if (pilot_isDisabled(pilot) || pilot_isFlag(pilot, PILOT_COOLDOWN)) {
1910       /* Do the slow brake thing */
1911       pilot->solid->speed_max = 0.;
1912       pilot_setThrust( pilot, 0. );
1913       pilot_setTurn( pilot, 0. );
1914 
1915       /* update the solid */
1916       pilot->solid->update( pilot->solid, dt );
1917       gl_getSpriteFromDir( &pilot->tsx, &pilot->tsy,
1918             pilot->ship->gfx_space, pilot->solid->dir );
1919 
1920       /* Engine glow decay. */
1921       if (pilot->engine_glow > 0.) {
1922          pilot->engine_glow -= pilot->speed / pilot->thrust * dt * pilot->solid->mass;
1923          if (pilot->engine_glow < 0.)
1924             pilot->engine_glow = 0.;
1925       }
1926 
1927       return;
1928    }
1929 
1930    /* Pilot is still alive */
1931    pilot->armour += pilot->armour_regen * dt;
1932    if (pilot->armour > pilot->armour_max)
1933       pilot->armour = pilot->armour_max;
1934 
1935    /* Regen shield */
1936    if (pilot->stimer <= 0.) {
1937       pilot->shield += pilot->shield_regen * dt;
1938       if (pilot->sbonus > 0.)
1939          pilot->shield += dt * (pilot->shield_regen * (pilot->sbonus / 1.5));
1940       if (pilot->shield > pilot->shield_max)
1941          pilot->shield = pilot->shield_max;
1942    }
1943 
1944    /*
1945     * Using RC circuit energy loading.
1946     *
1947     * Calculations (using y = [0:1])
1948     *
1949     *                                          \
1950     *    y = 1 - exp( -x / tau )               |
1951     *    y + dy = 1 - exp( -( x + dx ) / tau ) |  ==>
1952     *                                          /
1953     *
1954     *    ==> dy = exp( -x / tau ) * ( 1 - exp( -dx / tau ) ==>
1955     *    ==> [ dy = (1 - y) * ( 1 - exp( -dx / tau ) ) ]
1956     */
1957    pilot->energy += (pilot->energy_max - pilot->energy) *
1958          (1. - exp( -dt / pilot->energy_tau));
1959    pilot->energy -= pilot->energy_loss * dt;
1960    if (pilot->energy > pilot->energy_max)
1961       pilot->energy = pilot->energy_max;
1962    else if (pilot->energy < 0.) {
1963       pilot->energy = 0.;
1964       /* Stop all on outfits. */
1965       nchg += pilot_outfitOffAll( pilot );
1966    }
1967 
1968    /* Must recalculate stats because something changed state. */
1969    if (nchg > 0)
1970       pilot_calcStats( pilot );
1971 
1972    /* Player damage decay. */
1973    if (pilot->player_damage > 0.)
1974       pilot->player_damage -= dt * PILOT_HOSTILE_DECAY;
1975    else
1976       pilot->player_damage = 0.;
1977 
1978    /* Pilot is board/refueling.  Hack to match speeds. */
1979    if (pilot_isFlag(pilot, PILOT_REFUELBOARDING))
1980       pilot_refuel(pilot, dt);
1981 
1982    /* Pilot is boarding its target.  Hack to match speeds. */
1983    if (pilot_isFlag(pilot, PILOT_BOARDING)) {
1984       if (target==NULL)
1985          pilot_rmFlag(pilot, PILOT_BOARDING);
1986       else {
1987          /* Match speeds. */
1988          pilot->solid->vel = target->solid->vel;
1989 
1990          /* See if boarding is finished. */
1991          if (pilot->ptimer < 0.)
1992             pilot_boardComplete(pilot);
1993       }
1994    }
1995 
1996    /* Update weapons. */
1997    pilot_weapSetUpdate( pilot );
1998 
1999    if (!pilot_isFlag(pilot, PILOT_HYPERSPACE)) { /* limit the speed */
2000 
2001       /* pilot is afterburning */
2002       if (pilot_isFlag(pilot, PILOT_AFTERBURNER)) {
2003          /* Heat up the afterburner. */
2004          pilot_heatAddSlotTime(pilot, pilot->afterburner, dt);
2005 
2006          /* If the afterburner's efficiency is reduced to 0, shut it off. */
2007          if (pilot_heatEfficiencyMod(pilot->afterburner->heat_T,
2008                pilot->afterburner->outfit->u.afb.heat_base,
2009                pilot->afterburner->outfit->u.afb.heat_cap)==0)
2010             pilot_afterburnOver(pilot);
2011          else {
2012             if (pilot->id == PLAYER_ID) spfx_shake( 0.75*SHAKE_DECAY * dt); /* shake goes down at quarter speed */
2013             efficiency = pilot_heatEfficiencyMod( pilot->afterburner->heat_T,
2014                   pilot->afterburner->outfit->u.afb.heat_base,
2015                   pilot->afterburner->outfit->u.afb.heat_cap );
2016             thrust = MIN( 1., pilot->afterburner->outfit->u.afb.mass_limit / pilot->solid->mass) * efficiency;
2017 
2018             /* Adjust speed. Speed bonus falls as heat rises. */
2019             pilot->solid->speed_max = pilot->speed * (1. +
2020                   pilot->afterburner->outfit->u.afb.speed * thrust);
2021 
2022             /* Adjust thrust. Thrust bonus falls as heat rises. */
2023             pilot_setThrust(pilot, 1. + pilot->afterburner->outfit->u.afb.thrust * thrust);
2024          }
2025       }
2026       else
2027          pilot->solid->speed_max = pilot->speed;
2028    }
2029    else
2030       pilot->solid->speed_max = -1.; /* Disables max speed. */
2031 
2032    /* Set engine glow. */
2033    if (pilot->solid->thrust > 0.) {
2034       /*pilot->engine_glow += pilot->thrust / pilot->speed * dt;*/
2035       pilot->engine_glow += pilot->speed / pilot->thrust * dt * pilot->solid->mass;
2036       if (pilot->engine_glow > 1.)
2037          pilot->engine_glow = 1.;
2038    }
2039    else if (pilot->engine_glow > 0.) {
2040       pilot->engine_glow -= pilot->speed / pilot->thrust * dt * pilot->solid->mass;
2041       if (pilot->engine_glow < 0.)
2042          pilot->engine_glow = 0.;
2043    }
2044 
2045    /* Update the solid, must be run after limit_speed. */
2046    pilot->solid->update( pilot->solid, dt );
2047    gl_getSpriteFromDir( &pilot->tsx, &pilot->tsy,
2048          pilot->ship->gfx_space, pilot->solid->dir );
2049 }
2050 
2051 /**
2052  * @brief Deletes a pilot.
2053  *
2054  *    @param p Pilot to delete3.
2055  */
pilot_delete(Pilot * p)2056 void pilot_delete( Pilot* p )
2057 {
2058    Pilot *leader;
2059 
2060    /* Remove from parent's escort list */
2061    if (p->parent != 0) {
2062       leader = pilot_get(p->parent);
2063       if (leader != NULL)
2064          escort_rmList(leader, p->id);
2065    }
2066 
2067    /* Set flag to mark for deletion. */
2068    pilot_setFlag(p, PILOT_DELETE);
2069 }
2070 
2071 
2072 /**
2073  * @brief Handles pilot's hyperspace states.
2074  *
2075  *    @param p Pilot to handle hyperspace navigation.
2076  *    @param dt Current deltatick.
2077  */
pilot_hyperspace(Pilot * p,double dt)2078 static void pilot_hyperspace( Pilot* p, double dt )
2079 {
2080    StarSystem *sys;
2081    double a, diff;
2082    int can_hyp;
2083    HookParam hparam;
2084 
2085    /* pilot is actually in hyperspace */
2086    if (pilot_isFlag(p, PILOT_HYPERSPACE)) {
2087 
2088       /* Time to play sound. */
2089       if ((p->id == PLAYER_ID) &&
2090             (p->ptimer < sound_length(snd_hypPowUpJump)) &&
2091             (p->timer[0] == -1.)) {
2092          p->timer[0] = -2.;
2093          player_soundPlay( snd_hypPowUpJump, 1 );
2094       }
2095 
2096       /* has jump happened? */
2097       if (p->ptimer < 0.) {
2098          pilot_setFlag( p, PILOT_HYP_END );
2099          pilot_setThrust( p, 0. );
2100          if (p->id == PLAYER_ID) /* player.p just broke hyperspace */
2101             player_setFlag( PLAYER_HOOK_HYPER );
2102          else {
2103             hparam.type        = HOOK_PARAM_JUMP;
2104             hparam.u.lj.srcid  = cur_system->id;
2105             hparam.u.lj.destid = cur_system->jumps[ p->nav_hyperspace ].targetid;
2106 
2107             /* Should be run before messing with delete flag. */
2108             pilot_runHookParam( p, PILOT_HOOK_JUMP, &hparam, 1 );
2109 
2110             pilot_delete(p);
2111          }
2112          return;
2113       }
2114 
2115       /* keep acceling - hyperspace uses much bigger accel */
2116       pilot_setThrust( p, HYPERSPACE_THRUST*p->solid->mass/p->thrust );
2117    }
2118    /* engines getting ready for the jump */
2119    else if (pilot_isFlag(p, PILOT_HYP_BEGIN)) {
2120 
2121       /* Make sure still within range. */
2122       can_hyp = space_canHyperspace( p );
2123       if (!can_hyp) {
2124          pilot_hyperspaceAbort( p );
2125 
2126          if (pilot_isPlayer(p))
2127             if (!player_isFlag(PLAYER_AUTONAV))
2128                player_message( "\erStrayed too far from jump point: jump aborted." );
2129       }
2130       else if (pilot_isFlag(p,PILOT_AFTERBURNER)) {
2131          pilot_hyperspaceAbort( p );
2132 
2133          if (pilot_isPlayer(p))
2134             if (!player_isFlag(PLAYER_AUTONAV))
2135                player_message( "\erAfterburner active: jump aborted." );
2136       }
2137       else {
2138          if (p->ptimer < 0.) { /* engines ready */
2139             p->ptimer = HYPERSPACE_FLY_DELAY * p->stats.jump_delay;
2140             pilot_setFlag(p, PILOT_HYPERSPACE);
2141             if (p->id == PLAYER_ID)
2142                p->timer[0] = -1.;
2143          }
2144       }
2145    }
2146    /* pilot is getting ready for hyperspace */
2147    else {
2148       /* Make sure still within range. */
2149       can_hyp = space_canHyperspace( p );
2150       if (!can_hyp) {
2151          pilot_hyperspaceAbort( p );
2152 
2153          if (pilot_isPlayer(p))
2154             if (!player_isFlag(PLAYER_AUTONAV))
2155                player_message( "\erStrayed too far from jump point: jump aborted." );
2156       }
2157       else {
2158          /* If the ship needs to charge up its hyperdrive, brake. */
2159          if (!p->stats.misc_instant_jump &&
2160                !pilot_isFlag(p, PILOT_HYP_BRAKE) && !pilot_isStopped(p))
2161             pilot_brake(p);
2162          /* face target */
2163          else {
2164             /* Done braking or no braking required. */
2165             pilot_setFlag( p, PILOT_HYP_BRAKE);
2166             pilot_setThrust( p, 0. );
2167 
2168             /* Face system headed to. */
2169             sys  = cur_system->jumps[p->nav_hyperspace].target;
2170             a    = ANGLE( sys->pos.x - cur_system->pos.x, sys->pos.y - cur_system->pos.y );
2171             diff = pilot_face( p, a );
2172 
2173             if (ABS(diff) < MAX_DIR_ERR) { /* we can now prepare the jump */
2174                if (jp_isFlag( &cur_system->jumps[p->nav_hyperspace], JP_EXITONLY )) {
2175                   WARN( "Pilot '%s' trying to jump through exit-only jump from '%s' to '%s'",
2176                         p->name, cur_system->name, sys->name );
2177                }
2178                else {
2179                   pilot_setTurn( p, 0. );
2180                   p->ptimer = HYPERSPACE_ENGINE_DELAY * !p->stats.misc_instant_jump;
2181                   pilot_setFlag(p, PILOT_HYP_BEGIN);
2182                   /* Player plays sound. */
2183                   if ((p->id == PLAYER_ID) && !p->stats.misc_instant_jump)
2184                      player_soundPlay( snd_hypPowUp, 1 );
2185                }
2186             }
2187          }
2188       }
2189    }
2190 
2191    if (pilot_isPlayer(p))
2192       player_updateSpecific( p, dt );
2193 }
2194 
2195 
2196 /**
2197  * @brief Stops the pilot from hyperspacing.
2198  *
2199  * Can only stop in preparation mode.
2200  *
2201  *    @param p Pilot to handle stop hyperspace.
2202  */
pilot_hyperspaceAbort(Pilot * p)2203 void pilot_hyperspaceAbort( Pilot* p )
2204 {
2205    if (pilot_isFlag(p, PILOT_HYPERSPACE))
2206       return;
2207 
2208    if (pilot_isFlag(p, PILOT_HYP_BEGIN)) {
2209       /* Player plays sound. */
2210       if (p->id == PLAYER_ID) {
2211          player_soundStop();
2212          player_soundPlay( snd_hypPowDown, 1 );
2213       }
2214    }
2215    pilot_rmFlag(p, PILOT_HYP_BEGIN);
2216    pilot_rmFlag(p, PILOT_HYP_BRAKE);
2217    pilot_rmFlag(p, PILOT_HYP_PREP);
2218 }
2219 
2220 
2221 /**
2222  * @brief Attempts to start refueling the pilot's target.
2223  *
2224  *    @param p Pilot to try to start refueling.
2225  */
pilot_refuelStart(Pilot * p)2226 int pilot_refuelStart( Pilot *p )
2227 {
2228    Pilot *target;
2229 
2230    /* Check to see if target exists, remove flag if not. */
2231    target = pilot_get(p->target);
2232    if (target == NULL) {
2233       pilot_rmFlag(p, PILOT_REFUELING);
2234       return 0;
2235    }
2236 
2237    /* Conditions are the same as boarding, except disabled. */
2238    if (vect_dist(&p->solid->pos, &target->solid->pos) >
2239          target->ship->gfx_space->sw * PILOT_SIZE_APROX )
2240       return 0;
2241    else if ((pow2(VX(p->solid->vel)-VX(target->solid->vel)) +
2242             pow2(VY(p->solid->vel)-VY(target->solid->vel))) >
2243          (double)pow2(MAX_HYPERSPACE_VEL))
2244       return 0;
2245 
2246    /* Now start the boarding to refuel. */
2247    pilot_setFlag(p, PILOT_REFUELBOARDING);
2248    p->ptimer  = PILOT_REFUEL_TIME; /* Use timer to handle refueling. */
2249    p->pdata   = PILOT_REFUEL_QUANTITY;
2250    return 1;
2251 }
2252 
2253 
2254 /**
2255  * @brief Has the pilot refuel its target.
2256  *
2257  *    @param p Pilot that is actively refueling.
2258  *    @param dt Current delta tick.
2259  */
pilot_refuel(Pilot * p,double dt)2260 static void pilot_refuel( Pilot *p, double dt )
2261 {
2262    Pilot *target;
2263    double amount;
2264    int jumps;
2265 
2266    /* Check to see if target exists, remove flag if not. */
2267    target = pilot_get(p->target);
2268    if (target == NULL) {
2269       pilot_rmFlag(p, PILOT_REFUELBOARDING);
2270       pilot_rmFlag(p, PILOT_REFUELING);
2271       return;
2272    }
2273 
2274    /* Match speeds. */
2275    p->solid->vel = target->solid->vel;
2276 
2277    amount = CLAMP( 0., p->pdata, PILOT_REFUEL_RATE * dt);
2278    p->pdata -= amount;
2279 
2280    /* Move fuel. */
2281    p->fuel        -= amount;
2282    target->fuel   += amount;
2283    /* Stop refueling at max. */
2284    if (target->fuel > target->fuel_max) {
2285       p->ptimer      = -1.;
2286       target->fuel   = target->fuel_max;
2287    }
2288 
2289    /* Check to see if done. */
2290    if (p->ptimer < 0.) {
2291       /* Counteract accumulated floating point error by rounding up
2292        * if pilots have > 99.99% of a jump worth of fuel.
2293        */
2294 
2295       jumps = pilot_getJumps(p);
2296       if ((p->fuel / p->fuel_consumption - jumps) > 0.9999)
2297          p->fuel = p->fuel_consumption * (jumps + 1);
2298 
2299       jumps = pilot_getJumps(target);
2300       if ((target->fuel / target->fuel_consumption - jumps) > 0.9999)
2301          target->fuel = target->fuel_consumption * (jumps + 1);
2302 
2303       pilot_rmFlag(p, PILOT_REFUELBOARDING);
2304       pilot_rmFlag(p, PILOT_REFUELING);
2305    }
2306 }
2307 
2308 
2309 /**
2310  * @brief Calculates the hyperspace delay for a pilot.
2311  *
2312  *    @param p Pilot to calculate hyperspace delay for.
2313  *    @return The hyperspace delay.
2314  */
pilot_hyperspaceDelay(Pilot * p)2315 ntime_t pilot_hyperspaceDelay( Pilot *p )
2316 {
2317    int stu;
2318    stu = (int)(NT_STP_STU * p->stats.jump_delay);
2319    return ntime_create( 0, 0, stu );
2320 }
2321 
2322 
2323 /**
2324  * @brief Checks to see if the pilot has at least a certain amount of credits.
2325  *
2326  *    @param p Pilot to check to see if they have enough credits.
2327  *    @param amount Amount to check for.
2328  *    @return 1 if they have enough, 0 otherwise.
2329  */
pilot_hasCredits(Pilot * p,credits_t amount)2330 int pilot_hasCredits( Pilot *p, credits_t amount )
2331 {
2332    if (amount < 0)
2333       return 1;
2334    return (amount <= p->credits);
2335 }
2336 
2337 
2338 /**
2339  * @brief Modifies the amount of credits the pilot has.
2340  *
2341  *    @param p Pilot to modify amount of credits of.
2342  *    @param amount Quantity of credits to give/take.
2343  *    @return Amount of credits the pilot has.
2344  */
pilot_modCredits(Pilot * p,credits_t amount)2345 credits_t pilot_modCredits( Pilot *p, credits_t amount )
2346 {
2347    uint64_t ul;
2348 
2349    ul = (uint64_t) ABS(amount);
2350 
2351    if (amount > 0) {
2352       if (CREDITS_MAX-p->credits < (credits_t)ul)
2353          p->credits = CREDITS_MIN;
2354       else
2355          p->credits += ul;
2356    }
2357    else if (amount < 0) {
2358       if ((credits_t)ul > p->credits)
2359          p->credits = 0;
2360       else
2361          p->credits -= ul;
2362    }
2363    return p->credits;
2364 }
2365 
2366 
2367 /**
2368  * @brief Initialize pilot.
2369  *
2370  *    @param pilot Pilot to initialize.
2371  *    @param ship Ship pilot will be flying.
2372  *    @param name Pilot's name, if NULL ship's name will be used.
2373  *    @param faction Faction of the pilot.
2374  *    @param ai Name of the AI profile to use for the pilot.
2375  *    @param dir Initial direction to face (radians).
2376  *    @param pos Initial position.
2377  *    @param vel Initial velocity.
2378  *    @param flags Used for tweaking the pilot.
2379  */
pilot_init(Pilot * pilot,Ship * ship,const char * name,int faction,const char * ai,const double dir,const Vector2d * pos,const Vector2d * vel,const PilotFlags flags)2380 void pilot_init( Pilot* pilot, Ship* ship, const char* name, int faction, const char *ai,
2381       const double dir, const Vector2d* pos, const Vector2d* vel,
2382       const PilotFlags flags )
2383 {
2384    int i, p;
2385 
2386    /* Clear memory. */
2387    memset(pilot, 0, sizeof(Pilot));
2388 
2389    if (pilot_isFlagRaw(flags, PILOT_PLAYER)) /* Set player ID, should probably be fixed to something sane someday. */
2390       pilot->id = PLAYER_ID;
2391    else
2392       pilot->id = ++pilot_id; /* new unique pilot id based on pilot_id, can't be 0 */
2393 
2394    /* Defaults. */
2395    pilot->autoweap = 1;
2396 
2397    /* Basic information. */
2398    pilot->ship = ship;
2399    pilot->name = strdup( (name==NULL) ? ship->name : name );
2400 
2401    /* faction */
2402    pilot->faction = faction;
2403 
2404    /* solid */
2405    pilot->solid = solid_create(ship->mass, dir, pos, vel, SOLID_UPDATE_RK4);
2406 
2407    /* First pass to make sure requirements make sense. */
2408    pilot->armour = pilot->armour_max = 1.; /* hack to have full armour */
2409    pilot->shield = pilot->shield_max = 1.; /* ditto shield */
2410    pilot->energy = pilot->energy_max = 1.; /* ditto energy */
2411    pilot->fuel   = pilot->fuel_max   = 1.; /* ditto fuel */
2412    pilot_calcStats(pilot);
2413    pilot->stress = 0.; /* No stress. */
2414 
2415    /* Allocate outfit memory. */
2416    /* Slot types. */
2417    pilot->outfit_nstructure = ship->outfit_nstructure;
2418    pilot->outfit_structure = calloc( ship->outfit_nstructure, sizeof(PilotOutfitSlot) );
2419    pilot->outfit_nutility  = ship->outfit_nutility;
2420    pilot->outfit_utility   = calloc( ship->outfit_nutility, sizeof(PilotOutfitSlot) );
2421    pilot->outfit_nweapon   = ship->outfit_nweapon;
2422    pilot->outfit_weapon    = calloc( ship->outfit_nweapon, sizeof(PilotOutfitSlot) );
2423    /* Global. */
2424    pilot->noutfits = pilot->outfit_nstructure + pilot->outfit_nutility + pilot->outfit_nweapon;
2425    pilot->outfits  = calloc( pilot->noutfits, sizeof(PilotOutfitSlot*) );
2426    /* First pass copy data. */
2427    p = 0;
2428    for (i=0; i<pilot->outfit_nstructure; i++) {
2429       pilot->outfits[p] = &pilot->outfit_structure[i];
2430       pilot->outfits[p]->sslot = &ship->outfit_structure[i];
2431       if (ship->outfit_structure[i].data != NULL)
2432          pilot_addOutfitRaw( pilot, ship->outfit_structure[i].data, pilot->outfits[p] );
2433       p++;
2434    }
2435    for (i=0; i<pilot->outfit_nutility; i++) {
2436       pilot->outfits[p] = &pilot->outfit_utility[i];
2437       pilot->outfits[p]->sslot = &ship->outfit_utility[i];
2438       if (ship->outfit_utility[i].data != NULL)
2439          pilot_addOutfitRaw( pilot, ship->outfit_utility[i].data, pilot->outfits[p] );
2440       p++;
2441    }
2442    for (i=0; i<pilot->outfit_nweapon; i++) {
2443       pilot->outfits[p] = &pilot->outfit_weapon[i];
2444       pilot->outfits[p]->sslot = &ship->outfit_weapon[i];
2445       if (ship->outfit_weapon[i].data != NULL)
2446          pilot_addOutfitRaw( pilot, ship->outfit_weapon[i].data, pilot->outfits[p] );
2447       p++;
2448    }
2449    /* Second pass set ID. */
2450    for (i=0; i<pilot->noutfits; i++)
2451       pilot->outfits[i]->id = i;
2452 
2453    /* cargo - must be set before calcStats */
2454    pilot->cargo_free = pilot->ship->cap_cargo; /* should get redone with calcCargo */
2455 
2456    /* Initialize heat. */
2457    pilot_heatReset( pilot );
2458 
2459    /* set the pilot stats based on their ship and outfits */
2460    pilot_calcStats( pilot );
2461 
2462    /* Heal up the ship. */
2463    pilot->armour = pilot->armour_max;
2464    pilot->shield = pilot->shield_max;
2465    pilot->energy = pilot->energy_max;
2466    pilot->fuel   = pilot->fuel_max;
2467 
2468    /* Sanity check. */
2469 #ifdef DEBUGGING
2470    const char *str = pilot_checkSpaceworthy( pilot );
2471    if (str != NULL)
2472       DEBUG( "Pilot '%s' failed sanity check: %s", pilot->name, str );
2473 #endif /* DEBUGGING */
2474 
2475    /* set flags and functions */
2476    if (pilot_isFlagRaw(flags, PILOT_PLAYER)) {
2477       pilot->think            = player_think; /* players don't need to think! :P */
2478       pilot->update           = player_update; /* Players get special update. */
2479       pilot->render           = NULL; /* render will get called from player_think */
2480       pilot->render_overlay   = NULL;
2481       if (!pilot_isFlagRaw( flags, PILOT_EMPTY )) /* Sort of a hack. */
2482          player.p = pilot;
2483    }
2484    else {
2485       pilot->think            = ai_think;
2486       pilot->update           = pilot_update;
2487       pilot->render           = pilot_render;
2488       pilot->render_overlay   = pilot_renderOverlay;
2489    }
2490 
2491    /* Copy pilot flags. */
2492    pilot_copyFlagsRaw(pilot->flags, flags);
2493 
2494    /* Clear timers. */
2495    pilot_clearTimers(pilot);
2496 
2497    /* Update the x and y sprite positions. */
2498    gl_getSpriteFromDir( &pilot->tsx, &pilot->tsy,
2499          pilot->ship->gfx_space, pilot->solid->dir );
2500 
2501    /* Targets. */
2502    pilot_setTarget( pilot, pilot->id ); /* No target. */
2503    pilot->nav_planet       = -1;
2504    pilot->nav_hyperspace   = -1;
2505 
2506    /* Check takeoff. */
2507    if (pilot_isFlagRaw( flags, PILOT_TAKEOFF )) {
2508       pilot->ptimer = PILOT_TAKEOFF_DELAY;
2509    }
2510 
2511    /* Create empty table for messages. */
2512    lua_newtable(naevL);
2513    pilot->messages = luaL_ref(naevL, LUA_REGISTRYINDEX);
2514 
2515    /* AI */
2516    if (ai != NULL)
2517       ai_pinit( pilot, ai ); /* Must run before ai_create */
2518 }
2519 
2520 
2521 /**
2522  * @brief Creates a new pilot
2523  *
2524  * See pilot_init for parameters.
2525  *
2526  *    @return Pilot's id.
2527  *
2528  * @sa pilot_init
2529  */
pilot_create(Ship * ship,const char * name,int faction,const char * ai,const double dir,const Vector2d * pos,const Vector2d * vel,const PilotFlags flags)2530 unsigned int pilot_create( Ship* ship, const char* name, int faction, const char *ai,
2531       const double dir, const Vector2d* pos, const Vector2d* vel,
2532       const PilotFlags flags )
2533 {
2534    Pilot *dyn;
2535 
2536    /* Allocate pilot memory. */
2537    dyn = malloc(sizeof(Pilot));
2538    if (dyn == NULL) {
2539       WARN("Unable to allocate memory");
2540       return 0;
2541    }
2542 
2543    /* See if memory needs to grow */
2544    if (pilot_nstack+1 > pilot_mstack) { /* needs to grow */
2545       if (pilot_mstack == 0)
2546          pilot_mstack = PILOT_CHUNK_MIN;
2547       else
2548          pilot_mstack += MIN( pilot_mstack, PILOT_CHUNK_MAX );
2549       pilot_stack = realloc( pilot_stack, pilot_mstack*sizeof(Pilot*) );
2550    }
2551 
2552    /* Set the pilot in the stack -- must be there before initializing */
2553    pilot_stack[pilot_nstack] = dyn;
2554    pilot_nstack++; /* there's a new pilot */
2555 
2556    /* Initialize the pilot. */
2557    pilot_init( dyn, ship, name, faction, ai, dir, pos, vel, flags );
2558 
2559    return dyn->id;
2560 }
2561 
2562 
2563 /**
2564  * @brief Creates a pilot without adding it to the stack.
2565  *
2566  *    @param ship Ship for the pilot to use.
2567  *    @param name Name of the pilot ship (NULL uses ship name).
2568  *    @param faction Faction of the ship.
2569  *    @param ai AI to use.
2570  *    @param flags Flags for tweaking, PILOT_EMPTY is added.
2571  *    @return Pointer to the new pilot (not added to stack).
2572  */
pilot_createEmpty(Ship * ship,const char * name,int faction,const char * ai,PilotFlags flags)2573 Pilot* pilot_createEmpty( Ship* ship, const char* name,
2574       int faction, const char *ai, PilotFlags flags )
2575 {
2576    Pilot* dyn;
2577    dyn = malloc(sizeof(Pilot));
2578    if (dyn == NULL) {
2579       WARN("Unable to allocate memory");
2580       return 0;
2581    }
2582    pilot_setFlagRaw( flags, PILOT_EMPTY );
2583    pilot_init( dyn, ship, name, faction, ai, 0., NULL, NULL, flags );
2584    return dyn;
2585 }
2586 
2587 
2588 /**
2589  * @brief Copies src pilot to dest.
2590  *
2591  *    @param src Pilot to copy.
2592  *    @return Copy of src.
2593  */
pilot_copy(Pilot * src)2594 Pilot* pilot_copy( Pilot* src )
2595 {
2596    int i, p;
2597    Pilot *dest = malloc(sizeof(Pilot));
2598 
2599    /* Copy data over, we'll have to reset all the pointers though. */
2600    *dest = *src;
2601 
2602    /* Copy names. */
2603    if (src->name)
2604       dest->name = strdup(src->name);
2605    if (src->title)
2606       dest->title = strdup(src->title);
2607 
2608    /* Copy solid. */
2609    dest->solid = malloc(sizeof(Solid));
2610    *dest->solid = *src->solid;
2611 
2612    /* Copy outfits. */
2613    dest->noutfits = src->noutfits;
2614    dest->outfits  = malloc( sizeof(PilotOutfitSlot*) * dest->noutfits );
2615    dest->outfit_nstructure = src->outfit_nstructure;
2616    dest->outfit_structure  = malloc( sizeof(PilotOutfitSlot) * dest->outfit_nstructure );
2617    memcpy( dest->outfit_structure, src->outfit_structure,
2618          sizeof(PilotOutfitSlot) * dest->outfit_nstructure );
2619    dest->outfit_nutility = src->outfit_nutility;
2620    dest->outfit_utility  = malloc( sizeof(PilotOutfitSlot) * dest->outfit_nutility );
2621    memcpy( dest->outfit_utility, src->outfit_utility,
2622          sizeof(PilotOutfitSlot) * dest->outfit_nutility );
2623    dest->outfit_nweapon = src->outfit_nweapon;
2624    dest->outfit_weapon  = malloc( sizeof(PilotOutfitSlot) * dest->outfit_nweapon );
2625    memcpy( dest->outfit_weapon, src->outfit_weapon,
2626          sizeof(PilotOutfitSlot) * dest->outfit_nweapon );
2627    p = 0;
2628    for (i=0; i<dest->outfit_nstructure; i++)
2629       dest->outfits[p++] = &dest->outfit_structure[i];
2630    for (i=0; i<dest->outfit_nutility; i++)
2631       dest->outfits[p++] = &dest->outfit_utility[i];
2632    for (i=0; i<dest->outfit_nweapon; i++)
2633       dest->outfits[p++] = &dest->outfit_weapon[i];
2634    dest->afterburner = NULL;
2635 
2636    /* Hooks get cleared. */
2637    dest->hooks          = NULL;
2638    dest->nhooks         = 0;
2639 
2640    /* Copy has no escorts. */
2641    dest->escorts        = NULL;
2642    dest->nescorts       = 0;
2643 
2644    /* AI is not copied. */
2645    dest->task           = NULL;
2646 
2647    /* Set pointers and friends to NULL. */
2648    /* Commodities. */
2649    dest->commodities    = NULL;
2650    dest->ncommodities   = 0;
2651    /* Calculate stats. */
2652    pilot_calcStats(dest);
2653 
2654    /* Copy commodities. */
2655    for (i=0; i<src->ncommodities; i++)
2656       pilot_cargoAdd( dest, src->commodities[i].commodity,
2657             src->commodities[i].quantity, src->commodities[i].id );
2658 
2659    return dest;
2660 }
2661 
2662 
2663 /**
2664  * @brief Frees and cleans up a pilot
2665  *
2666  *    @param p Pilot to free.
2667  */
pilot_free(Pilot * p)2668 void pilot_free( Pilot* p )
2669 {
2670    int i;
2671 
2672    /* Clear up pilot hooks. */
2673    pilot_clearHooks(p);
2674 
2675    /* If hostile, must remove counter. */
2676    pilot_rmHostile(p);
2677 
2678    /* Free weapon sets. */
2679    pilot_weapSetFree(p);
2680 
2681    /* Free outfits. */
2682    if (p->outfits != NULL)
2683       free(p->outfits);
2684    if (p->outfit_structure != NULL)
2685       free(p->outfit_structure);
2686    if (p->outfit_utility != NULL)
2687       free(p->outfit_utility);
2688    if (p->outfit_weapon != NULL)
2689       free(p->outfit_weapon);
2690 
2691    /* Remove commodities. */
2692    while (p->commodities != NULL)
2693       pilot_cargoRmRaw( p, p->commodities[0].commodity,
2694             p->commodities[0].quantity, 1 );
2695 
2696    /* Clean up data. */
2697    if (p->ai != NULL)
2698       ai_destroy(p); /* Must be destroyed first if applicable. */
2699 
2700    /* Free name and title. */
2701    if (p->name != NULL)
2702       free(p->name);
2703    if (p->title != NULL)
2704       free(p->title);
2705    /* Case if pilot is the player. */
2706    if (player.p==p)
2707       player.p = NULL;
2708    solid_free(p->solid);
2709    if (p->mounted != NULL)
2710       free(p->mounted);
2711 
2712    /* Free escorts. */
2713    for (i=0; i<p->nescorts; i++)
2714       free(p->escorts[i].ship);
2715    if (p->escorts)
2716       free(p->escorts);
2717 
2718    /* Free comm message. */
2719    if (p->comm_msg != NULL)
2720       free(p->comm_msg);
2721 
2722    /* Free messages. */
2723    luaL_unref(naevL, p->messages, LUA_REGISTRYINDEX);
2724 
2725 #ifdef DEBUGGING
2726    memset( p, 0, sizeof(Pilot) );
2727 #endif /* DEBUGGING */
2728 
2729    free(p);
2730 }
2731 
2732 
2733 /**
2734  * @brief Destroys pilot from stack
2735  *
2736  *    @param p Pilot to destroy.
2737  */
pilot_destroy(Pilot * p)2738 void pilot_destroy(Pilot* p)
2739 {
2740    int i;
2741 
2742    /* find the pilot */
2743    for (i=0; i < pilot_nstack; i++)
2744       if (pilot_stack[i]==p)
2745          break;
2746 
2747    /* Remove faction if necessary. */
2748    if (p->presence > 0) {
2749       system_rmCurrentPresence( cur_system, p->faction, p->presence );
2750       p->presence = 0;
2751    }
2752 
2753    /* pilot is eliminated */
2754    pilot_free(p);
2755    pilot_nstack--;
2756 
2757    /* copy other pilots down */
2758    memmove(&pilot_stack[i], &pilot_stack[i+1], (pilot_nstack-i)*sizeof(Pilot*));
2759 }
2760 
2761 
2762 /**
2763  * @brief Frees the pilot stack.
2764  */
pilots_free(void)2765 void pilots_free (void)
2766 {
2767    int i;
2768 
2769    pilot_freeGlobalHooks();
2770 
2771    /* Free pilots. */
2772    for (i=0; i < pilot_nstack; i++)
2773       pilot_free(pilot_stack[i]);
2774    free(pilot_stack);
2775    pilot_stack = NULL;
2776    player.p = NULL;
2777    pilot_nstack = 0;
2778 }
2779 
2780 
2781 /**
2782  * @brief Cleans up the pilot stack - leaves the player.
2783  */
pilots_clean(void)2784 void pilots_clean (void)
2785 {
2786    int i;
2787    for (i=0; i < pilot_nstack; i++) {
2788       /* we'll set player.p at privileged position */
2789       if ((player.p != NULL) && (pilot_stack[i] == player.p)) {
2790          pilot_stack[0] = player.p;
2791          pilot_stack[0]->lockons = 0; /* Clear lockons. */
2792       }
2793       else /* rest get killed */
2794          pilot_free(pilot_stack[i]);
2795    }
2796 
2797    if (player.p != NULL) { /* set stack to 1 if pilot exists */
2798       pilot_nstack = 1;
2799       pilot_clearTimers( player.p ); /* Reset the player's timers. */
2800    }
2801    else
2802       pilot_nstack = 0;
2803 
2804    /* Clear global hooks. */
2805    pilots_clearGlobalHooks();
2806 }
2807 
2808 
2809 /**
2810  * @brief Clears all the pilots except the player.
2811  */
pilots_clear(void)2812 void pilots_clear (void)
2813 {
2814    int i;
2815    for (i=0; i < pilot_nstack; i++)
2816       if (!pilot_isPlayer( pilot_stack[i] ))
2817          pilot_delete( pilot_stack[i] );
2818 }
2819 
2820 
2821 /**
2822  * @brief Even cleans up the player.
2823  */
pilots_cleanAll(void)2824 void pilots_cleanAll (void)
2825 {
2826    pilots_clean();
2827    if (player.p != NULL) {
2828       pilot_free(player.p);
2829       player.p = NULL;
2830    }
2831    pilot_nstack = 0;
2832 }
2833 
2834 
2835 /**
2836  * @brief Updates all the pilots.
2837  *
2838  *    @param dt Delta tick for the update.
2839  */
pilots_update(double dt)2840 void pilots_update( double dt )
2841 {
2842    int i;
2843    Pilot *p;
2844 
2845    /* Now update all the pilots. */
2846    for (i=0; i<pilot_nstack; i++) {
2847       p = pilot_stack[i];
2848 
2849       /* Destroy pilot and go on. */
2850       if (pilot_isFlag(p, PILOT_DELETE)) {
2851          pilot_destroy(p);
2852          i--; /* Must decrement iterator. */
2853          continue;
2854       }
2855 
2856       /* Invisible, not doing anything. */
2857       if (pilot_isFlag(p, PILOT_INVISIBLE))
2858          continue;
2859 
2860       /* See if should think. */
2861       if ((p->think==NULL) || (p->ai==NULL))
2862          continue;
2863       if (pilot_isDisabled(p))
2864          continue;
2865       if (pilot_isFlag(p,PILOT_DEAD))
2866          continue;
2867 
2868       /* Hyperspace gets special treatment */
2869       if (pilot_isFlag(p, PILOT_HYP_PREP))
2870          pilot_hyperspace(p, dt);
2871       /* Entering hyperspace. */
2872       else if (pilot_isFlag(p, PILOT_HYP_END)) {
2873          if (VMOD(p->solid->vel) < 2*solid_maxspeed( p->solid, p->speed, p->thrust) )
2874             pilot_rmFlag(p, PILOT_HYP_END);
2875       }
2876       /* Must not be boarding to think. */
2877       else if (!pilot_isFlag(p, PILOT_BOARDING) &&
2878             !pilot_isFlag(p, PILOT_REFUELBOARDING) &&
2879             /* Must not be landing nor taking off. */
2880             !pilot_isFlag(p, PILOT_LANDING) &&
2881             !pilot_isFlag(p, PILOT_TAKEOFF))
2882          p->think(p, dt);
2883    }
2884 
2885    /* Now update all the pilots. */
2886    for (i=0; i<pilot_nstack; i++) {
2887       p = pilot_stack[i];
2888 
2889       /* Ignore. */
2890       if (pilot_isFlag(p, PILOT_DELETE))
2891          continue;
2892 
2893       /* Invisible, not doing anything. */
2894       if (pilot_isFlag(p, PILOT_INVISIBLE))
2895          continue;
2896 
2897       /* Just update the pilot. */
2898       if (p->update) /* update */
2899          p->update( p, dt );
2900    }
2901 }
2902 
2903 
2904 /**
2905  * @brief Renders all the pilots.
2906  *
2907  *    @param dt Current delta tick.
2908  */
pilots_render(double dt)2909 void pilots_render( double dt )
2910 {
2911    int i;
2912    for (i=0; i<pilot_nstack; i++) {
2913 
2914       /* Invisible, not doing anything. */
2915       if (pilot_isFlag(pilot_stack[i], PILOT_INVISIBLE))
2916          continue;
2917 
2918       if (pilot_stack[i]->render != NULL) /* render */
2919          pilot_stack[i]->render(pilot_stack[i], dt);
2920    }
2921 }
2922 
2923 
2924 /**
2925  * @brief Renders all the pilots overlays.
2926  *
2927  *    @param dt Current delta tick.
2928  */
pilots_renderOverlay(double dt)2929 void pilots_renderOverlay( double dt )
2930 {
2931    int i;
2932    for (i=0; i<pilot_nstack; i++) {
2933 
2934       /* Invisible, not doing anything. */
2935       if (pilot_isFlag(pilot_stack[i], PILOT_INVISIBLE))
2936          continue;
2937 
2938       if (pilot_stack[i]->render_overlay != NULL) /* render */
2939          pilot_stack[i]->render_overlay(pilot_stack[i], dt);
2940    }
2941 }
2942 
2943 
2944 /**
2945  * @brief Clears the pilot's timers.
2946  *
2947  *    @param pilot Pilot to clear timers of.
2948  */
pilot_clearTimers(Pilot * pilot)2949 void pilot_clearTimers( Pilot *pilot )
2950 {
2951    int i, n;
2952    PilotOutfitSlot *o;
2953 
2954    pilot->ptimer     = 0.; /* Pilot timer. */
2955    pilot->tcontrol   = 0.; /* AI control timer. */
2956    pilot->stimer     = 0.; /* Shield timer. */
2957    pilot->dtimer     = 0.; /* Disable timer. */
2958    for (i=0; i<MAX_AI_TIMERS; i++)
2959       pilot->timer[i] = 0.; /* Specific AI timers. */
2960    n = 0;
2961    for (i=0; i<pilot->noutfits; i++) {
2962       o = pilot->outfits[i];
2963       o->timer    = 0.; /* Last used timer. */
2964       o->stimer   = 0.; /* State timer. */
2965       if (o->state != PILOT_OUTFIT_OFF) {
2966          o->state    = PILOT_OUTFIT_OFF; /* Set off. */
2967          n++;
2968       }
2969    }
2970 
2971    /* Must recalculate stats. */
2972    if (n > 0)
2973       pilot_calcStats( pilot );
2974 }
2975 
2976 
2977 /**
2978  * @brief Gets the relative size(shipmass) between the current pilot and the specified target
2979  *
2980  *    @param p the pilot whose mass we will compare
2981  *    @return A number from 0 to 1 mapping the relative masses
2982  */
pilot_relsize(const Pilot * cur_pilot,const Pilot * p)2983 double pilot_relsize( const Pilot* cur_pilot, const Pilot* p )
2984 {
2985    return (1 - 1/(1 + ((double)cur_pilot->solid->mass / (double)p->solid->mass)));
2986 }
2987 
2988 /**
2989  * @brief Gets the relative damage output(total DPS) between the current pilot and the specified target
2990  *
2991  *    @param cur_pilot Reference pilot to compare against.
2992  *    @param p The pilot whose dps we will compare
2993  *    @return The relative dps of p with respect to cur_pilot (0.5 is equal, 1 is p is infinitely stronger, 0 is t is infinitely stronger).
2994  */
pilot_reldps(const Pilot * cur_pilot,const Pilot * p)2995 double pilot_reldps( const Pilot* cur_pilot, const Pilot* p )
2996 {
2997    int i;
2998    double DPSaccum_target = 0.;
2999    double DPSaccum_pilot = 0.;
3000    double delay_cache, damage_cache;
3001    Outfit *o;
3002    const Damage *dmg;
3003 
3004    for (i=0; i<p->outfit_nweapon; i++) {
3005       o = p->outfit_weapon[i].outfit;
3006       if (o == NULL)
3007          continue;
3008       dmg = outfit_damage( o );
3009       if (dmg == NULL)
3010          continue;
3011 
3012       damage_cache   = dmg->damage;
3013       delay_cache    = outfit_delay( o );
3014       if ((damage_cache > 0) && (delay_cache > 0))
3015          DPSaccum_target += ( damage_cache/delay_cache );
3016    }
3017 
3018    for (i=0; i<cur_pilot->outfit_nweapon; i++) {
3019       o = cur_pilot->outfit_weapon[i].outfit;
3020       if (o == NULL)
3021          continue;
3022       dmg = outfit_damage( o );
3023       if (dmg == NULL)
3024          continue;
3025 
3026       damage_cache   = dmg->damage;
3027       delay_cache    = outfit_delay( o );
3028       if ((damage_cache > 0) && (delay_cache > 0))
3029          DPSaccum_pilot += ( damage_cache/delay_cache );
3030    }
3031 
3032    if ((DPSaccum_target > 1e-6) && (DPSaccum_pilot > 1e-6))
3033       return DPSaccum_pilot / (DPSaccum_target + DPSaccum_pilot);
3034    else if (DPSaccum_pilot > 0.)
3035       return 1;
3036    return 0;
3037 }
3038 
3039 /**
3040  * @brief Gets the relative hp(combined shields and armour) between the current pilot and the specified target
3041  *
3042  *    @param cur_pilot Reference pilot.
3043  *    @param p the pilot whose shields/armour we will compare
3044  *    @return A number from 0 to 1 mapping the relative HPs (0.5 is equal, 1 is reference pilot is infinity, 0 is current pilot is infinity)
3045  */
pilot_relhp(const Pilot * cur_pilot,const Pilot * p)3046 double pilot_relhp( const Pilot* cur_pilot, const Pilot* p )
3047 {
3048    double c_hp = cur_pilot -> armour_max + cur_pilot -> shield_max;
3049    double p_hp = p -> armour_max + p -> shield_max;
3050    return c_hp / (p_hp + c_hp);
3051 }
3052 
3053 
3054 /**
3055  * @brief Gets the price or worth of a pilot in credits.
3056  *
3057  *    @param p Pilot to get worth of.
3058  *    @return Worth of the pilot.
3059  */
pilot_worth(const Pilot * p)3060 credits_t pilot_worth( const Pilot *p )
3061 {
3062    credits_t price;
3063    int i;
3064 
3065    /* Ship price is base price + outfit prices. */
3066    price = ship_basePrice( p->ship );
3067    for (i=0; i<p->noutfits; i++) {
3068       if (p->outfits[i]->outfit == NULL)
3069          continue;
3070       price += p->outfits[i]->outfit->price;
3071    }
3072 
3073    return price;
3074 }
3075 
3076 
3077 /**
3078  * @brief Sends a message
3079  *
3080  * @param p Pilot to send message
3081  * @param reciever Pilot to recieve it
3082  * @param idx Index of data on lua stack or 0
3083  */
pilot_msg(Pilot * p,Pilot * reciever,const char * type,unsigned int idx)3084 void pilot_msg(Pilot *p, Pilot *reciever, const char *type, unsigned int idx)
3085 {
3086    if (idx != 0)
3087       lua_pushvalue(naevL, idx); /* data */
3088    else
3089       lua_pushnil(naevL); /* data */
3090 
3091    lua_newtable(naevL); /* data, msg */
3092 
3093    lua_pushpilot(naevL, p->id); /* data, msg, sender */
3094    lua_rawseti(naevL, -2, 1); /* data, msg */
3095 
3096    lua_pushstring(naevL, type); /* data, msg, type */
3097    lua_rawseti(naevL, -2, 2); /* data, msg */
3098 
3099    lua_pushvalue(naevL, -2); /* data, msg, data */
3100    lua_rawseti(naevL, -2, 3); /* data, msg */
3101 
3102    lua_rawgeti(naevL, LUA_REGISTRYINDEX, reciever->messages); /* data, msg, messages */
3103    lua_pushvalue(naevL, -2); /* data, msg, messages, msg */
3104    lua_rawseti(naevL, -2, lua_objlen(naevL, -2)+1); /* data, msg, messages */
3105    lua_pop(naevL, 3); /*  */
3106 }
3107