naev 0.12.6
equipment.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include <math.h>
11#include <stdio.h>
12#include <stdlib.h>
13
14#include "naev.h"
16
17#include "equipment.h"
18
19#include "array.h"
20#include "conf.h"
21#include "debug.h"
22#include "dialogue.h"
23#include "equipment.h"
24#include "escort.h"
25#include "gui.h"
26#include "hook.h"
27#include "info.h"
28#include "land.h"
29#include "land_outfits.h"
30#include "log.h"
31#include "ndata.h"
32#include "nlua.h"
33#include "nlua_tk.h"
34#include "nstring.h"
35#include "ntime.h"
36#include "pilot_outfit.h"
37#include "player.h"
38#include "player_fleet.h"
39#include "shipstats.h"
40#include "slots.h"
41#include "tk/toolkit_priv.h" /* Yes, I'm a bad person, abstractions be damned! */
42#include "toolkit.h"
43
44/*
45 * Image array names.
46 */
47#define EQUIPMENT_SHIPS "iarAvailShips"
48#define EQUIPMENT_OUTFIT_TAB "tabOutfits"
49#define EQUIPMENT_OUTFITS "iarAvailOutfits"
50#define EQUIPMENT_FILTER "inpFilterOutfits"
51#define OUTFIT_TABS 5
52
53/* global/main window */
54#define BUTTON_WIDTH 200
55#define BUTTON_HEIGHT 40
56
57typedef struct ShipWidgetData_ {
58 unsigned int wid;
59 GLuint fbo;
60 GLuint tex;
61 GLuint texd;
62 double s;
64
65/*
66 * equipment stuff
67 */
69static double equipment_dir = 0.;
70static unsigned int equipment_lastick = 0;
71static unsigned int equipment_wid = 0;
72static int equipment_creating = 0;
73static int ship_mode = 0;
74static iar_data_t iar_data[OUTFIT_TABS];
75static Outfit **iar_outfits[OUTFIT_TABS];
77static nlua_env autoequip_env = LUA_NOREF; /* Autoequip env. */
78static int equipment_outfitMode = 0;
79
80/*
81 * prototypes
82 */
83/* Creation. */
84static void equipment_getDim( unsigned int wid, int *w, int *h, int *sw,
85 int *sh, int *ow, int *oh, int *ew, int *eh,
86 int *cw, int *ch, int *bw, int *bh );
87static void equipment_genShipList( unsigned int wid );
88static void equipment_genOutfitList( unsigned int wid );
89/* Widget. */
90static void equipment_genLists( unsigned int wid );
91static void equipment_toggleFav( unsigned int wid, const char *wgt );
92static void equipment_toggleDeploy( unsigned int wid, const char *wgt );
93static void equipment_renderColumn( double x, double y, double w, double h,
94 const PilotOutfitSlot *lst, const char *txt,
95 int selected, Outfit *o, Pilot *p,
96 const CstSlotWidget *wgt );
97static void equipment_renderSlots( double bx, double by, double bw, double bh,
98 void *data );
99static void equipment_renderMisc( double bx, double by, double bw, double bh,
100 void *data );
101static void equipment_renderOverlayColumn( double x, double y, double h,
102 PilotOutfitSlot *lst, int mover,
103 CstSlotWidget *wgt );
104static void equipment_renderOverlaySlots( double bx, double by, double bw,
105 double bh, void *data );
106static void equipment_renderShip( double bx, double by, double bw, double bh,
107 void *data );
108static void equipment_freeShipData( unsigned int wid, const char *unused );
109static int equipment_mouseInColumn( const PilotOutfitSlot *lst, double y,
110 double h, double my );
111static int equipment_mouseSlots( unsigned int wid, const SDL_Event *event,
112 double x, double y, double w, double h,
113 double rx, double ry, void *data );
114/* Misc. */
115static char eq_qCol( double cur, double base, int inv );
116static int equipment_swapSlot( unsigned int wid, Pilot *p,
117 PilotOutfitSlot *slot );
118static void equipment_sellShip( unsigned int wid, const char *str );
119static void equipment_renameShip( unsigned int wid, const char *str );
120static void equipment_shipMode( unsigned int wid, const char *str );
121static void equipment_rightClickShips( unsigned int wid, const char *str );
122static void equipment_transChangeShip( unsigned int wid, const char *str );
123static void equipment_changeShip( unsigned int wid );
124static void equipment_unequipShip( unsigned int wid, const char *str );
125static void equipment_autoequipShip( unsigned int wid, const char *str );
126static void equipment_filterOutfits( unsigned int wid, const char *str );
127static void equipment_rightClickOutfits( unsigned int wid, const char *str );
128static void equipment_outfitPopdown( unsigned int wid, const char *str );
129static void equipment_changeTab( unsigned int wid, const char *wgt, int old,
130 int tab );
131static int equipment_playerAddOutfit( const Outfit *o, int quantity );
132static int equipment_playerRmOutfit( const Outfit *o, int quantity );
133
134/* Filters. */
135static int equipment_filter( const Outfit *o );
136static int equipment_filterWeapon( const Outfit *o );
137static int equipment_filterUtility( const Outfit *o );
138static int equipment_filterStructure( const Outfit *o );
139static int equipment_filterCore( const Outfit *o );
140static int ( *tabfilters[] )( const Outfit *o ) = {
141 equipment_filter, equipment_filterWeapon, equipment_filterUtility,
142 equipment_filterStructure, equipment_filterCore,
143};
144
150void equipment_rightClickOutfits( unsigned int wid, const char *str )
151{
152 (void)str;
153 Outfit *o;
154 int id, active, minimal, n, nfits;
155 PilotOutfitSlot *slots;
156 Pilot *p;
157 OutfitSlotSize size;
158
159 active = window_tabWinGetActive( wid, EQUIPMENT_OUTFIT_TAB );
160 id = toolkit_getImageArrayPos( wid, EQUIPMENT_OUTFITS );
161
162 /* Did the user click on background or placeholder cell? */
163 if ( id < 0 || array_size( iar_outfits[active] ) == 0 )
164 return;
165
166 o = iar_outfits[active][id];
167 if ( o == NULL )
168 return;
169
170 /* Figure out which slot this stuff fits into */
171 switch ( o->slot.type ) {
172 case OUTFIT_SLOT_STRUCTURE:
173 slots = eq_wgt.selected->p->outfit_structure;
174 break;
175 case OUTFIT_SLOT_UTILITY:
176 slots = eq_wgt.selected->p->outfit_utility;
177 break;
178 case OUTFIT_SLOT_WEAPON:
179 slots = eq_wgt.selected->p->outfit_weapon;
180 break;
181 default:
182 return;
183 }
184 n = array_size( slots );
185
186 /* See how many slots it fits into. */
187 nfits = 0;
188 minimal = n;
189 for ( int i = 0; i < n; i++ ) {
190 /* Must fit the slot. */
191 if ( !outfit_fitsSlot( o, &slots[i].sslot->slot ) )
192 continue;
193
194 /* Must have valid slot size. */
195 if ( o->slot.size == OUTFIT_SLOT_SIZE_NA )
196 continue;
197
198 minimal = i;
199 nfits++;
200 }
201 /* Only fits in a single slot, so we might as well just swap it. */
202 if ( nfits == 1 ) {
203 eq_wgt.outfit = o;
204 p = eq_wgt.selected->p;
205 /* We have to call once to remove, once to add. */
206 if ( slots[minimal].outfit != NULL )
207 equipment_swapSlot( equipment_wid, p, &slots[minimal] );
208 eq_wgt.outfit = o;
209 equipment_swapSlot( equipment_wid, p, &slots[minimal] );
210
211 hooks_run( "equip" ); /* Equipped. */
212 return;
213 }
214
215 /* See if limit is applied and swap with shared limit slot. */
216 if ( o->limit != NULL ) {
217 minimal = n;
218 for ( int i = 0; i < n; i++ ) {
219 /* Must fit the slot. */
220 if ( !outfit_fitsSlot( o, &slots[i].sslot->slot ) )
221 continue;
222
223 /* Must have valid slot size. */
224 if ( o->slot.size == OUTFIT_SLOT_SIZE_NA )
225 continue;
226
227 /* Must have outfit with limit. */
228 if ( ( slots[i].outfit == NULL ) ||
229 ( slots[i].outfit->limit == NULL ) )
230 continue;
231
232 /* Must share a limit to be able to swap. */
233 if ( strcmp( slots[i].outfit->limit, o->limit ) != 0 )
234 continue;
235
236 minimal = i;
237 }
238 if ( minimal < n ) {
239 eq_wgt.outfit = o;
240 p = eq_wgt.selected->p;
241 /* Once to unequip and once to equip. */
242 equipment_swapSlot( equipment_wid, p, &slots[minimal] );
243 eq_wgt.outfit = o;
244 equipment_swapSlot( equipment_wid, p, &slots[minimal] );
245 hooks_run( "equip" ); /* Equipped. */
246 return;
247 }
248 }
249
250 /* Loop through outfit slots of the right type, try to find an empty one */
251 size = OUTFIT_SLOT_SIZE_NA;
252 minimal = n;
253 for ( int i = 0; i < n; i++ ) {
254 /* Slot full. */
255 if ( slots[i].outfit != NULL )
256 continue;
257
258 /* Must fit the slot. */
259 if ( !outfit_fitsSlot( o, &slots[i].sslot->slot ) )
260 continue;
261
262 /* Must have valid slot size. */
263 if ( o->slot.size == OUTFIT_SLOT_SIZE_NA )
264 continue;
265
266 /* Search for the smallest slot avaliable. */
267 if ( ( size == OUTFIT_SLOT_SIZE_NA ) ||
268 ( slots[i].sslot->slot.size < size ) ) {
269 size = slots[i].sslot->slot.size;
270 minimal = i;
271 }
272 }
273
274 /* Use the chosen one (if any). */
275 if ( minimal < n ) {
276 eq_wgt.outfit = o;
277 p = eq_wgt.selected->p;
278 equipment_swapSlot( equipment_wid, p, &slots[minimal] );
279 hooks_run( "equip" ); /* Equipped. */
280 }
281}
282
286static void equipment_getDim( unsigned int wid, int *w, int *h, int *sw,
287 int *sh, int *ow, int *oh, int *ew, int *eh,
288 int *cw, int *ch, int *bw, int *bh )
289{
290 int ssw, ssh;
291 /* Get window dimensions. */
292 window_dimWindow( wid, w, h );
293
294 /* Calculate image array dimensions. */
295 ssw = 550 + ( *w - LAND_WIDTH );
296 ssh = ( *h - 100 );
297 if ( sw != NULL )
298 *sw = ssw;
299 if ( sh != NULL )
300 *sh = ( 1 * ssh ) / 3;
301 if ( ow != NULL )
302 *ow = ssw;
303 if ( oh != NULL )
304 *oh = ( 2 * ssh ) / 3;
305
306 /* Calculate slot widget. */
307 if ( ew != NULL )
308 *ew = 180;
309 if ( eh != NULL )
310 *eh = *h - 100;
311
312 /* Calculate custom widget. */
313 if ( cw != NULL )
314 *cw = 120;
315 if ( ch != NULL )
316 *ch = 140 + ( ( player.fleet_capacity > 0 ) ? 40 : 0 );
317
318 /* Calculate button dimensions. */
319 if ( bw != NULL )
320 *bw = MIN( LAND_BUTTON_WIDTH,
321 ( *w - 20 - ( sw != NULL ? *sw : 0 ) - 40 - 20 - 60 ) / 5 );
322 if ( bh != NULL )
323 *bh = BUTTON_HEIGHT;
324}
325
329void equipment_open( unsigned int wid )
330{
331 int w, h, sw, sh, ow, oh, bw, bh, ew, eh, cw, ch, x, y;
332 ShipWidgetData *swd;
333
334 /* Load the outfit mode. */
335 equipment_outfitMode = player.eq_outfitMode;
336
337 /* Mark as generated. */
338 land_tabGenerate( LAND_WINDOW_EQUIPMENT );
339
340 /* Set global WID. */
341 equipment_wid = wid;
343
344 /* Get dimensions. */
345 equipment_getDim( wid, &w, &h, &sw, &sh, &ow, &oh, &ew, &eh, &cw, &ch, &bw,
346 &bh );
347
348 /* Buttons */
349 x = -20;
350 y = 20;
351 window_addButtonKey( wid, x, y, bw, bh, "btnCloseEquipment", _( "Take Off" ),
352 land_buttonTakeoff, SDLK_t );
353 x -= ( 15 + bw );
354 window_addButtonKey( wid, x, y, bw, bh, "btnSellShip", _( "Sell Ship" ),
355 equipment_sellShip, SDLK_s );
356 x -= ( 15 + bw );
357 window_addButtonKey( wid, x, y, bw, bh, "btnChangeShip", _( "Swap Ship" ),
359 x -= ( 15 + bw );
360 window_addButtonKey( wid, x, y, bw, bh, "btnUnequipShip", _( "Unequip" ),
361 equipment_unequipShip, SDLK_u );
362 x -= ( 15 + bw );
363 window_addButtonKey( wid, x, y, bw, bh, "btnAutoequipShip", _( "Autoequip" ),
364 equipment_autoequipShip, SDLK_a );
365
366 /* Prepare the outfit array. */
367 for ( int i = 0; i < OUTFIT_TABS; i++ ) {
368 toolkit_initImageArrayData( &iar_data[i] );
370 }
371 memset( iar_outfits, 0, sizeof( Outfit ** ) * OUTFIT_TABS );
372
373 /* Safe defaults. */
374 equipment_lastick = SDL_GetTicks();
375 equipment_dir = 0.;
376
377 /* Add ammo. */
379
380 /* text */
381 x = 20 + sw + 20 + 180 + 10;
382 y = -40;
383 window_addText( wid, x, y, 130, y - 20 + h - bh, 0, "txtSDesc", &gl_defFont,
384 &cFontGrey, NULL );
385 window_addText( wid, x, y, w - x - 128 - 30, y - 20 + h - bh, 0,
386 "txtAcquired", &gl_defFont, NULL, NULL );
387 x += 130;
388 window_addText( wid, x, y, w - x - 20 - 128 - 20, y - 20 + h - bh, 0,
389 "txtDDesc", &gl_defFont, NULL, NULL );
390
391 /* Generate lists. */
392 window_addText( wid, 30, -20, ow, gl_defFont.h, 0, "txtShipTitle",
393 &gl_defFont, NULL, _( "Available Ships" ) );
394 window_addText( wid, 30, -40 - sh - 20, ow, gl_defFont.h, 0,
395 "txtOutfitTitle", &gl_defFont, NULL,
396 _( "Available Outfits" ) );
397
398 /* Favourite checkbox, run before genLists. */
399 x = -20 - ( 128 - cw ) / 2;
400 y = -20 - 150 - ch;
401 window_addCheckbox( wid, x, y, cw, 30, "chkFav", _( "Favourite" ),
403 if ( player.fleet_capacity > 0 ) {
404 y -= 23;
405 window_addCheckbox( wid, x, y, cw, 30, "chkDeploy", _( "Deployed" ),
406 equipment_toggleDeploy, 0 );
407 }
408 x = -16;
409 y -= 23 + 10;
410 window_addButtonKey( wid, x, y, 128 + 8, bh, "btnRenameShip", _( "Rename" ),
411 equipment_renameShip, SDLK_r );
412 y -= bh + 10;
413 window_addButtonKey( wid, x, y, 128 + 8, bh, "btnShipMode",
414 _( "Toggle Display" ), equipment_shipMode, SDLK_d );
415
416 /* Generate lists. */
417 equipment_genLists( wid );
418
419 /* Slot widget. Designed so that 10 slots barely fit. */
420 equipment_slotWidget( wid, 20 + sw + 15, -40 - 5, ew, eh, &eq_wgt );
422 eq_wgt.canmodify = 1;
423
424 /* Separator. */
425 // window_addRect( wid, 20 + sw + 20, -40, 2, h-60, "rctDivider", &cGrey50, 0
426 // );
427
428 /* Custom widget (ship information). */
429 window_addCust( wid, -20 - ( 128 - cw ) / 2, -20 - 150, cw, ch, "cstMisc", 0,
430 equipment_renderMisc, NULL, NULL, NULL, NULL );
431 window_canFocusWidget( wid, "cstMisc", 0 );
432
433 /* Spinning ship. */
434 swd = malloc( sizeof( ShipWidgetData ) );
435 swd->wid = wid;
436 swd->s = ship_maxSize() / gl_screen.scale;
437 gl_fboCreate( &swd->fbo, &swd->tex, swd->s, swd->s );
438 gl_fboAddDepth( swd->fbo, &swd->texd, swd->s, swd->s );
439 window_addRect( wid, -20 + 4, -40 + 4, 128 + 8, 128 + 8, "rctShip", &cBlack,
440 1 );
441 window_addCust( wid, -20, -40, 128, 128, "cstShip", 0, equipment_renderShip,
442 NULL, NULL, NULL, swd );
443 window_custSetDynamic( wid, "cstShip", 1 );
444 window_canFocusWidget( wid, "cstShip", 0 );
445 window_onClose( wid, equipment_freeShipData );
446
447 /* Focus the ships image array. */
448 window_setFocus( wid, EQUIPMENT_SHIPS );
450}
451
462void equipment_slotWidget( unsigned int wid, double x, double y, double w,
463 double h, CstSlotWidget *data )
464{
465 /* Initialize data. */
467
468 /* Create the widget. */
469 window_addCust( wid, x, y, w, h, "cstEquipment", 0, equipment_renderSlots,
470 equipment_mouseSlots, NULL, NULL, data );
471 window_custSetClipping( wid, "cstEquipment", 0 );
472 window_custSetOverlay( wid, "cstEquipment", equipment_renderOverlaySlots );
473 window_canFocusWidget( wid, "cstEquipment", 0 );
474}
475
478static void equipment_renderColumn( double x, double y, double w, double h,
479 const PilotOutfitSlot *lst, const char *txt,
480 int selected, Outfit *o, Pilot *p,
481 const CstSlotWidget *wgt )
482{
483 const glColour *c, *dc, *rc;
484 glColour bc;
485
486 /* Shouldn't be happening, but let's be nice and not crash. */
487 if ( !array_size( lst ) )
488 return;
489
490 /* Render text. */
491 if ( ( o != NULL ) && ( lst[0].sslot->slot.type == o->slot.type ) )
492 c = &cFontGreen;
493 else
494 c = &cFontWhite;
495 gl_printMidRaw( &gl_smallFont, 60., x - 15., y + h + 10., c, -1., txt );
496
497 /* Iterate for all the slots. */
498 for ( int i = 0; i < array_size( lst ); i++ ) {
499 int cantoggle;
500 const glTexture *icon;
501
502 /* Skip slots hat are empty and the player can't do anything about. */
503 if ( lst[i].sslot->locked && !lst[i].sslot->visible &&
504 ( lst[i].outfit == NULL ) )
505 continue;
506
507 cantoggle = pilot_slotIsToggleable( &lst[i] );
508
509 /* Choose default colour. */
510 if ( wgt->weapons >= 0 ) {
511 int level = pilot_weapSetInSet( p, wgt->weapons, &lst[i] );
512 if ( level == 0 )
513 dc = &cFontRed;
514 else if ( level == 1 )
515 dc = &cFontYellow;
516 else if ( cantoggle )
517 dc = &cFontBlue;
518 else
519 dc = &cFontGrey;
520 } else
521 dc = outfit_slotSizeColour( &lst[i].sslot->slot );
522
523 if ( dc == NULL )
524 dc = &cGrey60;
525
526 /* Draw background. */
527 if ( ( wgt->weapons >= 0 ) &&
528 ( !cantoggle || pilot_weapSetCheck( p, wgt->weapons, &lst[i] ) ) )
529 bc = cFontGrey;
530 else
531 bc = *dc;
532 bc.a = 0.4;
533 toolkit_drawRect( x, y, w, h, &bc, NULL );
534
535 if ( lst[i].outfit != NULL ) {
536 outfit_gfxStoreLoad( (Outfit *)lst[i].outfit );
537 /* Draw bugger. */
538 gl_renderScale( lst[i].outfit->gfx_store, x, y, w, h, NULL );
539 } else if ( ( o != NULL ) &&
540 ( lst[i].sslot->slot.type == o->slot.type ) ) {
541 /* Render a thick frame with a yes/no colour, and geometric cue. */
542 int ok = ( !lst[i].sslot->locked &&
543 pilot_canEquip( p, &lst[i], o ) == NULL );
544 glUseProgram( shaders.status.program );
545 glUniform1f( shaders.status.paramf, ok );
546 gl_renderShader( x, y, w, h, 0., &shaders.status, NULL, 0 );
547 }
548
549 /* Must rechoose colour based on slot properties. */
550 rc = dc;
551 if ( wgt->canmodify ) {
552 if ( lst[i].sslot->locked )
553 rc = NULL;
554 else if ( lst[i].sslot->required )
555 rc = &cBrightRed;
556 else if ( lst[i].sslot->exclusive )
557 rc = &cWhite;
558 else if ( lst[i].sslot->slot.spid != 0 )
559 rc = &cBlack;
560 }
561
562 /* Draw outline. */
563 if ( i == selected )
564 toolkit_drawOutlineThick( x, y, w, h, 5, 7, &cGreen, NULL );
565 if ( rc != NULL )
566 toolkit_drawOutlineThick( x, y, w, h, 1, 3, rc, NULL );
567
568 /* Draw slot iccon if applicable. */
569 icon = sp_icon( lst[i].sslot->slot.spid );
570 if ( icon != NULL ) {
571 double sw = 14.;
572 double sh = 14.;
573 double sx = x + w - 10.;
574 double sy = y + h - 10.;
575
576 if ( icon->flags & OPENGL_TEX_SDF )
577 gl_renderSDF( icon, sx, sy, sw, sh, &cWhite, 0., 1. );
578 else
579 gl_renderScaleAspect( icon, sx, sy, sw, sh, NULL );
580 }
581
582 /* Go to next one. */
583 y -= h + 20;
584 }
585}
586
598static void equipment_calculateSlots( const Pilot *p, double bw, double bh,
599 double *w, double *h, int *n, int *m )
600{
601 double tw, th, s;
602 int tm;
603
604 /* Calculate size. */
605 tm = MAX(
606 MAX( array_size( p->outfit_weapon ), array_size( p->outfit_utility ) ),
607 array_size( p->outfit_structure ) );
608 th = bh / (double)tm;
609 tw = bw / 3.;
610 s = MIN( th, tw ) - 20.;
611 th = s;
612 tw = s;
613
614 /* Return. */
615 *w = tw;
616 *h = th;
617 *n = 3;
618 *m = tm;
619}
620
629static void equipment_renderSlots( double bx, double by, double bw, double bh,
630 void *data )
631{
632 double x, y;
633 double w, h;
634 double tw;
635 int n, m;
636 CstSlotWidget *wgt;
637 Pilot *p;
638 int selected;
639
640 /* Must have selected ship. */
641 wgt = (CstSlotWidget *)data;
642 if ( wgt->selected == NULL )
643 return;
644
645 /* Get data. */
646 p = wgt->selected->p;
647 selected = wgt->slot;
648
649 /* Get dimensions. */
650 equipment_calculateSlots( p, bw, bh, &w, &h, &n, &m );
651 tw = bw / (double)n;
652
653 /* Draw structure outfits. */
654 x = bx + ( tw - w ) / 2 + 2 * tw;
655 y = by + bh - ( h + 20 ) + ( h + 20 - h ) / 2;
656 equipment_renderColumn( x, y, w, h, p->outfit_structure, _( "Structure" ),
657 selected, wgt->outfit, p, wgt );
658
659 /* Draw systems outfits. */
660 selected -= array_size( p->outfit_structure );
661 x -= tw;
662 y = by + bh - ( h + 20 ) + ( h + 20 - h ) / 2;
663 equipment_renderColumn( x, y, w, h, p->outfit_utility, _( "Utility" ),
664 selected, wgt->outfit, p, wgt );
665
666 /* Draw weapon outfits. */
667 selected -= array_size( p->outfit_utility );
668 x -= tw;
669 y = by + bh - ( h + 20 ) + ( h + 20 - h ) / 2;
670 equipment_renderColumn( x, y, w, h, p->outfit_weapon, _( "Weapon" ),
671 selected, wgt->outfit, p, wgt );
672}
673
682static void equipment_renderMisc( double bx, double by, double bw, double bh,
683 void *data )
684{
685 (void)data;
686 Pilot *p;
687 double percent;
688 double x, y;
689 double w, h;
690
691 /* Must have selected ship. */
692 if ( eq_wgt.selected == NULL )
693 return;
694
695 p = eq_wgt.selected->p;
696
697 /* Base bar properties. */
698 w = bw;
699 h = 20;
700 x = bx;
701 y = by + bh - 30 - h;
702
703 /* Fleet capacity. */
704 if ( player.fleet_capacity > 0 ) {
705 gl_printMidRaw( &gl_smallFont, w, x, y + h + 7, &cFontWhite, -1.,
706 _( "Fleet Capacity" ) );
707
708 percent = CLAMP( 0., 1.,
709 1. - (double)player.fleet_used /
710 (double)player.fleet_capacity );
711 toolkit_drawRect( x, y - 2, w * percent, h + 4, &cBlue, NULL );
712 toolkit_drawRect( x + w * percent, y - 2, w * ( 1. - percent ), h + 4,
713 &cBlack, NULL );
714 gl_printMid( &gl_smallFont, w, x, y + h / 2. - gl_smallFont.h / 2.,
715 &cFontWhite, "%d / %d",
716 player.fleet_capacity - player.fleet_used,
717 player.fleet_capacity );
718
719 y -= gl_smallFont.h + 2 * h;
720 }
721
722 /* Render CPU. */
723 gl_printMidRaw( &gl_smallFont, w, x, y + h + 7, &cFontWhite, -1,
724 _( "CPU Free" ) );
725
726 percent = ( p->cpu_max > 0 )
727 ? CLAMP( 0., 1., (double)p->cpu / (double)p->cpu_max )
728 : 0.;
729 toolkit_drawRect( x, y - 2, w * percent, h + 4, &cGreen, NULL );
730 toolkit_drawRect( x + w * percent, y - 2, w * ( 1. - percent ), h + 4, &cRed,
731 NULL );
732 gl_printMid( &gl_smallFont, w, x, y + h / 2. - gl_smallFont.h / 2.,
733 &cFontWhite, "%d / %d", p->cpu, p->cpu_max );
734
735 y -= h;
736
737 /* Render mass limit. */
738 gl_printMidRaw( &gl_smallFont, w, x, y - 3, &cFontWhite, -1.,
739 _( "Mass Limit Left" ) );
740 y -= gl_smallFont.h + h;
741
742 percent = ( p->stats.engine_limit > 0 )
743 ? CLAMP( 0., 1.,
744 ( p->stats.engine_limit - p->solid.mass ) /
745 p->stats.engine_limit )
746 : 0.;
747 toolkit_drawRect( x, y - 2, w * percent, h + 4, &cGreen, NULL );
748 toolkit_drawRect( x + w * percent, y - 2, w * ( 1. - percent ), h + 4,
749 &cOrange, NULL );
750 gl_printMid( &gl_smallFont, w, x, y + h / 2. - gl_smallFont.h / 2.,
751 &cFontWhite, "%.0f / %.0f",
752 p->stats.engine_limit - p->solid.mass, p->stats.engine_limit );
753
754 y -= h;
755 if ( p->stats.engine_limit > 0. && p->solid.mass > p->stats.engine_limit ) {
756 gl_printMid( &gl_smallFont, w, x, y, &cFontRed,
757 _( "!! %.0f%% Slower !!" ),
758 ( 1. - p->speed / p->speed_base ) * 100 );
759 }
760}
761
772static void equipment_renderOverlayColumn( double x, double y, double h,
773 PilotOutfitSlot *lst, int mover,
774 CstSlotWidget *wgt )
775{
776 const glColour *c;
777 glColour tc;
778 int text_width, yoff;
779 const char *display;
780
781 /* Iterate for all the slots. */
782 for ( int i = 0; i < array_size( lst ); i++ ) {
783 int subtitle = 0;
784
785 /* Skip slots hat are empty and the player can't do anything about. */
786 if ( lst[i].sslot->locked && ( lst[i].outfit == NULL ) )
787 continue;
788
789 if ( lst[i].outfit != NULL ) {
790 /* See if needs a subtitle. */
791 if ( ( outfit_isLauncher( lst[i].outfit ) ||
792 ( outfit_isFighterBay( lst[i].outfit ) ) ) &&
793 ( lst[i].u.ammo.quantity < outfit_amount( lst[i].outfit ) ) )
794 subtitle = 1;
795 }
796 /* Draw bottom. */
797 if ( ( i == mover ) || subtitle ) {
798 int top = 0;
799 display = NULL;
800 if ( ( i == mover ) && wgt->canmodify ) {
801 if ( lst[i].sslot->locked ) {
802 top = 1;
803 display = _( "Locked" );
804 c = &cFontRed;
805 } else if ( lst[i].outfit != NULL ) {
806 top = 1;
807 display = pilot_canEquip( wgt->selected->p, &lst[i], NULL );
808 if ( display != NULL )
809 c = &cFontRed;
810 else {
811 display = _( "Right click to remove" );
812 c = &cFontGreen;
813 }
814 } else if ( ( wgt->outfit != NULL ) &&
815 ( lst->sslot->slot.type == wgt->outfit->slot.type ) ) {
816 top = 1;
817 display =
818 pilot_canEquip( wgt->selected->p, &lst[i], wgt->outfit );
819 if ( display != NULL )
820 c = &cFontRed;
821 else {
822 display = _( "Right click to add" );
823 c = &cFontGreen;
824 }
825 }
826 } else if ( lst[i].outfit != NULL ) {
827 top = 1;
828 }
829
830 if ( display != NULL ) {
831 text_width = gl_printWidthRaw( &gl_smallFont, display );
832 if ( top )
833 yoff = h + 2;
834 else
835 yoff = -gl_smallFont.h - 3;
836 tc.r = 0.;
837 tc.g = 0.;
838 tc.b = 0.;
839 tc.a = 0.9;
840 toolkit_drawRect( x, y - 5. + yoff, text_width + 60,
841 gl_smallFont.h + 10, &tc, NULL );
842 gl_printMaxRaw( &gl_smallFont, text_width, x + 5, y + yoff, c, -1.,
843 display );
844 }
845 }
846 /* Go to next one. */
847 y -= h + 20;
848 }
849}
850
859static void equipment_renderOverlaySlots( double bx, double by, double bw,
860 double bh, void *data )
861{
862 (void)bw;
863 Pilot *p;
864 int mover;
865 double x, y;
866 double w, h;
867 double tw;
868 int n, m;
869 PilotOutfitSlot *slot;
870 char alt[STRMAX];
871 const Outfit *o;
872 CstSlotWidget *wgt;
873
874 /* Get data. */
875 wgt = (CstSlotWidget *)data;
876 if ( wgt->selected == NULL )
877 return;
878 p = wgt->selected->p;
879
880 /* Get dimensions. */
881 equipment_calculateSlots( p, bw, bh, &w, &h, &n, &m );
882 tw = bw / (double)n;
883
884 /* Get selected. */
885 mover = wgt->mouseover;
886
887 /* Render weapon outfits. */
888 x = bx + ( tw - w ) / 2 + 2 * tw;
889 y = by + bh - ( h + 20 ) + ( h + 20 - h ) / 2;
890 equipment_renderOverlayColumn( x, y, h, p->outfit_structure, mover, wgt );
891 mover -= array_size( p->outfit_structure );
892 x -= tw;
893 y = by + bh - ( h + 20 ) + ( h + 20 - h ) / 2;
894 equipment_renderOverlayColumn( x, y, h, p->outfit_utility, mover, wgt );
895 mover -= array_size( p->outfit_utility );
896 x -= tw;
897 y = by + bh - ( h + 20 ) + ( h + 20 - h ) / 2;
898 equipment_renderOverlayColumn( x, y, h, p->outfit_weapon, mover, wgt );
899
900 /* Mouse must be over something. */
901 if ( wgt->mouseover < 0 )
902 return;
903
904 /* Get the slot. */
905 slot = p->outfits[wgt->mouseover];
906
907 /* For comfortability. */
908 o = slot->outfit;
909
910 /* Slot is empty. */
911 if ( o == NULL ) {
912 int pos;
913 if ( slot->sslot->slot.spid ) {
914 pos = scnprintf( alt, sizeof( alt ), "#o%s\n",
915 _( sp_display( slot->sslot->slot.spid ) ) );
916 } else
917 pos = 0;
918 pos +=
919 scnprintf( &alt[pos], sizeof( alt ) - pos, _( "#%c%s #%c%s #0slot" ),
921 _( slotSize( slot->sslot->slot.size ) ),
923 _( slotName( slot->sslot->slot.type ) ) );
924 if ( slot->sslot->exclusive && ( pos < (int)sizeof( alt ) ) )
925 pos +=
926 scnprintf( &alt[pos], sizeof( alt ) - pos, _( " [exclusive]" ) );
927 if ( slot->sslot->locked && ( pos < (int)sizeof( alt ) ) )
928 pos += scnprintf( &alt[pos], sizeof( alt ) - pos, "#r%s#0",
929 _( " [locked]" ) );
930 if ( slot->sslot->slot.spid )
931 scnprintf( &alt[pos], sizeof( alt ) - pos, "\n\n%s",
932 _( sp_description( slot->sslot->slot.spid ) ) );
933 toolkit_drawAltText( bx + wgt->altx, by + wgt->alty, alt );
934 return;
935 }
936
937 /* Get text. */
938 outfit_altText( alt, sizeof( alt ), o, ( p == player.p ) ? p : NULL );
939
940 /* Display temporary bonuses. */
941 if ( slot->lua_mem != LUA_NOREF ) {
942 size_t slen = strlen( alt );
943 ss_statsListDesc( slot->lua_stats, &alt[slen], sizeof( alt ) - slen, 1 );
944 }
945
946 /* Draw the text. */
947 toolkit_drawAltText( bx + wgt->altx, by + wgt->alty, alt );
948}
949
959static void equipment_renderShip( double bx, double by, double bw, double bh,
960 void *data )
961{
962 Pilot *p;
963 int s;
964 double px, py, pw, ph;
965 vec2 v;
966 GLint fbo;
967 const ShipWidgetData *swd = data;
968 const unsigned int wid = swd->wid;
969
970 /* Must have selected ship. */
971 if ( eq_wgt.selected == NULL )
972 return;
973 p = eq_wgt.selected->p;
974
975 /* Don't update if not selected. */
976 if ( window_isTop( wid ) ) {
977 unsigned int tick = SDL_GetTicks();
978 double dt = (double)( tick - equipment_lastick ) / 1000.;
979 equipment_lastick = tick;
980 p->solid.dir += p->turn * dt;
981 if ( p->solid.dir > 2. * M_PI )
982 p->solid.dir = fmod( p->solid.dir, 2. * M_PI );
983 } else
984 equipment_lastick = SDL_GetTicks();
985 gl_getSpriteFromDir( &p->tsx, &p->tsy, p->ship->sx, p->ship->sy,
986 p->solid.dir );
987
988 s = p->ship->size;
989
990 /* Render ship graphic. */
991 if ( s > bw ) {
992 pw = bw;
993 ph = bh;
994 } else {
995 pw = s;
996 ph = s;
997 }
998 px = bx + ( bw - pw ) / 2;
999 py = by + ( bh - ph ) / 2;
1000
1001 /* Render background. */
1002 gl_renderRect( px, py, pw, ph, &cBlack );
1003
1004 /* Use framebuffer to draw, have to use an additional one. */
1005 s = ceil(
1006 s / gl_screen.scale ); /* Have to correct for the true rendered size. */
1007 glGetIntegerv( GL_FRAMEBUFFER_BINDING, &fbo );
1008 pilot_renderFramebuffer( p, swd->fbo, gl_screen.nw, gl_screen.nh,
1009 &L_store_const );
1010 glBindFramebuffer( GL_FRAMEBUFFER, fbo );
1011 gl_renderTextureRaw( swd->tex, 0, px, py, pw, ph, 0., 0., s / swd->s,
1012 s / swd->s, NULL, 0. );
1013
1014#ifdef DEBUGGING
1015 if ( debug_isFlag( DEBUG_MARK_EMITTER ) ) {
1016 /* Visualize the trail emitters. */
1017 double dircos, dirsin;
1018 int i;
1019 mat4 H;
1020 int use_3d = ship_isFlag( p->ship, SHIP_3DTRAILS );
1021
1022 if ( use_3d ) {
1023 H = mat4_identity();
1024 // H.m[2][2] = -1.;
1025 if ( fabs( p->tilt ) > DOUBLE_TOL ) {
1026 mat4_rotate( &H, M_PI_2, 0.0, 1.0, 0.0 );
1027 mat4_rotate( &H, p->tilt, 1.0, 0.0, 0.0 );
1028 mat4_rotate( &H, -p->solid.dir, 0.0, 1.0, 0.0 );
1029 } else
1030 mat4_rotate( &H, -p->solid.dir + M_PI_2, 0.0, 1.0, 0.0 );
1031 mat4_rotate( &H, -M_PI / 4.0, 1., 0., 0. );
1032 } else {
1033 dircos = cos( equipment_dir );
1034 dirsin = sin( equipment_dir );
1035 }
1036
1037 for ( i = 0; i < array_size( p->ship->trail_emitters ); i++ ) {
1038 const ShipTrailEmitter *trail = &p->ship->trail_emitters[i];
1039
1040 if ( use_3d ) {
1041 vec3 v2;
1042 mat4_mul_vec( &v2, &H, &trail->pos );
1043 v.x = v2.v[0];
1044 v.y = v2.v[1];
1045 } else {
1046 v.x = trail->pos.v[0] * dircos - trail->pos.v[1] * dirsin;
1047 v.y = trail->pos.v[0] * dirsin + trail->pos.v[1] * dircos +
1048 trail->pos.v[2];
1049 v.x *= pw / p->ship->size;
1050 v.y *= ph / p->ship->size;
1051 v.y *= M_SQRT1_2;
1052 }
1053
1054 if ( trail->trail_spec->nebula )
1055 gl_renderCross( px + pw / 2. + v.x, py + ph / 2. + v.y, 2.,
1056 &cFontBlue );
1057 else
1058 gl_renderCross( px + pw / 2. + v.x, py + ph / 2. + v.y, 4.,
1059 &cInert );
1060 }
1061 }
1062#endif /* DEBUGGING */
1063
1064 if ( ( eq_wgt.slot >= 0 ) &&
1065 p->outfits[eq_wgt.slot]->sslot->slot.type == OUTFIT_SLOT_WEAPON ) {
1066 pilot_getMount( p, p->outfits[eq_wgt.slot], &v );
1067 px += pw / 2.;
1068 py += ph / 2.;
1069 v.x *= pw / p->ship->size;
1070 v.y *= ph / p->ship->size;
1071
1072 /* Render it. */
1073 glUseProgram( shaders.crosshairs.program );
1074 glUniform1f( shaders.crosshairs.paramf, 2. );
1075 gl_renderShader( px + v.x, py + v.y, 7., 7., 0., &shaders.crosshairs,
1076 &cRadar_player, 1 );
1077 }
1078}
1079
1080static void equipment_freeShipData( unsigned int wid, const char *unused )
1081{
1082 (void)unused;
1083 ShipWidgetData *swd = window_custGetData( wid, "cstShip" );
1084 glDeleteFramebuffers( 1, &swd->fbo );
1085 glDeleteTextures( 1, &swd->tex );
1086 glDeleteTextures( 1, &swd->texd );
1087 free( swd );
1088}
1089
1099static int equipment_mouseInColumn( const PilotOutfitSlot *lst, double y,
1100 double h, double my )
1101{
1102 for ( int i = 0; i < array_size( lst ); i++ ) {
1103 /* Skip slots hat are empty and the player can't do anything about. */
1104 if ( lst[i].sslot->locked && ( lst[i].outfit == NULL ) )
1105 continue;
1106
1107 /* See if in position. */
1108 if ( ( my > y ) && ( my < y + h + 20. ) )
1109 return i;
1110 y -= h + 20.;
1111 }
1112
1113 return -1.;
1114}
1115
1130static int equipment_mouseColumn( unsigned int wid, const SDL_Event *event,
1131 double mx, double my, double y, double h,
1132 PilotOutfitSlot *os, Pilot *p, int selected,
1133 CstSlotWidget *wgt )
1134{
1135 int ret = equipment_mouseInColumn( os, y, h, my );
1136 if ( ret < 0 )
1137 return 0;
1138
1139 if ( event->type == SDL_MOUSEBUTTONDOWN ) {
1140 /* Normal mouse usage. */
1141 if ( wgt->weapons < 0 ) {
1142 if ( event->button.button == SDL_BUTTON_LEFT ) {
1143 if ( wgt->canmodify ) {
1144 int sel = selected + ret;
1145 if ( wgt->slot == sel )
1146 wgt->slot = -1;
1147 else {
1148 const char *filtertext;
1149 int active, noutfits;
1150 Outfit **outfits;
1151
1152 /* Actually select the slot. */
1153 wgt->slot = sel;
1154
1155 /* Get the filter text. */
1156 filtertext = NULL;
1157 if ( widget_exists( equipment_wid, EQUIPMENT_FILTER ) ) {
1158 filtertext =
1159 window_getInput( equipment_wid, EQUIPMENT_FILTER );
1160 if ( strlen( filtertext ) == 0 )
1161 filtertext = NULL;
1162 }
1163
1164 /* See if we have no outfits selected, and if so, switch tabs.
1165 */
1166 /* TODO no freeing. */
1167 active = window_tabWinGetActive( equipment_wid,
1168 EQUIPMENT_OUTFIT_TAB );
1169 outfits = array_create( Outfit *);
1170 noutfits = player_getOutfitsFiltered(
1171 (const Outfit ***)&outfits, tabfilters[active],
1172 filtertext );
1173 if ( noutfits <= 0 ) {
1174 int best = active;
1175 int nbest = 0;
1176 for ( int i = 0; i < OUTFIT_TABS; i++ ) {
1177 array_erase( &outfits, array_begin( outfits ),
1178 array_end( outfits ) );
1179 noutfits = player_getOutfitsFiltered(
1180 (const Outfit ***)&outfits, tabfilters[i],
1181 filtertext );
1182 if ( noutfits > 0 ) {
1183 best = i;
1184 nbest = noutfits;
1185 }
1186 }
1187 array_free( outfits );
1188 if ( nbest > 0 ) {
1189 window_tabWinSetActive( equipment_wid,
1190 EQUIPMENT_OUTFIT_TAB, best );
1191 return 1;
1192 }
1193 } else
1194 array_free( outfits );
1195 }
1196 equipment_regenLists( wid, 1, 0 );
1197 }
1198 } else if ( ( event->button.button == SDL_BUTTON_RIGHT ) &&
1199 wgt->canmodify && !os[ret].sslot->locked ) {
1200 equipment_swapSlot( wid, p, &os[ret] );
1201 hooks_run( "equip" ); /* Equipped. */
1202 }
1203 }
1204 /* Viewing weapon slots. */
1205 else {
1206 int level;
1207 int exists = pilot_weapSetInSet( p, wgt->weapons, &os[ret] );
1208 /* Get the level of the selection. */
1209 if ( event->button.button == SDL_BUTTON_LEFT )
1210 level = 0;
1211 else if ( event->button.button == SDL_BUTTON_RIGHT )
1212 level = 1;
1213 else
1214 return 0; /* We ignore this type of click. */
1215 /* See if we should add it or remove it. */
1216 if ( exists == level )
1217 pilot_weapSetRm( p, wgt->weapons, &os[ret] );
1218 else
1219 pilot_weapSetAdd( p, wgt->weapons, &os[ret] );
1220 p->autoweap = 0; /* Disable autoweap. */
1221 info_update(); /* Need to update weapons. */
1222 }
1223 } else {
1224 wgt->mouseover = selected + ret;
1225 wgt->altx = mx;
1226 wgt->alty = my;
1227 }
1228
1229 return 1;
1230}
1231
1244static int equipment_mouseSlots( unsigned int wid, const SDL_Event *event,
1245 double mx, double my, double bw, double bh,
1246 double rx, double ry, void *data )
1247{
1248 (void)bw;
1249 (void)rx;
1250 (void)ry;
1251 Pilot *p;
1252 int selected;
1253 double x, y;
1254 double w, h;
1255 double tw;
1256 CstSlotWidget *wgt;
1257 int n, m;
1258
1259 /* Get data. */
1260 wgt = (CstSlotWidget *)data;
1261 if ( wgt->selected == NULL )
1262 return 0;
1263 p = wgt->selected->p;
1264
1265 /* Must be left click for now. */
1266 if ( ( event->type != SDL_MOUSEBUTTONDOWN ) &&
1267 ( event->type != SDL_MOUSEMOTION ) )
1268 return 0;
1269
1270 /* Is covered by something. */
1271 if ( widget_isCovered( wid, "cstEquipment", mx, my ) ) {
1272 wgt->mouseover = -1;
1273 return 0;
1274 }
1275
1276 /* Get dimensions. */
1277 equipment_calculateSlots( p, bw, bh, &w, &h, &n, &m );
1278 tw = bw / (double)n;
1279
1280 /* Go over each column and pass mouse event. */
1281 selected = 0;
1282 x = ( tw - w ) / 2 + 2 * tw;
1283 y = bh - ( h + 20 ) + ( h + 20 - h ) / 2 - 10;
1284 if ( ( mx > x - 10 ) && ( mx < x + w + 10 ) ) {
1285 int ret = equipment_mouseColumn( wid, event, mx, my, y, h,
1286 p->outfit_structure, p, selected, wgt );
1287 if ( ret )
1288 return !!( event->type == SDL_MOUSEBUTTONDOWN );
1289 }
1290 selected += array_size( p->outfit_structure );
1291 x -= tw;
1292 if ( ( mx > x - 10 ) && ( mx < x + w + 10 ) ) {
1293 int ret = equipment_mouseColumn( wid, event, mx, my, y, h,
1294 p->outfit_utility, p, selected, wgt );
1295 if ( ret )
1296 return !!( event->type == SDL_MOUSEBUTTONDOWN );
1297 }
1298 selected += array_size( p->outfit_utility );
1299 x -= tw;
1300 if ( ( mx > x - 10 ) && ( mx < x + w + 10 ) ) {
1301 int ret = equipment_mouseColumn( wid, event, mx, my, y, h,
1302 p->outfit_weapon, p, selected, wgt );
1303 if ( ret )
1304 return !!( event->type == SDL_MOUSEBUTTONDOWN );
1305 }
1306
1307 /* Not over anything. */
1308 wgt->mouseover = -1;
1309 return 0;
1310}
1311
1319static int equipment_swapSlot( unsigned int wid, Pilot *p,
1320 PilotOutfitSlot *slot )
1321{
1322 /* Slot can be changed later in land_refuel() so have to save early. */
1323 int selslot = eq_wgt.slot;
1324
1325 /* Remove outfit. */
1326 if ( slot->outfit != NULL ) {
1327 int ret;
1328 const Outfit *o = slot->outfit;
1329
1330 /* Must be able to remove. */
1331 if ( pilot_canEquip( eq_wgt.selected->p, slot, NULL ) != NULL )
1332 return 0;
1333
1334 /* Force recall fighters. */
1335 if ( slot->u.ammo.deployed > 0 ) {
1336 int dockslot = -1;
1337 if ( !dialogue_YesNo( _( "Recall Fighters" ),
1338 _( "This action will recall your deployed "
1339 "fighters. Is that OK?" ) ) ) {
1340 return 0;
1341 }
1342
1343 /* Get index of outfit slot */
1344 for ( int j = 0; j < array_size( p->outfits ); j++ ) {
1345 if ( p->outfits[j] == slot )
1346 dockslot = j;
1347 }
1348
1349 /* Recall fighters. */
1350 escort_clearDeployed( player.p, dockslot );
1351 }
1352
1353 /* Remove ammo first. */
1354 pilot_rmAmmo( eq_wgt.selected->p, slot, slot->u.ammo.quantity );
1355
1356 /* Remove outfit. */
1357 ret = pilot_rmOutfit( eq_wgt.selected->p, slot );
1358 if ( ret == 0 )
1360 }
1361 /* Add outfit. */
1362 else {
1363 int ret;
1364 const Outfit *o = eq_wgt.outfit;
1365 /* Must have outfit. */
1366 if ( o == NULL )
1367 return 0;
1368
1369 /* Must fit slot. */
1370 if ( !outfit_fitsSlot( o, &slot->sslot->slot ) )
1371 return 0;
1372
1373 /* Must be able to add. */
1374 if ( pilot_canEquip( eq_wgt.selected->p, slot, o ) != NULL )
1375 return 0;
1376
1377 /* Add outfit to ship. */
1378 ret = equipment_playerRmOutfit( o, 1 );
1379 if ( ret == 1 ) {
1380 pilot_addOutfitRaw( eq_wgt.selected->p, o, slot );
1381
1382 /* Recalculate stats. */
1383 if ( eq_wgt.selected->p->id > 0 )
1384 /* TODO there are cases, like outfits that conditionally add stats,
1385 * that we would want to actually initialize the Lua. However, the
1386 * Lua API requires a pilot to have an ID, so we would need to make
1387 * that requirement lax. Maybe add temporary ID or something? */
1388 pilot_outfitLInitAll( eq_wgt.selected->p );
1389 pilot_calcStats( eq_wgt.selected->p ); /* TODO avoid running twice. */
1390 }
1391
1393 }
1394
1395 /* Refuel if necessary. */
1396 land_refuel();
1397
1398 /* Recalculate stats. */
1399 pilot_calcStats( p );
1400 pilot_healLanded( p );
1401
1402 /* Redo the outfits thingy, while conserving slot. */
1403 equipment_regenLists( wid, 1, 1 );
1404 eq_wgt.slot = selslot;
1405
1406 /* Update outfits. */
1408
1409 /* Update weapon sets if needed. */
1410 if ( eq_wgt.selected->p->autoweap ) {
1411 pilot_weaponAuto( eq_wgt.selected->p );
1412 ws_copy( eq_wgt.selected->weapon_sets, eq_wgt.selected->p->weapon_sets );
1413 }
1414 pilot_weaponSafe( eq_wgt.selected->p );
1415
1416 /* Notify GUI of modification. */
1417 gui_setShip();
1418
1419 return 0;
1420}
1421
1429void equipment_regenLists( unsigned int wid, int outfits, int ships )
1430{
1431 int nship = 0, noutfit = 0;
1432 double offship = 0, offoutfit = 0;
1433 char *selship = NULL;
1434 const char *s = NULL;
1435 char *focused;
1436
1437 /* Ignore when creating, should be generated correctly anyway. */
1438 if ( equipment_creating )
1439 return;
1440
1441 /* Defaults. */
1442 nship = 0;
1443 offship = 0.;
1444
1445 /* Must exist. */
1447 return;
1448
1449 /* Save focus. */
1450 focused = window_getFocus( wid );
1451
1452 /* Save positions. */
1453 if ( outfits ) {
1454 noutfit = toolkit_getImageArrayPos( wid, EQUIPMENT_OUTFITS );
1455 offoutfit = toolkit_getImageArrayOffset( wid, EQUIPMENT_OUTFITS );
1456 window_destroyWidget( wid, EQUIPMENT_OUTFITS );
1457 }
1458 if ( ships ) {
1459 nship = toolkit_getImageArrayPos( wid, EQUIPMENT_SHIPS );
1460 offship = toolkit_getImageArrayOffset( wid, EQUIPMENT_SHIPS );
1461 s = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
1462 selship = strdup( s );
1463 window_destroyWidget( wid, EQUIPMENT_SHIPS );
1464 }
1465
1466 /* Regenerate lists. */
1467 equipment_genLists( wid );
1468
1469 /* Restore positions. */
1470 if ( outfits ) {
1471 toolkit_setImageArrayPos( wid, EQUIPMENT_OUTFITS, noutfit );
1472 toolkit_setImageArrayOffset( wid, EQUIPMENT_OUTFITS, offoutfit );
1473 equipment_updateOutfits( wid, NULL );
1474 }
1475 if ( ships ) {
1476 toolkit_setImageArrayPos( wid, EQUIPMENT_SHIPS, nship );
1477 toolkit_setImageArrayOffset( wid, EQUIPMENT_SHIPS, offship );
1478 /* Try to maintain same ship selected. */
1479 s = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
1480 if ( ( s != NULL ) && ( strcmp( s, selship ) != 0 ) ) {
1481 int ret = toolkit_setImageArray( wid, EQUIPMENT_SHIPS, selship );
1482 if ( ret != 0 ) /* Failed to maintain. */
1483 toolkit_setImageArrayPos( wid, EQUIPMENT_SHIPS, nship );
1484
1485 /* Update ships. */
1486 equipment_updateShips( wid, NULL );
1487 }
1488 free( selship );
1489 }
1490
1491 /* Restore focus. */
1492 window_setFocus( wid, focused );
1493 free( focused );
1494}
1495
1500int equipment_canSellPlayerShip( const char *shipname )
1501{
1502 int failure = 0;
1503 land_errClear();
1504 if ( strcmp( shipname, player.p->name ) == 0 ) { /* Already on-board. */
1505 land_errDialogueBuild( _( "You can't sell the ship you're piloting!" ) );
1506 failure = 1;
1507 }
1508
1509 return !failure;
1510}
1511
1516int equipment_canSwapPlayerShip( const char *shipname )
1517{
1518 int diff;
1519 Pilot *newship;
1520 const PlayerShip_t *ps = player_getPlayerShip( shipname );
1521 land_errClear();
1522
1523 if ( strcmp( shipname, player.p->name ) == 0 ) { /* Already onboard. */
1524 land_errDialogueBuild( _( "You're already onboard the %s." ), shipname );
1525 return 0;
1526 }
1527
1528 /* Ship can't be piloted by player. */
1529 if ( ship_isFlag( ps->p->ship, SHIP_NOPLAYER ) ) {
1530 land_errDialogueBuild( _( "You can not pilot the %s! The ship can only "
1531 "be used as an escort." ),
1532 shipname );
1533 return 0;
1534 }
1535
1536 /* Ship can't be set as an escort. */
1537 if ( ship_isFlag( player.p->ship, SHIP_NOESCORT ) ) {
1539 _( "You can not swap ships and set %s as an escort!" ),
1540 player.p->name );
1541 return 0;
1542 }
1543
1544 newship = ps->p;
1545 if ( ps->deployed )
1546 diff = 0;
1547 else
1548 diff = pilot_cargoUsed( player.p ) - pilot_cargoFree( newship ) -
1549 ( pfleet_cargoFree() -
1550 pilot_cargoFree( player.p ) ); /* Has to fit all the cargo. */
1551 diff = MAX(
1552 diff, pilot_cargoUsedMission( player.p ) -
1553 pilot_cargoFree( newship ) ); /* Has to fit all mission cargo. */
1554 if ( diff > 0 ) { /* Current ship has too much cargo. */
1556 n_( "You have %d tonne more cargo than the new ship can hold.",
1557 "You have %d tonnes more cargo than the new ship can hold.",
1558 diff ),
1559 diff );
1560 return 0;
1561 }
1562 if ( pilot_hasDeployed( player.p ) ) {
1563 if ( !dialogue_YesNo( _( "Recall Fighters" ),
1564 _( "This action will recall your deployed "
1565 "fighters. Is that OK?" ) ) ) {
1566 land_errDialogueBuild( _( "You have deployed fighters." ) );
1567 return 0;
1568 }
1569 /* Recall fighters. */
1571 }
1572 return 1;
1573}
1574
1579{
1580 Pilot *p;
1581
1582 /* Get player. */
1583 if ( eq_wgt.selected == NULL )
1584 p = player.p;
1585 else
1586 p = eq_wgt.selected->p;
1587
1588 /* Add ammo to all outfits. */
1589 pilot_fillAmmo( p );
1590
1591 /* Notify GUI of modification. */
1592 gui_setShip();
1593}
1594
1604int equipment_shipStats( char *buf, int max_len, const Pilot *s, int dpseps,
1605 int name )
1606{
1607 int l;
1608 double eps, dps;
1609
1610 dps = 0.;
1611 eps = 0.;
1612 /* Calculate damage and energy per second. */
1613 if ( dpseps )
1614 pilot_dpseps( s, &dps, &eps );
1615
1616 /* Write to buffer. */
1617 if ( name )
1618 l = scnprintf( buf, max_len, "%s\n", s->name );
1619 else
1620 l = 0;
1621 if ( dps > 0. )
1622 l += scnprintf( &buf[l], ( max_len - l ), _( "%.2f DPS [%.2f EPS]\n" ),
1623 dps, eps );
1624 if ( s->ship->desc_extra != NULL )
1625 l += scnprintf( &buf[l], ( max_len - l ), "%s\n",
1626 _( s->ship->desc_extra ) );
1627 l += scnprintf( &buf[l], ( max_len - l ), _( "%.0f fuel per jump\n" ),
1628 s->fuel_consumption );
1629 l += ss_statsDesc( &s->stats, &buf[l], ( max_len - l ), 0 );
1630 return l;
1631}
1632
1636static void equipment_toggleFav( unsigned int wid, const char *wgt )
1637{
1638 int state = window_checkboxState( wid, wgt );
1639 const char *shipname = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
1640 if ( strcmp( shipname, player.p->name ) == 0 ) { /* no ships */
1641 player.ps.favourite = state;
1642 } else {
1643 PlayerShip_t *ps = player_getPlayerShip( shipname );
1644 ps->favourite = state;
1645 }
1646
1647 /* Update ship to reflect changes. */
1648 equipment_regenLists( wid, 0, 1 );
1649}
1650
1651static void equipment_toggleDeploy( unsigned int wid, const char *wgt )
1652{
1653 int state = window_checkboxState( wid, wgt );
1654 const char *shipname = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
1655
1656 /* Can't deploy if no capacity. */
1657 if ( player.fleet_capacity <= 0 )
1658 return;
1659
1660 /* Only if current ship isn't selected try to deploy. */
1661 if ( strcmp( shipname, player.p->name ) != 0 ) {
1662 PlayerShip_t *ps = player_getPlayerShip( shipname );
1663 if ( state && ship_isFlag( ps->p->ship, SHIP_NOESCORT ) ) {
1664 dialogue_msg( _( "Invalid Escort" ),
1665 _( "You can not set your ship '%s' as an escort!" ),
1666 ps->p->name );
1667 return;
1668 }
1669 if ( pfleet_toggleDeploy( ps, state ) )
1670 return;
1671 } else
1672 window_checkboxSet( wid, wgt, 1 ); /* Player is always deployed. */
1673
1674 /* Update ship to reflect changes. */
1675 equipment_regenLists( wid, 0, 1 );
1676}
1677
1683static void equipment_genLists( unsigned int wid )
1684{
1685 equipment_genShipList( wid );
1687}
1688
1693static void equipment_genShipList( unsigned int wid )
1694{
1695 ImageArrayCell *cships;
1696 int nships;
1697 int w, h;
1698 int sw, sh;
1699 const PlayerShip_t *ps;
1700 char r[PATH_MAX];
1701 glTexture *t;
1702 int iconsize;
1703 int allships = 1;
1704
1705 /* Get dimensions. */
1706 equipment_getDim( wid, &w, &h, &sw, &sh, NULL, NULL, NULL, NULL, NULL, NULL,
1707 NULL, NULL );
1708
1709 if ( widget_exists( wid, EQUIPMENT_SHIPS ) )
1710 return;
1711
1712 eq_wgt.selected = NULL;
1713 if ( allships )
1714 nships = player_nships() + 1;
1715 else
1716 nships = 1;
1717 cships = calloc( nships, sizeof( ImageArrayCell ) );
1718 /* Add player's current ship. */
1719 cships[0].image = ship_gfxStore( player.p->ship, 256, 0., 0., 0. );
1720 cships[0].caption = strdup( player.p->name );
1721 cships[0].layers = gl_copyTexArray( player.p->ship->gfx_overlays );
1722 t = gl_newImage( OVERLAY_GFX_PATH "active.webp", 0 );
1723 cships[0].layers = gl_addTexArray( cships[0].layers, t );
1724 if ( player.ps.favourite ) {
1725 t = gl_newImage( OVERLAY_GFX_PATH "favourite.webp", 0 );
1726 cships[0].layers = gl_addTexArray( cships[0].layers, t );
1727 }
1728 if ( player.p->ship->rarity > 0 ) {
1729 snprintf( r, sizeof( r ), OVERLAY_GFX_PATH "rarity_%d.webp",
1730 player.p->ship->rarity );
1731 t = gl_newImage( r, 0 );
1732 cships[0].layers = gl_addTexArray( cships[0].layers, t );
1733 }
1734 if ( allships ) {
1736 ps = player_getShipStack();
1737 for ( int i = 1; i <= array_size( ps ); i++ ) {
1738 cships[i].image = ship_gfxStore( ps[i - 1].p->ship, 256, 0., 0., 0. );
1739 cships[i].caption = strdup( ps[i - 1].p->name );
1740 cships[i].layers = gl_copyTexArray( ps[i - 1].p->ship->gfx_overlays );
1741 if ( ps[i - 1].favourite ) {
1742 t = gl_newImage( OVERLAY_GFX_PATH "favourite.webp", 0 );
1743 cships[i].layers = gl_addTexArray( cships[i].layers, t );
1744 }
1745 if ( ps[i - 1].deployed ) {
1746 t = gl_newImage( OVERLAY_GFX_PATH "fleet.webp", 0 );
1747 cships[i].layers = gl_addTexArray( cships[i].layers, t );
1748 }
1749 if ( ps[i - 1].p->ship->rarity > 0 ) {
1750 snprintf( r, sizeof( r ), OVERLAY_GFX_PATH "rarity_%d.webp",
1751 ps[i - 1].p->ship->rarity );
1752 t = gl_newImage( r, 0 );
1753 cships[i].layers = gl_addTexArray( cships[i].layers, t );
1754 }
1755 }
1756 }
1757 /* Ship stats in alt text. */
1758 for ( int i = 0; i < nships; i++ ) {
1759 const Pilot *s = player_getShip( cships[i].caption );
1760 int l;
1761 cships[i].alt = malloc( STRMAX );
1762 l = snprintf( &cships[i].alt[0], STRMAX, _( "Ship Stats\n" ) );
1763 l = equipment_shipStats( &cships[i].alt[0], STRMAX - l, s, 1, 1 );
1764 if ( l == 0 ) {
1765 free( cships[i].alt );
1766 cships[i].alt = NULL;
1767 }
1768 }
1769
1770 /* Create the image array. */
1771 iconsize = 96;
1772 if ( !conf.big_icons ) {
1773 if ( toolkit_simImageArrayVisibleElements( sw, sh, iconsize, iconsize ) <
1774 nships )
1775 iconsize = 80;
1776 if ( toolkit_simImageArrayVisibleElements( sw, sh, iconsize, iconsize ) <
1777 nships )
1778 iconsize = 64;
1779 }
1780 window_addImageArray( wid, 20, -40, sw, sh, EQUIPMENT_SHIPS, iconsize,
1781 iconsize, cships, nships, equipment_updateShips,
1783 toolkit_setImageArrayAccept( wid, EQUIPMENT_SHIPS,
1785
1786 equipment_updateShips( wid, NULL );
1787}
1788
1789static int equipment_filter( const Outfit *o )
1790{
1791 Pilot *p = ( eq_wgt.selected == NULL ) ? NULL : eq_wgt.selected->p;
1792 const PlayerShip_t *ps;
1793
1794 /* Filter only those that fit slot. */
1795 if ( ( p != NULL ) && ( eq_wgt.slot >= 0 ) ) {
1796 const PilotOutfitSlot *pos = p->outfits[eq_wgt.slot];
1797 if ( !outfit_fitsSlot( o, &pos->sslot->slot ) )
1798 return 0;
1799 }
1800
1801 /* Standard filtering. */
1802 switch ( equipment_outfitMode ) {
1803 case 0:
1804 return 1;
1805
1806 case 1: /* Fits any ship of the player. */
1807 ps = player_getShipStack();
1808 for ( int j = 0; j < array_size( ps ); j++ ) {
1809 const Pilot *pp = ps[j].p;
1810 for ( int i = 0; i < array_size( pp->outfits ); i++ ) {
1811 if ( outfit_fitsSlot( o, &pp->outfits[i]->sslot->slot ) )
1812 return 1;
1813 }
1814 }
1815 return 0;
1816
1817 case 2: /* Fits currently selected ship. */
1818 if ( p == NULL )
1819 return 1;
1820 for ( int i = 0; i < array_size( p->outfits ); i++ ) {
1821 if ( outfit_fitsSlot( o, &p->outfits[i]->sslot->slot ) )
1822 return 1;
1823 }
1824 return 0;
1825
1826 case 3:
1827 return ( o->slot.size == OUTFIT_SLOT_SIZE_LIGHT );
1828 case 4:
1829 return ( o->slot.size == OUTFIT_SLOT_SIZE_MEDIUM );
1830 case 5:
1831 return ( o->slot.size == OUTFIT_SLOT_SIZE_HEAVY );
1832 }
1833 return 1;
1834}
1835static int equipment_filterWeapon( const Outfit *o )
1836{
1837 return equipment_filter( o ) && outfit_filterWeapon( o );
1838}
1839static int equipment_filterUtility( const Outfit *o )
1840{
1841 return equipment_filter( o ) && outfit_filterUtility( o );
1842}
1843static int equipment_filterStructure( const Outfit *o )
1844{
1845 return equipment_filter( o ) && outfit_filterStructure( o );
1846}
1847static int equipment_filterCore( const Outfit *o )
1848{
1849 return equipment_filter( o ) && outfit_filterCore( o );
1850}
1851
1856static void equipment_genOutfitList( unsigned int wid )
1857{
1858 int x, y, w, h, ow, oh;
1859 int ix, iy, iw, ih, barw; /* Input filter. */
1860 const char *filtertext;
1861 const char *tabnames[] = {
1862 _( "All" ), _( OUTFIT_LABEL_WEAPON ), _( OUTFIT_LABEL_UTILITY ),
1863 _( OUTFIT_LABEL_STRUCTURE ), _( OUTFIT_LABEL_CORE ) };
1864 int noutfits, active;
1865 ImageArrayCell *coutfits;
1866 int iconsize;
1867 const Pilot *p = ( eq_wgt.selected != NULL ) ? eq_wgt.selected->p : NULL;
1868
1869 /* Get dimensions. */
1870 equipment_getDim( wid, &w, &h, NULL, NULL, &ow, &oh, NULL, NULL, NULL, NULL,
1871 NULL, NULL );
1872
1873 /* Deselect. */
1874 eq_wgt.outfit = NULL;
1875
1876 /* Calculate position. */
1877 x = 20;
1878 y = 20;
1879
1880 /* Create tabbed window. */
1881 if ( !widget_exists( wid, EQUIPMENT_OUTFIT_TAB ) ) {
1882 window_addTabbedWindow( wid, x, y + oh - 30, ow, 30, EQUIPMENT_OUTFIT_TAB,
1883 OUTFIT_TABS, tabnames, 1 );
1884
1885 barw = window_tabWinGetBarWidth( wid, EQUIPMENT_OUTFIT_TAB );
1886
1887 iw = CLAMP( 0, 150, ow - barw - 30 );
1888 ih = 30;
1889
1890 ix = ow - iw + 15;
1891 iy = y + oh - 25 - 1;
1892
1893#ifdef DEBUGGING
1894 if ( iw <= 60 )
1895 WARN( _( "Very little space on equipment outfit tabs!" ) );
1896#endif /* DEBUGGING */
1897
1898 /* Add popdown menu stuff. */
1899 window_addButton( wid, ix + iw - 30, iy, 30, 30, "btnOutfitFilter", NULL,
1900 equipment_outfitPopdown );
1901 window_buttonCustomRender( wid, "btnOutfitFilter",
1902 window_buttonCustomRenderGear );
1903 iw -= 35;
1904
1905 /* Set text filter. */
1906 window_addInput( wid, ix, iy, iw, ih, EQUIPMENT_FILTER, 32, 1, NULL );
1907 inp_setEmptyText( wid, EQUIPMENT_FILTER, _( "Filter…" ) );
1908 window_setInputCallback( wid, EQUIPMENT_FILTER, equipment_filterOutfits );
1909 }
1910
1911 window_tabWinOnChange( wid, EQUIPMENT_OUTFIT_TAB, equipment_changeTab );
1912 active = window_tabWinGetActive( equipment_wid, EQUIPMENT_OUTFIT_TAB );
1913
1914 /* Widget must not already exist. */
1915 if ( widget_exists( wid, EQUIPMENT_OUTFITS ) )
1916 return;
1917
1918 /* Set up arrays. */
1919 array_free( iar_outfits[active] );
1920 iar_outfits[active] = array_create( Outfit * );
1921
1922 filtertext = NULL;
1923 if ( widget_exists( equipment_wid, EQUIPMENT_FILTER ) ) {
1924 filtertext = window_getInput( equipment_wid, EQUIPMENT_FILTER );
1925 if ( strlen( filtertext ) == 0 )
1926 filtertext = NULL;
1927 }
1928
1929 /* Get the outfits. */
1930 noutfits = player_getOutfitsFiltered( (const Outfit ***)&iar_outfits[active],
1931 tabfilters[active], filtertext );
1932 coutfits =
1933 outfits_imageArrayCells( (const Outfit **)iar_outfits[active], &noutfits,
1934 ( p == NULL ) ? player.p : p, 0 );
1935
1936 /* Create the actual image array. */
1937 iw = ow - 6;
1938 ih = oh - 37;
1939 iconsize = 96;
1940 if ( !conf.big_icons ) {
1941 if ( toolkit_simImageArrayVisibleElements( iw, ih, iconsize, iconsize ) <
1942 noutfits )
1943 iconsize = 80;
1944 if ( toolkit_simImageArrayVisibleElements( iw, ih, iconsize, iconsize ) <
1945 noutfits )
1946 iconsize = 64;
1947 }
1948 window_addImageArray( wid, x + 4, y + 3, iw, ih, EQUIPMENT_OUTFITS, iconsize,
1949 iconsize, coutfits, noutfits, equipment_updateOutfits,
1952
1953 toolkit_setImageArrayAccept( wid, EQUIPMENT_OUTFITS,
1955
1956 equipment_updateOutfits( wid, NULL );
1957}
1958
1962static char eq_qCol( double cur, double base, int inv )
1963{
1964 if ( cur > 1.2 * base )
1965 return ( inv ) ? 'r' : 'g';
1966 else if ( cur < 0.8 * base )
1967 return ( inv ) ? 'g' : 'r';
1968 return '0';
1969}
1970
1974static const char *eq_qSym( double cur, double base, int inv )
1975{
1976 if ( cur > 1.2 * base )
1977 return ( inv ) ? "!! " : "";
1978 else if ( cur < 0.8 * base )
1979 return ( inv ) ? "" : "!! ";
1980 return "";
1981}
1982
1983#define EQ_COMP( cur, base, inv ) \
1984 eq_qCol( cur, base, inv ), eq_qSym( cur, base, inv ), cur
1985#define EQ_COMP_I( cur, base, inv ) \
1986 eq_qCol( cur, base, inv ), eq_qSym( cur, base, inv )
1992void equipment_updateShips( unsigned int wid, const char *str )
1993{
1994 (void)str;
1995 char buf[STRMAX], buf_price[ECON_CRED_STRLEN];
1996 char errorReport[STRMAX_SHORT], tbuf[64];
1997 const char *shipname, *acquired, *modelname;
1998 char scargo[NUM2STRLEN];
1999 Pilot *ship;
2000 PlayerShip_t *ps, *prevship;
2001 char *nt;
2002 int onboard, cargo, jumps, favourite, deployed, x, h, spaceworthy;
2003 int ww, wh, sw, sh, bw, bh, hacquired, hname, hmodelname;
2004 int wgtw, wgth;
2005 size_t l = 0;
2006
2007 equipment_getDim( wid, &ww, &wh, &sw, &sh, NULL, NULL, NULL, NULL, NULL,
2008 NULL, &bw, &bh );
2009
2010 /* Clear defaults. */
2011 eq_wgt.slot = -1;
2012 eq_wgt.mouseover = -1;
2013 equipment_lastick = SDL_GetTicks();
2014
2015 /* Get the ship. */
2016 shipname = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
2017 if ( strcmp( shipname, player.p->name ) == 0 ) { /* no ships */
2018 ps = &player.ps;
2019 onboard = 1;
2020 deployed = 1;
2021 } else {
2022 ps = player_getPlayerShip( shipname );
2023 onboard = 0;
2024 deployed = ps->deployed;
2025 }
2026 ship = ps->p;
2027 favourite = ps->favourite;
2028 prevship = eq_wgt.selected;
2029
2030 /* Just in case, turn off outfits and reset stats. */
2031 effect_clear( &ship->effects );
2032 pilot_outfitOffAll( ship );
2033 if ( ship->id )
2034 pilot_outfitLInitAll( ship );
2035 pilot_calcStats( ship );
2036
2037 /* Select. */
2038 equipment_slotSelect( &eq_wgt, ps );
2039
2040 /* update text */
2041 credits2str( buf_price, player_shipPrice( shipname, 0 ),
2042 2 ); /* sell price */
2043 cargo = pilot_cargoFree( ship ) + pilot_cargoUsed( ship );
2044 nt = ntime_pretty( pilot_hyperspaceDelay( ship ), 2 );
2045
2046 /* Get ship error report. */
2047 spaceworthy =
2048 !pilot_reportSpaceworthy( ship, errorReport, sizeof( errorReport ) );
2049
2050 jumps = floor( ship->fuel_max / ship->fuel_consumption );
2051
2052 /* Get acquired text length. */
2053 x = 20 + sw + 20 + 180 + 10;
2054 acquired = ( ps->acquired )
2055 ? ps->acquired
2056 : _( "You do not remember how you acquired this ship." );
2057 window_dimWidget( wid, "txtAcquired", &wgtw, &wgth );
2058 hacquired = gl_printLinesRaw( &gl_defFont, wgtw, acquired );
2059 window_dimWidget( wid, "txtDDesc", &wgtw, &wgth );
2060 hname = gl_printLinesRaw( &gl_defFont, wgtw, ship->name );
2061 modelname = _( ship->ship->name );
2062 hmodelname = gl_printLinesRaw( &gl_defFont, wgtw, modelname );
2063
2064 /* Helper strings. */
2065 num2str( scargo, pilot_cargoUsed( ship ), 0 );
2066
2067 /* Destroy intrinsic widget if applicable. */
2068 if ( widget_exists( wid, "iarIntrinsic" ) )
2069 window_destroyWidget( wid, "iarIntrinsic" );
2070
2071 l += scnprintf( &buf[l], sizeof( buf ) - l, "%s", _( "Name:" ) );
2072 for ( int i = 0; i < hname - 1; i++ )
2073 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2074 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Model:" ) );
2075 for ( int i = 0; i < hmodelname - 1; i++ )
2076 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2077 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Class:" ) );
2078 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Acquired Date:" ) );
2079 for ( int i = 0; i < hacquired + 1; i++ )
2080 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2081 if ( ship_mode == 0 ) {
2082 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Value:" ) );
2083 if ( player.fleet_capacity > 0 )
2084 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s",
2085 _( "Fleet Capacity:" ) );
2086 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Crew:" ) );
2087 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Mass:" ) );
2088 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Jump Time:" ) );
2089 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Accel:" ) );
2090 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Speed:" ) );
2091 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Turn:" ) );
2092 l +=
2093 scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Time Constant:" ) );
2094 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Detected at:" ) );
2095 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Signature:" ) );
2096 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Stealth at:" ) );
2097 l +=
2098 scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Scanning time:" ) );
2099 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2100 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Absorption:" ) );
2101 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Shield:" ) );
2102 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Armour:" ) );
2103 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Energy:" ) );
2104 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Cargo Space:" ) );
2105 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Fuel:" ) );
2106 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2107 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Ship Status:" ) );
2108 } else {
2109 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Time Flown:" ) );
2110 l +=
2111 scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Jumped Times:" ) );
2112 l +=
2113 scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Landed Times:" ) );
2114 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Damage Done:" ) );
2115 l +=
2116 scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", _( "Damage Taken:" ) );
2117 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s",
2118 _( "Ships Destroyed:" ) );
2119 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n\n%s",
2120 _( "Intrinsic Outfits:" ) );
2121 }
2122 window_modifyText( wid, "txtSDesc", buf );
2123
2124 /* Fill the buffer. */
2125 /* Generic. */
2126 l = 0;
2127 l += scnprintf( &buf[l], sizeof( buf ) - l, "%s", ship->name );
2128 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", modelname );
2129 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s",
2130 _( ship_classDisplay( ship->ship ) ) );
2131 /* On some platforms, the acquired date got truncated to 32 bits and is not
2132 * restorable. It prints very low values instead of the true values. Since
2133 * we can't fix it, just detect it as a really small value and display as
2134 * unknown. This was detected at around 0.12.0, but is much older.
2135 * TODO remove this sometime around 0.14.0. */
2136 if ( ( ps->acquired_date == 0 ) ||
2137 ( ps->acquired_date < start_date() >> 4 ) )
2138 snprintf( tbuf, sizeof( tbuf ), _( "Unknown" ) );
2139 else
2140 ntime_prettyBuf( tbuf, sizeof( tbuf ), ps->acquired_date, 2 );
2141 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", tbuf );
2142
2143 h = gl_printHeightRaw( &gl_defFont, wgtw, buf );
2144 window_moveWidget( wid, "txtAcquired", x, -40 - h - 6 );
2145 window_modifyText( wid, "txtAcquired", acquired );
2146
2147 for ( int i = 0; i < hacquired + 1; i++ )
2148 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2149 if ( ship_mode == 0 ) {
2150 /* Some core stats. */
2151 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s", buf_price );
2152 if ( player.fleet_capacity > 0 )
2153 l +=
2154 scnprintf( &buf[l], sizeof( buf ) - l, "\n%d", ship->ship->points );
2155 l += scnprintf(
2156 &buf[l], sizeof( buf ) - l, "\n#%c%s%d#0",
2157 EQ_COMP( MAX( 1, (int)round( ship->crew ) ), ship->ship->crew, 0 ) );
2158 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s#0 %s",
2159 num2strU( ship->solid.mass, 0 ), UNIT_MASS );
2160 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2161 l += scnprintf( &buf[l], sizeof( buf ) - l, _( "%s average" ), nt );
2162 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2163 l +=
2164 scnprintf( &buf[l], sizeof( buf ) - l, _( "#%c%s%.0f#0 %s" ),
2165 EQ_COMP( ship->accel, ship->ship->accel, 0 ), UNIT_ACCEL );
2166 l += scnprintf(
2167 &buf[l], sizeof( buf ) - l,
2168 _( "\n#%c%s%.0f#0 %s (max #%c%s%.0f#0 %s)" ),
2169 EQ_COMP( ship->speed, ship->ship->speed, 0 ), UNIT_SPEED,
2170 EQ_COMP( solid_maxspeed( &ship->solid, ship->speed, ship->accel ),
2171 solid_maxspeed( &ship->solid, ship->ship->speed,
2172 ship->ship->accel ),
2173 0 ),
2174 UNIT_SPEED );
2175 l += scnprintf(
2176 &buf[l], sizeof( buf ) - l, _( "\n#%c%s%.0f#0 %s" ),
2177 EQ_COMP( ship->turn * 180. / M_PI, ship->ship->turn * 180. / M_PI, 0 ),
2178 UNIT_ROTATION );
2179 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%.0f%%",
2180 ship->stats.time_mod * ship->ship->dt_default * 100. );
2181 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s %s\n",
2182 num2strU( ship->ew_detection, 0 ), UNIT_DISTANCE );
2183 l += scnprintf( &buf[l], sizeof( buf ) - l, _( "%s %s" ),
2184 num2strU( ship->ew_signature, 0 ), UNIT_DISTANCE );
2185 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s %s\n",
2186 num2strU( ship->ew_stealth, 0 ), UNIT_DISTANCE );
2187 l += scnprintf( &buf[l], sizeof( buf ) - l, _( "%.1f %s" ),
2188 pilot_ewScanTime( ship ), UNIT_TIME );
2189 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n\n" );
2190 /* Health. */
2191 l += scnprintf(
2192 &buf[l], sizeof( buf ) - l, "#%c%s%.0f%%\n",
2193 EQ_COMP( ship->dmg_absorb * 100., ship->ship->dmg_absorb * 100., 0 ) );
2194 l += scnprintf( &buf[l], sizeof( buf ) - l, _( "#%c%s%s#0 %s" ),
2195 EQ_COMP_I( ship->shield_max, ship->ship->shield, 0 ),
2196 num2strU( ship->shield_max, 0 ), UNIT_ENERGY );
2197 l += scnprintf(
2198 &buf[l], sizeof( buf ) - l, _( " (#%c%s%s#0 %s)" ),
2199 EQ_COMP_I( ship->shield_regen, ship->ship->shield_regen, 0 ),
2200 num2strU( ship->shield_regen, 1 ), UNIT_POWER );
2201 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2202 l += scnprintf( &buf[l], sizeof( buf ) - l, _( "#%c%s%s#0 %s" ),
2203 EQ_COMP_I( ship->armour_max, ship->ship->armour, 0 ),
2204 num2strU( ship->armour_max, 0 ), UNIT_ENERGY );
2205 l += scnprintf(
2206 &buf[l], sizeof( buf ) - l, _( " (#%c%s%s#0 %s)" ),
2207 EQ_COMP_I( ship->armour_regen, ship->ship->armour_regen, 0 ),
2208 num2strU( ship->armour_regen, 1 ), UNIT_POWER );
2209 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2210 l += scnprintf( &buf[l], sizeof( buf ) - l, _( "#%c%s%s#0 %s" ),
2211 EQ_COMP_I( ship->energy_max, ship->ship->energy, 0 ),
2212 num2strU( ship->energy_max, 0 ), UNIT_ENERGY );
2213 l += scnprintf(
2214 &buf[l], sizeof( buf ) - l, _( " (#%c%s%s#0 %s)" ),
2215 EQ_COMP_I( ship->energy_regen, ship->ship->energy_regen, 0 ),
2216 num2strU( ship->energy_regen, 1 ), UNIT_POWER );
2217 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2218 /* Misc. */
2219 l += scnprintf( &buf[l], sizeof( buf ) - l, _( "%s / #%c%s%s#0 %s" ),
2220 scargo, EQ_COMP_I( cargo, ship->ship->cap_cargo, 0 ),
2221 num2strU( cargo, 0 ), UNIT_MASS );
2222 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2223 l += scnprintf( &buf[l], sizeof( buf ) - l, _( "%s %s (%d %s)" ),
2224 num2strU( ship->fuel_max, 0 ), UNIT_UNIT, jumps,
2225 n_( "jump", "jumps", jumps ) );
2226 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2227 /*l +=*/scnprintf( &buf[l], sizeof( buf ) - l, "\n#%c%s#0",
2228 spaceworthy ? '0' : 'r', errorReport );
2229 } else {
2230 int destroyed = 0;
2231 for ( int i = 0; i < SHIP_CLASS_TOTAL; i++ )
2232 destroyed += ps->ships_destroyed[i];
2233 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2234 l += scnprintf( &buf[l], sizeof( buf ) - l, _( "%s hours" ),
2235 num2strU( ps->time_played / 3600., 1 ) );
2236 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s",
2237 num2strU( ps->jumped_times, 0 ) );
2238 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n%s",
2239 num2strU( ps->landed_times, 0 ) );
2240 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2241 l += scnprintf( &buf[l], sizeof( buf ) - l, _( "%s %s" ),
2243 UNIT_ENERGY );
2244 l += scnprintf( &buf[l], sizeof( buf ) - l, "\n" );
2245 l +=
2246 scnprintf( &buf[l], sizeof( buf ) - l, _( "%s %s" ),
2248 UNIT_ENERGY );
2249 /*l +=*/scnprintf( &buf[l], sizeof( buf ) - l, "\n%s",
2250 num2strU( destroyed, 0 ) );
2251 }
2252 window_modifyText( wid, "txtDDesc", buf );
2253
2254 /* Add intrinsic outfit image array. */
2255 if ( ship_mode != 0 ) {
2256 int tx, ty, tw, th;
2257 ImageArrayCell *cells;
2258 int ncells = array_size( ship->outfit_intrinsic );
2259 Outfit const **outfits = (Outfit const **)array_create( Outfit *);
2260 for ( int i = 0; i < ncells; i++ )
2261 array_push_back( &outfits, ship->outfit_intrinsic[i].outfit );
2262
2263 cells = outfits_imageArrayCells( outfits, &ncells, ship, 0 );
2264
2265 window_posWidget( wid, "txtSDesc", &tx, &ty );
2266 window_dimWidget( wid, "txtSDesc", &tw, &th );
2267 ty = -40 - window_getTextHeight( wid, "txtSDesc" ) - 10;
2268 window_addImageArray( wid, tx, ty, ww - x - 128 - 30 - 10,
2269 ty - 20 + wh - bh - 30, "iarIntrinsic", 64, 64,
2270 cells, ncells, NULL, NULL, NULL );
2271
2272 array_free( outfits );
2273 }
2274
2275 /* Clean up. */
2276 free( nt );
2277
2278 /* Set checkboxes. */
2279 window_checkboxSet( wid, "chkFav", favourite );
2280 if ( player.fleet_capacity > 0 )
2281 window_checkboxSet( wid, "chkDeploy", deployed );
2282
2283 /* button disabling */
2284 if ( onboard ) {
2285 window_disableButton( wid, "btnSellShip" );
2286 window_disableButton( wid, "btnChangeShip" );
2287 } else {
2288 window_enableButton( wid, "btnChangeShip" );
2289 window_enableButton( wid, "btnSellShip" );
2290 }
2291
2292 /* Disallow selling of unique ships. */
2293 if ( ship_isFlag( ps->p->ship, SHIP_UNIQUE ) )
2294 window_disableButton( wid, "btnSellShip" );
2295
2296 /* If pilot-dependent outfit filter modes are active, we have to regenerate
2297 * outfits always. */
2298 if ( ( equipment_outfitMode == 2 ) && ( eq_wgt.selected != prevship ) )
2299 equipment_regenLists( wid, 1, 0 );
2300}
2301#undef EQ_COMP
2307void equipment_updateOutfits( unsigned int wid, const char *str )
2308{
2309 (void)str;
2310 int i, active;
2311
2312 /* Must have outfit. */
2313 active = window_tabWinGetActive( wid, EQUIPMENT_OUTFIT_TAB );
2314 i = toolkit_getImageArrayPos( wid, EQUIPMENT_OUTFITS );
2315 if ( i < 0 || array_size( iar_outfits[active] ) == 0 ) {
2316 eq_wgt.outfit = NULL;
2317 return;
2318 }
2319
2320 eq_wgt.outfit = iar_outfits[active][i];
2321}
2322
2328static void equipment_filterOutfits( unsigned int wid, const char *str )
2329{
2330 (void)str;
2331 equipment_regenLists( wid, 1, 0 );
2332}
2333
2337
2338static void equipment_outfitPopdownSelect( unsigned int wid, const char *str )
2339{
2340 int m = toolkit_getListPos( wid, str );
2341 if ( m == equipment_outfitMode )
2342 return;
2343
2345 equipment_regenLists( wid, 1, 0 );
2346 player.eq_outfitMode = m;
2347}
2348
2349static void equipment_outfitPopdownActivate( unsigned int wid, const char *str )
2350{
2352 window_destroyWidget( wid, str );
2353}
2354
2355static void equipment_outfitPopdown( unsigned int wid, const char *str )
2356{
2357 const char *name = "lstOutfitPopdown";
2358 const char *modes[] = {
2359 N_( "Show all outfits" ),
2360 N_( "Show only outfits equipable on any of your ships" ),
2361 N_( "Show only outfits equipable on current ship" ),
2362 N_( "Show only light outfits" ),
2363 N_( "Show only medium outfits" ),
2364 N_( "Show only heavy outfits" ),
2365 };
2366 char **modelist;
2367 const size_t n = sizeof( modes ) / sizeof( const char * );
2368 int x, y, w, h;
2369
2370 if ( widget_exists( wid, name ) ) {
2371 window_destroyWidget( wid, name );
2372 return;
2373 }
2374
2375 modelist = malloc( sizeof( modes ) );
2376 for ( size_t i = 0; i < n; i++ )
2377 modelist[i] = strdup( _( modes[i] ) );
2378
2379 window_dimWidget( wid, str, &w, &h );
2380 window_posWidget( wid, str, &x, &y );
2381 window_addList( wid, x + w, y - 120 + h, 350, 120, name, modelist, n,
2383 equipment_outfitPopdownActivate );
2384 window_setFocus( wid, name );
2385}
2386
2395static void equipment_changeTab( unsigned int wid, const char *wgt, int old,
2396 int tab )
2397{
2398 (void)wgt;
2399 iar_data_t old_data;
2400
2401 toolkit_saveImageArrayData( wid, EQUIPMENT_OUTFITS, &iar_data[old] );
2402
2403 /* Store the currently-saved positions for the new tab. */
2404 old_data = iar_data[tab];
2405
2406 /* Resetting the input will cause the outfit list to be regenerated. */
2407 if ( widget_exists( wid, EQUIPMENT_FILTER ) )
2408 window_setInput( wid, EQUIPMENT_FILTER, NULL );
2409 else
2410 equipment_regenLists( wid, 1, 0 );
2411
2412 /* Set positions for the new tab. This is necessary because the stored
2413 * position for the new tab may have exceeded the size of the old tab,
2414 * resulting in it being clipped. */
2415 toolkit_loadImageArrayData( wid, EQUIPMENT_OUTFITS, &old_data );
2416
2417 /* Focus the outfit image array. */
2418 window_setFocus( wid, EQUIPMENT_OUTFITS );
2419}
2420
2424static void equipment_rightClickShips( unsigned int wid, const char *str )
2425{
2426 const char *shipname = toolkit_getImageArray( wid, str );
2427 PlayerShip_t *ps = player_getPlayerShip( shipname );
2428
2429 /* Must have fleet capacity. */
2430 if ( player.fleet_capacity <= 0 )
2431 return;
2432
2433 /* Deploy the ship if applicable. */
2434 if ( ps == NULL )
2435 return;
2436
2437 /* Don't do anything for player ship. */
2438 if ( strcmp( shipname, player.p->name ) == 0 )
2439 return;
2440
2441 if ( !ps->deployed && ship_isFlag( ps->p->ship, SHIP_NOESCORT ) ) {
2442 dialogue_msg( _( "Invalid Escort" ),
2443 _( "You can not set your ship '%s' as an escort!" ),
2444 ps->p->name );
2445 return;
2446 }
2447
2448 /* Try to deploy. */
2449 if ( pfleet_toggleDeploy( ps, !ps->deployed ) )
2450 return;
2451
2452 if ( player.fleet_capacity > 0 )
2453 window_checkboxSet( wid, "chkDeploy", ps->deployed );
2454
2455 equipment_regenLists( wid, 0, 1 );
2456}
2457
2463static void equipment_transChangeShip( unsigned int wid, const char *str )
2464{
2465 (void)str;
2466
2467 equipment_changeShip( wid );
2468
2469 /* update the window to reflect the change */
2470 equipment_updateShips( wid, NULL );
2471}
2472
2476static void equipment_changeShip( unsigned int wid )
2477{
2478 const char *shipname, *filtertext;
2479 PlayerShip_t *ps;
2480 int i;
2481
2482 shipname = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
2483 ps = player_getPlayerShip( shipname );
2484 if ( ps != NULL ) {
2485 /* Swap deployed status. */
2486 player.ps.deployed = ps->deployed;
2487 }
2488
2489 if ( !equipment_canSwapPlayerShip( shipname ) ) {
2491 return;
2492 }
2493
2494 /* Store active tab, filter text, and positions for the outfits. */
2495 i = window_tabWinGetActive( wid, EQUIPMENT_OUTFIT_TAB );
2496 toolkit_saveImageArrayData( wid, EQUIPMENT_OUTFITS, &iar_data[i] );
2497 if ( widget_exists( wid, EQUIPMENT_FILTER ) )
2498 filtertext = window_getInput( equipment_wid, EQUIPMENT_FILTER );
2499 else
2500 filtertext = NULL;
2501
2502 /* Swap ship. */
2503 player_swapShip( shipname, 1 );
2505
2506 /* What happens here is the gui gets recreated when the player swaps ship.
2507 * This causes all the windows to be destroyed and the 'wid' we have here
2508 * becomes invalid. However, since we store it in a global variable we can
2509 * recover it and use it instead. */
2510 wid = equipment_wid;
2511
2512 /* Restore outfits image array properties. */
2513 window_tabWinSetActive( wid, EQUIPMENT_OUTFIT_TAB, i );
2514 toolkit_loadImageArrayData( wid, EQUIPMENT_OUTFITS, &iar_data[i] );
2515 if ( widget_exists( wid, EQUIPMENT_FILTER ) )
2516 window_setInput( wid, EQUIPMENT_FILTER, filtertext );
2517
2518 /* Regenerate ship widget. */
2519 equipment_regenLists( wid, 0, 1 );
2520
2521 /* Focus new ship. */
2522 toolkit_setImageArrayPos( wid, EQUIPMENT_SHIPS, 0 );
2523 toolkit_setImageArrayOffset( wid, EQUIPMENT_SHIPS, 0. );
2524}
2525
2532static void equipment_unequipShip( unsigned int wid, const char *str )
2533{
2534 (void)str;
2535 Pilot *ship = eq_wgt.selected->p;
2536
2537 /*
2538 * Unequipping is disallowed under two conditions. Firstly, the ship may not
2539 * be unequipped when it has fighters deployed in space. Secondly, it cannot
2540 * unequip if it's carrying more cargo than the ship normally fits, i.e.
2541 * by equipping cargo pods.
2542 */
2543 if ( pilot_cargoUsed( ship ) > ship->ship->cap_cargo ) {
2544 dialogue_alert( _( "You can't unequip your ship when you have more cargo "
2545 "than it can hold without modifications!" ) );
2546 return;
2547 }
2548 if ( dialogue_YesNo( _( "Unequip Ship" ), /* confirm */
2549 _( "Are you sure you want to remove all equipment from "
2550 "your ship?" ) ) == 0 )
2551 return;
2552 if ( pilot_hasDeployed( ship ) ) {
2553 if ( !dialogue_YesNo( _( "Recall Fighters" ),
2554 _( "This action will recall your deployed "
2555 "fighters. Is that OK?" ) ) )
2556 return;
2557 /* Recall fighters. */
2558 escort_clearDeployed( ship, -1 );
2559 }
2560
2561 /* Remove all outfits. */
2562 for ( int i = 0; i < array_size( ship->outfits ); i++ ) {
2563 int ret;
2564 PilotOutfitSlot *s = ship->outfits[i];
2565 const Outfit *o = s->outfit;
2566
2567 /* Skip null outfits. */
2568 if ( o == NULL )
2569 continue;
2570
2571 /* Ignore locked slots. */
2572 if ( s->sslot->locked )
2573 continue;
2574
2575 /* Remove ammo first. */
2576 pilot_rmAmmo( ship, s, pilot_maxAmmoO( ship, o ) );
2577
2578 /* Remove rest. */
2579 ret = pilot_rmOutfitRaw( ship, s );
2580 if ( ret == 0 )
2582 }
2583
2584 /* Recalculate stats. */
2585 pilot_calcStats( ship );
2586 pilot_healLanded( ship );
2587
2588 /* Regenerate list. */
2589 equipment_regenLists( wid, 1, 1 );
2590
2591 /* Regenerate outfits. */
2593
2594 /* Update weapon sets if needed. */
2595 if ( ship->autoweap ) {
2596 pilot_weaponAuto( ship );
2597 ws_copy( eq_wgt.selected->weapon_sets, ship->weapon_sets );
2598 }
2599 pilot_weaponSafe( ship );
2600
2601 /* Notify GUI of modification. */
2602 gui_setShip();
2603}
2604
2608static void equipment_autoequipShip( unsigned int wid, const char *str )
2609{
2610 (void)str;
2611 (void)wid;
2612 Pilot *ship;
2613 int doswap;
2614 const char *curship;
2615 const char *file = AUTOEQUIP_PATH;
2616
2617 ship = eq_wgt.selected->p;
2618
2619 /* Swap ship if necessary. */
2620 doswap = ( ship != player.p );
2621 if ( doswap ) {
2622 curship = player.p->name;
2623 player_swapShip( ship->name, 0 );
2624 }
2625
2626 /* Create the environment */
2627 if ( autoequip_env == LUA_NOREF ) {
2628 /* Read File. */
2629 size_t bufsize;
2630 char *buf = ndata_read( file, &bufsize );
2631 if ( buf == NULL ) {
2632 WARN( _( "File '%s' not found!" ), file );
2633 return;
2634 }
2635 /* New env. */
2636 autoequip_env = nlua_newEnv( file );
2637 nlua_loadStandard( autoequip_env );
2638 nlua_loadTk( autoequip_env );
2639 if ( nlua_dobufenv( autoequip_env, buf, bufsize, file ) != 0 ) {
2640 WARN( _( "Failed to run '%s': %s" ), file, lua_tostring( naevL, -1 ) );
2641 free( buf );
2642 lua_pop( naevL, 1 );
2643 goto autoequip_cleanup;
2644 }
2645
2646 free( buf );
2647 }
2648
2649 /* Run func. */
2650 nlua_getenv( naevL, autoequip_env, "autoequip" );
2651 if ( !lua_isfunction( naevL, -1 ) ) {
2652 WARN( _( "'%s' doesn't have valid required 'autoequip' function!" ),
2653 file );
2654 lua_pop( naevL, 1 );
2655 goto autoequip_cleanup;
2656 }
2657 lua_pushpilot( naevL, player.p->id );
2658 if ( nlua_pcall( autoequip_env, 1, 0 ) ) {
2659 WARN( _( "'%s' failed to run required 'autoequip' function: %s" ), file,
2660 lua_tostring( naevL, -1 ) );
2661 lua_pop( naevL, 1 );
2662 goto autoequip_cleanup;
2663 }
2664
2665 hooks_run( "equip" ); /* Equipped. */
2666
2667 /* Clean up. */
2668autoequip_cleanup:
2669 if ( doswap ) {
2670 player_swapShip( curship, 0 );
2671 toolkit_setImageArray( equipment_wid, EQUIPMENT_SHIPS, ship->name );
2673 }
2674}
2675
2681static void equipment_sellShip( unsigned int wid, const char *str )
2682{
2683 (void)str;
2684 char buf[ECON_CRED_STRLEN], *name;
2685 credits_t price;
2686 PlayerShip_t *ps;
2687 Pilot *p;
2688 const Ship *s;
2689 HookParam hparam[3];
2690 const char *shipname = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
2691
2692 if ( !equipment_canSellPlayerShip( shipname ) ) {
2694 return;
2695 }
2696
2697 /* Calculate price. */
2698 price = player_shipPrice( shipname, 0 );
2699 credits2str( buf, price, 2 );
2700
2701 /* Check if player really wants to sell. */
2702 if ( !dialogue_YesNo(
2703 _( "Sell Ship" ),
2704 _( "Are you sure you want to sell your ship %s for %s?" ), shipname,
2705 buf ) )
2706 return;
2707
2708 /* Check if deployed and undeploy. */
2709 ps = player_getPlayerShip( shipname );
2710 if ( ps->deployed ) {
2711 if ( pfleet_toggleDeploy( ps, 0 ) )
2712 return;
2713 }
2714
2715 /* Store ship type. */
2716 p = player_getShip( shipname );
2717 s = p->ship;
2718
2719 /* Sold. */
2720 name = strdup( shipname );
2721 player_modCredits( price );
2722 player_rmShip( shipname );
2723
2724 /* Destroy widget - must be before widget. */
2725 equipment_regenLists( wid, 0, 1 );
2726
2727 /* Display widget. */
2728 dialogue_msg( _( "Ship Sold" ), _( "You have sold your ship %s for %s." ),
2729 name, buf );
2730
2731 /* Run hook. */
2732 hparam[0].type = HOOK_PARAM_SHIP;
2733 hparam[0].u.ship = s;
2734 hparam[1].type = HOOK_PARAM_STRING;
2735 hparam[1].u.str = name;
2736 hparam[2].type = HOOK_PARAM_SENTINEL;
2737 hooks_runParam( "ship_sell", hparam );
2738 land_needsTakeoff( 1 );
2739 free( name );
2740}
2741
2748static void equipment_renameShip( unsigned int wid, const char *str )
2749{
2750 (void)str;
2751 const char *shipname = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
2752 Pilot *ship = player_getShip( shipname );
2753 char *newname = dialogue_input( _( "Ship Name" ), 1, 60,
2754 _( "Please enter a new name for your %s:" ),
2755 _( ship->ship->name ) );
2756
2757 /* Player cancelled the dialogue. */
2758 if ( newname == NULL )
2759 return;
2760
2761 /* Must not have same name. */
2762 if ( player_hasShip( newname ) ) {
2763 dialogue_msg( _( "Name Collision" ),
2764 _( "Please do not give the ship the same name as another "
2765 "of your ships." ) );
2766 free( newname );
2767 return;
2768 }
2769
2770 free( ship->name );
2771 ship->name = newname;
2772
2773 /* Destroy widget - must be before widget. */
2774 equipment_regenLists( wid, 0, 1 );
2775}
2776
2780static void equipment_shipMode( unsigned int wid, const char *str )
2781{
2782 ship_mode = 1 - ship_mode;
2783 equipment_updateShips( wid, str );
2784}
2785
2789static int equipment_playerAddOutfit( const Outfit *o, int quantity )
2790{
2791 if ( outfit_isProp( o, OUTFIT_PROP_UNIQUE ) &&
2792 ( player_outfitOwned( o ) > 0 ) )
2793 return 1;
2794 return player_addOutfit( o, quantity );
2795}
2796
2800static int equipment_playerRmOutfit( const Outfit *o, int quantity )
2801{
2802 if ( outfit_isProp( o, OUTFIT_PROP_UNIQUE ) )
2803 return 1;
2804 return player_rmOutfit( o, quantity );
2805}
2806
2811{
2812 /* Free stored positions. */
2813 for ( int i = 0; i < OUTFIT_TABS; i++ )
2814 array_free( iar_outfits[i] );
2815 memset( iar_outfits, 0, sizeof( Outfit ** ) * OUTFIT_TABS );
2816
2818}
2819
2824{
2825 if ( wgt == NULL )
2826 wgt = &eq_wgt;
2827 /* Safe defaults. */
2828 memset( wgt, 0, sizeof( CstSlotWidget ) );
2829 wgt->slot = -1;
2830 wgt->mouseover = -1;
2831 wgt->weapons = -1;
2832}
2833
2834void equipment_slotSelect( CstSlotWidget *wgt, PlayerShip_t *p )
2835{
2836 wgt->selected = p;
2837
2838 int needsgfx = 0;
2839 for ( int i = 0; i < array_size( p->p->outfits ); i++ ) {
2840 Outfit *o = (Outfit *)p->p->outfits[i]->outfit;
2841 if ( ( o != NULL ) && !outfit_gfxStoreLoaded( o ) ) {
2842 outfit_setProp( o, OUTFIT_PROP_NEEDSGFX );
2843 needsgfx = 1;
2844 }
2845 }
2846 if ( needsgfx )
2847 outfit_gfxStoreLoadNeeded();
2848}
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_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_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:134
#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
#define BUTTON_HEIGHT
Definition board.c:28
void credits2str(char *str, credits_t credits, int decimals)
Converts credits to a usable string for displaying.
Definition commodity.c:75
void dialogue_alert(const char *fmt,...)
Displays an alert popup with only an ok button and a message.
Definition dialogue.c:130
char * dialogue_input(const char *title, int min, int max, const char *fmt,...)
Creates a dialogue that allows the player to write a message.
Definition dialogue.c:441
void dialogue_msg(const char *caption, const char *fmt,...)
Opens a dialogue window with an ok button and a message.
Definition dialogue.c:227
int dialogue_YesNo(const char *caption, const char *fmt,...)
Runs a dialogue with both yes and no options.
Definition dialogue.c:352
void effect_clear(Effect **efxlist)
Clears an effect list, removing all active effects.
Definition effect.c:524
void equipment_slotDeselect(CstSlotWidget *wgt)
Deselects equipment stuff.
Definition equipment.c:2823
static int equipment_creating
Definition equipment.c:72
static const char * eq_qSym(double cur, double base, int inv)
Gets the symbol for comparing a current value vs a ship base value.
Definition equipment.c:1974
static void equipment_rightClickShips(unsigned int wid, const char *str)
Toggles deployed status.
Definition equipment.c:2424
static double equipment_dir
Definition equipment.c:69
static int equipment_mouseInColumn(const PilotOutfitSlot *lst, double y, double h, double my)
Handles a mouse press in column.
Definition equipment.c:1099
static CstSlotWidget eq_wgt
Definition equipment.c:68
static int ship_mode
Definition equipment.c:73
static int equipment_playerAddOutfit(const Outfit *o, int quantity)
Wrapper to only add unique outfits.
Definition equipment.c:2789
static void equipment_shipMode(unsigned int wid, const char *str)
Toggles the ship visualization mode.
Definition equipment.c:2780
static void equipment_changeTab(unsigned int wid, const char *wgt, int old, int tab)
Ensures the tab's selected item is reflected in the ship slot list.
Definition equipment.c:2395
static iar_data_t iar_data[OUTFIT_TABS]
Definition equipment.c:74
static void equipment_sellShip(unsigned int wid, const char *str)
Player tries to sell a ship.
Definition equipment.c:2681
static int equipment_playerRmOutfit(const Outfit *o, int quantity)
Wrapper to only remove unique outfits.
Definition equipment.c:2800
static void equipment_getDim(unsigned int wid, int *w, int *h, int *sw, int *sh, int *ow, int *oh, int *ew, int *eh, int *cw, int *ch, int *bw, int *bh)
Gets the window dimensions.
Definition equipment.c:286
static void equipment_renderColumn(double x, double y, double w, double h, const PilotOutfitSlot *lst, const char *txt, int selected, Outfit *o, Pilot *p, const CstSlotWidget *wgt)
Renders an outfit column.
Definition equipment.c:478
static void equipment_outfitPopdownSelect(unsigned int wid, const char *str)
Definition equipment.c:2338
void equipment_regenLists(unsigned int wid, int outfits, int ships)
Regenerates the equipment window lists.
Definition equipment.c:1429
static void equipment_renameShip(unsigned int wid, const char *str)
Renames the selected ship.
Definition equipment.c:2748
static void equipment_renderOverlayColumn(double x, double y, double h, PilotOutfitSlot *lst, int mover, CstSlotWidget *wgt)
Renders an outfit column.
Definition equipment.c:772
void equipment_cleanup(void)
Cleans up after the equipment stuff.
Definition equipment.c:2810
static void equipment_filterOutfits(unsigned int wid, const char *str)
Handles text input in the filter input widget.
Definition equipment.c:2328
static int equipment_outfitMode
Definition equipment.c:78
static void equipment_genLists(unsigned int wid)
Generates a new ship/outfit lists if needed.
Definition equipment.c:1683
static void equipment_rightClickOutfits(unsigned int wid, const char *str)
Handles right-click on unequipped outfit.
Definition equipment.c:150
void equipment_updateOutfits(unsigned int wid, const char *str)
Updates the player's outfit list.
Definition equipment.c:2307
static void equipment_transChangeShip(unsigned int wid, const char *str)
Changes ship.
Definition equipment.c:2463
static void equipment_genOutfitList(unsigned int wid)
Generates the outfit list.
Definition equipment.c:1856
static void equipment_toggleFav(unsigned int wid, const char *wgt)
Handles toggling of the favourite checkbox.
Definition equipment.c:1636
static void equipment_autoequipShip(unsigned int wid, const char *str)
Does the autoequip magic on the player's ship.
Definition equipment.c:2608
void equipment_slotWidget(unsigned int wid, double x, double y, double w, double h, CstSlotWidget *data)
Creates the slot widget and initializes it.
Definition equipment.c:462
void equipment_updateShips(unsigned int wid, const char *str)
Updates the player's ship window.
Definition equipment.c:1992
static void equipment_renderShip(double bx, double by, double bw, double bh, void *data)
Renders the ship in the equipment window.
Definition equipment.c:959
static void equipment_calculateSlots(const Pilot *p, double bw, double bh, double *w, double *h, int *n, int *m)
Calculates the size the slots need to be for a given window.
Definition equipment.c:598
static int equipment_mouseColumn(unsigned int wid, const SDL_Event *event, double mx, double my, double y, double h, PilotOutfitSlot *os, Pilot *p, int selected, CstSlotWidget *wgt)
Handles a mouse press in a column.
Definition equipment.c:1130
static void equipment_renderMisc(double bx, double by, double bw, double bh, void *data)
Renders the custom equipment widget.
Definition equipment.c:682
int equipment_canSwapPlayerShip(const char *shipname)
Makes sure it's valid to change ships in the equipment view.
Definition equipment.c:1516
int equipment_shipStats(char *buf, int max_len, const Pilot *s, int dpseps, int name)
Creates and allocates a string containing the ship stats.
Definition equipment.c:1604
void equipment_addAmmo(void)
Adds all the ammo it can to the player.
Definition equipment.c:1578
static int equipment_swapSlot(unsigned int wid, Pilot *p, PilotOutfitSlot *slot)
Swaps an equipment slot.
Definition equipment.c:1319
static char eq_qCol(double cur, double base, int inv)
Gets the colour for comparing a current value vs a ship base value.
Definition equipment.c:1962
static void equipment_changeShip(unsigned int wid)
Player attempts to change ship.
Definition equipment.c:2476
int equipment_canSellPlayerShip(const char *shipname)
Makes sure it's valid to sell a ship.
Definition equipment.c:1500
static int equipment_mouseSlots(unsigned int wid, const SDL_Event *event, double x, double y, double w, double h, double rx, double ry, void *data)
Does mouse input for the custom equipment widget.
Definition equipment.c:1244
static void equipment_unequipShip(unsigned int wid, const char *str)
Unequips the player's ship.
Definition equipment.c:2532
static unsigned int equipment_wid
Definition equipment.c:71
void equipment_open(unsigned int wid)
Opens the player's equipment window.
Definition equipment.c:329
static Outfit ** iar_outfits[OUTFIT_TABS]
Definition equipment.c:75
static void equipment_renderSlots(double bx, double by, double bw, double bh, void *data)
Renders the equipment slots.
Definition equipment.c:629
static void equipment_genShipList(unsigned int wid)
Generates the ship list.
Definition equipment.c:1693
static unsigned int equipment_lastick
Definition equipment.c:70
static void equipment_renderOverlaySlots(double bx, double by, double bw, double bh, void *data)
Renders the equipment overlay.
Definition equipment.c:859
int escort_clearDeployed(Pilot *p, int slot)
Clears deployed escorts of a pilot.
Definition escort.c:243
int gl_printHeightRaw(const glFont *ft_font, const int width, const char *text)
Gets the height of a non-formatted string.
Definition font.c:1050
glFont gl_smallFont
Definition font.c:159
int gl_printWidthRaw(const glFont *ft_font, const char *text)
Gets the width that it would take to print some text.
Definition font.c:984
glFont gl_defFont
Definition font.c:158
int gl_printMidRaw(const glFont *ft_font, int width, double x, double y, const glColour *c, double outlineR, const char *text)
Displays text centered in position and width.
Definition font.c:821
int gl_printLinesRaw(const glFont *ft_font, const int width, const char *text)
Gets the number of lines of a non-formatted string.
Definition font.c:1203
int gl_printMaxRaw(const glFont *ft_font, const int max, double x, double y, const glColour *c, double outlineR, const char *text)
Behaves like gl_printRaw but stops displaying text after a certain distance.
Definition font.c:753
int gl_printMid(const glFont *ft_font, const int width, double x, double y, const glColour *c, const char *fmt,...)
Displays text centered in position and width.
Definition font.c:863
void gui_setShip(void)
Player just upgraded their ship or modified it.
Definition gui.c:1880
int hooks_runParam(const char *stack, const HookParam *param)
Runs all the hooks of stack.
Definition hook.c:1029
int hooks_run(const char *stack)
Runs all the hooks of stack.
Definition hook.c:1049
Handles the info menu.
void info_update(void)
Updates the info windows.
Definition info.c:367
void land_errDialogueBuild(const char *fmt,...)
Generates error dialogues used by several landing tabs.
Definition land.c:226
void land_errClear(void)
Clear error dialogues.
Definition land.c:217
void land_buttonTakeoff(unsigned int wid, const char *unused)
Wrapper for takeoff mission button.
Definition land.c:1124
void land_refuel(void)
Refuels the player's current ship, if possible.
Definition land.c:995
int land_errDisplay(void)
Displays an error if applicable.
Definition land.c:248
int outfit_altText(char *buf, int n, const Outfit *o, const Pilot *plt)
Computes the alt text for an outfit.
ImageArrayCell * outfits_imageArrayCells(const Outfit **outfits, int *noutfits, const Pilot *p, int store)
Generates image array cells corresponding to outfits.
void outfits_updateEquipmentOutfits(void)
Updates the outfitter and equipment outfit image arrays.
mat4 mat4_identity(void)
Creates an identity matrix.
Definition mat4.c:335
void mat4_rotate(mat4 *m, double angle, double x, double y, double z)
Multiplies the given matrix by a rotation. (Follows the right-hand rule.)
Definition mat4.c:216
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 CLAMP(a, b, x)
Definition naev.h:41
#define MAX(x, y)
Definition naev.h:37
#define PATH_MAX
Definition naev.h:57
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
Definition ndata.c:207
int nlua_loadStandard(nlua_env env)
Loads the standard Naev Lua API.
Definition nlua.c:914
lua_State * naevL
Definition nlua.c:54
LuaPilot * lua_pushpilot(lua_State *L, LuaPilot pilot)
Pushes a pilot on the stack.
Definition nlua_pilot.c:576
int nlua_loadTk(nlua_env env)
Loads the Toolkit Lua library.
Definition nlua_tk.c:98
int num2str(char dest[NUM2STRLEN], double n, int decimals)
Converts a numeric value to a string.
Definition nstring.c:123
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
const char * num2strU(double n, int decimals)
Unsafe version of num2str that uses an internal buffer. Every call overwrites the return value.
Definition nstring.c:163
char * ntime_pretty(ntime_t t, int d)
Gets the time in a pretty human readable format.
Definition ntime.c:178
void ntime_prettyBuf(char *str, int max, ntime_t t, int d)
Gets the time in a pretty human readable format filling a preset buffer.
Definition ntime.c:194
glInfo gl_screen
Definition opengl.c:47
void gl_renderShader(double x, double y, double w, double h, double r, const SimpleShader *shd, const glColour *c, int center)
Renders a simple shader.
void gl_renderRect(double x, double y, double w, double h, const glColour *c)
Renders a rectangle.
void gl_renderSDF(const glTexture *texture, double x, double y, double w, double h, const glColour *c, double angle, double outline)
SDF Texture blitting backend.
void gl_renderScale(const glTexture *texture, double bx, double by, double bw, double bh, const glColour *c)
Blits a texture scaling it.
void gl_renderScaleAspect(const glTexture *texture, double bx, double by, double bw, double bh, const glColour *c)
Blits a texture scaling it to fit a rectangle, but conserves aspect ratio.
void gl_renderTextureRaw(GLuint texture, uint8_t flags, double x, double y, double w, double h, double tx, double ty, double tw, double th, const glColour *c, double angle)
Texture blitting backend.
void gl_renderCross(double x, double y, double r, const glColour *c)
Renders a cross at a given position.
int gl_fboCreate(GLuint *fbo, GLuint *tex, GLsizei width, GLsizei height)
Creates a framebuffer and its associated texture.
Definition opengl_tex.c:268
glTexture ** gl_addTexArray(glTexture **tex, glTexture *t)
Adds an element to a texture array.
glTexture ** gl_copyTexArray(glTexture **tex)
Copy a texture array.
glTexture * gl_newImage(const char *path, const unsigned int flags)
Loads an image as a texture.
Definition opengl_tex.c:587
int gl_fboAddDepth(GLuint fbo, GLuint *tex, GLsizei width, GLsizei height)
Adds a depth attachment to an FBO.
Definition opengl_tex.c:313
void gl_getSpriteFromDir(int *x, int *y, int sx, int sy, double dir)
Sets x and y to be the appropriate sprite for glTexture using dir.
Definition opengl_tex.c:977
int outfit_isLauncher(const Outfit *o)
Checks if outfit is a weapon launcher.
Definition outfit.c:649
char outfit_slotTypeColourFont(const OutfitSlot *os)
Gets a font colour character that roughly matches an outfit slot type colour.
Definition outfit.c:505
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
char outfit_slotSizeColourFont(const OutfitSlot *os)
Gets a font colour character that roughly matches an outfit slot size colour.
Definition outfit.c:487
int outfit_amount(const Outfit *o)
Gets the amount an outfit can hold.
Definition outfit.c:850
const char * slotName(const OutfitSlotType type)
Definition outfit.c:413
int outfit_gfxStoreLoad(Outfit *o)
Loads the store graphics for the outfit.
Definition outfit.c:198
const char * slotSize(const OutfitSlotSize o)
Gets the slot size as a string.
Definition outfit.c:436
const glColour * outfit_slotSizeColour(const OutfitSlot *os)
Gets the slot size colour for an outfit slot.
Definition outfit.c:469
void pilot_renderFramebuffer(Pilot *p, GLuint fbo, double fw, double fh, const Lighting *L)
Renders a pilot to a framebuffer.
Definition pilot.c:1849
ntime_t pilot_hyperspaceDelay(const Pilot *p)
Calculates the hyperspace delay for a pilot.
Definition pilot.c:3243
void pilot_dpseps(const Pilot *p, double *pdps, double *peps)
Calculates the dps and eps of a pilot.
Definition pilot.c:4377
int pilot_cargoFree(const Pilot *p)
Gets the pilot's free cargo space.
Definition pilot_cargo.c:53
int pilot_cargoUsedMission(const Pilot *p)
Gets how much mission cargo ship has on board.
int pilot_cargoUsed(const Pilot *p)
Gets how much cargo ship has on board.
double pilot_ewScanTime(const Pilot *p)
Gets the time it takes to scan a pilot.
Definition pilot_ew.c:40
int pilot_getMount(const Pilot *p, const PilotOutfitSlot *w, vec2 *v)
Gets the mount position of a pilot.
int pilot_rmOutfit(Pilot *pilot, PilotOutfitSlot *s)
Removes an outfit from the pilot.
void pilot_healLanded(Pilot *pilot)
Cures the pilot as if he was landed.
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.
void pilot_fillAmmo(Pilot *pilot)
Fills pilot's ammo completely.
int pilot_rmOutfitRaw(Pilot *pilot, PilotOutfitSlot *s)
Removes an outfit from the pilot without doing any checks.
int pilot_reportSpaceworthy(const Pilot *p, char *buf, int bufSize)
Pilot safety report - makes sure stats are safe.
int pilot_rmAmmo(Pilot *pilot, PilotOutfitSlot *s, int quantity)
Removes some ammo from the pilot stock.
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.
int pilot_addOutfitRaw(Pilot *pilot, const Outfit *outfit, PilotOutfitSlot *s)
Adds an outfit to the pilot, ignoring CPU or other limits.
void pilot_outfitLInitAll(Pilot *pilot)
Runs the pilot's Lua outfits init script.
void pilot_weapSetAdd(Pilot *p, int id, const PilotOutfitSlot *o)
Adds an outfit to a weapon set.
void pilot_weaponSafe(Pilot *p)
Sets the weapon set as safe.
void pilot_weapSetRm(Pilot *p, int id, const PilotOutfitSlot *o)
Removes a slot from a weapon set.
void ws_copy(PilotWeaponSet dest[PILOT_WEAPON_SETS], const PilotWeaponSet src[PILOT_WEAPON_SETS])
Copies a weapon set over.
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.
int pilot_weapSetCheck(Pilot *p, int id, const PilotOutfitSlot *o)
Checks to see if a slot is in a weapon set and usable.
void pilot_weaponAuto(Pilot *p)
Tries to automatically set and create the pilot's weapon set.
int player_nships(void)
Gets the amount of ships player has in storage.
Definition player.c:2814
int player_rmOutfit(const Outfit *o, int quantity)
Remove an outfit from the player's outfit stack.
Definition player.c:3053
int player_getOutfitsFiltered(const Outfit ***outfits, int(*filter)(const Outfit *o), const char *name)
Prepares two arrays for displaying in an image array.
Definition player.c:2953
void player_swapShip(const char *shipname, int move_cargo)
Swaps player's current ship with their ship named shipname.
Definition player.c:590
int player_addOutfit(const Outfit *o, int quantity)
Adds an outfit to the player outfit stack.
Definition player.c:2988
credits_t player_shipPrice(const char *shipname, int count_unique)
Calculates the price of one of the player's ships.
Definition player.c:733
const PlayerShip_t * player_getShipStack(void)
Gets the array (array.h) of the player's ships.
Definition player.c:2804
void player_rmShip(const char *shipname)
Removes one of the player's ships.
Definition player.c:775
PlayerShip_t * player_getPlayerShip(const char *shipname)
Gets a specific ship.
Definition player.c:2863
credits_t player_modCredits(credits_t amount)
Modifies the amount of credits the player has.
Definition player.c:1047
int player_hasShip(const char *shipname)
Sees if player has a ship of a name.
Definition player.c:2825
Player_t player
Definition player.c:77
void player_shipsSort(void)
Sorts the players ships.
Definition player.c:2769
int player_outfitOwned(const Outfit *o)
Gets how many of the outfit the player owns.
Definition player.c:2882
Pilot * player_getShip(const char *shipname)
Gets a specific ship.
Definition player.c:2844
int pfleet_toggleDeploy(PlayerShip_t *ps, int deploy)
Toggles a player ship as deployed.
int pfleet_cargoFree(void)
Gets the total amount of free cargo space in the player's fleet.
static const double c[]
Definition rng.c:256
const char * ship_classDisplay(const Ship *s)
Gets the ship's display class in human readable form.
Definition ship.c:203
double ship_maxSize(void)
Gets the maximum size of a ship.
Definition ship.c:1826
glTexture * ship_gfxStore(const Ship *s, int size, double dir, double updown, double glow)
Get the store gfx.
Definition ship.c:383
int ss_statsListDesc(const ShipStatList *ll, char *buf, int len, int newline)
Writes the ship statistics description.
Definition shipstats.c:820
int ss_statsDesc(const ShipStats *s, char *buf, int len, int newline)
Writes the ship statistics description.
Definition shipstats.c:864
const char * sp_description(unsigned int spid)
Gets the description of a slot property (in English).
Definition slots.c:175
const glTexture * sp_icon(unsigned int spid)
Gets the icon associated with the slot.
Definition slots.c:225
const char * sp_display(unsigned int spid)
Gets the display name of a slot property (in English).
Definition slots.c:165
ntime_t start_date(void)
Gets the starting date.
Definition start.c:236
Outfit * outfit
Definition equipment.h:15
double altx
Definition equipment.h:18
PlayerShip_t * selected
Definition equipment.h:14
double alty
Definition equipment.h:19
The actual hook parameter.
Definition hook.h:40
const char * str
Definition hook.h:44
union HookParam::@114305201244257020071001257133270030317263020235 u
HookParamType type
Definition hook.h:41
const Ship * ship
Definition hook.h:47
OutfitSlotSize size
Definition outfit.h:143
unsigned int spid
Definition outfit.h:140
OutfitSlotType type
Definition outfit.h:142
A ship outfit, depends radically on the type.
Definition outfit.h:372
char * limit
Definition outfit.h:386
OutfitSlot slot
Definition outfit.h:380
Stores an outfit the pilot has.
Definition pilot.h:145
PilotOutfitAmmo ammo
Definition pilot.h:174
ShipStatList * lua_stats
Definition pilot.h:180
ShipOutfitSlot * sslot
Definition pilot.h:151
const Outfit * outfit
Definition pilot.h:149
The representation of an in-game pilot.
Definition pilot.h:263
ShipStats stats
Definition pilot.h:348
double accel
Definition pilot.h:291
unsigned int id
Definition pilot.h:264
PilotWeaponSet weapon_sets[PILOT_WEAPON_SETS]
Definition pilot.h:376
double crew
Definition pilot.h:286
double ew_stealth
Definition pilot.h:323
double energy_regen
Definition pilot.h:316
PilotOutfitSlot ** outfits
Definition pilot.h:354
PilotOutfitSlot * outfit_intrinsic
Definition pilot.h:360
double armour_max
Definition pilot.h:304
double speed
Definition pilot.h:293
const Ship * ship
Definition pilot.h:274
double fuel_max
Definition pilot.h:309
double ew_detection
Definition pilot.h:320
int autoweap
Definition pilot.h:377
double energy_max
Definition pilot.h:315
Solid solid
Definition pilot.h:275
char * name
Definition pilot.h:265
double fuel_consumption
Definition pilot.h:311
double shield_regen
Definition pilot.h:307
Effect * effects
Definition pilot.h:351
double turn
Definition pilot.h:297
double shield_max
Definition pilot.h:305
double dmg_absorb
Definition pilot.h:308
double ew_signature
Definition pilot.h:321
double armour_regen
Definition pilot.h:306
Player ship.
Definition player.h:72
double time_played
Definition player.h:83
double dmg_done_armour
Definition player.h:87
double dmg_taken_armour
Definition player.h:89
ntime_t acquired_date
Definition player.h:85
int deployed
Definition player.h:80
double dmg_taken_shield
Definition player.h:88
unsigned int landed_times
Definition player.h:93
Pilot * p
Definition player.h:73
unsigned int ships_destroyed[SHIP_CLASS_TOTAL]
Definition player.h:90
char * acquired
Definition player.h:84
unsigned int jumped_times
Definition player.h:92
double dmg_done_shield
Definition player.h:86
int favourite
Definition player.h:77
int exclusive
Definition ship.h:74
int visible
Definition ship.h:77
OutfitSlot slot
Definition ship.h:72
int locked
Definition ship.h:76
double time_mod
Definition shipstats.h:356
Ship trail emitter.
Definition ship.h:88
const TrailSpec * trail_spec
Definition ship.h:91
Represents a space ship.
Definition ship.h:97
double shield_regen
Definition ship.h:142
double dt_default
Definition ship.h:136
double cap_cargo
Definition ship.h:135
char * desc_extra
Definition ship.h:122
char * name
Definition ship.h:100
double energy_regen
Definition ship.h:144
double armour
Definition ship.h:139
double armour_regen
Definition ship.h:140
int crew
Definition ship.h:130
double dmg_absorb
Definition ship.h:145
int points
Definition ship.h:110
double speed
Definition ship.h:127
double turn
Definition ship.h:126
double accel
Definition ship.h:125
double energy
Definition ship.h:143
double shield
Definition ship.h:141
double mass
Definition physics.h:45
int nebula
Definition spfx.h:60
Abstraction for rendering sprite sheets.
Definition opengl_tex.h:43
uint8_t flags
Definition opengl_tex.h:64
Definition mat4.h:12
Represents a 2d vector.
Definition vec2.h:45
Definition vec3.h:6
void window_dimWidget(unsigned int wid, const char *name, int *w, int *h)
Gets the dimensions of a widget.
Definition toolkit.c:415
void window_setFocus(unsigned int wid, const char *wgtname)
Sets the focused widget in a window.
Definition toolkit.c:2488
void window_dimWindow(unsigned int wid, int *w, int *h)
Gets the dimensions of a window.
Definition toolkit.c:370
int window_existsID(unsigned int wid)
Checks to see if a window with a certain ID exists.
Definition toolkit.c:608
void toolkit_drawAltText(int bx, int by, const char *alt)
Draws an alt text.
Definition toolkit.c:1460
void window_onClose(unsigned int wid, void(*fptr)(unsigned int, const char *))
Sets the default close function of the window.
Definition toolkit.c:824
void window_canFocusWidget(unsigned int wid, const char *name, int canfocus)
Allows or disallows focusing a widget.
Definition toolkit.c:517
void toolkit_drawRect(int x, int y, int w, int h, const glColour *c, const glColour *lc)
Draws a rectangle.
Definition toolkit.c:1370
void window_moveWidget(unsigned int wid, const char *name, int x, int y)
Moves a widget.
Definition toolkit.c:463
void window_posWidget(unsigned int wid, const char *name, int *x, int *y)
Gets a widget's position.
Definition toolkit.c:441
void window_destroyWidget(unsigned int wid, const char *wgtname)
Destroys a widget in a window.
Definition toolkit.c:1167
int widget_isCovered(unsigned int wid, const char *name, int x, int y)
Checks to see if a widget is covered or not.
Definition toolkit.c:561
void toolkit_drawOutlineThick(int x, int y, int w, int h, int b, int thick, const glColour *c, const glColour *lc)
Draws an outline.
Definition toolkit.c:1239
int window_isTop(unsigned int wid)
Checks to see if a window is at the top.
Definition toolkit.c:544
int widget_exists(unsigned int wid, const char *wgtname)
Checks to see if a widget exists.
Definition toolkit.c:1144
char * window_getFocus(unsigned int wid)
Gets the focused widget in a window (does strdup!!).
Definition toolkit.c:2514