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