naev 0.10.4
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, double time, int aim );
47static int pilot_shootWeapon( Pilot *p, PilotOutfitSlot *w, double time, int aim );
48static void pilot_weapSetUpdateRange( const Pilot *p, PilotWeaponSet *ws );
49unsigned int pilot_weaponSetShootStop( Pilot* p, PilotWeaponSet *ws, int level );
50
59{
60 return &p->weapon_sets[ id ];
61}
62
71static int pilot_weapSetFire( Pilot *p, PilotWeaponSet *ws, int level )
72{
73 int ret, s;
74 Pilot *pt;
75 AsteroidAnchor *field;
76 Asteroid *ast;
77 double time;
78 int isstealth;
79
80 isstealth = pilot_isFlag( p, PILOT_STEALTH );
81
82 ret = 0;
83 for (int i=0; i<array_size(ws->slots); i++) {
84 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
85 const Outfit *o = pos->outfit;
86
87 /* Ignore NULL outfits. */
88 if (o == NULL)
89 continue;
90
91 /* Only "active" outfits. */
92 if ((level != -1) && (ws->slots[i].level != level))
93 continue;
94
95 /* Only run once for each weapon type in the group. */
96 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 /* Only "locked on" outfits. */
111 if (outfit_isSeeker(o) && (pos->u.ammo.lockon_timer > 0.))
112 continue;
113
114 /* If inrange is set we only fire at targets in range. */
115 time = INFINITY; /* With no target we just set time to infinity. */
116
117 /* Calculate time to target if it is there. */
118 pt = pilot_getTarget( p );
119 if (pt != NULL)
120 time = pilot_weapFlyTime( o, p, &pt->solid->pos, &pt->solid->vel);
121 /* Looking for a closer targeted asteroid */
122 else if (p->nav_asteroid != -1) {
123 field = &cur_system->asteroids[p->nav_anchor];
124 ast = &field->asteroids[p->nav_asteroid];
125 time = pilot_weapFlyTime( o, p, &ast->pos, &ast->vel);
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, !ws->manual );
134 }
135
136 /* Destealth when attacking. */
137 if (isstealth && (ret>0))
138 pilot_destealth( p );
139
140 /* Trigger onshoot after stealth gets broken. */
141 if (ret > 0)
143
144 return ret;
145}
146
151{
152 for (int i=0; i<PILOT_WEAPON_SETS; i++) {
153 PilotWeaponSet *ws = &p->weapon_sets[i];
154 ws->active = 0;
155 }
156}
157
165void pilot_weapSetPress( Pilot* p, int id, int type )
166{
167 int l, on, n;
168 int isstealth = pilot_isFlag( p, PILOT_STEALTH );
169 PilotWeaponSet *ws = pilot_weapSet(p,id);
170 /* Case no outfits. */
171 if (ws->slots == NULL)
172 return;
173
174 /* Handle fire groups. */
175 switch (ws->type) {
176 case WEAPSET_TYPE_CHANGE:
177 /* On press just change active weapon set to whatever is available. */
178 if ((type > 0) && (array_size(ws->slots)>0)) {
179 if (id != p->active_set)
181 p->active_set = id;
182 }
183 break;
184
185 case WEAPSET_TYPE_WEAPON:
186 /* Activation philosophy here is to turn on while pressed and off
187 * when it's not held anymore. */
188 if (type > 0)
189 ws->active = 1;
190 else if (type < 0) {
191 ws->active = 0;
192 if (pilot_weaponSetShootStop( p, ws, -1 )) /* De-activate weapon set. */
193 pilot_calcStats( p ); /* Just in case there is a activated outfit here. */
194 }
195 break;
196
197 case WEAPSET_TYPE_ACTIVE:
198 /* The behaviour here is more complex. What we do is consider a group
199 * to be entirely off if not all outfits are either on or cooling down.
200 * In the case it's deemed to be off, all outfits that are off get turned
201 * on, otherwise all outfits that are on are turrned to cooling down. */
202 /* Only care about presses. */
203 if (type < 0)
204 break;
205
206 /* Must not be disabled or cooling down. */
207 if ((pilot_isDisabled(p)) || (pilot_isFlag(p, PILOT_COOLDOWN)))
208 return;
209
210 /* Decide what to do. */
211 on = 1;
212 l = array_size(ws->slots);
213 for (int i=0; i<l; i++) {
214 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
215 if (pos->state == PILOT_OUTFIT_OFF) {
216 on = 0;
217 break;
218 }
219 }
220
221 /* Turn them off. */
222 n = 0;
223 if (on) {
224 for (int i=0; i<l; i++) {
225 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
226 if (pos->state != PILOT_OUTFIT_ON)
227 continue;
228
229 n += pilot_outfitOff( p, pos );
230 }
231 }
232 /* Turn them on. */
233 else {
234 pilotoutfit_modified = 0;
235 for (int i=0; i<l; i++) {
236 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
237 if (pos->state != PILOT_OUTFIT_OFF)
238 continue;
239
240 n += pilot_outfitOn( p, pos );
241 }
242 /* Recalculate if anything changed. */
243 if (pilotoutfit_modified)
244 pilot_calcStats( p );
245 }
246 /* Must recalculate stats. */
247 if (n > 0) {
248 /* pilot_destealth should run calcStats already. */
249 if (isstealth)
250 pilot_destealth( p );
251 else
252 pilot_calcStats( p );
253 }
254
255 break;
256 }
257}
258
265{
266 if (pilot_isFlag( p, PILOT_HYP_BEGIN))
267 return;
268
269 for (int i=0; i<PILOT_WEAPON_SETS; i++) {
270 PilotWeaponSet *ws = &p->weapon_sets[i];
271 if (ws->slots == NULL)
272 continue;
273
274 /* Weapons must get "fired" every turn. */
275 if (ws->type == WEAPSET_TYPE_WEAPON) {
276 if (ws->active)
277 pilot_weapSetFire( p, ws, -1 );
278 }
279 }
280}
281
286{
287 for (int i=0; i<array_size(p->outfits); i++)
288 p->outfits[i]->level = -1;
289
290 for (int i=0; i<array_size(ws->slots); i++)
291 p->outfits[ ws->slots[i].slotid ]->level = ws->slots[i].level;
292}
293
302{
303 PilotWeaponSet *ws = pilot_weapSet(p,id);
304 return ws->type;
305}
306
314void pilot_weapSetType( Pilot* p, int id, int type )
315{
316 PilotWeaponSet *ws = pilot_weapSet(p,id);
317 ws->type = type;
318
319 /* Set levels just in case. */
320 if ((ws->type == WEAPSET_TYPE_WEAPON) ||
321 (ws->type == WEAPSET_TYPE_ACTIVE))
322 for (int i=0; i<array_size(ws->slots); i++)
323 ws->slots[i].level = 0;
324}
325
334{
335 PilotWeaponSet *ws = pilot_weapSet(p,id);
336 return ws->inrange;
337}
338
346void pilot_weapSetInrange( Pilot* p, int id, int inrange )
347{
348 PilotWeaponSet *ws = pilot_weapSet(p,id);
349 ws->inrange = inrange;
350}
351
360{
361 PilotWeaponSet *ws = pilot_weapSet(p,id);
362 return ws->manual;
363}
364
372void pilot_weapSetManual( Pilot* p, int id, int manual )
373{
374 PilotWeaponSet *ws = pilot_weapSet(p,id);
375 ws->manual = manual;
376}
377
381const char *pilot_weapSetName( Pilot* p, int id )
382{
383 PilotWeaponSet *ws = pilot_weapSet(p,id);
384 if (array_size(ws->slots)==0)
385 return _("Unused");
386 switch (ws->type) {
387 case WEAPSET_TYPE_CHANGE: return _("Weapons - Switched"); break;
388 case WEAPSET_TYPE_WEAPON: return _("Weapons - Instant"); break;
389 case WEAPSET_TYPE_ACTIVE: return _("Abilities - Toggled"); break;
390 }
391 return NULL;
392}
393
397void pilot_weapSetRmSlot( Pilot *p, int id, OutfitSlotType type )
398{
399 int n, l;
400 PilotWeaponSet *ws;
401
402 /* We must clean up the slots. */
403 n = 0; /* Number to remove. */
404 ws = pilot_weapSet(p,id);
405 if (ws->slots == NULL)
406 return;
407 l = array_size(ws->slots);
408 for (int i=0; i<l; i++) {
409 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
410
411 if (pos->sslot->slot.type != type)
412 continue;
413
414 /* Move down. */
415 memmove( &ws->slots[i], &ws->slots[i+1], sizeof(PilotWeaponSetOutfit) * (l-i-1) );
416 n++;
417 }
418
419 /* Remove surplus. */
420 array_erase( &ws->slots, &ws->slots[l-n], &ws->slots[l] );
421}
422
431void pilot_weapSetAdd( Pilot* p, int id, PilotOutfitSlot *o, int level )
432{
434 const Outfit *oo;
435 double r;
436 PilotWeaponSet *ws = pilot_weapSet(p,id);
437
438 /* Make sure outfit is valid. */
439 oo = o->outfit;
440 if (oo == NULL)
441 return;
442
443 /* Make sure outfit type is weapon (or usable). */
445 return;
446
447 /* Create if needed. */
448 if (ws->slots == NULL)
450
451 /* Check if already there. */
452 for (int i=0; i<array_size(ws->slots); i++) {
453 if (ws->slots[i].slotid == o->id) {
454 ws->slots[i].level = level;
455
456 /* Update if needed. */
457 if (id == p->active_set)
459 return;
460 }
461 }
462
463 /* Add it. */
464 slot = &array_grow( &ws->slots );
465 slot->level = level;
466 slot->slotid= o->id;
467 r = outfit_range(oo);
468 if (r > 0)
469 slot->range2 = pow2(r);
470
471 /* Updated cached weapset. */
472 o->weapset = -1;
473 for (int j=0; j<PILOT_WEAPON_SETS; j++) {
474 if (pilot_weapSetCheck(p, j, o) != -1) {
475 o->weapset = j;
476 break;
477 }
478 }
479
480 /* Update range. */
482
483 /* Update if needed. */
484 if (id == p->active_set)
486}
487
496{
497 PilotWeaponSet *ws = pilot_weapSet(p,id);
498 for (int i=0; i<array_size(ws->slots); i++) {
499 if (ws->slots[i].slotid != o->id)
500 continue;
501
502 array_erase( &ws->slots, &ws->slots[i], &ws->slots[i+1] );
503
504 /* Update range. */
506
507 /* Update if needed. */
508 if (id == p->active_set)
510
511 /* Updated cached weapset. */
512 o->weapset = -1;
513 for (int j=0; j<PILOT_WEAPON_SETS; j++) {
514 if (pilot_weapSetCheck(p, j, o) != -1) {
515 o->weapset = j;
516 break;
517 }
518 }
519
520 return;
521 }
522}
523
533{
534 PilotWeaponSet *ws = pilot_weapSet(p,id);
535 for (int i=0; i<array_size(ws->slots); i++)
536 if (ws->slots[i].slotid == o->id)
537 return ws->slots[i].level;
538
539 /* Not found. */
540 return -1;
541}
542
549{
550 for (int i=0; i<PILOT_WEAPON_SETS; i++)
551 pilot_weapSetUpdateRange( p, &p->weapon_sets[i] );
552}
553
561{
562 int lev;
563 double range, speed;
564 double range_accum[PILOT_WEAPSET_MAX_LEVELS];
565 int range_num[PILOT_WEAPSET_MAX_LEVELS];
566 double speed_accum[PILOT_WEAPSET_MAX_LEVELS];
567 int speed_num[PILOT_WEAPSET_MAX_LEVELS];
568
569 /* Calculate ranges. */
570 for (int i=0; i<PILOT_WEAPSET_MAX_LEVELS; i++) {
571 range_accum[i] = 0.;
572 range_num[i] = 0;
573 speed_accum[i] = 0.;
574 speed_num[i] = 0;
575 }
576 for (int i=0; i<array_size(ws->slots); i++) {
577 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
578 if (pos->outfit == NULL)
579 continue;
580
581 /* Get level. */
582 lev = ws->slots[i].level;
583 if (lev >= PILOT_WEAPSET_MAX_LEVELS)
584 continue;
585
586 /* Empty Launchers aren't valid */
587 if (outfit_isLauncher(pos->outfit) && (pos->u.ammo.quantity <= 0))
588 continue;
589
590 /* Get range. */
591 range = outfit_range(pos->outfit);
592 if (outfit_isLauncher(pos->outfit))
593 range *= p->stats.launch_range;
594 if (range >= 0.) {
595 /* Calculate. */
596 range_accum[ lev ] += range;
597 range_num[ lev ]++;
598 }
599
600 /* Get speed. */
601 speed = outfit_speed(pos->outfit);
602 if (speed >= 0.) {
603 /* Calculate. */
604 speed_accum[ lev ] += speed;
605 speed_num[ lev ]++;
606 }
607 }
608
609 /* Postprocess. */
610 for (int i=0; i<PILOT_WEAPSET_MAX_LEVELS; i++) {
611 /* Postprocess range. */
612 if (range_num[i] == 0)
613 ws->range[i] = 0;
614 else
615 ws->range[i] = range_accum[i] / (double) range_num[i];
616
617 /* Postprocess speed. */
618 if (speed_num[i] == 0)
619 ws->speed[i] = 0;
620 else
621 ws->speed[i] = speed_accum[i] / (double) speed_num[i];
622 }
623}
624
632double pilot_weapSetRange( Pilot* p, int id, int level )
633{
634 double range;
635 PilotWeaponSet *ws = pilot_weapSet(p,id);
636 if (level < 0) {
637 range = 0.;
638 for (int i=0; i<PILOT_WEAPSET_MAX_LEVELS; i++)
639 range += ws->range[i];
640 }
641 else
642 range = ws->range[ level ];
643
644 return range;
645}
646
654double pilot_weapSetSpeed( Pilot* p, int id, int level )
655{
656 double speed;
657 PilotWeaponSet *ws = pilot_weapSet(p,id);
658 if (level < 0) {
659 speed = 0.;
660 for (int i=0; i<PILOT_WEAPSET_MAX_LEVELS; i++)
661 speed += ws->speed[i];
662 }
663 else
664 speed = ws->speed[ level ];
665
666 return speed;
667}
668
676double pilot_weapSetAmmo( Pilot* p, int id, int level )
677{
678 PilotWeaponSet *ws;
679 int amount, nammo;
680 double ammo;
681
682 ammo = 0.;
683 nammo = 0;
684 ws = pilot_weapSet(p,id);
685 for (int i=0; i<array_size(ws->slots); i++) {
686 PilotOutfitSlot *s = p->outfits[ ws->slots[i].slotid ];
687 if ((level >= 0) && (ws->slots[i].level != level))
688 continue;
689 amount = pilot_maxAmmoO( p, s->outfit );
690 if (amount > 0) {
691 ammo += (double)s->u.ammo.quantity / (double)amount;
692 nammo++;
693 }
694 }
695 return (nammo==0) ? 0. : ammo / (double)nammo;
696}
697
704void pilot_weapSetCleanup( Pilot* p, int id )
705{
706 PilotWeaponSet *ws = pilot_weapSet(p,id);
707
708 array_free( ws->slots );
709 ws->slots = NULL;
710
711 /* Update range. */
713}
714
719{
720 for (int i=0; i<PILOT_WEAPON_SETS; i++)
721 pilot_weapSetCleanup( p, i );
722}
723
732{
733 return pilot_weapSet( p, id )->slots;
734}
735
743int pilot_shoot( Pilot* p, int level )
744{
745 PilotWeaponSet *ws = pilot_weapSet( p, p->active_set );
746
747 /* Fire weapons. */
748 if (ws->type == WEAPSET_TYPE_CHANGE) { /* Must be a change set or a weaponset. */
749 int ret = pilot_weapSetFire( p, ws, level );
750
751 /* Firing weapons aborts active cooldown. */
752 if (pilot_isFlag(p, PILOT_COOLDOWN) && ret)
753 pilot_cooldownEnd(p, NULL);
754
755 return ret;
756 }
757
758 return 0;
759}
760
769void pilot_shootStop( Pilot* p, int level )
770{
771 PilotWeaponSet *ws = pilot_weapSet( p, p->active_set );
772
773 /* Stop and see if must recalculate. */
774 if (pilot_weaponSetShootStop( p, ws, level ))
775 pilot_calcStats( p );
776}
777
788unsigned int pilot_weaponSetShootStop( Pilot* p, PilotWeaponSet *ws, int level )
789{
790 /* Stop all beams. */
791 int recalc = 0;
792 for (int i=0; i<array_size(ws->slots); i++) {
793 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
794
795 /* Must have associated outfit. */
796 if (pos->outfit == NULL)
797 continue;
798
799 /* Must match level. */
800 if ((level != -1) && (ws->slots[i].level != level))
801 continue;
802
803 /* Only handle beams. */
804 if (!outfit_isBeam(pos->outfit)) {
805 /* Turn off the state. */
806 if (outfit_isMod( pos->outfit )) {
807 pos->state = PILOT_OUTFIT_OFF;
808 recalc = 1;
809 }
810 continue;
811 }
812
813 /* Stop beam. */
814 if (pos->u.beamid > 0) {
815 /* Enforce minimum duration if set. */
816 if (pos->outfit->u.bem.min_duration > 0.) {
817
818 pos->stimer = pos->outfit->u.bem.min_duration -
819 (pos->outfit->u.bem.duration - pos->timer);
820
821 if (pos->stimer > 0.)
822 continue;
823 }
824
825 beam_end( p->id, pos->u.beamid );
826 pilot_stopBeam(p, pos);
827 }
828 }
829
830 return recalc;
831}
832
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 /* Lua test to stop beam. */
854 if ((w->outfit->lua_ontoggle != LUA_NOREF) &&
855 !pilot_outfitLOntoggle( p, w, 0 ))
856 return;
857
858 /* Calculate rate modifier. */
859 pilot_getRateMod( &rate_mod, &energy_mod, p, w->outfit );
860
861 /* Beam duration used. Compensate for the fact it's duration might have
862 * been shortened by heat. */
863 used = w->outfit->u.bem.duration - w->timer*(1.-pilot_heatAccuracyMod(w->heat_T));
864
865 w->timer = rate_mod * (used / w->outfit->u.bem.duration) * outfit_delay( w->outfit );
866 w->u.beamid = 0;
867}
868
877double pilot_weapFlyTime( const Outfit *o, const Pilot *parent, const vec2 *pos, const vec2 *vel )
878{
879 vec2 approach_vector, relative_location, orthoradial_vector;
880 double speed, radial_speed, orthoradial_speed, dist, t;
881
882 dist = vec2_dist( &parent->solid->pos, pos );
883
884 /* Beam weapons */
885 if (outfit_isBeam(o)) {
886 if (dist > o->u.bem.range)
887 return INFINITY;
888 return 0.;
889 }
890
891 /* A bay doesn't have range issues */
892 if (outfit_isFighterBay(o))
893 return 0.;
894
895 /* Missiles use absolute velocity while bolts and unguided rockets use relative vel */
896 if (outfit_isLauncher(o) && o->u.lau.ai != AMMO_AI_UNGUIDED)
897 vec2_cset( &approach_vector, - vel->x, - vel->y );
898 else
899 vec2_cset( &approach_vector, VX(parent->solid->vel) - vel->x,
900 VY(parent->solid->vel) - vel->y );
901
902 speed = outfit_speed(o);
903
904 /* Get the vector : shooter -> target */
905 vec2_cset( &relative_location, pos->x - VX(parent->solid->pos),
906 pos->y - VY(parent->solid->pos) );
907
908 /* Get the orthogonal vector */
909 vec2_cset(&orthoradial_vector, VY(parent->solid->pos) - pos->y,
910 pos->x - VX(parent->solid->pos) );
911
912 radial_speed = vec2_dot( &approach_vector, &relative_location );
913 radial_speed = radial_speed / VMOD(relative_location);
914
915 orthoradial_speed = vec2_dot(&approach_vector, &orthoradial_vector);
916 orthoradial_speed = orthoradial_speed / VMOD(relative_location);
917
918 if ( ((speed*speed - VMOD(approach_vector)*VMOD(approach_vector)) != 0) && (speed*speed - orthoradial_speed*orthoradial_speed) > 0)
919 t = dist * (sqrt( speed*speed - orthoradial_speed*orthoradial_speed ) - radial_speed) /
920 (speed*speed - VMOD(approach_vector)*VMOD(approach_vector));
921 else
922 return INFINITY;
923
924 /* if t < 0, try the other solution */
925 if (t < 0)
926 t = - dist * (sqrt( speed*speed - orthoradial_speed*orthoradial_speed ) + radial_speed) /
927 (speed*speed - VMOD(approach_vector)*VMOD(approach_vector));
928
929 /* if t still < 0, no solution */
930 if (t < 0)
931 return INFINITY;
932
933 return t;
934}
935
939static int pilot_shootWeaponSetOutfit( Pilot* p, PilotWeaponSet *ws, const Outfit *o, int level, double time, int aim )
940{
941 int ret;
942 int is_launcher, is_bay;
943 double rate_mod, energy_mod;
944 int maxp, minh;
945 double q, maxt;
946
947 /* Store number of shots. */
948 ret = 0;
949
951 if (outfit_isBeam(o)) {
952 for (int i=0; i<array_size(ws->slots); i++) {
953 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
954 if (pos->outfit == o && (level == -1 || level == ws->slots[i].level)) {
955 ret += pilot_shootWeapon( p, pos, 0, aim );
956 pos->inrange = ws->inrange; /* State if the weapon has to be turn off when out of range. */
957 }
958 }
959 return ret;
960 }
961
962 /* Stores if it is a launcher. */
963 is_launcher = outfit_isLauncher(o);
964
965 is_bay = outfit_isFighterBay(o);
966
967 /* Calculate rate modifier. */
968 pilot_getRateMod( &rate_mod, &energy_mod, p, o );
969
970 /* Find optimal outfit, coolest that can fire. */
971 minh = -1;
972 maxt = 0.;
973 maxp = -1;
974 q = 0.;
975 for (int i=0; i<array_size(ws->slots); i++) {
976 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
977
978 /* Only matching outfits. */
979 if (pos->outfit != o)
980 continue;
981
982 /* Only match levels. */
983 if ((level != -1) && (ws->slots[i].level != level))
984 continue;
985
986 /* Launcher only counts with ammo. */
987 if ((is_launcher || is_bay) && (pos->u.ammo.quantity <= 0))
988 continue;
989
990 /* Get coolest that can fire. */
991 if (pos->timer <= 0.) {
992 if (is_launcher) {
993 if ((minh < 0) || (p->outfits[ ws->slots[minh].slotid ]->u.ammo.quantity < pos->u.ammo.quantity))
994 minh = i;
995 }
996 else {
997 if ((minh < 0) || (p->outfits[ ws->slots[minh].slotid ]->heat_T > pos->heat_T))
998 minh = i;
999 }
1000 }
1001
1002 /* Save some stuff. */
1003 if ((maxp < 0) || (pos->timer > maxt)) {
1004 maxp = i;
1005 maxt = pos->timer;
1006 }
1007 q += 1.;
1008 }
1009
1010 /* No weapon can fire. */
1011 if (minh < 0)
1012 return 0;
1013
1014 /* Only fire if the last weapon to fire fired more than (q-1)/q ago. */
1015 if (maxt > rate_mod * outfit_delay(o) * ((q-1.) / q))
1016 return 0;
1017
1018 /* Shoot the weapon. */
1019 ret += pilot_shootWeapon( p, p->outfits[ ws->slots[minh].slotid ], time, aim );
1020
1021 return ret;
1022}
1023
1033static int pilot_shootWeapon( Pilot *p, PilotOutfitSlot *w, double time, int aim )
1034{
1035 vec2 vp, vv;
1036 double rate_mod, energy_mod;
1037 double energy;
1038 int dockslot = -1;
1039
1040 /* Make sure weapon has outfit. */
1041 if (w->outfit == NULL)
1042 return 0;
1043
1044 /* Reset beam shut-off if needed. */
1045 if (outfit_isBeam(w->outfit) && w->outfit->u.bem.min_duration)
1046 w->stimer = INFINITY;
1047
1048 /* check to see if weapon is ready */
1049 if (w->timer > 0.)
1050 return 0;
1051
1052 /* Calculate rate modifier. */
1053 pilot_getRateMod( &rate_mod, &energy_mod, p, w->outfit );
1054
1055 /* Get weapon mount position. */
1056 pilot_getMount( p, w, &vp );
1057
1058 /* Modify velocity to take into account the rotation. */
1059 vec2_cset( &vv, p->solid->vel.x - vp.y*p->solid->dir_vel,
1060 p->solid->vel.y + vp.x*p->solid->dir_vel );
1061
1062 /* Get absolute weapon mount position. */
1063 vp.x += p->solid->pos.x;
1064 vp.y += p->solid->pos.y;
1065
1066 /*
1067 * regular bolt weapons
1068 */
1069 if (outfit_isBolt(w->outfit)) {
1070 /* enough energy? */
1071 if (outfit_energy(w->outfit)*energy_mod > p->energy)
1072 return 0;
1073
1074 /* Lua test. */
1075 if ((w->outfit->lua_ontoggle != LUA_NOREF) &&
1076 !pilot_outfitLOntoggle( p, w, 1 ))
1077 return 0;
1078
1079 energy = outfit_energy(w->outfit)*energy_mod;
1080 p->energy -= energy;
1081 pilot_heatAddSlot( p, w );
1082 if (!outfit_isProp( w->outfit, OUTFIT_PROP_SHOOT_DRY )) {
1083 for (int i=0; i<w->outfit->u.blt.shots; i++)
1084 weapon_add( w, w->heat_T, p->solid->dir,
1085 &vp, &vv, p, p->target, time, aim );
1086 }
1087 }
1088
1089 /*
1090 * Beam weapons.
1091 */
1092 else if (outfit_isBeam(w->outfit)) {
1093 /* Don't fire if the existing beam hasn't been destroyed yet. */
1094 if (w->u.beamid > 0)
1095 return 0;
1096
1097 /* Check if enough energy to last a second. */
1098 if (outfit_energy(w->outfit)*energy_mod > p->energy)
1099 return 0;
1100
1101 /* Lua test. */
1102 if ((w->outfit->lua_ontoggle != LUA_NOREF) &&
1103 !pilot_outfitLOntoggle( p, w, 1 ))
1104 return 0;
1105
1107 w->state = PILOT_OUTFIT_ON;
1108 if (!outfit_isProp( w->outfit, OUTFIT_PROP_SHOOT_DRY )) {
1109 w->u.beamid = beam_start( w, p->solid->dir,
1110 &vp, &p->solid->vel, p, p->target, aim );
1111 }
1112
1113 w->timer = w->outfit->u.bem.duration;
1114
1115 return 1; /* Return early due to custom timer logic. */
1116 }
1117
1118 /*
1119 * missile launchers
1120 *
1121 * must be a secondary weapon
1122 */
1123 else if (outfit_isLauncher(w->outfit)) {
1124
1125 /* Shooter can't be the target - safety check for the player.p */
1126 if ((w->outfit->u.lau.ai != AMMO_AI_UNGUIDED) && (p->id==p->target))
1127 return 0;
1128
1129 /* Must have ammo left. */
1130 if (w->u.ammo.quantity <= 0)
1131 return 0;
1132
1133 /* enough energy? */
1134 if (outfit_energy(w->outfit)*energy_mod > p->energy)
1135 return 0;
1136
1137 /* Lua test. */
1138 if ((w->outfit->lua_ontoggle != LUA_NOREF) &&
1139 !pilot_outfitLOntoggle( p, w, 1 ))
1140 return 0;
1141
1142 energy = outfit_energy(w->outfit)*energy_mod;
1143 p->energy -= energy;
1144 pilot_heatAddSlot( p, w );
1145 if (!outfit_isProp( w->outfit, OUTFIT_PROP_SHOOT_DRY )) {
1146 for (int i=0; i<w->outfit->u.lau.shots; i++)
1147 weapon_add( w, w->heat_T, p->solid->dir,
1148 &vp, &vv, p, p->target, time, aim );
1149 }
1150
1151 pilot_rmAmmo( p, w, 1 );
1152
1153 /* Make the AI aware a seeker has been shot */
1154 if (outfit_isSeeker(w->outfit))
1155 p->shoot_indicator = 1;
1156
1157 /* If last ammo was shot, update the range */
1158 if (w->u.ammo.quantity <= 0) {
1159 for (int j=0; j<PILOT_WEAPON_SETS; j++)
1160 pilot_weapSetUpdateRange( p, &p->weapon_sets[j] );
1161 }
1162 }
1163
1164 /*
1165 * Fighter bays.
1166 */
1167 else if (outfit_isFighterBay(w->outfit)) {
1168
1169 /* Must have ammo left. */
1170 if (w->u.ammo.quantity <= 0)
1171 return 0;
1172
1173 /* Lua test. */
1174 if ((w->outfit->lua_ontoggle != LUA_NOREF) &&
1175 !pilot_outfitLOntoggle( p, w, 1 ))
1176 return 0;
1177
1178 /* Get index of outfit slot */
1179 for (int j=0; j<array_size(p->outfits); j++) {
1180 if (p->outfits[j] == w)
1181 dockslot = j;
1182 }
1183
1184 /* Create the escort. */
1185 if (!outfit_isProp( w->outfit, OUTFIT_PROP_SHOOT_DRY ))
1186 escort_create( p, w->outfit->u.bay.ship,
1187 &vp, &p->solid->vel, p->solid->dir, ESCORT_TYPE_BAY, 1, dockslot );
1188
1189 w->u.ammo.quantity -= 1; /* we just shot it */
1190 p->mass_outfit -= w->outfit->u.bay.ship_mass;
1191 pilot_updateMass( p );
1192 }
1193 else
1194 WARN(_("Shooting unknown weapon type: %s"), w->outfit->name);
1195
1196 /* Reset timer. */
1197 w->timer += rate_mod * outfit_delay( w->outfit );
1198
1199 return 1;
1200}
1201
1210void pilot_getRateMod( double *rate_mod, double* energy_mod,
1211 const Pilot* p, const Outfit *o )
1212{
1213 switch (o->type) {
1214 case OUTFIT_TYPE_BOLT:
1215 case OUTFIT_TYPE_BEAM:
1216 *rate_mod = 1. / p->stats.fwd_firerate; /* Invert. */
1217 *energy_mod = p->stats.fwd_energy;
1218 break;
1219 case OUTFIT_TYPE_TURRET_BOLT:
1220 case OUTFIT_TYPE_TURRET_BEAM:
1221 *rate_mod = 1. / p->stats.tur_firerate; /* Invert. */
1222 *energy_mod = p->stats.tur_energy;
1223 break;
1224
1225 case OUTFIT_TYPE_LAUNCHER:
1226 case OUTFIT_TYPE_TURRET_LAUNCHER:
1227 *rate_mod = 1. / p->stats.launch_rate;
1228 *energy_mod = 1.;
1229 break;
1230
1231 case OUTFIT_TYPE_FIGHTER_BAY:
1232 *rate_mod = 1. / p->stats.fbay_rate;
1233 *energy_mod = 1.;
1234 break;
1235
1236 default:
1237 *rate_mod = 1.;
1238 *energy_mod = 1.;
1239 break;
1240 }
1241}
1242
1249{
1250 for (int i=0; i<PILOT_WEAPON_SETS; i++) {
1251 PilotWeaponSet *ws = pilot_weapSet( p, i );
1252 array_erase( &ws->slots, array_begin(ws->slots), array_end(ws->slots) );
1253 }
1254}
1255
1269{
1270 int level, id;
1271
1272 /* Clear weapons. */
1273 pilot_weaponClear( p );
1274
1275 /* Set modes. */
1276 pilot_weapSetType( p, 0, WEAPSET_TYPE_CHANGE );
1277 pilot_weapSetType( p, 1, WEAPSET_TYPE_CHANGE );
1278 pilot_weapSetType( p, 2, WEAPSET_TYPE_CHANGE );
1279 pilot_weapSetType( p, 3, WEAPSET_TYPE_CHANGE );
1280 pilot_weapSetType( p, 4, WEAPSET_TYPE_WEAPON );
1281 pilot_weapSetType( p, 5, WEAPSET_TYPE_WEAPON );
1282 pilot_weapSetType( p, 6, WEAPSET_TYPE_ACTIVE );
1283 pilot_weapSetType( p, 7, WEAPSET_TYPE_ACTIVE );
1284 pilot_weapSetType( p, 8, WEAPSET_TYPE_ACTIVE );
1285 pilot_weapSetType( p, 9, WEAPSET_TYPE_WEAPON );
1286
1287 /* All should be inrange. */
1288 if (!pilot_isPlayer(p))
1289 for (int i=0; i<PILOT_WEAPON_SETS; i++) {
1290 pilot_weapSetInrange( p, i, 1 );
1291 /* Update range and speed (at 0)*/
1292 pilot_weapSetUpdateRange( p, &p->weapon_sets[i] );
1293 }
1294
1295 /* Iterate through all the outfits. */
1296 for (int i=0; i<array_size(p->outfits); i++) {
1297 PilotOutfitSlot *slot = p->outfits[i];
1298 const Outfit *o = slot->outfit;
1299
1300 /* Must be non-empty, and a weapon or active outfit. */
1301 if ((o == NULL) || !outfit_isActive(o)) {
1302 slot->level = -1; /* Clear level. */
1303 slot->weapset = -1;
1304 continue;
1305 }
1306
1307 /* Set level based on secondary flag. */
1308 level = outfit_isSecondary(o);
1309
1310 /* Manually defined group preempts others. */
1311 if (o->group) {
1312 id = o->group;
1313 }
1314 /* Bolts and beams. */
1315 else if (outfit_isBolt(o) || outfit_isBeam(o) ||
1316 (outfit_isLauncher(o) && !outfit_isSeeker(o))) {
1317 id = outfit_isTurret(o) ? 2 : 1;
1318 }
1319 /* Seekers. */
1320 else if (outfit_isLauncher(o) && outfit_isSeeker(o)) {
1321 id = 4;
1322 }
1323 /* Fighter bays. */
1324 else if (outfit_isFighterBay(o)) {
1325 id = 5;
1326 }
1327 /* Ignore rest. */
1328 else {
1329 slot->level = -1;
1330 continue;
1331 }
1332
1333 /* Add to its base group. */
1334 pilot_weapSetAdd( p, id, slot, level );
1335
1336 /* Also add another copy to another group. */
1337 if (id == 1) { /* Forward. */
1338 pilot_weapSetAdd( p, 0, slot, level ); /* Also get added to 'All'. */
1339 pilot_weapSetAdd( p, 3, slot, 0 ); /* Also get added to 'Fwd/Tur'. */
1340 }
1341 else if (id == 2) { /* Turrets. */
1342 pilot_weapSetAdd( p, 0, slot, level ); /* Also get added to 'All'. */
1343 pilot_weapSetAdd( p, 3, slot, 1 ); /* Also get added to 'Fwd/Tur'. */
1344 }
1345 else if (id == 4) { /* Seekers */
1346 pilot_weapSetAdd( p, 0, slot, level ); /* Also get added to 'All'. */
1347 if (outfit_isTurret(o))
1348 pilot_weapSetAdd( p, 9, slot, level ); /* Also get added to 'Turreted Seekers'. */
1349 }
1350 }
1351
1352 /* Update active weapon set. */
1353 pilot_weapSetUpdateOutfits( p, &p->weapon_sets[ p->active_set ] );
1354}
1355
1360{
1361 int i;
1362
1363 /* If current set isn't a fire group no need to worry. */
1364 if (p->weapon_sets[ p->active_set ].type == WEAPSET_TYPE_CHANGE) {
1365 /* Update active weapon set. */
1366 pilot_weapSetUpdateOutfits( p, &p->weapon_sets[ p->active_set ] );
1367 return;
1368 }
1369
1370 /* Find first fire group. */
1371 for (i=0; i<PILOT_WEAPON_SETS; i++)
1372 if (p->weapon_sets[i].type == WEAPSET_TYPE_CHANGE)
1373 break;
1374
1375 /* Set active set to first if all fire groups or first non-fire group. */
1376 if (i >= PILOT_WEAPON_SETS)
1377 p->active_set = 0;
1378 else
1379 p->active_set = i;
1380
1381 /* Update active weapon set. */
1382 pilot_weapSetUpdateOutfits( p, &p->weapon_sets[ p->active_set ] );
1383}
1384
1391{
1392 for (int j=0; j<PILOT_WEAPON_SETS; j++) {
1393 PilotWeaponSet *ws = &p->weapon_sets[j];
1394 int l = array_size(ws->slots);
1395 int n = 0;
1396 for (int i=0; i<l; i++) {
1397 PilotOutfitSlot *pos = p->outfits[ ws->slots[i].slotid ];
1398 if (pos->outfit != NULL)
1399 continue;
1400
1401 /* Move down. */
1402 memmove( &ws->slots[i], &ws->slots[i+1], sizeof(PilotWeaponSetOutfit) * (l-i-1) );
1403 n++;
1404 }
1405 /* Remove surplus. */
1406 if (n > 0)
1407 array_erase( &ws->slots, &ws->slots[l-n], &ws->slots[l] );
1408
1409 /* See if we must overwrite levels. */
1410 if ((ws->type == WEAPSET_TYPE_WEAPON) ||
1411 (ws->type == WEAPSET_TYPE_ACTIVE))
1412 for (int i=0; i<array_size(ws->slots); i++)
1413 ws->slots[i].level = 0;
1414
1415 /* Update range. */
1416 pilot_weapSetUpdateRange( p, ws );
1417 }
1418}
1419
1428{
1429 /* Must be equipped, not disabled, not cooling down. */
1430 if (o->outfit == NULL || (pilot_isDisabled(p)) || (pilot_isFlag(p, PILOT_COOLDOWN)))
1431 return 0;
1432
1433 if (outfit_isAfterburner( o->outfit )) { /* Afterburners */
1434 if ((o->outfit->lua_ontoggle != LUA_NOREF) &&
1435 !pilot_outfitLOntoggle( p, o, 0 ))
1436 return 0;
1438 }
1439 else if (outfit_isBeam( o->outfit )) {
1440 if ((o->outfit->lua_ontoggle != LUA_NOREF) &&
1441 !pilot_outfitLOntoggle( p, o, 0 ))
1442 return 0;
1443 /* Beams use stimer to represent minimum time until shutdown. */
1444 if (o->u.beamid>0) {
1445 beam_end( p->id, o->u.beamid );
1446 pilot_stopBeam(p, o);
1447 }
1448 }
1449 else if (!o->active)
1450 /* Case of a mod we can't toggle. */
1451 return 0;
1452 else if (o->outfit->lua_ontoggle != LUA_NOREF)
1453 /* TODO toggle Lua outfit. */
1454 return pilot_outfitLOntoggle( p, o, 0 );
1455 else {
1456 o->stimer = outfit_cooldown( o->outfit );
1457 o->state = PILOT_OUTFIT_COOLDOWN;
1458 }
1459
1460 return 1;
1461}
1462
1471{
1472 if (o->outfit == NULL)
1473 return 0;
1475 pilot_afterburn( p );
1476 else if (o->outfit->lua_ontoggle != LUA_NOREF)
1477 /* TODO toggle Lua outfit. */
1478 return pilot_outfitLOntoggle( p, o, 1 );
1479 else {
1480 o->state = PILOT_OUTFIT_ON;
1481 o->stimer = outfit_duration( o->outfit ) * p->stats.cooldown_mod;
1482 }
1483
1484 return 1;
1485}
1486
1494{
1495 int nchg;
1496
1497 nchg = 0;
1498 for (int i=0; i<array_size(p->outfits); i++) {
1499 PilotOutfitSlot *o = p->outfits[i];
1500 /* Picky about our outfits. */
1501 if (o->outfit == NULL)
1502 continue;
1503 if (o->state == PILOT_OUTFIT_ON)
1504 nchg += pilot_outfitOff( p, o );
1505 }
1506 return (nchg > 0);
1507}
1508
1513{
1514 double afb_mod;
1515
1516 if (p == NULL)
1517 return;
1518
1519 if (pilot_isFlag(p, PILOT_HYP_PREP) || pilot_isFlag(p, PILOT_HYPERSPACE) ||
1520 pilot_isFlag(p, PILOT_LANDING) || pilot_isFlag(p, PILOT_TAKEOFF) ||
1521 pilot_isDisabled(p) || pilot_isFlag(p, PILOT_COOLDOWN))
1522 return;
1523
1524 /* Not under manual control if is player. */
1525 if (pilot_isFlag( p, PILOT_MANUAL_CONTROL ) && pilot_isFlag( p, PILOT_PLAYER ))
1526 return;
1527
1529 if (p->afterburner == NULL)
1530 return;
1531
1532 /* The afterburner only works if its efficiency is high enough. */
1533 if (pilot_heatEfficiencyMod( p->afterburner->heat_T,
1534 p->afterburner->outfit->u.afb.heat_base,
1535 p->afterburner->outfit->u.afb.heat_cap ) < 0.3)
1536 return;
1537
1538 if (p->afterburner->state == PILOT_OUTFIT_OFF) {
1539 p->afterburner->state = PILOT_OUTFIT_ON;
1540 p->afterburner->stimer = outfit_duration( p->afterburner->outfit );
1541 pilot_setFlag(p,PILOT_AFTERBURNER);
1542 pilot_calcStats( p );
1543 pilot_destealth( p ); /* No afterburning stealth. */
1544
1545 /* @todo Make this part of a more dynamic activated outfit sound system. */
1546 sound_playPos(p->afterburner->outfit->u.afb.sound_on,
1547 p->solid->pos.x, p->solid->pos.y, p->solid->vel.x, p->solid->vel.y);
1548 }
1549
1550 if (pilot_isPlayer(p)) {
1551 afb_mod = MIN( 1., player.p->afterburner->outfit->u.afb.mass_limit / player.p->solid->mass );
1552 spfx_shake( afb_mod * player.p->afterburner->outfit->u.afb.rumble );
1553 }
1554}
1555
1560{
1561 if (p == NULL)
1562 return;
1563 if (p->afterburner == NULL)
1564 return;
1565
1566 if (p->afterburner->state == PILOT_OUTFIT_ON) {
1567 p->afterburner->state = PILOT_OUTFIT_OFF;
1568 pilot_rmFlag(p,PILOT_AFTERBURNER);
1569 pilot_calcStats( p );
1570
1571 /* @todo Make this part of a more dynamic activated outfit sound system. */
1572 sound_playPos(p->afterburner->outfit->u.afb.sound_off,
1573 p->solid->pos.x, p->solid->pos.y, p->solid->vel.x, p->solid->vel.y);
1574 }
1575}
1576
1580void ws_copy( PilotWeaponSet dest[PILOT_WEAPON_SETS], const PilotWeaponSet src[PILOT_WEAPON_SETS] )
1581{
1582 ws_free( dest );
1583 memcpy( dest, src, sizeof(PilotWeaponSet)*PILOT_WEAPON_SETS );
1584 for (int i=0; i<PILOT_WEAPON_SETS; i++)
1585 dest[i].slots = array_copy( PilotWeaponSetOutfit, src[i].slots );
1586}
1587
1591void ws_free( PilotWeaponSet ws[PILOT_WEAPON_SETS] )
1592{
1593 for (int i=0; i<PILOT_WEAPON_SETS; i++) {
1594 array_free( ws[i].slots );
1595 ws[i].slots = NULL;
1596 }
1597}
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 char *ship, const vec2 *pos, const vec2 *vel, double dir, EscortType_t type, int add, int dockslot)
Creates an escort.
Definition: escort.c:111
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:596
double outfit_speed(const Outfit *o)
Gets the outfit's speed.
Definition: outfit.c:742
int outfit_isBeam(const Outfit *o)
Checks if outfit is a beam type weapon.
Definition: outfit.c:488
int outfit_isActive(const Outfit *o)
Checks if outfit is an active outfit.
Definition: outfit.c:434
int outfit_isLauncher(const Outfit *o)
Checks if outfit is a weapon launcher.
Definition: outfit.c:498
int outfit_isSeeker(const Outfit *o)
Checks if outfit is a seeking weapon.
Definition: outfit.c:508
int outfit_isFighterBay(const Outfit *o)
Checks if outfit is a fighter bay.
Definition: outfit.c:550
int outfit_isAfterburner(const Outfit *o)
Checks if outfit is an afterburner.
Definition: outfit.c:541
double outfit_range(const Outfit *o)
Gets the outfit's range.
Definition: outfit.c:711
int outfit_isTurret(const Outfit *o)
Checks if outfit is a turret class weapon.
Definition: outfit.c:521
int outfit_isMod(const Outfit *o)
Checks if outfit is a ship modification.
Definition: outfit.c:532
double outfit_duration(const Outfit *o)
Gets the outfit's duration.
Definition: outfit.c:851
double outfit_cooldown(const Outfit *o)
Gets the outfit's cooldown.
Definition: outfit.c:866
int outfit_isBolt(const Outfit *o)
Checks if outfit is bolt type weapon.
Definition: outfit.c:478
double outfit_energy(const Outfit *o)
Gets the outfit's energy usage.
Definition: outfit.c:681
double outfit_delay(const Outfit *o)
Gets the outfit's delay.
Definition: outfit.c:658
Pilot * pilot_getTarget(Pilot *p)
Gets the target of a pilot using a fancy caching system.
Definition: pilot.c:607
void pilot_cooldownEnd(Pilot *p, const char *reason)
Terminates active cooldown.
Definition: pilot.c:1004
void pilot_destealth(Pilot *p)
Destealths a pilot.
Definition: pilot_ew.c:549
void pilot_heatAddSlot(const Pilot *p, PilotOutfitSlot *o)
Adds heat to an outfit slot.
Definition: pilot_heat.c:144
double pilot_heatEfficiencyMod(double T, double Tb, double Tc)
Returns a 0:1 modifier representing efficiency (1. being normal).
Definition: pilot_heat.c:252
double pilot_heatAccuracyMod(double T)
Returns a 0:1 modifier representing accuracy (0. being normal).
Definition: pilot_heat.c:296
int pilot_getMount(const Pilot *p, const PilotOutfitSlot *w, vec2 *v)
Gets the mount position of a pilot.
Definition: pilot_outfit.c:148
void pilot_updateMass(Pilot *pilot)
Updates the pilot stats after mass change.
void pilot_outfitLOnshoot(Pilot *pilot)
Runs the pilot's Lua outfits onshoot script.
int pilot_maxAmmoO(const Pilot *p, const Outfit *o)
Gets the maximum available ammo for a pilot for a specific outfit.
Definition: pilot_outfit.c:767
int pilot_slotIsToggleable(const PilotOutfitSlot *o)
Checks to see if a slot has an active outfit that can be toggleable.
void pilot_calcStats(Pilot *pilot)
Recalculates the pilot's stats based on his outfits.
Definition: pilot_outfit.c:877
int pilot_outfitLOntoggle(Pilot *pilot, PilotOutfitSlot *po, int on)
Handle the manual toggle of an outfit.
int pilot_rmAmmo(Pilot *pilot, PilotOutfitSlot *s, int quantity)
Removes some ammo from the pilot stock.
Definition: pilot_outfit.c:693
static int pilot_shootWeapon(Pilot *p, PilotOutfitSlot *w, double time, int aim)
Actually handles the shooting, how often the player.p can shoot and such.
void pilot_stopBeam(Pilot *p, PilotOutfitSlot *w)
Stops a beam outfit and sets delay as appropriate.
Definition: pilot_weapon.c:839
int pilot_weapSetTypeCheck(Pilot *p, int id)
Checks the current weapon set type.
Definition: pilot_weapon.c:301
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.
void pilot_weapSetInrange(Pilot *p, int id, int inrange)
Changes the weapon set inrange property.
Definition: pilot_weapon.c:346
void pilot_weaponSafe(Pilot *p)
Sets the weapon set as safe.
int pilot_shoot(Pilot *p, int level)
Makes the pilot shoot.
Definition: pilot_weapon.c:743
void pilot_weapSetManual(Pilot *p, int id, int manual)
Changes the weapon set manual property.
Definition: pilot_weapon.c:372
void pilot_afterburnOver(Pilot *p)
Deactivates the afterburner.
void pilot_weapSetCleanup(Pilot *p, int id)
Cleans up a weapon set.
Definition: pilot_weapon.c:704
int pilot_weapSetManualCheck(Pilot *p, int id)
Checks the current weapon set manual property.
Definition: pilot_weapon.c:359
static int pilot_weapSetFire(Pilot *p, PilotWeaponSet *ws, int level)
Fires a weapon set.
Definition: pilot_weapon.c:71
static int pilot_shootWeaponSetOutfit(Pilot *p, PilotWeaponSet *ws, const Outfit *o, int level, double time, int aim)
Calculates and shoots the appropriate weapons in a weapon set matching an outfit.
Definition: pilot_weapon.c:939
static void pilot_weapSetUpdateOutfits(Pilot *p, PilotWeaponSet *ws)
Updates the outfits with their current weapon set level.
Definition: pilot_weapon.c:285
void pilot_shootStop(Pilot *p, int level)
Have pilot stop shooting their weapon.
Definition: pilot_weapon.c:769
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.
Definition: pilot_weapon.c:560
void pilot_weapSetAdd(Pilot *p, int id, PilotOutfitSlot *o, int level)
Adds an outfit to a weapon set.
Definition: pilot_weapon.c:431
double pilot_weapFlyTime(const Outfit *o, const Pilot *parent, const vec2 *pos, const vec2 *vel)
Computes an estimation of ammo flying time.
Definition: pilot_weapon.c:877
int pilot_weapSetCheck(Pilot *p, int id, PilotOutfitSlot *o)
Checks to see if a slot is in a weapon set.
Definition: pilot_weapon.c:532
void pilot_weapSetAIClear(Pilot *p)
Useful function for AI, clears activeness of all weapon sets.
Definition: pilot_weapon.c:150
void pilot_weapSetRmSlot(Pilot *p, int id, OutfitSlotType type)
Removes slots by type from the weapon set.
Definition: pilot_weapon.c:397
void pilot_weapSetType(Pilot *p, int id, int type)
Changes the weapon sets mode.
Definition: pilot_weapon.c:314
PilotWeaponSet * pilot_weapSet(Pilot *p, int id)
Gets a weapon set from id.
Definition: pilot_weapon.c:58
void pilot_weapSetUpdate(Pilot *p)
Updates the pilot's weapon sets.
Definition: pilot_weapon.c:264
int pilot_outfitOffAll(Pilot *p)
Disables all active outfits for a pilot.
void pilot_weaponSetDefault(Pilot *p)
Gives the pilot a default weapon set.
PilotWeaponSetOutfit * pilot_weapSetList(Pilot *p, int id)
Lists the items in a pilot weapon set.
Definition: pilot_weapon.c:731
unsigned int pilot_weaponSetShootStop(Pilot *p, PilotWeaponSet *ws, int level)
Have pilot stop shooting a given weaponset.
Definition: pilot_weapon.c:788
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.
Definition: pilot_weapon.c:676
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.
Definition: pilot_weapon.c:333
void pilot_weapSetPress(Pilot *p, int id, int type)
Handles a weapon set press.
Definition: pilot_weapon.c:165
void pilot_weapSetFree(Pilot *p)
Frees a pilot's weapon sets.
Definition: pilot_weapon.c:718
const char * pilot_weapSetName(Pilot *p, int id)
Gets the name of a weapon set.
Definition: pilot_weapon.c:381
double pilot_weapSetSpeed(Pilot *p, int id, int level)
Gets the speed of the current pilot weapon set.
Definition: pilot_weapon.c:654
void pilot_weapSetRm(Pilot *p, int id, PilotOutfitSlot *o)
Removes a slot from a weapon set.
Definition: pilot_weapon.c:495
int pilot_outfitOff(Pilot *p, PilotOutfitSlot *o)
Disables a given active outfit.
double pilot_weapSetRange(Pilot *p, int id, int level)
Gets the range of the current pilot weapon set.
Definition: pilot_weapon.c:632
void pilot_weapSetUpdateStats(Pilot *p)
Update the weapon sets given pilot stat changes.
Definition: pilot_weapon.c:548
Player_t player
Definition: player.c:73
int sound_playPos(int sound, double px, double py, double vx, double vy)
Plays a sound based on position.
Definition: sound.c:835
StarSystem * cur_system
Definition: space.c:105
void spfx_shake(double mod)
Increases the current rumble level.
Definition: spfx.c:883
Represents an asteroid field anchor.
Definition: asteroid.h:100
Asteroid * asteroids
Definition: asteroid.h:105
Represents a single asteroid.
Definition: asteroid.h:76
vec2 vel
Definition: asteroid.h:87
vec2 pos
Definition: asteroid.h:86
double duration
Definition: outfit.h:158
double min_duration
Definition: outfit.h:159
double range
Definition: outfit.h:162
OutfitAmmoAI ai
Definition: outfit.h:207
OutfitSlotType type
Definition: outfit.h:104
A ship outfit, depends radically on the type.
Definition: outfit.h:304
int lua_ontoggle
Definition: outfit.h:349
OutfitLauncherData lau
Definition: outfit.h:373
OutfitBeamData bem
Definition: outfit.h:372
OutfitType type
Definition: outfit.h:369
unsigned int group
Definition: outfit.h:332
OutfitAfterburnerData afb
Definition: outfit.h:375
union Outfit::@22 u
int quantity
Definition: pilot.h:99
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:158
int inrange
Definition: pilot.h:163
PilotWeaponSetOutfit * slots
Definition: pilot.h:161
int active
Definition: pilot.h:160
double range[PILOT_WEAPSET_MAX_LEVELS]
Definition: pilot.h:165
int manual
Definition: pilot.h:164
double speed[PILOT_WEAPSET_MAX_LEVELS]
Definition: pilot.h:166
The representation of an in-game pilot.
Definition: pilot.h:210
Solid * solid
Definition: pilot.h:220
PilotOutfitSlot * afterburner
Definition: pilot.h:308
Pilot * p
Definition: player.h:101
OutfitSlot slot
Definition: ship.h:71
vec2 vel
Definition: physics.h:21
double mass
Definition: physics.h:18
vec2 pos
Definition: physics.h:22
Represents a 2d vector.
Definition: vec2.h:32
double y
Definition: vec2.h:34
double x
Definition: vec2.h:33
void weapon_add(PilotOutfitSlot *po, const double T, const double dir, const vec2 *pos, const vec2 *vel, const Pilot *parent, unsigned int target, double time, int aim)
Creates a new weapon.
Definition: weapon.c:1953
void beam_end(const unsigned int parent, unsigned int beam)
Ends a beam weapon.
Definition: weapon.c:2067
unsigned int beam_start(PilotOutfitSlot *po, const double dir, const vec2 *pos, const vec2 *vel, const Pilot *parent, const unsigned int target, int aim)
Starts a beam weapon.
Definition: weapon.c:2012