/* * File: xom.cc * Summary: All things Xomly * Written by: Zooko * * Modified for Crawl Reference by $Author$ on $Date$ */ #include "AppHdr.h" #include "beam.h" #include "branch.h" #include "effects.h" #include "it_use2.h" #include "items.h" #include "makeitem.h" #include "message.h" #include "misc.h" #include "mon-util.h" #include "monplace.h" #include "monstuff.h" #include "mutation.h" #include "ouch.h" #include "player.h" #include "randart.h" #include "religion.h" #include "spells3.h" #include "spl-cast.h" #include "spl-util.h" #include "state.h" #include "stuff.h" #include "view.h" #include "xom.h" #if DEBUG_RELIGION # define DEBUG_DIAGNOSTICS 1 # define DEBUG_GIFTS 1 #endif #if DEBUG_XOM # define DEBUG_DIAGNOSTICS 1 # define DEBUG_RELIGION 1 # define DEBUG_GIFTS 1 #endif // Which spell? First I copied all spells from you_spells(), then I // filtered some out (especially conjurations). Then I sorted them in // roughly ascending order of power. ([ds] Removed SUMMON_GUARDIAN and // SUMMON_DAEVA which are inappropriate for a god of chaos; need to // investigate substitutes). // static const spell_type xom_spells[] = { SPELL_BLINK, SPELL_CONFUSING_TOUCH, SPELL_MAGIC_MAPPING, SPELL_DETECT_ITEMS, SPELL_DETECT_CREATURES, SPELL_MASS_CONFUSION, SPELL_MASS_SLEEP, SPELL_CONTROLLED_BLINK, SPELL_STONESKIN, SPELL_RING_OF_FLAMES, SPELL_OLGREBS_TOXIC_RADIANCE, SPELL_TUKIMAS_VORPAL_BLADE, SPELL_FIRE_BRAND, SPELL_FREEZING_AURA, SPELL_POISON_WEAPON, SPELL_STONEMAIL, SPELL_WARP_BRAND, SPELL_ALTER_SELF, SPELL_TUKIMAS_DANCE, SPELL_SUMMON_BUTTERFLIES, SPELL_SUMMON_SMALL_MAMMAL, SPELL_CALL_IMP, SPELL_SUMMON_SCORPIONS, SPELL_FLY, SPELL_SPIDER_FORM, SPELL_STATUE_FORM, SPELL_ICE_FORM, SPELL_DRAGON_FORM, SPELL_SWARM, SPELL_SUMMON_WRAITHS, SPELL_SHADOW_CREATURES, SPELL_SUMMON_ELEMENTAL, SPELL_SUMMON_HORRIBLE_THINGS, SPELL_SUMMON_LARGE_MAMMAL, SPELL_CONJURE_BALL_LIGHTNING, SPELL_SUMMON_DRAGON, SPELL_DEATH_CHANNEL, SPELL_NECROMUTATION }; static const char *xom_try_this[] = { "\"Perhaps you should try this instead.\"", "\"Maybe this would work better.\"", "\"Catch!\"" }; static const char *xom_try_this_ring[] = { "\"Try this.\"", "\"Catch!\"", "\"Take this!\"" }; static const char *xom_try_this_other_thing[] = { "\"Perhaps you should try this instead.\"", "\"Have you considered using one of these?\"", "\"How about this?\"" }; static const char *xom_try_these_duds[] = { "\"Perhaps you should try this instead.\"", "\"Have you considered wearing one of these?\"", "\"Here you go.\"" }; static const char *xom_generic_beneficence[] = { "Xom grants you a gift!", "\"Here.\"", "Xom's generous nature manifests itself.", "Xom grants you an implement of some kind.", "\"Take this instrument of something!\"", "\"Take this token of my esteem.\"", "Xom smiles on you." }; const char *describe_xom_favour() { return (you.piety > 160) ? "A beloved toy of Xom." : (you.piety > 145) ? "A favourite toy of Xom." : (you.piety > 130) ? "A very special toy of Xom." : (you.piety > 115) ? "A special toy of Xom." : (you.piety > 100) ? "A toy of Xom." : (you.piety > 85) ? "A plaything of Xom." : (you.piety > 70) ? "A special plaything of Xom." : (you.piety > 55) ? "A very special plaything of Xom." : (you.piety > 40) ? "A favourite plaything of Xom." : "A beloved plaything of Xom."; } bool xom_is_nice() { // If you.gift_timeout was == 0, then Xom was BORED. // He HATES that. return (you.gift_timeout > 0 && you.piety > 100) || coinflip(); } static const char* xom_message_arrays[NUM_XOM_MESSAGE_TYPES][6] = { // XM_NORMAL { "Xom roars with laughter!", "Xom thinks this is hilarious!", "Xom is highly amused!", "Xom is amused.", "Xom is mildly amused.", "Xom is interested." }, // XM_INTRIGUED { "Xom is fascinated!", "Xom is very intrigued!", "Xom is intrigued!", "Xom is extremely interested.", "Xom is very interested.", "Xom is interested." } }; static void _xom_is_stimulated(int maxinterestingness, const char* message_array[], bool force_message) { if (you.religion != GOD_XOM || maxinterestingness <= 0) return; // Xom is not stimulated by his own acts, at least not directly. if (crawl_state.which_god_acting() == GOD_XOM) return; int interestingness = random2(maxinterestingness); #if DEBUG_RELIGION || DEBUG_GIFTS || DEBUG_XOM mprf(MSGCH_DIAGNOSTICS, "Xom: maxinterestingness = %d, interestingness = %d", maxinterestingness, interestingness); #endif if (interestingness > 255) interestingness = 255; bool was_stimulated = false; if (interestingness > you.gift_timeout && interestingness >= 12) { you.gift_timeout = interestingness; was_stimulated = true; } if (was_stimulated || force_message) god_speaks(GOD_XOM, ((interestingness > 200) ? message_array[5] : (interestingness > 100) ? message_array[4] : (interestingness > 75) ? message_array[3] : (interestingness > 50) ? message_array[2] : (interestingness > 25) ? message_array[1] : message_array[0])); } void xom_is_stimulated(int maxinterestingness, xom_message_type message_type, bool force_message) { _xom_is_stimulated(maxinterestingness, xom_message_arrays[message_type], force_message); } void xom_is_stimulated(int maxinterestingness, const std::string& message, bool force_message) { const char* message_array[6]; for (int i = 0; i < 6; i++) message_array[i] = message.c_str(); _xom_is_stimulated(maxinterestingness, message_array, force_message); } void xom_makes_you_cast_random_spell(int sever) { int spellenum = sever; god_acting gdact(GOD_XOM); const int nxomspells = ARRAYSIZE(xom_spells); if (spellenum > nxomspells) spellenum = nxomspells; const spell_type spell = xom_spells[random2(spellenum)]; god_speaks(GOD_XOM, "Xom's power flows through you!"); #if DEBUG_DIAGNOSTICS || DEBUG_RELIGION || DEBUG_XOM mprf(MSGCH_DIAGNOSTICS, "Xom_acts();spell: %d, spellenum: %d", spell, spellenum); #endif your_spells(spell, sever, false); } static void xom_make_item(object_class_type base, int subtype, int power, const char *failmsg = "\"No, never mind.\"") { int thing_created = items(true, base, subtype, true, power, MAKE_ITEM_RANDOM_RACE); if (thing_created == NON_ITEM) { god_speaks(GOD_XOM, failmsg); return; } god_acting gdact(GOD_XOM); move_item_to_grid( &thing_created, you.x_pos, you.y_pos ); canned_msg(MSG_SOMETHING_APPEARS); stop_running(); origin_acquired(mitm[thing_created], GOD_XOM); } static object_class_type get_unrelated_wield_class(object_class_type ref) { object_class_type objtype = OBJ_WEAPONS; if (ref == OBJ_WEAPONS) { if (random2(10)) objtype = OBJ_STAVES; else objtype = OBJ_MISCELLANY; } else if (ref == OBJ_STAVES) { if (random2(10)) objtype = OBJ_WEAPONS; else objtype = OBJ_MISCELLANY; } else { const int temp_rand = random2(3); objtype = (temp_rand == 0) ? OBJ_WEAPONS : (temp_rand == 1) ? OBJ_STAVES : OBJ_MISCELLANY; } return (objtype); } static bool xom_annoyance_gift(int power) { god_acting gdact(GOD_XOM); if (coinflip() && player_in_a_dangerous_place()) { const item_def *weapon = you.weapon(); // Xom has a sense of humour. if (coinflip() && weapon && weapon->cursed()) { // If you are wielding a cursed item then Xom will give // you an item of that same type. Ha ha! god_speaks(GOD_XOM, RANDOM_ELEMENT(xom_try_this)); if (coinflip()) // For added humour, give the same sub-type. xom_make_item(weapon->base_type, weapon->sub_type, power * 3); else acquirement(weapon->base_type, GOD_XOM); return (true); } const item_def *gloves = you.slot_item(EQ_GLOVES); if (coinflip() && gloves && gloves->cursed()) { // If you are wearing cursed gloves then Xom will give you // a ring. Ha ha! // // A random ring. (Not necessarily a good one.) god_speaks(GOD_XOM, RANDOM_ELEMENT(xom_try_this)); xom_make_item(OBJ_JEWELLERY, get_random_ring_type(), power * 3); return (true); }; const item_def *amulet = you.slot_item(EQ_AMULET); if (coinflip() && amulet && amulet->cursed()) { // If you are wearing a cursed amulet then Xom will give // you an amulet. Ha ha! god_speaks(GOD_XOM, RANDOM_ELEMENT(xom_try_this)); xom_make_item(OBJ_JEWELLERY, get_random_amulet_type(), power * 3); return (true); }; const item_def *left_ring = you.slot_item(EQ_LEFT_RING); const item_def *right_ring = you.slot_item(EQ_RIGHT_RING); if (coinflip() && ((left_ring && left_ring->cursed()) || (right_ring && right_ring->cursed()))) { // If you are wearing a cursed ring then Xom will give you // a ring. Ha ha! god_speaks(GOD_XOM, RANDOM_ELEMENT(xom_try_this_ring)); xom_make_item(OBJ_JEWELLERY, get_random_ring_type(), power * 3); return (true); } if (one_chance_in(5) && weapon) { // Xom will give you a wielded item of a type different // than what you are currently wielding. god_speaks(GOD_XOM, RANDOM_ELEMENT(xom_try_this_other_thing)); const object_class_type objtype = get_unrelated_wield_class(weapon->base_type); if (power > random2(256)) acquirement(objtype, GOD_XOM); else xom_make_item(objtype, OBJ_RANDOM, power * 3); return (true); } } return (false); } bool xom_gives_item(int power) { if (xom_annoyance_gift(power)) return (true); const item_def *cloak = you.slot_item(EQ_CLOAK); if (coinflip() && cloak && cloak->cursed()) { // If you are wearing a cursed cloak then Xom will give you a // cloak or body armour . Ha ha! god_speaks(GOD_XOM, RANDOM_ELEMENT(xom_try_these_duds)); xom_make_item(OBJ_ARMOUR, random2(10)? get_random_body_armour_type(you.your_level * 2) : ARM_CLOAK, power * 3); return (true); } god_speaks(GOD_XOM, RANDOM_ELEMENT(xom_generic_beneficence)); // There are two kinds of Xom gifts: acquirement and random // object. The result from acquirement is very good (usually as // good or better than random object), and it is sometimes tuned // to the player's skills and nature. Being tuned to the player's // skills and nature is not very Xomlike... if (power > random2(256)) { // random-type acquirement const int r = random2(7); const object_class_type objtype = (r == 0) ? OBJ_WEAPONS : (r == 1) ? OBJ_ARMOUR : (r == 2) ? OBJ_JEWELLERY : (r == 3) ? OBJ_BOOKS : (r == 4) ? OBJ_STAVES : (r == 5) ? OBJ_FOOD : (r == 6) ? OBJ_MISCELLANY : OBJ_GOLD; god_acting gdact(GOD_XOM); acquirement(objtype, GOD_XOM); } else { // random-type random object xom_make_item(OBJ_RANDOM, OBJ_RANDOM, power * 3); } more(); return (true); } bool there_are_monsters_nearby() { int ystart = you.y_pos - 9, xstart = you.x_pos - 9; int yend = you.y_pos + 9, xend = you.x_pos + 9; if (xstart < 0) xstart = 0; if (ystart < 0) ystart = 0; if (xend >= GXM) xend = GXM; if (ystart >= GYM) yend = GYM; /* monster check */ for ( int y = ystart; y < yend; ++y ) { for ( int x = xstart; x < xend; ++x ) { /* if you can see a nonfriendly monster then you feel unsafe */ if ( see_grid(x,y) ) { const int targ_monst = mgrd[x][y]; if ( targ_monst != NON_MONSTER ) { const monsters *mon = &menv[targ_monst]; if (!mons_is_submerged(mon)) return (true); } } } } return (false); } monsters *get_random_nearby_monster() { monsters *monster = NULL; /* not particular efficient, but oh well */ for (int it = 0, num = 0; it < MAX_MONSTERS; it++) { monsters *mons = &menv[it]; if (mons->alive() && mons_near(mons) && !mons_is_submerged(mons) && one_chance_in(++num)) { monster = mons; } } return (monster); } static monster_type xom_random_demon(int sever, bool use_greater_demons = true) { const int roll = random2(1000 - (27 - you.experience_level) * 10); #ifdef DEBUG_DIAGNOSTICS mprf(MSGCH_DIAGNOSTICS, "xom_random_demon: sever = %d, roll: %d", sever, roll); #endif const demon_class_type dct = roll >= 850 ? DEMON_GREATER : roll >= 340 ? DEMON_COMMON : DEMON_LESSER; const monster_type demontype = summon_any_demon( use_greater_demons || dct != DEMON_GREATER? dct : DEMON_COMMON); return (demontype); } // Returns a demon suitable for use in Xom's punishments, filtering out the // really nasty ones early on. static monster_type xom_random_punishment_demon(int sever) { monster_type demon = MONS_PROGRAM_BUG; do demon = xom_random_demon(sever); while ((demon == MONS_HELLION && you.experience_level < 12 && !one_chance_in(3 + (12 - you.experience_level) / 2))); return (demon); } // The nicer stuff (note: these things are not necessarily nice) static bool xom_is_good(int sever) { bool done = false; god_acting gdact(GOD_XOM); // This series of random calls produces a poisson-looking distribution: // initial hump, plus a long-ish tail. if (random2(sever) <= 1) { god_speaks(GOD_XOM, random_choose_string( "\"Go forth and destroy!\"", "\"Go forth and cause havoc, mortal!\"", "Xom grants you a minor favour.", "Xom smiles on you.", NULL)); potion_type type = (potion_type)random_choose( POT_HEALING, POT_HEAL_WOUNDS, POT_SPEED, POT_MIGHT, POT_INVISIBILITY, POT_BERSERK_RAGE, POT_EXPERIENCE, -1); // downplay this one a bit if (type == POT_EXPERIENCE && !one_chance_in(6)) type = POT_BERSERK_RAGE; if (type == POT_BERSERK_RAGE) you.berserk_penalty = NO_BERSERK_PENALTY; potion_effect(type, 150); done = true; } else if (random2(sever) <= 2) { xom_makes_you_cast_random_spell(sever); done = true; } else if (random2(sever) <= 3) { god_speaks(GOD_XOM, random_choose_string( "\"Serve the mortal, my children!\"", "Xom grants you some temporary aid.", "Xom momentarily opens a gate.", NULL)); int numdemons = std::min(random2(random2(random2(sever+1)+1)+1)+2, 16); for (int i = 0; i < numdemons; i++) { create_monster(xom_random_demon(sever), 3, BEH_GOD_GIFT, you.x_pos, you.y_pos, you.pet_target, MONS_PROGRAM_BUG); } done = true; } else if (random2(sever) <= 4) { const int radius = random2avg(sever/2, 3); god_speaks(GOD_XOM, random_choose_string( // XXX need some more creative flavor text "Xom alters the dungeon around you.", NULL)); vitrify_area(radius); done = true; } else if (random2(sever) <= 5) { xom_gives_item(sever); done = true; } else if (random2(sever) <= 6) { if (create_monster(xom_random_demon(sever), 6, BEH_GOD_GIFT, you.x_pos, you.y_pos, you.pet_target, MONS_PROGRAM_BUG) != -1) { god_speaks(GOD_XOM, random_choose_string( "\"Serve the mortal, my child!\"", "\"Serve the toy, my child!\"", "Xom opens a gate.", NULL)); done = true; } } else if (random2(sever) <= 7) { if (! there_are_monsters_nearby()) goto try_again; monsters* mon = get_random_nearby_monster(); if (mon && mon->holiness() == MH_NATURAL) { god_speaks(GOD_XOM, random_choose_string( "\"This might be better!\"", "\"Hum-dee-hum-dee-hum...\"", "Xom's power touches on a nearby monster.", "You hear Xom's avuncular chuckle.", NULL)); if (mons_friendly(mon)) monster_polymorph(mon, RANDOM_MONSTER, PPT_MORE); else monster_polymorph(mon, RANDOM_MONSTER, PPT_LESS); done = true; } } else if (random2(sever) <= 8) { xom_gives_item(sever); done = true; } else if (random2(sever) <= 9) { if (you.is_undead) goto try_again; god_speaks(GOD_XOM, random_choose_string( "\"You need some minor adjustments, mortal!\"", "\"Let me alter your pitiful body.\"", "Xom's power touches on you for a moment.", "You hear Xom's maniacal cackling.", NULL)); mpr("Your body is suffused with distortional energy."); set_hp(1 + random2(you.hp), false); deflate_hp(you.hp_max / 2, true); bool failMsg = true; for (int i = random2(4); i >= 0; --i) { if (mutate(RANDOM_GOOD_MUTATION, failMsg, true)) done = true; else failMsg = false; } } else if (random2(sever) <= 10) { if (create_monster( xom_random_demon(sever, one_chance_in(8)), 0, BEH_GOD_GIFT, you.x_pos, you.y_pos, you.pet_target, MONS_PROGRAM_BUG ) != -1) { god_speaks(GOD_XOM, random_choose_string( "Xom grants you a demonic assistant.", "Xom grants you a demonic servitor.", "Xom opens a gate.", NULL)); done = true; } } else if (random2(sever) <= 11) { if (player_in_a_dangerous_place()) if (you.hp <= random2(201)) you.attribute[ATTR_DIVINE_LIGHTNING_PROTECTION] = 1; mpr("The area is suffused with divine lightning!"); bolt beam; beam.beam_source = NON_MONSTER; beam.type = dchar_glyph(DCHAR_FIRED_BURST); beam.damage = dice_def( 3, 30 ); beam.flavour = BEAM_ELECTRICITY; beam.target_x = you.x_pos; beam.target_y = you.y_pos; beam.name = "blast of lightning"; beam.colour = LIGHTCYAN; beam.thrower = KILL_MISC; beam.aux_source = "Xom's lightning strike"; beam.ex_size = 2; beam.is_tracer = false; beam.is_explosion = true; explosion(beam); if (you.attribute[ATTR_DIVINE_LIGHTNING_PROTECTION]) { mpr("Your divine protection wanes."); you.attribute[ATTR_DIVINE_LIGHTNING_PROTECTION] = 0; } done = true; } try_again: return (done); } static bool xom_is_bad(int sever) { bool done = false; god_acting gdact(GOD_XOM); while (!done) { if (random2(sever) <= 2) { god_speaks(GOD_XOM, random_choose_string( "Xom almost notices you.", "Xom's attention almost turns to you for a moment.", "Xom's power almost touches on you for a moment.", "You almost hear Xom's maniacal laughter.", NULL)); miscast_effect( SPTYP_RANDOM, 0, 0, 0, "the mischief of Xom" ); done = true; } else if (random2(sever) <= 3) { god_speaks(GOD_XOM, random_choose_string( "Xom notices you.", "Xom's attention turns to you for a moment.", "Xom's power touches on you for a moment.", "You hear Xom's maniacal laughter.", NULL)); miscast_effect( SPTYP_RANDOM, 0, 0, random2(2), "the capriciousness of Xom" ); done = true; } else if (random2(sever) <= 4) { god_speaks(GOD_XOM, random_choose_string( "\"Suffer!\"", "Xom's malign attention turns to you for a moment.", "Xom's power touches on you for a moment.", "You hear Xom's maniacal laughter.", NULL)); lose_stat(STAT_RANDOM, 1 + random2(3), true, "the capriciousness of Xom" ); done = true; } else if (random2(sever) <= 5) { god_speaks(GOD_XOM, random_choose_string( "Xom notices you.", "Xom's attention turns to you for a moment.", "Xom's power touches on you for a moment.", "You hear Xom's maniacal laughter.", NULL)); miscast_effect( SPTYP_RANDOM, 0, 0, random2(3), "the capriciousness of Xom" ); done = true; } else if (random2(sever) <= 6) { if (you.is_undead) goto try_again; god_speaks(GOD_XOM, random_choose_string( "\"You need some minor improvements, mortal!\"", "\"Let me alter your body.\"", "Xom's power brushes against you for a moment.", "You hear Xom's avuncular chuckle.", NULL)); mpr("Your body is suffused with distortional energy."); set_hp(1 + random2(you.hp), false); deflate_hp(you.hp_max / 2, true); bool failMsg = true; for (int i = random2(4); i >= 0; --i) { if (mutate(RANDOM_XOM_MUTATION, failMsg, true)) done = true; else failMsg = false; } } else if (random2(sever) <= 7) { if (! there_are_monsters_nearby()) goto try_again; monsters* mon = get_random_nearby_monster(); ASSERT (mon != NULL); if ( mon->holiness() == MH_NATURAL ) { god_speaks(GOD_XOM, random_choose_string( "\"This might be better!\"", "\"Hum-dee-hum-dee-hum...\"", "Xom's power touches on a nearby monster.", "You hear Xom's avuncular chuckle.", NULL)); if (mons_friendly(mon)) monster_polymorph(mon, RANDOM_MONSTER, PPT_LESS); else monster_polymorph(mon, RANDOM_MONSTER, PPT_MORE); done = true; } } else if (random2(sever) <= 8) { if (you.is_undead) goto try_again; god_speaks(GOD_XOM, random_choose_string( "\"You have displeased me, mortal.\"", "\"You have grown too confident for your meagre worth.\"", "Xom's power touches on you for a moment.", "You hear Xom's maniacal laughter.", NULL)); if (one_chance_in(4)) { drain_exp(); if (random2(sever) > 3) drain_exp(); if (random2(sever) > 3) drain_exp(); } else { mpr("A wave of agony tears through your body!"); set_hp(1 + (you.hp / 2), false); } done = true; } else if (random2(sever) <= 9) { god_speaks(GOD_XOM, random_choose_string( "\"Time to have some fun!\"", "\"Fight to survive, mortal.\"", "\"Let's see if it's strong enough to survive yet.\"", "You hear Xom's maniacal laughter.", NULL)); if (one_chance_in(4)) dancing_weapon(100, true); // nasty, but fun else { const int numdemons = std::min(random2(random2(random2(sever+1)+1)+1)+1, 14); for (int i = 0; i < numdemons; i++) { create_monster( xom_random_punishment_demon(sever), 4, BEH_HOSTILE, you.x_pos, you.y_pos, MHITNOT, MONS_PROGRAM_BUG); } } done = true; } else if (random2(sever) <= 10) { god_speaks(GOD_XOM, random_choose_string( "\"Try this!\"", "Xom's attention turns to you.", "Xom's power touches on you.", "Xom giggles.", NULL)); miscast_effect( SPTYP_RANDOM, 0, 0, random2(4), "the severe capriciousness of Xom" ); done = true; } else if (one_chance_in(sever) && (you.level_type != LEVEL_ABYSS)) { god_speaks(GOD_XOM, random_choose_string( "\"You have grown too comfortable in your little world, mortal!\"", "Xom casts you into the Abyss!", "The world seems to spin as Xom's maniacal laughter rings in your ears.", NULL)); banished(DNGN_ENTER_ABYSS, "Xom"); done = true; } } try_again: return (done); } void xom_acts(bool niceness, int sever) { #if DEBUG_DIAGNOSTICS || DEBUG_RELIGION || DEBUG_XOM mprf(MSGCH_DIAGNOSTICS, "Xom_acts(%u, %d); piety: %u, interest: %u\n", niceness, sever, you.piety, you.gift_timeout); #endif entry_cause_type old_entry_cause = you.entry_cause; if (sever < 1) sever = 1; // Nemelex's deck of punishment drawing the Xom card if (crawl_state.is_god_acting() && crawl_state.which_god_acting() != GOD_XOM) { god_type which_god = crawl_state.which_god_acting(); if (crawl_state.is_god_retribution()) { niceness = false; mprf(MSGCH_GOD, which_god, "%s asks Xom for help in punishing you, and Xom happily " "agrees.", god_name(which_god).c_str()); } else { niceness = true; mprf(MSGCH_GOD, which_god, "%s calls in a favour from Xom.", god_name(which_god).c_str()); } } if (niceness && !one_chance_in(5)) { // Good stuff. while (!xom_is_good(sever)) ; } else { // Bad mojo. while (!xom_is_bad(sever)) ; } // Nemelex's deck of punishment drawing the Xom card if (crawl_state.is_god_acting() && crawl_state.which_god_acting() != GOD_XOM) { if (old_entry_cause != you.entry_cause && you.entry_cause_god == GOD_XOM) { you.entry_cause_god = crawl_state.which_god_acting(); } } if (you.religion == GOD_XOM && coinflip()) you.piety = MAX_PIETY - you.piety; } static void xom_check_less_runes(int runes_gones) { if (player_in_branch(BRANCH_HALL_OF_ZOT) || !(branches[BRANCH_HALL_OF_ZOT].branch_flags & BFLAG_HAS_ORB)) { return; } int runes_avail = you.attribute[ATTR_UNIQUE_RUNES] + you.attribute[ATTR_DEMONIC_RUNES] + you.attribute[ATTR_ABYSSAL_RUNES] - you.attribute[ATTR_RUNES_IN_ZOT]; int was_avail = runes_avail + runes_gones; // No longer enough available runes to get into Zot if (was_avail >= NUMBER_OF_RUNES_NEEDED && runes_avail < NUMBER_OF_RUNES_NEEDED) { xom_is_stimulated(128, "Xom snickers.", true); } } void xom_check_lost_item(const item_def& item) { if (item.base_type == OBJ_ORBS) xom_is_stimulated(255, "Xom laughs nastily.", true); else if (is_fixed_artefact(item)) xom_is_stimulated(128, "Xom snickers.", true); else if (is_rune(item)) { // If you'd dropped it, check if that means you'd dropped your // third rune and now don't have enough to get into Zot. if (item.flags & ISFLAG_BEEN_IN_INV) xom_check_less_runes(item.quantity); if (is_unique_rune(item)) xom_is_stimulated(255, "Xom snickers loudly.", true); else if (you.entry_cause == EC_SELF_EXPLICIT && !(item.flags & ISFLAG_BEEN_IN_INV)) { // Player voluntarily entered Pan or the Abyss looking for // runes, yet never found it. if (item.plus == RUNE_ABYSSAL && you.attribute[ATTR_ABYSSAL_RUNES] == 0) { // Ignore Abyss area shifts. if (you.level_type != LEVEL_ABYSS) // Abyssal runes are a lot more trouble to find // than demonic runes, so it gets twice the // stimulation. xom_is_stimulated(128, "Xom snickers.", true); } else if (item.plus == RUNE_DEMONIC && you.attribute[ATTR_DEMONIC_RUNES] == 0) { xom_is_stimulated(64, "Xom snickers softly.", true); } } } } void xom_check_destroyed_item(const item_def& item, int cause) { int amusement = 0; if (item.base_type == OBJ_ORBS) { xom_is_stimulated(255, "Xom laughs nastily.", true); return; } else if (is_fixed_artefact(item)) xom_is_stimulated(128, "Xom snickers.", true); else if (is_rune(item)) { xom_check_less_runes(item.quantity); if (is_unique_rune(item) || item.plus == RUNE_ABYSSAL) amusement = 255; else amusement = 64 * item.quantity; } xom_is_stimulated(amusement, amusement > 128 ? "Xom snickers loudly." : amusement > 64 ? "Xom snickers." : "Xom snickers softly.", true); }