naev 0.11.5
pilot_weapon.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
27#include "naev.h"
30#include "array.h"
31#include "escort.h"
32#include "log.h"
33#include "nxml.h"
34#include "nlua_pilot.h"
35#include "nlua_pilotoutfit.h"
36#include "pilot.h"
37#include "player.h"
38#include "spfx.h"
39#include "weapon.h"
40
41/*
42 * Prototypes.
43 */
45static int pilot_weapSetFire( Pilot *p, PilotWeaponSet *ws, int level );
46static int pilot_shootWeaponSetOutfit( Pilot* p, PilotWeaponSet *ws, const Outfit *o, int level, const Target *target, double time, int aim );
47static void pilot_weapSetUpdateRange( const Pilot *p, PilotWeaponSet *ws );
48unsigned int pilot_weaponSetShootStop( Pilot* p, PilotWeaponSet *ws, int level );
49
58{
59 return &p->weapon_sets[ id ];
60}
61
70static int pilot_weapSetFire( Pilot *p, PilotWeaponSet *ws, int level )
71{
72 int ret = 0;
73 int isstealth = pilot_isFlag( p, PILOT_STEALTH );
74 int target_set = 0;
75 double time;
76 Target wt;
77
78 for (int i=0; i<array_size(ws->slots); i++) {
79 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
80 const Outfit *o = pos->outfit;
81
82 /* Ignore NULL outfits. */
83 if (o == NULL)
84 continue;
85
86 /* Only "active" outfits. */
87 if ((level != -1) && (ws->slots[i].level != level))
88 continue;
89
90 /* Only weapons. */
91 if (!outfit_isWeapon(o))
92 continue;
93
94 /* Only run once for each weapon type in the group if not volley. */
95 if (!ws->volley) {
96 int s = 0;
97 for (int j=0; j<i; j++) {
98 /* Only active outfits. */
99 if ((level != -1) && (ws->slots[j].level != level))
100 continue;
101 /* Found a match. */
102 if (p->outfits[ ws->slots[j].slotid ]->outfit == o) {
103 s = 1;
104 break;
105 }
106 }
107 if (s!=0)
108 continue;
109 }
110
111 /* Only "locked on" outfits. */
112 if (outfit_isSeeker(o) && (pos->u.ammo.lockon_timer > 0.))
113 continue;
114
115 /* Lazy target setting. */
116 if (!target_set) {
117 pilot_weaponTarget( p, &wt );
118 target_set = 1;
119 }
120 time = weapon_targetFlyTime( o, p, &wt );
121
122 /* Only "inrange" outfits.
123 * XXX for simplicity we are using pilot position / velocity instead of mount point, which might be a bit off. */
124 if (ws->inrange && !outfit_isFighterBay(o) && ((outfit_duration(o) * p->stats.launch_range < time) ||
125 (!weapon_inArc( o, p, &wt, &p->solid.pos, &p->solid.vel, p->solid.dir, time))))
126 continue;
127
128 /* Shoot the weapon of the weaponset. */
129 if (ws->volley)
130 ret += pilot_shootWeapon( p, pos, &wt, time, !ws->manual );
131 else
132 ret += pilot_shootWeaponSetOutfit( p, ws, o, level, &wt, time, !ws->manual );
133 }
134
135 /* Destealth when attacking. */
136 if (isstealth && (ret>0))
137 pilot_destealth( p );
138
139 /* Trigger onshoot after stealth gets broken. */
140 if (ret > 0)
142
143 return ret;
144}
145
150{
151 for (int i=0; i<PILOT_WEAPON_SETS; i++) {
152 PilotWeaponSet *ws = &p->weapon_sets[i];
153 ws->active = 0;
154 }
155}
156
164void pilot_weapSetPress( Pilot* p, int id, int type )
165{
166 int l, on, n;
167 int isstealth = pilot_isFlag( p, PILOT_STEALTH );
168 PilotWeaponSet *ws = pilot_weapSet(p,id);
169 /* Case no outfits. */
170 if (ws->slots == NULL)
171 return;
172
173 /* Handle fire groups. */
174 switch (ws->type) {
175 case WEAPSET_TYPE_SWITCH:
176 /* On press just change active weapon set to whatever is available. */
177 if ((type > 0) && (array_size(ws->slots)>0)) {
178 p->active_set = id;
180 }
181 break;
182
183 case WEAPSET_TYPE_TOGGLE:
184 /* The behaviour here is more complex. What we do is consider a group
185 * to be entirely off if not all outfits are either on or cooling down.
186 * In the case it's deemed to be off, all outfits that are off get turned
187 * on, otherwise all outfits that are on are turrned to cooling down. */
188 /* Only care about presses. */
189 if (type < 0)
190 break;
191
192 /* Must not be disabled or cooling down. */
193 if ((pilot_isDisabled(p)) || (pilot_isFlag(p, PILOT_COOLDOWN)))
194 return;
195
196 /* Decide what to do. */
197 on = ws->active;
198
199 /* Clear change variables. */
200 n = 0;
201 pilotoutfit_modified = 0;
202 l = array_size(ws->slots);
203
204 /* Turn them off. */
205 if (on) {
206 ws->active = 0;
207
208 /* Weapons are weird still. */
209 n += pilot_weaponSetShootStop( p, ws, -1 );
210
211 for (int i=0; i<l; i++) {
212 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
213 if (pos->state != PILOT_OUTFIT_ON)
214 continue;
215
216 n += pilot_outfitOff( p, pos );
217 }
218 }
219 /* Turn them on. */
220 else {
221 ws->active = 1;
222
223 /* Weapons are weird still :/ */
224 n += pilot_weapSetFire( p, ws, -1 );
225
226 for (int i=0; i<l; i++) {
227 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
228 if (pos->state != PILOT_OUTFIT_OFF)
229 continue;
230
231 n += pilot_outfitOn( p, pos );
232 }
233 }
234 /* Must recalculate stats. */
235 if ((n > 0) || pilotoutfit_modified) {
236 /* pilot_destealth should run calcStats already. */
237 if (isstealth && (type>0))
238 pilot_destealth( p );
239 else
240 pilot_calcStats( p );
241 }
242 break;
243
244 case WEAPSET_TYPE_HOLD:
245 /* Activation philosophy here is to turn on while pressed and off
246 * when it's not held anymore. */
247
248 /* Must not be disabled or cooling down. */
249 if ((pilot_isDisabled(p)) || (pilot_isFlag(p, PILOT_COOLDOWN)))
250 return;
251
252 /* Clear change variables. */
253 l = array_size(ws->slots);
254 n = 0;
255 pilotoutfit_modified = 0;
256
257 if (type > 0) {
258 ws->active = 1;
259
260 /* Weapons are weird still :/ */
261 n += pilot_weapSetFire( p, ws, -1 );
262
263 /* Turn on outfits. */
264 for (int i=0; i<l; i++) {
265 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
266 if (pos->state != PILOT_OUTFIT_OFF)
267 continue;
268
269 n += pilot_outfitOn( p, pos );
270 }
271 }
272 else if (type < 0) {
273 ws->active = 0;
274 n += pilot_weaponSetShootStop( p, ws, -1 ); /* De-activate weapon set. */
275
276 /* Turn off outfits. */
277 for (int i=0; i<l; i++) {
278 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
279 if (pos->state != PILOT_OUTFIT_ON)
280 continue;
281
282 n += pilot_outfitOff( p, pos );
283 }
284 }
285
286 /* Must recalculate stats. */
287 if ((n > 0) || pilotoutfit_modified) {
288 /* pilot_destealth should run calcStats already. */
289 if (isstealth && (type>0))
290 pilot_destealth( p );
291 else
292 pilot_calcStats( p );
293 }
294 break;
295 }
296}
297
304{
305 if (pilot_isFlag( p, PILOT_HYP_BEGIN))
306 return;
307
308 for (int i=0; i<PILOT_WEAPON_SETS; i++) {
309 int n;
310 PilotWeaponSet *ws = &p->weapon_sets[i];
311 if (ws->slots == NULL)
312 continue;
313
314 /* Only care if on. */
315 if (!ws->active)
316 continue;
317
318 /* Weapons must get "fired" every turn. */
319 pilot_weapSetFire( p, ws, -1 );
320
321 /* Keep on toggling on. */
322 n = 0;
323 for (int j=0; j<array_size(ws->slots); j++) {
324 PilotOutfitSlot *pos = p->outfits[ ws->slots[j].slotid ];
325 if (pos->state != PILOT_OUTFIT_OFF)
326 continue;
327
328 n += pilot_outfitOn( p, pos );
329 }
330 if ((n > 0) || pilotoutfit_modified) {
331 /* pilot_destealth should run calcStats already. */
332 if (pilot_isFlag(p,PILOT_STEALTH))
333 pilot_destealth( p );
334 else
335 pilot_calcStats( p );
336 }
337 }
338}
339
344{
345 /* Make sure we have a valid active switched set. */
346 const PilotWeaponSet *wsa = pilot_weapSet( p, p->active_set );
347 if (wsa->type != WEAPSET_TYPE_SWITCH) {
348 for (int i=0; i<PILOT_WEAPON_SETS; i++) {
349 const PilotWeaponSet *wsi = pilot_weapSet( p, i );
350 if (wsi->type == WEAPSET_TYPE_SWITCH) {
351 p->active_set = i;
352 wsa = pilot_weapSet( p, p->active_set );
353 break;
354 }
355 }
356 }
357
358 /* Have to update slots potentially. */
359 for (int i=0; i<array_size(p->outfits); i++) {
360 PilotOutfitSlot *o = p->outfits[i];
361 o->weapset = -1;
362 for (int j=0; j<PILOT_WEAPON_SETS; j++) {
363 if (pilot_weapSetCheck(p, j, o) != -1) {
364 o->weapset = j;
365 break;
366 }
367 }
368 }
369
370 /* Update range. */
372
373 /* Just update levels of active weapon set. */
374 for (int i=0; i<array_size(p->outfits); i++)
375 p->outfits[i]->level = -1;
376 for (int i=0; i<array_size(wsa->slots); i++)
377 p->outfits[ wsa->slots[i].slotid ]->level = wsa->slots[i].level;
378}
379
388{
389 const PilotWeaponSet *ws = pilot_weapSet(p,id);
390 return ws->type;
391}
392
400void pilot_weapSetType( Pilot* p, int id, WeaponSetType type )
401{
402 PilotWeaponSet *ws = pilot_weapSet(p,id);
403 ws->type = type;
404 ws->active = 0; /* Disable no matter what. */
406}
407
416{
417 const PilotWeaponSet *ws = pilot_weapSet(p,id);
418 return ws->inrange;
419}
420
428void pilot_weapSetInrange( Pilot* p, int id, int inrange )
429{
430 PilotWeaponSet *ws = pilot_weapSet(p,id);
431 ws->inrange = inrange;
432}
433
442{
443 const PilotWeaponSet *ws = pilot_weapSet(p,id);
444 return ws->manual;
445}
446
454void pilot_weapSetManual( Pilot* p, int id, int manual )
455{
456 PilotWeaponSet *ws = pilot_weapSet(p,id);
457 ws->manual = manual;
458}
459
468{
469 const PilotWeaponSet *ws = pilot_weapSet(p,id);
470 return ws->volley;
471}
472
480void pilot_weapSetVolley( Pilot* p, int id, int volley )
481{
482 PilotWeaponSet *ws = pilot_weapSet(p,id);
483 ws->volley = volley;
484}
485
489const char *pilot_weapSetName( Pilot* p, int id )
490{
491 static char setname[STRMAX_SHORT];
492 const char *base, *type, *problem;
493 PilotWeaponSet *ws = pilot_weapSet(p,id);
494 problem = type = base = NULL;
495
496 switch (ws->type) {
497 case WEAPSET_TYPE_SWITCH:
498 type = p_("weapset","Switch");
499 break;
500 case WEAPSET_TYPE_TOGGLE:
501 type = p_("weapset","Toggle");
502 break;
503 case WEAPSET_TYPE_HOLD:
504 type = p_("weapset","Hold");
505 break;
506 default:
507 type = p_("weapset","Unknown");
508 break;
509 }
510
511 if (array_size(ws->slots)==0)
512 base = _("Empty");
513 else {
514 const Outfit *o = NULL;
515 int not_same = 0;
516 int has_weap = 0;
517 int has_util = 0;
518 int has_stru = 0;
519 for (int i=0; i<array_size(ws->slots); i++) {
520 const PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
521 if (pos->outfit==NULL) /* Ignore empty slots. */
522 continue;
523 if (!pilot_slotIsToggleable(pos)) /* Ignore non-active. */
524 continue;
525 if (o==NULL)
526 o = pos->outfit;
527 else if(o!=pos->outfit)
528 not_same = 1;
529 switch (pos->sslot->slot.type) {
530 case OUTFIT_SLOT_STRUCTURE:
531 has_stru++;
532 break;
533 case OUTFIT_SLOT_UTILITY:
534 has_util++;
535 break;
536 case OUTFIT_SLOT_WEAPON:
537 has_weap++;
538 break;
539 default:
540 break;
541 }
542 }
543 if (o==NULL)
544 base = _("Empty");
545 else if (not_same==0)
546 base = _(o->name);
547 else if (has_weap && !has_util && !has_stru)
548 base = p_("weapset","Weapons");
549 else if (!has_weap && has_util && !has_stru)
550 base = p_("weapset","Utilities");
551 else if (!has_weap && !has_util && has_stru)
552 base = p_("weapset","Structurals");
553 else
554 base = p_("weapset","Mixed");
555
556 /* Try to proactively detect issues with the weapon set. */
557 if (!has_weap && (ws->type==WEAPSET_TYPE_SWITCH))
558 problem = _("no weapons!");
559 }
560
561 if (problem!=NULL)
562 snprintf( setname, sizeof(setname), p_("weapset", "#o%s - %s#0 [#r%s#0]"), type, base, problem );
563 else
564 snprintf( setname, sizeof(setname), p_("weapset", "%s - %s"), type, base );
565 return setname;
566}
567
576void pilot_weapSetAdd( Pilot* p, int id, const PilotOutfitSlot *o, int level )
577{
579 PilotWeaponSet *ws = pilot_weapSet(p,id);
580
581 /* Create if needed. */
582 if (ws->slots == NULL)
584
585 /* Check if already there. */
586 for (int i=0; i<array_size(ws->slots); i++) {
587 if (ws->slots[i].slotid != o->id)
588 continue;
589 ws->slots[i].level = level;
591 return;
592 }
593
594 /* Add it. */
595 slot = &array_grow( &ws->slots );
596 slot->level = level;
597 slot->slotid= o->id;
598 if (o->outfit!=NULL)
599 slot->range2 = pow2(pilot_outfitRange( p, o->outfit ));
600 else
601 slot->range2 = 0.;
603}
604
612void pilot_weapSetRm( Pilot* p, int id, const PilotOutfitSlot *o )
613{
614 PilotWeaponSet *ws = pilot_weapSet(p,id);
615 for (int i=0; i<array_size(ws->slots); i++) {
616 if (ws->slots[i].slotid != o->id)
617 continue;
618
619 array_erase( &ws->slots, &ws->slots[i], &ws->slots[i+1] );
621 return;
622 }
623}
624
631void pilot_weapSetClear( Pilot* p, int id )
632{
633 PilotWeaponSet *ws = pilot_weapSet(p,id);
634 ws->type = WEAPSET_TYPE_SWITCH;
635 array_free( ws->slots );
636 ws->slots = NULL;
637
638 /* Update if needed. */
640}
641
645int pilot_weapSetInSet( Pilot* p, int id, const PilotOutfitSlot *o )
646{
647 const PilotWeaponSet *ws = pilot_weapSet(p,id);
648 for (int i=0; i<array_size(ws->slots); i++) {
649 /* Must match the current weapon. */
650 if (ws->slots[i].slotid != o->id)
651 continue;
652 return ws->slots[i].level;
653 }
654 /* Not found. */
655 return -1;
656}
657
666int pilot_weapSetCheck( Pilot* p, int id, const PilotOutfitSlot *o )
667{
668 const PilotWeaponSet *ws = pilot_weapSet(p,id);
669 for (int i=0; i<array_size(ws->slots); i++) {
670 /* Must match the current weapon. */
671 if (ws->slots[i].slotid != o->id)
672 continue;
673 /* Only weapons can be used as switch sets. */
674 if ((o->outfit!= NULL) && !outfit_isWeapon(o->outfit) && (ws->type==WEAPSET_TYPE_SWITCH))
675 continue;
676 return ws->slots[i].level;
677 }
678 /* Not found. */
679 return -1;
680}
681
688{
689 for (int i=0; i<PILOT_WEAPON_SETS; i++)
690 pilot_weapSetUpdateRange( p, &p->weapon_sets[i] );
691}
692
700{
701 int lev;
702 double range, speed;
703 double range_accum[PILOT_WEAPSET_MAX_LEVELS];
704 int range_num[PILOT_WEAPSET_MAX_LEVELS];
705 double speed_accum[PILOT_WEAPSET_MAX_LEVELS];
706 int speed_num[PILOT_WEAPSET_MAX_LEVELS];
707
708 /* Calculate ranges. */
709 for (int i=0; i<PILOT_WEAPSET_MAX_LEVELS; i++) {
710 range_accum[i] = 0.;
711 range_num[i] = 0;
712 speed_accum[i] = 0.;
713 speed_num[i] = 0;
714 }
715 for (int i=0; i<array_size(ws->slots); i++) {
716 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
717 if (pos->outfit == NULL)
718 continue;
719
720 /* Get level. */
721 lev = ws->slots[i].level;
722 if (lev >= PILOT_WEAPSET_MAX_LEVELS)
723 continue;
724
725 /* Empty Launchers aren't valid */
726 if (outfit_isLauncher(pos->outfit) && (pos->u.ammo.quantity <= 0))
727 continue;
728
729 /* Get range. */
730 range = pilot_outfitRange( p, pos->outfit );
731 if (range >= 0.) {
732 /* Calculate. */
733 range_accum[ lev ] += range;
734 range_num[ lev ]++;
735 }
736
737 /* Get speed. */
738 speed = outfit_speed(pos->outfit);
739 if (speed >= 0.) {
740 /* Calculate. */
741 speed_accum[ lev ] += speed;
742 speed_num[ lev ]++;
743 }
744 }
745
746 /* Postprocess. */
747 for (int i=0; i<PILOT_WEAPSET_MAX_LEVELS; i++) {
748 /* Postprocess range. */
749 if (range_num[i] == 0)
750 ws->range[i] = 0;
751 else
752 ws->range[i] = range_accum[i] / (double) range_num[i];
753
754 /* Postprocess speed. */
755 if (speed_num[i] == 0)
756 ws->speed[i] = 0;
757 else
758 ws->speed[i] = speed_accum[i] / (double) speed_num[i];
759 }
760}
761
769double pilot_weapSetRange( Pilot* p, int id, int level )
770{
771 double range;
772 PilotWeaponSet *ws = pilot_weapSet(p,id);
773 if (level < 0) {
774 range = 0.;
775 for (int i=0; i<PILOT_WEAPSET_MAX_LEVELS; i++)
776 range += ws->range[i];
777 }
778 else
779 range = ws->range[ level ];
780
781 return range;
782}
783
791double pilot_weapSetSpeed( Pilot* p, int id, int level )
792{
793 double speed;
794 PilotWeaponSet *ws = pilot_weapSet(p,id);
795 if (level < 0) {
796 speed = 0.;
797 for (int i=0; i<PILOT_WEAPSET_MAX_LEVELS; i++)
798 speed += ws->speed[i];
799 }
800 else
801 speed = ws->speed[ level ];
802
803 return speed;
804}
805
813double pilot_weapSetAmmo( Pilot* p, int id, int level )
814{
815 PilotWeaponSet *ws = pilot_weapSet(p,id);
816 double ammo = 0.;
817 int nammo = 0;
818 for (int i=0; i<array_size(ws->slots); i++) {
819 int amount;
820 PilotOutfitSlot *s = p->outfits[ ws->slots[i].slotid ];
821 if ((level >= 0) && (ws->slots[i].level != level))
822 continue;
823 amount = pilot_maxAmmoO( p, s->outfit );
824 if (amount > 0) {
825 ammo += (double)s->u.ammo.quantity / (double)amount;
826 nammo++;
827 }
828 }
829 return (nammo==0) ? 0. : ammo / (double)nammo;
830}
831
838void pilot_weapSetCleanup( Pilot* p, int id )
839{
840 PilotWeaponSet *ws = pilot_weapSet(p,id);
841
842 array_free( ws->slots );
843 ws->slots = NULL;
844
845 /* Update range. */
847}
848
853{
854 for (int i=0; i<PILOT_WEAPON_SETS; i++)
855 pilot_weapSetCleanup( p, i );
856}
857
866{
867 return pilot_weapSet( p, id )->slots;
868}
869
877int pilot_shoot( Pilot* p, int level )
878{
879 PilotWeaponSet *ws = pilot_weapSet( p, p->active_set );
880
881 /* Fire weapons. */
882 if (ws->type == WEAPSET_TYPE_SWITCH) { /* Must be a change set or a weaponset. */
883 int ret = pilot_weapSetFire( p, ws, level );
884
885 /* Firing weapons aborts active cooldown. */
886 if (pilot_isFlag(p, PILOT_COOLDOWN) && ret)
887 pilot_cooldownEnd(p, NULL);
888
889 return ret;
890 }
891
892 return 0;
893}
894
903void pilot_shootStop( Pilot* p, int level )
904{
905 PilotWeaponSet *ws = pilot_weapSet( p, p->active_set );
906
907 /* Stop and see if must recalculate. */
908 if (pilot_weaponSetShootStop( p, ws, level ))
909 pilot_calcStats( p );
910}
911
922unsigned int pilot_weaponSetShootStop( Pilot* p, PilotWeaponSet *ws, int level )
923{
924 /* Stop all beams. */
925 int recalc = 0;
926 for (int i=0; i<array_size(ws->slots); i++) {
927 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
928
929 /* Must have associated outfit. */
930 if (pos->outfit == NULL)
931 continue;
932
933 /* Must match level. */
934 if ((level != -1) && (ws->slots[i].level != level))
935 continue;
936
937 /* Only handle beams. */
938 if (!outfit_isBeam(pos->outfit))
939 continue;
940
941 /* Stop beam. */
942 if (pos->u.beamid > 0) {
943 /* Enforce minimum duration if set. */
944 if (pos->outfit->u.bem.min_duration > 0.) {
945
946 pos->stimer = pos->outfit->u.bem.min_duration -
947 (pos->outfit->u.bem.duration - pos->timer);
948
949 if (pos->stimer > 0.)
950 continue;
951 }
952
953 beam_end( pos->u.beamid );
954 pilot_stopBeam(p, pos);
955 }
956 }
957
958 return recalc;
959}
960
968{
969 double rate_mod, energy_mod, used;
970
971 /* There's nothing to do if the beam isn't active. */
972 if (w->u.beamid == 0)
973 return;
974
975 /* Safeguard against a nasty race condition. */
976 if (w->outfit == NULL) {
977 w->u.beamid = 0;
978 return;
979 }
980
981 /* Lua test to stop beam. */
982 /*
983 if ((w->outfit->lua_onshoot!= LUA_NOREF) &&
984 !pilot_outfitLOnshoot( p, w, 0 ))
985 return;
986 */
987
988 /* Calculate rate modifier. */
989 pilot_getRateMod( &rate_mod, &energy_mod, p, w->outfit );
990
991 /* Beam duration used. Compensate for the fact it's duration might have
992 * been shortened by heat. */
993 used = w->outfit->u.bem.duration - w->timer*(1.-pilot_heatAccuracyMod(w->heat_T));
994
995 w->timer = rate_mod * (used / w->outfit->u.bem.duration) * outfit_delay( w->outfit );
996 w->u.beamid = 0;
997 w->state = PILOT_OUTFIT_OFF;
998}
999
1008double pilot_weapFlyTime( const Outfit *o, const Pilot *parent, const vec2 *pos, const vec2 *vel )
1009{
1010 vec2 approach_vector, relative_location, orthoradial_vector;
1011 double speed, radial_speed, orthoradial_speed, dist, t;
1012
1013 dist = vec2_dist( &parent->solid.pos, pos );
1014
1015 /* Beam weapons */
1016 if (outfit_isBeam(o)) {
1017 if (dist > o->u.bem.range)
1018 return INFINITY;
1019 return 0.;
1020 }
1021
1022 /* A bay doesn't have range issues */
1023 if (outfit_isFighterBay(o))
1024 return 0.;
1025
1026 /* Missiles use absolute velocity while bolts and unguided rockets use relative vel */
1027 if (outfit_isLauncher(o) && o->u.lau.ai != AMMO_AI_UNGUIDED)
1028 vec2_cset( &approach_vector, - vel->x, - vel->y );
1029 else
1030 vec2_cset( &approach_vector, VX(parent->solid.vel) - vel->x,
1031 VY(parent->solid.vel) - vel->y );
1032
1033 speed = outfit_speed(o);
1034
1035 /* Get the vector : shooter -> target */
1036 vec2_cset( &relative_location, pos->x - VX(parent->solid.pos),
1037 pos->y - VY(parent->solid.pos) );
1038
1039 /* Get the orthogonal vector */
1040 vec2_cset(&orthoradial_vector, VY(parent->solid.pos) - pos->y,
1041 pos->x - VX(parent->solid.pos) );
1042
1043 radial_speed = vec2_dot( &approach_vector, &relative_location );
1044 radial_speed = radial_speed / VMOD(relative_location);
1045
1046 orthoradial_speed = vec2_dot(&approach_vector, &orthoradial_vector);
1047 orthoradial_speed = orthoradial_speed / VMOD(relative_location);
1048
1049 if ( ((speed*speed - VMOD(approach_vector)*VMOD(approach_vector)) != 0) && (speed*speed - orthoradial_speed*orthoradial_speed) > 0)
1050 t = dist * (sqrt( speed*speed - orthoradial_speed*orthoradial_speed ) - radial_speed) /
1051 (speed*speed - VMOD(approach_vector)*VMOD(approach_vector));
1052 else
1053 return INFINITY;
1054
1055 /* if t < 0, try the other solution */
1056 if (t < 0)
1057 t = - dist * (sqrt( speed*speed - orthoradial_speed*orthoradial_speed ) + radial_speed) /
1058 (speed*speed - VMOD(approach_vector)*VMOD(approach_vector));
1059
1060 /* if t still < 0, no solution */
1061 if (t < 0)
1062 return INFINITY;
1063
1064 return t;
1065}
1066
1075{
1076 Pilot *pt = pilot_getTarget( p );
1077 if (pt != NULL) {
1078 wt->type = TARGET_PILOT;
1079 wt->u.id = pt->id;
1080 return pt;
1081 }
1082 else if (p->nav_asteroid != -1) {
1083 wt->type = TARGET_ASTEROID;
1084 wt->u.ast.anchor = p->nav_anchor;
1085 wt->u.ast.asteroid = p->nav_asteroid;
1086 return NULL;
1087 }
1088 wt->type = TARGET_NONE;
1089 return NULL;
1090}
1091
1095static int pilot_shootWeaponSetOutfit( Pilot* p, PilotWeaponSet *ws, const Outfit *o, int level, const Target *target, double time, int aim )
1096{
1097 int ret;
1098 int is_launcher, is_bay;
1099 double rate_mod, energy_mod;
1100 int maxp, minh;
1101 double q, maxt;
1102
1103 /* Store number of shots. */
1104 ret = 0;
1105
1107 if (outfit_isBeam(o)) {
1108 for (int i=0; i<array_size(ws->slots); i++) {
1109 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
1110 if (pos->outfit == o && (level == -1 || level == ws->slots[i].level)) {
1111 ret += pilot_shootWeapon( p, pos, target, 0., aim );
1112 pos->inrange = ws->inrange; /* State if the weapon has to be turn off when out of range. */
1113 }
1114 }
1115 return ret;
1116 }
1117
1118 /* Stores if it is a launcher or bay. */
1119 is_launcher = outfit_isLauncher(o);
1120 is_bay = outfit_isFighterBay(o);
1121
1122 /* Calculate rate modifier. */
1123 pilot_getRateMod( &rate_mod, &energy_mod, p, o );
1124
1125 /* Find optimal outfit, coolest that can fire. */
1126 minh = -1;
1127 maxt = 0.;
1128 maxp = -1;
1129 q = 0.;
1130 for (int i=0; i<array_size(ws->slots); i++) {
1131 const PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
1132
1133 /* Only matching outfits. */
1134 if (pos->outfit != o)
1135 continue;
1136
1137 /* Only match levels. */
1138 if ((level != -1) && (ws->slots[i].level != level))
1139 continue;
1140
1141 /* Launcher only counts with ammo. */
1142 if ((is_launcher || is_bay) && (pos->u.ammo.quantity <= 0))
1143 continue;
1144
1145 /* Get coolest that can fire. */
1146 if (pos->timer <= 0.) {
1147 if (is_launcher) {
1148 if ((minh < 0) || (p->outfits[ ws->slots[minh].slotid ]->u.ammo.quantity < pos->u.ammo.quantity))
1149 minh = i;
1150 }
1151 else {
1152 if ((minh < 0) || (p->outfits[ ws->slots[minh].slotid ]->heat_T > pos->heat_T))
1153 minh = i;
1154 }
1155 }
1156
1157 /* Save some stuff. */
1158 if ((maxp < 0) || (pos->timer > maxt)) {
1159 maxp = i;
1160 maxt = pos->timer;
1161 }
1162 q += 1.;
1163 }
1164
1165 /* No weapon can fire. */
1166 if (minh < 0)
1167 return 0;
1168
1169 /* Only fire if the last weapon to fire fired more than (q-1)/q ago. */
1170 if (maxt > rate_mod * outfit_delay(o) * ((q-1.) / q))
1171 return 0;
1172
1173 /* Shoot the weapon. */
1174 ret += pilot_shootWeapon( p, p->outfits[ ws->slots[minh].slotid ], target, time, aim );
1175
1176 return ret;
1177}
1178
1189int pilot_shootWeapon( Pilot *p, PilotOutfitSlot *w, const Target *target, double time, int aim )
1190{
1191 vec2 vp, vv;
1192 double rate_mod, energy_mod;
1193 double energy;
1194
1195 /* Make sure weapon has outfit. */
1196 if (w->outfit == NULL)
1197 return 0;
1198
1199 /* Reset beam shut-off if needed. */
1200 if (outfit_isBeam(w->outfit) && w->outfit->u.bem.min_duration)
1201 w->stimer = INFINITY;
1202
1203 /* check to see if weapon is ready */
1204 if (w->timer > 0.)
1205 return 0;
1206
1207 /* Calculate rate modifier. */
1208 pilot_getRateMod( &rate_mod, &energy_mod, p, w->outfit );
1209
1210 /* Get weapon mount position. */
1211 pilot_getMount( p, w, &vp );
1212
1213 /* Modify velocity to take into account the rotation. */
1214 vec2_cset( &vv, p->solid.vel.x - vp.y*p->solid.dir_vel,
1215 p->solid.vel.y + vp.x*p->solid.dir_vel );
1216
1217 /* Get absolute weapon mount position. */
1218 vp.x += p->solid.pos.x;
1219 vp.y += p->solid.pos.y;
1220
1221 /* Regular bolt weapons. */
1222 if (outfit_isBolt(w->outfit)) {
1223 /* enough energy? */
1224 if (outfit_energy(w->outfit)*energy_mod > p->energy)
1225 return 0;
1226
1227 /* Lua test. */
1228 if ((aim >= 0) && (w->outfit->lua_onshoot != LUA_NOREF) &&
1229 !pilot_outfitLOnshoot( p, w ))
1230 return 0;
1231
1232 energy = outfit_energy(w->outfit)*energy_mod;
1233 p->energy -= energy;
1234 pilot_heatAddSlot( p, w );
1235 if (!outfit_isProp( w->outfit, OUTFIT_PROP_SHOOT_DRY )) {
1236 for (int i=0; i<w->outfit->u.blt.shots; i++) {
1237 weapon_add( w, NULL, p->solid.dir,
1238 &vp, &vv, p, target, time, aim );
1239 }
1240 }
1241 }
1242
1243 /*
1244 * Beam weapons.
1245 */
1246 else if (outfit_isBeam(w->outfit)) {
1247 /* Don't fire if the existing beam hasn't been destroyed yet. */
1248 if (w->u.beamid > 0)
1249 return 0;
1250
1251 /* Check if enough energy to last a second. */
1252 if (outfit_energy(w->outfit)*energy_mod > p->energy)
1253 return 0;
1254
1255 /* Lua test. */
1256 if ((aim>=0) && (w->outfit->lua_onshoot != LUA_NOREF) &&
1257 !pilot_outfitLOnshoot( p, w ))
1258 return 0;
1259
1261 w->state = PILOT_OUTFIT_ON;
1262 if (!outfit_isProp( w->outfit, OUTFIT_PROP_SHOOT_DRY )) {
1263 w->u.beamid = beam_start( w, p->solid.dir,
1264 &vp, &p->solid.vel, p, target, aim );
1265 }
1266
1267 w->timer = w->outfit->u.bem.duration;
1268
1269 return 1; /* Return early due to custom timer logic. */
1270 }
1271
1272 /*
1273 * Missile launchers
1274 */
1275 else if (outfit_isLauncher(w->outfit)) {
1276 Target wt;
1277 /* Must have ammo left. */
1278 if (w->u.ammo.quantity <= 0)
1279 return 0;
1280
1281 /* enough energy? */
1282 if (outfit_energy(w->outfit)*energy_mod > p->energy)
1283 return 0;
1284
1285 /* Shooter can't be the target - safety check for the player.p */
1286 if (target == NULL) {
1287 pilot_weaponTarget( p, &wt );
1288 target = &wt;
1289 }
1290 if ((w->outfit->u.lau.ai != AMMO_AI_UNGUIDED) && (target->type!=TARGET_PILOT))
1291 return 0;
1292
1293 /* Lua test. */
1294 if ((aim>=0) && (w->outfit->lua_onshoot != LUA_NOREF) &&
1295 !pilot_outfitLOnshoot( p, w ))
1296 return 0;
1297
1298 energy = outfit_energy(w->outfit)*energy_mod;
1299 p->energy -= energy;
1300 pilot_heatAddSlot( p, w );
1301 if (!outfit_isProp( w->outfit, OUTFIT_PROP_SHOOT_DRY )) {
1302 for (int i=0; i<w->outfit->u.lau.shots; i++)
1303 weapon_add( w, NULL, p->solid.dir,
1304 &vp, &vv, p, target, time, aim );
1305 }
1306
1307 pilot_rmAmmo( p, w, 1 );
1308
1309 /* Make the AI aware a seeker has been shot */
1310 if (outfit_isSeeker(w->outfit))
1311 p->shoot_indicator = 1;
1312
1313 /* If last ammo was shot, update the range */
1314 if (w->u.ammo.quantity <= 0) {
1315 for (int j=0; j<PILOT_WEAPON_SETS; j++)
1316 pilot_weapSetUpdateRange( p, &p->weapon_sets[j] );
1317 }
1318 }
1319
1320 /*
1321 * Fighter bays.
1322 */
1323 else if (outfit_isFighterBay(w->outfit)) {
1324 int dockslot = -1;
1325
1326 /* Must have ammo left. */
1327 if (w->u.ammo.quantity <= 0)
1328 return 0;
1329
1330 /* Lua test. */
1331 if ((aim>=0) && (w->outfit->lua_onshoot != LUA_NOREF) &&
1332 !pilot_outfitLOnshoot( p, w ))
1333 return 0;
1334
1335 /* Get index of outfit slot */
1336 for (int j=0; j<array_size(p->outfits); j++) {
1337 if (p->outfits[j] == w)
1338 dockslot = j;
1339 }
1340
1341 /* Create the escort. */
1342 if (!outfit_isProp( w->outfit, OUTFIT_PROP_SHOOT_DRY ))
1343 escort_create( p, w->outfit->u.bay.ship,
1344 &vp, &p->solid.vel, p->solid.dir, ESCORT_TYPE_BAY, 1, dockslot );
1345
1346 w->u.ammo.quantity -= 1; /* we just shot it */
1347 p->mass_outfit -= w->outfit->u.bay.ship_mass;
1348 pilot_updateMass( p );
1349 }
1350 else
1351 WARN(_("Shooting unknown weapon type: %s"), w->outfit->name);
1352
1353 /* Reset timer. */
1354 w->timer += rate_mod * outfit_delay( w->outfit );
1355
1356 return 1;
1357}
1358
1367void pilot_getRateMod( double *rate_mod, double* energy_mod,
1368 const Pilot* p, const Outfit *o )
1369{
1370 switch (o->type) {
1371 case OUTFIT_TYPE_BOLT:
1372 case OUTFIT_TYPE_BEAM:
1373 *rate_mod = 1. / p->stats.fwd_firerate; /* Invert. */
1374 *energy_mod = p->stats.fwd_energy;
1375 break;
1376 case OUTFIT_TYPE_TURRET_BOLT:
1377 case OUTFIT_TYPE_TURRET_BEAM:
1378 *rate_mod = 1. / p->stats.tur_firerate; /* Invert. */
1379 *energy_mod = p->stats.tur_energy;
1380 break;
1381
1382 case OUTFIT_TYPE_LAUNCHER:
1383 case OUTFIT_TYPE_TURRET_LAUNCHER:
1384 *rate_mod = 1. / p->stats.launch_rate;
1385 *energy_mod = 1.;
1386 break;
1387
1388 case OUTFIT_TYPE_FIGHTER_BAY:
1389 *rate_mod = 1. / p->stats.fbay_rate;
1390 *energy_mod = 1.;
1391 break;
1392
1393 default:
1394 *rate_mod = 1.;
1395 *energy_mod = 1.;
1396 break;
1397 }
1398}
1399
1406{
1407 for (int i=0; i<PILOT_WEAPON_SETS; i++) {
1408 PilotWeaponSet *ws = pilot_weapSet( p, i );
1409 array_erase( &ws->slots, array_begin(ws->slots), array_end(ws->slots) );
1410 }
1411}
1412
1426{
1427 int level, id;
1428
1429 /* Clear weapons. */
1430 pilot_weaponClear( p );
1431
1432 /* Set modes. */
1433 pilot_weapSetType( p, 0, WEAPSET_TYPE_SWITCH ); /* All weaps. */
1434 pilot_weapSetType( p, 1, WEAPSET_TYPE_SWITCH ); /* Forwards. */
1435 pilot_weapSetType( p, 2, WEAPSET_TYPE_SWITCH ); /* Turrets. */
1436 pilot_weapSetType( p, 3, WEAPSET_TYPE_SWITCH ); /* All weaps. */
1437 pilot_weapSetType( p, 4, WEAPSET_TYPE_HOLD ); /* Seekers. */
1438 pilot_weapSetType( p, 5, WEAPSET_TYPE_HOLD ); /* Fighter bays. */
1439 pilot_weapSetType( p, 6, WEAPSET_TYPE_HOLD );
1440 pilot_weapSetType( p, 7, WEAPSET_TYPE_HOLD );
1441 pilot_weapSetType( p, 8, WEAPSET_TYPE_HOLD );
1442 pilot_weapSetType( p, 9, WEAPSET_TYPE_HOLD ); /* Turret seekers. */
1443
1444 /* All should be inrange. */
1445 if (!pilot_isPlayer(p))
1446 for (int i=0; i<PILOT_WEAPON_SETS; i++) {
1447 pilot_weapSetInrange( p, i, 1 );
1448 /* Update range and speed (at 0)*/
1449 pilot_weapSetUpdateRange( p, &p->weapon_sets[i] );
1450 }
1451
1452 /* Iterate through all the outfits. */
1453 for (int i=0; i<array_size(p->outfits); i++) {
1454 PilotOutfitSlot *slot = p->outfits[i];
1455 const Outfit *o = slot->outfit;
1456
1457 /* Must be non-empty, and a weapon or active outfit. */
1458 if ((o == NULL) || !outfit_isActive(o)) {
1459 slot->level = -1; /* Clear level. */
1460 slot->weapset = -1;
1461 continue;
1462 }
1463
1464 /* Set level based on secondary flag. */
1465 level = outfit_isSecondary(o);
1466
1467 /* Manually defined group preempts others. */
1468 if (o->group) {
1469 id = o->group;
1470 }
1471 /* Bolts and beams. */
1472 else if (outfit_isBolt(o) || outfit_isBeam(o) ||
1473 (outfit_isLauncher(o) && !outfit_isSeeker(o))) {
1474 id = outfit_isTurret(o) ? 2 : 1;
1475 }
1476 /* Seekers. */
1477 else if (outfit_isLauncher(o) && outfit_isSeeker(o)) {
1478 id = 4;
1479 }
1480 /* Fighter bays. */
1481 else if (outfit_isFighterBay(o)) {
1482 id = 5;
1483 }
1484 /* Ignore rest. */
1485 else {
1486 slot->level = -1;
1487 continue;
1488 }
1489
1490 /* Add to its base group. */
1491 pilot_weapSetAdd( p, id, slot, level );
1492
1493 /* Also add another copy to another group. */
1494 if (id == 1) { /* Forward. */
1495 pilot_weapSetAdd( p, 0, slot, level ); /* Also get added to 'All'. */
1496 pilot_weapSetAdd( p, 3, slot, 0 ); /* Also get added to 'Fwd/Tur'. */
1497 }
1498 else if (id == 2) { /* Turrets. */
1499 pilot_weapSetAdd( p, 0, slot, level ); /* Also get added to 'All'. */
1500 pilot_weapSetAdd( p, 3, slot, 1 ); /* Also get added to 'Fwd/Tur'. */
1501 }
1502 else if (id == 4) { /* Seekers */
1503 pilot_weapSetAdd( p, 0, slot, level ); /* Also get added to 'All'. */
1504 if (outfit_isTurret(o))
1505 pilot_weapSetAdd( p, 9, slot, level ); /* Also get added to 'Turreted Seekers'. */
1506 }
1507 }
1508
1509 /* Update all outfits. */
1510 pilot_weaponSafe( p );
1511}
1512
1517{
1518 int i;
1519
1520 /* If current set isn't a fire group no need to worry. */
1521 if ((p->weapon_sets[ p->active_set ].type==WEAPSET_TYPE_SWITCH)
1522 && (array_size(p->weapon_sets[ p->active_set ].slots)>0)) {
1523 /* Update active weapon set. */
1524 pilot_weapSetUpdateOutfits( p, &p->weapon_sets[ p->active_set ] );
1525 return;
1526 }
1527
1528 /* Find first fire group. */
1529 for (i=0; i<PILOT_WEAPON_SETS; i++) {
1530 const PilotWeaponSet *ws = &p->weapon_sets[i];
1531 if ((ws->type==WEAPSET_TYPE_SWITCH) && (array_size(ws->slots)>0))
1532 break;
1533 }
1534
1535 /* Set active set to first if all fire groups or first non-fire group. */
1536 if (i >= PILOT_WEAPON_SETS) {
1537 /* Fallback to first switch group. */
1538 for (i=0; i<PILOT_WEAPON_SETS; i++) {
1539 const PilotWeaponSet *ws = &p->weapon_sets[i];
1540 if (ws->type==WEAPSET_TYPE_SWITCH)
1541 break;
1542 }
1543 p->active_set = (i>=PILOT_WEAPON_SETS) ? 0 : i;
1544 }
1545 else
1546 p->active_set = i;
1547
1548 /* Update active weapon set. */
1549 pilot_weapSetUpdateOutfits( p, &p->weapon_sets[ p->active_set ] );
1550}
1551
1558{
1559 for (int j=0; j<PILOT_WEAPON_SETS; j++) {
1560 PilotWeaponSet *ws = &p->weapon_sets[j];
1561
1562 /* Update range. */
1563 pilot_weapSetUpdateRange( p, ws );
1564 }
1565
1566 /* Update active weapon set. */
1567 pilot_weapSetUpdateOutfits( p, &p->weapon_sets[ p->active_set ] );
1568}
1569
1578{
1579 /* Must be equipped, not disabled, not cooling down. */
1580 if (o->outfit == NULL || (pilot_isDisabled(p)) || (pilot_isFlag(p, PILOT_COOLDOWN)))
1581 return 0;
1582
1583 if (outfit_isAfterburner( o->outfit )) { /* Afterburners */
1584 if ((o->outfit->lua_ontoggle != LUA_NOREF) &&
1585 !pilot_outfitLOntoggle( p, o, 0 ))
1586 return 0;
1588 }
1589 else if (outfit_isBeam( o->outfit )) {
1590 /*
1591 if ((o->outfit->lua_onshoot != LUA_NOREF) &&
1592 !pilot_outfitLOnshoot( p, o, 0 ))
1593 return 0;
1594 */
1595 /* Beams use stimer to represent minimum time until shutdown. */
1596 if (o->u.beamid>0) {
1597 beam_end( o->u.beamid );
1598 pilot_stopBeam(p, o);
1599 }
1600 }
1601 else if (!o->active)
1602 /* Case of a mod we can't toggle. */
1603 return 0;
1604 else if (o->outfit->lua_ontoggle != LUA_NOREF) {
1605 int ret = pilot_outfitLOntoggle( p, o, 0 );
1606 if (ret && outfit_isWeapon(o->outfit))
1607 o->state = PILOT_OUTFIT_OFF;
1608 return ret;
1609 }
1610 else {
1611 o->stimer = outfit_cooldown( o->outfit );
1612 if (o->stimer < 0.)
1613 o->state = PILOT_OUTFIT_OFF;
1614 else
1615 o->state = PILOT_OUTFIT_COOLDOWN;
1616 }
1617
1618 return 1;
1619}
1620
1629{
1630 if (o->outfit == NULL)
1631 return 0;
1633 pilot_afterburn( p );
1634 else if (o->outfit->lua_ontoggle != LUA_NOREF) {
1635 int ret = pilot_outfitLOntoggle( p, o, 1 );
1636 if (ret && outfit_isWeapon(o->outfit))
1637 o->state = PILOT_OUTFIT_ON;
1638 return ret;
1639 }
1640 else {
1641 o->state = PILOT_OUTFIT_ON;
1642 o->stimer = outfit_duration( o->outfit ) * p->stats.cooldown_mod;
1643 }
1644
1645 return 1;
1646}
1647
1655{
1656 int nchg = 0;
1657 for (int i=0; i<array_size(p->outfits); i++) {
1658 PilotOutfitSlot *o = p->outfits[i];
1659 /* Picky about our outfits. */
1660 if (o->outfit == NULL)
1661 continue;
1662 if (o->state == PILOT_OUTFIT_ON)
1663 nchg += pilot_outfitOff( p, o );
1664 }
1665 return (nchg > 0);
1666}
1667
1672{
1673 if (p == NULL)
1674 return;
1675
1676 if (pilot_isFlag(p, PILOT_HYP_PREP) || pilot_isFlag(p, PILOT_HYPERSPACE) ||
1677 pilot_isFlag(p, PILOT_LANDING) || pilot_isFlag(p, PILOT_TAKEOFF) ||
1678 pilot_isDisabled(p) || pilot_isFlag(p, PILOT_COOLDOWN))
1679 return;
1680
1681 /* Not under manual control if is player. */
1682 if (pilot_isFlag( p, PILOT_MANUAL_CONTROL ) && pilot_isPlayer(p))
1683 return;
1684
1686 if (p->afterburner == NULL)
1687 return;
1688
1689 /* Needs at least enough energy to afterburn fo 0.5 seconds. */
1690 if (p->energy < p->afterburner->outfit->u.afb.energy*0.5)
1691 return;
1692
1693 /* The afterburner only works if its efficiency is high enough. */
1694 if (pilot_heatEfficiencyMod( p->afterburner->heat_T, p->afterburner->outfit->overheat_min, p->afterburner->outfit->overheat_max ) < 0.1) {
1695 if (pilot_isPlayer(p))
1696 player_message(_("#r%s is overheating!#0"),_(p->afterburner->outfit->name));
1697 return;
1698 }
1699
1700 /* Turn it on. */
1701 if (p->afterburner->state == PILOT_OUTFIT_OFF) {
1702 p->afterburner->state = PILOT_OUTFIT_ON;
1703 p->afterburner->stimer = outfit_duration( p->afterburner->outfit );
1704 pilot_setFlag(p,PILOT_AFTERBURNER);
1705 pilot_calcStats( p );
1706 pilot_destealth( p ); /* No afterburning stealth. */
1707
1708 /* @todo Make this part of a more dynamic activated outfit sound system. */
1709 sound_playPos(p->afterburner->outfit->u.afb.sound_on,
1710 p->solid.pos.x, p->solid.pos.y, p->solid.vel.x, p->solid.vel.y);
1711 }
1712
1713 if (pilot_isPlayer(p)) {
1714 double afb_mod = MIN( 1., pilot_massFactor(player.p) );
1715 spfx_shake( afb_mod * player.p->afterburner->outfit->u.afb.rumble );
1716 }
1717}
1718
1723{
1724 if (p == NULL)
1725 return;
1726 if (p->afterburner == NULL)
1727 return;
1728
1729 if (p->afterburner->state == PILOT_OUTFIT_ON) {
1730 p->afterburner->state = PILOT_OUTFIT_OFF;
1731 pilot_rmFlag(p,PILOT_AFTERBURNER);
1732 pilot_calcStats( p );
1733
1734 /* @todo Make this part of a more dynamic activated outfit sound system. */
1735 sound_playPos(p->afterburner->outfit->u.afb.sound_off,
1736 p->solid.pos.x, p->solid.pos.y, p->solid.vel.x, p->solid.vel.y);
1737 }
1738}
1739
1743void ws_copy( PilotWeaponSet dest[PILOT_WEAPON_SETS], const PilotWeaponSet src[PILOT_WEAPON_SETS] )
1744{
1745 ws_free( dest );
1746 memcpy( dest, src, sizeof(PilotWeaponSet)*PILOT_WEAPON_SETS );
1747 for (int i=0; i<PILOT_WEAPON_SETS; i++)
1748 dest[i].slots = array_copy( PilotWeaponSetOutfit, src[i].slots );
1749}
1750
1754void ws_free( PilotWeaponSet ws[PILOT_WEAPON_SETS] )
1755{
1756 for (int i=0; i<PILOT_WEAPON_SETS; i++) {
1757 array_free( ws[i].slots );
1758 ws[i].slots = NULL;
1759 }
1760}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:158
#define array_copy(basic_type, ptr_array)
Returns a shallow copy of the input array.
Definition array.h:218
#define array_end(array)
Returns a pointer to the end of the reserved memory space.
Definition array.h:202
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition array.h:140
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:168
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:119
#define array_begin(array)
Returns a pointer to the beginning of the reserved memory space.
Definition array.h:194
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
unsigned int escort_create(Pilot *p, const Ship *ship, const vec2 *pos, const vec2 *vel, double dir, EscortType_t type, int add, int dockslot)
Creates an escort.
Definition escort.c:108
void player_message(const char *fmt,...)
Adds a mesg to the queue to be displayed on screen.
Definition gui.c:335
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:40
#define pow2(x)
Definition naev.h:46
int outfit_isSecondary(const Outfit *o)
Checks if outfit has the secondary flag set.
Definition outfit.c:662
double outfit_speed(const Outfit *o)
Gets the outfit's speed.
Definition outfit.c:796
int outfit_isBeam(const Outfit *o)
Checks if outfit is a beam type weapon.
Definition outfit.c:554
int outfit_isActive(const Outfit *o)
Checks if outfit is an active outfit.
Definition outfit.c:484
int outfit_isLauncher(const Outfit *o)
Checks if outfit is a weapon launcher.
Definition outfit.c:564
int outfit_isSeeker(const Outfit *o)
Checks if outfit is a seeking weapon.
Definition outfit.c:574
int outfit_isWeapon(const Outfit *o)
Checks to see if an outfit is a weapon.
Definition outfit.c:518
int outfit_isFighterBay(const Outfit *o)
Checks if outfit is a fighter bay.
Definition outfit.c:616
int outfit_isAfterburner(const Outfit *o)
Checks if outfit is an afterburner.
Definition outfit.c:607
int outfit_isTurret(const Outfit *o)
Checks if outfit is a turret class weapon.
Definition outfit.c:587
double outfit_duration(const Outfit *o)
Gets the outfit's duration.
Definition outfit.c:917
double outfit_cooldown(const Outfit *o)
Gets the outfit's cooldown.
Definition outfit.c:932
int outfit_isBolt(const Outfit *o)
Checks if outfit is bolt type weapon.
Definition outfit.c:544
double outfit_energy(const Outfit *o)
Gets the outfit's energy usage.
Definition outfit.c:757
double outfit_delay(const Outfit *o)
Gets the outfit's delay.
Definition outfit.c:734
Pilot * pilot_getTarget(Pilot *p)
Gets the target of a pilot using a fancy caching system.
Definition pilot.c:634
void pilot_cooldownEnd(Pilot *p, const char *reason)
Terminates active cooldown.
Definition pilot.c:959
void pilot_destealth(Pilot *p)
Destealths a pilot.
Definition pilot_ew.c:552
void pilot_heatAddSlot(const Pilot *p, PilotOutfitSlot *o)
Adds heat to an outfit slot.
Definition pilot_heat.c:133
double pilot_heatEfficiencyMod(double T, double Tb, double Tc)
Returns a 0:1 modifier representing efficiency (1. being normal).
Definition pilot_heat.c:235
double pilot_heatAccuracyMod(double T)
Returns a 0:1 modifier representing accuracy (0. being normal).
Definition pilot_heat.c:277
int pilot_getMount(const Pilot *p, const PilotOutfitSlot *w, vec2 *v)
Gets the mount position of a pilot.
void pilot_updateMass(Pilot *pilot)
Updates the pilot stats after mass change.
int pilot_maxAmmoO(const Pilot *p, const Outfit *o)
Gets the maximum available ammo for a pilot for a specific outfit.
int pilot_slotIsToggleable(const PilotOutfitSlot *o)
Checks to see if a slot has an active outfit that can be toggleable.
int pilot_outfitLOntoggle(const Pilot *pilot, PilotOutfitSlot *po, int on)
Handle the manual toggle of an outfit.
void pilot_calcStats(Pilot *pilot)
Recalculates the pilot's stats based on his outfits.
double pilot_massFactor(const Pilot *pilot)
Gets the factor at which speed gets worse.
int pilot_outfitLOnshoot(const Pilot *pilot, PilotOutfitSlot *po)
Handle the manual shoot of an outfit.
int pilot_rmAmmo(Pilot *pilot, PilotOutfitSlot *s, int quantity)
Removes some ammo from the pilot stock.
void pilot_outfitLOnshootany(Pilot *pilot)
Runs the pilot's Lua outfits onshootany script.
int pilot_weapSetTypeCheck(Pilot *p, int id)
Checks the current weapon set type.
void pilot_getRateMod(double *rate_mod, double *energy_mod, const Pilot *p, const Outfit *o)
Gets applicable fire rate and energy modifications for a pilot's weapon.
static int pilot_shootWeaponSetOutfit(Pilot *p, PilotWeaponSet *ws, const Outfit *o, int level, const Target *target, double time, int aim)
Calculates and shoots the appropriate weapons in a weapon set matching an outfit.
void pilot_weapSetInrange(Pilot *p, int id, int inrange)
Changes the weapon set inrange property.
void pilot_weaponSafe(Pilot *p)
Sets the weapon set as safe.
int pilot_shoot(Pilot *p, int level)
Makes the pilot shoot.
void pilot_weapSetManual(Pilot *p, int id, int manual)
Changes the weapon set manual property.
void pilot_afterburnOver(Pilot *p)
Deactivates the afterburner.
void pilot_weapSetCleanup(Pilot *p, int id)
Cleans up a weapon set.
void pilot_weapSetVolley(Pilot *p, int id, int volley)
Changes the weapon set volley property.
int pilot_weapSetManualCheck(Pilot *p, int id)
Checks the current weapon set manual property.
void pilot_stopBeam(const Pilot *p, PilotOutfitSlot *w)
Stops a beam outfit and sets delay as appropriate.
static int pilot_weapSetFire(Pilot *p, PilotWeaponSet *ws, int level)
Fires a weapon set.
static void pilot_weapSetUpdateOutfits(Pilot *p, PilotWeaponSet *ws)
Updates the outfits with their current weapon set level.
void pilot_weapSetRm(Pilot *p, int id, const PilotOutfitSlot *o)
Removes a slot from a weapon set.
void pilot_shootStop(Pilot *p, int level)
Have pilot stop shooting their weapon.
void pilot_afterburn(Pilot *p)
Activate the afterburner.
void pilot_weaponClear(Pilot *p)
Clears the pilots weapon settings.
void ws_copy(PilotWeaponSet dest[PILOT_WEAPON_SETS], const PilotWeaponSet src[PILOT_WEAPON_SETS])
Copies a weapon set over.
void ws_free(PilotWeaponSet ws[PILOT_WEAPON_SETS])
Frees a weapon set.
static void pilot_weapSetUpdateRange(const Pilot *p, PilotWeaponSet *ws)
Updates the weapon range for a pilot weapon set.
int pilot_weapSetVolleyCheck(Pilot *p, int id)
Checks the current weapon set volley property.
double pilot_weapFlyTime(const Outfit *o, const Pilot *parent, const vec2 *pos, const vec2 *vel)
Computes an estimation of ammo flying time.
void pilot_weapSetAIClear(Pilot *p)
Useful function for AI, clears activeness of all weapon sets.
PilotWeaponSet * pilot_weapSet(Pilot *p, int id)
Gets a weapon set from id.
Pilot * pilot_weaponTarget(Pilot *p, Target *wt)
Gets the weapon target of a pilot.
void pilot_weapSetUpdate(Pilot *p)
Updates the pilot's weapon sets.
int pilot_weapSetInSet(Pilot *p, int id, const PilotOutfitSlot *o)
Checks to see if a slot is in a weapon set.
int pilot_outfitOffAll(Pilot *p)
Disables all active outfits for a pilot.
void pilot_weaponSetDefault(Pilot *p)
Gives the pilot a default weapon set.
int pilot_weapSetCheck(Pilot *p, int id, const PilotOutfitSlot *o)
Checks to see if a slot is in a weapon set and usable.
PilotWeaponSetOutfit * pilot_weapSetList(Pilot *p, int id)
Lists the items in a pilot weapon set.
unsigned int pilot_weaponSetShootStop(Pilot *p, PilotWeaponSet *ws, int level)
Have pilot stop shooting a given weaponset.
int pilot_outfitOn(Pilot *p, PilotOutfitSlot *o)
Enable a given active outfit.
double pilot_weapSetAmmo(Pilot *p, int id, int level)
Gets the ammo of the current pilot weapon set.
int pilot_shootWeapon(Pilot *p, PilotOutfitSlot *w, const Target *target, double time, int aim)
Actually handles the shooting, how often the player.p can shoot and such.
void pilot_weapSetClear(Pilot *p, int id)
Clears a weapon set.
void pilot_weaponAuto(Pilot *p)
Tries to automatically set and create the pilot's weapon set.
int pilot_weapSetInrangeCheck(Pilot *p, int id)
Checks the current weapon set inrange property.
void pilot_weapSetPress(Pilot *p, int id, int type)
Handles a weapon set press.
void pilot_weapSetFree(Pilot *p)
Frees a pilot's weapon sets.
const char * pilot_weapSetName(Pilot *p, int id)
Gets the name of a weapon set.
double pilot_weapSetSpeed(Pilot *p, int id, int level)
Gets the speed of the current pilot weapon set.
int pilot_outfitOff(Pilot *p, PilotOutfitSlot *o)
Disables a given active outfit.
void pilot_weapSetType(Pilot *p, int id, WeaponSetType type)
Changes the weapon sets mode.
double pilot_weapSetRange(Pilot *p, int id, int level)
Gets the range of the current pilot weapon set.
void pilot_weapSetUpdateStats(Pilot *p)
Update the weapon sets given pilot stat changes.
void pilot_weapSetAdd(Pilot *p, int id, const PilotOutfitSlot *o, int level)
Adds an outfit to a weapon set.
Player_t player
Definition player.c:74
int sound_playPos(int sound, double px, double py, double vx, double vy)
Plays a sound based on position.
Definition sound.c:827
void spfx_shake(double mod)
Increases the current rumble level.
Definition spfx.c:898
double duration
Definition outfit.h:182
double min_duration
Definition outfit.h:183
double range
Definition outfit.h:186
OutfitAmmoAI ai
Definition outfit.h:231
OutfitSlotType type
Definition outfit.h:112
A ship outfit, depends radically on the type.
Definition outfit.h:328
int lua_ontoggle
Definition outfit.h:378
OutfitLauncherData lau
Definition outfit.h:406
OutfitBeamData bem
Definition outfit.h:405
OutfitType type
Definition outfit.h:402
unsigned int group
Definition outfit.h:361
union Outfit::@12 u
OutfitAfterburnerData afb
Definition outfit.h:408
char * name
Definition outfit.h:329
double lockon_timer
Definition pilot.h:101
Stores an outfit the pilot has.
Definition pilot.h:108
unsigned int beamid
Definition pilot.h:134
PilotOutfitAmmo ammo
Definition pilot.h:135
double stimer
Definition pilot.h:124
double heat_T
Definition pilot.h:117
PilotOutfitState state
Definition pilot.h:123
double timer
Definition pilot.h:125
unsigned int inrange
Definition pilot.h:130
ShipOutfitSlot * sslot
Definition pilot.h:114
const Outfit * outfit
Definition pilot.h:112
A pilot Weapon Set Outfit.
Definition pilot.h:146
A weapon set represents a set of weapons that have an action.
Definition pilot.h:164
PilotWeaponSetOutfit * slots
Definition pilot.h:167
double range[PILOT_WEAPSET_MAX_LEVELS]
Definition pilot.h:172
double speed[PILOT_WEAPSET_MAX_LEVELS]
Definition pilot.h:173
WeaponSetType type
Definition pilot.h:165
The representation of an in-game pilot.
Definition pilot.h:217
unsigned int id
Definition pilot.h:218
Solid solid
Definition pilot.h:227
PilotOutfitSlot * afterburner
Definition pilot.h:316
Pilot * p
Definition player.h:101
OutfitSlot slot
Definition ship.h:71
vec2 vel
Definition physics.h:48
vec2 pos
Definition physics.h:49
Represents a weapon target.
Definition target.h:19
Represents a 2d vector.
Definition vec2.h:32
double y
Definition vec2.h:34
double x
Definition vec2.h:33
double weapon_targetFlyTime(const Outfit *o, const Pilot *p, const Target *t)
Gets the fly time for a weapon target.
Definition weapon.c:2538
unsigned int beam_start(PilotOutfitSlot *po, double dir, const vec2 *pos, const vec2 *vel, const Pilot *parent, const Target *target, int aim)
Starts a beam weapon.
Definition weapon.c:2584
Weapon * weapon_add(PilotOutfitSlot *po, const Outfit *ref, double dir, const vec2 *pos, const vec2 *vel, const Pilot *parent, const Target *target, double time, int aim)
Creates a new weapon.
Definition weapon.c:2511
int weapon_inArc(const Outfit *o, const Pilot *parent, const Target *target, const vec2 *pos, const vec2 *vel, double dir, double time)
Gets the aim position of a turret weapon.
Definition weapon.c:1888
void beam_end(unsigned int beam)
Ends a beam weapon.
Definition weapon.c:2608