/* * Copyright (C) 2002-2007 The Warp Rogue Team * Part of the Warp Rogue Project * * This software is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License. * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY. * * See the license.txt file for more details. */ /* * Module Name: Perks * Description: - */ #define Uses_Ui #define Uses_Util #define Uses_Random #define Uses_ProgramManager #define Uses_Character #define Uses_Stats #define Uses_Party #define Uses_Object #define Uses_DataFile #define Uses_DynamicMessage #define Uses_Economy #define Uses_Talk #define Uses_Perception #define Uses_Equipment #define Uses_Actions #define Uses_Effect #include "mheader.h" #include "perks.h" /* * perk data structure */ typedef struct { /* the name of the perk */ const char name[PERK_NAME_SIZE]; /* true if the perk can be actively used */ bool useable; /* true if the perk can be gained by reading a tome */ bool in_tome; } PERK_DATA; static void battle_tactics_gain(CHARACTER *); static void battle_tactics_add_bonus(void); static void battle_tactics_remove_bonus(void); static void leader_gain(CHARACTER *); static void leader_add_bonus(void); static void leader_remove_bonus(void); /* * perk data */ static const PERK_DATA Perk[MAX_PERKS] = { {"Alertness", false, true}, {"Alien technology", false, true}, {"Battle tactics", false, true}, {"Biomancer", false, true}, {"Blademaster", false, true}, {"Combat evocation", false, true}, {"Critical shot", false, true}, {"Critical strike", false, true}, {"Crushing strike", false, true}, {"Daemon lore", false, true}, {"Daemonologist", false, true}, {"Die hard", false, false}, {"Disarm", true, true}, {"Disease attack", false, true}, {"Doctor", true, true}, {"Drug resistant", false, false}, {"Enhanced parry", false, true}, {"Fast draw", false, true}, {"First aid", false, true}, {"Grenadier", false, true}, {"Immunity to disease", false, false}, {"Immunity to fear", false, true}, {"Immunity to pain", false, false}, {"Immunity to poison", false, false}, {"Imperial language", false, false}, {"Jump pack", true, true}, {"Leader", false, true}, {"Manufacture drugs", true, true}, {"Marksman", false, true}, {"Persuade", false, true}, {"Psychic", false, true}, {"Psychic resistance", false, true}, {"Psychic stability", false, true}, {"Pyromancer", false, true}, {"Quickload", false, true}, {"Rapid shot", false, true}, {"Rapid strike", false, true}, {"Silent evocation", false, true}, {"Sneak attack", false, true}, {"Spot weakness", false, true}, {"Stealth", true, true}, {"Telekinetic", false, true}, {"Telepath", false, true}, {"Uncanny dodge", false, true}, {"Weapon maintenance", false, true} }; /* * the perk screen */ void perk_screen(PERK perk) { command_bar_set(1, CM_EXIT); render_perk_screen(perk); update_screen(); command_bar_get_command(); } /* * returns true if the passed perk is useable */ bool perk_useable(PERK perk) { return Perk[perk].useable; } /* * returns true if the passed perk can be gained by reading a tome */ bool perk_in_tome(PERK perk) { return Perk[perk].in_tome; } /* * returns the number of perks the passed character has */ N_PERKS character_n_perks(const CHARACTER *character) { N_PERKS n_perks; int i; n_perks = 0; for (i = 0; i < MAX_PERKS; i++) { if (character->perk[i]) { ++n_perks; } } return n_perks; } /* * returns the number of useable perks the passed character has */ N_PERKS character_n_useable_perks(const CHARACTER *character) { N_PERKS n_useable_perks; int i; n_useable_perks = 0; for (i = 0; i < MAX_PERKS; i++) { if (character->perk[i] && perk_useable(i)) { ++n_useable_perks; } } return n_useable_perks; } /* * returns the nth useable perk of the passed character */ PERK character_nth_useable_perk(const CHARACTER *character, int nth) { int i; int useable_perk_n; useable_perk_n = 0; for (i = 0; i < MAX_PERKS; i++) { if (character->perk[i] && perk_useable(i)) { ++useable_perk_n; if (useable_perk_n == nth) { return i; } } } die("*** CORE ERROR *** invalid character_nth_useable_perk() call"); /* NEVER REACHED */ return 0; } /* * returns the name of the passed perk */ const char * perk_name(PERK perk) { return Perk[perk].name; } /* * name -> perk index */ PERK name_to_perk(const char *name) { PERK i; for (i = 0; i < MAX_PERKS; i++) { if (strings_equal(name, Perk[i].name)) { return i; } } die("*** CORE ERROR *** invalid perk: %s", name); return PK_NIL; } /* * returns the description of a perk */ char * perk_description(char *description, PERK perk) { return data_file_perk_description(description, perk); } /* * gives the passed character the passed perk */ void give_perk(CHARACTER *character, PERK perk) { if (perk == PK_BATTLE_TACTICS) { battle_tactics_gain(character); } else if (perk == PK_LEADER) { leader_gain(character); } else { character->perk[perk] = true; } } /* * perk: stealth (use) */ bool stealth_use(CHARACTER *character) { if (enemies_are_watching(character)) { if (is_player_controlled_character(character)) { message(SID_GAME, "You cannot use this perk " \ "while you are being watched." ); } return false; } if (character->armour != NULL) { const OBJECT_DATA *object_data; object_data = object_static_data(character->armour); if (object_data->subtype != OSTYPE_LIGHT) { if (is_player_controlled_character(character)) { message(SID_GAME, "Your armour is too heavy." ); } return false; } } if (noisy_object_in_use(character)) { if (is_player_controlled_character(character)) { message(SID_GAME, "Noisy object in use."); } return false; } activate_stealth(character); if (character->party == PARTY_PLAYER) { dynamic_message(MSG_FADE_INTO_SHADOWS, character, NULL, MOT_NIL ); } return true; } /* * perk: disarm (use) */ void disarm_use(CHARACTER *character, CHARACTER *target) { if (!d100_test_passed(character->stat[S_CC].current) || d100_test_passed(target->stat[S_CC].current)) { dynamic_message(MSG_DISARM_FAILS, character, target, MOT_CHARACTER ); return; } action_drop_object(target, target->weapon, true); dynamic_message(MSG_DISARM, character, target, MOT_CHARACTER); } /* * perk: doctor (use) */ void doctor_use(CHARACTER *character, CHARACTER *target) { if (!d100_test_passed(character->stat[S_SG].current)) { dynamic_message(MSG_DOCTOR_FAILS, character, target, MOT_CHARACTER ); return; } dynamic_message(MSG_DOCTOR, character, target, MOT_CHARACTER); if (character_has_flag(target, CF_POISONED)) { effect_terminate(target, ET_POISONED); } if (character_has_flag(target, CF_DISEASED)) { effect_terminate(target, ET_DISEASED); } if (character_has_flag(target, CF_STUNNED)) { effect_terminate(target, ET_STUNNED); } target->injury -= dice(2, 5); if (target->injury < 0) target->injury = 0; } /* * perk: manufacture drugs (use) */ bool manufacture_drugs_use(CHARACTER *character) { manufacture_screen(character); return true; } /* * perk: battle tactics (character joins party) */ void battle_tactics_join(CHARACTER *character) { bool has_tactics; has_tactics = party_has_battle_tactics(); if (!has_tactics && character->perk[PK_BATTLE_TACTICS]) { battle_tactics_add_bonus(); } else if (has_tactics && !character->perk[PK_BATTLE_TACTICS]) { character->stat[S_IN].current += BATTLE_TACTICS_BONUS; character->stat[S_IN].total += BATTLE_TACTICS_BONUS; } } /* * perk: battle tactics (character leaves party) */ void battle_tactics_leave(CHARACTER *character) { bool has_tactics; has_tactics = party_has_battle_tactics(); if (!has_tactics && character->perk[PK_BATTLE_TACTICS]) { battle_tactics_remove_bonus(); } else if (has_tactics && !character->perk[PK_BATTLE_TACTICS]) { character->stat[S_IN].current -= BATTLE_TACTICS_BONUS; character->stat[S_IN].total -= BATTLE_TACTICS_BONUS; } } /* * perk: leader (character joins party) */ void leader_join(CHARACTER *character) { bool has_leader; has_leader = party_has_leader(); if (!has_leader && character->perk[PK_LEADER]) { leader_add_bonus(); } else if (has_leader && !character->perk[PK_LEADER]) { character->stat[S_LD].current += LEADER_BONUS; character->stat[S_LD].total += LEADER_BONUS; } } /* * perk: leader (character leaves party) */ void leader_leave(CHARACTER *character) { bool has_leader; has_leader = party_has_leader(); if (!has_leader && character->perk[PK_LEADER]) { leader_remove_bonus(); } else if (has_leader && !character->perk[PK_LEADER]) { character->stat[S_LD].current -= LEADER_BONUS; character->stat[S_LD].total -= LEADER_BONUS; } } /* * perk: uncanny dodge (hit modifier) */ HIT_MODIFIER uncanny_dodge_modifier(const CHARACTER *character) { const OBJECT *armour; armour = character->armour; if (armour == NULL) { return UNCANNY_DODGE_MODIFIER; } if (object_static_data(armour)->subtype == OSTYPE_HEAVY) { return 0; } return UNCANNY_DODGE_MODIFIER; } /* * perk: rapid strike * returns true if the requirements for a double strike are met */ bool rapid_strike_requirements_met(const CHARACTER *character, const OBJECT *character_weapon ) { WEIGHT weapon_weight; if (character_weapon == NULL) { return true; } weapon_weight = object_static_data(character_weapon)->weight; if (weapon_weight > character->stat[S_ST].current / 2) { return false; } return true; } /* * perk: battle tactics (character gains perk) */ static void battle_tactics_gain(CHARACTER *character) { bool add_bonus; add_bonus = true; character->stat[S_IN].current += BATTLE_TACTICS_BONUS; character->stat[S_IN].total += BATTLE_TACTICS_BONUS; if (character->party != PARTY_PLAYER) { character->perk[PK_BATTLE_TACTICS] = true; return; } if (party_has_battle_tactics()) { add_bonus = false; } character->perk[PK_BATTLE_TACTICS] = true; if (add_bonus) { battle_tactics_add_bonus(); } } /* * perk: battle tactics (adds bonus) */ static void battle_tactics_add_bonus(void) { LIST_NODE *node; CHARACTER *character; for (node = party_player()->head; node != NULL; node = node->next) { character = (CHARACTER *)node->data; if (character->perk[PK_BATTLE_TACTICS]) { continue; } character->stat[S_IN].current += BATTLE_TACTICS_BONUS; character->stat[S_IN].total += BATTLE_TACTICS_BONUS; } } /* * perk: battle tactics (removes bonus) */ static void battle_tactics_remove_bonus(void) { LIST_NODE *node; CHARACTER *character; for (node = party_player()->head; node != NULL; node = node->next) { character = (CHARACTER *)node->data; if (character->perk[PK_BATTLE_TACTICS]) { continue; } character->stat[S_IN].current -= BATTLE_TACTICS_BONUS; character->stat[S_IN].total -= BATTLE_TACTICS_BONUS; } } /* * perk: leader (character gains perk) */ static void leader_gain(CHARACTER *character) { bool add_bonus; add_bonus = true; character->stat[S_LD].current += LEADER_BONUS; character->stat[S_LD].total += LEADER_BONUS; if (character->party != PARTY_PLAYER) { character->perk[PK_LEADER] = true; return; } if (party_has_leader()) { add_bonus = false; } character->perk[PK_LEADER] = true; if (add_bonus) { leader_add_bonus(); } } /* * perk: leader (adds bonus) */ static void leader_add_bonus(void) { LIST_NODE *node; CHARACTER *character; for (node = party_player()->head; node != NULL; node = node->next) { character = (CHARACTER *)node->data; if (character->perk[PK_LEADER]) { continue; } character->stat[S_LD].current += LEADER_BONUS; character->stat[S_LD].total += LEADER_BONUS; } } /* * perk: leader (removes bonus) */ static void leader_remove_bonus(void) { LIST_NODE *node; CHARACTER *character; for (node = party_player()->head; node != NULL; node = node->next) { character = (CHARACTER *)node->data; if (character->perk[PK_LEADER]) { continue; } character->stat[S_LD].current -= LEADER_BONUS; character->stat[S_LD].total -= LEADER_BONUS; } }