diff options
Diffstat (limited to 'crawl-ref/source')
42 files changed, 1053 insertions, 99 deletions
diff --git a/crawl-ref/source/abl-show.cc b/crawl-ref/source/abl-show.cc index 343e2ce842..50843db6d5 100644 --- a/crawl-ref/source/abl-show.cc +++ b/crawl-ref/source/abl-show.cc @@ -1230,7 +1230,7 @@ static bool do_ability(const ability_def& abil) return (false); } - banished(DNGN_ENTER_PANDEMONIUM); + banished(DNGN_ENTER_PANDEMONIUM, "self"); break; case ABIL_CHANNELING: diff --git a/crawl-ref/source/abyss.cc b/crawl-ref/source/abyss.cc index 3a6a951205..f059eb2435 100644 --- a/crawl-ref/source/abyss.cc +++ b/crawl-ref/source/abyss.cc @@ -33,6 +33,7 @@ #include "terrain.h" #include "traps.h" #include "view.h" +#include "xom.h" // public for abyss generation void generate_abyss(void) @@ -40,6 +41,10 @@ void generate_abyss(void) int i, j; // loop variables int temp_rand; // probability determination {dlb} +#if DEBUG_ABYSS + mpr("generate_abyss().", MSGCH_DIAGNOSTICS); +#endif + for (i = 5; i < (GXM - 5); i++) { for (j = 5; j < (GYM - 5); j++) @@ -62,6 +67,10 @@ void generate_abyss(void) static void generate_area(int gx1, int gy1, int gx2, int gy2) { +#if DEBUG_ABYSS + mpr("generate_area().", MSGCH_DIAGNOSTICS); +#endif + int items_placed = 0; const int thickness = random2(70) + 30; int thing_created; @@ -127,6 +136,9 @@ static void generate_area(int gx1, int gy1, int gx2, int gy2) { thing_created = items(1, OBJ_MISCELLANY, MISC_RUNE_OF_ZOT, true, 51, 51); +#if DEBUG_ABYSS + mpr("Placing an Abyssal rune.", MSGCH_DIAGNOSTICS); +#endif } else { @@ -143,6 +155,9 @@ static void generate_area(int gx1, int gy1, int gx2, int gy2) } } + int exits_wanted = 0; + int altars_wanted = 0; + for (int i = gx1; i <= gx2; i++) { for (int j = gy1; j <= gy2; j++) @@ -151,9 +166,23 @@ static void generate_area(int gx1, int gy1, int gx2, int gy2) grd[i][j] = replaced[random2(5)]; if (one_chance_in(7500)) // place an exit + exits_wanted++; + + // Don't place exit under items + if (exits_wanted > 0 && igrd[i][j] == NON_ITEM) + { grd[i][j] = DNGN_EXIT_ABYSS; + exits_wanted--; +#if DEBUG_ABYSS + mpr("Placing Abyss exit.", MSGCH_DIAGNOSTICS); +#endif + } if (one_chance_in(10000)) // place an altar + altars_wanted++; + + // Don't place altars under items. + if (altars_wanted > 0 && igrd[i][j] == NON_ITEM) { do { @@ -168,8 +197,103 @@ static void generate_area(int gx1, int gy1, int gx2, int gy2) // Lugonu has a flat 50% chance of corrupting the altar if ( coinflip() ) grd[i][j] = DNGN_ALTAR_LUGONU; + + altars_wanted--; +#if DEBUG_ABYSS + mpr("Placing altar.", MSGCH_DIAGNOSTICS); +#endif + } + } + } +} + +static int abyss_exit_nearness() +{ + int nearness = INFINITE_DISTANCE; + + for (int x = you.x_pos - LOS_RADIUS; x < you.x_pos + LOS_RADIUS; x++) + for (int y = you.y_pos - LOS_RADIUS; y < you.y_pos + LOS_RADIUS; y++) + { + if (!in_bounds(x, y)) + continue; + + // HACK: Why doesn't is_terrain_known() work here? + if (grd[x][y] == DNGN_EXIT_ABYSS + && get_screen_glyph(x, y) != '\0') + { + nearness = MIN(nearness, + grid_distance(you.x_pos, you.y_pos, + x, y)); + } + } + + return (nearness); +} + +static int abyss_rune_nearness() +{ + int nearness = INFINITE_DISTANCE; + + for (int x = you.x_pos - LOS_RADIUS; x < you.x_pos + LOS_RADIUS; x++) + for (int y = you.y_pos - LOS_RADIUS; y < you.y_pos + LOS_RADIUS; y++) + { + if (!in_bounds(x, y)) + continue; + + // HACK: Why doesn't is_terrain_known() work here? + if (get_screen_glyph(x, y) != '\0') + { + int i = igrd[x][y]; + + while (i != NON_ITEM) + { + item_def& item(mitm[i]); + if (is_rune(item) && item.plus == RUNE_ABYSSAL) + nearness = MIN(nearness, + grid_distance(you.x_pos, you.y_pos, + x, y)); + i = item.link; + } } } + + return (nearness); +} + +static int exit_was_near; +static int rune_was_near; + +static void xom_check_nearness_setup() +{ + exit_was_near = abyss_exit_nearness(); + rune_was_near = abyss_rune_nearness(); +} + +// If the player was almost to the exit when it disppeared, Xom is +// exteremely amused. He's also extremely amused if the player winds +// up right next to an exit when there wasn't one there before. The +// same applies to Abyssal runes. +static void xom_check_nearness() +{ + // Update known terrain + viewwindow(true, false); + + int exit_is_near = abyss_exit_nearness(); + if ((exit_was_near < INFINITE_DISTANCE && + exit_is_near == INFINITE_DISTANCE) + || (exit_was_near == INFINITE_DISTANCE && + exit_is_near < INFINITE_DISTANCE)) + { + xom_is_stimulated(255); + } + + int rune_is_near = abyss_rune_nearness(); + if ((rune_was_near < INFINITE_DISTANCE && + rune_is_near == INFINITE_DISTANCE) + || (rune_was_near == INFINITE_DISTANCE && + rune_is_near < INFINITE_DISTANCE)) + { + xom_is_stimulated(255); } } @@ -184,6 +308,12 @@ static void abyss_lose_monster(monsters &mons) void area_shift(void) /*******************/ { +#if DEBUG_ABYSS + mpr("area_shift().", MSGCH_DIAGNOSTICS); +#endif + + xom_check_nearness_setup(); + for (unsigned int i = 0; i < MAX_MONSTERS; i++) { monsters &m = menv[i]; @@ -267,6 +397,8 @@ void area_shift(void) generate_area(5, 5, (GXM - 5), (GYM - 5)); + xom_check_nearness(); + for (unsigned int mcount = 0; mcount < 15; mcount++) { mons_place( RANDOM_MONSTER, BEH_HOSTILE, MHITNOT, false, 1, 1, @@ -290,6 +422,8 @@ void save_abyss_uniques() void abyss_teleport( bool new_area ) /**********************************/ { + xom_check_nearness_setup(); + int x, y, i, j, k; if (!new_area) @@ -311,11 +445,19 @@ void abyss_teleport( bool new_area ) if (i < 100) { +#if DEBUG_ABYSS + mpr("Non-new area Abyss teleport.", MSGCH_DIAGNOSTICS); +#endif you.moveto(x, y); + xom_check_nearness(); return; } } +#if DEBUG_ABYSS + mpr("New area Abyss teleport.", MSGCH_DIAGNOSTICS); +#endif + // teleport to a new area of the abyss: init_pandemonium(); // get new monsters @@ -343,6 +485,8 @@ void abyss_teleport( bool new_area ) UNIQ_LOST_IN_ABYSS ); } + xom_check_lost_item( mitm[k] ); + destroy_item( k ); } } @@ -367,6 +511,8 @@ void abyss_teleport( bool new_area ) generate_area( 10, 10, (GXM - 10), (GYM - 10) ); + xom_check_nearness(); + grd[you.x_pos][you.y_pos] = DNGN_FLOOR; if ( one_chance_in(5) ) grd[you.x_pos + 1][you.y_pos] = DNGN_ALTAR_LUGONU; diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc index 9a427e6b6f..6fa1b88ade 100644 --- a/crawl-ref/source/acr.cc +++ b/crawl-ref/source/acr.cc @@ -136,6 +136,7 @@ #include "tutorial.h" #include "view.h" #include "stash.h" +#include "xom.h" crawl_environment env; player you; @@ -588,13 +589,41 @@ static void handle_wizard_command( void ) break; + case 'C': + { + // this command isn't very exciting... feel free to replace + i = prompt_invent_item( "(Un)curse which item?", MT_INVLIST, -1 ); + if (i == PROMPT_ABORT) + { + canned_msg( MSG_OK ); + break; + } + + item_def& item(you.inv[i]); + + if (item_cursed(item)) + do_uncurse_item(item); + else + do_curse_item(item); + + break; + } + case 'B': if (you.level_type != LEVEL_ABYSS) - banished( DNGN_ENTER_ABYSS ); + banished( DNGN_ENTER_ABYSS, "wizard command" ); else down_stairs(you.your_level, DNGN_EXIT_ABYSS); break; + case CONTROL('A'): + if (you.level_type == LEVEL_ABYSS) + abyss_teleport(true); + else + mpr("You can only abyss_teleport() inside the Abyss."); + + break; + case 'g': debug_add_skills(); break; diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc index d78fa1cdc4..c4443068ff 100644 --- a/crawl-ref/source/beam.cc +++ b/crawl-ref/source/beam.cc @@ -60,6 +60,7 @@ #include "terrain.h" #include "traps.h" #include "view.h" +#include "xom.h" #define BEAM_STOP 1000 // all beams stopped by subtracting this // from remaining range @@ -1507,6 +1508,14 @@ void fire_beam( bolt &pbolt, item_def *item ) canned_msg(MSG_NOTHING_HAPPENS); } + if (!pbolt.is_tracer && pbolt.beam_source != NON_MONSTER) + { + if (pbolt.foe_hurt == 0 && pbolt.fr_hurt > 0) + xom_is_stimulated(128); + else if (pbolt.foe_helped > 0 && pbolt.fr_helped == 0) + xom_is_stimulated(128); + } + // that's it! #ifdef WIN32CONSOLE if (!pbolt.is_tracer) @@ -3146,6 +3155,8 @@ static int affect_player( bolt &beam ) } else { + bool nasty = true, nice = false; + // BEGIN enchantment beam if (beam.flavour != BEAM_HASTE && beam.flavour != BEAM_INVISIBILITY @@ -3156,6 +3167,12 @@ static int affect_player( bolt &beam ) && you_resist_magic( beam.ench_power )) { canned_msg(MSG_YOU_RESIST); + + // You *could* have gotten a free teleportation in the Abyss, + // but no, you resisted. + if (beam.flavour != BEAM_TELEPORT && you.level_type == LEVEL_ABYSS) + xom_is_stimulated(255); + return (range_used_on_hit(beam)); } @@ -3213,11 +3230,15 @@ static int affect_player( bolt &beam ) potion_effect( POT_SPEED, beam.ench_power ); contaminate_player( 1 ); beam.obvious_effect = true; + nasty = false; + nice = true; break; // haste case BEAM_HEALING: potion_effect( POT_HEAL_WOUNDS, beam.ench_power ); beam.obvious_effect = true; + nasty = false; + nice = true; break; // heal (heal wounds potion eff) case BEAM_PARALYSIS: @@ -3234,12 +3255,20 @@ static int affect_player( bolt &beam ) potion_effect( POT_INVISIBILITY, beam.ench_power ); contaminate_player( 1 + random2(2) ); beam.obvious_effect = true; + nasty = false; + nice = true; break; // invisibility // 6 is used by digging case BEAM_TELEPORT: you_teleport(); + + // An enemy helping you escape while in the Abyss, or an + // enemy stabalizing a teleport that was about to happen. + if (beam.attitude == ATT_HOSTILE && you.level_type == LEVEL_ABYSS) + xom_is_stimulated(255); + beam.obvious_effect = true; break; @@ -3312,6 +3341,28 @@ static int affect_player( bolt &beam ) break; } // end of switch (beam.colour) + if (nasty) + { + if (beam.attitude != ATT_HOSTILE) + { + beam.fr_hurt++; + xom_is_stimulated(128); + } + else + beam.foe_hurt++; + } + + if (nice) + { + if (beam.attitude != ATT_HOSTILE) + beam.fr_helped++; + else + { + beam.foe_helped++; + xom_is_stimulated(128); + } + } + // regardless of affect, we need to know if this is a stopper // or not - it seems all of the above are. return (range_used_on_hit(beam)); @@ -3359,6 +3410,9 @@ static int affect_player( bolt &beam ) } } + bool was_affected = false; + int old_hp = you.hp; + if (hurted < 0) hurted = 0; @@ -3367,10 +3421,16 @@ static int affect_player( bolt &beam ) if (beam.flavour == BEAM_MIASMA && hurted > 0) { if (player_res_poison() <= 0) + { poison_player(1); + was_affected = true; + } if (one_chance_in( 3 + 2 * player_prot_life() )) + { potion_effect( POT_SLOWING, 5 ); + was_affected = true; + } } // poisoning @@ -3383,17 +3443,22 @@ static int affect_player( bolt &beam ) && random2(100) < 90 - (3 * player_AC()))) { poison_player( 1 + random2(3) ); + was_affected = true; } } if (beam.name.find("throwing net") != std::string::npos) + { player_caught_in_net(); + was_affected = true; + } if (beam.name.find("curare") != std::string::npos) { if (random2(100) < 90 - (3 * player_AC())) { curare_hits_player( beam_ouch_agent(beam), 1 + random2(3) ); + was_affected = true; } } @@ -3403,7 +3468,10 @@ static int affect_player( bolt &beam ) || you.experience_level < 6)) { if (!player_equip( EQ_BODY_ARMOUR, ARM_MOTTLED_DRAGON_ARMOUR )) + { you.duration[DUR_LIQUID_FLAMES] += random2avg(7, 3) + 1; + was_affected = true; + } } // simple cases for scroll burns @@ -3429,6 +3497,21 @@ static int affect_player( bolt &beam ) mprf(MSGCH_DIAGNOSTICS, "Damage: %d", hurted ); #endif + if (hurted > 0 || old_hp < you.hp || was_affected) + { + if (beam.attitude != ATT_HOSTILE) + { + beam.fr_hurt++; + + // Xom's ammusement at the player being damaged is handled + // elsewhere. + if (was_affected) + xom_is_stimulated(128); + } + else + beam.foe_hurt++; + } + beam_ouch( hurted, beam ); return (range_used_on_hit( beam )); @@ -3462,6 +3545,24 @@ static int name_to_skill_level(const std::string& name) return (2 * you.skills[type]); } +static void update_hurt_or_helped(bolt &beam, monsters *mon) +{ + if (beam.attitude != mons_attitude(mon)) + { + if (nasty_beam(mon, beam)) + beam.foe_hurt++; + else if (nice_beam(mon, beam)) + beam.foe_helped++; + } + else + { + if (nasty_beam(mon, beam)) + beam.fr_hurt++; + else if (nice_beam(mon, beam)) + beam.fr_helped++; + } +} + // return amount of range used up by affectation of this monster static int affect_monster(bolt &beam, monsters *mon) { @@ -3513,6 +3614,7 @@ static int affect_monster(bolt &beam, monsters *mon) "crumbles away!"); } beam.obvious_effect = true; + update_hurt_or_helped(beam, mon); mon->hit_points = 0; monster_die(mon, beam); return (BEAM_STOP); @@ -3585,6 +3687,7 @@ static int affect_monster(bolt &beam, monsters *mon) beam.msg_generated = true; break; default: + update_hurt_or_helped(beam, mon); break; } return (rangeUsed); @@ -3725,6 +3828,8 @@ static int affect_monster(bolt &beam, monsters *mon) return (0); } + update_hurt_or_helped(beam, mon); + // the beam hit. if (mons_near(mon)) { @@ -4606,6 +4711,18 @@ bool nasty_beam(monsters *mon, bolt &beam) return (true); } +bool nice_beam( struct monsters *mon, struct bolt &beam ) +{ + // haste + if (beam.flavour == BEAM_HASTE || beam.flavour == BEAM_HEALING + || beam.flavour == BEAM_INVISIBILITY) + { + return (true); + } + + return (false); +} + //////////////////////////////////////////////////////////////////////////// // bolt @@ -4624,6 +4741,7 @@ bolt::bolt() : range(0), rangeMax(0), type(SYM_ZAP), colour(BLACK), is_thrown(false), target_first(false), aimed_at_spot(false), aux_source(), obvious_effect(false), effect_known(true), fr_count(0), foe_count(0), fr_power(0), foe_power(0), + fr_hurt(0), foe_hurt(0), fr_helped(0), foe_helped(0), is_tracer(false), aimed_at_feet(false), msg_generated(false), in_explosion_phase(false), smart_monster(false), can_see_invis(false), attitude(ATT_HOSTILE), foe_ratio(0), diff --git a/crawl-ref/source/beam.h b/crawl-ref/source/beam.h index 11cd0c5f6e..ee8819748b 100644 --- a/crawl-ref/source/beam.h +++ b/crawl-ref/source/beam.h @@ -150,6 +150,8 @@ struct bolt bool effect_known; // did we _know_ this would happen? int fr_count, foe_count; // # of times a friend/foe is "hit" int fr_power, foe_power; // total levels/hit dice affected + int fr_hurt, foe_hurt; // # of friends/foes actually hurt + int fr_helped, foe_helped; // # of friends/foes actually helped // INTERNAL use - should not usually be set outside of beam.cc bool is_tracer; // is this a tracer? @@ -195,7 +197,7 @@ void fire_beam( struct bolt &pbolt, item_def *item = NULL ); * called from: beam * *********************************************************************** */ bool nasty_beam( struct monsters *mon, struct bolt &beam ); - +bool nice_beam( struct monsters *mon, struct bolt &beam ); // last updated 12may2000 {dlb} /* *********************************************************************** diff --git a/crawl-ref/source/branch.cc b/crawl-ref/source/branch.cc index f5a1a33769..b3cdc0e652 100644 --- a/crawl-ref/source/branch.cc +++ b/crawl-ref/source/branch.cc @@ -278,7 +278,7 @@ Branch branches[] = { NULL, NULL, 0, '0', false }, - { BRANCH_HALL_OF_ZOT, BRANCH_MAIN_DUNGEON, 5, 27, 0, 0, + { BRANCH_HALL_OF_ZOT, BRANCH_MAIN_DUNGEON, 5, 27, BFLAG_HAS_ORB, 0, DNGN_ENTER_ZOT, DNGN_RETURN_FROM_ZOT, "Zot", "the Realm of Zot", "Zot", NULL, diff --git a/crawl-ref/source/branch.h b/crawl-ref/source/branch.h index 9ddc1a1d0a..d587f56c38 100644 --- a/crawl-ref/source/branch.h +++ b/crawl-ref/source/branch.h @@ -18,7 +18,8 @@ enum branch_flag_type BFLAG_NO_TELE_CONTROL = (1 << 0), // Teleport control not allowed. BFLAG_NOT_MAPPABLE = (1 << 1), // Branch levels not mappable. - BFLAG_NO_MAGIC_MAP = (1 << 2) // Branch levels can't be magic mapped. + BFLAG_NO_MAGIC_MAP = (1 << 2), // Branch levels can't be magic mapped. + BFLAG_HAS_ORB = (1 << 3) // Orb is on the floor in this branch }; struct Branch diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc index 50b17ed9a2..5b6843726e 100644 --- a/crawl-ref/source/command.cc +++ b/crawl-ref/source/command.cc @@ -1598,9 +1598,11 @@ static void list_wizard_commands() cols.add_formatted(0, "a : acquirement\n" "A : set all skills to level\n" + "Ctrl-A : generate new Abyss area\n" "b : controlled blink\n" "B : banish yourself to the Abyss\n" "c : card effect\n" + "C : (un)curse item\n" "g : add a skill\n" "G : banish all monsters\n" "Ctrl-G : save ghost (bones file)\n" @@ -1618,11 +1620,11 @@ static void list_wizard_commands() "r : change character's species\n" "s : gain 20000 skill points\n" "S : set skill to level\n" - "t : tweak object properties\n" - "T : make a trap\n", + "t : tweak object properties\n", true, true); cols.add_formatted(1, + "T : make a trap\n" "v : show gold value of an item\n" "x : gain an experience level\n" "Ctrl-X : change experience level\n" diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc index 6fad244740..8ab66a40e6 100644 --- a/crawl-ref/source/debug.cc +++ b/crawl-ref/source/debug.cc @@ -2549,12 +2549,17 @@ void debug_card() mpr("Unknown card."); return; } - + + std::string wanted = buf; + lowercase(wanted); + bool found_card = false; for ( int i = 0; i < NUM_CARDS; ++i ) { const card_type c = static_cast<card_type>(i); - if ( strstr(card_name(c), buf) != NULL ) + std::string card = card_name(c); + lowercase(card); + if ( card.find(wanted) != std::string::npos ) { card_effect(c, DECK_RARITY_LEGENDARY); found_card = true; diff --git a/crawl-ref/source/decks.cc b/crawl-ref/source/decks.cc index 313e5197a4..7a728b0f46 100644 --- a/crawl-ref/source/decks.cc +++ b/crawl-ref/source/decks.cc @@ -44,6 +44,7 @@ #include "transfor.h" #include "traps.h" #include "view.h" +#include "xom.h" #define VECFROM(x) (x), (x) + ARRAYSIZE(x) #define DEFVEC(Z) static std::vector<card_type> Z(VECFROM(a_##Z)) @@ -169,7 +170,8 @@ const char* card_name(card_type card) return "a very buggy card"; } -static card_type choose_one_card(const item_def& item, bool message) +static card_type choose_one_card(const item_def& item, bool message, + bool &was_oddity) { std::vector<card_type> *pdeck = NULL; switch ( item.sub_type ) @@ -212,7 +214,8 @@ static card_type choose_one_card(const item_def& item, bool message) { if ( message ) mpr("This card doesn't seem to belong here."); - pdeck = &deck_of_oddities; + pdeck = &deck_of_oddities; + was_oddity = true; } card_type chosen = (*pdeck)[random2(pdeck->size())]; @@ -225,6 +228,13 @@ static card_type choose_one_card(const item_def& item, bool message) return chosen; } +static card_type choose_one_card(const item_def& item, bool message) +{ + bool garbage; + + return choose_one_card(item, message, garbage); +} + static bool wielding_deck() { if ( you.equip[EQ_WEAPON] == -1 ) @@ -449,7 +459,52 @@ void evoke_deck( item_def& deck ) // If the deck wasn't marked, draw a fair card. if ( deck.plus2 == 0 ) { - card_effect( choose_one_card(deck, true), deck_rarity(deck) ); + // Could a Xom worshipper ever get a stacked deck in the first + // place? + int amusement = 64; + bool was_oddity = false; + card_type card = choose_one_card(deck, true, was_oddity); + + if (!item_type_known(deck)) + amusement *= 2; + // Expecting one type of card but got another, real funny. + else if (was_oddity) + amusement = 255; + + if (player_in_a_dangerous_place()) + amusement *= 2; + + switch (card) + { + case CARD_XOM: + // Handled elswehre + amusement = 0; + break; + + case CARD_BLANK: + // Boring + amusement = 0; + break; + + case CARD_DAMNATION: + // Nothing happened, boring. + if (you.level_type != LEVEL_DUNGEON) + amusement = 0; + break; + + case CARD_MINEFIELD: + case CARD_FAMINE: + case CARD_CURSE: + // Always hilarious. + amusement = 255; + + default: + break; + } + + card_effect(card, deck_rarity(deck) ); + + xom_is_stimulated(amusement); if ( deck.sub_type != MISC_DECK_OF_PUNISHMENT ) { @@ -458,6 +513,7 @@ void evoke_deck( item_def& deck ) if (one_chance_in(3)) brownie_points++; } + } else { @@ -660,7 +716,7 @@ static void damnation_card(int power, deck_rarity_type rarity) if ( mon_to_banish == NON_MONSTER ) // banish yourself! { - banished(DNGN_ENTER_ABYSS); + banished(DNGN_ENTER_ABYSS, "drawing a card"); break; // don't banish anything else } else @@ -1293,6 +1349,19 @@ void card_effect(card_type which_card, deck_rarity_type rarity) msg::stream << "You have drawn " << card_name( which_card ) << '.' << std::endl; + if (which_card == CARD_XOM && !crawl_state.is_god_acting()) + { + if (you.religion == GOD_XOM) + { + // Being a self-centered diety, Xom *always* finds this + // maximally hilarious. + god_speaks(GOD_XOM, "Xom roars with laughter!"); + you.gift_timeout = 255; + } + else if (you.penance[GOD_XOM] > 0) + god_speaks(GOD_XOM, "Xom laughs nastily."); + } + switch (which_card) { case CARD_BLANK: break; @@ -1377,6 +1446,13 @@ void card_effect(card_type which_card, deck_rarity_type rarity) break; } + if (you.religion == GOD_XOM && which_card == CARD_BLANK) + { + god_speaks(GOD_XOM, "\"How boring, lets spice things up a little.\""); + + xom_acts(abs(you.piety - 100)); + } + return; } diff --git a/crawl-ref/source/delay.cc b/crawl-ref/source/delay.cc index 941d82d223..326fa33151 100644 --- a/crawl-ref/source/delay.cc +++ b/crawl-ref/source/delay.cc @@ -42,6 +42,7 @@ #include "travel.h" #include "tutorial.h" #include "view.h" +#include "xom.h" extern std::vector<SelItem> items_for_multidrop; @@ -293,6 +294,17 @@ bool is_being_butchered(const item_def &item) return (false); } +// Xom is amused by a potential food source going to waste, and is +// more amused the hungrier you are. +static void xom_check_corpse_waste() +{ + int food_need = 7000 - you.hunger; + if (food_need < 0) + food_need = 0; + + xom_is_stimulated(64 + (191 * food_need / 6000)); +} + void handle_delay( void ) /***********************/ { @@ -353,24 +365,41 @@ void handle_delay( void ) // Note that a monster could have raised the corpse and another // monster could die and create a corpse with the same ID number... // However, it would not be at the player's square like the - // original and that's why we do it this way. Note that - // we ignore the conversion to skeleton possibility just to - // be nice. -- bwr + // original and that's why we do it this way. if (is_valid_item( mitm[ delay.parm1 ] ) && mitm[ delay.parm1 ].base_type == OBJ_CORPSES && mitm[ delay.parm1 ].x == you.x_pos && mitm[ delay.parm1 ].y == you.y_pos ) { - // special < 100 is the rottenness check - if ( (mitm[delay.parm1].special < 100) && - (delay.parm2 >= 100) ) + if (mitm[ delay.parm1 ].sub_type == CORPSE_SKELETON) { - mpr("The corpse rots.", MSGCH_ROTTEN_MEAT); - delay.parm2 = 99; // don't give the message twice + mpr("The corpse rots away into a skeleton!"); + if (you.species == SP_GHOUL) + xom_check_corpse_waste(); + else + xom_is_stimulated(32); + delay.duration = 0; } + else + { + // special < 100 is the rottenness check + if ( (mitm[delay.parm1].special < 100) && + (delay.parm2 >= 100) ) + { + mpr("The corpse rots.", MSGCH_ROTTEN_MEAT); + delay.parm2 = 99; // don't give the message twice + + if (you.species != SP_VAMPIRE + && you.species != SP_MUMMY + && you.species != SP_GHOUL) + { + xom_check_corpse_waste(); + } + } - // mark work done on the corpse in case we stop -- bwr - mitm[ delay.parm1 ].plus2++; + // mark work done on the corpse in case we stop -- bwr + mitm[ delay.parm1 ].plus2++; + } } else { @@ -589,6 +618,20 @@ static void finish_delay(const delay_queue_item &delay) const item_def &item = mitm[delay.parm1]; if (is_valid_item(item) && item.base_type == OBJ_CORPSES) { + + if (item.sub_type == CORPSE_SKELETON) + { + mpr("The corpse rots away into a skeleton just before you " + "finish butchering it!"); + + if (you.species == SP_GHOUL) + xom_check_corpse_waste(); + else + xom_is_stimulated(64); + + break; + } + mprf("You finish %s the corpse into pieces.", (you.has_usable_claws() || you.mutation[MUT_FANGS] == 3) ? "ripping" : "chopping"); @@ -679,7 +722,8 @@ static void armour_wear_effects(const int item_slot) set_ident_flags(arm, ISFLAG_EQ_ARMOUR_MASK ); mprf("You finish putting on %s.", arm.name(DESC_NOCAP_YOUR).c_str()); - const equipment_type eq_slot = get_armour_slot(arm); + const equipment_type eq_slot = get_armour_slot(arm); + const bool known_cursed = item_known_cursed(arm); if (eq_slot == EQ_BODY_ARMOUR) { @@ -811,7 +855,16 @@ static void armour_wear_effects(const int item_slot) { mpr( "Oops, that feels deathly cold." ); learned_something_new(TUT_YOU_CURSED); - xom_is_stimulated(128); + + // Cursed cloaks prevent you from removing body armour + int cloak_mult = 1; + if (get_armour_slot(arm) == EQ_CLOAK) + cloak_mult = 2; + + if (known_cursed) + xom_is_stimulated(32 * cloak_mult); + else + xom_is_stimulated(64 * cloak_mult); } if (eq_slot == EQ_SHIELD) diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc index e66dcc0a5d..2ef983478d 100644 --- a/crawl-ref/source/describe.cc +++ b/crawl-ref/source/describe.cc @@ -54,7 +54,7 @@ #include "stuff.h" #include "spl-util.h" #include "tutorial.h" - +#include "xom.h" // ======================================================================== // Internal Functions diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc index 5a0922f7af..bc8bebd375 100644 --- a/crawl-ref/source/effects.cc +++ b/crawl-ref/source/effects.cc @@ -40,15 +40,16 @@ #include "ouch.h" #include "player.h" #include "randart.h" -#include "religion.h" #include "skills2.h" #include "spells3.h" #include "spells4.h" #include "spl-book.h" #include "spl-util.h" +#include "state.h" #include "stuff.h" #include "terrain.h" #include "view.h" +#include "xom.h" // torment_monsters is called with power 0 because torment is // UNRESISTABLE except for being undead or having torment @@ -158,10 +159,39 @@ void banished(dungeon_feature_type gate_type, const std::string &who) take_note(Note(NOTE_USER_NOTE, 0, 0, what.c_str()), true); } - down_stairs(you.your_level, gate_type); // heh heh + // Now figure out how we got here. + if (who.find("self") != std::string::npos || who == you.your_name + || who == "you" || who == "You" || crawl_state.is_god_acting()) + { + // down_stairs() will take care of setting things. + you.entry_cause = EC_UNKNOWN; + } + else if (who.find("distortion") != std::string::npos) + { + if (who.find("wield") != std::string::npos) + { + if (who.find("unknowing") != std::string::npos) + you.entry_cause = EC_SELF_ACCIDENT; + else + you.entry_cause = EC_SELF_RISKY; + } + else if (who.find("affixation") != std::string::npos) + you.entry_cause = EC_SELF_ACCIDENT; + else if (who.find("branding") != std::string::npos) + you.entry_cause = EC_SELF_RISKY; + else + you.entry_cause = EC_MONSTER; + } + else if (who == "drawing a card") + you.entry_cause = EC_SELF_RISKY; + else if (who.find("miscast") != std::string::npos) + you.entry_cause = EC_MISCAST; + else if (who == "wizard command") + you.entry_cause = EC_SELF_EXPLICIT; + else + you.entry_cause = EC_MONSTER; - if (gate_type == DNGN_ENTER_ABYSS || gate_type == DNGN_ENTER_PANDEMONIUM) - xom_is_stimulated(255); + down_stairs(you.your_level, gate_type, you.entry_cause); // heh heh } bool forget_spell(void) diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index 4d9ac82043..61ddc607ce 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -171,6 +171,8 @@ enum attribute_type ATTR_GOD_GIFT_COUNT, //jmf: added to help manage god gift giving ATTR_DELAYED_FIREBALL, // bwr: reserve fireballs ATTR_HELD, // caught in a net + ATTR_ABYSS_ENTOURAGE, // maximum number of hostile monsters in + // sight of the player while in the Abyss. NUM_ATTRIBUTES }; @@ -1340,6 +1342,19 @@ enum level_area_type // you.level_type NUM_LEVEL_AREA_TYPES }; +enum entry_cause_type +{ + EC_UNKNOWN, + EC_SELF_EXPLICIT, + EC_SELF_RISKY, // i.e., wielding an id'd distorion weapon + EC_SELF_ACCIDENT, // i.e., wielding an un-id'd distortion weapon + EC_MISCAST, + EC_GOD_RETRIUBTION, + EC_GOD_ACT, // Xom sending the player somewhere for amusement + EC_MONSTER, + NUM_ENTRY_CAUSE_TYPES +}; + // Can't change this order without breaking saves. enum map_marker_type { diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index a605bc89f1..578af53e2f 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -615,6 +615,9 @@ public: level_area_type level_type; std::string level_type_name; + entry_cause_type entry_cause; + god_type entry_cause_god; + branch_type where_are_you; FixedVector<unsigned char, 30> branch_stairs; diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index b56720a117..7d8471429e 100644 --- a/crawl-ref/source/fight.cc +++ b/crawl-ref/source/fight.cc @@ -76,6 +76,7 @@ #include "transfor.h" #include "tutorial.h" #include "view.h" +#include "xom.h" #define HIT_WEAK 7 #define HIT_MED 18 @@ -475,11 +476,27 @@ bool melee_attack::attack() identify_mimic(atk); identify_mimic(def); + // Xom thinks fumbles are funny... if (attacker->fumbles_attack()) { - xom_is_stimulated(14); // Xom thinks that is funny. + // ... and thinks fumbling when trying to hit yourself is just + // hilarious. + if (attacker == defender) + xom_is_stimulated(255); + else + xom_is_stimulated(14); return (false); } + // Non-fumbled self-attacks due to confusion are still pretty + // funny, though. + else if (attacker == defender && attacker->confused()) + { + // And is still hilarious if it's the player. + if (attacker->atype() == ACT_PLAYER) + xom_is_stimulated(255); + else + xom_is_stimulated(128); + } // Allow god to get offended, etc. attacker->attacking(defender); diff --git a/crawl-ref/source/files.cc b/crawl-ref/source/files.cc index 2e69a145c1..190d75cb9c 100644 --- a/crawl-ref/source/files.cc +++ b/crawl-ref/source/files.cc @@ -90,6 +90,7 @@ #include "travel.h" #include "tutorial.h" #include "view.h" +#include "xom.h" void save_level(int level_saved, level_area_type lt, branch_type where_were_you); @@ -823,6 +824,29 @@ static void grab_followers() } } +// Should be called afetr grab_followers(), so that items carried by +// followers won't be considered lost. +static void _xom_check_lost_items(level_area_type old_level_type) +{ + if (old_level_type == LEVEL_DUNGEON) + return; + + int amusement = 0; + + for (int i = 0; i < MAX_ITEMS; i++) + { + item_def& item(mitm[i]); + + if (!is_valid_item(item)) + continue; + + // Item is in player intentory, so it's not lost. + if (item.x == -1 && item.y == -1) + continue; + + xom_check_lost_item(item); + } +} bool load( dungeon_feature_type stair_taken, int load_mode, level_area_type old_level_type, char old_level, @@ -872,6 +896,8 @@ bool load( dungeon_feature_type stair_taken, int load_mode, save_level( old_level, LEVEL_DUNGEON, where_were_you2 ); } + _xom_check_lost_items(old_level_type); + // Try to open level savefile. FILE *levelFile = fopen(cha_fil.c_str(), "rb"); @@ -1027,6 +1053,9 @@ bool load( dungeon_feature_type stair_taken, int load_mode, curr_PlaceInfo.assert_validity(); } + if (just_created_level) + you.attribute[ATTR_ABYSS_ENTOURAGE] = 0; + return just_created_level; } // end load() diff --git a/crawl-ref/source/food.cc b/crawl-ref/source/food.cc index bbad92c57c..97d5319e7d 100644 --- a/crawl-ref/source/food.cc +++ b/crawl-ref/source/food.cc @@ -50,6 +50,7 @@ #include "stuff.h" #include "transfor.h" #include "tutorial.h" +#include "xom.h" static int determine_chunk_effect(int which_chunk_type, bool rotten_chunk); static void eat_chunk( int chunk_effect ); diff --git a/crawl-ref/source/it_use2.cc b/crawl-ref/source/it_use2.cc index e313767cf9..02708f95fb 100644 --- a/crawl-ref/source/it_use2.cc +++ b/crawl-ref/source/it_use2.cc @@ -40,6 +40,7 @@ #include "spl-util.h" #include "stuff.h" #include "view.h" +#include "xom.h" // From an actual potion, pow == 40 -- bwr bool potion_effect( potion_type pot_eff, int pow ) @@ -472,7 +473,7 @@ void unwield_item(bool showMsgs) // int effect = 9 - random2avg( you.skills[SK_TRANSLOCATIONS] * 2, 2 ); miscast_effect( SPTYP_TRANSLOCATION, 9, 90, 100, - "a distortion effect" ); + "distortion unwield" ); break; // when more are added here, *must* duplicate unwielding diff --git a/crawl-ref/source/it_use3.cc b/crawl-ref/source/it_use3.cc index 9ee69e8f87..96b5bc1f98 100644 --- a/crawl-ref/source/it_use3.cc +++ b/crawl-ref/source/it_use3.cc @@ -52,6 +52,7 @@ #include "state.h" #include "stuff.h" #include "view.h" +#include "xom.h" static bool ball_of_energy(void); static bool ball_of_fixation(void); diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc index 212e2ea00e..18d4c1ffe5 100644 --- a/crawl-ref/source/item_use.cc +++ b/crawl-ref/source/item_use.cc @@ -61,6 +61,7 @@ #include "player.h" #include "randart.h" #include "religion.h" +#include "shopping.h" #include "skills.h" #include "skills2.h" #include "spells1.h" @@ -74,6 +75,7 @@ #include "transfor.h" #include "tutorial.h" #include "view.h" +#include "xom.h" static bool drink_fountain(); static bool enchant_armour(); @@ -401,6 +403,8 @@ void wield_effects(int item_wield_2, bool showMsgs) { unsigned char i_dam = 0; + const bool known_cursed = item_known_cursed(you.inv[item_wield_2]); + // and here we finally get to the special effects of wielding {dlb} if (you.inv[item_wield_2].base_type == OBJ_MISCELLANY) { @@ -593,7 +597,11 @@ void wield_effects(int item_wield_2, bool showMsgs) break; case SPWPN_DISTORTION: - miscast_effect( SPTYP_TRANSLOCATION, 9, 90, 100, "a distortion effect" ); + if (!was_known) + xom_is_stimulated(128); + miscast_effect( SPTYP_TRANSLOCATION, 9, 90, 100, + was_known ? "distortion wield" : + "uknowning distortion wield"); break; case SPWPN_SINGING_SWORD: @@ -645,7 +653,10 @@ void wield_effects(int item_wield_2, bool showMsgs) if (item_cursed( you.inv[item_wield_2] )) { mpr("It sticks to your hand!"); - xom_is_stimulated(64); + if (known_cursed) + xom_is_stimulated(32); + else + xom_is_stimulated(64); } } @@ -2279,8 +2290,11 @@ void jewellery_wear_effects(item_def &item) // Randart jewellery shouldn't auto-ID just because the // base type is known. Somehow the player should still // be told, preferably by message. (jpeg) - const bool artefact = is_random_artefact( item ); - const bool identified = fully_identified( item ); + const bool artefact = is_random_artefact( item ); + const bool identified = fully_identified( item ); + const bool known_cursed = item_known_cursed( item ); + const bool known_bad = item_type_known( item ) + && (item_value( item ) <= 2); switch (item.sub_type) { @@ -2406,7 +2420,11 @@ void jewellery_wear_effects(item_def &item) mprf("Oops, that %s feels deathly cold.", jewellery_is_amulet(item)? "amulet" : "ring"); learned_something_new(TUT_YOU_CURSED); - xom_is_stimulated(128); + + if (known_cursed || known_bad) + xom_is_stimulated(64); + else + xom_is_stimulated(128); } // cursed or not, we know that since we've put the ring on @@ -3268,7 +3286,7 @@ static bool affix_weapon_enchantment() // from unwield_item miscast_effect( SPTYP_TRANSLOCATION, 9, 90, 100, - "a distortion effect" ); + "distortion affixation" ); success = false; break; diff --git a/crawl-ref/source/itemprop.cc b/crawl-ref/source/itemprop.cc index cd77235da0..1ed8f1cc52 100644 --- a/crawl-ref/source/itemprop.cc +++ b/crawl-ref/source/itemprop.cc @@ -37,6 +37,7 @@ #include "stuff.h" #include "transfor.h" #include "view.h" +#include "xom.h" // XXX: name strings in most of the following are currently unused! @@ -445,6 +446,26 @@ bool item_known_uncursed( const item_def &item ) void do_curse_item( item_def &item ) { + // Xom is amused by the player's items being cursed, especially + // if they're worn/equipped. + if (!(item.flags & ISFLAG_CURSED) && item.x == -1 && item.y == -1) + { + int amusement = 64; + + if (item_is_equipped(item)) + { + amusement *= 2; + + // Cursed cloaks prevent you from removing body armour + if (item.base_type == OBJ_ARMOUR + && get_armour_slot(item) == EQ_CLOAK) + { + amusement *= 2; + } + } + xom_is_stimulated(amusement); + } + item.flags |= ISFLAG_CURSED; } diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc index 60bacbd15b..7d7daea1b6 100644 --- a/crawl-ref/source/items.cc +++ b/crawl-ref/source/items.cc @@ -34,6 +34,7 @@ #include "externs.h" #include "beam.h" +#include "branch.h" #include "cloud.h" #include "debug.h" #include "delay.h" @@ -73,6 +74,7 @@ #include "transfor.h" #include "tutorial.h" #include "view.h" +#include "xom.h" static bool invisible_to_player( const item_def& item ); static void item_list_on_square( std::vector<const item_def*>& items, @@ -530,6 +532,8 @@ void destroy_item_stack( int x, int y ) UNIQ_LOST_IN_ABYSS ); } + xom_check_lost_item( mitm[o] ); + mitm[o].base_type = OBJ_UNASSIGNED; mitm[o].quantity = 0; } @@ -944,8 +948,16 @@ static std::string origin_place_desc(const item_def &item) bool is_rune(const item_def &item) { - return (item.base_type == OBJ_MISCELLANY && - item.sub_type == MISC_RUNE_OF_ZOT); + return (item.base_type == OBJ_MISCELLANY + && item.sub_type == MISC_RUNE_OF_ZOT); +} + +bool is_unique_rune(const item_def &item) +{ + return (item.base_type == OBJ_MISCELLANY + && item.sub_type == MISC_RUNE_OF_ZOT + && item.plus != RUNE_DEMONIC + && item.plus != RUNE_ABYSSAL); } bool origin_describable(const item_def &item) @@ -1473,8 +1485,12 @@ int move_item_to_player( int obj, int quant_got, bool quiet ) if (!quiet) mpr("Now all you have to do is get back out of the dungeon!"); you.char_direction = GDT_ASCENDING; + xom_is_stimulated(255, XM_INTRIGUED); } + if (item.base_type == OBJ_ORBS && you.level_type == LEVEL_DUNGEON) + unset_branch_flags(BFLAG_HAS_ORB); + you.turn_is_over = true; return (retval); @@ -1542,6 +1558,9 @@ void move_item_to_grid( int *const obj, int x, int y ) mitm[*obj].link = igrd[x][y]; igrd[x][y] = *obj; + if (mitm[*obj].base_type == OBJ_ORBS && you.level_type == LEVEL_DUNGEON) + set_branch_flags(BFLAG_HAS_ORB); + return; } @@ -3026,6 +3045,30 @@ item_def find_item_type(object_class_type base_type, std::string name) return (item); } +bool item_is_equipped(const item_def &item) +{ + if (item.x != -1 || item.y != -1) + return (false); + + for (int i = 0; i < NUM_EQUIP; i++) + { + if (you.equip[i] == EQ_NONE) + continue; + + item_def& eq(you.inv[you.equip[i]]); + + if (!is_valid_item(eq)) + continue; + + if (eq.slot == item.slot) + return (true); + else if (&eq == &item) + return (true); + } + + return (false); +} + //////////////////////////////////////////////////////////////////////// // item_def functions. diff --git a/crawl-ref/source/items.h b/crawl-ref/source/items.h index f223c35382..9d0b8eb93c 100644 --- a/crawl-ref/source/items.h +++ b/crawl-ref/source/items.h @@ -163,9 +163,12 @@ void autopickup(); int find_free_slot(const item_def &i); bool is_rune(const item_def &item); +bool is_unique_rune(const item_def &item); bool need_to_autoinscribe(); void request_autoinscribe(bool do_inscribe = true); void autoinscribe(); +bool item_is_equipped(const item_def &item); + #endif diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc index 5ad6c4a742..e09c774295 100644 --- a/crawl-ref/source/misc.cc +++ b/crawl-ref/source/misc.cc @@ -68,6 +68,7 @@ #include "skills2.h" #include "spells3.h" #include "stash.h" +#include "state.h" #include "stuff.h" #include "terrain.h" #include "transfor.h" @@ -75,6 +76,7 @@ #include "travel.h" #include "tutorial.h" #include "view.h" +#include "xom.h" // void place_chunks(int mcls, unsigned char rot_status, unsigned char chx, // unsigned char chy, unsigned char ch_col) @@ -466,7 +468,54 @@ static void leaving_level_now() you.level_type_name = newtype; } -void up_stairs(dungeon_feature_type force_stair) +static void set_entry_cause(entry_cause_type default_cause, + level_area_type old_level_type) +{ + ASSERT(default_cause != NUM_ENTRY_CAUSE_TYPES); + + if (old_level_type == you.level_type) + return; + + if (crawl_state.is_god_acting()) + { + if (crawl_state.is_god_retribution()) + you.entry_cause = EC_GOD_RETRIUBTION; + else + you.entry_cause = EC_GOD_ACT; + + you.entry_cause_god = crawl_state.which_god_acting(); + } + else if (default_cause != EC_UNKNOWN) + { + you.entry_cause = default_cause; + you.entry_cause_god = GOD_NO_GOD; + } + else + { + you.entry_cause = EC_SELF_EXPLICIT; + you.entry_cause_god = GOD_NO_GOD; + } +} + +static int runes_in_pack() +{ + int num_runes = 0; + + for (int i = 0; i < ENDOFPACK; i++) + { + if (is_valid_item( you.inv[i] ) + && you.inv[i].base_type == OBJ_MISCELLANY + && you.inv[i].sub_type == MISC_RUNE_OF_ZOT) + { + num_runes += you.inv[i].quantity; + } + } + + return num_runes; +} + +void up_stairs(dungeon_feature_type force_stair, + entry_cause_type entry_cause) { dungeon_feature_type stair_find = force_stair? force_stair : grd[you.x_pos][you.y_pos]; @@ -617,6 +666,9 @@ void up_stairs(dungeon_feature_type force_stair) load(stair_taken, LOAD_ENTER_LEVEL, old_level_type, old_level, old_where); + set_entry_cause(entry_cause, old_level_type); + entry_cause = you.entry_cause; + you.turn_is_over = true; save_game_state(); @@ -625,6 +677,16 @@ void up_stairs(dungeon_feature_type force_stair) viewwindow(1, true); + // Left Zot without enough runes to get back in (probably because + // of dropping some runes within Zot), but need to get back in Zot + // to get the Orb? Zom finds that funny. + if (stair_find == DNGN_RETURN_FROM_ZOT + && runes_in_pack() < NUMBER_OF_RUNES_NEEDED + && (branches[BRANCH_HALL_OF_ZOT].branch_flags & BFLAG_HAS_ORB)) + { + xom_is_stimulated(255, "Xom snickers loudly.", true); + } + if (you.skills[SK_TRANSLOCATIONS] > 0 && !allow_control_teleport( true )) mpr( "You sense a powerful magical force warping space.", MSGCH_WARN ); @@ -687,7 +749,8 @@ void up_stairs(dungeon_feature_type force_stair) } } // end up_stairs() -void down_stairs( int old_level, dungeon_feature_type force_stair ) +void down_stairs( int old_level, dungeon_feature_type force_stair, + entry_cause_type entry_cause ) { int i; const level_area_type old_level_type = you.level_type; @@ -761,17 +824,7 @@ void down_stairs( int old_level, dungeon_feature_type force_stair ) if (stair_find == DNGN_ENTER_ZOT) { - int num_runes = 0; - - for (i = 0; i < ENDOFPACK; i++) - { - if (is_valid_item( you.inv[i] ) - && you.inv[i].base_type == OBJ_MISCELLANY - && you.inv[i].sub_type == MISC_RUNE_OF_ZOT) - { - num_runes += you.inv[i].quantity; - } - } + int num_runes = runes_in_pack(); if (num_runes < NUMBER_OF_RUNES_NEEDED) { @@ -871,7 +924,8 @@ void down_stairs( int old_level, dungeon_feature_type force_stair ) if (stair_find == DNGN_EXIT_ABYSS || stair_find == DNGN_EXIT_PANDEMONIUM) { mpr("You pass through the gate."); - more(); + if (!(you.wizard && crawl_state.is_replaying_keys())) + more(); } if (old_level_type != you.level_type && you.level_type == LEVEL_DUNGEON) @@ -953,9 +1007,64 @@ void down_stairs( int old_level, dungeon_feature_type force_stair ) const bool newlevel = load(stair_taken, LOAD_ENTER_LEVEL, old_level_type, old_level, old_where); - + + set_entry_cause(entry_cause, old_level_type); + entry_cause = you.entry_cause; + if (newlevel) - xom_is_stimulated(49); + { + switch(you.level_type) + { + case LEVEL_DUNGEON: + xom_is_stimulated(49); + break; + + case LEVEL_PORTAL_VAULT: + // Portal vaults aren't as interesting. + xom_is_stimulated(25); + break; + + case LEVEL_LABYRINTH: + // Finding the way out of a labyrinth interests Xom. + xom_is_stimulated(98); + break; + + case LEVEL_ABYSS: + case LEVEL_PANDEMONIUM: + { + // Paranoia + if (old_level_type == you.level_type) + break; + + PlaceInfo &place_info = you.get_place_info(); + + // Entering voluntarily only stimulates Xom if you've never + // been there before + if ((place_info.num_visits == 1 && place_info.levels_seen == 1) + || entry_cause != EC_SELF_EXPLICIT) + { + if (crawl_state.is_god_acting()) + xom_is_stimulated(256); + else if (entry_cause == EC_SELF_EXPLICIT) + { + // Entering Pandemonium or the Abyss for the first + // time *voluntarily* stimulates Xom much more than + // entering a normal dungeon level for the first time. + xom_is_stimulated(128, XM_INTRIGUED); + } + else if (entry_cause == EC_SELF_RISKY) + xom_is_stimulated(128); + else + xom_is_stimulated(256); + } + + break; + } + + default: + ASSERT(false); + } + } unsigned char pc = 0; unsigned char pt = random2avg(28, 3); diff --git a/crawl-ref/source/misc.h b/crawl-ref/source/misc.h index 7d594af86e..460c76e403 100644 --- a/crawl-ref/source/misc.h +++ b/crawl-ref/source/misc.h @@ -39,8 +39,9 @@ void search_around( bool only_adjacent = false ); /* *********************************************************************** * called from: acr - effects - spells3 * *********************************************************************** */ -void down_stairs(int old_level, dungeon_feature_type force_stair = DNGN_UNSEEN); - +void down_stairs(int old_level, + dungeon_feature_type force_stair = DNGN_UNSEEN, + entry_cause_type entry_cause = EC_UNKNOWN); // last updated 12may2000 {dlb} /* *********************************************************************** @@ -75,7 +76,8 @@ void turn_corpse_into_chunks( item_def &item ); /* *********************************************************************** * called from: acr * *********************************************************************** */ -void up_stairs(dungeon_feature_type force_stair = DNGN_UNSEEN); +void up_stairs(dungeon_feature_type force_stair = DNGN_UNSEEN, + entry_cause_type entry_cause = EC_UNKNOWN); // last updated 12may2000 {dlb} /* *********************************************************************** diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc index c9775005c9..c06233b88a 100644 --- a/crawl-ref/source/monstuff.cc +++ b/crawl-ref/source/monstuff.cc @@ -67,6 +67,7 @@ #include "tutorial.h" #include "view.h" #include "stash.h" +#include "xom.h" static bool handle_special_ability(monsters *monster, bolt & beem); static bool handle_pickup(monsters *monster); @@ -242,6 +243,10 @@ bool curse_an_item( bool decay_potions ) /* don't change you.inv_special (just for fun) */ if (you.inv[item].base_type == OBJ_POTIONS) { + // Xom is amused by useful potions being ruined. + if (item_value(you.inv[item], true) / you.inv[item].quantity > 2) + xom_is_stimulated(32 * you.inv[item].quantity); + you.inv[item].sub_type = POT_DECAY; unset_ident_flags( you.inv[item], ISFLAG_IDENT_MASK ); // all different } diff --git a/crawl-ref/source/mutation.cc b/crawl-ref/source/mutation.cc index f59a2b99d2..cf6cb04919 100644 --- a/crawl-ref/source/mutation.cc +++ b/crawl-ref/source/mutation.cc @@ -46,6 +46,7 @@ #include "stuff.h" #include "transfor.h" #include "view.h" +#include "xom.h" int how_mutated(void); diff --git a/crawl-ref/source/ouch.cc b/crawl-ref/source/ouch.cc index ebfb0d2b7a..636a9234dc 100644 --- a/crawl-ref/source/ouch.cc +++ b/crawl-ref/source/ouch.cc @@ -77,6 +77,7 @@ #include "stuff.h" #include "tutorial.h" #include "view.h" +#include "xom.h" static void end_game( scorefile_entry &se ); @@ -682,12 +683,24 @@ static void xom_checks_damage(kill_method_type death_type, //if (you.hp <= dam) // xom_is_stimulated(32); - if ((death_type != KILLED_BY_MONSTER && death_type != KILLED_BY_BEAM) - || death_source < 0 || death_source >= MAX_MONSTERS) + if (death_type == KILLED_BY_TARGETTING) + { + // Xom thinks the player hurting him/herself is funny. + xom_is_stimulated(255 * dam / (dam + you.hp)); + return; + } + else if (death_type == KILLED_BY_FALLING_DOWN_STAIRS) + { + // Xom thinks falling down the stairs is hilarious + xom_is_stimulated(255); + return; + } + else if ((death_type != KILLED_BY_MONSTER && death_type != KILLED_BY_BEAM) + || death_source < 0 || death_source >= MAX_MONSTERS) { return ; } - + int amusementvalue = 1; const monsters *monster = &menv[death_source]; @@ -695,6 +708,13 @@ static void xom_checks_damage(kill_method_type death_type, if (!monster->alive()) return; + if (mons_attitude(monster) == ATT_FRIENDLY) + { + // Xom thinks collateral damage is funny + xom_is_stimulated(255 * dam / (dam + you.hp)); + return; + } + int leveldif = monster->hit_dice - you.experience_level; if (leveldif == 0) diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index c897a1432e..e6e36f69be 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -70,6 +70,7 @@ #include "travel.h" #include "tutorial.h" #include "view.h" +#include "xom.h" std::string pronoun_you(description_level_type desc) { @@ -5121,7 +5122,9 @@ void player::init() your_level = 0; level_type = LEVEL_DUNGEON; - where_are_you = BRANCH_MAIN_DUNGEON; + entry_cause = EC_SELF_EXPLICIT; + entry_cause_god = GOD_NO_GOD; + where_are_you = BRANCH_MAIN_DUNGEON; char_direction = GDT_DESCENDING; prev_targ = MHITNOT; diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc index 3aacbe4df7..1171c62c47 100644 --- a/crawl-ref/source/religion.cc +++ b/crawl-ref/source/religion.cc @@ -73,6 +73,7 @@ #include "terrain.h" #include "tutorial.h" #include "view.h" +#include "xom.h" #if DEBUG_RELIGION # define DEBUG_DIAGNOSTICS 1 diff --git a/crawl-ref/source/religion.h b/crawl-ref/source/religion.h index 1d124a1fa2..20bf075669 100644 --- a/crawl-ref/source/religion.h +++ b/crawl-ref/source/religion.h @@ -44,21 +44,10 @@ bool god_likes_butchery(god_type god); bool god_hates_butchery(god_type god); void divine_retribution(god_type god); -bool xom_is_nice(); -void xom_is_stimulated(int maxinterestingness); -void xom_acts(bool niceness, int sever); -const char *describe_xom_favour(); - bool beogh_water_walk(); void beogh_idol_revenge(); bool ely_destroy_weapons(); void trog_burn_books(); bool tso_stab_safe_monster(const actor *act); - -inline void xom_acts(int sever) -{ - xom_acts(xom_is_nice(), sever); -} - #endif diff --git a/crawl-ref/source/skills2.cc b/crawl-ref/source/skills2.cc index b21d075fcc..91bfaca21c 100644 --- a/crawl-ref/source/skills2.cc +++ b/crawl-ref/source/skills2.cc @@ -34,7 +34,6 @@ #include "menu.h" #include "player.h" #include "randart.h" -#include "religion.h" #include "stuff.h" #include "transfor.h" #include "view.h" diff --git a/crawl-ref/source/spells2.cc b/crawl-ref/source/spells2.cc index 0d1f1ef171..6b1a19a48d 100644 --- a/crawl-ref/source/spells2.cc +++ b/crawl-ref/source/spells2.cc @@ -50,6 +50,7 @@ #include "terrain.h" #include "traps.h" #include "view.h" +#include "xom.h" static int raise_corpse( int corps, int corx, int cory, beh_type corps_beh, int corps_hit, int actual ); @@ -379,9 +380,16 @@ int animate_a_corpse( int axps, int ayps, beh_type corps_beh, int corps_hit, if (is_animatable_corpse(item) && (class_allowed == CORPSE_BODY || item.sub_type == CORPSE_SKELETON)) { + bool was_butchering = is_being_butchered(item); + rc = raise_corpse(objl, axps, ayps, corps_beh, corps_hit, 1); if ( rc ) + { mpr("The dead are walking!"); + + if (was_butchering) + xom_is_stimulated(255); + } break; } objl = item.link; @@ -611,7 +619,7 @@ bool brand_weapon(int which_brand, int power) // with removing the miscast effect. We may need to revise the spell // to level 8 or 9. XXX. // miscast_effect(SPTYP_TRANSLOCATION, - // 9, 90, 100, "a distortion effect"); + // 9, 90, 100, "distortion branding"); break; case SPWPN_PAIN: diff --git a/crawl-ref/source/spells3.cc b/crawl-ref/source/spells3.cc index 3560fbb80f..f8e6621329 100644 --- a/crawl-ref/source/spells3.cc +++ b/crawl-ref/source/spells3.cc @@ -43,7 +43,6 @@ #include "place.h" #include "player.h" #include "randart.h" -#include "religion.h" #include "spells1.h" #include "spells4.h" #include "spl-cast.h" @@ -51,6 +50,7 @@ #include "stuff.h" #include "traps.h" #include "view.h" +#include "xom.h" bool cast_selective_amnesia(bool force) { @@ -654,10 +654,17 @@ static bool teleport_player( bool allow_control, bool new_abyss_area ) void you_teleport_now( bool allow_control, bool new_abyss_area ) { const bool randtele = teleport_player(allow_control, new_abyss_area); + // Xom is amused by uncontrolled teleports that land you in a - // dangerous place. - if (randtele && player_in_a_dangerous_place()) + // dangerous place, unless the player is in the Abyss and + // teleported to escape from all the monsters chasing him/her, + // since in that case the new dangerous area is almost certainly + // *less* dangerous than the old dangerous area. + if (randtele && player_in_a_dangerous_place() + && you.level_type != LEVEL_ABYSS) + { xom_is_stimulated(255); + } } bool entomb(int powc) diff --git a/crawl-ref/source/spells4.cc b/crawl-ref/source/spells4.cc index 4fddf1ddf0..486c649878 100644 --- a/crawl-ref/source/spells4.cc +++ b/crawl-ref/source/spells4.cc @@ -44,7 +44,6 @@ #include "ouch.h" #include "player.h" #include "randart.h" -#include "religion.h" #include "skills.h" #include "spells1.h" #include "spells4.h" @@ -1360,12 +1359,12 @@ static int distortion_monsters(int x, int y, int pow, int message) if (you.skills[SK_TRANSLOCATIONS] < random2(8)) { miscast_effect( SPTYP_TRANSLOCATION, pow / 9 + 1, pow, 100, - "a distortion effect" ); + "cast bend on self" ); } else { miscast_effect( SPTYP_TRANSLOCATION, 1, 1, 100, - "a distortion effect" ); + "cast bend on self" ); } return 1; diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc index 31377824bc..565e98e98d 100644 --- a/crawl-ref/source/spl-cast.cc +++ b/crawl-ref/source/spl-cast.cc @@ -55,6 +55,7 @@ #include "stuff.h" #include "transfor.h" #include "view.h" +#include "xom.h" #ifdef DOS #include <conio.h> diff --git a/crawl-ref/source/state.cc b/crawl-ref/source/state.cc index 25e314eda6..21c25d8450 100644 --- a/crawl-ref/source/state.cc +++ b/crawl-ref/source/state.cc @@ -298,7 +298,7 @@ void game_state::inc_god_acting(god_type which_god, bool is_retribution) } god_act.which_god = which_god; - god_act.retribution = is_retribution; + god_act.retribution = is_retribution || god_act.retribution; god_act.depth++; } diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc index 7921420285..478c53d5ad 100644 --- a/crawl-ref/source/tags.cc +++ b/crawl-ref/source/tags.cc @@ -777,6 +777,8 @@ static void tag_construct_you(struct tagHeader &th) marshallByte(th,you.berserk_penalty); marshallByte(th,you.level_type); marshallString(th, you.level_type_name); + marshallByte(th,you.entry_cause); + marshallByte(th,you.entry_cause_god); marshallByte(th,you.synch_time); marshallByte(th,you.disease); marshallByte(th,you.species); @@ -1124,6 +1126,8 @@ static void tag_read_you(struct tagHeader &th, char minorVersion) you.berserk_penalty = unmarshallByte(th); you.level_type = static_cast<level_area_type>( unmarshallByte(th) ); you.level_type_name = unmarshallString(th); + you.entry_cause = static_cast<entry_cause_type>( unmarshallByte(th) ); + you.entry_cause_god = static_cast<god_type>( unmarshallByte(th) ); you.synch_time = unmarshallByte(th); you.disease = unmarshallByte(th); you.species = static_cast<species_type>(unmarshallByte(th)); diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc index 0fd7ced541..0fd01f982c 100644 --- a/crawl-ref/source/view.cc +++ b/crawl-ref/source/view.cc @@ -66,6 +66,7 @@ #include "terrain.h" #include "travel.h" #include "tutorial.h" +#include "xom.h" // These are hidden from the rest of the world... use the functions // below to get information about the map grid. @@ -1011,6 +1012,8 @@ void monster_grid(bool do_updates) void fire_monster_alerts() { + int num_hostile = 0; + for (int s = 0; s < MAX_MONSTERS; s++) { monsters *monster = &menv[s]; @@ -1037,6 +1040,9 @@ void fire_monster_alerts() // Monster was viewed this turn monster->flags |= MF_WAS_IN_VIEW; + + if (mons_attitude(monster) == ATT_HOSTILE) + num_hostile++; } else { @@ -1051,6 +1057,21 @@ void fire_monster_alerts() } } + // Xom thinks it's hilarious the way the player picks up an ever + // growing entourage of monsters while running through the Abyss. + // To approximate this, if the number of hostile monsters in view + // is greater than it ever was for this particular trip to the + // Abyss, Xom is stimulated in proprotion to the number of + // hostile monsters. Thus if the entourage doesn't grow, then + // Xom becomes bored. + if (you.level_type == LEVEL_ABYSS + && you.attribute[ATTR_ABYSS_ENTOURAGE] < num_hostile) + { + you.attribute[ATTR_ABYSS_ENTOURAGE] = num_hostile; + + xom_is_stimulated(16 * num_hostile); + } + monsters_seen_this_turn.clear(); } diff --git a/crawl-ref/source/xom.cc b/crawl-ref/source/xom.cc index ed8b244b5c..3c4cc6f6b7 100644 --- a/crawl-ref/source/xom.cc +++ b/crawl-ref/source/xom.cc @@ -21,6 +21,7 @@ #include "mutation.h" #include "ouch.h" #include "player.h" +#include "randart.h" #include "religion.h" #include "spells3.h" #include "spl-cast.h" @@ -28,6 +29,18 @@ #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 @@ -110,9 +123,34 @@ bool xom_is_nice() return (you.gift_timeout > 0 && you.piety > 100); } -void xom_is_stimulated(int maxinterestingness) +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) + if (you.religion != GOD_XOM || maxinterestingness <= 0) return; // Xom is not stimulated by his own acts, at least not directly. @@ -120,21 +158,49 @@ void xom_is_stimulated(int maxinterestingness) return; int interestingness = random2(maxinterestingness); - if (interestingness < 12) - return; + +#if DEBUG_RELIGION || DEBUG_GIFTS || DEBUG_XOM + mprf(MSGCH_DIAGNOSTICS, + "Xom: maxinterestingness = %d, interestingness = %d", + maxinterestingness, interestingness); +#endif + if (interestingness > 255) interestingness = 255; - if (interestingness > you.gift_timeout) + + bool was_stimulated = false; + if (interestingness > you.gift_timeout && interestingness >= 12) { you.gift_timeout = interestingness; - god_speaks(GOD_XOM, - ((interestingness > 200) ? "Xom roars with laughter!" : - (interestingness > 100) ? "Xom thinks this is hilarious!" : - (interestingness > 75) ? "Xom is highly amused!" : - (interestingness > 50) ? "Xom is amused." : - (interestingness > 25) ? "Xom is mildly amused." : - "Xom is interested.")); + 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, 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) @@ -151,7 +217,7 @@ void xom_makes_you_cast_random_spell(int sever) god_speaks(GOD_XOM, "Xom's power flows through you!"); -#if DEBUG_DIAGNOSTICS +#if DEBUG_DIAGNOSTICS || DEBUG_RELIGION || DEBUG_XOM mprf(MSGCH_DIAGNOSTICS, "Xom_acts();spell: %d, spellenum: %d", spell, spellenum); #endif @@ -547,7 +613,7 @@ static bool xom_is_good(int sever) (temp_rand == 0) ? "\"You need some minor adjustments, mortal!\"" : (temp_rand == 1) ? "\"Let me alter your pitiful body.\"" : (temp_rand == 2) ? "Xom's power touches on you for a moment." - : "You hear Xom's maniacal chuckling."); + : "You hear Xom's maniacal cackling."); mpr("Your body is suffused with distortional energy."); set_hp(1 + random2(you.hp), false); @@ -818,14 +884,37 @@ static bool xom_is_bad(int sever) void xom_acts(bool niceness, int sever) { -#if DEBUG_DIAGNOSTICS +#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)); + } + else + { + niceness = true; + mprf(MSGCH_GOD, which_god, + "%s calls in a favour from Xom.", god_name(which_god)); + } + } + if (niceness) { // Good stuff. @@ -838,7 +927,48 @@ void xom_acts(bool niceness, int sever) 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 = 200 - you.piety; } + +void xom_check_lost_item(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 (is_unique_rune(item)) + xom_is_stimulated(255, "Xom snickers loudly.", true); + else if (you.entry_cause == EC_SELF_EXPLICIT && + !(item.flags & ISFLAG_DROPPED)) + { + // Player voluntarily entered Pan or the Abyss looking + // for runes, yet never found it. + if (item.plus == RUNE_ABYSSAL) + { + // 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 + xom_is_stimulated(64, "Xom snickers softly.", true); + } + } +} diff --git a/crawl-ref/source/xom.h b/crawl-ref/source/xom.h new file mode 100644 index 0000000000..c68bafafbc --- /dev/null +++ b/crawl-ref/source/xom.h @@ -0,0 +1,41 @@ +/* + * File: xom.h + * Summary: Misc Xom related functions. + * Written by: Linley Henzell + * + * Modified for Crawl Reference by $Author$ on $Date$ + * + * Change History (most recent first): + * + * <1> 09/28/07 MPC Split from religion.h + */ + +#ifndef XOM_H +#define XOM_H + +class item_def; + +enum xom_message_type +{ + XM_NORMAL, + XM_INTRIGUED, + NUM_XOM_MESSAGE_TYPES +}; + +void xom_is_stimulated(int maxinterestingness, + xom_message_type message_type = XM_NORMAL, + bool force_message = false); +void xom_is_stimulated(int maxinterestingness, std::string message, + bool force_message = false); +bool xom_is_nice(); +void xom_acts(bool niceness, int sever); +const char *describe_xom_favour(); + +inline void xom_acts(int sever) +{ + xom_acts(xom_is_nice(), sever); +} + +void xom_check_lost_item(item_def& item); + +#endif |