/* * 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: Effect * Description: Effects */ #define Uses_Util #define Uses_Character #define Uses_Random #define Uses_Stats #define Uses_Event #define Uses_DynamicMessage #define Uses_Death #define Uses_Ai #include "mheader.h" #include "effect.h" #define N_EFFECTS 5 #define EFFECT_NAME_SIZE 20 #define POISON_DAMAGE 1 #define DISEASE_RESISTANCE_TEST_MODIFIER -20 /* * effect data structure */ typedef struct { /* the name of the effect */ const char name[EFFECT_NAME_SIZE]; /* the associated character flag */ const CHARACTER_FLAG flag; /* called when the effect is activated */ void (* const activate)(EVENT *); /* called when the effect is terminated */ void (* const terminate)(EVENT *); } EFFECT_DATA; static bool effect_already_in_effect(const CHARACTER *, EFFECT ); static void effect_poisoned(EVENT *); static void effect_poisoned_terminate(EVENT *); static void effect_diseased(EVENT *); static void effect_diseased_terminate(EVENT *); static void effect_broken(EVENT *); static void effect_broken_terminate(EVENT *); static void effect_stunned(EVENT *); static void effect_stunned_terminate(EVENT *); static void effect_psychic_overload(EVENT *); static void effect_psychic_overload_terminate(EVENT *); /* * effect data */ static const EFFECT_DATA Effect[N_EFFECTS] = { {"Poisoned", CF_POISONED, effect_poisoned, effect_poisoned_terminate }, {"Diseased", CF_DISEASED, effect_diseased, effect_diseased_terminate }, {"Broken", CF_BROKEN, effect_broken, effect_broken_terminate }, {"Stunned", CF_STUNNED, effect_stunned, effect_stunned_terminate }, {"Psychic overload", CF_PSYCHIC_OVERLOAD, effect_psychic_overload, effect_psychic_overload_terminate } }; /* * activates an effect */ void effect_activate(CHARACTER *character, EFFECT effect, PHASE_TIME duration) { const EFFECT_DATA *effect_data; EVENT *event; effect_data = &Effect[effect]; if (effect_already_in_effect(character, effect)) { return; } if (effect == ET_PSYCHIC_OVERLOAD) { } else if (effect == ET_BROKEN) { dynamic_message(MSG_MORALE_BROKEN, character, NULL, MOT_NIL ); } else { dynamic_message(MSG_EFFECT_ACTIVATION, character, effect_data->name, MOT_STRING ); } event = event_create(EVT_EFFECT_TERMINATION, duration); event->character = character; event->effect = effect; (*effect_data->activate)(event); character_set_flag(character, effect_data->flag); event_set(event); } /* * terminates an effect */ void effect_terminate(CHARACTER *character, EFFECT effect) { event_execute( effect_termination_event(character, effect) ); } /* * terminates an effect (event) */ void effect_terminate_event(EVENT *event) { const EFFECT_DATA *effect_data; effect_data = &Effect[event->effect]; if (event->effect == ET_PSYCHIC_OVERLOAD) { /* no message yet */ } else if (event->effect == ET_BROKEN) { dynamic_message(MSG_MORALE_RECOVERED, event->character, NULL, MOT_NIL ); } else { dynamic_message(MSG_EFFECT_TERMINATION, event->character, effect_data->name, MOT_STRING ); } (*effect_data->terminate)(event); character_remove_flag(event->character, effect_data->flag); } /* * returns true if the character has managed to resist poison */ bool poison_resisted(const CHARACTER *character) { if (character_has_flag(character, CF_MACHINE)) { return true; } if (character->perk[PK_IMMUNITY_TO_POISON]) { return true; } return false; } /* * returns true if the character has managed to resist disease */ bool disease_resisted(const CHARACTER *character) { if (character_has_flag(character, CF_MACHINE)) { return true; } if (character->perk[PK_IMMUNITY_TO_DISEASE]) { return true; } if (d100_test_passed(character->stat[S_TN].current + DISEASE_RESISTANCE_TEST_MODIFIER)) { return true; } return false; } /* * returns true if the character has managed to resist pain */ bool pain_resisted(const CHARACTER *character) { if (character_has_flag(character, CF_MACHINE)) { return true; } if (character_has_flag(character, CF_STOIC) || character->perk[PK_IMMUNITY_TO_PAIN]) { return true; } if (d100_test_passed(character->stat[S_LD].current)) { return true; } return false; } /* * returns true if the character has managed to resist fear */ bool fear_resisted(const CHARACTER *character) { if (character_has_flag(character, CF_MACHINE)) { return true; } if (character_has_flag(character, CF_STOIC) || character->perk[PK_IMMUNITY_TO_FEAR]) { return true; } if (d100_test_passed(character->stat[S_LD].current)) { return true; } return false; } /* * poison damage event */ void poison_damage(EVENT *event) { CHARACTER *character; character = event->character; character->injury += POISON_DAMAGE; handle_destruction(&character->location, NULL, DT_POISON); } /* * returns true if passed effect is already in effect (sic!) */ static bool effect_already_in_effect(const CHARACTER *character, EFFECT effect ) { const EFFECT_DATA *effect_data; effect_data = &Effect[effect]; if (character_has_flag(character, effect_data->flag)) { return true; } return false; } /* * effect: poisoned */ static void effect_poisoned(EVENT *event) { EVENT *damage_event; damage_event = event_create(EVT_POISON_DAMAGE, TIME_CONTINUOUS); damage_event->character = event->character; event_set(damage_event); } /* * effect: poisoned (termination) */ static void effect_poisoned_terminate(EVENT *event) { EVENT *damage_event; damage_event = remove_character_event(event->character, EVT_POISON_DAMAGE ); event_destroy(damage_event); } /* * effect: diseased */ static void effect_diseased(EVENT *event) { int i; for (i = 0; i < MAX_STATS; i++) { event->stat_modifier[i] = -dice(2, 10); } stat_modifiers_apply(event->character, event->stat_modifier); } /* * effect: diseased (termination) */ static void effect_diseased_terminate(EVENT *event) { stat_modifiers_revert(event->character, event->stat_modifier, STAT_MODIFIER_MAX ); } /* * effect: broken */ static void effect_broken(EVENT *event) { ai_set_state(event->character, AI_STATE_FLEE); } /* * effect: broken (termination) */ static void effect_broken_terminate(EVENT *event) { NOT_USED(event); } /* * effect: stunned */ static void effect_stunned(EVENT *event) { EVENT *removed_event; removed_event = remove_character_event( event->character, EVT_RELOAD_WEAPON ); if (removed_event != NULL) { event_destroy(removed_event); } removed_event = remove_character_event( event->character, EVT_UNJAM_WEAPON ); if (removed_event != NULL) { event_destroy(removed_event); } } /* * effect: stunned (termination) */ static void effect_stunned_terminate(EVENT *event) { /* nothing to do here */ NOT_USED(event); } /* * effect: psychic overload */ static void effect_psychic_overload(EVENT *event) { STAT_MODIFIER wp_reduction; dynamic_message(MSG_PSYCHIC_OVERLOAD, event->character, NULL, MOT_NIL ); wp_reduction = -dice(4, 10); if (character_has_flag(event->character, CF_NOMAT) && !event->character->perk[PK_DRUG_RESISTANT]) { wp_reduction *= 2; } event->stat_modifier[S_FR] = wp_reduction; stat_modifiers_apply(event->character, event->stat_modifier); } /* * effect: psychic overload (termination) */ static void effect_psychic_overload_terminate(EVENT *event) { stat_modifiers_revert(event->character, event->stat_modifier, STAT_MODIFIER_MAX ); }