Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
613 lines (522 sloc) 15.6 KB
/*
* See Licensing and Copyright notice in naev.h
*/
/**
* @file board.c
*
* @brief Deals with boarding ships.
*/
#include "board.h"
#include "naev.h"
#include "log.h"
#include "pilot.h"
#include "player.h"
#include "toolkit.h"
#include "space.h"
#include "rng.h"
#include "economy.h"
#include "hook.h"
#include "damagetype.h"
#include "nstring.h"
#define BOARDING_WIDTH 380 /**< Boarding window width. */
#define BOARDING_HEIGHT 200 /**< Boarding window height. */
#define BUTTON_WIDTH 50 /**< Boarding button width. */
#define BUTTON_HEIGHT 30 /**< Boarding button height. */
static int board_stopboard = 0; /**< Whether or not to unboard. */
static int board_boarded = 0;
/*
* prototypes
*/
static void board_stealCreds( unsigned int wdw, char* str );
static void board_stealCargo( unsigned int wdw, char* str );
static void board_stealFuel( unsigned int wdw, char* str );
static void board_stealAmmo( unsigned int wdw, char* str );
static int board_trySteal( Pilot *p );
static int board_fail( unsigned int wdw );
static void board_update( unsigned int wdw );
/**
* @brief Gets if the player is boarded.
*/
int player_isBoarded (void)
{
return board_boarded;
}
/**
* @fn void player_board (void)
*
* @brief Attempt to board the player's target.
*
* Creates the window on success.
*/
void player_board (void)
{
Pilot *p;
unsigned int wdw;
char c;
HookParam hparam[2];
/* Not disabled. */
if (pilot_isDisabled(player.p))
return;
if (player.p->target==PLAYER_ID) {
/* We don't try to find far away targets, only nearest and see if it matches.
* However, perhaps looking for first boardable target within a certain range
* could be more interesting. */
player_targetNearest();
p = pilot_get(player.p->target);
if ((!pilot_isDisabled(p) && !pilot_isFlag(p,PILOT_BOARDABLE)) ||
pilot_isFlag(p,PILOT_NOBOARD)) {
player_targetClear();
player_message("\erYou need a target to board first!");
return;
}
}
else
p = pilot_get(player.p->target);
c = pilot_getFactionColourChar( p );
/* More checks. */
if (pilot_isFlag(p,PILOT_NOBOARD)) {
player_message("\erTarget ship can not be boarded.");
return;
}
else if (pilot_isFlag(p,PILOT_BOARDED)) {
player_message("\erYour target cannot be boarded again.");
return;
}
else if (!pilot_isDisabled(p) && !pilot_isFlag(p,PILOT_BOARDABLE)) {
player_message("\erYou cannot board a ship that isn't disabled!");
return;
}
else if (vect_dist(&player.p->solid->pos,&p->solid->pos) >
p->ship->gfx_space->sw * PILOT_SIZE_APROX) {
player_message("\erYou are too far away to board your target.");
return;
}
else if ((pow2(VX(player.p->solid->vel)-VX(p->solid->vel)) +
pow2(VY(player.p->solid->vel)-VY(p->solid->vel))) >
(double)pow2(MAX_HYPERSPACE_VEL)) {
player_message("\erYou are going too fast to board the ship.");
return;
}
/* We'll recover it if it's the pilot's ex-escort. */
else if (p->parent == PLAYER_ID) {
/* Try to recover. */
pilot_dock( p, player.p, 0 );
if (pilot_isFlag(p, PILOT_DELETE )) { /* Hack to see if it boarded. */
player_message("\epYou recover \eg%s\ep into your fighter bay.", p->name);
return;
}
}
/* Is boarded. */
board_boarded = 1;
/* pilot will be boarded */
pilot_setFlag(p,PILOT_BOARDED);
player_message("\epBoarding ship \e%c%s\e0.", c, p->name);
/* Don't unboard. */
board_stopboard = 0;
/*
* run hook if needed
*/
hparam[0].type = HOOK_PARAM_PILOT;
hparam[0].u.lp.pilot = p->id;
hparam[1].type = HOOK_PARAM_SENTINEL;
hooks_runParam( "board", hparam );
pilot_runHookParam(p, PILOT_HOOK_BOARD, hparam, 1);
hparam[0].u.lp.pilot = PLAYER_ID;
pilot_runHookParam(p, PILOT_HOOK_BOARDING, hparam, 1);
if (board_stopboard) {
board_boarded = 0;
return;
}
/*
* create the boarding window
*/
wdw = window_create( "Boarding", -1, -1, BOARDING_WIDTH, BOARDING_HEIGHT );
window_addText( wdw, 20, -30, 120, 60,
0, "txtCargo", &gl_smallFont, &cDConsole,
"Credits:\n"
"Cargo:\n"
"Fuel:\n"
"Ammo:\n"
);
window_addText( wdw, 80, -30, 120, 60,
0, "txtData", &gl_smallFont, &cBlack, NULL );
window_addButton( wdw, 20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnStealCredits", "Credits", board_stealCreds);
window_addButton( wdw, 20+BUTTON_WIDTH+20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnStealCargo", "Cargo", board_stealCargo);
window_addButton( wdw, 20+2*(BUTTON_WIDTH+20), 20, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnStealFuel", "Fuel", board_stealFuel);
window_addButton( wdw, 20+3*(BUTTON_WIDTH+20), 20, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnStealAmmo", "Ammo", board_stealAmmo);
window_addButton( wdw, -20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnBoardingClose", "Leave", board_exit );
board_update(wdw);
}
/**
* @brief Forces unboarding of the pilot.
*/
void board_unboard (void)
{
board_stopboard = 1;
}
/**
* @brief Closes the boarding window.
*
* @param wdw Window triggering the function.
* @param str Unused.
*/
void board_exit( unsigned int wdw, char* str )
{
(void) str;
window_destroy( wdw );
/* Is not boarded. */
board_boarded = 0;
}
/**
* @brief Attempt to steal the boarded ship's credits.
*
* @param wdw Window triggering the function.
* @param str Unused.
*/
static void board_stealCreds( unsigned int wdw, char* str )
{
(void)str;
Pilot* p;
p = pilot_get(player.p->target);
if (p->credits==0) { /* you can't steal from the poor */
player_message("\epThe ship has no credits.");
return;
}
if (board_fail(wdw)) return;
player_modCredits( p->credits );
p->credits = 0;
board_update( wdw ); /* update the lack of credits */
player_message("\epYou manage to steal the ship's credits.");
}
/**
* @brief Attempt to steal the boarded ship's cargo.
*
* @param wdw Window triggering the function.
* @param str Unused.
*/
static void board_stealCargo( unsigned int wdw, char* str )
{
(void)str;
int q;
Pilot* p;
p = pilot_get(player.p->target);
if (p->ncommodities==0) { /* no cargo */
player_message("\epThe ship has no cargo.");
return;
}
else if (pilot_cargoFree(player.p) <= 0) {
player_message("\erYou have no room for the ship's cargo.");
return;
}
if (board_fail(wdw)) return;
/** steal as much as possible until full - @todo let player choose */
q = 1;
while ((p->ncommodities > 0) && (q!=0)) {
q = pilot_cargoAdd( player.p, p->commodities[0].commodity,
p->commodities[0].quantity, 0 );
pilot_cargoRm( p, p->commodities[0].commodity, q );
}
board_update( wdw );
player_message("\epYou manage to steal the ship's cargo.");
}
/**
* @brief Attempt to steal the boarded ship's fuel.
*
* @param wdw Window triggering the function.
* @param str Unused.
*/
static void board_stealFuel( unsigned int wdw, char* str )
{
(void)str;
Pilot* p;
p = pilot_get(player.p->target);
if (p->fuel <= 0.) { /* no fuel. */
player_message("\epThe ship has no fuel.");
return;
}
else if (player.p->fuel == player.p->fuel_max) {
player_message("\erYour ship is at maximum fuel capacity.");
return;
}
if (board_fail(wdw))
return;
/* Steal fuel. */
player.p->fuel += p->fuel;
p->fuel = 0.;
/* Make sure doesn't overflow. */
if (player.p->fuel > player.p->fuel_max) {
p->fuel = player.p->fuel - player.p->fuel_max;
player.p->fuel = player.p->fuel_max;
}
board_update( wdw );
player_message("\epYou manage to steal the ship's fuel.");
}
/**
* @brief Attempt to steal the boarded ship's ammo.
*
* @param wdw Window triggering the function.
* @param str Unused.
*/
static void board_stealAmmo( unsigned int wdw, char* str )
{
Pilot* p;
int nreloaded, i, nammo, x;
PilotOutfitSlot *target_outfit_slot, *player_outfit_slot;
Outfit *target_outfit, *ammo, *player_outfit;
(void)str;
nreloaded = 0;
p = pilot_get(player.p->target);
/* Target has no ammo */
if (pilot_countAmmo(p) <= 0) {
player_message("\erThe ship has no ammo.");
return;
}
/* Player is already at max ammo */
if (pilot_countAmmo(player.p) >= pilot_maxAmmo(player.p)) {
player_message("\erYou are already at max ammo.");
return;
}
if (board_fail(wdw))
return;
/* Steal the ammo */
for (i=0; i<p->noutfits; i++) {
target_outfit_slot = p->outfits[i];
if (target_outfit_slot == NULL)
continue;
target_outfit = target_outfit_slot->outfit;
if (target_outfit == NULL)
continue;
/* outfit isn't a launcher */
if (!outfit_isLauncher(target_outfit)) {
continue;
}
nammo = target_outfit_slot->u.ammo.quantity;
ammo = target_outfit_slot->u.ammo.outfit;
/* launcher has no ammo */
if (ammo == NULL)
continue;
if (nammo <= 0) {
continue;
}
for (x=0; x<player.p->noutfits; x++) {
int nadded = 0;
player_outfit_slot = player.p->outfits[x];
if (player_outfit_slot == NULL)
continue;
player_outfit = player_outfit_slot->outfit;
if (player_outfit == NULL)
continue;
if (!outfit_isLauncher(player_outfit)) {
continue;
}
if (strcmp(ammo->name, player_outfit_slot->u.ammo.outfit->name) != 0) {
continue;
}
/* outfit's ammo matches; try to add to player and remove from target */
nadded = pilot_addAmmo(player.p, player_outfit_slot, ammo, nammo);
nammo -= nadded;
pilot_rmAmmo(p, target_outfit_slot, nadded);
nreloaded += nadded;
if (nadded > 0)
player_message("\epYou looted %d %s(s)", nadded, ammo->name);
if (nammo <= 0) {
break;
}
}
if (nammo <= 0) {
continue;
}
}
if (nreloaded <= 0)
player_message("\erThere is no ammo compatible with your launchers on board.");
pilot_updateMass(player.p);
pilot_weaponSane(player.p);
pilot_updateMass(p);
pilot_weaponSane(p);
board_update(wdw);
}
/**
* @brief Checks to see if the pilot can steal from its target.
*
* @param p Pilot stealing from its target.
* @return 0 if successful, 1 if fails, -1 if fails and kills target.
*/
static int board_trySteal( Pilot *p )
{
Pilot *target;
Damage dmg;
/* Get the target. */
target = pilot_get(p->target);
if (target == NULL)
return 1;
/* See if was successful. */
if (RNGF() > (0.5 * (10. + target->crew)/(10. + p->crew)))
return 0;
/* Triggered self destruct. */
if (RNGF() < 0.4) {
/* Don't actually kill. */
target->shield = 0.;
target->armour = 1.;
/* This will make the boarding ship take the possible faction hit. */
dmg.type = dtype_get("normal");
dmg.damage = 100.;
dmg.penetration = 1.;
dmg.disable = 0.;
pilot_hit( target, NULL, p->id, &dmg, 1 );
/* Return ship dead. */
return -1;
}
return 1;
}
/**
* @brief Checks to see if the hijack attempt failed.
*
* @return 1 on failure to board, otherwise 0.
*/
static int board_fail( unsigned int wdw )
{
int ret;
ret = board_trySteal( player.p );
if (ret == 0)
return 0;
else if (ret < 0) /* killed ship. */
player_message("\epYou have tripped the ship's self-destruct mechanism!");
else /* you just got locked out */
player_message("\epThe ship's security system locks %s out.",
(player.p->ship->crew > 0) ? "your crew" : "you" );
board_exit( wdw, NULL);
return 1;
}
/**
* @brief Updates the boarding window fields.
*
* @param wdw Window to update.
*/
static void board_update( unsigned int wdw )
{
int i, j;
char str[PATH_MAX];
char cred[ECON_CRED_STRLEN];
Pilot* p;
p = pilot_get(player.p->target);
j = 0;
/* Credits. */
credits2str( cred, p->credits, 2 );
j += nsnprintf( &str[j], PATH_MAX, "%s\n", cred );
/* Commodities. */
if ((p->ncommodities==0) && (j < PATH_MAX))
j += snprintf( &str[j], PATH_MAX-j, "none\n" );
else {
for (i=0; i<p->ncommodities; i++) {
if (j > PATH_MAX)
break;
if (p->commodities[i].commodity == NULL)
continue;
j += snprintf( &str[j], PATH_MAX-j, "%d %s\n",
p->commodities[i].quantity, p->commodities[i].commodity->name );
}
}
/* Fuel. */
if (p->fuel <= 0.) {
if (j < PATH_MAX)
j += snprintf( &str[j], PATH_MAX-j, "none\n" );
}
else {
if (j < PATH_MAX)
j += snprintf( &str[j], PATH_MAX-j, "%.0f Units\n", p->fuel );
}
/* Missiles */
int nmissiles = pilot_countAmmo(p);
if (nmissiles <= 0) {
if (j < PATH_MAX)
j += snprintf( &str[j], PATH_MAX-j, "none\n" );
}
else {
if (j < PATH_MAX)
j += snprintf( &str[j], PATH_MAX-j, "%d missiles\n", nmissiles );
}
window_modifyText( wdw, "txtData", str );
}
/**
* @brief Has a pilot attempt to board another pilot.
*
* @param p Pilot doing the boarding.
* @return 1 if target was boarded.
*/
int pilot_board( Pilot *p )
{
Pilot *target;
HookParam hparam[2];
/* Make sure target is sane. */
target = pilot_get(p->target);
if (target == NULL) {
DEBUG("NO TARGET");
return 0;
}
/* Check if can board. */
if (!pilot_isDisabled(target))
return 0;
else if (vect_dist(&p->solid->pos, &target->solid->pos) >
target->ship->gfx_space->sw * PILOT_SIZE_APROX )
return 0;
else if ((pow2(VX(p->solid->vel)-VX(target->solid->vel)) +
pow2(VY(p->solid->vel)-VY(target->solid->vel))) >
(double)pow2(MAX_HYPERSPACE_VEL))
return 0;
else if (pilot_isFlag(target,PILOT_BOARDED))
return 0;
/* Set the boarding flag. */
pilot_setFlag(target, PILOT_BOARDED);
pilot_setFlag(p, PILOT_BOARDING);
/* Set time it takes to board. */
p->ptimer = 3.;
/* Run pilot board hook. */
hparam[0].type = HOOK_PARAM_PILOT;
hparam[0].u.lp.pilot = p->id;
hparam[1].type = HOOK_PARAM_SENTINEL;
pilot_runHookParam(target, PILOT_HOOK_BOARDING, hparam, 1);
hparam[0].u.lp.pilot = target->id;
pilot_runHookParam(target, PILOT_HOOK_BOARD, hparam, 1);
return 1;
}
/**
* @brief Finishes the boarding.
*
* @param p Pilot to finish the boarding.
*/
void pilot_boardComplete( Pilot *p )
{
int ret;
Pilot *target;
credits_t worth;
char creds[ ECON_CRED_STRLEN ];
/* Make sure target is sane. */
target = pilot_get(p->target);
if (target == NULL)
return;
/* In the case of the player take fewer credits. */
if (pilot_isPlayer(target)) {
worth = MIN( 0.1*pilot_worth(target), target->credits );
p->credits += worth;
target->credits -= worth;
credits2str( creds, worth, 2 );
player_message( "\e%c%s\e0 has plundered %s credits from your ship!",
pilot_getFactionColourChar(p), p->name, creds );
}
else {
/* Steal stuff, we only do credits for now. */
ret = board_trySteal(p);
if (ret == 0) {
/* Normally just plunder it all. */
p->credits += target->credits;
target->credits = 0.;
}
}
/* Finish the boarding. */
pilot_rmFlag(p, PILOT_BOARDING);
}
Jump to Line
Something went wrong with that request. Please try again.