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