1 /*
2 * See Licensing and Copyright notice in naev.h
3 */
4
5 /**
6 * @file land_shipyard.c
7 *
8 * @brief Handles the shipyard at land.
9 */
10
11
12 #include "land_shipyard.h"
13
14 #include "naev.h"
15
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include "nstring.h"
19 #include <math.h>
20 #include <assert.h>
21 #include "log.h"
22 #include "player.h"
23 #include "space.h"
24 #include "toolkit.h"
25 #include "tk/toolkit_priv.h"
26 #include "dialogue.h"
27 #include "map_find.h"
28
29
30 /*
31 * Vars.
32 */
33 static Ship* shipyard_selected = NULL; /**< Currently selected shipyard ship. */
34
35
36 /*
37 * Helper functions.
38 */
39 static void shipyard_buy( unsigned int wid, char* str );
40 static void shipyard_trade( unsigned int wid, char* str );
41 static void shipyard_rmouse( unsigned int wid, char* widget_name );
42 static void shipyard_renderSlots( double bx, double by, double bw, double bh, void *data );
43 static void shipyard_renderSlotsRow( double bx, double by, double bw, char *str, ShipOutfitSlot *s, int n );
44 static void shipyard_find( unsigned int wid, char* str );
45
46
47 /**
48 * @brief Opens the shipyard window.
49 */
shipyard_open(unsigned int wid)50 void shipyard_open( unsigned int wid )
51 {
52 int i;
53 Ship **ships;
54 char **sships;
55 glTexture **tships;
56 int nships;
57 int w, h;
58 int iw, ih;
59 int bw, bh, padding, off;
60 int th;
61 int y;
62 const char *buf;
63
64 /* Mark as generated. */
65 land_tabGenerate(LAND_WINDOW_SHIPYARD);
66
67 /* Init vars. */
68 shipyard_selected = NULL;
69
70 /* Get window dimensions. */
71 window_dimWindow( wid, &w, &h );
72
73 /* Calculate image array dimensions. */
74 iw = 310 + (w-800);
75 ih = h - 60;
76
77 /* Left padding + per-button padding * nbuttons */
78 padding = 40 + 20 * 4;
79
80 /* Calculate button dimensions. */
81 bw = (w - iw - padding) / 4;
82 bh = LAND_BUTTON_HEIGHT;
83
84 /* buttons */
85 window_addButtonKey( wid, off = -20, 20,
86 bw, bh, "btnCloseShipyard",
87 "Take Off", land_buttonTakeoff, SDLK_t );
88 window_addButtonKey( wid, off -= 20+bw, 20,
89 bw, bh, "btnTradeShip",
90 "Trade-In", shipyard_trade, SDLK_r );
91 window_addButtonKey( wid, off -= 20+bw, 20,
92 bw, bh, "btnBuyShip",
93 "Buy", shipyard_buy, SDLK_b );
94 window_addButtonKey( wid, off -= 20+bw, 20,
95 bw, bh, "btnFindShips",
96 "Find Ships", shipyard_find, SDLK_f );
97
98 /* target gfx */
99 window_addRect( wid, -41, -50,
100 SHIP_TARGET_W, SHIP_TARGET_H, "rctTarget", &cBlack, 0 );
101 window_addImage( wid, -40, -50,
102 SHIP_TARGET_W, SHIP_TARGET_H, "imgTarget", NULL, 1 );
103
104 /* slot types */
105 window_addCust( wid, -20, -160, 148, 70, "cstSlots", 0.,
106 shipyard_renderSlots, NULL, NULL );
107
108 /* stat text */
109 window_addText( wid, -40, -240, 128, 200, 0, "txtStats",
110 &gl_smallFont, &cBlack, NULL );
111
112 /* text */
113 buf = "Model:\n"
114 "Class:\n"
115 "Fabricator:\n"
116 "Crew:\n"
117 "\n"
118 "CPU:\n"
119 "Mass:\n"
120 "Thrust:\n"
121 "Speed:\n"
122 "Turn:\n"
123 "\n"
124 "Absorption:\n"
125 "Shield:\n"
126 "Armour:\n"
127 "Energy:\n"
128 "Cargo Space:\n"
129 "Fuel:\n"
130 "Fuel Use:\n"
131 "Price:\n"
132 "Money:\n"
133 "License:\n";
134 th = gl_printHeightRaw( &gl_smallFont, 100, buf );
135 y = -55;
136 window_addText( wid, 40+iw+20, y,
137 100, th, 0, "txtSDesc", &gl_smallFont, &cDConsole, buf );
138 window_addText( wid, 40+iw+20+100, y,
139 w-(40+iw+20+100)-20, th, 0, "txtDDesc", &gl_smallFont, &cBlack, NULL );
140 y -= th;
141 window_addText( wid, 20+iw+40, y,
142 w-(20+iw+40) - 180, 185, 0, "txtDescription",
143 &gl_smallFont, NULL, NULL );
144
145 /* set up the ships to buy/sell */
146 ships = tech_getShip( land_planet->tech, &nships );
147 if (nships <= 0) {
148 sships = malloc(sizeof(char*));
149 sships[0] = strdup("None");
150 tships = malloc(sizeof(glTexture*));
151 tships[0] = NULL;
152 nships = 1;
153 }
154 else {
155 sships = malloc(sizeof(char*)*nships);
156 tships = malloc(sizeof(glTexture*)*nships);
157 for (i=0; i<nships; i++) {
158 sships[i] = strdup(ships[i]->name);
159 tships[i] = ships[i]->gfx_store;
160 }
161 free(ships);
162 }
163 window_addImageArray( wid, 20, 20,
164 iw, ih, "iarShipyard", 64./96.*128., 64.,
165 tships, sships, nships, shipyard_update, shipyard_rmouse );
166
167 /* write the shipyard stuff */
168 shipyard_update(wid, NULL);
169 /* Set default keyboard focuse to the list */
170 window_setFocus( wid , "iarShipyard" );
171 }
172 /**
173 * @brief Updates the ships in the shipyard window.
174 * @param wid Window to update the ships in.
175 * @param str Unused.
176 */
shipyard_update(unsigned int wid,char * str)177 void shipyard_update( unsigned int wid, char* str )
178 {
179 (void)str;
180 char *shipname, *license_text;
181 Ship* ship;
182 char buf[PATH_MAX], buf2[ECON_CRED_STRLEN], buf3[ECON_CRED_STRLEN];
183 size_t len;
184
185 shipname = toolkit_getImageArray( wid, "iarShipyard" );
186
187 /* No ships */
188 if (strcmp(shipname,"None")==0) {
189 window_modifyImage( wid, "imgTarget", NULL, 0, 0 );
190 window_disableButton( wid, "btnBuyShip");
191 window_disableButton( wid, "btnTradeShip");
192 nsnprintf( buf, PATH_MAX,
193 "None\n"
194 "NA\n"
195 "NA\n"
196 "NA\n"
197 "\n"
198 "NA\n"
199 "NA\n"
200 "NA\n"
201 "NA\n"
202 "NA\n"
203 "\n"
204 "NA\n"
205 "NA\n"
206 "NA\n"
207 "NA\n"
208 "NA\n"
209 "NA\n"
210 "NA\n"
211 "NA\n"
212 "NA\n"
213 "NA\n" );
214 window_modifyImage( wid, "imgTarget", NULL, 0, 0 );
215 window_modifyText( wid, "txtStats", NULL );
216 window_modifyText( wid, "txtDescription", NULL );
217 window_modifyText( wid, "txtDDesc", buf );
218 return;
219 }
220
221 ship = ship_get( shipname );
222 shipyard_selected = ship;
223
224 /* update image */
225 window_modifyImage( wid, "imgTarget", ship->gfx_store, 0, 0 );
226
227 /* update text */
228 window_modifyText( wid, "txtStats", ship->desc_stats );
229 window_modifyText( wid, "txtDescription", ship->description );
230 price2str( buf2, ship_buyPrice(ship), player.p->credits, 2 );
231 credits2str( buf3, player.p->credits, 2 );
232
233 /* Remove the word " License". It's redundant and makes the text overflow
234 into another text box */
235 license_text = ship->license;
236 if (license_text) {
237 len = strlen(ship->license);
238 if (strcmp(" License", ship->license + len - 8) == 0) {
239 license_text = malloc(len - 7);
240 assert(license_text);
241 memcpy(license_text, ship->license, len - 8);
242 license_text[len - 8] = '\0';
243 }
244 }
245 nsnprintf( buf, PATH_MAX,
246 "%s\n"
247 "%s\n"
248 "%s\n"
249 "%d\n"
250 "\n"
251 "%.0f teraflops\n"
252 "%.0f tons\n"
253 "%.0f kN/ton\n"
254 "%.0f m/s\n"
255 "%.0f deg/s\n"
256 "\n"
257 "%.0f%% damage\n"
258 "%.0f MJ (%.1f MW)\n"
259 "%.0f MJ (%.1f MW)\n"
260 "%.0f MJ (%.1f MW)\n"
261 "%.0f tons\n"
262 "%d units\n"
263 "%.0f units\n"
264 "%s credits\n"
265 "%s credits\n"
266 "%s\n",
267 ship->name,
268 ship_class(ship),
269 ship->fabricator,
270 ship->crew,
271 /* Weapons & Manoeuvrability */
272 ship->cpu,
273 ship->mass,
274 ship->thrust,
275 ship->speed,
276 ship->turn*180/M_PI,
277 /* Misc */
278 ship->dmg_absorb*100.,
279 ship->shield, ship->shield_regen,
280 ship->armour, ship->armour_regen,
281 ship->energy, ship->energy_regen,
282 ship->cap_cargo,
283 ship->fuel,
284 ship->fuel_consumption,
285 buf2,
286 buf3,
287 (license_text != NULL) ? license_text : "None" );
288 window_modifyText( wid, "txtDDesc", buf );
289
290 if (license_text != ship->license)
291 free(license_text);
292
293 if (!shipyard_canBuy( shipname, land_planet ))
294 window_disableButtonSoft( wid, "btnBuyShip");
295 else
296 window_enableButton( wid, "btnBuyShip");
297
298 if (!shipyard_canTrade( shipname ))
299 window_disableButtonSoft( wid, "btnTradeShip");
300 else
301 window_enableButton( wid, "btnTradeShip");
302 }
303
304
305 /**
306 * @brief Starts the map find with ship search selected.
307 * @param wid Window buying outfit from.
308 * @param str Unused.
309 */
shipyard_find(unsigned int wid,char * str)310 static void shipyard_find( unsigned int wid, char* str )
311 {
312 (void) str;
313 map_inputFindType(wid, "ship");
314 }
315
316
317 /**
318 * @brief Player right-clicks on a ship.
319 * @param wid Window player is buying ship from.
320 * @param widget_name Name of the window. (unused)
321 * @param shipname Name of the ship the player wants to buy. (unused)
322 */
shipyard_rmouse(unsigned int wid,char * widget_name)323 static void shipyard_rmouse( unsigned int wid, char* widget_name )
324 {
325 return shipyard_buy(wid, widget_name);
326 }
327
328
329 /**
330 * @brief Player attempts to buy a ship.
331 * @param wid Window player is buying ship from.
332 * @param str Unused.
333 */
shipyard_buy(unsigned int wid,char * str)334 static void shipyard_buy( unsigned int wid, char* str )
335 {
336 (void)str;
337 char *shipname, buf[ECON_CRED_STRLEN];
338 Ship* ship;
339
340 shipname = toolkit_getImageArray( wid, "iarShipyard" );
341 if (strcmp(shipname, "None") == 0)
342 return;
343
344 ship = ship_get( shipname );
345
346 credits_t targetprice = ship_buyPrice(ship);
347
348 if (land_errDialogue( shipname, "buyShip" ))
349 return;
350
351 credits2str( buf, targetprice, 2 );
352 if (dialogue_YesNo("Are you sure?", /* confirm */
353 "Do you really want to spend %s on a new ship?", buf )==0)
354 return;
355
356 /* player just got a new ship */
357 if (player_newShip( ship, NULL, 0, 0 ) == NULL) {
358 /* Player actually aborted naming process. */
359 return;
360 }
361 player_modCredits( -targetprice ); /* ouch, paying is hard */
362
363 /* Update shipyard. */
364 shipyard_update(wid, NULL);
365 }
366
367 /**
368 * @brief Makes sure it's sane to buy a ship.
369 * @param shipname Ship being bought.
370 */
shipyard_canBuy(char * shipname,Planet * planet)371 int shipyard_canBuy ( char *shipname, Planet *planet )
372 {
373 Ship* ship;
374 ship = ship_get( shipname );
375 int failure = 0;
376 credits_t price;
377
378 price = ship_buyPrice(ship);
379
380 /* Must have enough credits and the necessary license. */
381 if ((!player_hasLicense(ship->license)) &&
382 ((planet == NULL) || (!planet_hasService(planet, PLANET_SERVICE_BLACKMARKET)))) {
383 land_errDialogueBuild( "You lack the %s.", ship->license );
384 failure = 1;
385 }
386 if (!player_hasCredits( price )) {
387 char buf[ECON_CRED_STRLEN];
388 credits2str( buf, price - player.p->credits, 2 );
389 land_errDialogueBuild( "You need %s more credits.", buf);
390 failure = 1;
391 }
392 return !failure;
393 }
394
395 /**
396 * @brief Makes sure it's sane to sell a ship.
397 * @param shipname Ship being sold.
398 */
can_sell(char * shipname)399 int can_sell( char* shipname )
400 {
401 int failure = 0;
402 if (strcmp( shipname, player.p->name )==0) { /* Already on-board. */
403 land_errDialogueBuild( "You can't sell the ship you're piloting!", shipname );
404 failure = 1;
405 }
406
407 return !failure;
408 }
409
410 /**
411 * @brief Makes sure it's sane to change ships.
412 * @param shipname Ship being changed to.
413 */
can_swap(char * shipname)414 int can_swap( char* shipname )
415 {
416 int failure = 0;
417 Ship* ship;
418 ship = ship_get( shipname );
419
420 if (pilot_cargoUsed(player.p) > ship->cap_cargo) { /* Current ship has too much cargo. */
421 land_errDialogueBuild( "You have %g tons more cargo than the new ship can hold.",
422 pilot_cargoUsed(player.p) - ship->cap_cargo, ship->name );
423 failure = 1;
424 }
425 if (pilot_hasDeployed(player.p)) { /* Escorts are in space. */
426 land_errDialogueBuild( "You can't strand your fighters in space.");
427 failure = 1;
428 }
429 return !failure;
430 }
431
432
433 /**
434 * @brief Makes sure it's sane to buy a ship, trading the old one in simultaneously.
435 * @param shipname Ship being bought.
436 */
shipyard_canTrade(char * shipname)437 int shipyard_canTrade( char* shipname )
438 {
439 int failure = 0;
440 Ship* ship;
441 ship = ship_get( shipname );
442 credits_t price;
443
444 price = ship_buyPrice( ship );
445
446 /* Must have the necessary license, enough credits, and be able to swap ships. */
447 if (!player_hasLicense(ship->license)) {
448 land_errDialogueBuild( "You lack the %s.", ship->license );
449 failure = 1;
450 }
451 if (!player_hasCredits( price - player_shipPrice(player.p->name))) {
452 credits_t creditdifference = price - (player_shipPrice(player.p->name) + player.p->credits);
453 char buf[ECON_CRED_STRLEN];
454 credits2str( buf, creditdifference, 2 );
455 land_errDialogueBuild( "You need %s more credits.", buf);
456 failure = 1;
457 }
458 if (!can_swap( shipname ))
459 failure = 1;
460 return !failure;
461 }
462
463
464 /**
465 * @brief Player attempts to buy a ship, trading the current ship in.
466 * @param wid Window player is buying ship from.
467 * @param str Unused.
468 */
shipyard_trade(unsigned int wid,char * str)469 static void shipyard_trade( unsigned int wid, char* str )
470 {
471 (void)str;
472 char *shipname, buf[ECON_CRED_STRLEN], buf2[ECON_CRED_STRLEN],
473 buf3[ECON_CRED_STRLEN], buf4[ECON_CRED_STRLEN];
474 Ship* ship;
475
476 shipname = toolkit_getImageArray( wid, "iarShipyard" );
477 if (strcmp(shipname, "None") == 0)
478 return;
479
480 ship = ship_get( shipname );
481
482 credits_t targetprice = ship_buyPrice(ship);
483 credits_t playerprice = player_shipPrice(player.p->name);
484
485 if (land_errDialogue( shipname, "tradeShip" ))
486 return;
487
488 credits2str( buf, targetprice, 2 );
489 credits2str( buf2, playerprice, 2 );
490 credits2str( buf3, targetprice - playerprice, 2 );
491 credits2str( buf4, playerprice - targetprice, 2 );
492
493 /* Display the correct dialogue depending on the new ship's price versus the player's. */
494 if ( targetprice == playerprice ) {
495 if (dialogue_YesNo("Are you sure?", /* confirm */
496 "Your %s is worth %s, exactly as much as the new ship, so no credits need be exchanged. Are you sure you want to trade your ship in?",
497 player.p->ship->name, buf2)==0)
498 return;
499 }
500 else if ( targetprice < playerprice ) {
501 if (dialogue_YesNo("Are you sure?", /* confirm */
502 "Your %s is worth %s credits, more than the new ship. For your ship, you will get the new %s and %s credits. Are you sure you want to trade your ship in?",
503 player.p->ship->name, buf2, ship->name, buf4)==0)
504 return;
505 }
506 else if ( targetprice > playerprice ) {
507 if (dialogue_YesNo("Are you sure?", /* confirm */
508 "Your %s is worth %s, so the new ship will cost %s credits. Are you sure you want to trade your ship in?",
509 player.p->ship->name, buf2, buf3)==0)
510 return;
511 }
512
513 /* player just got a new ship */
514 if (player_newShip( ship, NULL, 1, 0 ) == NULL)
515 return; /* Player aborted the naming process. */
516
517 player_modCredits( playerprice - targetprice ); /* Modify credits by the difference between ship values. */
518
519 land_refuel();
520
521 /* The newShip call will trigger a loadGUI that will recreate the land windows. Therefore the land ID will
522 * be void. We must reload in in order to properly update it again.*/
523 wid = land_getWid(LAND_WINDOW_SHIPYARD);
524
525 /* Update shipyard. */
526 shipyard_update(wid, NULL);
527 }
528
529
530 /**
531 * @brief Custom widget render function for the slot widget.
532 */
shipyard_renderSlots(double bx,double by,double bw,double bh,void * data)533 static void shipyard_renderSlots( double bx, double by, double bw, double bh, void *data )
534 {
535 (void) data;
536 double x, y, w;
537 Ship *ship;
538
539 /* Make sure a valid ship is selected. */
540 ship = shipyard_selected;
541 if (ship == NULL)
542 return;
543
544 y = by + bh;
545
546 /* Draw rotated text. */
547 y -= 10;
548 gl_print( &gl_smallFont, bx, y, &cBlack, "Slots:" );
549
550 x = bx + 10.;
551 w = bw - 10.;
552
553 /* Weapon slots. */
554 y -= 20;
555 shipyard_renderSlotsRow( x, y, w, "W", ship->outfit_weapon, ship->outfit_nweapon );
556
557 /* Utility slots. */
558 y -= 20;
559 shipyard_renderSlotsRow( x, y, w, "U", ship->outfit_utility, ship->outfit_nutility );
560
561 /* Structure slots. */
562 y -= 20;
563 shipyard_renderSlotsRow( x, y, w, "S", ship->outfit_structure, ship->outfit_nstructure );
564 }
565
566
567 /**
568 * @brief Renders a row of ship slots.
569 */
shipyard_renderSlotsRow(double bx,double by,double bw,char * str,ShipOutfitSlot * s,int n)570 static void shipyard_renderSlotsRow( double bx, double by, double bw, char *str, ShipOutfitSlot *s, int n )
571 {
572 (void) bw;
573 int i;
574 double x;
575 const glColour *c;
576
577 x = bx;
578
579 /* Print text. */
580 gl_print( &gl_smallFont, bx, by, &cBlack, str );
581
582 /* Draw squares. */
583 for (i=0; i<n; i++) {
584 c = outfit_slotSizeColour( &s[i].slot );
585 if (c == NULL)
586 c = &cBlack;
587
588 x += 15.;
589 toolkit_drawRect( x, by, 10, 10, c, NULL );
590 }
591 }
592
593
594
595
596
597