naev 0.11.5
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"
17#include "equipment.h"
18
19#include "array.h"
20#include "conf.h"
21#include "debug.h"
22#include "dialogue.h"
23#include "escort.h"
24#include "gui.h"
25#include "hook.h"
26#include "info.h"
27#include "land.h"
28#include "land_outfits.h"
29#include "log.h"
30#include "map.h"
31#include "mission.h"
32#include "ndata.h"
33#include "nstring.h"
34#include "nlua.h"
35#include "nlua_tk.h"
36#include "ntime.h"
37#include "player.h"
38#include "player_fleet.h"
39#include "pilot_outfit.h"
40#include "shipstats.h"
41#include "slots.h"
42#include "tk/toolkit_priv.h" /* Yes, I'm a bad person, abstractions be damned! */
43#include "toolkit.h"
44
45/*
46 * Image array names.
47 */
48#define EQUIPMENT_SHIPS "iarAvailShips"
49#define EQUIPMENT_OUTFIT_TAB "tabOutfits"
50#define EQUIPMENT_OUTFITS "iarAvailOutfits"
51#define EQUIPMENT_FILTER "inpFilterOutfits"
52#define OUTFIT_TABS 5
53
54/* global/main window */
55#define BUTTON_WIDTH 200
56#define BUTTON_HEIGHT 40
58/*
59 * equipment stuff
60 */
62static double equipment_dir = 0.;
63static unsigned int equipment_lastick = 0;
64static unsigned int equipment_wid = 0;
65static int equipment_creating = 0;
66static int ship_mode = 0;
67static iar_data_t iar_data[OUTFIT_TABS];
68static Outfit **iar_outfits[OUTFIT_TABS];
69static nlua_env autoequip_env = LUA_NOREF; /* Autoequip env. */
70static int equipment_outfitMode = 0;
72/*
73 * prototypes
74 */
75/* Creation. */
76static void equipment_getDim( unsigned int wid, int *w, int *h,
77 int *sw, int *sh, int *ow, int *oh,
78 int *ew, int *eh,
79 int *cw, int *ch, int *bw, int *bh );
80static void equipment_genShipList( unsigned int wid );
81static void equipment_genOutfitList( unsigned int wid );
82/* Widget. */
83static void equipment_genLists( unsigned int wid );
84static void equipment_toggleFav( unsigned int wid, const char *wgt );
85static void equipment_toggleDeploy( unsigned int wid, const char *wgt );
86static void equipment_renderColumn( double x, double y, double w, double h,
87 const PilotOutfitSlot *lst, const char *txt,
88 int selected, Outfit *o, Pilot *p, const CstSlotWidget *wgt );
89static void equipment_renderSlots( double bx, double by, double bw, double bh, void *data );
90static void equipment_renderMisc( double bx, double by, double bw, double bh, void *data );
91static void equipment_renderOverlayColumn( double x, double y, double h,
92 PilotOutfitSlot *lst, int mover, CstSlotWidget *wgt );
93static void equipment_renderOverlaySlots( double bx, double by, double bw, double bh,
94 void *data );
95static void equipment_renderShip( double bx, double by, double bw, double bh, void *data );
96static int equipment_mouseInColumn( const PilotOutfitSlot *lst, double y, double h, double my );
97static int equipment_mouseSlots( unsigned int wid, const SDL_Event* event,
98 double x, double y, double w, double h, double rx, double ry, void *data );
99/* Misc. */
100static char eq_qCol( double cur, double base, int inv );
101static int equipment_swapSlot( unsigned int wid, Pilot *p, PilotOutfitSlot *slot );
102static void equipment_sellShip( unsigned int wid, const char* str );
103static void equipment_renameShip( unsigned int wid, const char *str );
104static void equipment_shipMode( unsigned int wid, const char *str );
105static void equipment_rightClickShips( unsigned int wid, const char* str );
106static void equipment_transChangeShip( unsigned int wid, const char* str );
107static void equipment_changeShip( unsigned int wid );
108static void equipment_unequipShip( unsigned int wid, const char* str );
109static void equipment_autoequipShip( unsigned int wid, const char* str );
110static void equipment_filterOutfits( unsigned int wid, const char *str );
111static void equipment_rightClickOutfits( unsigned int wid, const char* str );
112static void equipment_outfitPopdown( unsigned int wid, const char* str );
113static void equipment_changeTab( unsigned int wid, const char *wgt, int old, int tab );
114static int equipment_playerAddOutfit( const Outfit *o, int quantity );
115static int equipment_playerRmOutfit( const Outfit *o, int quantity );
116
122void equipment_rightClickOutfits( unsigned int wid, const char* str )
123{
124 (void) str;
125 Outfit *o;
126 int id, active, minimal, n, nfits;
127 PilotOutfitSlot* slots;
128 Pilot *p;
129 OutfitSlotSize size;
130
131 active = window_tabWinGetActive( wid, EQUIPMENT_OUTFIT_TAB );
132 id = toolkit_getImageArrayPos( wid, EQUIPMENT_OUTFITS );
133
134 /* Did the user click on background or placeholder cell? */
135 if (id < 0 || array_size(iar_outfits[active])==0)
136 return;
137
138 o = iar_outfits[active][id];
139 if (o==NULL)
140 return;
141
142 /* Figure out which slot this stuff fits into */
143 switch (o->slot.type) {
144 case OUTFIT_SLOT_STRUCTURE:
146 break;
147 case OUTFIT_SLOT_UTILITY:
149 break;
150 case OUTFIT_SLOT_WEAPON:
151 slots = eq_wgt.selected->p->outfit_weapon;
152 break;
153 default:
154 return;
155 }
156 n = array_size(slots);
157
158 /* See how many slots it fits into. */
159 nfits = 0;
160 minimal = n;
161 for (int i=0; i<n; i++) {
162 /* Must fit the slot. */
163 if (!outfit_fitsSlot( o, &slots[i].sslot->slot))
164 continue;
165
166 /* Must have valid slot size. */
167 if (o->slot.size == OUTFIT_SLOT_SIZE_NA)
168 continue;
169
170 minimal = i;
171 nfits++;
172 }
173 /* Only fits in a single slot, so we might as well just swap it. */
174 if (nfits==1) {
175 eq_wgt.outfit = o;
176 p = eq_wgt.selected->p;
177 /* We have to call once to remove, once to add. */
178 if (slots[minimal].outfit != NULL)
179 equipment_swapSlot( equipment_wid, p, &slots[minimal] );
180 eq_wgt.outfit = o;
181 equipment_swapSlot( equipment_wid, p, &slots[minimal] );
182
183 hooks_run( "equip" ); /* Equipped. */
184 return;
185 }
186
187 /* See if limit is applied and swap with shared limit slot. */
188 if (o->limit != NULL) {
189 minimal = n;
190 for (int i=0; i<n; i++) {
191 /* Must fit the slot. */
192 if (!outfit_fitsSlot( o, &slots[i].sslot->slot))
193 continue;
194
195 /* Must have valid slot size. */
196 if (o->slot.size == OUTFIT_SLOT_SIZE_NA)
197 continue;
198
199 /* Must have outfit with limit. */
200 if ((slots[i].outfit == NULL) || (slots[i].outfit->limit == NULL))
201 continue;
202
203 /* Must share a limit to be able to swap. */
204 if (strcmp(slots[i].outfit->limit,o->limit)!=0)
205 continue;
206
207 minimal = i;
208 }
209 if (minimal < n) {
210 eq_wgt.outfit = o;
211 p = eq_wgt.selected->p;
212 /* Once to unequip and once to equip. */
213 equipment_swapSlot( equipment_wid, p, &slots[minimal] );
214 eq_wgt.outfit = o;
215 equipment_swapSlot( equipment_wid, p, &slots[minimal] );
216 hooks_run( "equip" ); /* Equipped. */
217 return;
218 }
219 }
220
221 /* Loop through outfit slots of the right type, try to find an empty one */
222 size = OUTFIT_SLOT_SIZE_NA;
223 minimal = n;
224 for (int i=0; i<n; i++) {
225 /* Slot full. */
226 if (slots[i].outfit != NULL)
227 continue;
228
229 /* Must fit the slot. */
230 if (!outfit_fitsSlot( o, &slots[i].sslot->slot))
231 continue;
232
233 /* Must have valid slot size. */
234 if (o->slot.size == OUTFIT_SLOT_SIZE_NA)
235 continue;
236
237 /* Search for the smallest slot avaliable. */
238 if ((size == OUTFIT_SLOT_SIZE_NA) || (slots[i].sslot->slot.size < size)){
239 size = slots[i].sslot->slot.size;
240 minimal = i;
241 }
242 }
243
244 /* Use the chosen one (if any). */
245 if (minimal < n) {
246 eq_wgt.outfit = o;
247 p = eq_wgt.selected->p;
248 equipment_swapSlot( equipment_wid, p, &slots[minimal] );
249 hooks_run( "equip" ); /* Equipped. */
250 }
251}
252
256static void equipment_getDim( unsigned int wid, int *w, int *h,
257 int *sw, int *sh, int *ow, int *oh,
258 int *ew, int *eh,
259 int *cw, int *ch, int *bw, int *bh )
260{
261 int ssw, ssh;
262 /* Get window dimensions. */
263 window_dimWindow( wid, w, h );
264
265 /* Calculate image array dimensions. */
266 ssw = 550 + (*w - LAND_WIDTH);
267 ssh = (*h - 100);
268 if (sw != NULL)
269 *sw = ssw;
270 if (sh != NULL)
271 *sh = (1*ssh)/3;
272 if (ow != NULL)
273 *ow = ssw;
274 if (oh != NULL)
275 *oh = (2*ssh)/3;
276
277 /* Calculate slot widget. */
278 if (ew != NULL)
279 *ew = 180;
280 if (eh != NULL)
281 *eh = *h - 100;
282
283 /* Calculate custom widget. */
284 if (cw != NULL)
285 *cw = 120;
286 if (ch != NULL)
287 *ch = 140 + ((player.fleet_capacity > 0) ? 40 : 0);
288
289 /* Calculate button dimensions. */
290 if (bw != NULL)
291 *bw = (*w - 20 - (sw!=NULL?*sw:0) - 40 - 20 - 60) / 5;
292 if (bh != NULL)
293 *bh = BUTTON_HEIGHT;
294}
295
299void equipment_open( unsigned int wid )
300{
301 int w,h, sw,sh, ow,oh, bw,bh, ew,eh, cw,ch, x,y;
302 unsigned int *widptr;
303
304 /* Load the outfit mode. */
306
307 /* Mark as generated. */
308 land_tabGenerate(LAND_WINDOW_EQUIPMENT);
309
310 /* Set global WID. */
311 equipment_wid = wid;
313
314 /* Get dimensions. */
315 equipment_getDim( wid, &w, &h, &sw, &sh, &ow, &oh,
316 &ew, &eh, &cw, &ch, &bw, &bh );
317
318 /* Buttons */
319 x = -20+4;
320 y = 20;
321 window_addButtonKey( wid, x, y,
322 bw, bh, "btnCloseEquipment",
323 _("Take Off"), land_buttonTakeoff, SDLK_t );
324 x -= (15+bw);
325 window_addButtonKey( wid, x, y,
326 bw, bh, "btnSellShip",
327 _("Sell Ship"), equipment_sellShip, SDLK_s );
328 x -= (15+bw);
329 window_addButtonKey( wid, x, y,
330 bw, bh, "btnChangeShip",
331 _("Swap Ship"), equipment_transChangeShip, SDLK_p );
332 x -= (15+bw);
333 window_addButtonKey( wid, x, y,
334 bw, bh, "btnUnequipShip",
335 _("Unequip"), equipment_unequipShip, SDLK_u );
336 x -= (15+bw);
337 window_addButtonKey( wid, x, y,
338 bw, bh, "btnAutoequipShip",
339 _("Autoequip"), equipment_autoequipShip, SDLK_a );
340
341 /* Prepare the outfit array. */
342 for (int i=0; i<OUTFIT_TABS; i++) {
343 toolkit_initImageArrayData( &iar_data[i] );
345 }
346 memset( iar_outfits, 0, sizeof(Outfit**) * OUTFIT_TABS );
347
348 /* Safe defaults. */
349 equipment_lastick = SDL_GetTicks();
350 equipment_dir = 0.;
351
352 /* Add ammo. */
354
355 /* text */
356 x = 20+sw+20+180+10;
357 y = -40;
358 window_addText( wid, x, y,
359 130, y-20+h-bh, 0, "txtSDesc", &gl_defFont, &cFontGrey, NULL );
360 window_addText( wid, x, y,
361 w-x-128-30, y-20+h-bh, 0, "txtAcquired", &gl_defFont, NULL, NULL );
362 x += 130;
363 window_addText( wid, x, y,
364 w-x-20-128-20, y-20+h-bh, 0, "txtDDesc", &gl_defFont, NULL, NULL );
365
366 /* Generate lists. */
367 window_addText( wid, 30, -20,
368 ow, gl_defFont.h, 0, "txtShipTitle", &gl_defFont, NULL, _("Available Ships") );
369 window_addText( wid, 30, -40-sh-20,
370 ow, gl_defFont.h, 0, "txtOutfitTitle", &gl_defFont, NULL, _("Available Outfits") );
371
372 /* Favourite checkbox, run before genLists. */
373 x = -20-(128-cw)/2;
374 y = -20-150-ch;
375 window_addCheckbox( wid, x, y, cw, 30, "chkFav", _("Favourite"), equipment_toggleFav, 0 );
376 if (player.fleet_capacity > 0) {
377 y -= 23;
378 window_addCheckbox( wid, x, y, cw, 30, "chkDeploy", _("Deployed"), equipment_toggleDeploy, 0 );
379 }
380 x = -16;
381 y -= 23 + 10;
382 window_addButtonKey( wid, x, y, 128+8, bh, "btnRenameShip",
383 _("Rename"), equipment_renameShip, SDLK_r );
384 y -= bh + 10;
385 window_addButtonKey( wid, x, y, 128+8, bh, "btnShipMode",
386 _("Toggle Display"), equipment_shipMode, SDLK_d );
387
388 /* Generate lists. */
389 equipment_genLists( wid );
390
391 /* Slot widget. Designed so that 10 slots barely fit. */
392 equipment_slotWidget( wid, 20+sw+15, -40-5, ew, eh, &eq_wgt );
394 eq_wgt.canmodify = 1;
395
396 /* Separator. */
397 // window_addRect( wid, 20 + sw + 20, -40, 2, h-60, "rctDivider", &cGrey50, 0 );
398
399 /* Custom widget (ship information). */
400 window_addCust( wid, -20-(128-cw)/2, -20-150, cw, ch, "cstMisc", 0,
401 equipment_renderMisc, NULL, NULL, NULL, NULL );
402 window_canFocusWidget( wid, "cstMisc", 0 );
403
404 /* Spinning ship. */
405 widptr = malloc(sizeof(unsigned int));
406 *widptr = wid;
407 window_addRect( wid, -20+4, -40+4, 128+8, 128+8, "rctShip", &cBlack, 1 );
408 window_addCust( wid, -20, -40, 128, 128, "cstShip", 0,
409 equipment_renderShip, NULL, NULL, NULL, widptr );
410 window_custSetDynamic( wid, "cstShip", 1 );
411 window_canFocusWidget( wid, "cstShip", 0 );
412 window_custAutoFreeData( wid, "cstShip" );
413
414 /* Focus the ships image array. */
415 window_setFocus( wid , EQUIPMENT_SHIPS );
417}
418
429void equipment_slotWidget( unsigned int wid,
430 double x, double y, double w, double h,
431 CstSlotWidget *data )
432{
433 /* Initialize data. */
435
436 /* Create the widget. */
437 window_addCust( wid, x, y, w, h, "cstEquipment", 0,
438 equipment_renderSlots, equipment_mouseSlots, NULL, NULL, data );
439 window_custSetClipping( wid, "cstEquipment", 0 );
440 window_custSetOverlay( wid, "cstEquipment", equipment_renderOverlaySlots );
441 window_canFocusWidget( wid, "cstEquipment", 0 );
442}
446static void equipment_renderColumn( double x, double y, double w, double h,
447 const PilotOutfitSlot *lst, const char *txt,
448 int selected, Outfit *o, Pilot *p, const CstSlotWidget *wgt )
449{
450 const glColour *c, *dc, *rc;
451 glColour bc;
452
453 /* Shouldn't be happening, but let's be nice and not crash. */
454 if (!array_size(lst))
455 return;
456
457 /* Render text. */
458 if ((o != NULL) && (lst[0].sslot->slot.type == o->slot.type))
459 c = &cFontGreen;
460 else
461 c = &cFontWhite;
463 x-15., y+h+10., c, -1., txt );
464
465 /* Iterate for all the slots. */
466 for (int i=0; i<array_size(lst); i++) {
467 int cantoggle;
468 const glTexture *icon;
469
470 /* Skip slots hat are empty and the player can't do anything about. */
471 if (lst[i].sslot->locked && (lst[i].outfit == NULL))
472 continue;
473
474 cantoggle = pilot_slotIsToggleable( &lst[i] );
475
476 /* Choose default colour. */
477 if (wgt->weapons >= 0) {
478 int level = pilot_weapSetInSet( p, wgt->weapons, &lst[i] );
479 if (level == 0)
480 dc = &cFontRed;
481 else if (level == 1)
482 dc = &cFontYellow;
483 else if (cantoggle)
484 dc = &cFontBlue;
485 else
486 dc = &cFontGrey;
487 }
488 else
489 dc = outfit_slotSizeColour( &lst[i].sslot->slot );
490
491 if (dc == NULL)
492 dc = &cGrey60;
493
494 /* Draw background. */
495 if ((wgt->weapons >= 0) && (!cantoggle || pilot_weapSetCheck( p, wgt->weapons, &lst[i] )))
496 bc = cFontGrey;
497 else
498 bc = *dc;
499 bc.a = 0.4;
500 toolkit_drawRect( x, y, w, h, &bc, NULL );
501
502 if (lst[i].outfit != NULL) {
503 /* Draw bugger. */
504 gl_renderScale( lst[i].outfit->gfx_store,
505 x, y, w, h, NULL );
506 }
507 else if ((o != NULL) &&
508 (lst[i].sslot->slot.type == o->slot.type)) {
509 /* Render a thick frame with a yes/no colour, and geometric cue. */
510 int ok = (!lst[i].sslot->locked && pilot_canEquip( p, &lst[i], o ) == NULL);
511 glUseProgram( shaders.status.program );
512 glUniform1f( shaders.status.paramf, ok );
513 gl_renderShader( x, y, w, h, 0., &shaders.status, NULL, 0 );
514 }
515
516 /* Must rechoose colour based on slot properties. */
517 rc = dc;
518 if (wgt->canmodify) {
519 if (lst[i].sslot->locked)
520 rc = NULL;
521 else if (lst[i].sslot->required)
522 rc = &cBrightRed;
523 else if (lst[i].sslot->exclusive)
524 rc = &cWhite;
525 else if (lst[i].sslot->slot.spid != 0)
526 rc = &cBlack;
527 }
528
529 /* Draw outline. */
530 if (i==selected)
531 toolkit_drawOutlineThick( x, y, w, h, 5, 7, &cGreen, NULL );
532 if (rc != NULL)
533 toolkit_drawOutlineThick( x, y, w, h, 1, 3, rc, NULL );
534
535 /* Draw slot iccon if applicable. */
536 icon = sp_icon( lst[i].sslot->slot.spid );
537 if (icon != NULL) {
538 double sw = 14.;
539 double sh = 14.;
540 double sx = x+w-10.;
541 double sy = y+h-10.;
542
543 if (icon->flags & OPENGL_TEX_SDF)
544 gl_renderSDF( icon, sx, sy, sw, sh, &cWhite, 0., 1. );
545 else
546 gl_renderScaleAspect( icon, sx, sy, sw, sh, NULL );
547 }
548
549 /* Go to next one. */
550 y -= h+20;
551 }
552}
553
565static void equipment_calculateSlots( const Pilot *p, double bw, double bh,
566 double *w, double *h, int *n, int *m )
567{
568 double tw, th, s;
569 int tm;
570
571 /* Calculate size. */
572 tm = MAX( MAX( array_size(p->outfit_weapon), array_size(p->outfit_utility) ), array_size(p->outfit_structure) );
573 th = bh / (double)tm;
574 tw = bw / 3.;
575 s = MIN( th, tw ) - 20.;
576 th = s;
577 tw = s;
578
579 /* Return. */
580 *w = tw;
581 *h = th;
582 *n = 3;
583 *m = tm;
584}
594static void equipment_renderSlots( double bx, double by, double bw, double bh, void *data )
595{
596 double x, y;
597 double w, h;
598 double tw;
599 int n, m;
600 CstSlotWidget *wgt;
601 Pilot *p;
602 int selected;
603
604 /* Must have selected ship. */
605 wgt = (CstSlotWidget*) data;
606 if (wgt->selected == NULL)
607 return;
608
609 /* Get data. */
610 p = wgt->selected->p;
611 selected = wgt->slot;
612
613 /* Get dimensions. */
614 equipment_calculateSlots( p, bw, bh, &w, &h, &n, &m );
615 tw = bw / (double)n;
616
617 /* Draw structure outfits. */
618 x = bx + (tw-w)/2 + 2*tw;
619 y = by + bh - (h+20) + (h+20-h)/2;
620 equipment_renderColumn( x, y, w, h,
621 p->outfit_structure, _("Structure"),
622 selected, wgt->outfit, p, wgt );
623
624 /* Draw systems outfits. */
625 selected -= array_size(p->outfit_structure);
626 x -= tw;
627 y = by + bh - (h+20) + (h+20-h)/2;
628 equipment_renderColumn( x, y, w, h,
629 p->outfit_utility, _("Utility"),
630 selected, wgt->outfit, p, wgt );
631
632 /* Draw weapon outfits. */
633 selected -= array_size(p->outfit_utility);
634 x -= tw;
635 y = by + bh - (h+20) + (h+20-h)/2;
636 equipment_renderColumn( x, y, w, h,
637 p->outfit_weapon, _("Weapon"),
638 selected, wgt->outfit, p, wgt );
639}
649static void equipment_renderMisc( double bx, double by, double bw, double bh, void *data )
650{
651 (void) data;
652 Pilot *p;
653 double percent;
654 double x, y;
655 double w, h;
656
657 /* Must have selected ship. */
658 if (eq_wgt.selected == NULL)
659 return;
660
661 p = eq_wgt.selected->p;
662
663 /* Base bar properties. */
664 w = bw;
665 h = 20;
666 x = bx;
667 y = by + bh - 30 - h;
668
669 /* Fleet capacity. */
670 if (player.fleet_capacity > 0) {
671 gl_printMidRaw( &gl_smallFont, w, x, y + h + 7, &cFontWhite, -1., _("Fleet Capacity") );
672
673 percent = CLAMP(0., 1., 1.-(double)player.fleet_used / (double)player.fleet_capacity );
674 toolkit_drawRect( x, y - 2, w * percent, h + 4, &cBlue, NULL );
675 toolkit_drawRect( x + w * percent, y - 2, w * (1.-percent), h + 4, &cBlack, NULL );
677 x, y + h / 2. - gl_smallFont.h / 2.,
678 &cFontWhite, "%d / %d", player.fleet_capacity-player.fleet_used, player.fleet_capacity );
679
680 y -= gl_smallFont.h + 2*h;
681 }
682
683 /* Render CPU. */
684 gl_printMidRaw( &gl_smallFont, w, x, y + h + 7, &cFontWhite, -1, _("CPU Free") );
685
686 percent = (p->cpu_max > 0) ? CLAMP(0., 1., (double)p->cpu / (double)p->cpu_max) : 0.;
687 toolkit_drawRect( x, y - 2, w * percent, h + 4, &cGreen, NULL );
688 toolkit_drawRect( x + w * percent, y - 2, w * (1.-percent), h + 4, &cRed, NULL );
689 gl_printMid( &gl_smallFont, w, x, y + h / 2. - gl_smallFont.h / 2., &cFontWhite, "%d / %d", p->cpu, p->cpu_max );
690
691 y -= h;
692
693 /* Render mass limit. */
694 gl_printMidRaw( &gl_smallFont, w, x, y-3, &cFontWhite, -1., _("Mass Limit Left") );
695 y -= gl_smallFont.h + h;
696
697 percent = (p->stats.engine_limit > 0) ? CLAMP(0., 1.,
698 (p->stats.engine_limit - p->solid.mass) / p->stats.engine_limit) : 0.;
699 toolkit_drawRect( x, y - 2, w * percent, h + 4, &cGreen, NULL );
700 toolkit_drawRect( x + w * percent, y - 2, w * (1.-percent), h + 4, &cOrange, NULL );
702 x, y + h / 2. - gl_smallFont.h / 2.,
703 &cFontWhite, "%.0f / %.0f", p->stats.engine_limit - p->solid.mass, p->stats.engine_limit );
704
705 y -= h;
706 if (p->stats.engine_limit > 0. && p->solid.mass > p->stats.engine_limit) {
708 x, y, &cFontRed, _("!! %.0f%% Slower !!"),
709 (1. - p->speed / p->speed_base) * 100);
710 }
711}
712
723static void equipment_renderOverlayColumn( double x, double y, double h,
724 PilotOutfitSlot *lst, int mover, CstSlotWidget *wgt )
725{
726 const glColour *c;
727 glColour tc;
728 int text_width, yoff;
729 const char *display;
730
731 /* Iterate for all the slots. */
732 for (int i=0; i<array_size(lst); i++) {
733 int subtitle = 0;
734
735 /* Skip slots hat are empty and the player can't do anything about. */
736 if (lst[i].sslot->locked && (lst[i].outfit == NULL))
737 continue;
738
739 if (lst[i].outfit != NULL) {
740 /* See if needs a subtitle. */
741 if ((outfit_isLauncher(lst[i].outfit) ||
742 (outfit_isFighterBay(lst[i].outfit))) &&
743 (lst[i].u.ammo.quantity < outfit_amount(lst[i].outfit)))
744 subtitle = 1;
745 }
746 /* Draw bottom. */
747 if ((i==mover) || subtitle) {
748 int top = 0;
749 display = NULL;
750 if ((i==mover) && wgt->canmodify) {
751 if (lst[i].sslot->locked) {
752 top = 1;
753 display = _("Locked");
754 c = &cFontRed;
755 }
756 else if (lst[i].outfit != NULL) {
757 top = 1;
758 display = pilot_canEquip( wgt->selected->p, &lst[i], NULL );
759 if (display != NULL)
760 c = &cFontRed;
761 else {
762 display = _("Right click to remove");
763 c = &cFontGreen;
764 }
765 }
766 else if ((wgt->outfit != NULL) &&
767 (lst->sslot->slot.type == wgt->outfit->slot.type)) {
768 top = 1;
769 display = pilot_canEquip( wgt->selected->p, &lst[i], wgt->outfit );
770 if (display != NULL)
771 c = &cFontRed;
772 else {
773 display = _("Right click to add");
774 c = &cFontGreen;
775 }
776 }
777 }
778 else if (lst[i].outfit != NULL) {
779 top = 1;
780 }
781
782 if (display != NULL) {
783 text_width = gl_printWidthRaw( &gl_smallFont, display );
784 if (top)
785 yoff = h + 2;
786 else
787 yoff = -gl_smallFont.h - 3;
788 tc.r = 0.;
789 tc.g = 0.;
790 tc.b = 0.;
791 tc.a = 0.9;
792 toolkit_drawRect( x, y -5. + yoff,
793 text_width+60, gl_smallFont.h+10,
794 &tc, NULL );
795 gl_printMaxRaw( &gl_smallFont, text_width,
796 x+5, y + yoff,
797 c, -1., display );
798 }
799 }
800 /* Go to next one. */
801 y -= h+20;
802 }
803}
813static void equipment_renderOverlaySlots( double bx, double by, double bw, double bh,
814 void *data )
815{
816 (void) bw;
817 Pilot *p;
818 int mover;
819 double x, y;
820 double w, h;
821 double tw;
822 int n, m;
823 PilotOutfitSlot *slot;
824 char alt[STRMAX];
825 const Outfit *o;
826 CstSlotWidget *wgt;
827
828 /* Get data. */
829 wgt = (CstSlotWidget*) data;
830 if (wgt->selected == NULL)
831 return;
832 p = wgt->selected->p;
833
834 /* Get dimensions. */
835 equipment_calculateSlots( p, bw, bh, &w, &h, &n, &m );
836 tw = bw / (double)n;
837
838 /* Get selected. */
839 mover = wgt->mouseover;
840
841 /* Render weapon outfits. */
842 x = bx + (tw-w)/2 + 2*tw;
843 y = by + bh - (h+20) + (h+20-h)/2;
845 p->outfit_structure, mover, wgt );
846 mover -= array_size(p->outfit_structure);
847 x -= tw;
848 y = by + bh - (h+20) + (h+20-h)/2;
850 p->outfit_utility, mover, wgt );
851 mover -= array_size(p->outfit_utility);
852 x -= tw;
853 y = by + bh - (h+20) + (h+20-h)/2;
855 p->outfit_weapon, mover, wgt );
856
857 /* Mouse must be over something. */
858 if (wgt->mouseover < 0)
859 return;
860
861 /* Get the slot. */
862 slot = p->outfits[wgt->mouseover];
863
864 /* For comfortability. */
865 o = slot->outfit;
866
867 /* Slot is empty. */
868 if (o == NULL) {
869 int pos;
870 if (slot->sslot->slot.spid) {
871 pos = scnprintf( alt, sizeof(alt),
872 "#o%s\n", _( sp_display( slot->sslot->slot.spid ) ) );
873 }
874 else
875 pos = 0;
876 pos += scnprintf( &alt[pos], sizeof(alt)-pos, _( "#%c%s #%c%s #0slot" ),
877 outfit_slotSizeColourFont( &slot->sslot->slot ), _(slotSize( slot->sslot->slot.size )),
878 outfit_slotTypeColourFont( &slot->sslot->slot ), _(slotName( slot->sslot->slot.type )) );
879 if (slot->sslot->exclusive && (pos < (int)sizeof(alt)))
880 pos += scnprintf( &alt[pos], sizeof(alt)-pos,
881 _(" [exclusive]") );
882 if (slot->sslot->locked && (pos < (int)sizeof(alt)))
883 pos += scnprintf( &alt[pos], sizeof(alt)-pos,
884 "#r%s#0", _(" [locked]") );
885 if (slot->sslot->slot.spid)
886 scnprintf( &alt[pos], sizeof(alt)-pos,
887 "\n\n%s", _( sp_description( slot->sslot->slot.spid ) ) );
888 toolkit_drawAltText( bx + wgt->altx, by + wgt->alty, alt );
889 return;
890 }
891
892 /* Get text. */
893 outfit_altText( alt, sizeof(alt), o, (p==player.p) ? p : NULL );
894
895 /* Display temporary bonuses. */
896 if (slot->lua_mem != LUA_NOREF) {
897 size_t slen = strlen(alt);
898 ss_statsListDesc( slot->lua_stats, &alt[slen], sizeof(alt)-slen, 1 );
899 }
900
901 /* Draw the text. */
902 toolkit_drawAltText( bx + wgt->altx, by + wgt->alty, alt );
903}
904
914static void equipment_renderShip( double bx, double by,
915 double bw, double bh, void *data )
916{
917 Pilot *p;
918 int sx, sy;
919 double px, py;
920 double pw, ph;
921 vec2 v;
922 const unsigned int *wid = data;
923
924 /* Must have selected ship. */
925 if (eq_wgt.selected == NULL)
926 return;
927 p = eq_wgt.selected->p;
928
929 /* Don't update if not selected. */
930 if (window_isTop(*wid)) {
931 unsigned int tick = SDL_GetTicks();
932 double dt = (double)(tick - equipment_lastick)/1000.;
933 equipment_lastick = tick;
934 equipment_dir += p->turn * dt;
935 if (equipment_dir > 2.*M_PI)
936 equipment_dir = fmod( equipment_dir, 2.*M_PI );
937 }
938 else
939 equipment_lastick = SDL_GetTicks();
940 gl_getSpriteFromDir( &sx, &sy, p->ship->gfx_space, equipment_dir );
941
942 /* Render ship graphic. */
943 if (p->ship->gfx_space->sw > bw) {
944 pw = 128.;
945 ph = 128.;
946 }
947 else {
948 pw = p->ship->gfx_space->sw;
949 ph = p->ship->gfx_space->sh;
950 }
951
952 px = bx + (bw-pw)/2;
953 py = by + (bh-ph)/2;
954 gl_renderRect( px, py, pw, ph, &cBlack );
955 gl_renderScaleSprite( p->ship->gfx_space, px, py, sx, sy, pw, ph, NULL );
956
957#ifdef DEBUGGING
958 if (debug_isFlag(DEBUG_MARK_EMITTER)) {
959 /* Visualize the trail emitters. */
960 double dircos, dirsin;
961 int i;
962 dircos = cos(equipment_dir);
963 dirsin = sin(equipment_dir);
964 for (i=0; i<array_size(p->ship->trail_emitters); i++) {
965 v.x = p->ship->trail_emitters[i].x_engine * dircos -
966 p->ship->trail_emitters[i].y_engine * dirsin;
967 v.y = p->ship->trail_emitters[i].x_engine * dirsin +
968 p->ship->trail_emitters[i].y_engine * dircos +
969 p->ship->trail_emitters[i].h_engine;
970 v.x *= pw / p->ship->gfx_space->sw;
971 v.y *= ph / p->ship->gfx_space->sh;
972 if (p->ship->trail_emitters[i].trail_spec->nebula)
973 gl_renderCross(px + pw/2. + v.x, py + ph/2. + v.y*M_SQRT1_2, 2., &cFontBlue);
974 else
975 gl_renderCross(px + pw/2. + v.x, py + ph/2. + v.y*M_SQRT1_2, 4., &cInert);
976 }
977 }
978#endif /* DEBUGGING */
979
980 if ((eq_wgt.slot >= 0) && p->outfits[eq_wgt.slot]->sslot->slot.type==OUTFIT_SLOT_WEAPON) {
981 p->tsx = sx;
982 p->tsy = sy;
983 pilot_getMount( p, p->outfits[eq_wgt.slot], &v );
984 px += pw/2.;
985 py += ph/2.;
986 v.x *= pw / p->ship->gfx_space->sw;
987 v.y *= ph / p->ship->gfx_space->sh;
988
989 /* Render it. */
990 glUseProgram(shaders.crosshairs.program);
991 glUniform1f(shaders.crosshairs.paramf, 2.);
992 gl_renderShader( px+v.x, py+v.y, 7., 7., 0., &shaders.crosshairs, &cRadar_player, 1 );
993 }
994}
1004static int equipment_mouseInColumn( const PilotOutfitSlot *lst, double y, double h, double my )
1005{
1006 for (int i=0; i<array_size(lst); i++) {
1007 /* Skip slots hat are empty and the player can't do anything about. */
1008 if (lst[i].sslot->locked && (lst[i].outfit == NULL))
1009 continue;
1010
1011 /* See if in position. */
1012 if ((my > y) && (my < y+h+20.))
1013 return i;
1014 y -= h+20.;
1015 }
1016
1017 return -1.;
1018}
1033static int equipment_mouseColumn( unsigned int wid, const SDL_Event* event,
1034 double mx, double my, double y, double h, PilotOutfitSlot* os,
1035 Pilot *p, int selected, CstSlotWidget *wgt )
1036{
1037 int ret = equipment_mouseInColumn( os, y, h, my );
1038 if (ret < 0)
1039 return 0;
1040
1041 if (event->type == SDL_MOUSEBUTTONDOWN) {
1042 /* Normal mouse usage. */
1043 if (wgt->weapons < 0) {
1044 if (event->button.button == SDL_BUTTON_LEFT) {
1045 if (wgt->canmodify) {
1046 int sel = selected + ret;
1047 if (wgt->slot==sel)
1048 wgt->slot = -1;
1049 else
1050 wgt->slot = sel;
1051 equipment_regenLists( wid, 1, 0 );
1052 }
1053 }
1054 else if ((event->button.button == SDL_BUTTON_RIGHT) &&
1055 wgt->canmodify && !os[ret].sslot->locked) {
1056 equipment_swapSlot( wid, p, &os[ret] );
1057 hooks_run( "equip" ); /* Equipped. */
1058 }
1059 }
1060 /* Viewing weapon slots. */
1061 else {
1062 int level;
1063 int exists = pilot_weapSetInSet( p, wgt->weapons, &os[ret] );
1064 /* Get the level of the selection. */
1065 if (event->button.button == SDL_BUTTON_LEFT)
1066 level = 0;
1067 else if (event->button.button == SDL_BUTTON_RIGHT)
1068 level = 1;
1069 else
1070 return 0; /* We ignore this type of click. */
1071 /* See if we should add it or remove it. */
1072 if (exists==level)
1073 pilot_weapSetRm( p, wgt->weapons, &os[ret] );
1074 else
1075 pilot_weapSetAdd( p, wgt->weapons, &os[ret], level );
1076 p->autoweap = 0; /* Disable autoweap. */
1077 info_update(); /* Need to update weapons. */
1078 }
1079 }
1080 else {
1081 wgt->mouseover = selected + ret;
1082 wgt->altx = mx;
1083 wgt->alty = my;
1084 }
1085
1086 return 1;
1087}
1101static int equipment_mouseSlots( unsigned int wid, const SDL_Event* event,
1102 double mx, double my, double bw, double bh,
1103 double rx, double ry, void *data )
1104{
1105 (void) bw;
1106 (void) rx;
1107 (void) ry;
1108 Pilot *p;
1109 int selected;
1110 double x, y;
1111 double w, h;
1112 double tw;
1113 CstSlotWidget *wgt;
1114 int n, m;
1115
1116 /* Get data. */
1117 wgt = (CstSlotWidget*) data;
1118 if (wgt->selected == NULL)
1119 return 0;
1120 p = wgt->selected->p;
1121
1122 /* Must be left click for now. */
1123 if ((event->type != SDL_MOUSEBUTTONDOWN) &&
1124 (event->type != SDL_MOUSEMOTION))
1125 return 0;
1126
1127 /* Is covered by something. */
1128 if (widget_isCovered( wid, "cstEquipment", mx, my )) {
1129 wgt->mouseover = -1;
1130 return 0;
1131 }
1132
1133 /* Get dimensions. */
1134 equipment_calculateSlots( p, bw, bh, &w, &h, &n, &m );
1135 tw = bw / (double)n;
1136
1137 /* Go over each column and pass mouse event. */
1138 selected = 0;
1139 x = (tw-w)/2 + 2*tw;
1140 y = bh - (h+20) + (h+20-h)/2 - 10;
1141 if ((mx > x-10) && (mx < x+w+10)) {
1142 int ret = equipment_mouseColumn( wid, event, mx, my, y, h,
1143 p->outfit_structure, p, selected, wgt );
1144 if (ret)
1145 return !!(event->type == SDL_MOUSEBUTTONDOWN);
1146 }
1147 selected += array_size(p->outfit_structure);
1148 x -= tw;
1149 if ((mx > x-10) && (mx < x+w+10)) {
1150 int ret = equipment_mouseColumn( wid, event, mx, my, y, h,
1151 p->outfit_utility, p, selected, wgt );
1152 if (ret)
1153 return !!(event->type == SDL_MOUSEBUTTONDOWN);
1154 }
1155 selected += array_size(p->outfit_utility);
1156 x -= tw;
1157 if ((mx > x-10) && (mx < x+w+10)) {
1158 int ret = equipment_mouseColumn( wid, event, mx, my, y, h,
1159 p->outfit_weapon, p, selected, wgt );
1160 if (ret)
1161 return !!(event->type == SDL_MOUSEBUTTONDOWN);
1162 }
1163
1164 /* Not over anything. */
1165 wgt->mouseover = -1;
1166 return 0;
1167}
1168
1176static int equipment_swapSlot( unsigned int wid, Pilot *p, PilotOutfitSlot *slot )
1177{
1178 /* Slot can be changed later in land_refuel() so have to save early. */
1179 int selslot = eq_wgt.slot;
1180
1181 /* Remove outfit. */
1182 if (slot->outfit != NULL) {
1183 int ret;
1184 const Outfit *o = slot->outfit;
1185
1186 /* Must be able to remove. */
1187 if (pilot_canEquip( eq_wgt.selected->p, slot, NULL ) != NULL)
1188 return 0;
1189
1190 /* Remove ammo first. */
1191 pilot_rmAmmo( eq_wgt.selected->p, slot, slot->u.ammo.quantity );
1192
1193 /* Remove outfit. */
1194 ret = pilot_rmOutfit( eq_wgt.selected->p, slot );
1195 if (ret == 0)
1197 }
1198 /* Add outfit. */
1199 else {
1200 int ret;
1201 const Outfit *o = eq_wgt.outfit;
1202 /* Must have outfit. */
1203 if (o==NULL)
1204 return 0;
1205
1206 /* Must fit slot. */
1207 if (!outfit_fitsSlot( o, &slot->sslot->slot ))
1208 return 0;
1209
1210 /* Must be able to add. */
1211 if (pilot_canEquip( eq_wgt.selected->p, slot, o ) != NULL)
1212 return 0;
1213
1214 /* Add outfit to ship. */
1215 ret = equipment_playerRmOutfit( o, 1 );
1216 if (ret == 1) {
1217 pilot_addOutfitRaw( eq_wgt.selected->p, o, slot );
1218
1219 /* Recalculate stats. */
1221 pilot_calcStats( eq_wgt.selected->p ); /* TODO avoid running twice. */
1222 }
1223
1225 }
1226
1227 /* Refuel if necessary. */
1228 land_refuel();
1229
1230 /* Recalculate stats. */
1231 pilot_calcStats( p );
1232 pilot_healLanded( p );
1233
1234 /* Redo the outfits thingy, while conserving slot. */
1235 equipment_regenLists( wid, 1, 1 );
1236 eq_wgt.slot = selslot;
1237
1238 /* Update outfits. */
1240
1241 /* Update weapon sets if needed. */
1242 if (eq_wgt.selected->p->autoweap) {
1245 }
1247
1248 /* Notify GUI of modification. */
1249 gui_setShip();
1250
1251 return 0;
1252}
1253
1261void equipment_regenLists( unsigned int wid, int outfits, int ships )
1262{
1263 int nship = 0, noutfit = 0;
1264 double offship = 0, offoutfit = 0;
1265 char *selship = NULL;
1266 const char *s = NULL;
1267 char *focused;
1268
1269 /* Ignore when creating, should be generated correctly anyway. */
1271 return;
1272
1273 /* Defaults. */
1274 nship = 0;
1275 offship = 0.;
1276
1277 /* Must exist. */
1279 return;
1280
1281 /* Save focus. */
1282 focused = window_getFocus( wid );
1283
1284 /* Save positions. */
1285 if (outfits) {
1286 noutfit = toolkit_getImageArrayPos( wid, EQUIPMENT_OUTFITS );
1287 offoutfit = toolkit_getImageArrayOffset( wid, EQUIPMENT_OUTFITS );
1288 window_destroyWidget( wid, EQUIPMENT_OUTFITS );
1289 }
1290 if (ships) {
1291 nship = toolkit_getImageArrayPos( wid, EQUIPMENT_SHIPS );
1292 offship = toolkit_getImageArrayOffset( wid, EQUIPMENT_SHIPS );
1293 s = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
1294 selship = strdup( s );
1295 window_destroyWidget( wid, EQUIPMENT_SHIPS );
1296 }
1297
1298 /* Regenerate lists. */
1299 equipment_genLists( wid );
1300
1301 /* Restore positions. */
1302 if (outfits) {
1303 toolkit_setImageArrayPos( wid, EQUIPMENT_OUTFITS, noutfit );
1304 toolkit_setImageArrayOffset( wid, EQUIPMENT_OUTFITS, offoutfit );
1305 equipment_updateOutfits( wid, NULL );
1306 }
1307 if (ships) {
1308 toolkit_setImageArrayPos( wid, EQUIPMENT_SHIPS, nship );
1309 toolkit_setImageArrayOffset( wid, EQUIPMENT_SHIPS, offship );
1310 /* Try to maintain same ship selected. */
1311 s = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
1312 if ((s != NULL) && (strcmp(s,selship)!=0)) {
1313 int ret = toolkit_setImageArray( wid, EQUIPMENT_SHIPS, selship );
1314 if (ret != 0) /* Failed to maintain. */
1315 toolkit_setImageArrayPos( wid, EQUIPMENT_SHIPS, nship );
1316
1317 /* Update ships. */
1318 equipment_updateShips( wid, NULL );
1319 }
1320 free( selship );
1321 }
1322
1323 /* Restore focus. */
1324 window_setFocus( wid, focused );
1325 free(focused);
1326}
1327
1332int equipment_canSellPlayerShip( const char *shipname )
1333{
1334 int failure = 0;
1335 land_errClear();
1336 if (strcmp( shipname, player.p->name )==0) { /* Already on-board. */
1337 land_errDialogueBuild( _("You can't sell the ship you're piloting!") );
1338 failure = 1;
1339 }
1340
1341 return !failure;
1342}
1343
1348int equipment_canSwapPlayerShip( const char *shipname )
1349{
1350 int diff;
1351 Pilot *newship;
1352 const PlayerShip_t *ps = player_getPlayerShip( shipname );
1353 land_errClear();
1354
1355 if (strcmp(shipname,player.p->name)==0) { /* Already onboard. */
1356 land_errDialogueBuild( _("You're already onboard the %s."), shipname );
1357 return 0;
1358 }
1359
1360 /* Ship can't be piloted by player. */
1361 if (ship_isFlag( ps->p->ship, SHIP_NOPLAYER )) {
1362 land_errDialogueBuild( _("You can not pilot the %s! The ship can only be used as an escort."), shipname );
1363 return 0;
1364 }
1365
1366 /* Ship can't be set as an escort. */
1367 if (ship_isFlag( player.p->ship, SHIP_NOESCORT )) {
1368 land_errDialogueBuild( _("You can not swap ships and set %s as an escort!"), player.p->name );
1369 return 0;
1370 }
1371
1372 newship = ps->p;
1373 if (ps->deployed)
1374 diff = 0;
1375 else
1376 diff = pilot_cargoUsed(player.p) - pilot_cargoFree(newship) - (pfleet_cargoFree() - pilot_cargoFree(player.p)); /* Has to fit all the cargo. */
1377 diff = MAX( diff, pilot_cargoUsedMission(player.p) - pilot_cargoFree(newship) ); /* Has to fit all mission cargo. */
1378 if (diff > 0) { /* Current ship has too much cargo. */
1380 "You have %d tonne more cargo than the new ship can hold.",
1381 "You have %d tonnes more cargo than the new ship can hold.",
1382 diff),
1383 diff );
1384 return 0;
1385 }
1386 if (pilot_hasDeployed( player.p )) {
1387 if (!dialogue_YesNo(_("Recall Fighters"), _("This action will recall your deployed fighters. Is that OK?"))) {
1388 land_errDialogueBuild( _("You have deployed fighters.") );
1389 return 0;
1390 }
1391 /* Recall fighters. */
1393 }
1394 return 1;
1395}
1396
1401{
1402 Pilot *p;
1403
1404 /* Get player. */
1405 if (eq_wgt.selected == NULL)
1406 p = player.p;
1407 else
1408 p = eq_wgt.selected->p;
1409
1410 /* Add ammo to all outfits. */
1411 pilot_fillAmmo( p );
1412
1413 /* Notify GUI of modification. */
1414 gui_setShip();
1415}
1416
1426int equipment_shipStats( char *buf, int max_len, const Pilot *s, int dpseps, int name )
1427{
1428 int l;
1429 double eps, dps;
1430
1431 dps = 0.;
1432 eps = 0.;
1433 /* Calculate damage and energy per second. */
1434 if (dpseps)
1435 pilot_dpseps( s, &dps, &eps );
1436
1437 /* Write to buffer. */
1438 if (name)
1439 l = scnprintf( buf, max_len, "%s\n", s->name );
1440 else
1441 l = 0;
1442 if (dps > 0.)
1443 l += scnprintf( &buf[l], (max_len-l),
1444 _("%.2f DPS [%.2f EPS]\n"), dps, eps );
1445 if (s->ship->desc_extra != NULL)
1446 l += scnprintf( &buf[l], (max_len-l), "%s\n", _(s->ship->desc_extra) );
1447 l += ss_statsDesc( &s->stats, &buf[l], (max_len-l), 0 );
1448 return l;
1449}
1450
1454static void equipment_toggleFav( unsigned int wid, const char *wgt )
1455{
1456 int state = window_checkboxState( wid, wgt );
1457 const char *shipname = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
1458 if (strcmp(shipname,player.p->name)==0) { /* no ships */
1459 player.ps.favourite = state;
1460 }
1461 else {
1462 PlayerShip_t *ps = player_getPlayerShip( shipname );
1463 ps->favourite = state;
1464 }
1465
1466 /* Update ship to reflect changes. */
1467 equipment_regenLists( wid, 0, 1 );
1468}
1469
1470static void equipment_toggleDeploy( unsigned int wid, const char *wgt )
1471{
1472 int state = window_checkboxState( wid, wgt );
1473 const char *shipname = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
1474
1475 /* Can't deploy if no capacity. */
1476 if (player.fleet_capacity <= 0)
1477 return;
1478
1479 /* Only if current ship isn't selected try to deploy. */
1480 if (strcmp(shipname,player.p->name)!=0) {
1481 PlayerShip_t *ps = player_getPlayerShip( shipname );
1482 if (state && ship_isFlag( ps->p->ship, SHIP_NOESCORT )) {
1483 dialogue_msg( _("Invalid Escort"), _("You can not set your ship '%s' as an escort!"), ps->p->name );
1484 return;
1485 }
1486 if (pfleet_toggleDeploy( ps, state ))
1487 return;
1488 }
1489 else
1490 window_checkboxSet( wid, wgt, 1 ); /* Player is always deployed. */
1491
1492 /* Update ship to reflect changes. */
1493 equipment_regenLists( wid, 0, 1 );
1494}
1495
1501static void equipment_genLists( unsigned int wid )
1502{
1503 equipment_genShipList( wid );
1505}
1506
1511static void equipment_genShipList( unsigned int wid )
1512{
1513 ImageArrayCell *cships;
1514 int nships;
1515 int w, h;
1516 int sw, sh;
1517 const PlayerShip_t *ps;
1518 char r[PATH_MAX];
1519 glTexture *t;
1520 int iconsize;
1521
1522 /* Get dimensions. */
1523 equipment_getDim( wid, &w, &h, &sw, &sh, NULL, NULL,
1524 NULL, NULL, NULL, NULL, NULL, NULL );
1525
1526 if (widget_exists( wid, EQUIPMENT_SHIPS ))
1527 return;
1528
1529 eq_wgt.selected = NULL;
1530 if (spob_hasService(land_spob, SPOB_SERVICE_SHIPYARD))
1531 nships = player_nships()+1;
1532 else
1533 nships = 1;
1534 cships = calloc( nships, sizeof(ImageArrayCell) );
1535 /* Add player's current ship. */
1536 cships[0].image = gl_dupTexture(player.p->ship->gfx_store);
1537 cships[0].caption = strdup(player.p->name);
1538 cships[0].layers = gl_copyTexArray( player.p->ship->gfx_overlays );
1539 t = gl_newImage( OVERLAY_GFX_PATH"active.webp", 0 );
1540 cships[0].layers = gl_addTexArray( cships[0].layers, t );
1541 if (player.ps.favourite) {
1542 t = gl_newImage( OVERLAY_GFX_PATH"favourite.webp", 0 );
1543 cships[0].layers = gl_addTexArray( cships[0].layers, t );
1544 }
1545 if (player.p->ship->rarity > 0) {
1546 snprintf( r, sizeof(r), OVERLAY_GFX_PATH"rarity_%d.webp", player.p->ship->rarity );
1547 t = gl_newImage( r, 0 );
1548 cships[0].layers = gl_addTexArray( cships[0].layers, t );
1549 }
1550 if (spob_hasService(land_spob, SPOB_SERVICE_SHIPYARD)) {
1552 ps = player_getShipStack();
1553 for (int i=1; i<=array_size(ps); i++) {
1554 cships[i].image = gl_dupTexture( ps[i-1].p->ship->gfx_store );
1555 cships[i].caption = strdup( ps[i-1].p->name );
1556 cships[i].layers = gl_copyTexArray( ps[i-1].p->ship->gfx_overlays );
1557 if (ps[i-1].favourite) {
1558 t = gl_newImage( OVERLAY_GFX_PATH"favourite.webp", 0 );
1559 cships[i].layers = gl_addTexArray( cships[i].layers, t );
1560 }
1561 if (ps[i-1].deployed) {
1562 t = gl_newImage( OVERLAY_GFX_PATH"fleet.webp", 0 );
1563 cships[i].layers = gl_addTexArray( cships[i].layers, t );
1564 }
1565 if (ps[i-1].p->ship->rarity > 0) {
1566 snprintf( r, sizeof(r), OVERLAY_GFX_PATH"rarity_%d.webp", ps[i-1].p->ship->rarity );
1567 t = gl_newImage( r, 0 );
1568 cships[i].layers = gl_addTexArray( cships[i].layers, t );
1569 }
1570 }
1571 }
1572 /* Ship stats in alt text. */
1573 for (int i=0; i<nships; i++) {
1574 const Pilot *s = player_getShip( cships[i].caption );
1575 int l;
1576 cships[i].alt = malloc( STRMAX );
1577 l = snprintf( &cships[i].alt[0], STRMAX, _("Ship Stats\n") );
1578 l = equipment_shipStats( &cships[i].alt[0], STRMAX-l, s, 1, 1 );
1579 if (l == 0) {
1580 free( cships[i].alt );
1581 cships[i].alt = NULL;
1582 }
1583 }
1584
1585 /* Create the image array. */
1586 iconsize = 96;
1587 if (!conf.big_icons) {
1588 if (toolkit_simImageArrayVisibleElements(sw,sh,iconsize,iconsize) < nships)
1589 iconsize = 80;
1590 if (toolkit_simImageArrayVisibleElements(sw,sh,iconsize,iconsize) < nships)
1591 iconsize = 64;
1592 }
1593 window_addImageArray( wid, 20, -40,
1594 sw, sh, EQUIPMENT_SHIPS, iconsize, iconsize,
1596 toolkit_setImageArrayAccept( wid, EQUIPMENT_SHIPS, equipment_transChangeShip );
1597
1598 equipment_updateShips(wid, NULL);
1599}
1600
1601static int equipment_filter( const Outfit *o ) {
1602 Pilot *p = (eq_wgt.selected==NULL) ? NULL : eq_wgt.selected->p;
1603 const PlayerShip_t *ps;
1604
1605 /* Filter only those that fit slot. */
1606 if ((p!=NULL) && (eq_wgt.slot >= 0)) {
1607 const PilotOutfitSlot *pos = p->outfits[ eq_wgt.slot ];
1608 if (!outfit_fitsSlot( o, &pos->sslot->slot ))
1609 return 0;
1610 }
1611
1612 /* Standard filtering. */
1613 switch (equipment_outfitMode) {
1614 case 0:
1615 return 1;
1616
1617 case 1: /* Fits any ship of the player. */
1618 ps = player_getShipStack();
1619 for (int j=0; j < array_size(ps); j++) {
1620 const Pilot *pp = ps[j].p;
1621 for (int i=0; i < array_size(pp->outfits); i++) {
1622 if (outfit_fitsSlot( o, &pp->outfits[i]->sslot->slot ))
1623 return 1;
1624 }
1625 }
1626 return 0;
1627
1628 case 2: /* Fits currently selected ship. */
1629 if (p==NULL)
1630 return 1;
1631 for (int i=0; i < array_size(p->outfits); i++) {
1632 if (outfit_fitsSlot( o, &p->outfits[i]->sslot->slot ))
1633 return 1;
1634 }
1635 return 0;
1636
1637 case 3:
1638 return (o->slot.size==OUTFIT_SLOT_SIZE_LIGHT);
1639 case 4:
1640 return (o->slot.size==OUTFIT_SLOT_SIZE_MEDIUM);
1641 case 5:
1642 return (o->slot.size==OUTFIT_SLOT_SIZE_HEAVY);
1643 }
1644 return 1;
1645}
1646static int equipment_filterWeapon( const Outfit *o ) {
1647 return equipment_filter(o) && outfit_filterWeapon(o);
1648}
1649static int equipment_filterUtility( const Outfit *o ) {
1650 return equipment_filter(o) && outfit_filterUtility(o);
1651}
1652static int equipment_filterStructure( const Outfit *o ) {
1653 return equipment_filter(o) && outfit_filterStructure(o);
1654}
1655static int equipment_filterCore( const Outfit *o ) {
1656 return equipment_filter(o) && outfit_filterCore(o);
1657}
1658
1663static void equipment_genOutfitList( unsigned int wid )
1664{
1665 int x, y, w, h, ow, oh;
1666 int ix, iy, iw, ih, barw; /* Input filter. */
1667 const char *filtertext;
1668 int (*tabfilters[])( const Outfit *o ) = {
1669 equipment_filter,
1670 equipment_filterWeapon,
1671 equipment_filterUtility,
1672 equipment_filterStructure,
1673 equipment_filterCore,
1674 };
1675 const char *tabnames[] = {
1676 _("All"), _(OUTFIT_LABEL_WEAPON), _(OUTFIT_LABEL_UTILITY), _(OUTFIT_LABEL_STRUCTURE), _(OUTFIT_LABEL_CORE)
1677 };
1678 int noutfits, active;
1679 ImageArrayCell *coutfits;
1680 int iconsize;
1681 const Pilot *p = (eq_wgt.selected != NULL) ? eq_wgt.selected->p : NULL;
1682
1683 /* Get dimensions. */
1684 equipment_getDim( wid, &w, &h, NULL, NULL, &ow, &oh,
1685 NULL, NULL, NULL, NULL, NULL, NULL );
1686
1687 /* Deselect. */
1688 eq_wgt.outfit = NULL;
1689
1690 /* Calculate position. */
1691 x = 20;
1692 y = 20;
1693
1694 /* Create tabbed window. */
1695 if (!widget_exists( wid, EQUIPMENT_OUTFIT_TAB )) {
1696 window_addTabbedWindow( wid, x, y + oh - 30, ow, 30,
1697 EQUIPMENT_OUTFIT_TAB, OUTFIT_TABS, tabnames, 1 );
1698
1699 barw = window_tabWinGetBarWidth( wid, EQUIPMENT_OUTFIT_TAB );
1700
1701 iw = CLAMP(0, 150, ow - barw - 30);
1702 ih = 30;
1703
1704 ix = ow - iw + 15;
1705 iy = y + oh - 25 - 1;
1706
1707#ifdef DEBUGGING
1708 if (iw <= 60)
1709 WARN(_("Very little space on equipment outfit tabs!"));
1710#endif /* DEBUGGING */
1711
1712 /* Add popdown menu stuff. */
1713 window_addButton( wid, ix+iw-30, iy, 30, 30, "btnOutfitFilter", NULL, equipment_outfitPopdown );
1714 window_buttonCustomRender( wid, "btnOutfitFilter", window_buttonCustomRenderGear );
1715 iw -= 35;
1716
1717 /* Set text filter. */
1718 window_addInput( wid, ix, iy, iw, ih, EQUIPMENT_FILTER, 32, 1, NULL );
1719 inp_setEmptyText( wid, EQUIPMENT_FILTER, _("Filter…") );
1720 window_setInputCallback( wid, EQUIPMENT_FILTER, equipment_filterOutfits );
1721 }
1722
1723 window_tabWinOnChange( wid, EQUIPMENT_OUTFIT_TAB, equipment_changeTab );
1724 active = window_tabWinGetActive( equipment_wid, EQUIPMENT_OUTFIT_TAB );
1725
1726 /* Widget must not already exist. */
1727 if (widget_exists( wid, EQUIPMENT_OUTFITS ))
1728 return;
1729
1730 /* Allocate space. */
1731 noutfits = MAX( 1, player_numOutfits() ); /* This is the most we'll need, probably less due to filtering. */
1732 array_free( iar_outfits[active] );
1733 iar_outfits[active] = array_create( Outfit* );
1734
1735 filtertext = NULL;
1736 if (widget_exists(equipment_wid, EQUIPMENT_FILTER)) {
1737 filtertext = window_getInput( equipment_wid, EQUIPMENT_FILTER );
1738 if (strlen(filtertext) == 0)
1739 filtertext = NULL;
1740 }
1741
1742 /* Get the outfits. */
1743 noutfits = player_getOutfitsFiltered( (const Outfit***)&iar_outfits[active], tabfilters[active], filtertext );
1744 coutfits = outfits_imageArrayCells( (const Outfit**)iar_outfits[active], &noutfits, (p==NULL) ? player.p : p, 0 );
1745
1746 /* Create the actual image array. */
1747 iw = ow - 6;
1748 ih = oh - 37;
1749 iconsize = 96;
1750 if (!conf.big_icons) {
1751 if (toolkit_simImageArrayVisibleElements(iw,ih,iconsize,iconsize) < noutfits)
1752 iconsize = 80;
1753 if (toolkit_simImageArrayVisibleElements(iw,ih,iconsize,iconsize) < noutfits)
1754 iconsize = 64;
1755 }
1756 window_addImageArray( wid, x + 4, y + 3, iw, ih,
1757 EQUIPMENT_OUTFITS, iconsize, iconsize,
1758 coutfits, noutfits,
1762
1763 toolkit_setImageArrayAccept( wid, EQUIPMENT_OUTFITS, equipment_rightClickOutfits );
1764
1765 equipment_updateOutfits(wid, NULL);
1766}
1767
1771static char eq_qCol( double cur, double base, int inv )
1772{
1773 if (cur > 1.2*base)
1774 return (inv) ? 'r' : 'g';
1775 else if (cur < 0.8*base)
1776 return (inv) ? 'g' : 'r';
1777 return '0';
1778}
1779
1783static const char* eq_qSym( double cur, double base, int inv )
1784{
1785 if (cur > 1.2*base)
1786 return (inv) ? "!! " : "";
1787 else if (cur < 0.8*base)
1788 return (inv) ? "" : "!! ";
1789 return "";
1790}
1791
1792#define EQ_COMP( cur, base, inv ) \
1793eq_qCol( cur, base, inv ), eq_qSym( cur, base, inv ), cur
1794#define EQ_COMP_I( cur, base, inv ) \
1795eq_qCol( cur, base, inv ), eq_qSym( cur, base, inv )
1801void equipment_updateShips( unsigned int wid, const char* str )
1802{
1803 (void) str;
1804 char buf[STRMAX], buf_price[ECON_CRED_STRLEN];
1805 char errorReport[STRMAX_SHORT], tbuf[64];
1806 const char *shipname, *acquired;
1807 char scargo[NUM2STRLEN];
1808 Pilot *ship;
1809 PlayerShip_t *ps, *prevship;
1810 char *nt;
1811 int onboard, cargo, jumps, favourite, deployed, x, h, spaceworthy;
1812 int ww, wh, sw, sh, bw, bh, hacquired, hname;
1813 int wgtw, wgth;
1814 size_t l = 0;
1815
1816 equipment_getDim( wid, &ww, &wh, &sw, &sh, NULL, NULL, NULL, NULL, NULL, NULL, &bw, &bh );
1817
1818 /* Clear defaults. */
1819 eq_wgt.slot = -1;
1820 eq_wgt.mouseover = -1;
1821 equipment_lastick = SDL_GetTicks();
1822
1823 /* Get the ship. */
1824 shipname = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
1825 if (strcmp(shipname,player.p->name)==0) { /* no ships */
1826 ps = &player.ps;
1827 onboard = 1;
1828 deployed = 1;
1829 }
1830 else {
1831 ps = player_getPlayerShip( shipname );
1832 onboard = 0;
1833 deployed = ps->deployed;
1834 }
1835 ship = ps->p;
1836 favourite = ps->favourite;
1837 prevship = eq_wgt.selected;
1838 eq_wgt.selected = ps;
1839
1840 /* update text */
1841 credits2str( buf_price, player_shipPrice(shipname,0), 2 ); /* sell price */
1842 cargo = pilot_cargoFree(ship) + pilot_cargoUsed(ship);
1843 nt = ntime_pretty( pilot_hyperspaceDelay( ship ), 2 );
1844
1845 /* Get ship error report. */
1846 spaceworthy = !pilot_reportSpaceworthy( ship, errorReport, sizeof(errorReport));
1847
1848 jumps = floor(ship->fuel_max / ship->fuel_consumption);
1849
1850 /* Get acquired text length. */
1851 x = 20+sw+20+180+10;
1852 acquired = (ps->acquired) ? ps->acquired : _("You do not remember how you acquired this ship.");
1853 window_dimWidget( wid, "txtAcquired", &wgtw, &wgth );
1854 hacquired = gl_printLinesRaw( &gl_defFont, wgtw, acquired );
1855 window_dimWidget( wid, "txtDDesc", &wgtw, &wgth );
1856 hname = gl_printLinesRaw( &gl_defFont, wgtw, ship->name );
1857
1858 /* Helper strings. */
1859 num2str( scargo, pilot_cargoUsed(ship), 0 );
1860
1861 /* Destroy intrinsic widget if applicable. */
1862 if (widget_exists( wid, "iarIntrinsic" ))
1863 window_destroyWidget( wid, "iarIntrinsic" );
1864
1865 l += scnprintf( &buf[l], sizeof(buf)-l, "%s", _("Name:") );
1866 for (int i=0; i<hname-1; i++)
1867 l += scnprintf( &buf[l], sizeof(buf)-l, "\n" );
1868 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Model:") );
1869 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Class:") );
1870 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Acquired Date:") );
1871 for (int i=0; i<hacquired+1; i++)
1872 l += scnprintf( &buf[l], sizeof(buf)-l, "\n" );
1873 if (ship_mode==0) {
1874 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Value:") );
1875 if (player.fleet_capacity > 0)
1876 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Fleet Capacity:") );
1877 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Crew:") );
1878 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Mass:") );
1879 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Jump Time:") );
1880 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Accel:") );
1881 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Speed:") );
1882 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Turn:") );
1883 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Time Constant:") );
1884 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Detected at:") );
1885 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Signature:") );
1886 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Stealth at:") );
1887 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Scanning time:") );
1888 l += scnprintf( &buf[l], sizeof(buf)-l, "\n" );
1889 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Absorption:") );
1890 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Shield:") );
1891 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Armour:") );
1892 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Energy:") );
1893 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Cargo Space:") );
1894 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Fuel:") );
1895 l += scnprintf( &buf[l], sizeof(buf)-l, "\n" );
1896 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Ship Status:") );
1897 }
1898 else {
1899 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Time Flown:") );
1900 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Jumped Times:") );
1901 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Landed Times:") );
1902 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Damage Done:") );
1903 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Damage Taken:") );
1904 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _("Ships Destroyed:") );
1905 l += scnprintf( &buf[l], sizeof(buf)-l, "\n\n%s", _("Intrinsic Outfits:") );
1906 }
1907 window_modifyText( wid, "txtSDesc", buf );
1908
1909 /* Fill the buffer. */
1910 /* Generic. */
1911 l = 0;
1912 l += scnprintf( &buf[l], sizeof(buf)-l, "%s", ship->name );
1913 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _(ship->ship->name) );
1914 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", _(ship_classDisplay(ship->ship)) );
1915 if (ps->acquired_date==0)
1916 snprintf( tbuf, sizeof(tbuf), _("Unknown") );
1917 else
1918 ntime_prettyBuf( tbuf, sizeof(tbuf), ps->acquired_date, 2 );
1919 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", tbuf );
1920
1921 h = gl_printHeightRaw( &gl_defFont, wgtw, buf );
1922 window_moveWidget( wid, "txtAcquired", x, -40-h-6 );
1923 window_modifyText( wid, "txtAcquired", acquired );
1924
1925 for (int i=0; i<hacquired+1; i++)
1926 l += scnprintf( &buf[l], sizeof(buf)-l, "\n" );
1927 if (ship_mode==0) {
1928 /* Some core stats. */
1929 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", buf_price );
1930 if (player.fleet_capacity > 0)
1931 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%d", ship->ship->points );
1932 l += scnprintf( &buf[l], sizeof(buf)-l, "\n#%c%s%d#0", EQ_COMP( (int)floor(ship->crew), ship->ship->crew, 0 ) );
1933 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s#0 %s", num2strU(ship->solid.mass,0), UNIT_MASS );
1934 l += scnprintf( &buf[l], sizeof(buf)-l, "\n" );
1935 l += scnprintf( &buf[l], sizeof(buf)-l, _("%s average"), nt );
1936 l += scnprintf( &buf[l], sizeof(buf)-l, "\n" );
1937 l += scnprintf( &buf[l], sizeof(buf)-l, _("#%c%s%.0f#0 %s"),
1938 EQ_COMP( ship->accel, ship->ship->accel, 0 ), UNIT_ACCEL );
1939 l += scnprintf( &buf[l], sizeof(buf)-l, _("\n#%c%s%.0f#0 %s (max #%c%s%.0f#0 %s)"),
1940 EQ_COMP( ship->speed, ship->ship->speed, 0 ), UNIT_SPEED,
1941 EQ_COMP( solid_maxspeed( &ship->solid, ship->speed, ship->accel ),
1942 solid_maxspeed( &ship->solid, ship->ship->speed, ship->ship->accel), 0 ), UNIT_SPEED );
1943 l += scnprintf( &buf[l], sizeof(buf)-l, _("\n#%c%s%.0f#0 %s"),
1944 EQ_COMP( ship->turn*180./M_PI, ship->ship->turn*180./M_PI, 0 ), UNIT_ROTATION );
1945 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%.0f%%", ship->stats.time_mod * ship->ship->dt_default*100. );
1946 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s %s\n", num2strU(ship->ew_detection,0), UNIT_DISTANCE );
1947 l += scnprintf( &buf[l], sizeof(buf)-l, _("%s %s"), num2strU(ship->ew_signature,0), UNIT_DISTANCE );
1948 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s %s\n", num2strU(ship->ew_stealth,0), UNIT_DISTANCE );
1949 l += scnprintf( &buf[l], sizeof(buf)-l, _("%.1f %s"), pilot_ewScanTime(ship), UNIT_TIME );
1950 l += scnprintf( &buf[l], sizeof(buf)-l, "\n\n" );
1951 /* Health. */
1952 l += scnprintf( &buf[l], sizeof(buf)-l, "#%c%s%.0f%%\n", EQ_COMP( ship->dmg_absorb*100., ship->ship->dmg_absorb*100., 0 ) );
1953 l += scnprintf( &buf[l], sizeof(buf)-l, _("#%c%s%s#0 %s"),
1954 EQ_COMP_I( ship->shield_max, ship->ship->shield, 0 ), num2strU(ship->shield_max,0), UNIT_ENERGY );
1955 l += scnprintf( &buf[l], sizeof(buf)-l, _(" (#%c%s%s#0 %s)"),
1956 EQ_COMP_I( ship->shield_regen, ship->ship->shield_regen, 0 ), num2strU(ship->shield_regen,1), UNIT_POWER );
1957 l += scnprintf( &buf[l], sizeof(buf)-l, "\n" );
1958 l += scnprintf( &buf[l], sizeof(buf)-l, _("#%c%s%s#0 %s"),
1959 EQ_COMP_I( ship->armour_max, ship->ship->armour, 0 ), num2strU(ship->armour_max,0), UNIT_ENERGY );
1960 l += scnprintf( &buf[l], sizeof(buf)-l, _(" (#%c%s%s#0 %s)"),
1961 EQ_COMP_I( ship->armour_regen, ship->ship->armour_regen, 0 ), num2strU(ship->armour_regen,1), UNIT_POWER );
1962 l += scnprintf( &buf[l], sizeof(buf)-l, "\n" );
1963 l += scnprintf( &buf[l], sizeof(buf)-l, _("#%c%s%s#0 %s"),
1964 EQ_COMP_I( ship->energy_max, ship->ship->energy, 0 ), num2strU(ship->energy_max,0), UNIT_ENERGY );
1965 l += scnprintf( &buf[l], sizeof(buf)-l, _(" (#%c%s%s#0 %s)"),
1966 EQ_COMP_I( ship->energy_regen, ship->ship->energy_regen, 0 ), num2strU(ship->energy_regen,1), UNIT_POWER );
1967 l += scnprintf( &buf[l], sizeof(buf)-l, "\n" );
1968 /* Misc. */
1969 l += scnprintf( &buf[l], sizeof(buf)-l, _("%s / #%c%s%s#0 %s"),
1970 scargo, EQ_COMP_I( cargo, ship->ship->cap_cargo, 0 ), num2strU(cargo,0), UNIT_MASS );
1971 l += scnprintf( &buf[l], sizeof(buf)-l, "\n" );
1972 l += scnprintf( &buf[l], sizeof(buf)-l, _("%s %s (%d %s)"),
1973 num2strU(ship->fuel_max,0), UNIT_UNIT, jumps, n_( "jump", "jumps", jumps ) );
1974 l += scnprintf( &buf[l], sizeof(buf)-l, "\n" );
1975 /*l +=*/ scnprintf( &buf[l], sizeof(buf)-l, "\n#%c%s#0", spaceworthy ? '0' : 'r', errorReport );
1976 }
1977 else {
1978 int destroyed = 0;
1979 for (int i=0; i<SHIP_CLASS_TOTAL; i++)
1980 destroyed += ps->ships_destroyed[i];
1981 l += scnprintf( &buf[l], sizeof(buf)-l, "\n" );
1982 l += scnprintf( &buf[l], sizeof(buf)-l, _("%s hours"), num2strU(ps->time_played/3600.,1) );
1983 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", num2strU(ps->jumped_times,0) );
1984 l += scnprintf( &buf[l], sizeof(buf)-l, "\n%s", num2strU(ps->landed_times,0) );
1985 l += scnprintf( &buf[l], sizeof(buf)-l, "\n" );
1986 l += scnprintf( &buf[l], sizeof(buf)-l, _("%s %s"), num2strU(ps->dmg_done_shield+ps->dmg_done_armour,0), UNIT_ENERGY );
1987 l += scnprintf( &buf[l], sizeof(buf)-l, "\n" );
1988 l += scnprintf( &buf[l], sizeof(buf)-l, _("%s %s"), num2strU(ps->dmg_taken_shield+ps->dmg_taken_armour,0), UNIT_ENERGY );
1989 /*l +=*/ scnprintf( &buf[l], sizeof(buf)-l, "\n%s", num2strU(destroyed,0) );
1990 }
1991 window_modifyText( wid, "txtDDesc", buf );
1992
1993 /* Add intrinsic outfit image array. */
1994 if (ship_mode!=0) {
1995 int tx, ty, tw, th;
1996 ImageArrayCell *cells;
1997 int ncells = array_size(ship->outfit_intrinsic);
1998 Outfit const **outfits = (Outfit const**) array_create( Outfit* );
1999 for (int i=0; i<ncells; i++)
2000 array_push_back( &outfits, ship->outfit_intrinsic[i].outfit );
2001
2002 cells = outfits_imageArrayCells( outfits, &ncells, ship, 0 );
2003
2004 window_posWidget( wid, "txtSDesc", &tx, &ty );
2005 window_dimWidget( wid, "txtSDesc", &tw, &th );
2006 ty = -40-window_getTextHeight( wid, "txtSDesc" )-10;
2007 window_addImageArray( wid, tx, ty, ww-x-128-30-10, ty-20+wh-bh-30, "iarIntrinsic",
2008 64, 64, cells, ncells, NULL, NULL, NULL );
2009
2010 array_free(outfits);
2011 }
2012
2013 /* Clean up. */
2014 free( nt );
2015
2016 /* Set checkboxes. */
2017 window_checkboxSet( wid, "chkFav", favourite );
2018 if (player.fleet_capacity > 0)
2019 window_checkboxSet( wid, "chkDeploy", deployed );
2020
2021 /* button disabling */
2022 if (onboard) {
2023 window_disableButton( wid, "btnSellShip" );
2024 window_disableButton( wid, "btnChangeShip" );
2025 }
2026 else {
2027 window_enableButton( wid, "btnChangeShip" );
2028 window_enableButton( wid, "btnSellShip" );
2029 }
2030
2031 /* If pilot-dependent outfit filter modes are active, we have to regenerate outfits always. */
2032 if ((equipment_outfitMode==2) && (eq_wgt.selected != prevship))
2033 equipment_regenLists( wid, 1, 0 );
2034}
2035#undef EQ_COMP
2041void equipment_updateOutfits( unsigned int wid, const char* str )
2042{
2043 (void) str;
2044 int i, active;
2045
2046 /* Must have outfit. */
2047 active = window_tabWinGetActive( wid, EQUIPMENT_OUTFIT_TAB );
2048 i = toolkit_getImageArrayPos( wid, EQUIPMENT_OUTFITS );
2049 if (i < 0 || array_size(iar_outfits[active])==0) {
2050 eq_wgt.outfit = NULL;
2051 return;
2052 }
2053
2054 eq_wgt.outfit = iar_outfits[active][i];
2055}
2056
2062static void equipment_filterOutfits( unsigned int wid, const char *str )
2063{
2064 (void) str;
2065 equipment_regenLists(wid, 1, 0);
2066}
2067
2072static void equipment_outfitPopdownSelect( unsigned int wid, const char *str )
2073{
2074 int m = toolkit_getListPos( wid, str );
2075 if (m == equipment_outfitMode)
2076 return;
2077
2079 equipment_regenLists( wid, 1, 0 );
2081}
2082
2083static void equipment_outfitPopdownActivate( unsigned int wid, const char *str )
2084{
2086 window_destroyWidget( wid, str );
2087}
2088
2089static void equipment_outfitPopdown( unsigned int wid, const char* str )
2090{
2091 const char *name = "lstOutfitPopdown";
2092 const char *modes[] = {
2093 N_("Show all outfits"),
2094 N_("Show only outfits equipable on any of your ships"),
2095 N_("Show only outfits equipable on current ship"),
2096 N_("Show only light outfits"),
2097 N_("Show only medium outfits"),
2098 N_("Show only heavy outfits"),
2099 };
2100 char **modelist;
2101 const size_t n = sizeof(modes) / sizeof(const char*);
2102 int x, y, w, h;
2103
2104 if (widget_exists( wid, name )) {
2105 window_destroyWidget( wid, name );
2106 return;
2107 }
2108
2109 modelist = malloc(sizeof(modes));
2110 for (size_t i=0; i<n; i++)
2111 modelist[i] = strdup( _(modes[i]) );
2112
2113 window_dimWidget( wid, str, &w, &h );
2114 window_posWidget( wid, str, &x, &y );
2115 window_addList( wid, x+w, y-120+h, 350, 120, name, modelist, n, equipment_outfitMode, equipment_outfitPopdownSelect, equipment_outfitPopdownActivate );
2116 window_setFocus( wid, name );
2117}
2118
2127static void equipment_changeTab( unsigned int wid, const char *wgt, int old, int tab )
2128{
2129 (void) wgt;
2130 iar_data_t old_data;
2131
2132 toolkit_saveImageArrayData( wid, EQUIPMENT_OUTFITS, &iar_data[old] );
2133
2134 /* Store the currently-saved positions for the new tab. */
2135 old_data = iar_data[tab];
2136
2137 /* Resetting the input will cause the outfit list to be regenerated. */
2138 if (widget_exists(wid, EQUIPMENT_FILTER))
2139 window_setInput(wid, EQUIPMENT_FILTER, NULL);
2140 else
2141 equipment_regenLists(wid, 1, 0);
2142
2143 /* Set positions for the new tab. This is necessary because the stored
2144 * position for the new tab may have exceeded the size of the old tab,
2145 * resulting in it being clipped. */
2146 toolkit_loadImageArrayData( wid, EQUIPMENT_OUTFITS, &old_data );
2147
2148 /* Focus the outfit image array. */
2149 window_setFocus( wid, EQUIPMENT_OUTFITS );
2150}
2151
2155static void equipment_rightClickShips( unsigned int wid, const char *str )
2156{
2157 const char *shipname = toolkit_getImageArray( wid, str );
2158 PlayerShip_t *ps = player_getPlayerShip( shipname );
2159
2160 /* Must have fleet capacity. */
2161 if (player.fleet_capacity <= 0)
2162 return;
2163
2164 /* Deploy the ship if applicable. */
2165 if (ps == NULL)
2166 return;
2167
2168 /* Don't do anything for player ship. */
2169 if (strcmp(shipname,player.p->name)==0)
2170 return;
2171
2172 if (!ps->deployed && ship_isFlag( ps->p->ship, SHIP_NOESCORT )) {
2173 dialogue_msg( _("Invalid Escort"), _("You can not set your ship '%s' as an escort!"), ps->p->name );
2174 return;
2175 }
2176
2177 /* Try to deploy. */
2178 if (pfleet_toggleDeploy( ps, !ps->deployed ))
2179 return;
2180
2181 if (player.fleet_capacity > 0)
2182 window_checkboxSet( wid, "chkDeploy", ps->deployed );
2183
2184 equipment_regenLists( wid, 0, 1 );
2185}
2186
2192static void equipment_transChangeShip( unsigned int wid, const char* str )
2193{
2194 (void) str;
2195
2196 equipment_changeShip( wid );
2197
2198 /* update the window to reflect the change */
2199 equipment_updateShips( wid, NULL );
2200}
2205static void equipment_changeShip( unsigned int wid )
2206{
2207 const char *shipname, *filtertext;
2208 PlayerShip_t *ps;
2209 int i;
2210
2211 shipname = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
2212 ps = player_getPlayerShip( shipname );
2213 if (ps != NULL) {
2214 /* Swap deployed status. */
2215 player.ps.deployed = ps->deployed;
2216 }
2217
2218 if (!equipment_canSwapPlayerShip( shipname )) {
2220 return;
2221 }
2222
2223 /* Store active tab, filter text, and positions for the outfits. */
2224 i = window_tabWinGetActive( wid, EQUIPMENT_OUTFIT_TAB );
2225 toolkit_saveImageArrayData( wid, EQUIPMENT_OUTFITS, &iar_data[i] );
2226 if (widget_exists(wid, EQUIPMENT_FILTER))
2227 filtertext = window_getInput( equipment_wid, EQUIPMENT_FILTER );
2228 else
2229 filtertext = NULL;
2230
2231 /* Swap ship. */
2232 player_swapShip( shipname, 1 );
2234
2235 /* What happens here is the gui gets recreated when the player swaps ship.
2236 * This causes all the windows to be destroyed and the 'wid' we have here
2237 * becomes invalid. However, since we store it in a global variable we can
2238 * recover it and use it instead. */
2239 wid = equipment_wid;
2240
2241 /* Restore outfits image array properties. */
2242 window_tabWinSetActive( wid, EQUIPMENT_OUTFIT_TAB, i );
2243 toolkit_loadImageArrayData( wid, EQUIPMENT_OUTFITS, &iar_data[i] );
2244 if (widget_exists(wid, EQUIPMENT_FILTER))
2245 window_setInput(wid, EQUIPMENT_FILTER, filtertext);
2246
2247 /* Regenerate ship widget. */
2248 equipment_regenLists( wid, 0, 1 );
2249
2250 /* Focus new ship. */
2251 toolkit_setImageArrayPos( wid, EQUIPMENT_SHIPS, 0 );
2252 toolkit_setImageArrayOffset( wid, EQUIPMENT_SHIPS, 0. );
2253}
2254
2261static void equipment_unequipShip( unsigned int wid, const char* str )
2262{
2263 (void) str;
2264 Pilot *ship = eq_wgt.selected->p;
2265
2266 /*
2267 * Unequipping is disallowed under two conditions. Firstly, the ship may not
2268 * be unequipped when it has fighters deployed in space. Secondly, it cannot
2269 * unequip if it's carrying more cargo than the ship normally fits, i.e.
2270 * by equipping cargo pods.
2271 */
2272 if (pilot_cargoUsed(ship) > ship->ship->cap_cargo) {
2273 dialogue_alert( _("You can't unequip your ship when you have more cargo than it can hold without modifications!") );
2274 return;
2275 }
2276 if (dialogue_YesNo(_("Unequip Ship"), /* confirm */
2277 _("Are you sure you want to remove all equipment from your ship?"))==0)
2278 return;
2279 if (pilot_hasDeployed( ship )) {
2280 if (!dialogue_YesNo(_("Recall Fighters"), _("This action will recall your deployed fighters. Is that OK?")))
2281 return;
2282 /* Recall fighters. */
2283 escort_clearDeployed( ship );
2284 }
2285
2286 /* Remove all outfits. */
2287 for (int i=0; i<array_size(ship->outfits); i++) {
2288 int ret;
2289 PilotOutfitSlot *s = ship->outfits[i];
2290 const Outfit *o = s->outfit;
2291
2292 /* Skip null outfits. */
2293 if (o==NULL)
2294 continue;
2295
2296 /* Ignore locked slots. */
2297 if (s->sslot->locked)
2298 continue;
2299
2300 /* Remove ammo first. */
2301 pilot_rmAmmo( ship, s, pilot_maxAmmoO(ship, o) );
2302
2303 /* Remove rest. */
2304 ret = pilot_rmOutfitRaw( ship, s );
2305 if (ret==0)
2307 }
2308
2309 /* Recalculate stats. */
2310 pilot_calcStats( ship );
2311 pilot_healLanded( ship );
2312
2313 /* Regenerate list. */
2314 equipment_regenLists( wid, 1, 1 );
2315
2316 /* Regenerate outfits. */
2318
2319 /* Update weapon sets if needed. */
2320 if (ship->autoweap) {
2321 pilot_weaponAuto( ship );
2323 }
2324 pilot_weaponSafe( ship );
2325
2326 /* Notify GUI of modification. */
2327 gui_setShip();
2328}
2329
2333static void equipment_autoequipShip( unsigned int wid, const char* str )
2334{
2335 (void) str;
2336 (void) wid;
2337 Pilot *ship;
2338 int doswap;
2339 const char *curship;
2340 const char *file = AUTOEQUIP_PATH;
2341
2342 ship = eq_wgt.selected->p;
2343
2344 /* Swap ship if necessary. */
2345 doswap = (ship != player.p);
2346 if (doswap) {
2347 curship = player.p->name;
2348 player_swapShip( ship->name, 0 );
2349 }
2350
2351 /* Create the environment */
2352 if (autoequip_env == LUA_NOREF) {
2353 /* Read File. */
2354 size_t bufsize;
2355 char *buf = ndata_read( file, &bufsize );
2356 if (buf == NULL) {
2357 WARN( _("File '%s' not found!"), file );
2358 return;
2359 }
2360 /* New env. */
2361 autoequip_env = nlua_newEnv();
2362 nlua_loadStandard( autoequip_env );
2363 nlua_loadTk( autoequip_env );
2364 if (nlua_dobufenv(autoequip_env, buf, bufsize, file) != 0) {
2365 WARN(_("Failed to run '%s': %s"), file, lua_tostring(naevL,-1));
2366 free(buf);
2367 lua_pop(naevL,1);
2368 goto autoequip_cleanup;
2369 }
2370
2371 free(buf);
2372 }
2373
2374 /* Run func. */
2375 nlua_getenv( naevL, autoequip_env, "autoequip" );
2376 if (!lua_isfunction(naevL,-1)) {
2377 WARN(_("'%s' doesn't have valid required 'autoequip' function!"), file);
2378 lua_pop(naevL,1);
2379 goto autoequip_cleanup;
2380 }
2381 lua_pushpilot(naevL,player.p->id);
2382 if (nlua_pcall( autoequip_env, 1, 0 )) {
2383 WARN(_("'%s' failed to run required 'autoequip' function: %s"), file, lua_tostring(naevL,-1));
2384 lua_pop(naevL,1);
2385 goto autoequip_cleanup;
2386 }
2387
2388 hooks_run( "equip" ); /* Equipped. */
2389
2390 /* Clean up. */
2391autoequip_cleanup:
2392 if (doswap) {
2393 player_swapShip( curship, 0 );
2394 toolkit_setImageArray( equipment_wid, EQUIPMENT_SHIPS, ship->name );
2396 }
2397}
2398
2404static void equipment_sellShip( unsigned int wid, const char* str )
2405{
2406 (void) str;
2407 char buf[ECON_CRED_STRLEN], *name;
2408 credits_t price;
2409 PlayerShip_t *ps;
2410 Pilot *p;
2411 const Ship *s;
2412 HookParam hparam[3];
2413 const char *shipname = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
2414
2415 if (!equipment_canSellPlayerShip( shipname )) {
2417 return;
2418 }
2419
2420 /* Calculate price. */
2421 price = player_shipPrice(shipname,0);
2422 credits2str( buf, price, 2 );
2423
2424 /* Check if player really wants to sell. */
2425 if (!dialogue_YesNo( _("Sell Ship"),
2426 _("Are you sure you want to sell your ship %s for %s?"),
2427 shipname, buf))
2428 return;
2429
2430 /* Check if deployed and undeploy. */
2431 ps = player_getPlayerShip( shipname );
2432 if (ps->deployed) {
2433 if (pfleet_toggleDeploy( ps, 0 ))
2434 return;
2435 }
2436
2437 /* Store ship type. */
2438 p = player_getShip( shipname );
2439 s = p->ship;
2440
2441 /* Sold. */
2442 name = strdup(shipname);
2443 player_modCredits( price );
2444 player_rmShip( shipname );
2445
2446 /* Destroy widget - must be before widget. */
2447 equipment_regenLists( wid, 0, 1 );
2448
2449 /* Display widget. */
2450 dialogue_msg( _("Ship Sold"),
2451 _("You have sold your ship %s for %s."), name, buf );
2452
2453 /* Run hook. */
2454 hparam[0].type = HOOK_PARAM_SHIP;
2455 hparam[0].u.ship = s;
2456 hparam[1].type = HOOK_PARAM_STRING;
2457 hparam[1].u.str = name;
2458 hparam[2].type = HOOK_PARAM_SENTINEL;
2459 hooks_runParam( "ship_sell", hparam );
2460 land_needsTakeoff( 1 );
2461 free(name);
2462}
2463
2470static void equipment_renameShip( unsigned int wid, const char *str )
2471{
2472 (void) str;
2473 const char *shipname = toolkit_getImageArray( wid, EQUIPMENT_SHIPS );
2474 Pilot *ship = player_getShip(shipname);
2475 char *newname = dialogue_input( _("Ship Name"), 1, 60,
2476 _("Please enter a new name for your %s:"), _(ship->ship->name) );
2477
2478 /* Player cancelled the dialogue. */
2479 if (newname == NULL)
2480 return;
2481
2482 /* Must not have same name. */
2483 if (player_hasShip(newname)) {
2484 dialogue_msg( _("Name Collision"),
2485 _("Please do not give the ship the same name as another of your ships."));
2486 free(newname);
2487 return;
2488 }
2489
2490 free (ship->name);
2491 ship->name = newname;
2492
2493 /* Destroy widget - must be before widget. */
2494 equipment_regenLists( wid, 0, 1 );
2495}
2496
2500static void equipment_shipMode( unsigned int wid, const char *str )
2501{
2502 ship_mode = 1-ship_mode;
2503 equipment_updateShips( wid, str );
2504}
2505
2509static int equipment_playerAddOutfit( const Outfit *o, int quantity )
2510{
2511 if (outfit_isProp(o,OUTFIT_PROP_UNIQUE) && (player_outfitOwned(o)>0))
2512 return 1;
2513 return player_addOutfit(o,quantity);
2514}
2515
2519static int equipment_playerRmOutfit( const Outfit *o, int quantity )
2520{
2521 if (outfit_isProp(o,OUTFIT_PROP_UNIQUE))
2522 return 1;
2523 return player_rmOutfit(o,quantity);
2524}
2525
2530{
2531 /* Free stored positions. */
2532 for (int i=0; i<OUTFIT_TABS; i++)
2533 array_free( iar_outfits[i] );
2534 memset( iar_outfits, 0, sizeof(Outfit**) * OUTFIT_TABS );
2535
2537}
2538
2543{
2544 if (wgt==NULL)
2545 wgt = &eq_wgt;
2546 /* Safe defaults. */
2547 memset( wgt, 0, sizeof(CstSlotWidget) );
2548 wgt->slot = -1;
2549 wgt->mouseover = -1;
2550 wgt->weapons = -1;
2551}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:158
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:168
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:129
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
void credits2str(char *str, credits_t credits, int decimals)
Converts credits to a usable string for displaying.
Definition commodity.c:59
void dialogue_alert(const char *fmt,...)
Displays an alert popup with only an ok button and a message.
Definition dialogue.c:132
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:230
int dialogue_YesNo(const char *caption, const char *fmt,...)
Runs a dialogue with both yes and no options.
Definition dialogue.c:352
void equipment_slotDeselect(CstSlotWidget *wgt)
Deselects equipment stuff.
Definition equipment.c:2542
static int equipment_creating
Definition equipment.c:65
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:1783
static void equipment_rightClickShips(unsigned int wid, const char *str)
Toggles deployed status.
Definition equipment.c:2155
static double equipment_dir
Definition equipment.c:62
static int equipment_mouseInColumn(const PilotOutfitSlot *lst, double y, double h, double my)
Handles a mouse press in column.
Definition equipment.c:1004
static CstSlotWidget eq_wgt
Definition equipment.c:61
static int ship_mode
Definition equipment.c:66
static int equipment_playerAddOutfit(const Outfit *o, int quantity)
Wrapper to only add unique outfits.
Definition equipment.c:2509
static void equipment_shipMode(unsigned int wid, const char *str)
Toggles the ship visualization mode.
Definition equipment.c:2500
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:2127
static iar_data_t iar_data[OUTFIT_TABS]
Definition equipment.c:67
static void equipment_sellShip(unsigned int wid, const char *str)
Player tries to sell a ship.
Definition equipment.c:2404
static int equipment_playerRmOutfit(const Outfit *o, int quantity)
Wrapper to only remove unique outfits.
Definition equipment.c:2519
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:256
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:446
static void equipment_outfitPopdownSelect(unsigned int wid, const char *str)
Definition equipment.c:2072
void equipment_regenLists(unsigned int wid, int outfits, int ships)
Regenerates the equipment window lists.
Definition equipment.c:1261
static void equipment_renameShip(unsigned int wid, const char *str)
Renames the selected ship.
Definition equipment.c:2470
static void equipment_renderOverlayColumn(double x, double y, double h, PilotOutfitSlot *lst, int mover, CstSlotWidget *wgt)
Renders an outfit column.
Definition equipment.c:723
void equipment_cleanup(void)
Cleans up after the equipment stuff.
Definition equipment.c:2529
static void equipment_filterOutfits(unsigned int wid, const char *str)
Handles text input in the filter input widget.
Definition equipment.c:2062
static int equipment_outfitMode
Definition equipment.c:70
static void equipment_genLists(unsigned int wid)
Generates a new ship/outfit lists if needed.
Definition equipment.c:1501
static void equipment_rightClickOutfits(unsigned int wid, const char *str)
Handles right-click on unequipped outfit.
Definition equipment.c:122
void equipment_updateOutfits(unsigned int wid, const char *str)
Updates the player's outfit list.
Definition equipment.c:2041
static void equipment_transChangeShip(unsigned int wid, const char *str)
Changes ship.
Definition equipment.c:2192
#define BUTTON_HEIGHT
Definition equipment.c:56
static void equipment_genOutfitList(unsigned int wid)
Generates the outfit list.
Definition equipment.c:1663
static void equipment_toggleFav(unsigned int wid, const char *wgt)
Handles toggling of the favourite checkbox.
Definition equipment.c:1454
static void equipment_autoequipShip(unsigned int wid, const char *str)
Does the autoequip magic on the player's ship.
Definition equipment.c:2333
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:429
void equipment_updateShips(unsigned int wid, const char *str)
Updates the player's ship window.
Definition equipment.c:1801
static void equipment_renderShip(double bx, double by, double bw, double bh, void *data)
Renders the ship in the equipment window.
Definition equipment.c:914
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:565
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:1033
static void equipment_renderMisc(double bx, double by, double bw, double bh, void *data)
Renders the custom equipment widget.
Definition equipment.c:649
int equipment_canSwapPlayerShip(const char *shipname)
Makes sure it's valid to change ships in the equipment view.
Definition equipment.c:1348
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:1426
void equipment_addAmmo(void)
Adds all the ammo it can to the player.
Definition equipment.c:1400
static int equipment_swapSlot(unsigned int wid, Pilot *p, PilotOutfitSlot *slot)
Swaps an equipment slot.
Definition equipment.c:1176
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:1771
static void equipment_changeShip(unsigned int wid)
Player attempts to change ship.
Definition equipment.c:2205
int equipment_canSellPlayerShip(const char *shipname)
Makes sure it's valid to sell a ship.
Definition equipment.c:1332
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:1101
static void equipment_unequipShip(unsigned int wid, const char *str)
Unequips the player's ship.
Definition equipment.c:2261
static unsigned int equipment_wid
Definition equipment.c:64
void equipment_open(unsigned int wid)
Opens the player's equipment window.
Definition equipment.c:299
static Outfit ** iar_outfits[OUTFIT_TABS]
Definition equipment.c:68
static void equipment_renderSlots(double bx, double by, double bw, double bh, void *data)
Renders the equipment slots.
Definition equipment.c:594
static void equipment_genShipList(unsigned int wid)
Generates the ship list.
Definition equipment.c:1511
static unsigned int equipment_lastick
Definition equipment.c:63
static void equipment_renderOverlaySlots(double bx, double by, double bw, double bh, void *data)
Renders the equipment overlay.
Definition equipment.c:813
int escort_clearDeployed(Pilot *p)
Clears deployed escorts of a pilot.
Definition escort.c:228
int gl_printHeightRaw(const glFont *ft_font, const int width, const char *text)
Gets the height of a non-formatted string.
Definition font.c:1027
glFont gl_smallFont
Definition font.c:154
int gl_printWidthRaw(const glFont *ft_font, const char *text)
Gets the width that it would take to print some text.
Definition font.c:961
glFont gl_defFont
Definition font.c:153
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:788
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:1086
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:721
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:835
void gui_setShip(void)
Player just upgraded their ship or modified it.
Definition gui.c:1791
int hooks_runParam(const char *stack, const HookParam *param)
Runs all the hooks of stack.
Definition hook.c:979
int hooks_run(const char *stack)
Runs all the hooks of stack.
Definition hook.c:999
Handles the info menu.
void land_errDialogueBuild(const char *fmt,...)
Generates error dialogues used by several landing tabs.
Definition land.c:208
Spob * land_spob
Definition land.c:83
void land_errClear(void)
Clear error dialogues.
Definition land.c:199
void land_buttonTakeoff(unsigned int wid, const char *unused)
Wrapper for takeoff mission button.
Definition land.c:851
void land_refuel(void)
Refuels the player's current ship, if possible.
Definition land.c:733
int land_errDisplay(void)
Displays an error if applicable.
Definition land.c:228
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.
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition naev.h:40
#define CLAMP(a, b, x)
Definition naev.h:41
#define MAX(x, y)
Definition naev.h:39
#define PATH_MAX
Definition naev.h:50
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
Definition ndata.c:154
int nlua_loadStandard(nlua_env env)
Loads the standard Naev Lua API.
Definition nlua.c:798
LuaPilot * lua_pushpilot(lua_State *L, LuaPilot pilot)
Pushes a pilot on the stack.
Definition nlua_pilot.c:563
int nlua_loadTk(nlua_env env)
Loads the Toolkit Lua library.
Definition nlua_tk.c:93
int num2str(char dest[NUM2STRLEN], double n, int decimals)
Converts a numeric value to a string.
Definition nstring.c:120
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:99
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:173
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:188
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_renderScaleSprite(const glTexture *sprite, double bx, double by, int sx, int sy, double bw, double bh, const glColour *c)
Blits a scaled sprite, position is in absolute screen coordinates.
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_renderCross(double x, double y, double r, const glColour *c)
Renders a cross at a given position.
glTexture * gl_dupTexture(const glTexture *texture)
Duplicates a texture.
Definition opengl_tex.c:917
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:675
void gl_getSpriteFromDir(int *x, int *y, const glTexture *t, const double dir)
Sets x and y to be the appropriate sprite for glTexture using dir.
Definition opengl_tex.c:967
int outfit_isLauncher(const Outfit *o)
Checks if outfit is a weapon launcher.
Definition outfit.c:564
char outfit_slotTypeColourFont(const OutfitSlot *os)
Gets a font colour character that roughly matches an outfit slot type colour.
Definition outfit.c:425
int outfit_fitsSlot(const Outfit *o, const OutfitSlot *s)
Checks to see if an outfit fits a slot.
Definition outfit.c:1047
int outfit_isFighterBay(const Outfit *o)
Checks if outfit is a fighter bay.
Definition outfit.c:616
char outfit_slotSizeColourFont(const OutfitSlot *os)
Gets a font colour character that roughly matches an outfit slot size colour.
Definition outfit.c:408
int outfit_amount(const Outfit *o)
Gets the amount an outfit can hold.
Definition outfit.c:746
const char * slotName(const OutfitSlotType type)
Definition outfit.c:335
const char * slotSize(const OutfitSlotSize o)
Gets the slot size as a string.
Definition outfit.c:358
const glColour * outfit_slotSizeColour(const OutfitSlot *os)
Gets the slot size colour for an outfit slot.
Definition outfit.c:391
ntime_t pilot_hyperspaceDelay(const Pilot *p)
Calculates the hyperspace delay for a pilot.
Definition pilot.c:2948
void pilot_dpseps(const Pilot *p, double *pdps, double *peps)
Calculates the dps and eps of a pilot.
Definition pilot.c:3994
int pilot_cargoFree(const Pilot *p)
Gets the pilot's free cargo space.
Definition pilot_cargo.c:51
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_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_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.
void pilot_weapSetAdd(Pilot *p, int id, const PilotOutfitSlot *o, int level)
Adds an outfit to a weapon set.
int player_nships(void)
Gets the amount of ships player has in storage.
Definition player.c:2654
int player_rmOutfit(const Outfit *o, int quantity)
Remove an outfit from the player's outfit stack.
Definition player.c:2890
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:2795
void player_swapShip(const char *shipname, int move_cargo)
Swaps player's current ship with their ship named shipname.
Definition player.c:526
int player_addOutfit(const Outfit *o, int quantity)
Adds an outfit to the player outfit stack.
Definition player.c:2828
int player_numOutfits(void)
Gets the amount of different outfits in the player outfit stack.
Definition player.c:2816
credits_t player_shipPrice(const char *shipname, int count_unique)
Calculates the price of one of the player's ships.
Definition player.c:667
const PlayerShip_t * player_getShipStack(void)
Gets the array (array.h) of the player's ships.
Definition player.c:2644
void player_rmShip(const char *shipname)
Removes one of the player's ships.
Definition player.c:705
PlayerShip_t * player_getPlayerShip(const char *shipname)
Gets a specific ship.
Definition player.c:2703
credits_t player_modCredits(credits_t amount)
Modifies the amount of credits the player has.
Definition player.c:975
int player_hasShip(const char *shipname)
Sees if player has a ship of a name.
Definition player.c:2665
Player_t player
Definition player.c:74
void player_shipsSort(void)
Sorts the players ships.
Definition player.c:2610
int player_outfitOwned(const Outfit *o)
Gets how many of the outfit the player owns.
Definition player.c:2722
Pilot * player_getShip(const char *shipname)
Gets a specific ship.
Definition player.c:2684
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:264
const char * ship_classDisplay(const Ship *s)
Gets the ship's display class in human readable form.
Definition ship.c:176
int ss_statsListDesc(const ShipStatList *ll, char *buf, int len, int newline)
Writes the ship statistics description.
Definition shipstats.c:780
int ss_statsDesc(const ShipStats *s, char *buf, int len, int newline)
Writes the ship statistics description.
Definition shipstats.c:824
const char * sp_description(unsigned int spid)
Gets the description of a slot property (in English).
Definition slots.c:169
const glTexture * sp_icon(unsigned int spid)
Gets the icon associated with the slot.
Definition slots.c:209
const char * sp_display(unsigned int spid)
Gets the display name of a slot property (in English).
Definition slots.c:159
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:38
const char * str
Definition hook.h:42
HookParamType type
Definition hook.h:39
union HookParam::@25 u
const Ship * ship
Definition hook.h:45
OutfitSlotSize size
Definition outfit.h:113
unsigned int spid
Definition outfit.h:110
OutfitSlotType type
Definition outfit.h:112
A ship outfit, depends radically on the type.
Definition outfit.h:328
char * limit
Definition outfit.h:342
OutfitSlot slot
Definition outfit.h:336
Stores an outfit the pilot has.
Definition pilot.h:108
PilotOutfitAmmo ammo
Definition pilot.h:135
ShipStatList * lua_stats
Definition pilot.h:140
ShipOutfitSlot * sslot
Definition pilot.h:114
const Outfit * outfit
Definition pilot.h:112
The representation of an in-game pilot.
Definition pilot.h:217
ShipStats stats
Definition pilot.h:294
double accel
Definition pilot.h:242
unsigned int id
Definition pilot.h:218
PilotWeaponSet weapon_sets[PILOT_WEAPON_SETS]
Definition pilot.h:319
double crew
Definition pilot.h:238
double ew_stealth
Definition pilot.h:273
PilotOutfitSlot * outfit_structure
Definition pilot.h:301
double energy_regen
Definition pilot.h:266
PilotOutfitSlot ** outfits
Definition pilot.h:300
PilotOutfitSlot * outfit_utility
Definition pilot.h:302
PilotOutfitSlot * outfit_intrinsic
Definition pilot.h:304
double armour_max
Definition pilot.h:254
double speed
Definition pilot.h:244
const Ship * ship
Definition pilot.h:226
double fuel_max
Definition pilot.h:259
double ew_detection
Definition pilot.h:271
int autoweap
Definition pilot.h:321
double energy_max
Definition pilot.h:265
Solid solid
Definition pilot.h:227
char * name
Definition pilot.h:219
double fuel_consumption
Definition pilot.h:261
double shield_regen
Definition pilot.h:257
double turn
Definition pilot.h:247
PilotOutfitSlot * outfit_weapon
Definition pilot.h:303
double shield_max
Definition pilot.h:255
double dmg_absorb
Definition pilot.h:258
double ew_signature
Definition pilot.h:272
double armour_regen
Definition pilot.h:256
int big_icons
Definition conf.h:125
Player ship.
Definition player.h:73
time_t acquired_date
Definition player.h:85
double time_played
Definition player.h:83
double dmg_done_armour
Definition player.h:87
double dmg_taken_armour
Definition player.h:89
int deployed
Definition player.h:80
double dmg_taken_shield
Definition player.h:88
unsigned int landed_times
Definition player.h:92
Pilot * p
Definition player.h:74
unsigned int ships_destroyed[SHIP_CLASS_TOTAL]
Definition player.h:90
char * acquired
Definition player.h:84
unsigned int jumped_times
Definition player.h:91
PilotWeaponSet weapon_sets[PILOT_WEAPON_SETS]
Definition player.h:75
double dmg_done_shield
Definition player.h:86
int favourite
Definition player.h:77
Pilot * p
Definition player.h:101
PlayerShip_t ps
Definition player.h:102
int fleet_used
Definition player.h:125
int fleet_capacity
Definition player.h:126
int eq_outfitMode
Definition player.h:121
int exclusive
Definition ship.h:73
OutfitSlot slot
Definition ship.h:71
int locked
Definition ship.h:75
double time_mod
Definition shipstats.h:308
Represents a space ship.
Definition ship.h:94
double shield_regen
Definition ship.h:130
double dt_default
Definition ship.h:124
double cap_cargo
Definition ship.h:123
char * desc_extra
Definition ship.h:110
char * name
Definition ship.h:95
double energy_regen
Definition ship.h:132
double armour
Definition ship.h:127
double armour_regen
Definition ship.h:128
int crew
Definition ship.h:118
double dmg_absorb
Definition ship.h:133
int points
Definition ship.h:99
double speed
Definition ship.h:115
double turn
Definition ship.h:114
int rarity
Definition ship.h:100
double accel
Definition ship.h:113
glTexture * gfx_store
Definition ship.h:141
double energy
Definition ship.h:131
glTexture ** gfx_overlays
Definition ship.h:143
double shield
Definition ship.h:129
double mass
Definition physics.h:45
int h
Definition font.h:18
Abstraction for rendering sprite sheets.
Definition opengl_tex.h:36
uint8_t flags
Definition opengl_tex.h:57
Represents a 2d vector.
Definition vec2.h:32
void window_dimWidget(unsigned int wid, const char *name, int *w, int *h)
Gets the dimensions of a widget.
Definition toolkit.c:416
void window_setFocus(unsigned int wid, const char *wgtname)
Sets the focused widget in a window.
Definition toolkit.c:2471
void window_dimWindow(unsigned int wid, int *w, int *h)
Gets the dimensions of a window.
Definition toolkit.c:371
int window_existsID(unsigned int wid)
Checks to see if a window with a certain ID exists.
Definition toolkit.c:612
void toolkit_drawAltText(int bx, int by, const char *alt)
Draws an alt text.
Definition toolkit.c:1429
void window_canFocusWidget(unsigned int wid, const char *name, int canfocus)
Allows or disallows focusing a widget.
Definition toolkit.c:521
void toolkit_drawRect(int x, int y, int w, int h, const glColour *c, const glColour *lc)
Draws a rectangle.
Definition toolkit.c:1343
void window_moveWidget(unsigned int wid, const char *name, int x, int y)
Moves a widget.
Definition toolkit.c:465
void window_posWidget(unsigned int wid, const char *name, int *x, int *y)
Gets a widget's position.
Definition toolkit.c:442
void window_destroyWidget(unsigned int wid, const char *wgtname)
Destroys a widget in a window.
Definition toolkit.c:1165
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:566
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:1216
int window_isTop(unsigned int wid)
Checks to see if a window is at the top.
Definition toolkit.c:549
int widget_exists(unsigned int wid, const char *wgtname)
Checks to see if a widget exists.
Definition toolkit.c:1142
char * window_getFocus(unsigned int wid)
Gets the focused widget in a window (does strdup!!).
Definition toolkit.c:2497