naev 0.12.6
pilot_weapon.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
16#include "naev.h"
18
19#include "array.h"
20#include "escort.h"
21#include "log.h"
22#include "nlua_pilotoutfit.h"
23#include "pilot.h"
24#include "player.h"
25// #include "player_autonav.h"
26#include "sound.h"
27#include "spfx.h"
28#include "weapon.h"
29
30/*
31 * Prototypes.
32 */
34static int pilot_shootWeaponSetOutfit( Pilot *p, const Outfit *o,
35 const Target *target, double time,
36 int aim );
37static void pilot_weapSetUpdateRange( const Pilot *p, PilotWeaponSet *ws );
38
47{
48 return &p->weapon_sets[id];
49}
50
55{
56 for ( int i = 0; i < PILOT_WEAPON_SETS; i++ ) {
57 PilotWeaponSet *ws = &p->weapon_sets[i];
58 ws->active = 0;
59 }
60}
61
62static int pilot_weapSetPressToggle( Pilot *p, PilotWeaponSet *ws )
63{
64 int ret = 0;
65 /* Toggle state. */
66 for ( int j = 0; j < array_size( ws->slots ); j++ ) {
67 PilotOutfitSlot *pos = p->outfits[ws->slots[j].slotid];
68 if ( pos->outfit == NULL )
69 continue;
70 if ( !( pos->flags & PILOTOUTFIT_TOGGLEABLE ) )
71 continue;
72 /* Holding has priority over toggling. */
73 if ( pos->flags & PILOTOUTFIT_ISON_HOLD )
74 continue;
75
76 /* Flip state to ON/OFF. */
77 if ( pos->flags & PILOTOUTFIT_ISON ) {
78 pos->flags &= ~PILOTOUTFIT_ISON_TOGGLE;
79 } else {
80 pos->flags |= PILOTOUTFIT_ISON_TOGGLE;
81 if ( ws->volley )
82 pos->flags |= PILOTOUTFIT_VOLLEY;
83 if ( ws->inrange )
84 pos->flags |= PILOTOUTFIT_INRANGE;
85 if ( ws->manual )
86 pos->flags |= PILOTOUTFIT_MANUAL;
87 }
88 ret = 1;
89 }
90 return ret;
91}
92
101int pilot_weapSetPress( Pilot *p, int id, int type )
102{
103 PilotWeaponSet *ws = pilot_weapSet( p, id );
104 int ret = 0;
105 WeaponSetType t;
106
107 /* Case no outfits. */
108 if ( ws->slots == NULL )
109 return 0;
110
111 /* Must not be disabled or cooling down. */
112 if ( ( pilot_isDisabled( p ) ) || ( pilot_isFlag( p, PILOT_COOLDOWN ) ) )
113 return 0;
114
115 /* If not advanced we'll override the types. */
116 if ( p->advweap )
117 t = ws->type;
118 else {
119 if ( id < 2 )
120 t = WEAPSET_TYPE_HOLD;
121 else
122 t = WEAPSET_TYPE_DEFAULT;
123 }
124
125 /* Handle fire groups. */
126 switch ( t ) {
127 case WEAPSET_TYPE_DEFAULT:
128 /* Tap is toggle, hold is hold. */
129 if ( type < 0 ) {
130 ret = ( ws->active != 0 );
131 ws->active = 0;
132 } else if ( type > 1 ) {
133 ret = ( ws->active != 1 );
134 ws->active = 1;
135 } else {
136 ret = ( ws->active != 0 );
137 ws->active = 0;
138 ret |= pilot_weapSetPressToggle( p, ws );
139 }
140 break;
141
142 case WEAPSET_TYPE_TOGGLE:
143 /* This just toggles an outfit on until it turns off. If it is on, it
144 * toggles it off instead. */
145 /* Only care about presses. */
146 if ( type < 0 )
147 break;
148 ret = pilot_weapSetPressToggle( p, ws );
149 break;
150
151 case WEAPSET_TYPE_HOLD:
152 /* Activation philosophy here is to turn on while pressed and off
153 * when it's not held anymore. */
154
155 /* Clear change variables. */
156 if ( type > 0 ) {
157 ret = ( ws->active != 1 );
158 ws->active = 1;
159 } else if ( type < 0 ) {
160 ret = ( ws->active != 0 );
161 ws->active = 0;
162 }
163 break;
164 }
165 return ret;
166}
167
173{
174 int non, noff;
175 int breakstealth;
176
177 /* First pass to remove all hold flags, as we recompute it. */
178 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
179 PilotOutfitSlot *pos = p->outfits[i];
180 pos->flags &= ~PILOTOUTFIT_ISON_HOLD;
181 }
182
183 /* Now mark all the outfits as on or off. */
184 for ( int i = 0; i < PILOT_WEAPON_SETS; i++ ) {
185 PilotWeaponSet *ws = &p->weapon_sets[i];
186 if ( ws->slots == NULL )
187 continue;
188
189 /* Only care if on. */
190 if ( !ws->active )
191 continue;
192
193 /* Only care about HOLD sets. */
194 if ( p->advweap && ( ws->type == WEAPSET_TYPE_TOGGLE ) )
195 continue;
196
197 /* Time to mark as on. */
198 for ( int j = 0; j < array_size( ws->slots ); j++ ) {
199 PilotOutfitSlot *pos = p->outfits[ws->slots[j].slotid];
200 if ( pos->outfit == NULL )
201 continue;
202 /* If held, we clear the toggle. */
203 pos->flags &= ~PILOTOUTFIT_ISON_TOGGLE;
204 pos->flags |= PILOTOUTFIT_ISON_HOLD;
205 if ( ws->volley )
206 pos->flags |= PILOTOUTFIT_VOLLEY;
207 if ( ws->inrange )
208 pos->flags |= PILOTOUTFIT_INRANGE;
209 if ( ws->manual )
210 pos->flags |= PILOTOUTFIT_MANUAL;
211 }
212 }
213
214 /* Last pass figures out what to do. */
215 breakstealth = 0;
216 non = noff = 0;
217 pilotoutfit_modified = 0;
218 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
219 PilotOutfitSlot *pos = p->outfits[i];
220 const Outfit *o = pos->outfit;
221 if ( o == NULL )
222 continue;
223 if ( !( pos->flags & PILOTOUTFIT_TOGGLEABLE ) )
224 continue;
225
226 if ( pos->flags & ( PILOTOUTFIT_ISON_TOGGLE | PILOTOUTFIT_ISON_HOLD ) )
227 pos->flags |= PILOTOUTFIT_ISON;
228 else
229 pos->flags &= ~( PILOTOUTFIT_ISON | PILOTOUTFIT_DYNAMIC_FLAGS );
230
231 /* Se whether to turn on or off. */
232 if ( pos->flags & PILOTOUTFIT_ISON ) {
233 /* Weapons are handled separately. */
234 if ( outfit_isWeapon( o ) && ( o->lua_ontoggle == LUA_NOREF ) )
235 continue;
236
237 if ( pos->state == PILOT_OUTFIT_OFF ) {
238 int n = pilot_outfitOn( p, pos );
239 if ( ( n > 0 ) &&
240 !outfit_isProp( pos->outfit, OUTFIT_PROP_STEALTH_ON ) )
241 breakstealth = 1;
242 else
243 pos->flags &= ~( PILOTOUTFIT_ISON | PILOTOUTFIT_DYNAMIC_FLAGS );
244 non += n;
245 }
246 } else {
247 if ( pos->state == PILOT_OUTFIT_ON )
248 noff += pilot_outfitOff( p, pos, 1 );
249 }
250 }
251
252 /* Now update stats and shit as necessary. */
253 if ( ( non + noff > 0 ) || pilotoutfit_modified ) {
254 /* pilot_destealth should run calcStats already. */
255 if ( pilot_isFlag( p, PILOT_STEALTH ) && breakstealth )
256 pilot_destealth( p );
257 else
258 pilot_calcStats( p );
259 }
260}
261
268{
269 int n, shotweap, target_set, breakstealth;
270 double time;
271 Target wt;
272
273 if ( pilot_isFlag( p, PILOT_HYP_BEGIN ) )
274 return;
275
276 breakstealth = 0;
277 shotweap = 0;
278 n = 0;
279 target_set = 0;
280 pilotoutfit_modified = 0;
281 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
282 PilotOutfitSlot *pos = p->outfits[i];
283 const Outfit *o = pos->outfit;
284 int volley;
285 int shot = 0;
286 if ( o == NULL )
287 continue;
288 if ( !( pos->flags & PILOTOUTFIT_TOGGLEABLE ) )
289 continue;
290 if ( !( pos->flags & PILOTOUTFIT_ISON ) )
291 continue;
292 if ( !outfit_isWeapon( o ) )
293 continue;
294
295 /* @TODO Make beams not fire all at once. */
296 volley = ( ( pos->flags & PILOTOUTFIT_VOLLEY ) || outfit_isBeam( o ) );
297
298 /* For non-volley mode we want to run once per outfit type. */
299 if ( !volley ) {
300 int s = 0;
301 for ( int j = 0; j < i; j++ ) {
302 const PilotOutfitSlot *posj = p->outfits[j];
303 if ( posj->flags != PILOTOUTFIT_ISON )
304 continue;
305 /* Found a match. */
306 if ( posj->outfit == o ) {
307 s = 1;
308 break;
309 }
310 }
311 if ( s != 0 )
312 continue;
313 }
314
315 /* Only "locked on" outfits. */
316 if ( outfit_isSeeker( o ) && ( pos->u.ammo.lockon_timer > 0. ) )
317 continue;
318
319 /* Lazy target setting. */
320 if ( !target_set ) {
321 pilot_weaponTarget( p, &wt );
322 target_set = 1;
323 }
324 time = weapon_targetFlyTime( o, p, &wt );
325
326 /* Only "inrange" outfits.
327 * XXX for simplicity we are using pilot position / velocity instead of
328 * mount point, which might be a bit off. */
329 if ( ( pos->flags & PILOTOUTFIT_INRANGE ) && !outfit_isFighterBay( o ) ) {
330 /* Check range for different types. */
331 if ( time < 0. )
332 continue;
333 else if ( outfit_isBolt( o ) ) {
334 if ( pilot_outfitRange( p, o ) / o->u.blt.speed < time )
335 continue;
336 } else if ( outfit_isLauncher( o ) ) {
337 if ( o->u.lau.duration * p->stats.launch_range *
338 p->stats.weapon_range <
339 time )
340 continue;
341 }
342
343 /* Must be in aiming arc if applicable. */
344 if ( !weapon_inArc( o, p, &wt, &p->solid.pos, &p->solid.vel,
345 p->solid.dir, time ) )
346 continue;
347 }
348
349 /* Shoot the weapon of the weaponset. */
350 if ( volley )
351 shot = pilot_shootWeapon( p, pos, &wt, time,
352 !( pos->flags & PILOTOUTFIT_MANUAL ) );
353 else
355 p, o, &wt, time, !( pos->flags & PILOTOUTFIT_MANUAL ) );
356 shotweap += shot;
357 n++;
358
359 if ( shot && !outfit_isProp( pos->outfit, OUTFIT_PROP_STEALTH_ON ) )
360 breakstealth = 1;
361 }
362
363 /* Now update stats and shit as necessary. */
364 if ( ( n > 0 ) || pilotoutfit_modified ) {
365 /* pilot_destealth should run calcStats already. */
366 if ( pilot_isFlag( p, PILOT_STEALTH ) && breakstealth )
367 pilot_destealth( p );
368 else
369 pilot_calcStats( p );
370
371 /* Firing stuff aborts active cooldown. */
372 if ( pilot_isFlag( p, PILOT_COOLDOWN ) && ( shotweap > 0 ) )
373 pilot_cooldownEnd( p, NULL );
374
375 /* Trigger onshoot after stealth gets broken. */
376 if ( shotweap > 0 )
378 }
379}
380
385{
386 /* Have to update slots potentially. */
387 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
388 PilotOutfitSlot *o = p->outfits[i];
389 o->weapset = -1;
390 for ( int j = 0; j < PILOT_WEAPON_SETS; j++ ) {
391 if ( pilot_weapSetCheck( p, j, o ) != -1 ) {
392 o->weapset = j;
393 break;
394 }
395 }
396 }
397
398 /* Update range. */
400}
401
410{
411 const PilotWeaponSet *ws = pilot_weapSet( p, id );
412 return ws->type;
413}
414
422void pilot_weapSetType( Pilot *p, int id, WeaponSetType type )
423{
424 PilotWeaponSet *ws = pilot_weapSet( p, id );
425 ws->type = type;
426 ws->active = 0; /* Disable no matter what. */
427 // p->autoweap = 0;
429}
430
439{
440 const PilotWeaponSet *ws = pilot_weapSet( p, id );
441 return ws->inrange;
442}
443
451void pilot_weapSetInrange( Pilot *p, int id, int inrange )
452{
453 PilotWeaponSet *ws = pilot_weapSet( p, id );
454 ws->inrange = inrange;
455 // p->autoweap = 0;
456}
457
466{
467 const PilotWeaponSet *ws = pilot_weapSet( p, id );
468 return ws->manual;
469}
470
478void pilot_weapSetManual( Pilot *p, int id, int manual )
479{
480 PilotWeaponSet *ws = pilot_weapSet( p, id );
481 ws->manual = manual;
482 // p->autoweap = 0;
483}
484
493{
494 const PilotWeaponSet *ws = pilot_weapSet( p, id );
495 return ws->volley;
496}
497
505void pilot_weapSetVolley( Pilot *p, int id, int volley )
506{
507 PilotWeaponSet *ws = pilot_weapSet( p, id );
508 ws->volley = volley;
509 // p->autoweap = 0;
510}
511
515const char *pilot_weapSetName( Pilot *p, int id )
516{
517 static char setname[STRMAX_SHORT];
518 const char *base, *type;
519 PilotWeaponSet *ws = pilot_weapSet( p, id );
520 type = base = NULL;
521
522 switch ( ws->type ) {
523 case WEAPSET_TYPE_DEFAULT:
524 type = p_( "weapset", "Default" );
525 break;
526 case WEAPSET_TYPE_TOGGLE:
527 type = p_( "weapset", "Toggle" );
528 break;
529 case WEAPSET_TYPE_HOLD:
530 type = p_( "weapset", "Hold" );
531 break;
532 default:
533 type = p_( "weapset", "Unknown" );
534 break;
535 }
536
537 if ( array_size( ws->slots ) == 0 )
538 base = _( "Empty" );
539 else {
540 const Outfit *o = NULL;
541 int not_same = 0;
542 int has_weap = 0;
543 int has_util = 0;
544 int has_stru = 0;
545 for ( int i = 0; i < array_size( ws->slots ); i++ ) {
546 const PilotOutfitSlot *pos = p->outfits[ws->slots[i].slotid];
547 if ( pos->outfit == NULL ) /* Ignore empty slots. */
548 continue;
549 if ( !pilot_slotIsToggleable( pos ) ) /* Ignore non-active. */
550 continue;
551 if ( o == NULL )
552 o = pos->outfit;
553 else if ( o != pos->outfit )
554 not_same = 1;
555 switch ( pos->sslot->slot.type ) {
556 case OUTFIT_SLOT_STRUCTURE:
557 has_stru++;
558 break;
559 case OUTFIT_SLOT_UTILITY:
560 has_util++;
561 break;
562 case OUTFIT_SLOT_WEAPON:
563 has_weap++;
564 break;
565 default:
566 break;
567 }
568 }
569 if ( o == NULL )
570 base = _( "Empty" );
571 else if ( not_same == 0 )
572 base = _( o->name );
573 else if ( has_weap && !has_util && !has_stru )
574 base = p_( "weapset", "Weapons" );
575 else if ( !has_weap && has_util && !has_stru )
576 base = p_( "weapset", "Utilities" );
577 else if ( !has_weap && !has_util && has_stru )
578 base = p_( "weapset", "Structurals" );
579 else
580 base = p_( "weapset", "Mixed" );
581 }
582
583 if ( p->advweap )
584 snprintf( setname, sizeof( setname ), p_( "weapset", "%s - %s" ), type,
585 base );
586 else
587 snprintf( setname, sizeof( setname ), "%s", base );
588 return setname;
589}
590
598void pilot_weapSetAdd( Pilot *p, int id, const PilotOutfitSlot *o )
599{
601 PilotWeaponSet *ws = pilot_weapSet( p, id );
602
603 /* Create if needed. */
604 if ( ws->slots == NULL )
606
607 /* Check if already there. */
608 for ( int i = 0; i < array_size( ws->slots ); i++ ) {
609 if ( ws->slots[i].slotid != o->id )
610 continue;
611 return;
612 }
613
614 /* Add it. */
615 slot = &array_grow( &ws->slots );
616 slot->slotid = o->id;
617 if ( o->outfit != NULL )
618 slot->range2 = pow2( pilot_outfitRange( p, o->outfit ) );
619 else
620 slot->range2 = 0.;
622}
623
631void pilot_weapSetRm( Pilot *p, int id, const PilotOutfitSlot *o )
632{
633 PilotWeaponSet *ws = pilot_weapSet( p, id );
634 for ( int i = 0; i < array_size( ws->slots ); i++ ) {
635 if ( ws->slots[i].slotid != o->id )
636 continue;
637
638 array_erase( &ws->slots, &ws->slots[i], &ws->slots[i + 1] );
640 return;
641 }
642}
643
650void pilot_weapSetClear( Pilot *p, int id )
651{
652 PilotWeaponSet *ws = pilot_weapSet( p, id );
653 ws->type = WEAPSET_TYPE_TOGGLE;
654 array_free( ws->slots );
655 ws->slots = NULL;
656 // p->autoweap = 0;
657
658 /* Update if needed. */
660}
661
665int pilot_weapSetInSet( Pilot *p, int id, const PilotOutfitSlot *o )
666{
667 const PilotWeaponSet *ws = pilot_weapSet( p, id );
668 for ( int i = 0; i < array_size( ws->slots ); i++ ) {
669 /* Must match the current weapon. */
670 if ( ws->slots[i].slotid != o->id )
671 continue;
672 return 0;
673 }
674 /* Not found. */
675 return -1;
676}
677
686int pilot_weapSetCheck( Pilot *p, int id, const PilotOutfitSlot *o )
687{
688 const PilotWeaponSet *ws = pilot_weapSet( p, id );
689 for ( int i = 0; i < array_size( ws->slots ); i++ ) {
690 /* Must match the current weapon. */
691 if ( ws->slots[i].slotid != o->id )
692 continue;
693 return 0;
694 }
695 /* Not found. */
696 return -1;
697}
698
705{
706 for ( int i = 0; i < PILOT_WEAPON_SETS; i++ )
707 pilot_weapSetUpdateRange( p, &p->weapon_sets[i] );
708}
709
717{
718 double range, speed;
719 double range_accum;
720 int range_num;
721 double speed_accum;
722 int speed_num;
723
724 /* Calculate ranges. */
725 range_accum = 0.;
726 range_num = 0;
727 speed_accum = 0.;
728 speed_num = 0;
729 ws->range_min = INFINITY;
730
731 for ( int i = 0; i < array_size( ws->slots ); i++ ) {
732 PilotOutfitSlot *pos = p->outfits[ws->slots[i].slotid];
733 if ( pos->outfit == NULL )
734 continue;
735
736 /* Empty Launchers aren't valid */
737 if ( outfit_isLauncher( pos->outfit ) && ( pos->u.ammo.quantity <= 0 ) )
738 continue;
739
740 /* Get range. */
741 range = pilot_outfitRange( p, pos->outfit );
742 if ( range >= 0. ) {
743 /* Calculate. */
744 range_accum += range;
745 range_num++;
746
747 /* Add minimum. */
748 ws->range_min = MIN( range, ws->range_min );
749 }
750
751 /* Get speed. */
752 speed = outfit_speed( pos->outfit );
753 if ( speed >= 0. ) {
754 /* Calculate. */
755 speed_accum += speed;
756 speed_num++;
757 }
758 }
759
760 /* Postprocess range. */
761 if ( range_num == 0 )
762 ws->range = 0;
763 else
764 ws->range = range_accum / (double)range_num;
765
766 /* Postprocess speed. */
767 if ( speed_num == 0 )
768 ws->speed = 0;
769 else
770 ws->speed = speed_accum / (double)speed_num;
771}
772
779double pilot_weapSetRangeMin( Pilot *p, int id )
780{
781 PilotWeaponSet *ws = pilot_weapSet( p, id );
782 return ws->range_min;
783}
784
791double pilot_weapSetRange( Pilot *p, int id )
792{
793 PilotWeaponSet *ws = pilot_weapSet( p, id );
794 return ws->range;
795}
796
803double pilot_weapSetSpeed( Pilot *p, int id )
804{
805 PilotWeaponSet *ws = pilot_weapSet( p, id );
806 return ws->speed;
807}
808
815double pilot_weapSetAmmo( Pilot *p, int id )
816{
817 PilotWeaponSet *ws = pilot_weapSet( p, id );
818 double ammo = 0.;
819 int nammo = 0;
820 for ( int i = 0; i < array_size( ws->slots ); i++ ) {
821 int amount;
822 PilotOutfitSlot *s = p->outfits[ws->slots[i].slotid];
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
877{
878 double rate_mod, energy_mod, used;
879
880 /* There's nothing to do if the beam isn't active. */
881 if ( w->u.beamid == 0 )
882 return;
883
884 /* Safeguard against a nasty race condition. */
885 if ( w->outfit == NULL ) {
886 w->u.beamid = 0;
887 return;
888 }
889
890 /* Lua test to stop beam. */
891 /*
892 if ((w->outfit->lua_onshoot!= LUA_NOREF) &&
893 !pilot_outfitLOnshoot( p, w, 0 ))
894 return;
895 */
896
897 /* Calculate rate modifier. */
898 pilot_getRateMod( &rate_mod, &energy_mod, p, w->outfit );
899
900 /* Beam duration used. Compensate for the fact its duration might have
901 * been shortened by heat. */
902 used = w->outfit->u.bem.duration -
903 w->timer * ( 1. - pilot_heatAccuracyMod( w->heat_T ) );
904
905 w->timer = rate_mod * MAX( w->outfit->u.bem.min_delay,
906 ( used / w->outfit->u.bem.duration ) *
907 outfit_delay( w->outfit ) );
908 w->u.beamid = 0;
909 w->state = PILOT_OUTFIT_OFF;
910}
911
920double pilot_weapFlyTime( const Outfit *o, const Pilot *parent, const vec2 *pos,
921 const vec2 *vel )
922{
923 vec2 approach_vector, relative_location, orthoradial_vector;
924 double speed, radial_speed, orthoradial_speed, dist, t;
925
926 dist = vec2_dist( &parent->solid.pos, pos );
927
928 /* Beam weapons */
929 if ( outfit_isBeam( o ) ) {
930 double range_mod;
931 if ( o->type == OUTFIT_TYPE_BEAM )
932 range_mod = parent->stats.fwd_range * parent->stats.weapon_range;
933 else
934 range_mod = parent->stats.tur_range * parent->stats.weapon_range;
935
936 if ( dist <= o->u.bem.range * range_mod )
937 return INFINITY;
938 return -1.; /* Impossible. */
939 }
940
941 /* A bay doesn't have range issues */
942 if ( outfit_isFighterBay( o ) )
943 return -1.;
944
945 /* Missiles use absolute velocity while bolts and unguided rockets use
946 * relative vel */
947 if ( outfit_isLauncher( o ) && o->u.lau.ai != AMMO_AI_UNGUIDED )
948 vec2_cset( &approach_vector, -vel->x, -vel->y );
949 else
950 vec2_cset( &approach_vector, VX( parent->solid.vel ) - vel->x,
951 VY( parent->solid.vel ) - vel->y );
952
953 speed = outfit_speed( o );
954 if ( outfit_isLauncher( o ) )
955 speed *= parent->stats.launch_speed;
956
957 /* Get the vector : shooter -> target */
958 vec2_cset( &relative_location, pos->x - VX( parent->solid.pos ),
959 pos->y - VY( parent->solid.pos ) );
960
961 /* Get the orthogonal vector */
962 vec2_cset( &orthoradial_vector, VY( parent->solid.pos ) - pos->y,
963 pos->x - VX( parent->solid.pos ) );
964
965 radial_speed = vec2_dot( &approach_vector, &relative_location );
966 radial_speed = radial_speed / VMOD( relative_location );
967
968 orthoradial_speed = vec2_dot( &approach_vector, &orthoradial_vector );
969 orthoradial_speed = orthoradial_speed / VMOD( relative_location );
970
971 if ( ( ( speed * speed -
972 VMOD( approach_vector ) * VMOD( approach_vector ) ) != 0 ) &&
973 ( speed * speed - orthoradial_speed * orthoradial_speed ) > 0 )
974 t = dist *
975 ( sqrt( speed * speed - orthoradial_speed * orthoradial_speed ) -
976 radial_speed ) /
977 ( speed * speed - VMOD( approach_vector ) * VMOD( approach_vector ) );
978 else
979 return INFINITY;
980
981 /* if t < 0, try the other solution */
982 if ( t < 0 )
983 t = -dist *
984 ( sqrt( speed * speed - orthoradial_speed * orthoradial_speed ) +
985 radial_speed ) /
986 ( speed * speed - VMOD( approach_vector ) * VMOD( approach_vector ) );
987
988 /* if t still < 0, no solution */
989 if ( t < 0 )
990 return INFINITY;
991
992 return t;
993}
994
1003{
1004 Pilot *pt = pilot_getTarget( p );
1005 if ( pt != NULL ) {
1006 wt->type = TARGET_PILOT;
1007 wt->u.id = pt->id;
1008 return pt;
1009 } else if ( p->nav_asteroid != -1 ) {
1010 wt->type = TARGET_ASTEROID;
1011 wt->u.ast.anchor = p->nav_anchor;
1012 wt->u.ast.asteroid = p->nav_asteroid;
1013 return NULL;
1014 }
1015 wt->type = TARGET_NONE;
1016 return NULL;
1017}
1018
1023static int pilot_shootWeaponSetOutfit( Pilot *p, const Outfit *o,
1024 const Target *target, double time,
1025 int aim )
1026{
1027 int is_launcher, is_bay;
1028 double rate_mod, energy_mod;
1029 int maxp, minh;
1030 double q, maxt;
1031
1032 /* Stores if it is a launcher or bay. */
1033 is_launcher = outfit_isLauncher( o );
1034 is_bay = outfit_isFighterBay( o );
1035
1036 /* Calculate rate modifier. */
1037 pilot_getRateMod( &rate_mod, &energy_mod, p, o );
1038
1039 /* Find optimal outfit, coolest that can fire. */
1040 minh = -1;
1041 maxt = 0.;
1042 maxp = -1;
1043 q = 0.;
1044 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
1045 const PilotOutfitSlot *pos = p->outfits[i];
1046
1047 /* Only matching outfits. */
1048 if ( pos->outfit != o )
1049 continue;
1050
1051 /* Must be on. */
1052 if ( !( pos->flags & PILOTOUTFIT_ISON ) )
1053 continue;
1054
1055 /* Launcher only counts with ammo. */
1056 if ( ( is_launcher || is_bay ) && ( pos->u.ammo.quantity <= 0 ) )
1057 continue;
1058
1059 /* Get coolest that can fire. */
1060 if ( pos->timer <= 0. ) {
1061 if ( is_launcher ) {
1062 if ( ( minh < 0 ) ||
1063 ( p->outfits[minh]->u.ammo.quantity < pos->u.ammo.quantity ) )
1064 minh = i;
1065 } else {
1066 if ( ( minh < 0 ) || ( p->outfits[minh]->heat_T > pos->heat_T ) )
1067 minh = i;
1068 }
1069 }
1070
1071 /* Save some stuff. */
1072 if ( ( maxp < 0 ) || ( pos->timer > maxt ) ) {
1073 maxp = i;
1074 maxt = pos->timer;
1075 }
1076 q += 1.;
1077 }
1078
1079 /* No weapon can fire. */
1080 if ( minh < 0 )
1081 return 0;
1082
1083 /* Only fire if the last weapon to fire fired more than (q-1)/q ago. */
1084 if ( maxt > rate_mod * outfit_delay( o ) * ( ( q - 1. ) / q ) )
1085 return 0;
1086
1087 /* Shoot the weapon. */
1088 return pilot_shootWeapon( p, p->outfits[minh], target, time, aim );
1089}
1090
1104 double time, int aim )
1105{
1106 vec2 vp, vv;
1107 double rate_mod, energy_mod;
1108 double energy;
1109
1110 /* Make sure weapon has outfit. */
1111 if ( w->outfit == NULL )
1112 return 0;
1113
1114 /* check to see if weapon is ready */
1115 if ( w->timer > 0. )
1116 return 0;
1117
1118 /* Calculate rate modifier. */
1119 pilot_getRateMod( &rate_mod, &energy_mod, p, w->outfit );
1120
1121 /* Get weapon mount position. */
1122 pilot_getMount( p, w, &vp );
1123
1124 /* Modify velocity to take into account the rotation. */
1125 vec2_cset( &vv, p->solid.vel.x - vp.y * p->solid.dir_vel,
1126 p->solid.vel.y + vp.x * p->solid.dir_vel );
1127
1128 /* Get absolute weapon mount position. */
1129 vp.x += p->solid.pos.x;
1130 vp.y += p->solid.pos.y;
1131
1132 /* Regular bolt weapons. */
1133 if ( outfit_isBolt( w->outfit ) ) {
1134 /* enough energy? */
1135 if ( outfit_energy( w->outfit ) * energy_mod > p->energy )
1136 return 0;
1137
1138 /* Lua test. */
1139 if ( ( aim >= 0 ) && ( w->outfit->lua_onshoot != LUA_NOREF ) &&
1140 !pilot_outfitLOnshoot( p, w ) )
1141 return 0;
1142
1143 energy = outfit_energy( w->outfit ) * energy_mod;
1144 p->energy -= energy;
1145 pilot_heatAddSlot( p, w );
1146 if ( !outfit_isProp( w->outfit, OUTFIT_PROP_SHOOT_DRY ) ) {
1147 for ( int i = 0; i < w->outfit->u.blt.shots; i++ ) {
1148 weapon_add( w, NULL, p->solid.dir, &vp, &vv, p, target, time, aim );
1149 }
1150 }
1151 }
1152
1153 /*
1154 * Beam weapons.
1155 */
1156 else if ( outfit_isBeam( w->outfit ) ) {
1157 /* Don't fire if the existing beam hasn't been destroyed yet. */
1158 if ( w->u.beamid > 0 )
1159 return 0;
1160
1161 /* Check if enough energy to last a second. */
1162 if ( outfit_energy( w->outfit ) * energy_mod > p->energy )
1163 return 0;
1164
1165 /* Lua test. */
1166 if ( ( aim >= 0 ) && ( w->outfit->lua_onshoot != LUA_NOREF ) &&
1167 !pilot_outfitLOnshoot( p, w ) )
1168 return 0;
1169
1171 w->state = PILOT_OUTFIT_ON;
1172 if ( !outfit_isProp( w->outfit, OUTFIT_PROP_SHOOT_DRY ) ) {
1173 w->u.beamid =
1174 beam_start( w, p->solid.dir, &vp, &p->solid.vel, p, target, aim );
1175 }
1176
1177 w->timer = w->outfit->u.bem.duration;
1178
1179 return 1; /* Return early due to custom timer logic. */
1180 }
1181
1182 /*
1183 * Missile launchers
1184 */
1185 else if ( outfit_isLauncher( w->outfit ) ) {
1186 Target wt;
1187 /* Must have ammo left. */
1188 if ( w->u.ammo.quantity <= 0 )
1189 return 0;
1190
1191 /* enough energy? */
1192 if ( outfit_energy( w->outfit ) * energy_mod > p->energy )
1193 return 0;
1194
1195 /* Shooter can't be the target - safety check for the player.p */
1196 if ( target == NULL ) {
1197 pilot_weaponTarget( p, &wt );
1198 target = &wt;
1199 }
1200 // if ((w->outfit->u.lau.ai != AMMO_AI_UNGUIDED) &&
1201 // !((target->type==TARGET_PILOT) || (target->type==TARGET_ASTEROID)))
1202 if ( ( w->outfit->u.lau.ai != AMMO_AI_UNGUIDED ) &&
1203 ( target->type != TARGET_PILOT ) )
1204 return 0;
1205
1206 /* Lua test. */
1207 if ( ( aim >= 0 ) && ( w->outfit->lua_onshoot != LUA_NOREF ) &&
1208 !pilot_outfitLOnshoot( p, w ) )
1209 return 0;
1210
1211 energy = outfit_energy( w->outfit ) * energy_mod;
1212 p->energy -= energy;
1213 pilot_heatAddSlot( p, w );
1214 if ( !outfit_isProp( w->outfit, OUTFIT_PROP_SHOOT_DRY ) ) {
1215 for ( int i = 0; i < w->outfit->u.lau.shots; i++ )
1216 weapon_add( w, NULL, p->solid.dir, &vp, &vv, p, target, time, aim );
1217 }
1218
1219 pilot_rmAmmo( p, w, 1 );
1220
1221 /* Make the AI aware a seeker has been shot */
1222 if ( outfit_isSeeker( w->outfit ) )
1223 p->shoot_indicator = 1;
1224
1225 /* If last ammo was shot, update the range */
1226 if ( w->u.ammo.quantity <= 0 ) {
1227 for ( int j = 0; j < PILOT_WEAPON_SETS; j++ )
1228 pilot_weapSetUpdateRange( p, &p->weapon_sets[j] );
1229 }
1230 }
1231
1232 /*
1233 * Fighter bays.
1234 */
1235 else if ( outfit_isFighterBay( w->outfit ) ) {
1236 int dockslot = -1;
1237
1238 /* Must have ammo left. */
1239 if ( w->u.ammo.quantity <= 0 )
1240 return 0;
1241
1242 /* Lua test. */
1243 if ( ( aim >= 0 ) && ( w->outfit->lua_onshoot != LUA_NOREF ) &&
1244 !pilot_outfitLOnshoot( p, w ) )
1245 return 0;
1246
1247 /* Get index of outfit slot */
1248 for ( int j = 0; j < array_size( p->outfits ); j++ ) {
1249 if ( p->outfits[j] == w )
1250 dockslot = j;
1251 }
1252
1253 /* Create the escort. */
1254 if ( !outfit_isProp( w->outfit, OUTFIT_PROP_SHOOT_DRY ) )
1255 escort_create( p, w->outfit->u.bay.ship, &vp, &p->solid.vel,
1256 p->solid.dir, ESCORT_TYPE_BAY, 1, dockslot );
1257
1258 w->u.ammo.quantity -= 1; /* we just shot it */
1259 p->mass_outfit -= w->outfit->u.bay.ship_mass;
1260 pilot_updateMass( p );
1261 } else
1262 WARN( _( "Shooting unknown weapon type: %s" ), w->outfit->name );
1263
1264 /* Reset timer. */
1265 w->timer += rate_mod * outfit_delay( w->outfit );
1266
1267 /* Reset autonav if is player. */
1268 // if ( pilot_isPlayer( p ) )
1269 // player_autonavReset( 1. );
1270
1271 return 1;
1272}
1273
1283void pilot_getRateMod( double *rate_mod, double *energy_mod, const Pilot *p,
1284 const Outfit *o )
1285{
1286 switch ( o->type ) {
1287 case OUTFIT_TYPE_BOLT:
1288 case OUTFIT_TYPE_BEAM:
1289 *rate_mod = 1. / ( p->stats.fwd_firerate *
1290 p->stats.weapon_firerate ); /* Invert. */
1291 *energy_mod = p->stats.fwd_energy * p->stats.weapon_energy;
1292 break;
1293 case OUTFIT_TYPE_TURRET_BOLT:
1294 case OUTFIT_TYPE_TURRET_BEAM:
1295 *rate_mod = 1. / ( p->stats.tur_firerate *
1296 p->stats.weapon_firerate ); /* Invert. */
1297 *energy_mod = p->stats.tur_energy * p->stats.weapon_energy;
1298 break;
1299
1300 case OUTFIT_TYPE_LAUNCHER:
1301 case OUTFIT_TYPE_TURRET_LAUNCHER:
1302 *rate_mod =
1303 1. / ( p->stats.launch_rate * p->stats.weapon_firerate ); /* Invert. */
1304 *energy_mod = p->stats.launch_energy * p->stats.weapon_energy;
1305 break;
1306
1307 case OUTFIT_TYPE_FIGHTER_BAY:
1308 *rate_mod = 1. / p->stats.fbay_rate;
1309 *energy_mod = 1.;
1310 break;
1311
1312 default:
1313 *rate_mod = 1.;
1314 *energy_mod = 1.;
1315 break;
1316 }
1317}
1318
1325{
1326 for ( int i = 0; i < PILOT_WEAPON_SETS; i++ ) {
1327 PilotWeaponSet *ws = pilot_weapSet( p, i );
1328 array_erase( &ws->slots, array_begin( ws->slots ),
1329 array_end( ws->slots ) );
1330 }
1331}
1332
1346{
1347 int idnext = 2;
1348 int hasfb = 0;
1349 int haspd = 0;
1350 int isplayer = pilot_isPlayer( p );
1351
1352 /* Clear weapons. */
1353 pilot_weaponClear( p );
1354
1355 /* Set modes. */
1356 pilot_weapSetType( p, 0, WEAPSET_TYPE_HOLD ); /* Primary. */
1357 pilot_weapSetType( p, 1, WEAPSET_TYPE_HOLD ); /* Secondary. */
1358
1359 if ( isplayer ) {
1360 for ( int i = 2; i < PILOT_WEAPON_SETS; i++ )
1361 pilot_weapSetType( p, i, WEAPSET_TYPE_DEFAULT );
1362
1363 /* See if fighter bays or point defense. */
1364 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
1365 PilotOutfitSlot *slot = p->outfits[i];
1366 const Outfit *o = slot->outfit;
1367
1368 if ( o == NULL )
1369 continue;
1370 if ( !pilot_slotIsToggleable( slot ) ) /* Ignore non-active. */
1371 continue;
1372
1373 if ( outfit_isFighterBay( o ) )
1374 hasfb = 1;
1375 else if ( outfit_isProp( o, OUTFIT_PROP_WEAP_POINTDEFENSE ) )
1376 haspd = 1;
1377 }
1378
1379 /* Determine weapon ids so they get together. */
1380 if ( haspd ) {
1381 haspd = 2; /* 0 weapset. */
1382 idnext++;
1383 }
1384 if ( hasfb ) {
1385 hasfb = 2 + !!haspd; /* 0 or 1 weapset. */
1386 idnext++;
1387 }
1388 } else {
1389 /* Set weapon sets. */
1390 for ( int i = 2; i < PILOT_WEAPON_SETS; i++ )
1391 pilot_weapSetType( p, i, WEAPSET_TYPE_TOGGLE );
1392 }
1393
1394 /* Iterate through all the outfits. */
1395 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
1396 PilotOutfitSlot *slot = p->outfits[i];
1397 const Outfit *o = slot->outfit;
1398 int id;
1399
1400 if ( o == NULL )
1401 continue;
1402 if ( !pilot_slotIsToggleable( slot ) ) /* Ignore non-active. */
1403 continue;
1404
1405 if ( isplayer ) {
1406 /* Manually defined group preempts others. */
1407 if ( o->group )
1408 id = o->group +
1409 2; /* Start counting after primary /secondary weapon sets. */
1410 else if ( outfit_isSecondary( o ) )
1411 id = 1; /* Secondary override. */
1412 /* Bolts and beams. */
1413 else if ( !outfit_isProp( o, OUTFIT_PROP_WEAP_POINTDEFENSE ) &&
1414 ( outfit_isBolt( o ) || outfit_isBeam( o ) ||
1415 ( outfit_isLauncher( o ) && !outfit_isSeeker( o ) ) ) )
1416 id = 0; /* Primary. */
1417 /* Seekers. */
1418 else if ( outfit_isLauncher( o ) && outfit_isSeeker( o ) )
1419 id = 1; /* Secondary. */
1420 /* Point defense. */
1421 else if ( outfit_isProp( o, OUTFIT_PROP_WEAP_POINTDEFENSE ) )
1422 id = haspd;
1423 /* Fighter bays. */
1424 else if ( outfit_isFighterBay( o ) )
1425 id = hasfb;
1426 /* Rest just incrcement. */
1427 else {
1428 id = idnext++;
1429 /* Ran out of space. */
1430 if ( id >= PILOT_WEAPON_SETS )
1431 break;
1432 }
1433 } else {
1434 if ( outfit_isSecondary( o ) )
1435 id = 1; /* Secondary override. */
1436 /* Bolts and beams. */
1437 else if ( outfit_isBolt( o ) || outfit_isBeam( o ) ||
1438 ( outfit_isLauncher( o ) && !outfit_isSeeker( o ) ) )
1439 id = 0; /* Primary. */
1440 /* Point defense. */
1441 else if ( outfit_isProp( o, OUTFIT_PROP_WEAP_POINTDEFENSE ) )
1442 id = 2;
1443 /* Fighter bays. */
1444 else if ( outfit_isFighterBay( o ) )
1445 id = 3;
1446 else
1447 continue;
1448 }
1449
1450 /* Add to its base group. */
1451 pilot_weapSetAdd( p, id, slot );
1452
1453 /* Add to additional special groups if not player. */
1454 if ( !isplayer ) {
1455 if ( outfit_isTurret( o ) )
1456 pilot_weapSetAdd( p, 4, slot );
1457 if ( outfit_isLauncher( o ) && outfit_isSeeker( o ) ) {
1458 pilot_weapSetAdd( p, 5, slot );
1459 if ( outfit_isTurret( o ) )
1460 pilot_weapSetAdd( p, 6, slot );
1461 }
1462 }
1463 }
1464
1465 /* All should be inrange. */
1466 for ( int i = 0; i < PILOT_WEAPON_SETS; i++ ) {
1467 pilot_weapSetInrange( p, i, 1 );
1468 /* Update range and speed (at 0)*/
1469 pilot_weapSetUpdateRange( p, &p->weapon_sets[i] );
1470 }
1471
1472 /* Update all outfits. */
1473 pilot_weaponSafe( p );
1474}
1475
1482{
1483 for ( int j = 0; j < PILOT_WEAPON_SETS; j++ ) {
1484 PilotWeaponSet *ws = &p->weapon_sets[j];
1485
1486 /* Update range. */
1487 pilot_weapSetUpdateRange( p, ws );
1488 }
1489}
1490
1499int pilot_outfitOff( Pilot *p, PilotOutfitSlot *o, int natural )
1500{
1501 /* Must be equipped, not disabled, not cooling down. */
1502 if ( o->outfit == NULL || ( pilot_isDisabled( p ) ) ||
1503 ( pilot_isFlag( p, PILOT_COOLDOWN ) ) )
1504 return 0;
1505
1506 if ( outfit_isAfterburner( o->outfit ) ) { /* Afterburners */
1507 if ( ( o->outfit->lua_ontoggle != LUA_NOREF ) &&
1508 !pilot_outfitLOntoggle( p, o, 0, natural ) )
1509 return 0;
1510 o->flags &= ~PILOTOUTFIT_DYNAMIC_FLAGS;
1512
1513 } else if ( outfit_isBeam( o->outfit ) ) {
1514 if ( ( o->outfit->lua_ontoggle != LUA_NOREF ) &&
1515 !pilot_outfitLOntoggle( p, o, 0, natural ) )
1516 return 0;
1517 o->flags &= ~PILOTOUTFIT_DYNAMIC_FLAGS;
1518
1519 /* Beams use stimer to represent minimum time until shutdown. */
1520 if ( o->u.beamid > 0 ) {
1521 beam_end( o->u.beamid );
1522 pilot_stopBeam( p, o ); /* Sets the state. */
1523 } else
1524 o->state = PILOT_OUTFIT_OFF;
1525 } else if ( !( o->flags & PILOTOUTFIT_TOGGLEABLE ) )
1526 /* Case of a mod we can't toggle. */
1527 return 0;
1528 else if ( o->outfit->lua_ontoggle != LUA_NOREF ) {
1529 int ret = pilot_outfitLOntoggle( p, o, 0, natural );
1530 if ( ret ) {
1531 if ( outfit_isWeapon( o->outfit ) )
1532 o->state = PILOT_OUTFIT_OFF;
1533 o->flags &= ~( PILOTOUTFIT_DYNAMIC_FLAGS | PILOTOUTFIT_ISON );
1534 }
1535 return ret;
1536 } else {
1537 o->stimer = outfit_cooldown( o->outfit );
1538 if ( o->stimer < 0. )
1539 o->state = PILOT_OUTFIT_OFF;
1540 else
1541 o->state = PILOT_OUTFIT_COOLDOWN;
1542 o->flags &= ~PILOTOUTFIT_DYNAMIC_FLAGS;
1543 }
1544
1545 return 1;
1546}
1547
1556{
1557 if ( pos->outfit == NULL )
1558 return 0;
1559 if ( outfit_isAfterburner( pos->outfit ) ) {
1560 int ret = pilot_afterburn( p );
1561 if ( ret )
1562 pos->state = PILOT_OUTFIT_ON;
1563 return ret;
1564 } else if ( pos->outfit->lua_ontoggle != LUA_NOREF ) {
1565 int ret = pilot_outfitLOntoggle( p, pos, 1, 1 );
1566 if ( ret && outfit_isWeapon( pos->outfit ) )
1567 pos->state = PILOT_OUTFIT_ON;
1568 return ret;
1569 } else {
1570 pos->state = PILOT_OUTFIT_ON;
1571 pos->stimer = outfit_duration( pos->outfit ) * p->stats.cooldown_mod;
1572 }
1573
1574 return 1;
1575}
1576
1584{
1585 int nchg = 0;
1586 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
1587 PilotOutfitSlot *pos = p->outfits[i];
1588 /* Picky about our outfits. */
1589 if ( pos->outfit == NULL )
1590 continue;
1591 if ( pos->state == PILOT_OUTFIT_ON )
1592 nchg += pilot_outfitOff( p, pos, 0 );
1593 }
1594 return ( nchg > 0 );
1595}
1596
1604{
1605 int nchg = 0;
1606 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
1607 PilotOutfitSlot *o = p->outfits[i];
1608 /* Picky about our outfits. */
1609 if ( o->outfit == NULL )
1610 continue;
1611 if ( outfit_isProp( o->outfit, OUTFIT_PROP_STEALTH_ON ) )
1612 continue;
1613 if ( o->state == PILOT_OUTFIT_ON )
1614 nchg += pilot_outfitOff( p, o, 0 );
1615 }
1616 return ( nchg > 0 );
1617}
1618
1623{
1624 if ( p == NULL )
1625 return 0;
1626
1627 if ( pilot_isFlag( p, PILOT_HYP_PREP ) ||
1628 pilot_isFlag( p, PILOT_HYPERSPACE ) ||
1629 pilot_isFlag( p, PILOT_LANDING ) || pilot_isFlag( p, PILOT_TAKEOFF ) ||
1630 pilot_isDisabled( p ) || pilot_isFlag( p, PILOT_COOLDOWN ) )
1631 return 0;
1632
1633 /* Not under manual control if is player. */
1634 if ( pilot_isFlag( p, PILOT_MANUAL_CONTROL ) && pilot_isPlayer( p ) )
1635 return 0;
1636
1638 if ( p->afterburner == NULL )
1639 return 0;
1640
1641 /* Needs at least enough energy to afterburn fo 0.5 seconds. */
1642 if ( p->energy < p->afterburner->outfit->u.afb.energy * 0.5 )
1643 return 0;
1644
1645 /* The afterburner only works if its efficiency is high enough. */
1647 p->afterburner->heat_T, p->afterburner->outfit->overheat_min,
1648 p->afterburner->outfit->overheat_max ) < 0.1 ) {
1649 if ( pilot_isPlayer( p ) )
1650 player_message( _( "#r%s is overheating!#0" ),
1651 _( p->afterburner->outfit->name ) );
1652 return 0;
1653 }
1654
1655 /* Turn it on. */
1656 if ( p->afterburner->state == PILOT_OUTFIT_OFF ) {
1657 p->afterburner->state = PILOT_OUTFIT_ON;
1658 p->afterburner->stimer = outfit_duration( p->afterburner->outfit );
1659 pilot_setFlag( p, PILOT_AFTERBURNER );
1660 if ( !outfit_isProp( p->afterburner->outfit, OUTFIT_PROP_STEALTH_ON ) )
1661 pilot_destealth( p ); /* No afterburning stealth. */
1662 pilot_calcStats( p );
1663
1664 /* @todo Make this part of a more dynamic activated outfit sound system.
1665 */
1666 sound_playPos( p->afterburner->outfit->u.afb.sound_on, p->solid.pos.x,
1667 p->solid.pos.y, p->solid.vel.x, p->solid.vel.y );
1668 }
1669
1670 if ( pilot_isPlayer( p ) ) {
1671 double afb_mod = MIN( 1., pilot_massFactor( player.p ) );
1672 spfx_shake( afb_mod * player.p->afterburner->outfit->u.afb.rumble );
1673 }
1674 return 1;
1675}
1676
1681{
1682 if ( p == NULL )
1683 return;
1684 if ( p->afterburner == NULL )
1685 return;
1686
1687 if ( p->afterburner->state == PILOT_OUTFIT_ON ) {
1688 p->afterburner->state = PILOT_OUTFIT_OFF;
1689 pilot_rmFlag( p, PILOT_AFTERBURNER );
1690 pilot_calcStats( p );
1691
1692 /* @todo Make this part of a more dynamic activated outfit sound system.
1693 */
1694 sound_playPos( p->afterburner->outfit->u.afb.sound_off, p->solid.pos.x,
1695 p->solid.pos.y, p->solid.vel.x, p->solid.vel.y );
1696 }
1697}
1698
1702void ws_copy( PilotWeaponSet dest[PILOT_WEAPON_SETS],
1703 const PilotWeaponSet src[PILOT_WEAPON_SETS] )
1704{
1705 ws_free( dest );
1706 memcpy( dest, src, sizeof( PilotWeaponSet ) * PILOT_WEAPON_SETS );
1707 for ( int i = 0; i < PILOT_WEAPON_SETS; i++ )
1708 dest[i].slots = array_copy( PilotWeaponSetOutfit, src[i].slots );
1709}
1710
1714void ws_free( PilotWeaponSet ws[PILOT_WEAPON_SETS] )
1715{
1716 for ( int i = 0; i < PILOT_WEAPON_SETS; i++ ) {
1717 array_free( ws[i].slots );
1718 ws[i].slots = NULL;
1719 }
1720}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
#define array_copy(basic_type, ptr_array)
Returns a shallow copy of the input array.
Definition array.h:230
#define array_end(array)
Returns a pointer to the end of the reserved memory space.
Definition array.h:214
#define array_erase(ptr_array, first, last)
Erases elements in interval [first, last).
Definition array.h:148
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:179
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:122
#define array_begin(array)
Returns a pointer to the beginning of the reserved memory space.
Definition array.h:206
#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:105
void player_message(const char *fmt,...)
Adds a mesg to the queue to be displayed on screen.
Definition gui.c:353
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:39
#define pow2(x)
Definition naev.h:53
#define MAX(x, y)
Definition naev.h:37
int outfit_isSecondary(const Outfit *o)
Checks if outfit has the secondary flag set.
Definition outfit.c:747
double outfit_speed(const Outfit *o)
Gets the outfit's speed.
Definition outfit.c:908
int outfit_isBeam(const Outfit *o)
Checks if outfit is a beam type weapon.
Definition outfit.c:639
int outfit_isLauncher(const Outfit *o)
Checks if outfit is a weapon launcher.
Definition outfit.c:649
int outfit_isSeeker(const Outfit *o)
Checks if outfit is a seeking weapon.
Definition outfit.c:659
int outfit_isWeapon(const Outfit *o)
Checks to see if an outfit is a weapon.
Definition outfit.c:603
int outfit_isFighterBay(const Outfit *o)
Checks if outfit is a fighter bay.
Definition outfit.c:701
int outfit_isAfterburner(const Outfit *o)
Checks if outfit is an afterburner.
Definition outfit.c:692
int outfit_isTurret(const Outfit *o)
Checks if outfit is a turret class weapon.
Definition outfit.c:672
double outfit_duration(const Outfit *o)
Gets the outfit's duration.
Definition outfit.c:1030
double outfit_cooldown(const Outfit *o)
Gets the outfit's cooldown.
Definition outfit.c:1044
int outfit_isBolt(const Outfit *o)
Checks if outfit is bolt type weapon.
Definition outfit.c:629
double outfit_energy(const Outfit *o)
Gets the outfit's energy usage.
Definition outfit.c:863
double outfit_delay(const Outfit *o)
Gets the outfit's delay.
Definition outfit.c:834
Pilot * pilot_getTarget(Pilot *p)
Gets the target of a pilot using a fancy caching system.
Definition pilot.c:655
void pilot_cooldownEnd(Pilot *p, const char *reason)
Terminates active cooldown.
Definition pilot.c:986
void pilot_destealth(Pilot *p)
Destealths a pilot.
Definition pilot_ew.c:585
void pilot_heatAddSlot(const Pilot *p, PilotOutfitSlot *o)
Adds heat to an outfit slot.
Definition pilot_heat.c:135
double pilot_heatEfficiencyMod(double T, double Tb, double Tc)
Returns a 0:1 modifier representing efficiency (1. being normal).
Definition pilot_heat.c:237
double pilot_heatAccuracyMod(double T)
Returns a 0:1 modifier representing accuracy (0. being normal).
Definition pilot_heat.c:281
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.
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_outfitLOntoggle(const Pilot *pilot, PilotOutfitSlot *po, int on, int natural)
Handle the manual toggle of an outfit.
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.
void pilot_weapSetAdd(Pilot *p, int id, const PilotOutfitSlot *o)
Adds an outfit to a weapon set.
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.
void pilot_weapSetInrange(Pilot *p, int id, int inrange)
Changes the weapon set inrange property.
int pilot_afterburn(Pilot *p)
Activate the afterburner.
void pilot_weaponSafe(Pilot *p)
Sets the weapon set as safe.
void pilot_weapSetManual(Pilot *p, int id, int manual)
Changes the weapon set manual property.
void pilot_afterburnOver(Pilot *p)
Deactivates the afterburner.
static int pilot_shootWeaponSetOutfit(Pilot *p, const Outfit *o, const Target *target, double time, int aim)
Calculates and shoots the appropriate weapons in a weapon set matching an outfit.
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 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_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.
double pilot_weapSetRangeMin(Pilot *p, int id)
Gets the minimum range of the current pilot 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.
double pilot_weapSetSpeed(Pilot *p, int id)
Gets the speed of the current pilot weapon set.
double pilot_weapSetAmmo(Pilot *p, int id)
Gets the ammo of the current pilot 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.
int pilot_outfitOn(Pilot *p, PilotOutfitSlot *pos)
Enable a given active outfit.
int pilot_outfitOff(Pilot *p, PilotOutfitSlot *o, int natural)
Disables a given active outfit.
void pilot_weapSetUpdateOutfitState(Pilot *p)
Updates the local state of all the pilot's outfits based on the weapon sets.
double pilot_weapSetRange(Pilot *p, int id)
Gets the range 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.
int pilot_outfitOffAllStealth(Pilot *p)
Disables all active outfits for a pilot.
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_weapSetFree(Pilot *p)
Frees a pilot's weapon sets.
int pilot_weapSetPress(Pilot *p, int id, int type)
Handles a weapon set press.
const char * pilot_weapSetName(Pilot *p, int id)
Gets the name of a weapon set.
void pilot_weapSetType(Pilot *p, int id, WeaponSetType type)
Changes the weapon sets mode.
void pilot_weapSetUpdateStats(Pilot *p)
Update the weapon sets given pilot stat changes.
Player_t player
Definition player.c:77
int sound_playPos(int sound, double px, double py, double vx, double vy)
Plays a sound based on position.
Definition sound.c:832
void spfx_shake(double mod)
Increases the current rumble level.
Definition spfx.c:966
double speed
Definition outfit.h:183
OutfitAmmoAI ai
Definition outfit.h:271
OutfitSlotType type
Definition outfit.h:142
A ship outfit, depends radically on the type.
Definition outfit.h:372
int lua_ontoggle
Definition outfit.h:427
OutfitLauncherData lau
Definition outfit.h:456
OutfitBoltData blt
Definition outfit.h:454
OutfitType type
Definition outfit.h:452
unsigned int group
Definition outfit.h:407
union Outfit::@125206365173064275134261143013340144375262037177 u
char * name
Definition outfit.h:373
double lockon_timer
Definition pilot.h:117
Stores an outfit the pilot has.
Definition pilot.h:145
unsigned int beamid
Definition pilot.h:172
PilotOutfitAmmo ammo
Definition pilot.h:174
double stimer
Definition pilot.h:161
double heat_T
Definition pilot.h:154
PilotOutfitState state
Definition pilot.h:160
double timer
Definition pilot.h:162
ShipOutfitSlot * sslot
Definition pilot.h:151
const Outfit * outfit
Definition pilot.h:149
A pilot Weapon Set Outfit.
Definition pilot.h:187
A weapon set represents a set of weapons that have an action.
Definition pilot.h:207
double range
Definition pilot.h:216
double range_min
Definition pilot.h:215
PilotWeaponSetOutfit * slots
Definition pilot.h:210
double speed
Definition pilot.h:217
WeaponSetType type
Definition pilot.h:208
The representation of an in-game pilot.
Definition pilot.h:263
ShipStats stats
Definition pilot.h:348
unsigned int id
Definition pilot.h:264
Solid solid
Definition pilot.h:275
OutfitSlot slot
Definition ship.h:72
double launch_speed
Definition shipstats.h:306
double fwd_range
Definition shipstats.h:324
double weapon_range
Definition shipstats.h:291
double tur_range
Definition shipstats.h:333
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:45
double y
Definition vec2.h:47
double x
Definition vec2.h:46
double weapon_targetFlyTime(const Outfit *o, const Pilot *p, const Target *t)
Gets the fly time for a weapon target.
Definition weapon.c:2715
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:2755
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:2689
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:2051
void beam_end(unsigned int beam)
Ends a beam weapon.
Definition weapon.c:2779