naev 0.12.5
pilot_outfit.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include "naev.h"
12
13#include "array.h"
14#include "difficulty.h"
15#include "escort.h"
16#include "gui.h"
17#include "log.h"
18#include "nlua.h"
19#include "nlua_outfit.h"
20#include "nlua_pilot.h"
21#include "nlua_pilotoutfit.h"
22#include "nstring.h"
23#include "ntracing.h"
24#include "outfit.h"
25#include "pilot.h"
26#include "player.h"
27#include "slots.h"
28#include "space.h"
29
30static int stealth_break = 0;
31
32/*
33 * Prototypes.
34 */
35static void pilot_calcStatsSlot( Pilot *pilot, PilotOutfitSlot *slot );
36static const char *outfitkeytostr( OutfitKey key );
37
51 double *a, double dt )
52{
53 double arc, max;
54 int locked;
55
56 /* No target. */
57 if ( wt->type == TARGET_NONE )
58 return;
59
60 /* Nota seeker. */
61 if ( !outfit_isSeeker( o->outfit ) )
62 return;
63
64 /* Check arc. */
65 arc = o->outfit->u.lau.arc;
66 if ( arc > 0. ) {
67
68 /* We use an external variable to set and update the angle if necessary.
69 */
70 if ( *a < 0. ) {
71 double x, y, ang;
72 if ( wt->type == TARGET_PILOT ) {
73 x = t->solid.pos.x - p->solid.pos.x;
74 y = t->solid.pos.y - p->solid.pos.y;
75 } else if ( wt->type == TARGET_ASTEROID ) {
76 const Asteroid *ast = &cur_system->asteroids[wt->u.ast.anchor]
77 .asteroids[wt->u.ast.asteroid];
78 x = ast->sol.pos.x - p->solid.pos.x;
79 y = ast->sol.pos.y - p->solid.pos.y;
80 } else {
81 x = y = 0.;
82 }
83 ang = ANGLE( x, y );
84 *a = FABS( angle_diff( ang, p->solid.dir ) );
85 }
86
87 /* Decay if not in arc. */
88 if ( *a > arc ) {
89 /* When a lock is lost, immediately gain half the lock timer.
90 * This is meant as an incentive for the aggressor not to lose the
91 * lock, and for the target to try and break the lock. */
92 double old = o->u.ammo.lockon_timer;
93 /* Limit decay to the lock-on time for this launcher. */
94 max = o->outfit->u.lau.lockon;
95 o->u.ammo.lockon_timer += dt;
96 if ( ( old <= 0. ) && ( o->u.ammo.lockon_timer > 0. ) )
97 o->u.ammo.lockon_timer += o->outfit->u.lau.lockon / 2.;
98
99 /* Cap at max. */
100 if ( o->u.ammo.lockon_timer > max )
101 o->u.ammo.lockon_timer = max;
102
103 /* Out of arc. */
104 o->u.ammo.in_arc = 0;
105 return;
106 }
107 }
108
109 /* In arc. */
110 o->u.ammo.in_arc = 1;
111 locked = ( o->u.ammo.lockon_timer < 0. );
112
113 /* Lower timer. When the timer reaches zero, the lock is established. */
114 max = -o->outfit->u.lau.lockon / 3.;
115 if ( o->u.ammo.lockon_timer > max ) {
116 /* Targetting is linear and can't be faster than the time specified (can
117 * be slower though). */
118 double mod = pilot_ewWeaponTrack( p, t, o->outfit->u.lau.trackmin,
119 o->outfit->u.lau.trackmax );
120 if ( p->stats.launch_lockon <= 0. )
121 o->u.ammo.lockon_timer = max;
122 else
123 o->u.ammo.lockon_timer -= dt * mod / p->stats.launch_lockon;
124
125 /* Cap at -max/3. */
126 if ( o->u.ammo.lockon_timer < max )
127 o->u.ammo.lockon_timer = max;
128
129 /* Trigger lockon hook. */
130 if ( !locked && ( o->u.ammo.lockon_timer < 0. ) )
131 pilot_runHook( p, PILOT_HOOK_LOCKON );
132 }
133}
134
141{
142 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
143 PilotOutfitSlot *o = p->outfits[i];
144 if ( o->outfit == NULL )
145 continue;
146 if ( !outfit_isSeeker( o->outfit ) )
147 continue;
148
149 /* Clear timer. */
150 o->u.ammo.lockon_timer = o->outfit->u.lau.lockon;
151
152 /* Clear arc. */
153 o->u.ammo.in_arc = 0;
154 }
155}
156
167int pilot_getMount( const Pilot *p, const PilotOutfitSlot *w, vec2 *v )
168{
169 double x, y;
170 const ShipMount *m = &w->sslot->mount;
171
172 if ( ship_isFlag( p->ship, SHIP_3DMOUNTS ) ) {
173 vec3 v2;
175 mat4_mul_vec( &v2, &H, &m->pos );
176 x = v2.v[0];
177 y = v2.v[1];
178 } else {
179 double cm, sm;
180 cm = cos( p->solid.dir );
181 sm = sin( p->solid.dir );
182 x = m->pos.v[0] * cm - m->pos.v[1] * sm;
183 y = m->pos.v[0] * sm + m->pos.v[1] * cm + m->pos.v[2];
184 y *= M_SQRT1_2;
185 }
186
187 /* Get the mount and add the player.p offset. */
188 vec2_cset( v, x, y );
189 return 0;
190}
191
199int pilot_dock( Pilot *p, Pilot *target )
200{
201 int i;
202 PilotOutfitSlot *dockslot;
203
204 /* Must belong to target */
205 if ( p->dockpilot != target->id )
206 return -1;
207
208 /* Must have a dockslot */
209 dockslot = pilot_getDockSlot( p );
210 if ( dockslot == NULL )
211 return -1;
212
213 /* Must be close. */
214 if ( vec2_dist( &p->solid.pos, &target->solid.pos ) >
215 target->ship->size * PILOT_SIZE_APPROX )
216 return -1;
217
218 /* Cannot be going much faster. */
219 if ( vec2_dist2( &p->solid.vel, &target->solid.vel ) >
220 pow2( MAX_HYPERSPACE_VEL ) )
221 return -1;
222
223 /* Grab dock ammo */
224 i = p->dockslot;
225
226 /* Try to add fighter. */
227 dockslot->u.ammo.deployed--;
228 p->dockpilot = 0;
229 p->dockslot = -1;
230
231 /* Add the pilot's outfit. */
232 if ( pilot_addAmmo( target, target->outfits[i], 1 ) != 1 )
233 WARN( _( "Unable to add ammo to '%s' from docking pilot '%s'!" ),
234 target->name, p->name );
235
236 /* Remove from pilot's escort list. */
237 for ( i = 0; i < array_size( target->escorts ); i++ ) {
238 if ( ( target->escorts[i].type == ESCORT_TYPE_BAY ) &&
239 ( target->escorts[i].id == p->id ) )
240 break;
241 }
242 /* Not found as pilot's escorts. */
243 if ( i >= array_size( target->escorts ) )
244 WARN( _( "Docking pilot '%s' not found in pilot '%s's escort list!" ),
245 target->name, p->name );
246 /* Remove escort pilot. */
247 escort_rmListIndex( target, i );
248
249 /* Destroy the pilot. */
250 pilot_delete( p );
251
252 return 0;
253}
254
262{
263 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
264 if ( p->outfits[i]->outfit == NULL )
265 continue;
266 if ( outfit_isFighterBay( p->outfits[i]->outfit ) )
267 if ( p->outfits[i]->u.ammo.deployed > 0 )
268 return 1;
269 }
270 return 0;
271}
272
283int pilot_addOutfitRaw( Pilot *pilot, const Outfit *outfit, PilotOutfitSlot *s )
284{
285 /* Set the outfit. */
286 s->flags = 0;
287 s->state = PILOT_OUTFIT_OFF;
288 s->outfit = outfit;
289
290 /* Set some default parameters. */
291 s->timer = 0.;
292
293 /* Some per-case scenarios. */
294 if ( outfit_isFighterBay( outfit ) ) {
295 s->u.ammo.quantity = 0;
296 s->u.ammo.deployed = 0;
297 pilot->nfighterbays++;
298 } else if ( outfit_isTurret( outfit ) ) /* used to speed up AI */
299 pilot->nturrets++;
300 else if ( outfit_isBolt( outfit ) )
301 pilot->ncannons++;
302 else if ( outfit_isAfterburner( outfit ) )
303 pilot->nafterburners++;
304 if ( outfit_isLauncher( outfit ) ) {
305 s->u.ammo.quantity = 0;
306 s->u.ammo.deployed = 0; /* Just in case. */
307 }
308
309 if ( outfit_isBeam( outfit ) ) { /* Used to speed up some calculations. */
310 s->u.beamid = 0;
311 pilot->nbeams++;
312 }
313
314 /* Check if active. */
315 if ( outfit_isActive( outfit ) )
316 s->flags |= PILOTOUTFIT_ACTIVE;
317 else
318 s->flags &= ~PILOTOUTFIT_ACTIVE;
319
320 /* Check if toggleable. */
321 if ( outfit_isToggleable( outfit ) )
322 s->flags |= PILOTOUTFIT_TOGGLEABLE;
323 else
324 s->flags &= ~PILOTOUTFIT_TOGGLEABLE;
325
326 /* Update heat. */
328
329 /* Disable lua for now. */
330 s->lua_mem = LUA_NOREF;
331 ss_free( s->lua_stats ); /* Just in case. */
332 s->lua_stats = NULL;
333
334 /* Initialize if active thingy if necessary. */
335 pilot_outfitLAdd( pilot, s );
336
337 return 0;
338}
339
349int pilot_addOutfitTest( Pilot *pilot, const Outfit *outfit,
350 const PilotOutfitSlot *s, int warn )
351{
352 const char *str;
353
354 /* See if slot has space. */
355 if ( s->outfit != NULL ) {
356 if ( warn )
357 WARN( _( "Pilot '%s': trying to add outfit '%s' to slot that already "
358 "has an outfit" ),
359 pilot->name, outfit->name );
360 return -1;
361 } else if ( ( outfit_cpu( outfit ) < 0 ) &&
362 ( pilot->cpu < ABS( outfit_cpu( outfit ) ) ) ) {
363 if ( warn )
364 WARN( _( "Pilot '%s': Not enough CPU to add outfit '%s'" ),
365 pilot->name, outfit->name );
366 return -1;
367 } else if ( ( str = pilot_canEquip( pilot, s, outfit ) ) != NULL ) {
368 if ( warn )
369 WARN( _( "Pilot '%s': Trying to add outfit but %s" ), pilot->name,
370 str );
371 return -1;
372 }
373 return 0;
374}
375
384int pilot_addOutfit( Pilot *pilot, const Outfit *outfit, PilotOutfitSlot *s )
385{
386 /* Test to see if outfit can be added. */
387 int ret = pilot_addOutfitTest( pilot, outfit, s, 1 );
388 if ( ret != 0 )
389 return -1;
390
391 /* Add outfit. */
392 ret = pilot_addOutfitRaw( pilot, outfit, s );
393
394 /* Recalculate the stats */
395 pilot_calcStats( pilot );
396
397 return ret;
398}
399
403int pilot_addOutfitIntrinsicRaw( Pilot *pilot, const Outfit *outfit )
404{
406
407 if ( !outfit_isMod( outfit ) ) {
408 WARN( _( "Instrinsic outfits must be modifiers!" ) );
409 return -1;
410 }
411
412 if ( pilot->outfit_intrinsic == NULL )
414
415 s = &array_grow( &pilot->outfit_intrinsic );
416 memset( s, 0, sizeof( PilotOutfitSlot ) );
417 return pilot_addOutfitRaw( pilot, outfit, s );
418}
419
423int pilot_addOutfitIntrinsic( Pilot *pilot, const Outfit *outfit )
424{
426 int ret;
427
428 if ( !outfit_isMod( outfit ) ) {
429 WARN( _( "Instrinsic outfits must be modifiers!" ) );
430 return -1;
431 }
432
433 if ( pilot->outfit_intrinsic == NULL )
435
436 s = &array_grow( &pilot->outfit_intrinsic );
437 memset( s, 0, sizeof( PilotOutfitSlot ) );
438 ret = pilot_addOutfitRaw( pilot, outfit, s );
439 if ( pilot->id > 0 )
440 pilot_outfitLInit( pilot, s );
441
442 return ret;
443}
444
448int pilot_rmOutfitIntrinsic( Pilot *pilot, const Outfit *outfit )
449{
450 int ret = 0;
451 for ( int i = 0; i < array_size( pilot->outfit_intrinsic ); i++ ) {
452 PilotOutfitSlot *s = &pilot->outfit_intrinsic[i];
453 if ( s->outfit != outfit )
454 continue;
455 ret = pilot_rmOutfitRaw( pilot, s );
456 array_erase( &pilot->outfit_intrinsic, s, s + 1 );
457 break;
458 }
459 /* Recalculate the stats */
460 if ( ret )
461 pilot_calcStats( pilot );
462 return ret;
463}
464
468int pilot_hasIntrinsic( const Pilot *pilot, const Outfit *outfit )
469{
470 int ret = 0;
471 for ( int i = 0; i < array_size( pilot->outfit_intrinsic ); i++ ) {
472 const PilotOutfitSlot *s = &pilot->outfit_intrinsic[i];
473 if ( s->outfit != outfit )
474 continue;
475 ret++;
476 }
477 return ret;
478}
479
490{
491 int ret;
492
493 /* Force turn off if necessary. */
494 if ( s->state == PILOT_OUTFIT_ON )
495 pilot_outfitOff( pilot, s, 0 );
496
497 /* Run remove hook if necessary. */
498 pilot_outfitLRemove( pilot, s );
499
500 /* Decrement counters if necessary. */
501 if ( s->outfit != NULL ) {
502 if ( outfit_isTurret( s->outfit ) )
503 pilot->nturrets--;
504 else if ( outfit_isBolt( s->outfit ) )
505 pilot->ncannons--;
506 else if ( outfit_isAfterburner( s->outfit ) )
507 pilot->nafterburners--;
508 else if ( outfit_isFighterBay( s->outfit ) )
509 pilot->nfighterbays--;
510 if ( outfit_isBeam( s->outfit ) )
511 pilot->nbeams--;
512 }
513
514 /* Remove the outfit. */
515 ret = ( s->outfit == NULL );
516 s->outfit = NULL;
517 s->flags = 0; /* Clear flags. */
518 // s->weapset = -1;
519
520 /* Remove secondary and such if necessary. */
521 if ( pilot->afterburner == s )
522 pilot->afterburner = NULL;
523
524 /* Clear Lua if necessary. */
525 if ( s->lua_mem != LUA_NOREF ) {
526 luaL_unref( naevL, LUA_REGISTRYINDEX, s->lua_mem );
527 s->lua_mem = LUA_NOREF;
528 }
529
530 /* Clean up stats. */
531 ss_free( s->lua_stats );
532 s->lua_stats = NULL;
533
534 return ret;
535}
536
545{
546 int ret;
547 const char *str = pilot_canEquip( pilot, s, NULL );
548 if ( str != NULL ) {
549 WARN( _( "Pilot '%s': Trying to remove outfit but %s" ), pilot->name,
550 str );
551 return -1;
552 }
553
554 ret = pilot_rmOutfitRaw( pilot, s );
555
556 /* recalculate the stats */
557 pilot_calcStats( pilot );
558
559 return ret;
560}
561
569{
570 for ( int i = 0; i < array_size( p->outfits ); i++ )
571 if ( ( p->outfits[i]->outfit != NULL ) &&
572 !outfit_fitsSlot( p->outfits[i]->outfit,
573 &p->outfits[i]->sslot->slot ) )
574 return 0;
575 return 1;
576}
577
585{
586 for ( int i = 0; i < array_size( p->outfits ); i++ )
587 if ( p->outfits[i]->sslot->required && p->outfits[i]->outfit == NULL )
588 return 0;
589 return 1;
590}
591
599{
600 return !pilot_reportSpaceworthy( p, NULL, 0 );
601}
602
611int pilot_reportSpaceworthy( const Pilot *p, char *buf, int bufSize )
612{
613#define SPACEWORTHY_CHECK( cond, msg ) \
614 if ( cond ) { \
615 ret++; \
616 if ( buf != NULL ) { \
617 if ( pos > 0 ) \
618 pos += scnprintf( &buf[pos], bufSize - pos, "\n" ); \
619 pos += scnprintf( &buf[pos], bufSize - pos, ( msg ) ); \
620 } \
621 }
622 int pos = 0;
623 int ret = 0;
624
625 /* Core Slots */
626 SPACEWORTHY_CHECK( !pilot_slotsCheckRequired( p ),
627 _( "!! Not All Core Slots are equipped" ) );
628 /* CPU. */
629 SPACEWORTHY_CHECK( p->cpu < 0, _( "!! Insufficient CPU" ) );
630
631 /* Movement. */
632 SPACEWORTHY_CHECK( p->accel < 0, _( "!! Insufficient Accel" ) );
633 SPACEWORTHY_CHECK( p->speed < 0, _( "!! Insufficient Speed" ) );
634 SPACEWORTHY_CHECK( p->turn < 0, _( "!! Insufficient Turn" ) );
635
636 /* Health. */
637 SPACEWORTHY_CHECK( p->armour < 0., _( "!! Insufficient Armour" ) );
638 SPACEWORTHY_CHECK( p->armour_regen < 0.,
639 _( "!! Insufficient Armour Regeneration" ) );
640 SPACEWORTHY_CHECK( p->shield < 0., _( "!! Insufficient Shield" ) );
641 SPACEWORTHY_CHECK( p->shield_regen < 0.,
642 _( "!! Insufficient Shield Regeneration" ) );
643 SPACEWORTHY_CHECK( p->energy_max < 0., _( "!! Insufficient Energy" ) );
644 SPACEWORTHY_CHECK( ( p->energy_regen <= 0. ) && ( p->energy_max > 0. ),
645 _( "!! Insufficient Energy Regeneration" ) );
646
647 /* Misc. */
648 SPACEWORTHY_CHECK( p->fuel_max < 0, _( "!! Insufficient Fuel Maximum" ) );
649 SPACEWORTHY_CHECK( p->fuel_consumption < 0,
650 _( "!! Insufficient Fuel Consumption" ) );
651 SPACEWORTHY_CHECK( p->cargo_free < 0,
652 _( "!! Insufficient Free Cargo Space" ) );
653 SPACEWORTHY_CHECK( p->crew < 0, _( "!! Insufficient Crew" ) );
654 SPACEWORTHY_CHECK( pilot_massFactor( p ) < 0.05, ( "!! Too Heavy" ) );
655
656 /* No need to mess with the string. */
657 if ( buf == NULL )
658 return ret;
659
660 /* Buffer is full, lets write that there is more then what's copied */
661 if ( pos > bufSize - 1 ) {
662 buf[bufSize - 4] = '.';
663 buf[bufSize - 3] = '.';
664 buf[bufSize - 2] = '.';
665 /* buf[bufSize-1]='\0'; already done for us */
666 } else if ( pos == 0 ) {
667 /* String is empty so no errors encountered */
668 pos += scnprintf( buf, bufSize, _( "Spaceworthy" ) );
669 if ( ship_isFlag( p->ship, SHIP_NOPLAYER ) )
670 pos += scnprintf( &buf[pos], bufSize - pos, "\n#o%s#0",
671 _( "Escort only" ) );
672 if ( ship_isFlag( p->ship, SHIP_NOESCORT ) )
673 /* pos +=*/scnprintf( &buf[pos], bufSize - pos, "\n#o%s#0",
674 _( "Lead ship only" ) );
675 }
676
677 return ret;
678}
679#undef SPACEWORTHY_CHECK
680
688int pilot_hasOutfitLimit( const Pilot *p, const char *limit )
689{
690 if ( limit == NULL )
691 return 0;
692 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
693 const Outfit *o = p->outfits[i]->outfit;
694 if ( o == NULL )
695 continue;
696 if ( ( o->limit != NULL ) && ( strcmp( o->limit, limit ) == 0 ) )
697 return 1;
698 }
699 for ( int i = 0; i < array_size( p->outfit_intrinsic ); i++ ) {
700 const Outfit *o = p->outfit_intrinsic[i].outfit;
701 if ( ( o->limit != NULL ) && ( strcmp( o->limit, limit ) == 0 ) )
702 return 1;
703 }
704 return 0;
705}
706
715const char *pilot_canEquip( const Pilot *p, const PilotOutfitSlot *s,
716 const Outfit *o )
717{
718 /* Just in case. */
719 if ( ( p == NULL ) || ( s == NULL ) )
720 return _( "Nothing selected." );
721
722 if ( o != NULL ) {
723 /* Check slot type. */
724 if ( !outfit_fitsSlot( o, &s->sslot->slot ) )
725 return _( "Does not fit slot." );
726 /* Check outfit limit. */
727 if ( ( o->limit != NULL ) && pilot_hasOutfitLimit( p, o->limit ) )
728 return _( "Already have an outfit of this type installed" );
729 /* Check to see if already equipped unique. */
730 if ( outfit_isProp( o, OUTFIT_PROP_UNIQUE ) &&
731 ( pilot_numOutfit( p, o ) > 0 ) )
732 return _( "Can only install unique outfit once." );
733 }
734
735 return NULL;
736}
737
746int pilot_addAmmo( Pilot *pilot, PilotOutfitSlot *s, int quantity )
747{
748 int q, max;
749
750 /* Failure cases. */
751 if ( s->outfit == NULL ) {
752 WARN( _( "Pilot '%s': Trying to add ammo to unequipped slot." ),
753 pilot->name );
754 return 0;
755 } else if ( !outfit_isLauncher( s->outfit ) &&
757 return 0;
758
759 /* Add the ammo. */
760 max = pilot_maxAmmoO( pilot, s->outfit ) - s->u.ammo.deployed;
761 q = s->u.ammo.quantity; /* Amount have. */
762 s->u.ammo.quantity += quantity;
763 s->u.ammo.quantity = MIN( max, s->u.ammo.quantity );
764 q = s->u.ammo.quantity - q; /* Amount actually added. */
765 pilot->mass_outfit += q * outfit_ammoMass( s->outfit );
766 pilot_updateMass( pilot );
767
768 return q;
769}
770
779int pilot_rmAmmo( Pilot *pilot, PilotOutfitSlot *s, int quantity )
780{
781 int q;
782
783 /* Failure cases. */
784 if ( !outfit_isLauncher( s->outfit ) && !outfit_isFighterBay( s->outfit ) )
785 return 0;
786 else if ( s->outfit == NULL ) {
787 WARN( _( "Pilot '%s': Trying to remove ammo from unequipped slot." ),
788 pilot->name );
789 return 0;
790 }
791
792 /* Remove ammo. */
793 q = MIN( quantity, s->u.ammo.quantity );
794 s->u.ammo.quantity -= q;
795 pilot->mass_outfit -= q * outfit_ammoMass( s->outfit );
796 pilot_updateMass( pilot );
797 /* We don't set the outfit to null so it "remembers" old ammo. */
798
799 return q;
800}
801
808int pilot_countAmmo( const Pilot *pilot )
809{
810 int nammo = 0;
811 for ( int i = 0; i < array_size( pilot->outfits ); i++ ) {
812 const Outfit *outfit;
813 PilotOutfitSlot *po = pilot->outfits[i];
814 if ( po == NULL )
815 continue;
816 outfit = po->outfit;
817 if ( outfit == NULL )
818 continue;
819 if ( !outfit_isLauncher( po->outfit ) )
820 continue;
821 nammo += po->u.ammo.quantity;
822 }
823 return nammo;
824}
825
832int pilot_maxAmmo( const Pilot *pilot )
833{
834 int max = 0;
835 for ( int i = 0; i < array_size( pilot->outfits ); i++ ) {
836 const Outfit *outfit;
837 PilotOutfitSlot *po = pilot->outfits[i];
838 if ( po == NULL )
839 continue;
840 outfit = po->outfit;
841 if ( outfit == NULL )
842 continue;
843 if ( !outfit_isLauncher( outfit ) )
844 continue;
845 max += outfit->u.lau.amount;
846 }
847 max = round( (double)max * pilot->stats.ammo_capacity );
848 return max;
849}
850
854int pilot_maxAmmoO( const Pilot *p, const Outfit *o )
855{
856 int max;
857 if ( o == NULL )
858 return 0;
859 else if ( outfit_isLauncher( o ) )
860 max = MAX( 0, round( (double)o->u.lau.amount * p->stats.ammo_capacity ) );
861 else if ( outfit_isFighterBay( o ) )
862 max = MAX( 0, round( (double)o->u.bay.amount * p->stats.fbay_capacity ) );
863 else
864 max = 0;
865 return max;
866}
867
873void pilot_fillAmmo( Pilot *pilot )
874{
875 for ( int i = 0; i < array_size( pilot->outfits ); i++ ) {
876 int ammo_threshold;
877 const Outfit *o = pilot->outfits[i]->outfit;
878
879 /* Must be valid outfit. */
880 if ( o == NULL )
881 continue;
882
883 /* Initial (raw) ammo threshold */
884 ammo_threshold = pilot_maxAmmoO( pilot, o );
885
886 /* Adjust for deployed fighters if needed */
887 if ( outfit_isFighterBay( o ) )
888 ammo_threshold -= pilot->outfits[i]->u.ammo.deployed;
889
890 /* Add ammo. */
891 pilot_addAmmo( pilot, pilot->outfits[i],
892 ammo_threshold - pilot->outfits[i]->u.ammo.quantity );
893 }
894}
895
896double pilot_outfitRange( const Pilot *p, const Outfit *o )
897{
898 if ( outfit_isBolt( o ) ) {
899 double range =
900 o->u.blt.falloff + ( o->u.blt.range - o->u.blt.falloff ) / 2.;
901 if ( p != NULL ) {
902 if ( outfit_isTurret( o ) )
903 range *= p->stats.tur_range * p->stats.weapon_range;
904 else if ( outfit_isForward( o ) )
905 range *= p->stats.fwd_range * p->stats.weapon_range;
906 }
907 return range;
908 } else if ( outfit_isBeam( o ) ) {
909 double range = o->u.bem.range;
910 if ( p != NULL ) {
911 if ( outfit_isTurret( o ) )
912 range *= p->stats.tur_range * p->stats.weapon_range;
913 else if ( outfit_isForward( o ) )
914 range *= p->stats.fwd_range * p->stats.weapon_range;
915 }
916 return range;
917 } else if ( outfit_isLauncher( o ) ) {
918 double duration = o->u.lau.duration;
919 double accel = o->u.lau.accel;
920 double speed = o->u.lau.speed;
921 double speed_max = o->u.lau.speed_max;
922 if ( p != NULL ) {
923 duration *= p->stats.launch_range * p->stats.weapon_range;
924 speed *= p->stats.launch_speed;
925 accel *= p->stats.launch_accel;
926 speed_max *= p->stats.launch_speed;
927 }
928 if ( o->u.lau.accel != 0. ) {
929 double speedinc;
930 if ( speed >
931 0. ) /* Ammo that don't start stopped don't have max speed. */
932 speedinc = INFINITY;
933 else
934 speedinc = speed_max - speed;
935 double at = speedinc / accel;
936 if ( at < duration )
937 return speedinc * ( duration - at / 2. ) + speed * duration;
938
939 /* Maximum speed will never be reached. */
940 return pow2( duration ) * accel / 2. + duration * speed;
941 }
942 return speed * duration;
943 } else if ( outfit_isFighterBay( o ) )
944 return INFINITY;
945 return -1.;
946}
947
951static void pilot_calcStatsSlot( Pilot *pilot, PilotOutfitSlot *slot )
952{
953 const Outfit *o = slot->outfit;
954 ShipStats *s = &pilot->stats;
955
956 /* Outfit must exist. */
957 if ( o == NULL )
958 return;
959
960 /* Modify CPU. */
961 pilot->cpu += outfit_cpu( o );
962
963 /* Add mass. */
964 pilot->mass_outfit += o->mass;
965
966 /* Keep a separate counter for required (core) outfits. */
967 if ( sp_required( o->slot.spid ) )
968 pilot->base_mass += o->mass;
969
970 /* Add ammo mass. */
971 if ( outfit_isLauncher( o ) )
972 pilot->mass_outfit += slot->u.ammo.quantity * o->u.lau.ammo_mass;
973 else if ( outfit_isFighterBay( o ) )
974 pilot->mass_outfit += slot->u.ammo.quantity * o->u.bay.ship_mass;
975
976 if ( outfit_isAfterburner( o ) ) /* Afterburner */
977 pilot->afterburner = slot; /* Set afterburner */
978
979 /* Lua mods apply their stats. */
980 if ( slot->lua_mem != LUA_NOREF )
981 ss_statsMergeFromList( &pilot->stats, slot->lua_stats );
982
983 /* Has update function. */
984 if ( o->lua_update != LUA_NOREF )
985 pilot->outfitlupdate = 1;
986
987 /* Apply modifications. */
988 if ( outfit_isMod( o ) ) { /* Modification */
989 /* Active outfits must be on to affect stuff. */
990 if ( ( slot->flags & PILOTOUTFIT_ACTIVE ) &&
991 !( slot->state == PILOT_OUTFIT_ON ) )
992 return;
993 /* Add stats. */
995
996 } else if ( outfit_isAfterburner( o ) ) { /* Afterburner */
997 /* Active outfits must be on to affect stuff. */
998 if ( ( slot->flags & PILOTOUTFIT_ACTIVE ) &&
999 !( slot->state == PILOT_OUTFIT_ON ) )
1000 return;
1001 /* Add stats. */
1003 pilot_setFlag(
1004 pilot,
1005 PILOT_AFTERBURNER ); /* We use old school flags for this still... */
1006 pilot->energy_regen -=
1007 pilot->afterburner->outfit->u.afb.energy; /* energy loss */
1008 } else {
1009 /* Always add stats for non mod/afterburners. */
1011 }
1012}
1013
1020{
1021 double ac, sc, ec, tm; /* temporary health coefficients to set */
1022 ShipStats *s;
1023
1024 /*
1025 * Set up the basic stuff
1026 */
1027 /* mass */
1028 pilot->solid.mass = pilot->ship->mass;
1029 pilot->base_mass = pilot->solid.mass;
1030 /* cpu */
1031 pilot->cpu = 0.;
1032 /* movement */
1033 pilot->accel_base = pilot->ship->accel;
1034 pilot->turn_base = pilot->ship->turn;
1035 pilot->speed_base = pilot->ship->speed;
1036 /* crew */
1037 pilot->crew = pilot->ship->crew;
1038 /* cargo */
1039 pilot->cap_cargo = pilot->ship->cap_cargo;
1040 /* fuel_consumption. */
1041 pilot->fuel_consumption = pilot->ship->fuel_consumption;
1042 /* health */
1043 ac = ( pilot->armour_max > 0. ) ? pilot->armour / pilot->armour_max : 0.;
1044 sc = ( pilot->shield_max > 0. ) ? pilot->shield / pilot->shield_max : 0.;
1045 ec = ( pilot->energy_max > 0. ) ? pilot->energy / pilot->energy_max : 0.;
1046 pilot->armour_max = pilot->ship->armour;
1047 pilot->shield_max = pilot->ship->shield;
1048 pilot->fuel_max = pilot->ship->fuel;
1049 pilot->armour_regen = pilot->ship->armour_regen;
1050 pilot->shield_regen = pilot->ship->shield_regen;
1051 /* Absorption. */
1052 pilot->dmg_absorb = pilot->ship->dmg_absorb;
1053 /* Energy. */
1054 pilot->energy_max = pilot->ship->energy;
1055 pilot->energy_regen = pilot->ship->energy_regen;
1056 /* Misc. */
1057 pilot->outfitlupdate = 0;
1058 /* Stats. */
1059 s = &pilot->stats;
1060 tm = s->time_mod;
1061 *s = pilot->ship->stats_array;
1062
1063 /* Player and escorts gets difficulty applied. */
1064 if ( pilot_isWithPlayer( pilot ) )
1065 difficulty_apply( s );
1066
1067 /* Now add outfit changes */
1068 pilot->mass_outfit = 0.;
1069 for ( int i = 0; i < array_size( pilot->outfit_intrinsic ); i++ )
1070 pilot_calcStatsSlot( pilot, &pilot->outfit_intrinsic[i] );
1071 for ( int i = 0; i < array_size( pilot->outfits ); i++ )
1072 pilot_calcStatsSlot( pilot, pilot->outfits[i] );
1073
1074 /* Merge stats. */
1075 ss_statsMergeFromList( &pilot->stats, pilot->ship_stats );
1076 ss_statsMergeFromList( &pilot->stats, pilot->intrinsic_stats );
1077
1078 /* Compute effects. */
1079 effect_compute( &pilot->stats, pilot->effects );
1080
1081 /* Apply system effects. */
1082 ss_statsMergeFromList( &pilot->stats, cur_system->stats );
1083
1084 /* Apply stealth malus. */
1085 if ( pilot_isFlag( pilot, PILOT_STEALTH ) ) {
1086 s->accel_mod *= 0.8;
1087 s->turn_mod *= 0.8;
1088 s->speed_mod *= 0.5;
1089 }
1090
1091 /*
1092 * Absolute increases.
1093 */
1094 /* Movement. */
1095 pilot->accel_base += s->accel;
1096 pilot->turn_base += s->turn * M_PI / 180.;
1097 pilot->speed_base += s->speed;
1098 /* Health. */
1099 pilot->armour_max += s->armour;
1100 pilot->armour_regen += s->armour_regen;
1101 pilot->shield_max += s->shield;
1102 pilot->shield_regen += s->shield_regen;
1103 pilot->energy_max += s->energy;
1104 pilot->energy_regen += s->energy_regen;
1105 /* Misc. */
1106 pilot->fuel_max += s->fuel;
1107 pilot->cap_cargo += s->cargo;
1108
1109 /*
1110 * Relative increases.
1111 */
1112 /* Movement. */
1113 pilot->accel_base *= s->accel_mod;
1114 pilot->turn_base *= s->turn_mod;
1115 pilot->speed_base *= s->speed_mod;
1116 /* Health. */
1117 pilot->armour_max *= s->armour_mod;
1118 pilot->armour_regen *= s->armour_regen_mod;
1119 pilot->shield_max *= s->shield_mod;
1120 pilot->shield_regen *= s->shield_regen_mod;
1121 pilot->energy_max *= s->energy_mod;
1122 pilot->energy_regen *= s->energy_regen_mod;
1123 /* We'll be kind and allow negative shield and energy. */
1124 pilot->shield_max = MAX( 0., pilot->shield_max );
1125 pilot->energy_max = MAX( 0., pilot->energy_max );
1126 /* Enforce health to be at least 0 after mods, so that something like -1000%
1127 * would just set it to 0 instead of negative. */
1128 pilot->armour_regen = MAX( 0., pilot->armour_regen );
1129 pilot->shield_regen = MAX( 0., pilot->shield_regen );
1130 pilot->energy_regen = MAX( 0., pilot->energy_regen );
1131 /* cpu */
1132 pilot->cpu_max =
1133 (int)floor( (float)( pilot->ship->cpu + s->cpu_max ) * s->cpu_mod );
1134 pilot->cpu += pilot->cpu_max; /* CPU is negative, this just sets it so it's
1135 based off of cpu_max. */
1136 /* Misc. */
1137 pilot->crew = pilot->crew * s->crew_mod + s->crew;
1138 pilot->fuel_consumption *= s->fuel_usage_mod;
1139 pilot->fuel_max *= s->fuel_mod;
1140 pilot->cap_cargo *= s->cargo_mod;
1142
1143 /*
1144 * Flat increases.
1145 */
1146 pilot->armour_regen -= s->armour_regen_malus;
1147 pilot->shield_regen -= s->shield_regen_malus;
1148 pilot->energy_regen -= s->energy_regen_malus;
1149 pilot->dmg_absorb = pilot->dmg_absorb + s->absorb;
1150
1151 /* Give the pilot his health proportion back */
1152 pilot->armour = ac * pilot->armour_max;
1153 pilot->shield = sc * pilot->shield_max;
1154 pilot->energy = ec * pilot->energy_max;
1155
1156 /* Deployed fighters with no mothership take damage over time. */
1157 if ( pilot_isFlag( pilot, PILOT_CARRIER_DIED ) ) {
1158 const Pilot *parent = pilot_get( pilot->parent );
1159 if ( parent == NULL ) {
1160 pilot->armour_regen = MIN( -3.5, pilot->armour_regen - 10. );
1161 pilot->stats.disable += 35.;
1162 }
1163 }
1164
1165 /* Dump excess fuel */
1166 pilot->fuel = MIN( pilot->fuel, pilot->fuel_max );
1167
1168 /* Cargo has to be reset. */
1169 pilot_cargoCalc( pilot ); /* Calls pilot_updateMass. */
1170
1171 /* Calculate the heat. */
1172 pilot_heatCalc( pilot );
1173
1174 /* Update GUI as necessary. */
1175 gui_setGeneric( pilot );
1176
1177 /* Update weapon set range. */
1178 pilot_weapSetUpdateStats( pilot );
1179
1180 /* In case the time_mod has changed. */
1181 if ( pilot_isPlayer( pilot ) && ( tm != s->time_mod ) )
1183}
1184
1189{
1190 pilot->armour = pilot->armour_max;
1191 pilot->shield = pilot->shield_max;
1192 pilot->energy = pilot->energy_max;
1193
1194 pilot->stress = 0.;
1195 pilot->stimer = 0.;
1196 pilot->sbonus = 0.;
1197
1198 pilot_fillAmmo( pilot );
1199
1200 for ( int i = 0; i < array_size( pilot->escorts ); i++ ) {
1201 const Escort_t *e = &pilot->escorts[i];
1202 Pilot *pe = pilot_get( e->id );
1203
1204 if ( pe != NULL )
1205 pilot_healLanded( pe );
1206 }
1207}
1208
1212PilotOutfitSlot *pilot_getSlotByName( Pilot *pilot, const char *name )
1213{
1214 for ( int i = 0; i < array_size( pilot->outfits ); i++ ) {
1215 PilotOutfitSlot *s = pilot->outfits[i];
1216 if ( ( s->sslot->name != NULL ) &&
1217 ( strcmp( s->sslot->name, name ) == 0 ) )
1218 return s;
1219 }
1220 return NULL;
1221}
1222
1226double pilot_massFactor( const Pilot *pilot )
1227{
1228 double mass = pilot->solid.mass;
1229 if ( ( pilot->stats.engine_limit > 0. ) &&
1230 ( mass > pilot->stats.engine_limit ) ) {
1231 double f =
1232 ( mass - pilot->stats.engine_limit ) / pilot->stats.engine_limit;
1233 return 1. / ( 1. + f + f + 4. * pow( f, 3. ) );
1234 }
1235 return 1.;
1236}
1237
1244{
1245 double factor;
1246
1247 /* Recompute effective mass if something changed. */
1248 pilot->solid.mass = MAX( pilot->stats.mass_mod * pilot->ship->mass +
1249 pilot->stats.cargo_inertia * pilot->mass_cargo +
1250 pilot->mass_outfit,
1251 0. );
1252
1253 /* Set and apply limit. */
1254 factor = pilot_massFactor( pilot );
1255 pilot->accel = factor * pilot->accel_base;
1256 pilot->turn = factor * pilot->turn_base;
1257 pilot->speed = factor * pilot->speed_base;
1258
1259 /* limit the maximum speed if limiter is active */
1260 if ( pilot_isFlag( pilot, PILOT_HASSPEEDLIMIT ) ) {
1261 pilot->speed = pilot->speed_limit - pilot->accel / 3.;
1262 /* Speed must never go negative. */
1263 if ( pilot->speed < 0. ) {
1264 /* If speed DOES go negative, we have to lower accel. */
1265 pilot->accel = 3. * pilot->speed_limit;
1266 pilot->speed = 0.;
1267 }
1268 }
1269 /* Need to recalculate electronic warfare mass change. */
1270 pilot_ewUpdateStatic( pilot );
1271
1272 /* Update ship stuff. */
1273 if ( pilot_isPlayer( pilot ) )
1274 gui_setShip();
1275}
1276
1284{
1285 return ( o->flags & PILOTOUTFIT_TOGGLEABLE );
1286}
1287
1291static void pilot_outfitLRun( Pilot *p,
1292 void ( *const func )( const Pilot *p,
1293 PilotOutfitSlot *po,
1294 const void *data ),
1295 const void *data )
1296{
1297 pilotoutfit_modified = 0;
1298 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
1299 PilotOutfitSlot *po = p->outfits[i];
1300 if ( po->outfit == NULL )
1301 continue;
1302 func( p, po, data );
1303 }
1304 for ( int i = 0; i < array_size( p->outfit_intrinsic ); i++ ) {
1305 PilotOutfitSlot *po = &p->outfit_intrinsic[i];
1306 if ( po->outfit == NULL )
1307 continue;
1308 func( p, po, data );
1309 }
1310 /* Recalculate if anything changed. */
1311 if ( pilotoutfit_modified ) {
1312 /* TODO pilot_calcStats can be called twice here. */
1314 pilot_calcStats( p );
1315 }
1316}
1317static void outfitLRunWarning( const Pilot *p, const Outfit *o,
1318 const char *name, const char *error )
1319{
1320 WARN( _( "Pilot '%s''s outfit '%s' -> '%s':\n%s" ), p->name, o->name, name,
1321 error );
1322}
1323
1327static int pilot_outfitLmem( PilotOutfitSlot *po, nlua_env env )
1328{
1329 int oldmem;
1330 /* Create the memory if necessary and initialize stats. */
1331 if ( po->lua_mem == LUA_NOREF ) {
1332 lua_newtable( naevL ); /* mem */
1333 po->lua_mem = luaL_ref( naevL, LUA_REGISTRYINDEX ); /* */
1334 }
1335 /* Get old memory. */
1336 nlua_getenv( naevL, env, "mem" ); /* oldmem */
1337 oldmem = luaL_ref( naevL, LUA_REGISTRYINDEX ); /* */
1338 /* Set the memory. */
1339 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->lua_mem ); /* mem */
1340 nlua_setenv( naevL, env, "mem" ); /* */
1341 return oldmem;
1342}
1343
1347static void pilot_outfitLunmem( nlua_env env, int oldmem )
1348{
1349 lua_rawgeti( naevL, LUA_REGISTRYINDEX, oldmem );
1350 nlua_setenv( naevL, env, "mem" ); /* pm */
1351 luaL_unref( naevL, LUA_REGISTRYINDEX, oldmem );
1352}
1353
1354static const char *pilot_outfitLDescExtra( const Pilot *p, const Outfit *o )
1355{
1356 static char descextra[STRMAX];
1357 const char *de;
1358 if ( o->lua_descextra == LUA_NOREF )
1359 return ( o->desc_extra != NULL ) ? _( o->desc_extra ) : NULL;
1360
1361 /* Set up the function: init( p, po ) */
1362 lua_rawgeti( naevL, LUA_REGISTRYINDEX, o->lua_descextra ); /* f */
1363 if ( ( p != NULL ) && ( p->id > 0 ) ) /* Needs valid ID. */
1364 lua_pushpilot( naevL, p->id ); /* f, p */
1365 else
1366 lua_pushnil( naevL ); /* f, p */
1367 lua_pushoutfit( naevL, o ); /* f, p, o */
1368 if ( nlua_pcall( o->lua_env, 2, 1 ) ) { /* */
1369 outfitLRunWarning( p, o, "descextra", lua_tostring( naevL, -1 ) );
1370 lua_pop( naevL, 1 );
1371 descextra[0] = '\0';
1372 return descextra;
1373 }
1374 /* Case no return we just pass nothing. */
1375 if ( lua_isnoneornil( naevL, -1 ) ) {
1376 lua_pop( naevL, 1 );
1377 return NULL;
1378 }
1379 de = luaL_checkstring( naevL, -1 );
1380 strncpy( descextra, de, sizeof( descextra ) - 1 );
1381 lua_pop( naevL, 1 );
1382 return descextra;
1383}
1384
1394const char *pilot_outfitDescription( const Pilot *p, const Outfit *o )
1395{
1396 static char o_description[STRMAX];
1397 const char *de = pilot_outfitLDescExtra( p, o );
1398 if ( de == NULL )
1399 return _( o->desc_raw );
1400 snprintf( o_description, sizeof( o_description ), "%s\n%s", _( o->desc_raw ),
1401 de );
1402 return o_description;
1403}
1404
1415const char *pilot_outfitSummary( const Pilot *p, const Outfit *o, int withname )
1416{
1417 static char o_summary[STRMAX];
1418 const char *de = pilot_outfitLDescExtra( p, o );
1419 if ( de == NULL ) {
1420 if ( withname )
1421 snprintf( o_summary, sizeof( o_summary ), "%s\n%s", _( o->name ),
1422 o->summary_raw );
1423 else
1424 snprintf( o_summary, sizeof( o_summary ), "%s", o->summary_raw );
1425 } else {
1426 if ( withname )
1427 snprintf( o_summary, sizeof( o_summary ), "%s\n%s\n%s", _( o->name ),
1428 o->summary_raw, de );
1429 else
1430 snprintf( o_summary, sizeof( o_summary ), "%s\n%s", o->summary_raw,
1431 de );
1432 }
1433 return o_summary;
1434}
1435
1439double pilot_outfitSpeed( const Pilot *p, const Outfit *o )
1440{
1441 if ( outfit_isBolt( o ) )
1442 return o->u.blt.speed;
1443 else if ( outfit_isLauncher( o ) ) {
1444 double t;
1445 double accel = o->u.lau.accel;
1446 double speed = o->u.lau.speed;
1447 double speed_max = o->u.lau.speed_max;
1448 double duration = o->u.lau.duration;
1449 if ( p != NULL ) {
1450 speed *= p->stats.launch_speed;
1451 accel *= p->stats.launch_accel;
1452 speed_max *= p->stats.launch_speed;
1453 duration *= p->stats.launch_range * p->stats.weapon_range;
1454 }
1455
1456 if ( o->u.lau.accel == 0. )
1457 return speed;
1458
1459 if ( speed >
1460 0. ) /* Ammo that don't start stopped don't have max speed. */
1461 t = INFINITY;
1462 else
1463 t = ( speed_max - speed ) / accel; /* Time to reach max speed */
1464
1465 /* Reaches max speed. */
1466 if ( t < duration )
1467 return ( accel * t * t / 2. +
1468 ( speed_max - speed ) * ( duration - t ) ) /
1469 duration +
1470 speed;
1471 /* Doesn't reach max speed. */
1472 else
1473 return accel * duration / 2. + speed;
1474 }
1475 return -1.;
1476}
1477
1484{
1485 pilotoutfit_modified = 0;
1486 for ( int i = 0; i < array_size( pilot->outfits ); i++ )
1487 pilot_outfitLInit( pilot, pilot->outfits[i] );
1488 for ( int i = 0; i < array_size( pilot->outfit_intrinsic ); i++ )
1489 pilot_outfitLInit( pilot, &pilot->outfit_intrinsic[i] );
1490 /* Recalculate if anything changed. */
1491 if ( pilotoutfit_modified ) {
1492 /* TODO calcStats computed twice maybe. */
1494 pilot_calcStats( pilot );
1495 }
1496}
1497
1502{
1503 int oldmem;
1504
1505 if ( po->outfit == NULL )
1506 return 0;
1507 if ( po->outfit->lua_onadd == LUA_NOREF )
1508 return 0;
1509
1510 /* Create the memory if necessary and initialize stats. */
1511 oldmem = pilot_outfitLmem( po, po->outfit->lua_env );
1512
1513 /* Set up the function: init( p, po ) */
1514 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_onadd ); /* f */
1515 lua_pushpilot( naevL, pilot->id ); /* f, p */
1516 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
1517 if ( nlua_pcall( po->outfit->lua_env, 2, 0 ) ) { /* */
1518 outfitLRunWarning( pilot, po->outfit, "onadd",
1519 lua_tostring( naevL, -1 ) );
1520 lua_pop( naevL, 1 );
1521 pilot_outfitLunmem( po->outfit->lua_env, oldmem );
1522 return -1;
1523 }
1524 pilot_outfitLunmem( po->outfit->lua_env, oldmem );
1525 return 1;
1526}
1527
1532{
1533 int oldmem;
1534
1535 if ( po->outfit == NULL )
1536 return 0;
1537 if ( po->outfit->lua_onremove == LUA_NOREF )
1538 return 0;
1539
1540 /* Create the memory if necessary and initialize stats. */
1541 oldmem = pilot_outfitLmem( po, po->outfit->lua_env );
1542
1543 /* Set up the function: init( p, po ) */
1544 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_onremove ); /* f */
1545 lua_pushpilot( naevL, pilot->id ); /* f, p */
1546 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
1547 if ( nlua_pcall( po->outfit->lua_env, 2, 0 ) ) { /* */
1548 outfitLRunWarning( pilot, po->outfit, "onremove",
1549 lua_tostring( naevL, -1 ) );
1550 lua_pop( naevL, 1 );
1551 pilot_outfitLunmem( po->outfit->lua_env, oldmem );
1552 return -1;
1553 }
1554 pilot_outfitLunmem( po->outfit->lua_env, oldmem );
1555 return 1;
1556}
1557
1566{
1567 int lua_oinit, oldmem;
1568 nlua_env lua_env;
1569
1570 if ( po->outfit == NULL )
1571 return 0;
1572
1573 if ( po->outfit->lua_env == LUA_NOREF )
1574 return 0;
1575
1576 lua_oinit = po->outfit->lua_init;
1577 lua_env = po->outfit->lua_env;
1578
1579 /* Create the memory if necessary and initialize stats. */
1580 oldmem = pilot_outfitLmem( po, lua_env );
1581
1582 if ( lua_oinit == LUA_NOREF ) {
1583 pilot_outfitLunmem( po->outfit->lua_env, oldmem );
1584 return 0;
1585 }
1586
1587 /* Set up the function: init( p, po ) */
1588 lua_rawgeti( naevL, LUA_REGISTRYINDEX, lua_oinit ); /* f */
1589 lua_pushpilot( naevL, pilot->id ); /* f, p */
1590 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
1591 if ( nlua_pcall( lua_env, 2, 0 ) ) { /* */
1592 outfitLRunWarning( pilot, po->outfit, "init", lua_tostring( naevL, -1 ) );
1593 lua_pop( naevL, 1 );
1594 pilot_outfitLunmem( po->outfit->lua_env, oldmem );
1595 return -1;
1596 }
1597 pilot_outfitLunmem( po->outfit->lua_env, oldmem );
1598 return 1;
1599}
1600
1601static void outfitLUpdate( const Pilot *pilot, PilotOutfitSlot *po,
1602 const void *data )
1603{
1604 double dt;
1605 int oldmem;
1606 if ( po->outfit->lua_update == LUA_NOREF )
1607 return;
1608
1609 nlua_env env = po->outfit->lua_env;
1610
1611 /* The data. */
1612 dt = *(double *)data;
1613
1614 /* Set the memory. */
1615 oldmem = pilot_outfitLmem( po, env );
1616
1617 /* Set up the function: update( p, po, dt ) */
1618 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_update ); /* f */
1619 lua_pushpilot( naevL, pilot->id ); /* f, p */
1620 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
1621 lua_pushnumber( naevL, dt ); /* f, p, po, dt */
1622 if ( nlua_pcall( env, 3, 0 ) ) { /* */
1623 outfitLRunWarning( pilot, po->outfit, "update",
1624 lua_tostring( naevL, -1 ) );
1625 lua_pop( naevL, 1 );
1626 }
1627 pilot_outfitLunmem( env, oldmem );
1628}
1635void pilot_outfitLUpdate( Pilot *pilot, double dt )
1636{
1637 if ( !pilot->outfitlupdate )
1638 return;
1639
1640 NTracingZone( _ctx, 1 );
1641 pilot_outfitLRun( pilot, outfitLUpdate, &dt );
1642 NTracingZoneEnd( _ctx );
1643}
1644
1645static void outfitLOutofenergy( const Pilot *pilot, PilotOutfitSlot *po,
1646 const void *data )
1647{
1648 (void)data;
1649 int oldmem;
1650
1651 if ( po->outfit->lua_outofenergy == LUA_NOREF )
1652 return;
1653
1654 nlua_env env = po->outfit->lua_env;
1655
1656 /* Set the memory. */
1657 oldmem = pilot_outfitLmem( po, env );
1658
1659 /* Set up the function: outofenergy( p, po ) */
1660 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_outofenergy ); /* f */
1661 lua_pushpilot( naevL, pilot->id ); /* f, p */
1662 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
1663 if ( nlua_pcall( env, 2, 0 ) ) { /* */
1664 outfitLRunWarning( pilot, po->outfit, "outofenergy",
1665 lua_tostring( naevL, -1 ) );
1666 lua_pop( naevL, 1 );
1667 }
1668 pilot_outfitLunmem( env, oldmem );
1669}
1676{
1677 pilot_outfitLRun( pilot, outfitLOutofenergy, NULL );
1678}
1679
1681 double armour;
1682 double shield;
1683 unsigned int attacker;
1684};
1685static void outfitLOnhit( const Pilot *pilot, PilotOutfitSlot *po,
1686 const void *data )
1687{
1688 double armour, shield;
1689 unsigned int attacker;
1690 const struct OnhitData *odat;
1691 int oldmem;
1692
1693 if ( po->outfit->lua_onhit == LUA_NOREF )
1694 return;
1695
1696 nlua_env env = po->outfit->lua_env;
1697
1698 /* Data. */
1699 odat = (const struct OnhitData *)data;
1700 armour = odat->armour;
1701 shield = odat->shield;
1702 attacker = odat->attacker;
1703
1704 /* Set the memory. */
1705 oldmem = pilot_outfitLmem( po, env );
1706
1707 /* Set up the function: onhit( p, po, armour, shield ) */
1708 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_onhit ); /* f */
1709 lua_pushpilot( naevL, pilot->id ); /* f, p */
1710 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
1711 lua_pushnumber( naevL, armour ); /* f, p, po, a */
1712 lua_pushnumber( naevL, shield ); /* f, p, po, a, s */
1713 lua_pushpilot( naevL, attacker ); /* f, p, po, a, s, attacker */
1714 if ( nlua_pcall( env, 5, 0 ) ) { /* */
1715 outfitLRunWarning( pilot, po->outfit, "onhit",
1716 lua_tostring( naevL, -1 ) );
1717 lua_pop( naevL, 1 );
1718 }
1719 pilot_outfitLunmem( env, oldmem );
1720}
1729void pilot_outfitLOnhit( Pilot *pilot, double armour, double shield,
1730 unsigned int attacker )
1731{
1732 const struct OnhitData data = {
1733 .armour = armour, .shield = shield, .attacker = attacker };
1734 pilot_outfitLRun( pilot, outfitLOnhit, &data );
1735}
1736
1747int pilot_outfitLOntoggle( const Pilot *pilot, PilotOutfitSlot *po, int on,
1748 int natural )
1749{
1750 nlua_env env = po->outfit->lua_env;
1751 int ret, oldmem;
1752 pilotoutfit_modified = 0;
1753
1754 /* Set the memory. */
1755 oldmem = pilot_outfitLmem( po, env );
1756
1757 /* Set up the function: ontoggle( p, po, armour, shield ) */
1758 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_ontoggle ); /* f */
1759 lua_pushpilot( naevL, pilot->id ); /* f, p */
1760 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
1761 lua_pushboolean( naevL, on ); /* f, p, po, on */
1762 lua_pushboolean( naevL, natural ); /* f, p, po, on, natural */
1763 if ( nlua_pcall( env, 4, 1 ) ) { /* */
1764 outfitLRunWarning( pilot, po->outfit, "ontoggle",
1765 lua_tostring( naevL, -1 ) );
1766 lua_pop( naevL, 1 );
1767 pilot_outfitLunmem( env, oldmem );
1768 return 0;
1769 }
1770
1771 /* Handle return boolean. */
1772 ret = lua_toboolean( naevL, -1 );
1773 lua_pop( naevL, 1 );
1774 pilot_outfitLunmem( env, oldmem );
1775 return ret;
1776}
1777
1786{
1787 nlua_env env = po->outfit->lua_env;
1788 int ret, oldmem;
1789 pilotoutfit_modified = 0;
1790
1791 /* Set the memory. */
1792 oldmem = pilot_outfitLmem( po, env );
1793
1794 /* Set up the function: onshoot( p, po, armour, shield ) */
1795 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_onshoot ); /* f */
1796 lua_pushpilot( naevL, pilot->id ); /* f, p */
1797 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
1798 if ( nlua_pcall( env, 2, 1 ) ) { /* */
1799 outfitLRunWarning( pilot, po->outfit, "onshoot",
1800 lua_tostring( naevL, -1 ) );
1801 lua_pop( naevL, 1 );
1802 pilot_outfitLunmem( env, oldmem );
1803 return 0;
1804 }
1805
1806 /* Handle return boolean. */
1807 ret = lua_toboolean( naevL, -1 );
1808 lua_pop( naevL, 1 );
1809 pilot_outfitLunmem( env, oldmem );
1810 return ret || pilotoutfit_modified; /* Even if the script says it didn't
1811 change, it may have been modified. */
1812}
1813
1815 int done;
1816 int success;
1817 double timer;
1818};
1819static void outfitLCooldown( const Pilot *pilot, PilotOutfitSlot *po,
1820 const void *data )
1821{
1822 int done, success, oldmem;
1823 double timer;
1824 const struct CooldownData *cdat;
1825
1826 if ( po->outfit->lua_cooldown == LUA_NOREF )
1827 return;
1828
1829 nlua_env env = po->outfit->lua_env;
1830
1831 cdat = (const struct CooldownData *)data;
1832 done = cdat->done;
1833 success = cdat->success;
1834 timer = cdat->timer;
1835
1836 /* Set the memory. */
1837 oldmem = pilot_outfitLmem( po, env );
1838
1839 /* Set up the function: cooldown( p, po, done, success/timer ) */
1840 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_cooldown ); /* f */
1841 lua_pushpilot( naevL, pilot->id ); /* f, p */
1842 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
1843 lua_pushboolean( naevL, done ); /* f, p, po, done */
1844 if ( done )
1845 lua_pushboolean( naevL, success ); /* f, p, po, done, success */
1846 else
1847 lua_pushnumber( naevL, timer ); /* f, p, po, done, timer */
1848 if ( nlua_pcall( env, 4, 0 ) ) { /* */
1849 outfitLRunWarning( pilot, po->outfit, "cooldown",
1850 lua_tostring( naevL, -1 ) );
1851 lua_pop( naevL, 1 );
1852 }
1853 pilot_outfitLunmem( env, oldmem );
1854}
1864void pilot_outfitLCooldown( Pilot *pilot, int done, int success, double timer )
1865{
1866 const struct CooldownData data = {
1867 .done = done, .success = success, .timer = timer };
1868 pilot_outfitLRun( pilot, outfitLCooldown, &data );
1869}
1870
1871static void outfitLOnshootany( const Pilot *pilot, PilotOutfitSlot *po,
1872 const void *data )
1873{
1874 (void)data;
1875 int oldmem;
1876 if ( po->outfit->lua_onshootany == LUA_NOREF )
1877 return;
1878
1879 nlua_env env = po->outfit->lua_env;
1880
1881 /* Set the memory. */
1882 oldmem = pilot_outfitLmem( po, env );
1883
1884 /* Set up the function: onshootany( p, po ) */
1885 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_onshootany ); /* f */
1886 lua_pushpilot( naevL, pilot->id ); /* f, p */
1887 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
1888 if ( nlua_pcall( env, 2, 0 ) ) { /* */
1889 outfitLRunWarning( pilot, po->outfit, "onshootany",
1890 lua_tostring( naevL, -1 ) );
1891 lua_pop( naevL, 1 );
1892 }
1893 pilot_outfitLunmem( env, oldmem );
1894}
1901{
1902 pilot_outfitLRun( pilot, outfitLOnshootany, NULL );
1903}
1904
1905static void outfitLOnstealth( const Pilot *pilot, PilotOutfitSlot *po,
1906 const void *data )
1907{
1908 (void)data;
1909 int oldmem;
1910 if ( po->outfit->lua_onstealth == LUA_NOREF )
1911 return;
1912
1913 nlua_env env = po->outfit->lua_env;
1914
1915 /* Set the memory. */
1916 oldmem = pilot_outfitLmem( po, env );
1917
1918 /* Set up the function: onstealth( p, po, stealthed ) */
1919 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_onstealth ); /* f */
1920 lua_pushpilot( naevL, pilot->id ); /* f, p */
1921 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
1922 lua_pushboolean(
1923 naevL, pilot_isFlag( pilot, PILOT_STEALTH ) ); /* f, p, po, steathed */
1924 if ( nlua_pcall( env, 3, 0 ) ) { /* */
1925 outfitLRunWarning( pilot, po->outfit, "onstealth",
1926 lua_tostring( naevL, -1 ) );
1927 lua_pop( naevL, 1 );
1928 }
1929 pilot_outfitLunmem( env, oldmem );
1930}
1938{
1939 pilot_outfitLRun( pilot, outfitLOnstealth, NULL );
1940 return pilotoutfit_modified;
1941}
1942
1943static void outfitLOnscan( const Pilot *pilot, PilotOutfitSlot *po,
1944 const void *data )
1945{
1946 (void)data;
1947 int oldmem;
1948 if ( po->outfit->lua_onscan == LUA_NOREF )
1949 return;
1950
1951 nlua_env env = po->outfit->lua_env;
1952
1953 /* Set the memory. */
1954 oldmem = pilot_outfitLmem( po, env );
1955
1956 /* Set up the function: onscan( p, po, target ) */
1957 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_onscan ); /* f */
1958 lua_pushpilot( naevL, pilot->id ); /* f, p */
1959 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
1960 lua_pushpilot( naevL, pilot->target ); /* f, p, po, t */
1961 if ( nlua_pcall( env, 3, 0 ) ) { /* */
1962 outfitLRunWarning( pilot, po->outfit, "onscan",
1963 lua_tostring( naevL, -1 ) );
1964 lua_pop( naevL, 1 );
1965 }
1966 pilot_outfitLunmem( env, oldmem );
1967}
1974{
1975 pilot_outfitLRun( pilot, outfitLOnscan, NULL );
1976}
1977
1978static void outfitLOnscanned( const Pilot *pilot, PilotOutfitSlot *po,
1979 const void *data )
1980{
1981 const Pilot *scanner;
1982 int oldmem;
1983 if ( po->outfit->lua_onscanned == LUA_NOREF )
1984 return;
1985
1986 nlua_env env = po->outfit->lua_env;
1987 scanner = (const Pilot *)data;
1988
1989 /* Set the memory. */
1990 oldmem = pilot_outfitLmem( po, env );
1991
1992 /* Set up the function: onscanned( p, po, stealthed ) */
1993 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_onscanned ); /* f */
1994 lua_pushpilot( naevL, pilot->id ); /* f, p */
1995 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
1996 lua_pushpilot( naevL, scanner->id ); /* f, p, po, scanner */
1997 if ( nlua_pcall( env, 3, 0 ) ) { /* */
1998 outfitLRunWarning( pilot, po->outfit, "onscanned",
1999 lua_tostring( naevL, -1 ) );
2000 lua_pop( naevL, 1 );
2001 }
2002 pilot_outfitLunmem( env, oldmem );
2003}
2010void pilot_outfitLOnscanned( Pilot *pilot, const Pilot *scanner )
2011{
2012 pilot_outfitLRun( pilot, outfitLOnscanned, scanner );
2013}
2014
2015static void outfitLOnland( const Pilot *pilot, PilotOutfitSlot *po,
2016 const void *data )
2017{
2018 (void)data;
2019 int oldmem;
2020 if ( po->outfit->lua_land == LUA_NOREF )
2021 return;
2022
2023 nlua_env env = po->outfit->lua_env;
2024
2025 /* Set the memory. */
2026 oldmem = pilot_outfitLmem( po, env );
2027
2028 /* Set up the function: land( p, po ) */
2029 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_land ); /* f */
2030 lua_pushpilot( naevL, pilot->id ); /* f, p */
2031 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
2032 if ( nlua_pcall( env, 2, 0 ) ) { /* */
2033 outfitLRunWarning( pilot, po->outfit, "land", lua_tostring( naevL, -1 ) );
2034 lua_pop( naevL, 1 );
2035 }
2036 pilot_outfitLunmem( env, oldmem );
2037}
2044{
2045 pilot_outfitLRun( pilot, outfitLOnland, NULL );
2046}
2047
2048static void outfitLOntakeoff( const Pilot *pilot, PilotOutfitSlot *po,
2049 const void *data )
2050{
2051 (void)data;
2052 int oldmem;
2053 if ( po->outfit->lua_takeoff == LUA_NOREF )
2054 return;
2055
2056 nlua_env env = po->outfit->lua_env;
2057
2058 /* Set the memory. */
2059 oldmem = pilot_outfitLmem( po, env );
2060
2061 /* Set up the function: takeoff( p, po ) */
2062 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_takeoff ); /* f */
2063 lua_pushpilot( naevL, pilot->id ); /* f, p */
2064 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
2065 if ( nlua_pcall( env, 2, 0 ) ) { /* */
2066 outfitLRunWarning( pilot, po->outfit, "takeoff",
2067 lua_tostring( naevL, -1 ) );
2068 lua_pop( naevL, 1 );
2069 }
2070 pilot_outfitLunmem( env, oldmem );
2071}
2078{
2079 pilot_outfitLRun( pilot, outfitLOntakeoff, NULL );
2080}
2081
2082static void outfitLOnjumpin( const Pilot *pilot, PilotOutfitSlot *po,
2083 const void *data )
2084{
2085 (void)data;
2086 int oldmem;
2087 if ( po->outfit->lua_jumpin == LUA_NOREF )
2088 return;
2089
2090 nlua_env env = po->outfit->lua_env;
2091
2092 /* Set the memory. */
2093 oldmem = pilot_outfitLmem( po, env );
2094
2095 /* Set up the function: takeoff( p, po ) */
2096 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_jumpin ); /* f */
2097 lua_pushpilot( naevL, pilot->id ); /* f, p */
2098 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
2099 if ( nlua_pcall( env, 2, 0 ) ) { /* */
2100 outfitLRunWarning( pilot, po->outfit, "jumpin",
2101 lua_tostring( naevL, -1 ) );
2102 lua_pop( naevL, 1 );
2103 }
2104 pilot_outfitLunmem( env, oldmem );
2105}
2112{
2113 pilot_outfitLRun( pilot, outfitLOnjumpin, NULL );
2114}
2115
2116static void outfitLOnboard( const Pilot *pilot, PilotOutfitSlot *po,
2117 const void *data )
2118{
2119 const Pilot *target;
2120 int oldmem;
2121 if ( po->outfit->lua_board == LUA_NOREF )
2122 return;
2123
2124 nlua_env env = po->outfit->lua_env;
2125 target = (const Pilot *)data;
2126
2127 /* Set the memory. */
2128 oldmem = pilot_outfitLmem( po, env );
2129
2130 /* Set up the function: board( p, po, stealthed ) */
2131 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_board ); /* f */
2132 lua_pushpilot( naevL, pilot->id ); /* f, p */
2133 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
2134 lua_pushpilot( naevL, target->id ); /* f, p, po, target */
2135 if ( nlua_pcall( env, 3, 0 ) ) { /* */
2136 outfitLRunWarning( pilot, po->outfit, "board",
2137 lua_tostring( naevL, -1 ) );
2138 lua_pop( naevL, 1 );
2139 }
2140 pilot_outfitLunmem( env, oldmem );
2141}
2148void pilot_outfitLOnboard( Pilot *pilot, const Pilot *target )
2149{
2150 pilot_outfitLRun( pilot, outfitLOnboard, target );
2151}
2152
2153static const char *outfitkeytostr( OutfitKey key )
2154{
2155 switch ( key ) {
2156 case OUTFIT_KEY_ACCEL:
2157 return "accel";
2158 case OUTFIT_KEY_LEFT:
2159 return "left";
2160 case OUTFIT_KEY_RIGHT:
2161 return "right";
2162 }
2163 return NULL;
2164}
2165
2166static void outfitLOnkeydoubletap( const Pilot *pilot, PilotOutfitSlot *po,
2167 const void *data )
2168{
2169 int oldmem;
2170 OutfitKey key;
2171 if ( po->outfit->lua_keydoubletap == LUA_NOREF )
2172 return;
2173 key = *( (const OutfitKey *)data );
2174
2175 nlua_env env = po->outfit->lua_env;
2176
2177 /* Set the memory. */
2178 oldmem = pilot_outfitLmem( po, env );
2179
2180 /* Set up the function: takeoff( p, po ) */
2181 lua_rawgeti( naevL, LUA_REGISTRYINDEX,
2182 po->outfit->lua_keydoubletap ); /* f */
2183 lua_pushpilot( naevL, pilot->id ); /* f, p */
2184 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
2185 lua_pushstring( naevL, outfitkeytostr( key ) );
2186 if ( nlua_pcall( env, 3, 1 ) ) { /* */
2187 outfitLRunWarning( pilot, po->outfit, "keydoubletap",
2188 lua_tostring( naevL, -1 ) );
2189 lua_pop( naevL, 1 );
2190 }
2191 pilot_outfitLunmem( env, oldmem );
2192
2193 /* Broke stealth. */
2194 if ( ( po->state == PILOT_OUTFIT_ON ) || lua_toboolean( naevL, -1 ) )
2195 stealth_break = 1;
2196 lua_pop( naevL, 1 );
2197}
2198void pilot_outfitLOnkeydoubletap( Pilot *pilot, OutfitKey key )
2199{
2200 if ( pilot_isDisabled( pilot ) )
2201 return;
2202 stealth_break = 0;
2203 pilot_outfitLRun( pilot, outfitLOnkeydoubletap, &key );
2204 if ( stealth_break && pilot_isFlag( pilot, PILOT_STEALTH ) )
2205 pilot_destealth( pilot );
2206}
2207
2208static void outfitLOnkeyrelease( const Pilot *pilot, PilotOutfitSlot *po,
2209 const void *data )
2210{
2211 int oldmem;
2212 OutfitKey key;
2213 if ( po->outfit->lua_keyrelease == LUA_NOREF )
2214 return;
2215 key = *( (const OutfitKey *)data );
2216
2217 nlua_env env = po->outfit->lua_env;
2218
2219 /* Set the memory. */
2220 oldmem = pilot_outfitLmem( po, env );
2221
2222 /* Set up the function: takeoff( p, po ) */
2223 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_keyrelease ); /* f */
2224 lua_pushpilot( naevL, pilot->id ); /* f, p */
2225 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
2226 lua_pushstring( naevL, outfitkeytostr( key ) );
2227 if ( nlua_pcall( env, 3, 0 ) ) { /* */
2228 outfitLRunWarning( pilot, po->outfit, "keyrelease",
2229 lua_tostring( naevL, -1 ) );
2230 lua_pop( naevL, 1 );
2231 }
2232 pilot_outfitLunmem( env, oldmem );
2233}
2234void pilot_outfitLOnkeyrelease( Pilot *pilot, OutfitKey key )
2235{
2236 pilot_outfitLRun( pilot, outfitLOnkeyrelease, &key );
2237}
2238
2245{
2246 /* TODO we might want to run this on intrinsic outfits too... */
2247 pilotoutfit_modified = 0;
2248 for ( int i = 0; i < array_size( pilot->outfits ); i++ ) {
2249 int oldmem;
2250 PilotOutfitSlot *po = pilot->outfits[i];
2251 if ( po->outfit == NULL )
2252 continue;
2253 if ( po->outfit->lua_cleanup == LUA_NOREF )
2254 continue;
2255 /* Pilot could be created and then erased without getting properly
2256 * initialized. */
2257 if ( po->lua_mem == LUA_NOREF )
2258 continue;
2259
2260 nlua_env env = po->outfit->lua_env;
2261
2262 /* Set the memory. */
2263 oldmem = pilot_outfitLmem( po, env );
2264
2265 /* Set up the function: cleanup( p, po ) */
2266 lua_rawgeti( naevL, LUA_REGISTRYINDEX, po->outfit->lua_cleanup ); /* f */
2267 lua_pushpilot( naevL, pilot->id ); /* f, p */
2268 lua_pushpilotoutfit( naevL, po ); /* f, p, po */
2269 if ( nlua_pcall( env, 2, 0 ) ) { /* */
2270 outfitLRunWarning( pilot, po->outfit, "cleanup",
2271 lua_tostring( naevL, -1 ) );
2272 lua_pop( naevL, 1 );
2273 }
2274 pilot_outfitLunmem( env, oldmem );
2275 }
2276 /* Pilot gets cleaned up so no need to recalculate stats. */
2277}
Provides macros to work with dynamic arrays.
#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_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
void effect_compute(ShipStats *s, const Effect *efxlist)
Updates shipstats from effect list.
Definition effect.c:548
void escort_rmListIndex(Pilot *p, int i)
Remove from escorts list.
Definition escort.c:70
void gui_setShip(void)
Player just upgraded their ship or modified it.
Definition gui.c:1880
void gui_setGeneric(const Pilot *pilot)
Calls trigger functions depending on who the pilot is.
Definition gui.c:1913
void mat4_mul_vec(vec3 *out, const mat4 *m, const vec3 *v)
Multiplies a matrix with a vector (out = m * v);.
Definition mat4.c:67
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:39
#define ABS(x)
Definition naev.h:32
#define pow2(x)
Definition naev.h:53
#define FABS(x)
Definition naev.h:34
#define MAX(x, y)
Definition naev.h:37
lua_State * naevL
Definition nlua.c:54
const Outfit ** lua_pushoutfit(lua_State *L, const Outfit *outfit)
Pushes a outfit on the stack.
LuaPilot * lua_pushpilot(lua_State *L, LuaPilot pilot)
Pushes a pilot on the stack.
Definition nlua_pilot.c:576
PilotOutfitSlot ** lua_pushpilotoutfit(lua_State *L, PilotOutfitSlot *po)
Pushes a pilot outfit on the stack.
int scnprintf(char *text, size_t maxlen, const char *fmt,...)
Like snprintf(), but returns the number of characters ACTUALLY "printed" into the buffer....
Definition nstring.c:102
int outfit_isBeam(const Outfit *o)
Checks if outfit is a beam type weapon.
Definition outfit.c:639
double outfit_cpu(const Outfit *o)
Gets the outfit's cpu usage.
Definition outfit.c:891
int outfit_isActive(const Outfit *o)
Checks if outfit is an active outfit.
Definition outfit.c:567
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_isToggleable(const Outfit *o)
Checks if outfit can be toggled.
Definition outfit.c:584
int outfit_fitsSlot(const Outfit *o, const OutfitSlot *s)
Checks to see if an outfit fits a slot.
Definition outfit.c:1180
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_ammoMass(const Outfit *o)
Gets the outfit's ammunition mass.
Definition outfit.c:1017
int outfit_isForward(const Outfit *o)
Checks if outfit is a fixed mounted weapon.
Definition outfit.c:619
int outfit_isMod(const Outfit *o)
Checks if outfit is a ship modification.
Definition outfit.c:683
int outfit_isBolt(const Outfit *o)
Checks if outfit is bolt type weapon.
Definition outfit.c:629
mat4 pilot_local_transform(const Pilot *p)
Gets the local transformation matrix of a pilot.
Definition pilot.c:4507
PilotOutfitSlot * pilot_getDockSlot(Pilot *p)
Gets the dock slot of the pilot.
Definition pilot.c:793
Pilot * pilot_get(unsigned int id)
Pulls a pilot out of the pilot_stack based on ID.
Definition pilot.c:640
void pilot_delete(Pilot *p)
Deletes a pilot.
Definition pilot.c:2965
int pilot_numOutfit(const Pilot *p, const Outfit *o)
Checks to see how many of an outfit a pilot has.
Definition pilot.c:3269
void pilot_cargoCalc(Pilot *pilot)
Calculates how much cargo ship has left and such.
void pilot_destealth(Pilot *p)
Destealths a pilot.
Definition pilot_ew.c:585
double pilot_ewWeaponTrack(const Pilot *p, const Pilot *t, double trackmin, double trackmax)
Calculates the weapon lead (1. is 100%, 0. is 0%)..
Definition pilot_ew.c:407
void pilot_ewUpdateStatic(Pilot *p)
Updates the pilot's static electronic warfare properties.
Definition pilot_ew.c:101
void pilot_heatCalcSlot(PilotOutfitSlot *o)
Calculates the heat parameters for a pilot's slot.
Definition pilot_heat.c:85
void pilot_heatCalc(Pilot *p)
Calculates the heat parameters for a pilot.
Definition pilot_heat.c:32
int pilot_runHook(Pilot *p, int hook_type)
Tries to run a pilot hook if he has it.
Definition pilot_hook.c:104
int pilot_getMount(const Pilot *p, const PilotOutfitSlot *w, vec2 *v)
Gets the mount position of a pilot.
static int stealth_break
void pilot_lockUpdateSlot(Pilot *p, PilotOutfitSlot *o, Pilot *t, Target *wt, double *a, double dt)
Updates the lockons on the pilot's launchers.
int pilot_rmOutfit(Pilot *pilot, PilotOutfitSlot *s)
Removes an outfit from the pilot.
void pilot_updateMass(Pilot *pilot)
Updates the pilot stats after mass change.
int pilot_addOutfitIntrinsicRaw(Pilot *pilot, const Outfit *outfit)
Adds an outfit as an intrinsic slot.
void pilot_outfitLCooldown(Pilot *pilot, int done, int success, double timer)
Handle cooldown hooks for outfits.
int pilot_hasOutfitLimit(const Pilot *p, const char *limit)
Checks to see if a pilot has an outfit with a specific outfit type.
void pilot_outfitLOutfofenergy(Pilot *pilot)
Handles when the pilot runs out of energy.
const char * pilot_outfitDescription(const Pilot *p, const Outfit *o)
Gets the description of an outfit for a given pilot.
void pilot_healLanded(Pilot *pilot)
Cures the pilot as if he was landed.
int pilot_slotsCheckRequired(const Pilot *p)
Pilot required (core) slot filled check - makes sure they are filled.
int pilot_slotsCheckSafety(const Pilot *p)
Pilot slot safety check - makes sure stats are safe.
int pilot_outfitLAdd(const Pilot *pilot, PilotOutfitSlot *po)
Outfit is added to a ship.
int pilot_maxAmmoO(const Pilot *p, const Outfit *o)
Gets the maximum available ammo for a pilot for a specific outfit.
int pilot_hasDeployed(const Pilot *p)
Checks to see if the pilot has deployed ships.
static void pilot_outfitLunmem(nlua_env env, int oldmem)
Cleans up the outfit memory for a slot.
int pilot_addOutfit(Pilot *pilot, const Outfit *outfit, PilotOutfitSlot *s)
Adds an outfit to the pilot.
void pilot_outfitLOnjumpin(Pilot *pilot)
Runs Lua outfits when pilot jumps into a system.
int pilot_slotIsToggleable(const PilotOutfitSlot *o)
Checks to see if a slot has an active outfit that can be toggleable.
void pilot_outfitLOntakeoff(Pilot *pilot)
Runs Lua outfits when pilot takes off from a spob.
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.
void pilot_fillAmmo(Pilot *pilot)
Fills pilot's ammo completely.
int pilot_addAmmo(Pilot *pilot, PilotOutfitSlot *s, int quantity)
Adds some ammo to the pilot stock.
int pilot_hasIntrinsic(const Pilot *pilot, const Outfit *outfit)
Gets how many copies of an intrinsic a pilot has.
const char * pilot_outfitSummary(const Pilot *p, const Outfit *o, int withname)
Gets the summary of an outfit for a give pilot.
int pilot_rmOutfitRaw(Pilot *pilot, PilotOutfitSlot *s)
Removes an outfit from the pilot without doing any checks.
static void pilot_calcStatsSlot(Pilot *pilot, PilotOutfitSlot *slot)
Computes the stats for a pilot's slot.
int pilot_outfitLInit(const Pilot *pilot, PilotOutfitSlot *po)
Runs the pilot's Lua outfits init script for an outfit.
int pilot_countAmmo(const Pilot *pilot)
Gets the number of ammo units on the ship.
void pilot_lockClear(Pilot *p)
Clears pilot's missile lockon timers.
int pilot_outfitLOnstealth(Pilot *pilot)
Runs the pilot's Lua outfits onhit script.
void pilot_outfitLCleanup(Pilot *pilot)
Handle cleanup hooks for outfits.
PilotOutfitSlot * pilot_getSlotByName(Pilot *pilot, const char *name)
Gets the outfit slot by name.
static int pilot_outfitLmem(PilotOutfitSlot *po, nlua_env env)
Sets up the outfit memory for a slot.
int pilot_maxAmmo(const Pilot *pilot)
The maximum amount of ammo the pilot's current ship can hold.
void pilot_outfitLOnscan(Pilot *pilot)
Runs Lua outfits when pilot scanned their target.
int pilot_reportSpaceworthy(const Pilot *p, char *buf, int bufSize)
Pilot safety report - makes sure stats are safe.
int pilot_outfitLOntoggle(const Pilot *pilot, PilotOutfitSlot *po, int on, int natural)
Handle the manual toggle of an outfit.
int pilot_addOutfitTest(Pilot *pilot, const Outfit *outfit, const PilotOutfitSlot *s, int warn)
Tests to see if an outfit can be added.
int pilot_outfitLOnshoot(const Pilot *pilot, PilotOutfitSlot *po)
Handle the manual shoot of an outfit.
void pilot_outfitLOnboard(Pilot *pilot, const Pilot *target)
Runs Lua outfits when pilot boards a target.
int pilot_outfitLRemove(const Pilot *pilot, PilotOutfitSlot *po)
Outfit is removed froma ship.
int pilot_rmAmmo(Pilot *pilot, PilotOutfitSlot *s, int quantity)
Removes some ammo from the pilot stock.
void pilot_outfitLOnland(Pilot *pilot)
Runs Lua outfits when pilot lands on a spob.
int pilot_dock(Pilot *p, Pilot *target)
Docks the pilot on its target pilot.
void pilot_outfitLOnscanned(Pilot *pilot, const Pilot *scanner)
Runs Lua outfits when pilot was scanned by scanner.
const char * pilot_canEquip(const Pilot *p, const PilotOutfitSlot *s, const Outfit *o)
Checks to see if can equip/remove an outfit from a slot.
void pilot_outfitLOnshootany(Pilot *pilot)
Runs the pilot's Lua outfits onshootany script.
void pilot_outfitLUpdate(Pilot *pilot, double dt)
Runs the pilot's Lua outfits update script.
int pilot_addOutfitRaw(Pilot *pilot, const Outfit *outfit, PilotOutfitSlot *s)
Adds an outfit to the pilot, ignoring CPU or other limits.
static void pilot_outfitLRun(Pilot *p, void(*const func)(const Pilot *p, PilotOutfitSlot *po, const void *data), const void *data)
Wrapper that does all the work for us.
int pilot_rmOutfitIntrinsic(Pilot *pilot, const Outfit *outfit)
Removes an outfit from an intrinsic slot.
int pilot_isSpaceworthy(const Pilot *p)
Pilot safety check - makes sure stats are safe.
int pilot_addOutfitIntrinsic(Pilot *pilot, const Outfit *outfit)
Adds an outfit as an intrinsic slot.
void pilot_outfitLOnhit(Pilot *pilot, double armour, double shield, unsigned int attacker)
Runs the pilot's Lua outfits onhit script.
double pilot_outfitSpeed(const Pilot *p, const Outfit *o)
Gets the speed of an outfit given a pilot.
void pilot_outfitLInitAll(Pilot *pilot)
Runs the pilot's Lua outfits init script.
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.
void pilot_weapSetUpdateStats(Pilot *p)
Update the weapon sets given pilot stat changes.
void player_resetSpeed(void)
Resets the player speed stuff.
Definition player.c:1521
void ss_free(ShipStatList *ll)
Frees a list of ship stats.
Definition shipstats.c:934
int ss_statsMergeFromList(ShipStats *stats, const ShipStatList *list)
Updates a stat structure from a stat list.
Definition shipstats.c:666
int sp_required(unsigned int spid)
Gets whether or not a slot property is required.
Definition slots.c:185
StarSystem * cur_system
Definition space.c:110
Represents a single asteroid.
Definition asteroid.h:88
Solid sol
Definition asteroid.h:98
Stores an escort.
Definition pilot.h:252
unsigned int id
Definition pilot.h:255
EscortType_t type
Definition pilot.h:254
double range
Definition outfit.h:222
double falloff
Definition outfit.h:185
double range
Definition outfit.h:184
double speed
Definition outfit.h:183
unsigned int spid
Definition outfit.h:140
A ship outfit, depends radically on the type.
Definition outfit.h:372
union Outfit::@052125200133344144252153256241104103242010347340 u
int lua_ontoggle
Definition outfit.h:427
int lua_onscan
Definition outfit.h:434
char * limit
Definition outfit.h:386
int lua_jumpin
Definition outfit.h:438
OutfitLauncherData lau
Definition outfit.h:456
int lua_cooldown
Definition outfit.h:435
char * desc_raw
Definition outfit.h:392
OutfitBeamData bem
Definition outfit.h:455
OutfitBoltData blt
Definition outfit.h:454
int lua_keyrelease
Definition outfit.h:441
int lua_takeoff
Definition outfit.h:437
int lua_outofenergy
Definition outfit.h:430
int lua_land
Definition outfit.h:436
int lua_onscanned
Definition outfit.h:433
int lua_onshootany
Definition outfit.h:431
int lua_update
Definition outfit.h:426
int lua_descextra
Definition outfit.h:419
OutfitSlot slot
Definition outfit.h:380
int lua_onhit
Definition outfit.h:429
char * summary_raw
Definition outfit.h:393
OutfitAfterburnerData afb
Definition outfit.h:458
int lua_onshoot
Definition outfit.h:428
OutfitFighterBayData bay
Definition outfit.h:459
int lua_cleanup
Definition outfit.h:425
ShipStatList * stats
Definition outfit.h:410
nlua_env lua_env
Definition outfit.h:418
int lua_onstealth
Definition outfit.h:432
int lua_onadd
Definition outfit.h:421
int lua_board
Definition outfit.h:439
int lua_keydoubletap
Definition outfit.h:440
int lua_onremove
Definition outfit.h:422
double mass
Definition outfit.h:384
char * desc_extra
Definition outfit.h:394
char * name
Definition outfit.h:373
int lua_init
Definition outfit.h:424
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
PilotOutfitState state
Definition pilot.h:160
ShipStatList * lua_stats
Definition pilot.h:180
double timer
Definition pilot.h:162
ShipOutfitSlot * sslot
Definition pilot.h:151
const Outfit * outfit
Definition pilot.h:149
The representation of an in-game pilot.
Definition pilot.h:263
int nturrets
Definition pilot.h:364
ShipStatList * ship_stats
Definition pilot.h:343
ShipStats stats
Definition pilot.h:348
double accel
Definition pilot.h:291
double shield
Definition pilot.h:303
double speed_limit
Definition pilot.h:296
unsigned int id
Definition pilot.h:264
double turn_base
Definition pilot.h:298
int cpu_max
Definition pilot.h:285
double crew
Definition pilot.h:286
unsigned int parent
Definition pilot.h:390
double energy_regen
Definition pilot.h:316
PilotOutfitSlot ** outfits
Definition pilot.h:354
PilotOutfitSlot * outfit_intrinsic
Definition pilot.h:360
int outfitlupdate
Definition pilot.h:368
double armour_max
Definition pilot.h:304
double speed
Definition pilot.h:293
double speed_base
Definition pilot.h:294
const Ship * ship
Definition pilot.h:274
double sbonus
Definition pilot.h:431
double fuel_max
Definition pilot.h:309
ShipStatList * intrinsic_stats
Definition pilot.h:345
int ncannons
Definition pilot.h:363
double energy
Definition pilot.h:314
double stress
Definition pilot.h:302
double mass_cargo
Definition pilot.h:277
double stimer
Definition pilot.h:430
double energy_max
Definition pilot.h:315
Solid solid
Definition pilot.h:275
double fuel
Definition pilot.h:310
PilotOutfitSlot * afterburner
Definition pilot.h:372
double accel_base
Definition pilot.h:292
char * name
Definition pilot.h:265
int nbeams
Definition pilot.h:365
double mass_outfit
Definition pilot.h:278
Escort_t * escorts
Definition pilot.h:391
int cpu
Definition pilot.h:284
double fuel_consumption
Definition pilot.h:311
double base_mass
Definition pilot.h:276
int nfighterbays
Definition pilot.h:366
double shield_regen
Definition pilot.h:307
unsigned int target
Definition pilot.h:400
int nafterburners
Definition pilot.h:367
Effect * effects
Definition pilot.h:351
double cap_cargo
Definition pilot.h:288
double turn
Definition pilot.h:297
double armour
Definition pilot.h:301
double shield_max
Definition pilot.h:305
double dmg_absorb
Definition pilot.h:308
double armour_regen
Definition pilot.h:306
Represents a ship weapon mount point.
Definition ship.h:64
vec3 pos
Definition ship.h:65
char * name
Definition ship.h:73
OutfitSlot slot
Definition ship.h:72
Represents ship statistics, properties ship can use.
Definition shipstats.h:229
double energy
Definition shipstats.h:239
double armour_regen
Definition shipstats.h:250
double armour
Definition shipstats.h:249
double crew_mod
Definition shipstats.h:287
double ammo_capacity
Definition shipstats.h:301
double engine_limit
Definition shipstats.h:340
double cargo_inertia
Definition shipstats.h:271
double shield_regen_malus
Definition shipstats.h:248
double shield_regen
Definition shipstats.h:245
double mass_mod
Definition shipstats.h:288
double energy_regen_malus
Definition shipstats.h:243
double disable
Definition shipstats.h:255
double absorb
Definition shipstats.h:263
double energy_regen
Definition shipstats.h:240
double turn_mod
Definition shipstats.h:235
double armour_regen_mod
Definition shipstats.h:252
double shield
Definition shipstats.h:244
double accel_mod
Definition shipstats.h:236
double accel
Definition shipstats.h:233
double shield_regen_mod
Definition shipstats.h:247
double speed
Definition shipstats.h:231
double speed_mod
Definition shipstats.h:234
double engine_limit_rel
Definition shipstats.h:339
double fuel_usage_mod
Definition shipstats.h:260
double cpu_max
Definition shipstats.h:262
double shield_mod
Definition shipstats.h:246
double armour_regen_malus
Definition shipstats.h:253
double fuel_mod
Definition shipstats.h:259
double cpu_mod
Definition shipstats.h:261
double energy_regen_mod
Definition shipstats.h:242
double time_mod
Definition shipstats.h:356
double energy_mod
Definition shipstats.h:241
double turn
Definition shipstats.h:232
double armour_mod
Definition shipstats.h:251
double cargo_mod
Definition shipstats.h:258
double shield_regen
Definition ship.h:142
double cap_cargo
Definition ship.h:135
ShipStats stats_array
Definition ship.h:182
int fuel
Definition ship.h:133
double energy_regen
Definition ship.h:144
double armour
Definition ship.h:139
int fuel_consumption
Definition ship.h:134
double armour_regen
Definition ship.h:140
int crew
Definition ship.h:130
double dmg_absorb
Definition ship.h:145
double cpu
Definition ship.h:132
double speed
Definition ship.h:127
double turn
Definition ship.h:126
double accel
Definition ship.h:125
double size
Definition ship.h:149
double energy
Definition ship.h:143
double shield
Definition ship.h:141
double mass
Definition ship.h:131
vec2 vel
Definition physics.h:48
double mass
Definition physics.h:45
vec2 pos
Definition physics.h:49
Represents a weapon target.
Definition target.h:19
Definition mat4.h:12
Represents a 2d vector.
Definition vec2.h:45
double y
Definition vec2.h:47
double x
Definition vec2.h:46
Definition vec3.h:6