Merge branch 'master' of git://github.com/BTAxis/naev into testmission
[naev.git] / src / comm.c
blob978986414e91df713e3561ee306875a581d9b220
1 /*
2 * See Licensing and Copyright notice in naev.h
3 */
6 /**
7 * @file comm.c
9 * @brief For communicating with planets/pilots.
13 #include "comm.h"
15 #include "naev.h"
17 #include "log.h"
18 #include "toolkit.h"
19 #include "dialogue.h"
20 #include "pilot.h"
21 #include "rng.h"
22 #include "nlua.h"
23 #include "player.h"
24 #include "opengl.h"
25 #include "ai.h"
26 #include "ai_extra.h"
29 #define BUTTON_WIDTH 80 /**< Button width. */
30 #define BUTTON_HEIGHT 30 /**< Button height. */
32 #define GRAPHIC_WIDTH 256 /**< Width of graphic. */
33 #define GRAPHIC_HEIGHT 256 /**< Height of graphic. */
36 static Pilot *comm_pilot = NULL; /**< Pilot currently talking to. */
37 static Planet *comm_planet = NULL; /**< Planet currently talking to. */
38 static glTexture *comm_graphic = NULL; /**< Pilot's graphic. */
41 /* We need direct pilot access. */
42 extern Pilot** pilot_stack;
43 extern int pilot_nstack;
47 * Prototypes.
49 /* Static. */
50 static unsigned int comm_open( glTexture *gfx, int faction,
51 int override, int bribed, char *name );
52 static unsigned int comm_openPilotWindow (void);
53 static void comm_addPilotSpecialButtons( unsigned int wid );
54 static void comm_close( unsigned int wid, char *unused );
55 static void comm_bribePilot( unsigned int wid, char *unused );
56 static void comm_bribePlanet( unsigned int wid, char *unused );
57 static void comm_requestFuel( unsigned int wid, char *unused );
58 static int comm_getNumber( double *val, char* str );
59 static const char* comm_getString( char *str );
62 /**
63 * @brief Checks to see if comm is open.
65 * @return 1 if comm is open.
67 int comm_isOpen (void)
69 return window_exists( "Communication Channel" );
73 /**
74 * @brief Opens the communication dialogue with a pilot.
76 * @param pilot Pilot to communicate with.
77 * @return 0 on success.
79 int comm_openPilot( unsigned int pilot )
81 const char *msg;
82 char c;
83 unsigned int wid;
84 int run;
85 Pilot *p;
87 /* Get the pilot. */
88 p = pilot_get( pilot );
89 comm_pilot = p;
90 c = pilot_getFactionColourChar( p );
92 if (comm_pilot == NULL)
93 return -1;
95 /* Must not be jumping. */
96 if (pilot_isFlag(comm_pilot, PILOT_HYPERSPACE)) {
97 player_message("\e%c%s\er is jumping and can't respond.", c, comm_pilot->name);
98 return 0;
101 /* Must not be disabled. */
102 if (pilot_isFlag(comm_pilot, PILOT_DISABLED)) {
103 player_message("\e%c%s\er does not respond.", c, comm_pilot->name);
104 return 0;
107 /* Check to see if pilot wants to communicate. */
108 msg = comm_getString( "comm_no" );
109 if (msg != NULL) {
110 player_message( msg );
111 return 0;
114 /* Set up for the comm_get* functions. */
115 ai_setPilot( comm_pilot );
117 /* Have pilot stop hailing. */
118 pilot_rmFlag( comm_pilot, PILOT_HAILING );
120 /* Create the pilot window. */
121 wid = comm_openPilotWindow();
123 /* Run hooks if needed. */
124 run = pilot_runHook( comm_pilot, PILOT_HOOK_HAIL );
125 if (run > 0) {
126 /* Reopen window in case something changed. */
127 comm_close( wid, NULL );
128 comm_pilot = p;
129 comm_openPilotWindow();
132 return 0;
137 * @brief Creates the pilot window.
139 static unsigned int comm_openPilotWindow (void)
141 unsigned int wid;
143 /* Create the generic comm window. */
144 wid = comm_open( ship_loadCommGFX( comm_pilot->ship ),
145 comm_pilot->faction,
146 pilot_isHostile(comm_pilot) ? -1 : pilot_isFriendly(comm_pilot) ? 1 : 0,
147 pilot_isFlag(comm_pilot, PILOT_BRIBED),
148 comm_pilot->name );
150 /* Add special buttons. */
151 comm_addPilotSpecialButtons( wid );
153 return wid;
158 * @brief Adds the pilot special buttons to a window.
160 * @param wid Window to add pilot special buttons to.
162 static void comm_addPilotSpecialButtons( unsigned int wid )
164 window_addButton( wid, -20, 20 + BUTTON_HEIGHT + 20,
165 BUTTON_WIDTH, BUTTON_HEIGHT, "btnGreet", "Greet", NULL );
166 if (!pilot_isFlag(comm_pilot, PILOT_BRIBED) && /* Not already bribed. */
167 ((faction_getPlayer( comm_pilot->faction ) < 0) || /* Hostile. */
168 pilot_isHostile(comm_pilot)))
169 window_addButton( wid, -20, 20 + 2*BUTTON_HEIGHT + 40,
170 BUTTON_WIDTH, BUTTON_HEIGHT, "btnBribe", "Bribe", comm_bribePilot );
171 else
172 window_addButton( wid, -20, 20 + 2*BUTTON_HEIGHT + 40,
173 BUTTON_WIDTH, BUTTON_HEIGHT, "btnRequest",
174 "Refuel", comm_requestFuel );
179 * @brief Opens a communication dialogue with a planet.
181 * @param planet Planet to communicate with.
182 * @return 0 on success.
184 int comm_openPlanet( Planet *planet )
186 unsigned int wid;
188 /* Must not be disabled. */
189 if (!planet_hasService(planet, PLANET_SERVICE_INHABITED)) {
190 player_message("%s does not respond.", planet->name);
191 return 0;
194 comm_planet = planet;
196 /* Create the generic comm window. */
197 wid = comm_open( gl_dupTexture( comm_planet->gfx_space ),
198 comm_planet->faction, 0, 0, comm_planet->name );
200 /* Add special buttons. */
201 if (areEnemies(player->faction, planet->faction) &&
202 !planet->bribed)
203 window_addButton( wid, -20, 20 + BUTTON_HEIGHT + 20,
204 BUTTON_WIDTH, BUTTON_HEIGHT, "btnBribe", "Bribe", comm_bribePlanet );
206 return 0;
211 * @brief Sets up the comm window.
213 * @param gfx Graphic to use for the comm window (is freed).
214 * @param faction Faction of what you're communicating with.
215 * @param override If positive sets to ally, if negative sets to hostile.
216 * @param name Name of object talking to.
217 * @return The comm window id.
219 static unsigned int comm_open( glTexture *gfx, int faction,
220 int override, int bribed, char *name )
222 int x,y, w;
223 glTexture *logo;
224 char *stand;
225 unsigned int wid;
226 glColour *c;
228 /* Clean up. */
229 if (comm_graphic != NULL) {
230 /* First clean up if needed. */
231 gl_freeTexture(comm_graphic);
232 comm_graphic = NULL;
235 /* Get faction details. */
236 comm_graphic = gfx;
237 logo = faction_logoSmall(faction);
239 /* Get standing colour / text. */
240 if (bribed) {
241 stand = "Neutral";
242 c = &cNeutral;
244 else if (override < 0) {
245 stand = "Hostile";
246 c = &cHostile;
248 else if (override > 0) {
249 stand = "Friendly";
250 c = &cFriend;
252 else {
253 stand = faction_getStandingBroad(faction_getPlayer( faction ));
254 c = faction_getColour( faction );
256 w = MAX(gl_printWidth( NULL, name ), gl_printWidth( NULL, stand ));
257 y = gl_defFont.h*2 + 15;
258 if (logo != NULL) {
259 w += logo->w;
260 y = MAX( y, logo->h );
262 x = (GRAPHIC_WIDTH - w) / 2;
264 /* Create the window. */
265 wid = window_create( "Communication Channel", -1, -1,
266 20 + GRAPHIC_WIDTH + 20 + BUTTON_WIDTH + 20,
267 30 + GRAPHIC_HEIGHT + y + 5 + 20 );
269 /* Create the ship image. */
270 window_addRect( wid, 19, -30, GRAPHIC_WIDTH+1, GRAPHIC_HEIGHT + y + 5,
271 "rctGFX", &cGrey10, 1 );
272 window_addImage( wid, 20 + (GRAPHIC_WIDTH-(int)comm_graphic->w)/2,
273 -30 - (GRAPHIC_HEIGHT-(int)comm_graphic->h)/2,
274 "imgGFX", comm_graphic, 0 );
276 /* Faction logo. */
277 if (logo != NULL) {
278 window_addImage( wid, x, -30 - GRAPHIC_HEIGHT - 5,
279 "imgFaction", logo, 0 );
280 x += logo->w + 10;
281 y -= (logo->h - (gl_defFont.h*2 + 15)) / 2;
284 /* Name. */
285 window_addText( wid, x, -30 - GRAPHIC_HEIGHT - y + gl_defFont.h*2 + 10,
286 GRAPHIC_WIDTH - x, 20, 0, "txtName",
287 NULL, &cDConsole, name );
289 /* Standing. */
290 window_addText( wid, x, -30 - GRAPHIC_HEIGHT - y + gl_defFont.h + 5,
291 GRAPHIC_WIDTH - x, 20, 0, "txtStanding", NULL, c, stand );
293 /* Buttons. */
294 window_addButton( wid, -20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
295 "btnClose", "Close", comm_close );
297 return wid;
302 * @brief Closes the comm window.
304 * @param wid ID of window calling the function.
305 * @param unused Unused.
307 static void comm_close( unsigned int wid, char *unused )
309 /* Clean up a bit after ourselves. */
310 if (comm_graphic != NULL) {
311 gl_freeTexture(comm_graphic);
312 comm_graphic = NULL;
314 comm_pilot = NULL;
315 comm_planet = NULL;
316 /* Close the window. */
317 window_close( wid, unused );
322 * @brief Tries to bribe the pilot.
324 * @param wid ID of window calling the function.
325 * @param unused Unused.
327 static void comm_bribePilot( unsigned int wid, char *unused )
329 (void) unused;
330 int answer;
331 double d;
332 unsigned int price;
333 const char *str;
334 lua_State *L;
336 /* Unbribeable. */
337 str = comm_getString( "bribe_no" );
338 if (str != NULL) {
339 dialogue_msg("Bribe Pilot", "%s", str );
340 return;
343 /* Get amount pilot wants. */
344 if (comm_getNumber( &d, "bribe" )) {
345 WARN("Pilot '%s' accepts bribes but doesn't give price!", comm_pilot->name );
346 d = 0.;
348 price = (unsigned int) d;
350 /* Check to see if already bribed. */
351 if (price == 0) {
352 dialogue_msg("Bribe Pilot", "\"Money won't save your hide now!\"");
353 return;
356 /* Bribe message. */
357 str = comm_getString( "bribe_prompt" );
358 if (str == NULL) {
359 answer = dialogue_YesNo( "Bribe Pilot", "\"I'm gonna need at least %u credits to not leave you as a hunk of floating debris.\"\n\nPay %u credits?", price, price );
361 else
362 answer = dialogue_YesNo( "Bribe Pilot", "%s\n\nPay %u credits?", str, price );
364 /* Said no. */
365 if (answer == 0) {
366 dialogue_msg("Bribe Pilot", "You decide not to pay.");
367 return;
370 /* Check if has the money. */
371 if (!player_hasCredits( price )) {
372 dialogue_msg("Bribe Pilot", "You don't have enough credits for the bribery.");
373 return;
376 player_modCredits( -price );
377 str = comm_getString( "bribe_paid" );
378 if (str == NULL)
379 dialogue_msg("Bribe Pilot", "\"Pleasure to do business with you.\"");
380 else
381 dialogue_msg("Bribe Pilot", "%s", str);
383 /* Mark as bribed and don't allow bribing again. */
384 pilot_setFlag( comm_pilot, PILOT_BRIBED );
385 pilot_rmHostile( comm_pilot );
387 /* Stop hyperspace if necessary. */
388 pilot_rmFlag( comm_pilot, PILOT_HYP_PREP );
389 pilot_rmFlag( comm_pilot, PILOT_HYP_BEGIN );
391 /* Don't allow rebribe. */
392 L = comm_pilot->ai->L;
393 lua_getglobal(L, "mem");
394 lua_pushnumber(L, 0);
395 lua_setfield(L, -2, "bribe");
396 lua_pop(L,1);
398 /* Reopen window. */
399 window_destroy( wid );
400 comm_openPilot( comm_pilot->id );
405 * @brief Tries to bribe the planet
407 * @param wid ID of window calling the function.
408 * @param unused Unused.
410 static void comm_bribePlanet( unsigned int wid, char *unused )
412 (void) unused;
413 int i, j;
414 int answer;
415 unsigned int price;
416 double d;
417 double n, m;
418 double o, p;
419 double q, r;
420 double standing;
421 Fleet *f;
423 /* Price. */
424 standing = faction_getPlayer( comm_planet->faction );
425 /* Get number of hostiles and mass of hostiles. */
426 n = 0.;
427 m = 0.;
428 for (i=0; i<pilot_nstack; i++) {
429 if (areAllies(comm_planet->faction, pilot_stack[i]->faction)) {
430 n += 1.;
431 m += pilot_stack[i]->solid->mass;
434 /* Get now the presence factor - get mass of possible ships and mass */
435 o = 0.;
436 p = 0.;
437 for (i=0; i<cur_system->nfleets; i++) {
438 f = cur_system->fleets[i].fleet;
439 if (areAllies(comm_planet->faction, f->faction)) {
440 q = 0;
441 r = 0;
442 for (j=0; j<f->npilots; j++) {
443 q += (double)f->pilots[j].chance / 100.;
444 r += f->pilots[j].ship->mass;
446 q *= (double)cur_system->fleets[i].chance / 100.;
447 o += q;
448 p += r;
451 /* Calculate the price. */
452 n = MAX(0., 1.);
453 o = MAX(0., 1.);
454 d = 2000.; /* Base price. */
455 d *= 0.5 * (o * (sqrt( p / o ) / 9.5)) + /* Base on presence. */
456 0.5 * (n * (sqrt( m / n ) / 9.5)); /* Base on current hostiles. */
457 d *= 1. + (-1. * standing) / 50.; /* Modify by standing. */
458 price = (unsigned int) d;
460 /* Yes/No input. */
461 answer = dialogue_YesNo( "Bribe Starport",
462 "\"I'll let you land for the small sum of %u credits.\"\n\nPay %u credits?",
463 price, price );
465 /* Said no. */
466 if (answer == 0) {
467 dialogue_msg("Bribe Starport", "You decide not to pay.");
468 return;
471 /* Check if has the money. */
472 if (!player_hasCredits( price )) {
473 dialogue_msg("Bribe Starport", "You don't have enough credits for the bribery.");
474 return;
477 /* Pay the money. */
478 player_modCredits( -price );
479 dialogue_msg("Bribe Starport", "You have permission to dock.");
481 /* Mark as bribed and don't allow bribing again. */
482 comm_planet->bribed = 1;
484 /* Reopen window. */
485 window_destroy( wid );
486 comm_openPlanet( comm_planet );
491 * @brief Tries to request help from the pilot.
493 * @param wid ID of window calling the function.
494 * @param unused Unused.
496 static void comm_requestFuel( unsigned int wid, char *unused )
498 (void) wid;
499 (void) unused;
500 double val;
501 const char *msg;
502 int ret;
503 unsigned int price;
505 /* Check to see if ship has a no refuel message. */
506 msg = comm_getString( "refuel_no" );
507 if (msg != NULL) {
508 dialogue_msg( "Request Fuel", msg );
509 return;
512 /* Must need refueling. */
513 if (player->fuel >= player->fuel_max) {
514 dialogue_msg( "Request Fuel", "Your fuel deposits are already full." );
515 return;
518 /* See if pilot has enough fuel. */
519 if (comm_pilot->fuel < 200.) {
520 dialogue_msg( "Request Fuel",
521 "\"Sorry, I don't have enough fuel to spare at the moment.\"" );
522 return;
525 /* See if player can get refueled. */
526 ret = comm_getNumber( &val, "refuel" );
527 msg = comm_getString( "refuel_msg" );
528 if ((ret != 0) || (msg == NULL)) {
529 dialogue_msg( "Request Fuel", "\"Sorry, I'm busy now.\"" );
530 return;
532 price = (int) val;
534 /* Check to see if is already refueling. */
535 if (pilot_isFlag(comm_pilot, PILOT_REFUELING)) {
536 dialogue_msg( "Request Fuel", "Pilot is already refueling you." );
537 return;
540 /* See if player really wants to pay. */
541 if (price > 0) {
542 ret = dialogue_YesNo( "Request Fuel", "%s\n\nPay %u credits?", msg, price );
543 if (ret == 0) {
544 dialogue_msg( "Request Fuel", "You decide not to pay." );
545 return;
548 else
549 dialogue_msg( "Request Fuel", "%s", msg );
551 /* Check if he has the money. */
552 if (!player_hasCredits( price )) {
553 dialogue_msg( "Request Fuel", "You need %u more credits!",
554 price - player->credits);
555 return;
558 /* Take money. */
559 player_modCredits( -price );
560 pilot_modCredits( comm_pilot, price );
562 /* Start refueling. */
563 pilot_rmFlag(comm_pilot, PILOT_HYP_PREP | PILOT_HYP_BEGIN);
564 pilot_setFlag(comm_pilot, PILOT_REFUELING);
565 ai_refuel( comm_pilot, player->id );
567 /* Last message. */
568 if (price > 0)
569 dialogue_msg( "Request Fuel", "\"On my way.\"" );
574 * @brief Gets the amount the communicating pilot wants as a bribe.
576 * Valid targets for now are:
577 * - "bribe": amount pilot wants to be paid.
578 * - "refuel": amount pilot wants to be paid for refueling the player.
580 * @param[out] val Value of the number gotten.
581 * @param str Name of number to get.
582 * @return 0 for success, 1 on error (including not found).
584 static int comm_getNumber( double *val, char* str )
586 int ret;
587 lua_State *L;
589 /* Set up the state. */
590 L = comm_pilot->ai->L;
591 lua_getglobal( L, "mem" );
593 /* Get number amount. */
594 lua_getfield( L, -1, str );
595 /* Check to see if it's a number. */
596 if (!lua_isnumber(L, -1))
597 ret = 1;
598 else {
599 *val = lua_tonumber(L, -1);
600 ret = 0;
602 /* Clean up. */
603 lua_pop(L, 2);
604 return ret;
609 * @brief Gets a string from the pilot's memory.
611 * Valid targets are:
612 * - comm_no: message of communication failure.
613 * - bribe_no: unbribe message
614 * - bribe_prompt: bribe prompt
615 * - bribe_paid: paid message
617 * @param str String to get.
618 * @return String matching str.
620 static const char* comm_getString( char *str )
622 lua_State *L;
623 const char *ret;
625 /* Get memory table. */
626 L = comm_pilot->ai->L;
627 lua_getglobal( L, "mem" );
629 /* Get str message. */
630 lua_getfield(L, -1, str );
631 if (!lua_isstring(L, -1))
632 ret = NULL;
633 else
634 ret = lua_tostring(L, -1);
635 lua_pop(L, 2);
637 return ret;