1 /*
2 * See Licensing and Copyright notice in naev.h
3 */
4
5
6 /**
7 * @file pilot_weapon.c
8 *
9 * @brief Handles pilot weapon sets.
10 *
11 * Cheat sheet: how this works (it's complicated).
12 *
13 * KEYPRESS
14 * 1) weapSetPress
15 * 2) weapSetFire
16 * 2.1) Modifications get turned on/off
17 * 2.2) Weapons go to shootWeaponSetOutfit
18 *
19 * UPDATE
20 * 1) weapSetUpdate
21 * 2.1) fire set => weapSetFire
22 * 2.1.1) Modifications get turned on/off
23 * 2.1.2) Weapons go to shootWeaponSetOutfit
24 * 2.2) weapsetUpdateOutfits
25 *
26 * So to actually modify stuff, chances are you want to go to pilot_weapSetFire.
27 */
28
29
30 #include "pilot.h"
31
32 #include "naev.h"
33
34 #include "nxml.h"
35
36 #include "log.h"
37 #include "array.h"
38 #include "weapon.h"
39 #include "escort.h"
40
41 #include "player.h"
42 #include "spfx.h"
43
44
45 /*
46 * Prototypes.
47 */
48 static void pilot_weapSetUpdateOutfits( Pilot* p, PilotWeaponSet *ws );
49 static PilotWeaponSet* pilot_weapSet( Pilot* p, int id );
50 static int pilot_weapSetFire( Pilot *p, PilotWeaponSet *ws, int level );
51 static int pilot_shootWeaponSetOutfit( Pilot* p, PilotWeaponSet *ws, Outfit *o, int level, double time );
52 static int pilot_shootWeapon( Pilot* p, PilotOutfitSlot* w, double time );
53 static void pilot_weapSetUpdateRange( PilotWeaponSet *ws );
54
55
56 /**
57 * @brief Gets a weapon set from id.
58 *
59 * @param p Pilot to get weapon set from.
60 * @param id ID of the weapon set.
61 * @return The weapon set matching id.
62 */
pilot_weapSet(Pilot * p,int id)63 static PilotWeaponSet* pilot_weapSet( Pilot* p, int id )
64 {
65 return &p->weapon_sets[ id ];
66 }
67
68
69 /**
70 * @brief Fires a weapon set.
71 *
72 * @param p Pilot firing weaponsets.
73 * @param ws Weapon set to fire.
74 * @param level Level of the firing weapon set.
75 */
pilot_weapSetFire(Pilot * p,PilotWeaponSet * ws,int level)76 static int pilot_weapSetFire( Pilot *p, PilotWeaponSet *ws, int level )
77 {
78 int i, j, ret, s;
79 Pilot *pt;
80 double time;
81 Outfit *o;
82
83 /* Case no outfits. */
84 if (ws->slots == NULL)
85 return 0;
86
87 /* Fire. */
88 ret = 0;
89 for (i=0; i<array_size(ws->slots); i++) {
90 o = ws->slots[i].slot->outfit;
91
92 /* Ignore NULL outfits. */
93 if (o == NULL)
94 continue;
95
96 /* Only "active" outfits. */
97 if ((level != -1) && (ws->slots[i].level != level))
98 continue;
99
100 /* Only run once for each weapon type in the group. */
101 s = 0;
102 for (j=0; j<i; j++) {
103 /* Only active outfits. */
104 if ((level != -1) && (ws->slots[j].level != level))
105 continue;
106 /* Found a match. */
107 if (ws->slots[j].slot->outfit == o) {
108 s = 1;
109 break;
110 }
111 }
112 if (s!=0)
113 continue;
114
115 /* Only "locked on" outfits. */
116 if (outfit_isSeeker(o) &&
117 (ws->slots[i].slot->u.ammo.lockon_timer > 0.))
118 continue;
119
120 /* If inrange is set we only fire at targets in range. */
121 time = INFINITY; /* With no target we just set time to infinity. */
122 if (p->target != p->id){
123 pt = pilot_get( p->target );
124 if (pt != NULL)
125 time = pilot_weapFlyTime( o, p, pt);
126 }
127
128 /* Only "inrange" outfits. */
129 if ( ws->inrange && outfit_duration(o) < time)
130 continue;
131
132 /* Shoot the weapon of the weaponset. */
133 ret += pilot_shootWeaponSetOutfit( p, ws, o, level, time );
134 }
135
136 return ret;
137 }
138
139
140 /**
141 * @brief Useful function for AI, clears activeness of all weapon sets.
142 */
pilot_weapSetAIClear(Pilot * p)143 void pilot_weapSetAIClear( Pilot* p )
144 {
145 int i;
146 PilotWeaponSet *ws;
147 for (i=0; i<PILOT_WEAPON_SETS; i++) {
148 ws = &p->weapon_sets[i];
149 ws->active = 0;
150 }
151 }
152
153
154 /**
155 * @brief Handles a weapon set press.
156 *
157 * @param p Pilot the weapon set belongs to.
158 * @param id ID of the weapon set.
159 * @param type Is +1 if it's a press or -1 if it's a release.
160 */
pilot_weapSetPress(Pilot * p,int id,int type)161 void pilot_weapSetPress( Pilot* p, int id, int type )
162 {
163 int i, l, on, n;
164 PilotWeaponSet *ws;
165
166 ws = pilot_weapSet(p,id);
167 /* Case no outfits. */
168 if (ws->slots == NULL)
169 return;
170
171 /* Handle fire groups. */
172 switch (ws->type) {
173 case WEAPSET_TYPE_CHANGE:
174 /* On press just change active weapon set to whatever is available. */
175 if (type > 0) {
176 if (id != p->active_set)
177 pilot_weapSetUpdateOutfits( p, ws );
178 p->active_set = id;
179 }
180 break;
181
182 case WEAPSET_TYPE_WEAPON:
183 /* Activation philosophy here is to turn on while pressed and off
184 * when it's not held anymore. */
185 if (type > 0)
186 ws->active = 1;
187 else if (type < 0)
188 ws->active = 0;
189 break;
190
191 case WEAPSET_TYPE_ACTIVE:
192 /* The behaviour here is more complex. What we do is consider a group
193 * to be entirely off if not all outfits are either on or cooling down.
194 * In the case it's deemed to be off, all outfits that are off get turned
195 * on, otherwise all outfits that are on are turrned to cooling down. */
196 /* Only care about presses. */
197 if (type < 0)
198 break;
199
200 /* Must not be disabled or cooling down. */
201 if ((pilot_isDisabled(p)) || (pilot_isFlag(p, PILOT_COOLDOWN)))
202 return;
203
204 /* Decide what to do. */
205 on = 1;
206 l = array_size(ws->slots);
207 for (i=0; i<l; i++) {
208 if (ws->slots[i].slot->state == PILOT_OUTFIT_OFF) {
209 on = 0;
210 break;
211 }
212 }
213
214 /* Turn them off. */
215 n = 0;
216 if (on) {
217 for (i=0; i<l; i++) {
218 if (ws->slots[i].slot->state != PILOT_OUTFIT_ON)
219 continue;
220
221 n += pilot_outfitOff( p, ws->slots[i].slot );
222 }
223 }
224 /* Turn them on. */
225 else {
226 for (i=0; i<l; i++) {
227 if (ws->slots[i].slot->state != PILOT_OUTFIT_OFF)
228 continue;
229 if (outfit_isAfterburner(ws->slots[i].slot->outfit))
230 pilot_afterburn( p );
231 else {
232 ws->slots[i].slot->state = PILOT_OUTFIT_ON;
233 ws->slots[i].slot->stimer = outfit_duration( ws->slots[i].slot->outfit );
234 }
235 n++;
236 }
237 }
238 /* Must recalculate stats. */
239 if (n > 0)
240 pilot_calcStats( p );
241
242 break;
243 }
244 }
245
246
247 /**
248 * @brief Updates the pilot's weapon sets.
249 *
250 * @param p Pilot to update.
251 */
pilot_weapSetUpdate(Pilot * p)252 void pilot_weapSetUpdate( Pilot* p )
253 {
254 PilotWeaponSet *ws;
255 int i;
256
257 /* Must not be doing hyperspace procedures. */
258 if (pilot_isFlag( p, PILOT_HYP_BEGIN))
259 return;
260
261 for (i=0; i<PILOT_WEAPON_SETS; i++) {
262 ws = &p->weapon_sets[i];
263 if (ws->slots == NULL)
264 continue;
265
266 /* Weapons must get "fired" every turn. */
267 if (ws->type == WEAPSET_TYPE_WEAPON) {
268 if (ws->active)
269 pilot_weapSetFire( p, ws, -1 );
270 }
271 }
272 }
273
274
275 /**
276 * @brief Updates the outfits with their current weapon set level.
277 */
pilot_weapSetUpdateOutfits(Pilot * p,PilotWeaponSet * ws)278 static void pilot_weapSetUpdateOutfits( Pilot* p, PilotWeaponSet *ws )
279 {
280 int i;
281
282 for (i=0; i<p->noutfits; i++)
283 p->outfits[i]->level = -1;
284
285 if (ws->slots != NULL)
286 for (i=0; i<array_size(ws->slots); i++)
287 ws->slots[i].slot->level = ws->slots[i].level;
288 }
289
290
291 /**
292 * @brief Checks the current weapon set type.
293 *
294 * @param p Pilot to manipulate.
295 * @param id ID of the weapon set to check.
296 * @return The type of the weapon set.
297 */
pilot_weapSetTypeCheck(Pilot * p,int id)298 int pilot_weapSetTypeCheck( Pilot* p, int id )
299 {
300 PilotWeaponSet *ws;
301 ws = pilot_weapSet(p,id);
302 return ws->type;
303 }
304
305
306 /**
307 * @brief Changes the weapon sets mode.
308 *
309 * @param p Pilot to manipulate.
310 * @param id ID of the weapon set.
311 * @param fire Whether or not to enable fire mode.
312 */
pilot_weapSetType(Pilot * p,int id,int type)313 void pilot_weapSetType( Pilot* p, int id, int type )
314 {
315 int i;
316 PilotWeaponSet *ws;
317
318 ws = pilot_weapSet(p,id);
319 ws->type = type;
320
321 /* Set levels just in case. */
322 if (ws->slots == NULL)
323 return;
324
325 /* See if we must overwrite levels. */
326 if ((ws->type == WEAPSET_TYPE_WEAPON) ||
327 (ws->type == WEAPSET_TYPE_ACTIVE))
328 for (i=0; i<array_size(ws->slots); i++)
329 ws->slots[i].level = 0;
330 }
331
332
333 /**
334 * @brief Checks the current weapon set inrange property.
335 *
336 * @param p Pilot to manipulate.
337 * @param id ID of the weapon set to check.
338 * @return The inrange mode of the weapon set.
339 */
pilot_weapSetInrangeCheck(Pilot * p,int id)340 int pilot_weapSetInrangeCheck( Pilot* p, int id )
341 {
342 PilotWeaponSet *ws;
343 ws = pilot_weapSet(p,id);
344 return ws->inrange;
345 }
346
347
348 /**
349 * @brief Changes the weapon set inrange property.
350 *
351 * @param p Pilot to manipulate.
352 * @param id ID of the weapon set.
353 * @param inrange Whether or not to only fire at stuff in range.
354 */
pilot_weapSetInrange(Pilot * p,int id,int inrange)355 void pilot_weapSetInrange( Pilot* p, int id, int inrange )
356 {
357 PilotWeaponSet *ws;
358 ws = pilot_weapSet(p,id);
359 ws->inrange = inrange;
360 }
361
362
363 /**
364 * @brief Gets the name of a weapon set.
365 */
pilot_weapSetName(Pilot * p,int id)366 const char *pilot_weapSetName( Pilot* p, int id )
367 {
368 PilotWeaponSet *ws;
369 ws = pilot_weapSet(p,id);
370 if ((ws->slots == NULL) || (array_size(ws->slots)==0))
371 return "Unused";
372 switch (ws->type) {
373 case WEAPSET_TYPE_CHANGE: return "Weapons - Switched"; break;
374 case WEAPSET_TYPE_WEAPON: return "Weapons - Instant"; break;
375 case WEAPSET_TYPE_ACTIVE: return "Abilities - Toggled"; break;
376 }
377 return NULL;
378 }
379
380
381 /**
382 * @brief Removes slots by type from the weapon set.
383 */
pilot_weapSetRmSlot(Pilot * p,int id,OutfitSlotType type)384 void pilot_weapSetRmSlot( Pilot *p, int id, OutfitSlotType type )
385 {
386 int i, n, l;
387 PilotWeaponSet *ws;
388
389 /* We must clean up the slots. */
390 n = 0; /* Number to remove. */
391 ws = pilot_weapSet(p,id);
392 if (ws->slots == NULL)
393 return;
394 l = array_size(ws->slots);
395 for (i=0; i<l; i++) {
396 if (ws->slots->slot->sslot->slot.type != type)
397 continue;
398
399 /* Move down. */
400 memmove( &ws->slots[i], &ws->slots[i+1], sizeof(PilotWeaponSetOutfit) * (l-i-1) );
401 n++;
402 }
403
404 /* Remove surplus. */
405 array_erase( &ws->slots, &ws->slots[l-n], &ws->slots[l] );
406 }
407
408
409 /**
410 * @brief Adds an outfit to a weapon set.
411 *
412 * @param p Pilot to manipulate.
413 * @param id ID of the weapon set.
414 * @param o Outfit to add.
415 * @param level Level of the trigger.
416 */
pilot_weapSetAdd(Pilot * p,int id,PilotOutfitSlot * o,int level)417 void pilot_weapSetAdd( Pilot* p, int id, PilotOutfitSlot *o, int level )
418 {
419 PilotWeaponSet *ws;
420 PilotWeaponSetOutfit *slot;
421 Outfit *oo;
422 int i, j;
423 double r;
424
425 ws = pilot_weapSet(p,id);
426
427 /* Make sure outfit is valid. */
428 oo = o->outfit;
429 if (oo == NULL)
430 return;
431
432 /* Make sure outfit type is weapon (or usable). */
433 if (!outfit_isActive(oo))
434 return;
435
436 /* Create if needed. */
437 if (ws->slots == NULL)
438 ws->slots = array_create( PilotWeaponSetOutfit );
439
440 /* Check if already there. */
441 for (i=0; i<array_size(ws->slots); i++) {
442 if (ws->slots[i].slot == o) {
443 ws->slots[i].level = level;
444
445 /* Update if needed. */
446 if (id == p->active_set)
447 pilot_weapSetUpdateOutfits( p, ws );
448 return;
449 }
450 }
451
452 /* Add it. */
453 slot = &array_grow( &ws->slots );
454 slot->level = level;
455 slot->slot = o;
456 r = outfit_range(oo);
457 if (r > 0)
458 slot->range2 = pow2(r);
459
460 /* Updated cached weapset. */
461 o->weapset = -1;
462 for (j=0; j<PILOT_WEAPON_SETS; j++) {
463 if (pilot_weapSetCheck(p, j, o) != -1) {
464 o->weapset = j;
465 break;
466 }
467 }
468
469 /* Update range. */
470 pilot_weapSetUpdateRange( ws );
471
472 /* Update if needed. */
473 if (id == p->active_set)
474 pilot_weapSetUpdateOutfits( p, ws );
475 }
476
477
478 /**
479 * @brief Removes a slot from a weapon set.
480 *
481 * @param p Pilot who owns the weapon set.
482 * @param id ID of the weapon set.
483 * @param o Outfit to remove.
484 */
pilot_weapSetRm(Pilot * p,int id,PilotOutfitSlot * o)485 void pilot_weapSetRm( Pilot* p, int id, PilotOutfitSlot *o )
486 {
487 PilotWeaponSet *ws;
488 int i, j;
489
490 /* Make sure it has slots. */
491 ws = pilot_weapSet(p,id);
492 if (ws->slots == NULL)
493 return;
494
495 /* Find the slot. */
496 for (i=0; i<array_size(ws->slots); i++) {
497 if (ws->slots[i].slot != o)
498 continue;
499
500 array_erase( &ws->slots, &ws->slots[i], &ws->slots[i+1] );
501
502 /* Update range. */
503 pilot_weapSetUpdateRange( ws );
504
505 /* Update if needed. */
506 if (id == p->active_set)
507 pilot_weapSetUpdateOutfits( p, ws );
508
509 /* Updated cached weapset. */
510 o->weapset = -1;
511 for (j=0; j<PILOT_WEAPON_SETS; j++) {
512 if (pilot_weapSetCheck(p, j, o) != -1) {
513 o->weapset = j;
514 break;
515 }
516 }
517
518 return;
519 }
520 }
521
522
523 /**
524 * @brief Checks to see if a slot is in a weapon set.
525 *
526 * @param p Pilot to check.
527 * @param id ID of the weapon set.
528 * @param o Outfit slot to check.
529 * @return The level to which it belongs (or -1 if it isn't set).
530 */
pilot_weapSetCheck(Pilot * p,int id,PilotOutfitSlot * o)531 int pilot_weapSetCheck( Pilot* p, int id, PilotOutfitSlot *o )
532 {
533 PilotWeaponSet *ws;
534 int i;
535
536 /* Make sure it has slots. */
537 ws = pilot_weapSet(p,id);
538 if (ws->slots == NULL)
539 return -1;
540
541 /* Find the slot. */
542 for (i=0; i<array_size(ws->slots); i++)
543 if (ws->slots[i].slot == o)
544 return ws->slots[i].level;
545
546 /* Not found. */
547 return -1;
548 }
549
550
551 /**
552 * @brief Updates the weapon range for a pilot weapon set.
553 *
554 * @param ws Weapon Set to update range for.
555 */
pilot_weapSetUpdateRange(PilotWeaponSet * ws)556 static void pilot_weapSetUpdateRange( PilotWeaponSet *ws )
557 {
558 int i, lev;
559 double range, speed;
560 double range_accum[PILOT_WEAPSET_MAX_LEVELS];
561 int range_num[PILOT_WEAPSET_MAX_LEVELS];
562 double speed_accum[PILOT_WEAPSET_MAX_LEVELS];
563 int speed_num[PILOT_WEAPSET_MAX_LEVELS];
564
565 /* No slots. */
566 if (ws->slots == NULL) {
567 for (i=0; i<PILOT_WEAPSET_MAX_LEVELS; i++) {
568 ws->range[i] = 0.;
569 ws->speed[i] = 0.;
570 }
571 return;
572 }
573
574 /* Calculate ranges. */
575 for (i=0; i<PILOT_WEAPSET_MAX_LEVELS; i++) {
576 range_accum[i] = 0.;
577 range_num[i] = 0;
578 speed_accum[i] = 0.;
579 speed_num[i] = 0;
580 }
581 for (i=0; i<array_size(ws->slots); i++) {
582 if (ws->slots[i].slot->outfit == NULL)
583 continue;
584
585 /* Get level. */
586 lev = ws->slots[i].level;
587 if (lev >= PILOT_WEAPSET_MAX_LEVELS)
588 continue;
589
590 /* Empty Launchers aren't valid */
591 if (outfit_isLauncher(ws->slots[i].slot->outfit) && ws->slots[i].slot->u.ammo.quantity <= 0)
592 continue;
593
594 /* Get range. */
595 range = outfit_range(ws->slots[i].slot->outfit);
596 if (range >= 0.) {
597 /* Calculate. */
598 range_accum[ lev ] += range;
599 range_num[ lev ]++;
600 }
601
602 /* Get speed. */
603 speed = outfit_speed(ws->slots[i].slot->outfit);
604 if (speed >= 0.) {
605 /* Calculate. */
606 speed_accum[ lev ] += speed;
607 speed_num[ lev ]++;
608 }
609 }
610
611 /* Postprocess. */
612 for (i=0; i<PILOT_WEAPSET_MAX_LEVELS; i++) {
613 /* Postprocess range. */
614 if (range_num[i] == 0)
615 ws->range[i] = 0;
616 else
617 ws->range[i] = range_accum[i] / (double) range_num[i];
618
619 /* Postprocess speed. */
620 if (speed_num[i] == 0)
621 ws->speed[i] = 0;
622 else
623 ws->speed[i] = speed_accum[i] / (double) speed_num[i];
624 }
625 }
626
627
628 /**
629 * @brief Gets the range of the current pilot weapon set.
630 *
631 * @param p Pilot to get the range of.
632 * @param id ID of weapon set to get the range of.
633 * @param Level of the weapons to get the range of (-1 for all).
634 */
pilot_weapSetRange(Pilot * p,int id,int level)635 double pilot_weapSetRange( Pilot* p, int id, int level )
636 {
637 PilotWeaponSet *ws;
638 int i;
639 double range;
640
641 ws = pilot_weapSet(p,id);
642 if (level < 0) {
643 range = 0;
644 for (i=0; i<PILOT_WEAPSET_MAX_LEVELS; i++)
645 range += ws->range[i];
646 }
647 else
648 range = ws->range[ level ];
649
650 return range;
651 }
652
653
654 /**
655 * @brief Gets the speed of the current pilot weapon set.
656 *
657 * @param p Pilot to get the speed of.
658 * @param id ID of weapon set to get the speed of.
659 * @param Level of the weapons to get the speed of (-1 for all).
660 */
pilot_weapSetSpeed(Pilot * p,int id,int level)661 double pilot_weapSetSpeed( Pilot* p, int id, int level )
662 {
663 PilotWeaponSet *ws;
664 int i;
665 double speed;
666
667 ws = pilot_weapSet(p,id);
668 if (level < 0) {
669 speed = 0;
670 for (i=0; i<PILOT_WEAPSET_MAX_LEVELS; i++)
671 speed += ws->speed[i];
672 }
673 else
674 speed = ws->speed[ level ];
675
676 return speed;
677 }
678
679
680 /**
681 * @brief Cleans up a weapon set.
682 *
683 * @param p Pilot who owns the weapon set.
684 * @param id ID of the weapon set to clean up.
685 */
pilot_weapSetCleanup(Pilot * p,int id)686 void pilot_weapSetCleanup( Pilot* p, int id )
687 {
688 PilotWeaponSet *ws;
689
690 ws = pilot_weapSet(p,id);
691
692 if (ws->slots != NULL)
693 array_free( ws->slots );
694 ws->slots = NULL;
695
696 /* Update range. */
697 pilot_weapSetUpdateRange( ws );
698 }
699
700
701 /**
702 * @brief Frees a pilot's weapon sets.
703 */
pilot_weapSetFree(Pilot * p)704 void pilot_weapSetFree( Pilot* p )
705 {
706 int i;
707 for (i=0; i<PILOT_WEAPON_SETS; i++)
708 pilot_weapSetCleanup( p, i );
709 }
710
711
712
713 /**
714 * @brief Lists the items in a pilot weapon set.
715 *
716 * @param p Pilot who owns the weapon set.
717 * @param id ID of the weapon set.
718 * @param[out] n Number of elements in the list.
719 * @return The array of pilot weaponset outfits.
720 */
pilot_weapSetList(Pilot * p,int id,int * n)721 PilotWeaponSetOutfit* pilot_weapSetList( Pilot* p, int id, int *n )
722 {
723 PilotWeaponSet *ws;
724
725 ws = pilot_weapSet(p,id);
726 if (ws->slots == NULL) {
727 *n = 0;
728 return NULL;
729 }
730
731 *n = array_size(ws->slots);
732 return ws->slots;
733 }
734
735
736 /**
737 * @brief Makes the pilot shoot.
738 *
739 * @param p The pilot which is shooting.
740 * @param level Level of the shot.
741 * @return The number of shots fired.
742 */
pilot_shoot(Pilot * p,int level)743 int pilot_shoot( Pilot* p, int level )
744 {
745 PilotWeaponSet *ws;
746 int ret;
747
748 /* Get active set. */
749 ws = pilot_weapSet( p, p->active_set );
750
751 /* Fire weapons. */
752 if (ws->type == WEAPSET_TYPE_CHANGE) { /* Must be a change set or a weaponset. */
753 ret = pilot_weapSetFire( p, ws, level );
754
755 /* Firing weapons aborts active cooldown. */
756 if (pilot_isFlag(p, PILOT_COOLDOWN) && ret)
757 pilot_cooldownEnd(p, NULL);
758
759 return ret;
760 }
761
762 return 0;
763 }
764
765
766 /**
767 * @brief Have pilot stop shooting their weapon.
768 *
769 * Only really deals with beam weapons.
770 *
771 * @param p Pilot that was shooting.
772 * @param level Level of the shot.
773 */
pilot_shootStop(Pilot * p,int level)774 void pilot_shootStop( Pilot* p, int level )
775 {
776 int i, recalc;
777 PilotWeaponSet *ws;
778 PilotOutfitSlot *slot;
779
780 /* Get active set. */
781 ws = pilot_weapSet( p, p->active_set );
782
783 /* Case no outfits. */
784 if (ws->slots == NULL)
785 return;
786
787 /* Stop all beams. */
788 recalc = 0;
789 for (i=0; i<array_size(ws->slots); i++) {
790 slot = ws->slots[i].slot;
791
792 /* Must have associated outfit. */
793 if (ws->slots[i].slot->outfit == NULL)
794 continue;
795
796 /* Must match level. */
797 if ((level != -1) && (ws->slots[i].level != level))
798 continue;
799
800 /* Only handle beams. */
801 if (!outfit_isBeam(slot->outfit)) {
802 /* Turn off the state. */
803 if (outfit_isMod( slot->outfit )) {
804 slot->state = PILOT_OUTFIT_OFF;
805 recalc = 1;
806 }
807 continue;
808 }
809
810 /* Stop beam. */
811 if (ws->slots[i].slot->u.beamid > 0) {
812 /* Enforce minimum duration if set. */
813 if (slot->outfit->u.bem.min_duration > 0.) {
814
815 slot->stimer = slot->outfit->u.bem.min_duration -
816 (slot->outfit->u.bem.duration - slot->timer);
817
818 if (slot->stimer > 0.)
819 continue;
820 }
821
822 beam_end( p->id, slot->u.beamid );
823 pilot_stopBeam(p, slot);
824 }
825 }
826
827 /* Must recalculate. */
828 if (recalc)
829 pilot_calcStats( p );
830 }
831
832
833 /**
834 * @brief Stops a beam outfit and sets delay as appropriate.
835 *
836 * @param p Pilot that is firing.
837 * @param w Pilot's beam outfit.
838 */
pilot_stopBeam(Pilot * p,PilotOutfitSlot * w)839 void pilot_stopBeam( Pilot *p, PilotOutfitSlot *w )
840 {
841 double rate_mod, energy_mod, used;
842
843 /* There's nothing to do if the beam isn't active. */
844 if (w->u.beamid == 0)
845 return;
846
847 /* Safeguard against a nasty race condition. */
848 if (w->outfit == NULL) {
849 w->u.beamid = 0;
850 return;
851 }
852
853 /* Calculate rate modifier. */
854 pilot_getRateMod( &rate_mod, &energy_mod, p, w->outfit );
855
856 /* Beam duration used. */
857 used = w->outfit->u.bem.duration - w->timer;
858
859 w->timer = rate_mod * (used / w->outfit->u.bem.duration) * outfit_delay( w->outfit );
860 w->u.beamid = 0;
861 }
862
863
864 /**
865 * @brief Computes an estimation of ammo flying time
866 *
867 * @param w the weapon that shoot
868 * @param parent Parent of the weapon
869 * @param target Target of the weapon
870 */
pilot_weapFlyTime(Outfit * o,Pilot * parent,Pilot * target)871 double pilot_weapFlyTime( Outfit *o, Pilot *parent, Pilot *target)
872 {
873 Vector2d approach_vector, relative_location, orthoradial_vector;
874 double speed, radial_speed, orthoradial_speed, dist, t;
875
876 dist = vect_dist( &parent->solid->pos, &target->solid->pos );
877
878 /* Beam weapons */
879 if (outfit_isBeam(o))
880 {
881 if (dist > o->u.bem.range)
882 return INFINITY;
883 return 0.;
884 }
885
886 /* A bay doesn't have range issues */
887 if (outfit_isFighterBay(o))
888 return 0.;
889
890 /* Rockets use absolute velocity while bolt use relative vel */
891 if (outfit_isLauncher(o))
892 vect_cset( &approach_vector, - VX(target->solid->vel), - VY(target->solid->vel) );
893 else
894 vect_cset( &approach_vector, VX(parent->solid->vel) - VX(target->solid->vel),
895 VY(parent->solid->vel) - VY(target->solid->vel) );
896
897 speed = outfit_speed(o);
898
899 /* Get the vector : shooter -> target*/
900 vect_cset( &relative_location, VX(target->solid->pos) - VX(parent->solid->pos),
901 VY(target->solid->pos) - VY(parent->solid->pos) );
902
903 /* Get the orthogonal vector*/
904 vect_cset(&orthoradial_vector, VY(parent->solid->pos) - VY(target->solid->pos),
905 VX(target->solid->pos) - VX(parent->solid->pos) );
906
907 radial_speed = vect_dot( &approach_vector, &relative_location );
908 radial_speed = radial_speed / VMOD(relative_location);
909
910 orthoradial_speed = vect_dot(&approach_vector, &orthoradial_vector);
911 orthoradial_speed = orthoradial_speed / VMOD(relative_location);
912
913 if( ((speed*speed - VMOD(approach_vector)*VMOD(approach_vector)) != 0) && (speed*speed - orthoradial_speed*orthoradial_speed) > 0)
914 t = dist * (sqrt( speed*speed - orthoradial_speed*orthoradial_speed ) - radial_speed) /
915 (speed*speed - VMOD(approach_vector)*VMOD(approach_vector));
916 else
917 return INFINITY;
918
919 /* if t < 0, try the other solution*/
920 if (t < 0)
921 t = - dist * (sqrt( speed*speed - orthoradial_speed*orthoradial_speed ) + radial_speed) /
922 (speed*speed - VMOD(approach_vector)*VMOD(approach_vector));
923
924 /* if t still < 0, no solution*/
925 if (t < 0)
926 return INFINITY;
927
928 return t;
929 }
930
931
932 /**
933 * @brief Calculates and shoots the appropriate weapons in a weapon set matching an outfit.
934 */
pilot_shootWeaponSetOutfit(Pilot * p,PilotWeaponSet * ws,Outfit * o,int level,double time)935 static int pilot_shootWeaponSetOutfit( Pilot* p, PilotWeaponSet *ws, Outfit *o, int level, double time )
936 {
937 int i, ret;
938 int is_launcher, is_bay;
939 double rate_mod, energy_mod;
940 PilotOutfitSlot *w;
941 int maxp, minh;
942 double q, maxt;
943
944 /* Store number of shots. */
945 ret = 0;
946
947 /** @TODO Make beams not fire all at once. */
948 if (outfit_isBeam(o)) {
949 for (i=0; i<array_size(ws->slots); i++)
950 if (ws->slots[i].slot->outfit == o)
951 ret += pilot_shootWeapon( p, ws->slots[i].slot, 0 );
952 return ret;
953 }
954
955 /* Stores if it is a launcher. */
956 is_launcher = outfit_isLauncher(o);
957
958 is_bay = outfit_isFighterBay(o);
959
960 /* Calculate rate modifier. */
961 pilot_getRateMod( &rate_mod, &energy_mod, p, o );
962
963 /* Find optimal outfit, coolest that can fire. */
964 minh = -1;
965 maxt = 0.;
966 maxp = -1;
967 q = 0.;
968 for (i=0; i<array_size(ws->slots); i++) {
969 /* Only matching outfits. */
970 if (ws->slots[i].slot->outfit != o)
971 continue;
972
973 /* Only match levels. */
974 if ((level != -1) && (ws->slots[i].level != level))
975 continue;
976
977 /* Simplicity. */
978 w = ws->slots[i].slot;
979
980 /* Launcher only counts with ammo. */
981 if ((is_launcher || is_bay) && ((w->u.ammo.outfit == NULL) || (w->u.ammo.quantity <= 0)))
982 continue;
983
984 /* Get coolest that can fire. */
985 if (w->timer <= 0.) {
986 if (is_launcher) {
987 if ((minh < 0) || (ws->slots[minh].slot->u.ammo.quantity < w->u.ammo.quantity))
988 minh = i;
989 }
990 else {
991 if ((minh < 0) || (ws->slots[minh].slot->heat_T > w->heat_T))
992 minh = i;
993 }
994 }
995
996 /* Save some stuff. */
997 if ((maxp < 0) || (w->timer > maxt)) {
998 maxp = i;
999 maxt = w->timer;
1000 }
1001 q += 1.;
1002 }
1003
1004 /* No weapon can fire. */
1005 if (minh < 0)
1006 return 0;
1007
1008 /* Only fire if the last weapon to fire fired more than (q-1)/q ago. */
1009 if (maxt > rate_mod * outfit_delay(o) * ((q-1.) / q))
1010 return 0;
1011
1012 /* Shoot the weapon. */
1013 ret += pilot_shootWeapon( p, ws->slots[minh].slot, time );
1014
1015 return ret;
1016 }
1017
1018
1019 /**
1020 * @brief Actually handles the shooting, how often the player.p can shoot and such.
1021 *
1022 * @param p Pilot that is shooting.
1023 * @param w Pilot's outfit to shoot.
1024 * @param time Expected flight time.
1025 * @return 0 if nothing was shot and 1 if something was shot.
1026 */
pilot_shootWeapon(Pilot * p,PilotOutfitSlot * w,double time)1027 static int pilot_shootWeapon( Pilot* p, PilotOutfitSlot* w, double time )
1028 {
1029 Vector2d vp, vv;
1030 double rate_mod, energy_mod;
1031 double energy;
1032 int j;
1033
1034 /* Make sure weapon has outfit. */
1035 if (w->outfit == NULL)
1036 return 0;
1037
1038 /* Reset beam shut-off if needed. */
1039 if (outfit_isBeam(w->outfit) && w->outfit->u.bem.min_duration)
1040 w->stimer = INFINITY;
1041
1042 /* check to see if weapon is ready */
1043 if (w->timer > 0.)
1044 return 0;
1045
1046 /* Calculate rate modifier. */
1047 pilot_getRateMod( &rate_mod, &energy_mod, p, w->outfit );
1048
1049 /* Get weapon mount position. */
1050 pilot_getMount( p, w, &vp );
1051 vp.x += p->solid->pos.x;
1052 vp.y += p->solid->pos.y;
1053
1054 /* Modify velocity to take into account the rotation. */
1055 vect_cset( &vv, p->solid->vel.x + vp.x*p->solid->dir_vel,
1056 p->solid->vel.y + vp.y*p->solid->dir_vel );
1057
1058 /*
1059 * regular bolt weapons
1060 */
1061 if (outfit_isBolt(w->outfit)) {
1062
1063 /* enough energy? */
1064 if (outfit_energy(w->outfit)*energy_mod > p->energy)
1065 return 0;
1066
1067 energy = outfit_energy(w->outfit)*energy_mod;
1068 p->energy -= energy;
1069 pilot_heatAddSlot( p, w );
1070 weapon_add( w->outfit, w->heat_T, p->solid->dir,
1071 &vp, &p->solid->vel, p, p->target, time );
1072 }
1073
1074 /*
1075 * Beam weapons.
1076 */
1077 else if (outfit_isBeam(w->outfit)) {
1078 /* Don't fire if the existing beam hasn't been destroyed yet. */
1079 if (w->u.beamid > 0)
1080 return 0;
1081
1082 /* Check if enough energy to last a second. */
1083 if (outfit_energy(w->outfit)*energy_mod > p->energy)
1084 return 0;
1085
1086 /** @todo Handle warmup stage. */
1087 w->state = PILOT_OUTFIT_ON;
1088 w->u.beamid = beam_start( w->outfit, p->solid->dir,
1089 &vp, &p->solid->vel, p, p->target, w );
1090
1091 w->timer = w->outfit->u.bem.duration;
1092
1093 return 1; /* Return early due to custom timer logic. */
1094 }
1095
1096 /*
1097 * missile launchers
1098 *
1099 * must be a secondary weapon
1100 */
1101 else if (outfit_isLauncher(w->outfit)) {
1102
1103 /* Shooter can't be the target - sanity check for the player.p */
1104 if ((w->outfit->u.lau.ammo->u.amm.ai != AMMO_AI_DUMB) && (p->id==p->target))
1105 return 0;
1106
1107 /* Must have ammo left. */
1108 if ((w->u.ammo.outfit == NULL) || (w->u.ammo.quantity <= 0))
1109 return 0;
1110
1111 /* enough energy? */
1112 if (outfit_energy(w->u.ammo.outfit)*energy_mod > p->energy)
1113 return 0;
1114
1115 energy = outfit_energy(w->u.ammo.outfit)*energy_mod;
1116 p->energy -= energy;
1117 pilot_heatAddSlot( p, w );
1118 weapon_add( w->outfit, w->heat_T, p->solid->dir,
1119 &vp, &p->solid->vel, p, p->target, time );
1120
1121 w->u.ammo.quantity -= 1; /* we just shot it */
1122 p->mass_outfit -= w->u.ammo.outfit->mass;
1123 p->solid->mass -= w->u.ammo.outfit->mass;
1124
1125 pilot_updateMass( p );
1126
1127 /* If last ammo was shot, update the range */
1128 if (w->u.ammo.quantity <= 0) {
1129 for (j=0; j<PILOT_WEAPON_SETS; j++)
1130 pilot_weapSetUpdateRange( &p->weapon_sets[j] );
1131 }
1132 }
1133
1134 /*
1135 * Fighter bays.
1136 */
1137 else if (outfit_isFighterBay(w->outfit)) {
1138
1139 /* Must have ammo left. */
1140 if ((w->u.ammo.outfit == NULL) || (w->u.ammo.quantity <= 0))
1141 return 0;
1142
1143 /* Create the escort. */
1144 escort_create( p, w->u.ammo.outfit->u.fig.ship,
1145 &vp, &p->solid->vel, p->solid->dir, ESCORT_TYPE_BAY, 1 );
1146
1147 w->u.ammo.quantity -= 1; /* we just shot it */
1148 p->mass_outfit -= w->u.ammo.outfit->mass;
1149 w->u.ammo.deployed += 1; /* Mark as deployed. */
1150 pilot_updateMass( p );
1151 }
1152 else
1153 WARN("Shooting unknown weapon type: %s", w->outfit->name);
1154
1155
1156 /* Reset timer. */
1157 w->timer += rate_mod * outfit_delay( w->outfit );
1158
1159 return 1;
1160 }
1161
1162
1163 /**
1164 * @brief Gets applicable fire rate and energy modifications for a pilot's weapon.
1165 *
1166 * @param[out] rate_mod Fire rate multiplier.
1167 * @param[out] energy_mod Energy use multiplier.
1168 * @param p Pilot who owns the outfit.
1169 * @param w Pilot's outfit.
1170 */
pilot_getRateMod(double * rate_mod,double * energy_mod,Pilot * p,Outfit * o)1171 void pilot_getRateMod( double *rate_mod, double* energy_mod,
1172 Pilot* p, Outfit *o )
1173 {
1174 switch (o->type) {
1175 case OUTFIT_TYPE_BOLT:
1176 *rate_mod = 2. - p->stats.fwd_firerate; /* Invert. */
1177 *energy_mod = p->stats.fwd_energy;
1178 break;
1179 case OUTFIT_TYPE_TURRET_BOLT:
1180 *rate_mod = 2. - p->stats.tur_firerate; /* Invert. */
1181 *energy_mod = p->stats.tur_energy;
1182 break;
1183
1184 default:
1185 *rate_mod = 1.;
1186 *energy_mod = 1.;
1187 break;
1188 }
1189 }
1190
1191
1192 /**
1193 * @brief Clears the pilots weapon settings.
1194 *
1195 * @param p Pilot whose weapons we're clearing.
1196 */
pilot_weaponClear(Pilot * p)1197 void pilot_weaponClear( Pilot *p )
1198 {
1199 int i;
1200 PilotWeaponSet *ws;
1201
1202 for (i=0; i<PILOT_WEAPON_SETS; i++) {
1203 ws = pilot_weapSet( p, i );
1204 if (ws->slots != NULL)
1205 array_erase( &ws->slots, &ws->slots[0], &ws->slots[ array_size(ws->slots) ] );
1206 }
1207 }
1208
1209
1210 /**
1211 * @brief Tries to automatically set and create the pilot's weapon set.
1212 *
1213 * Weapon set 0 is for all weapons. <br />
1214 * Weapon set 1 is for forward weapons. Ammo using weapons are secondaries. <br />
1215 * Weapon set 2 is for turret weapons. Ammo using weapons are secondaries. <br />
1216 * Weapon set 3 is for all weapons. Forwards are primaries and turrets are secondaries. <br />
1217 * Weapon set 4 is for seeking weapons. High payload variants are secondaries. <br />
1218 * Weapon set 5 is for fighter bays. <br />
1219 *
1220 * @param p Pilot to automagically generate weapon lists.
1221 */
pilot_weaponAuto(Pilot * p)1222 void pilot_weaponAuto( Pilot *p )
1223 {
1224 PilotOutfitSlot *slot;
1225 Outfit *o;
1226 int i, level, id;
1227
1228 /* Clear weapons. */
1229 pilot_weaponClear( p );
1230
1231 /* Set modes. */
1232 pilot_weapSetType( p, 0, WEAPSET_TYPE_CHANGE );
1233 pilot_weapSetType( p, 1, WEAPSET_TYPE_CHANGE );
1234 pilot_weapSetType( p, 2, WEAPSET_TYPE_CHANGE );
1235 pilot_weapSetType( p, 3, WEAPSET_TYPE_CHANGE );
1236 pilot_weapSetType( p, 4, WEAPSET_TYPE_WEAPON );
1237 pilot_weapSetType( p, 5, WEAPSET_TYPE_WEAPON );
1238 pilot_weapSetType( p, 6, WEAPSET_TYPE_ACTIVE );
1239 pilot_weapSetType( p, 7, WEAPSET_TYPE_ACTIVE );
1240 pilot_weapSetType( p, 8, WEAPSET_TYPE_ACTIVE );
1241 pilot_weapSetType( p, 9, WEAPSET_TYPE_ACTIVE );
1242
1243 /* All should be inrange. */
1244 if (!pilot_isPlayer(p))
1245 for (i=0; i<PILOT_WEAPON_SETS; i++){
1246 pilot_weapSetInrange( p, i, 1 );
1247 /* Update range and speed (at 0)*/
1248 pilot_weapSetUpdateRange( &p->weapon_sets[i] );
1249 }
1250
1251 /* Iterate through all the outfits. */
1252 for (i=0; i<p->noutfits; i++) {
1253 slot = p->outfits[i];
1254 o = slot->outfit;
1255
1256 /* Must be non-empty, and a weapon or active outfit. */
1257 if ((o == NULL) || !outfit_isActive(o)) {
1258 slot->level = -1; /* Clear level. */
1259 slot->weapset = -1;
1260 continue;
1261 }
1262
1263 /* Manually defined group preempts others. */
1264 if (o->group) {
1265 id = o->group;
1266 }
1267 /* Bolts and beams. */
1268 else if (outfit_isBolt(o) || outfit_isBeam(o) ||
1269 (outfit_isLauncher(o) && !outfit_isSeeker(o->u.lau.ammo))) {
1270 id = outfit_isTurret(o) ? 2 : 1;
1271 }
1272 /* Seekers. */
1273 else if (outfit_isLauncher(o) && outfit_isSeeker(o->u.lau.ammo)) {
1274 id = 4;
1275 }
1276 /* Fighter bays. */
1277 else if (outfit_isFighterBay(o)) {
1278 id = 5;
1279 }
1280 /* Ignore rest. */
1281 else {
1282 slot->level = -1;
1283 continue;
1284 }
1285
1286 /* Set level based on secondary flag. */
1287 level = outfit_isSecondary(o);
1288
1289 /* Add to its base group. */
1290 pilot_weapSetAdd( p, id, slot, level );
1291
1292 /* Also add another copy to another group. */
1293 if (id == 1) { /* Forward. */
1294 pilot_weapSetAdd( p, 0, slot, level ); /* Also get added to 'All'. */
1295 pilot_weapSetAdd( p, 3, slot, 0 ); /* Also get added to 'Fwd/Tur'. */
1296 }
1297 else if (id == 2) { /* Turrets. */
1298 pilot_weapSetAdd( p, 0, slot, level ); /* Also get added to 'All'. */
1299 pilot_weapSetAdd( p, 3, slot, 1 ); /* Also get added to 'Fwd/Tur'. */
1300 }
1301 else if (id == 4) /* Seekers */
1302 pilot_weapSetAdd( p, 0, slot, level ); /* Also get added to 'All'. */
1303 }
1304
1305 /* Update active weapon set. */
1306 pilot_weapSetUpdateOutfits( p, &p->weapon_sets[ p->active_set ] );
1307 }
1308
1309
1310 /**
1311 * @brief Gives the pilot a default weapon set.
1312 */
pilot_weaponSetDefault(Pilot * p)1313 void pilot_weaponSetDefault( Pilot *p )
1314 {
1315 int i;
1316
1317 /* If current set isn't a fire group no need to worry. */
1318 if (p->weapon_sets[ p->active_set ].type != WEAPSET_TYPE_CHANGE) {
1319 /* Update active weapon set. */
1320 pilot_weapSetUpdateOutfits( p, &p->weapon_sets[ p->active_set ] );
1321 return;
1322 }
1323
1324 /* Find first fire group. */
1325 for (i=0; i<PILOT_WEAPON_SETS; i++)
1326 if (p->weapon_sets[i].type == WEAPSET_TYPE_CHANGE)
1327 break;
1328
1329 /* Set active set to first if all fire groups or first non-fire group. */
1330 if (i >= PILOT_WEAPON_SETS)
1331 p->active_set = 0;
1332 else
1333 p->active_set = i;
1334
1335 /* Update active weapon set. */
1336 pilot_weapSetUpdateOutfits( p, &p->weapon_sets[ p->active_set ] );
1337 }
1338
1339
1340 /**
1341 * @brief Sets the weapon set as sane.
1342 *
1343 * @param p Pilot to set weapons as sane.
1344 */
pilot_weaponSane(Pilot * p)1345 void pilot_weaponSane( Pilot *p )
1346 {
1347 int i, j;
1348 int n, l;
1349 PilotWeaponSet *ws;
1350
1351 for (j=0; j<PILOT_WEAPON_SETS; j++) {
1352 ws = &p->weapon_sets[j];
1353 if (ws->slots == NULL)
1354 continue;
1355
1356 l = array_size(ws->slots);
1357 n = 0;
1358 for (i=0; i<l; i++) {
1359 if (ws->slots[i].slot->outfit != NULL)
1360 continue;
1361
1362 /* Move down. */
1363 memmove( &ws->slots[i], &ws->slots[i+1], sizeof(PilotWeaponSetOutfit) * (l-i-1) );
1364 n++;
1365 }
1366 /* Remove surplus. */
1367 if (n > 0)
1368 array_erase( &ws->slots, &ws->slots[l-n], &ws->slots[l] );
1369
1370 /* See if we must overwrite levels. */
1371 if ((ws->type == WEAPSET_TYPE_WEAPON) ||
1372 (ws->type == WEAPSET_TYPE_ACTIVE))
1373 for (i=0; i<array_size(ws->slots); i++)
1374 ws->slots[i].level = 0;
1375 }
1376
1377 /* Update range. */
1378 pilot_weapSetUpdateRange( ws );
1379 }
1380
1381 /**
1382 * @brief Disables a given active outfit.
1383 *
1384 * @param p Pilot whose outfit we are disabling.
1385 * @return Whether the outfit was actually disabled.
1386 */
pilot_outfitOff(Pilot * p,PilotOutfitSlot * o)1387 int pilot_outfitOff( Pilot *p, PilotOutfitSlot *o )
1388 {
1389 /* Must not be disabled or cooling down. */
1390 if ((pilot_isDisabled(p)) || (pilot_isFlag(p, PILOT_COOLDOWN)))
1391 return 0;
1392
1393 if (outfit_isAfterburner( o->outfit )) /* Afterburners */
1394 pilot_afterburnOver( p );
1395 else if (outfit_isBeam( o->outfit )) {
1396 /* Beams use stimer to represent minimum time until shutdown. */
1397 o->stimer = -1;
1398 }
1399 else {
1400 o->stimer = outfit_cooldown( o->outfit );
1401 o->state = PILOT_OUTFIT_COOLDOWN;
1402 }
1403
1404 return 1;
1405 }
1406
1407 /**
1408 * @brief Disables all active outfits for a pilot.
1409 *
1410 * @param p Pilot whose outfits we are disabling.
1411 * @return Whether any outfits were actually disabled.
1412 */
pilot_outfitOffAll(Pilot * p)1413 int pilot_outfitOffAll( Pilot *p )
1414 {
1415 PilotOutfitSlot *o;
1416 int nchg;
1417 int i;
1418
1419 nchg = 0;
1420 for (i=0; i<p->noutfits; i++) {
1421 o = p->outfits[i];
1422 /* Picky about our outfits. */
1423 if (o->outfit == NULL)
1424 continue;
1425 if (!o->active)
1426 continue;
1427 if (o->state == PILOT_OUTFIT_ON)
1428 nchg += pilot_outfitOff( p, o );
1429 }
1430 return (nchg > 0);
1431 }
1432
1433 /**
1434 * @brief Activate the afterburner.
1435 */
pilot_afterburn(Pilot * p)1436 void pilot_afterburn (Pilot *p)
1437 {
1438 double afb_mod;
1439
1440 if (p == NULL)
1441 return;
1442
1443 if (pilot_isFlag(p, PILOT_HYP_PREP) || pilot_isFlag(p, PILOT_HYPERSPACE) ||
1444 pilot_isFlag(p, PILOT_LANDING) || pilot_isFlag(p, PILOT_TAKEOFF) ||
1445 pilot_isDisabled(p) || pilot_isFlag(p, PILOT_COOLDOWN))
1446 return;
1447
1448 /* Not under manual control if is player. */
1449 if (pilot_isFlag( p, PILOT_MANUAL_CONTROL ) && pilot_isFlag( p, PILOT_PLAYER ))
1450 return;
1451
1452 /** @todo fancy effect? */
1453 if (p->afterburner == NULL)
1454 return;
1455
1456 /* The afterburner only works if its efficiency is high enough. */
1457 if (pilot_heatEfficiencyMod( p->afterburner->heat_T,
1458 p->afterburner->outfit->u.afb.heat_base,
1459 p->afterburner->outfit->u.afb.heat_cap ) < 0.3)
1460 return;
1461
1462 if (p->afterburner->state == PILOT_OUTFIT_OFF) {
1463 p->afterburner->state = PILOT_OUTFIT_ON;
1464 p->afterburner->stimer = outfit_duration( p->afterburner->outfit );
1465 pilot_setFlag(p,PILOT_AFTERBURNER);
1466 pilot_calcStats( p );
1467
1468 /* @todo Make this part of a more dynamic activated outfit sound system. */
1469 sound_playPos(p->afterburner->outfit->u.afb.sound_on,
1470 p->solid->pos.x, p->solid->pos.y, p->solid->vel.x, p->solid->vel.y);
1471 }
1472
1473 if (pilot_isPlayer(p)) {
1474 afb_mod = MIN( 1., player.p->afterburner->outfit->u.afb.mass_limit / player.p->solid->mass );
1475 spfx_shake( afb_mod * player.p->afterburner->outfit->u.afb.rumble * SHAKE_MAX );
1476 }
1477 }
1478
1479
1480 /**
1481 * @brief Deactivates the afterburner.
1482 */
pilot_afterburnOver(Pilot * p)1483 void pilot_afterburnOver (Pilot *p)
1484 {
1485 if (p == NULL)
1486 return;
1487 if (p->afterburner == NULL)
1488 return;
1489
1490 if (p->afterburner->state == PILOT_OUTFIT_ON) {
1491 p->afterburner->state = PILOT_OUTFIT_OFF;
1492 pilot_rmFlag(p,PILOT_AFTERBURNER);
1493 pilot_calcStats( p );
1494
1495 /* @todo Make this part of a more dynamic activated outfit sound system. */
1496 sound_playPos(p->afterburner->outfit->u.afb.sound_off,
1497 p->solid->pos.x, p->solid->pos.y, p->solid->vel.x, p->solid->vel.y);
1498 }
1499 }
1500