1 /*
2 * See Licensing and Copyright notice in naev.h
3 */
4
5 /**
6 * @file board.c
7 *
8 * @brief Deals with boarding ships.
9 */
10
11
12 #include "board.h"
13
14 #include "naev.h"
15
16 #include "log.h"
17 #include "pilot.h"
18 #include "player.h"
19 #include "toolkit.h"
20 #include "space.h"
21 #include "rng.h"
22 #include "economy.h"
23 #include "hook.h"
24 #include "damagetype.h"
25 #include "nstring.h"
26
27
28 #define BOARDING_WIDTH 380 /**< Boarding window width. */
29 #define BOARDING_HEIGHT 200 /**< Boarding window height. */
30
31 #define BUTTON_WIDTH 50 /**< Boarding button width. */
32 #define BUTTON_HEIGHT 30 /**< Boarding button height. */
33
34
35 static int board_stopboard = 0; /**< Whether or not to unboard. */
36 static int board_boarded = 0;
37
38
39 /*
40 * prototypes
41 */
42 static void board_stealCreds( unsigned int wdw, char* str );
43 static void board_stealCargo( unsigned int wdw, char* str );
44 static void board_stealFuel( unsigned int wdw, char* str );
45 static void board_stealAmmo( unsigned int wdw, char* str );
46 static int board_trySteal( Pilot *p );
47 static int board_fail( unsigned int wdw );
48 static void board_update( unsigned int wdw );
49
50
51 /**
52 * @brief Gets if the player is boarded.
53 */
player_isBoarded(void)54 int player_isBoarded (void)
55 {
56 return board_boarded;
57 }
58
59
60 /**
61 * @fn void player_board (void)
62 *
63 * @brief Attempt to board the player's target.
64 *
65 * Creates the window on success.
66 */
player_board(void)67 void player_board (void)
68 {
69 Pilot *p;
70 unsigned int wdw;
71 char c;
72 HookParam hparam[2];
73
74 /* Not disabled. */
75 if (pilot_isDisabled(player.p))
76 return;
77
78 if (player.p->target==PLAYER_ID) {
79 /* We don't try to find far away targets, only nearest and see if it matches.
80 * However, perhaps looking for first boardable target within a certain range
81 * could be more interesting. */
82 player_targetNearest();
83 p = pilot_get(player.p->target);
84 if ((!pilot_isDisabled(p) && !pilot_isFlag(p,PILOT_BOARDABLE)) ||
85 pilot_isFlag(p,PILOT_NOBOARD)) {
86 player_targetClear();
87 player_message("\erYou need a target to board first!");
88 return;
89 }
90 }
91 else
92 p = pilot_get(player.p->target);
93 c = pilot_getFactionColourChar( p );
94
95 /* More checks. */
96 if (pilot_isFlag(p,PILOT_NOBOARD)) {
97 player_message("\erTarget ship can not be boarded.");
98 return;
99 }
100 else if (pilot_isFlag(p,PILOT_BOARDED)) {
101 player_message("\erYour target cannot be boarded again.");
102 return;
103 }
104 else if (!pilot_isDisabled(p) && !pilot_isFlag(p,PILOT_BOARDABLE)) {
105 player_message("\erYou cannot board a ship that isn't disabled!");
106 return;
107 }
108 else if (vect_dist(&player.p->solid->pos,&p->solid->pos) >
109 p->ship->gfx_space->sw * PILOT_SIZE_APROX) {
110 player_message("\erYou are too far away to board your target.");
111 return;
112 }
113 else if ((pow2(VX(player.p->solid->vel)-VX(p->solid->vel)) +
114 pow2(VY(player.p->solid->vel)-VY(p->solid->vel))) >
115 (double)pow2(MAX_HYPERSPACE_VEL)) {
116 player_message("\erYou are going too fast to board the ship.");
117 return;
118 }
119 /* We'll recover it if it's the pilot's ex-escort. */
120 else if (p->parent == PLAYER_ID) {
121 /* Try to recover. */
122 pilot_dock( p, player.p, 0 );
123 if (pilot_isFlag(p, PILOT_DELETE )) { /* Hack to see if it boarded. */
124 player_message("\epYou recover \eg%s\ep into your fighter bay.", p->name);
125 return;
126 }
127 }
128
129 /* Is boarded. */
130 board_boarded = 1;
131
132 /* pilot will be boarded */
133 pilot_setFlag(p,PILOT_BOARDED);
134 player_message("\epBoarding ship \e%c%s\e0.", c, p->name);
135
136 /* Don't unboard. */
137 board_stopboard = 0;
138
139 /*
140 * run hook if needed
141 */
142 hparam[0].type = HOOK_PARAM_PILOT;
143 hparam[0].u.lp = p->id;
144 hparam[1].type = HOOK_PARAM_SENTINEL;
145 hooks_runParam( "board", hparam );
146 pilot_runHookParam(p, PILOT_HOOK_BOARD, hparam, 1);
147 hparam[0].u.lp = PLAYER_ID;
148 pilot_runHookParam(p, PILOT_HOOK_BOARDING, hparam, 1);
149
150 if (board_stopboard) {
151 board_boarded = 0;
152 return;
153 }
154
155 /*
156 * create the boarding window
157 */
158 wdw = window_create( "Boarding", -1, -1, BOARDING_WIDTH, BOARDING_HEIGHT );
159
160 window_addText( wdw, 20, -30, 120, 60,
161 0, "txtCargo", &gl_smallFont, &cDConsole,
162 "Credits:\n"
163 "Cargo:\n"
164 "Fuel:\n"
165 "Ammo:\n"
166 );
167 window_addText( wdw, 80, -30, 120, 60,
168 0, "txtData", &gl_smallFont, &cBlack, NULL );
169
170 window_addButton( wdw, 20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
171 "btnStealCredits", "Credits", board_stealCreds);
172 window_addButton( wdw, 20+BUTTON_WIDTH+20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
173 "btnStealCargo", "Cargo", board_stealCargo);
174 window_addButton( wdw, 20+2*(BUTTON_WIDTH+20), 20, BUTTON_WIDTH, BUTTON_HEIGHT,
175 "btnStealFuel", "Fuel", board_stealFuel);
176 window_addButton( wdw, 20+3*(BUTTON_WIDTH+20), 20, BUTTON_WIDTH, BUTTON_HEIGHT,
177 "btnStealAmmo", "Ammo", board_stealAmmo);
178 window_addButton( wdw, -20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
179 "btnBoardingClose", "Leave", board_exit );
180
181 board_update(wdw);
182 }
183
184
185 /**
186 * @brief Forces unboarding of the pilot.
187 */
board_unboard(void)188 void board_unboard (void)
189 {
190 board_stopboard = 1;
191 }
192
193
194 /**
195 * @brief Closes the boarding window.
196 *
197 * @param wdw Window triggering the function.
198 * @param str Unused.
199 */
board_exit(unsigned int wdw,char * str)200 void board_exit( unsigned int wdw, char* str )
201 {
202 (void) str;
203 window_destroy( wdw );
204
205 /* Is not boarded. */
206 board_boarded = 0;
207 }
208
209
210 /**
211 * @brief Attempt to steal the boarded ship's credits.
212 *
213 * @param wdw Window triggering the function.
214 * @param str Unused.
215 */
board_stealCreds(unsigned int wdw,char * str)216 static void board_stealCreds( unsigned int wdw, char* str )
217 {
218 (void)str;
219 Pilot* p;
220
221 p = pilot_get(player.p->target);
222
223 if (p->credits==0) { /* you can't steal from the poor */
224 player_message("\epThe ship has no credits.");
225 return;
226 }
227
228 if (board_fail(wdw)) return;
229
230 player_modCredits( p->credits );
231 p->credits = 0;
232 board_update( wdw ); /* update the lack of credits */
233 player_message("\epYou manage to steal the ship's credits.");
234 }
235
236
237 /**
238 * @brief Attempt to steal the boarded ship's cargo.
239 *
240 * @param wdw Window triggering the function.
241 * @param str Unused.
242 */
board_stealCargo(unsigned int wdw,char * str)243 static void board_stealCargo( unsigned int wdw, char* str )
244 {
245 (void)str;
246 int q;
247 Pilot* p;
248
249 p = pilot_get(player.p->target);
250
251 if (p->ncommodities==0) { /* no cargo */
252 player_message("\epThe ship has no cargo.");
253 return;
254 }
255 else if (pilot_cargoFree(player.p) <= 0) {
256 player_message("\erYou have no room for the ship's cargo.");
257 return;
258 }
259
260 if (board_fail(wdw)) return;
261
262 /** steal as much as possible until full - @todo let player choose */
263 q = 1;
264 while ((p->ncommodities > 0) && (q!=0)) {
265 q = pilot_cargoAdd( player.p, p->commodities[0].commodity,
266 p->commodities[0].quantity, 0 );
267 pilot_cargoRm( p, p->commodities[0].commodity, q );
268 }
269
270 board_update( wdw );
271 player_message("\epYou manage to steal the ship's cargo.");
272 }
273
274
275 /**
276 * @brief Attempt to steal the boarded ship's fuel.
277 *
278 * @param wdw Window triggering the function.
279 * @param str Unused.
280 */
board_stealFuel(unsigned int wdw,char * str)281 static void board_stealFuel( unsigned int wdw, char* str )
282 {
283 (void)str;
284 Pilot* p;
285
286 p = pilot_get(player.p->target);
287
288 if (p->fuel <= 0.) { /* no fuel. */
289 player_message("\epThe ship has no fuel.");
290 return;
291 }
292 else if (player.p->fuel == player.p->fuel_max) {
293 player_message("\erYour ship is at maximum fuel capacity.");
294 return;
295 }
296
297 if (board_fail(wdw))
298 return;
299
300 /* Steal fuel. */
301 player.p->fuel += p->fuel;
302 p->fuel = 0.;
303
304 /* Make sure doesn't overflow. */
305 if (player.p->fuel > player.p->fuel_max) {
306 p->fuel = player.p->fuel - player.p->fuel_max;
307 player.p->fuel = player.p->fuel_max;
308 }
309
310 board_update( wdw );
311 player_message("\epYou manage to steal the ship's fuel.");
312 }
313
314
315 /**
316 * @brief Attempt to steal the boarded ship's ammo.
317 *
318 * @param wdw Window triggering the function.
319 * @param str Unused.
320 */
board_stealAmmo(unsigned int wdw,char * str)321 static void board_stealAmmo( unsigned int wdw, char* str )
322 {
323 Pilot* p;
324 int nreloaded, i, nammo, x;
325 PilotOutfitSlot *target_outfit_slot, *player_outfit_slot;
326 Outfit *target_outfit, *ammo, *player_outfit;
327 (void)str;
328 nreloaded = 0;
329 p = pilot_get(player.p->target);
330 /* Target has no ammo */
331 if (pilot_countAmmo(p) <= 0) {
332 player_message("\erThe ship has no ammo.");
333 return;
334 }
335 /* Player is already at max ammo */
336 if (pilot_countAmmo(player.p) >= pilot_maxAmmo(player.p)) {
337 player_message("\erYou are already at max ammo.");
338 return;
339 }
340 if (board_fail(wdw))
341 return;
342 /* Steal the ammo */
343 for (i=0; i<p->noutfits; i++) {
344 target_outfit_slot = p->outfits[i];
345 if (target_outfit_slot == NULL)
346 continue;
347 target_outfit = target_outfit_slot->outfit;
348 if (target_outfit == NULL)
349 continue;
350 /* outfit isn't a launcher */
351 if (!outfit_isLauncher(target_outfit)) {
352 continue;
353 }
354 nammo = target_outfit_slot->u.ammo.quantity;
355 ammo = target_outfit_slot->u.ammo.outfit;
356 /* launcher has no ammo */
357 if (ammo == NULL)
358 continue;
359 if (nammo <= 0) {
360 continue;
361 }
362 for (x=0; x<player.p->noutfits; x++) {
363 int nadded = 0;
364 player_outfit_slot = player.p->outfits[x];
365 if (player_outfit_slot == NULL)
366 continue;
367 player_outfit = player_outfit_slot->outfit;
368 if (player_outfit == NULL)
369 continue;
370 if (!outfit_isLauncher(player_outfit)) {
371 continue;
372 }
373 if (strcmp(ammo->name, player_outfit_slot->u.ammo.outfit->name) != 0) {
374 continue;
375 }
376 /* outfit's ammo matches; try to add to player and remove from target */
377 nadded = pilot_addAmmo(player.p, player_outfit_slot, ammo, nammo);
378 nammo -= nadded;
379 pilot_rmAmmo(p, target_outfit_slot, nadded);
380 nreloaded += nadded;
381 if (nadded > 0)
382 player_message("\epYou looted %d %s(s)", nadded, ammo->name);
383 if (nammo <= 0) {
384 break;
385 }
386 }
387 if (nammo <= 0) {
388 continue;
389 }
390 }
391 if (nreloaded <= 0)
392 player_message("\erThere is no ammo compatible with your launchers on board.");
393 pilot_updateMass(player.p);
394 pilot_weaponSane(player.p);
395 pilot_updateMass(p);
396 pilot_weaponSane(p);
397 board_update(wdw);
398 }
399
400
401 /**
402 * @brief Checks to see if the pilot can steal from its target.
403 *
404 * @param p Pilot stealing from its target.
405 * @return 0 if successful, 1 if fails, -1 if fails and kills target.
406 */
board_trySteal(Pilot * p)407 static int board_trySteal( Pilot *p )
408 {
409 Pilot *target;
410 Damage dmg;
411
412 /* Get the target. */
413 target = pilot_get(p->target);
414 if (target == NULL)
415 return 1;
416
417 /* See if was successful. */
418 if (RNGF() > (0.5 * (10. + target->crew)/(10. + p->crew)))
419 return 0;
420
421 /* Triggered self destruct. */
422 if (RNGF() < 0.4) {
423 /* Don't actually kill. */
424 target->shield = 0.;
425 target->armour = 1.;
426 /* This will make the boarding ship take the possible faction hit. */
427 dmg.type = dtype_get("normal");
428 dmg.damage = 100.;
429 dmg.penetration = 1.;
430 dmg.disable = 0.;
431 pilot_hit( target, NULL, p->id, &dmg, 1 );
432 /* Return ship dead. */
433 return -1;
434 }
435
436 return 1;
437 }
438
439
440 /**
441 * @brief Checks to see if the hijack attempt failed.
442 *
443 * @return 1 on failure to board, otherwise 0.
444 */
board_fail(unsigned int wdw)445 static int board_fail( unsigned int wdw )
446 {
447 int ret;
448
449 ret = board_trySteal( player.p );
450
451 if (ret == 0)
452 return 0;
453 else if (ret < 0) /* killed ship. */
454 player_message("\epYou have tripped the ship's self-destruct mechanism!");
455 else /* you just got locked out */
456 player_message("\epThe ship's security system locks %s out.",
457 (player.p->ship->crew > 0) ? "your crew" : "you" );
458
459 board_exit( wdw, NULL);
460 return 1;
461 }
462
463
464 /**
465 * @brief Updates the boarding window fields.
466 *
467 * @param wdw Window to update.
468 */
board_update(unsigned int wdw)469 static void board_update( unsigned int wdw )
470 {
471 int i, j;
472 char str[PATH_MAX];
473 char cred[ECON_CRED_STRLEN];
474 Pilot* p;
475
476 p = pilot_get(player.p->target);
477 j = 0;
478
479 /* Credits. */
480 credits2str( cred, p->credits, 2 );
481 j += nsnprintf( &str[j], PATH_MAX, "%s\n", cred );
482
483 /* Commodities. */
484 if ((p->ncommodities==0) && (j < PATH_MAX))
485 j += snprintf( &str[j], PATH_MAX-j, "none\n" );
486 else {
487 for (i=0; i<p->ncommodities; i++) {
488 if (j > PATH_MAX)
489 break;
490 if (p->commodities[i].commodity == NULL)
491 continue;
492 j += snprintf( &str[j], PATH_MAX-j, "%d %s\n",
493 p->commodities[i].quantity, p->commodities[i].commodity->name );
494 }
495 }
496
497 /* Fuel. */
498 if (p->fuel <= 0.) {
499 if (j < PATH_MAX)
500 j += snprintf( &str[j], PATH_MAX-j, "none\n" );
501 }
502 else {
503 if (j < PATH_MAX)
504 j += snprintf( &str[j], PATH_MAX-j, "%.0f Units\n", p->fuel );
505 }
506
507 /* Missiles */
508 int nmissiles = pilot_countAmmo(p);
509 if (nmissiles <= 0) {
510 if (j < PATH_MAX)
511 j += snprintf( &str[j], PATH_MAX-j, "none\n" );
512 }
513 else {
514 if (j < PATH_MAX)
515 j += snprintf( &str[j], PATH_MAX-j, "%d missiles\n", nmissiles );
516 }
517
518 window_modifyText( wdw, "txtData", str );
519 }
520
521
522 /**
523 * @brief Has a pilot attempt to board another pilot.
524 *
525 * @param p Pilot doing the boarding.
526 * @return 1 if target was boarded.
527 */
pilot_board(Pilot * p)528 int pilot_board( Pilot *p )
529 {
530 Pilot *target;
531 HookParam hparam[2];
532
533 /* Make sure target is sane. */
534 target = pilot_get(p->target);
535 if (target == NULL) {
536 DEBUG("NO TARGET");
537 return 0;
538 }
539
540 /* Check if can board. */
541 if (!pilot_isDisabled(target))
542 return 0;
543 else if (vect_dist(&p->solid->pos, &target->solid->pos) >
544 target->ship->gfx_space->sw * PILOT_SIZE_APROX )
545 return 0;
546 else if ((pow2(VX(p->solid->vel)-VX(target->solid->vel)) +
547 pow2(VY(p->solid->vel)-VY(target->solid->vel))) >
548 (double)pow2(MAX_HYPERSPACE_VEL))
549 return 0;
550 else if (pilot_isFlag(target,PILOT_BOARDED))
551 return 0;
552
553 /* Set the boarding flag. */
554 pilot_setFlag(target, PILOT_BOARDED);
555 pilot_setFlag(p, PILOT_BOARDING);
556
557 /* Set time it takes to board. */
558 p->ptimer = 3.;
559
560 /* Run pilot board hook. */
561 hparam[0].type = HOOK_PARAM_PILOT;
562 hparam[0].u.lp = p->id;
563 hparam[1].type = HOOK_PARAM_SENTINEL;
564 pilot_runHookParam(target, PILOT_HOOK_BOARDING, hparam, 1);
565 hparam[0].u.lp = target->id;
566 pilot_runHookParam(target, PILOT_HOOK_BOARD, hparam, 1);
567
568 return 1;
569 }
570
571
572 /**
573 * @brief Finishes the boarding.
574 *
575 * @param p Pilot to finish the boarding.
576 */
pilot_boardComplete(Pilot * p)577 void pilot_boardComplete( Pilot *p )
578 {
579 int ret;
580 Pilot *target;
581 credits_t worth;
582 char creds[ ECON_CRED_STRLEN ];
583
584 /* Make sure target is sane. */
585 target = pilot_get(p->target);
586 if (target == NULL)
587 return;
588
589 /* In the case of the player take fewer credits. */
590 if (pilot_isPlayer(target)) {
591 worth = MIN( 0.1*pilot_worth(target), target->credits );
592 p->credits += worth;
593 target->credits -= worth;
594 credits2str( creds, worth, 2 );
595 player_message( "\e%c%s\e0 has plundered %s credits from your ship!",
596 pilot_getFactionColourChar(p), p->name, creds );
597 }
598 else {
599 /* Steal stuff, we only do credits for now. */
600 ret = board_trySteal(p);
601 if (ret == 0) {
602 /* Normally just plunder it all. */
603 p->credits += target->credits;
604 target->credits = 0.;
605 }
606 }
607
608 /* Finish the boarding. */
609 pilot_rmFlag(p, PILOT_BOARDING);
610 }
611
612
613