diff options
Diffstat (limited to 'stone_soup/crawl-ref/source/items.cc')
-rw-r--r-- | stone_soup/crawl-ref/source/items.cc | 3070 |
1 files changed, 0 insertions, 3070 deletions
diff --git a/stone_soup/crawl-ref/source/items.cc b/stone_soup/crawl-ref/source/items.cc deleted file mode 100644 index 7c7a822343..0000000000 --- a/stone_soup/crawl-ref/source/items.cc +++ /dev/null @@ -1,3070 +0,0 @@ -/* - * File: items.cc - * Summary: Misc (mostly) inventory related functions. - * Written by: Linley Henzell - * - * Change History (most recent first): - * - * <9> 7/08/01 MV Added messages for chunks/corpses rotting - * <8> 8/07/99 BWR Added Rune stacking - * <7> 6/13/99 BWR Added auto staff detection - * <6> 6/12/99 BWR Fixed time system. - * <5> 6/9/99 DML Autopickup - * <4> 5/26/99 JDJ Drop will attempt to take off armour. - * <3> 5/21/99 BWR Upped armour skill learning slightly. - * <2> 5/20/99 BWR Added assurance that against inventory count being wrong. - * <1> -/--/-- LRH Created - */ - -#include "AppHdr.h" -#include "items.h" -#include "clua.h" - -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <ctype.h> - -#ifdef DOS -#include <conio.h> -#endif - -#include "externs.h" - -#include "beam.h" -#include "cloud.h" -#include "debug.h" -#include "delay.h" -#include "effects.h" -#include "invent.h" -#include "it_use2.h" -#include "item_use.h" -#include "itemname.h" -#include "itemprop.h" -#include "misc.h" -#include "monplace.h" -#include "monstuff.h" -#include "mstuff2.h" -#include "mon-util.h" -#include "mutation.h" -#include "player.h" -#include "randart.h" -#include "religion.h" -#include "shopping.h" -#include "skills.h" -#include "spl-cast.h" -#include "stuff.h" -#include "stash.h" - -static void autopickup(void); -static bool is_stackable_item( const item_def &item ); - -// Used to be called "unlink_items", but all it really does is make -// sure item coordinates are correct to the stack they're in. -- bwr -void fix_item_coordinates(void) -{ - int x,y,i; - - // nails all items to the ground (i.e. sets x,y) - for (x = 0; x < GXM; x++) - { - for (y = 0; y < GYM; y++) - { - i = igrd[x][y]; - - while (i != NON_ITEM) - { - mitm[i].x = x; - mitm[i].y = y; - i = mitm[i].link; - } - } - } -} - -// This function uses the items coordinates to relink all the igrd lists. -void link_items(void) -{ - int i,j; - - // first, initailize igrd array - for (i = 0; i < GXM; i++) - { - for (j = 0; j < GYM; j++) - igrd[i][j] = NON_ITEM; - } - - // link all items on the grid, plus shop inventory, - // DON'T link the huge pile of monster items at (0,0) - - for (i = 0; i < MAX_ITEMS; i++) - { - if (!is_valid_item(mitm[i]) || (mitm[i].x == 0 && mitm[i].y == 0)) - { - // item is not assigned, or is monster item. ignore. - mitm[i].link = NON_ITEM; - continue; - } - - // link to top - mitm[i].link = igrd[ mitm[i].x ][ mitm[i].y ]; - igrd[ mitm[i].x ][ mitm[i].y ] = i; - } -} // end link_items() - -static bool item_ok_to_clean(int item) -{ - // 5. never clean food or Orbs - if (mitm[item].base_type == OBJ_FOOD || mitm[item].base_type == OBJ_ORBS) - return false; - - // never clean runes - if (mitm[item].base_type == OBJ_MISCELLANY - && mitm[item].sub_type == MISC_RUNE_OF_ZOT) - { - return false; - } - - return true; -} - -// returns index number of first available space, or NON_ITEM for -// unsuccessful cleanup (should be exceedingly rare!) -int cull_items(void) -{ - // XXX: Not the prettiest of messages, but the player - // deserves to know whenever this kicks in. -- bwr - mpr( "Too many items on level, removing some.", MSGCH_WARN ); - - /* rules: - 1. Don't cleanup anything nearby the player - 2. Don't cleanup shops - 3. Don't cleanup monster inventory - 4. Clean 15% of items - 5. never remove food, orbs, runes - 7. uniques weapons are moved to the abyss - 8. randarts are simply lost - 9. unrandarts are 'destroyed', but may be generated again - */ - - int x,y, item, next; - int first_cleaned = NON_ITEM; - - // 2. avoid shops by avoiding (0,5..9) - // 3. avoid monster inventory by iterating over the dungeon grid - for (x = 5; x < GXM; x++) - { - for (y = 5; y < GYM; y++) - { - // 1. not near player! - if (x > you.x_pos - 9 && x < you.x_pos + 9 - && y > you.y_pos - 9 && y < you.y_pos + 9) - { - continue; - } - - // iterate through the grids list of items: - for (item = igrd[x][y]; item != NON_ITEM; item = next) - { - next = mitm[item].link; // in case we can't get it later. - - if (item_ok_to_clean(item) && random2(100) < 15) - { - if (is_fixed_artefact( mitm[item] )) - { - // 7. move uniques to abyss - set_unique_item_status( OBJ_WEAPONS, mitm[item].special, - UNIQ_LOST_IN_ABYSS ); - } - else if (is_unrandom_artefact( mitm[item] )) - { - // 9. unmark unrandart - int x = find_unrandart_index(item); - if (x >= 0) - set_unrandart_exist(x, 0); - } - - // POOF! - destroy_item( item ); - if (first_cleaned == NON_ITEM) - first_cleaned = item; - } - } // end for item - - } // end y - } // end x - - return (first_cleaned); -} - -// Note: This function is to isolate all the checks to see if -// an item is valid (often just checking the quantity). -// -// It shouldn't be used a a substitute for those cases -// which actually want to check the quantity (as the -// rules for unused objects might change). -bool is_valid_item( const item_def &item ) -{ - return (item.base_type != OBJ_UNASSIGNED && item.quantity > 0); -} - -// Reduce quantity of an inventory item, do cleanup if item goes away. -// -// Returns true if stack of items no longer exists. -bool dec_inv_item_quantity( int obj, int amount ) -{ - bool ret = false; - - if (you.equip[EQ_WEAPON] == obj) - you.wield_change = true; - - if (you.inv[obj].quantity <= amount) - { - for (int i = 0; i < NUM_EQUIP; i++) - { - if (you.equip[i] == obj) - { - you.equip[i] = -1; - if (i == EQ_WEAPON) - { - unwield_item( obj ); - canned_msg( MSG_EMPTY_HANDED ); - } - } - } - - you.inv[obj].base_type = OBJ_UNASSIGNED; - you.inv[obj].quantity = 0; - - ret = true; - } - else - { - you.inv[obj].quantity -= amount; - } - - burden_change(); - - return (ret); -} - -// Reduce quantity of a monster/grid item, do cleanup if item goes away. -// -// Returns true if stack of items no longer exists. -bool dec_mitm_item_quantity( int obj, int amount ) -{ - if (mitm[obj].quantity <= amount) - { - destroy_item( obj ); - return (true); - } - - mitm[obj].quantity -= amount; - - return (false); -} - -void inc_inv_item_quantity( int obj, int amount ) -{ - if (you.equip[EQ_WEAPON] == obj) - you.wield_change = true; - - you.inv[obj].quantity += amount; - burden_change(); -} - -void inc_mitm_item_quantity( int obj, int amount ) -{ - mitm[obj].quantity += amount; -} - -void init_item( int item ) -{ - if (item == NON_ITEM) - return; - - mitm[item].base_type = OBJ_UNASSIGNED; - mitm[item].sub_type = 0; - mitm[item].plus = 0; - mitm[item].plus2 = 0; - mitm[item].special = 0; - mitm[item].quantity = 0; - mitm[item].colour = 0; - mitm[item].flags = 0; - - mitm[item].x = 0; - mitm[item].y = 0; - mitm[item].link = NON_ITEM; -} - -// Returns an unused mitm slot, or NON_ITEM if none available. -// The reserve is the number of item slots to not check. -// Items may be culled if a reserve <= 10 is specified. -int get_item_slot( int reserve ) -{ - ASSERT( reserve >= 0 ); - - int item = NON_ITEM; - - for (item = 0; item < (MAX_ITEMS - reserve); item++) - { - if (!is_valid_item( mitm[item] )) - break; - } - - if (item >= MAX_ITEMS - reserve) - { - item = (reserve <= 10) ? cull_items() : NON_ITEM; - - if (item == NON_ITEM) - return (NON_ITEM); - } - - ASSERT( item != NON_ITEM ); - - init_item( item ); - - return (item); -} - -void unlink_item( int dest ) -{ - int c = 0; - int cy = 0; - - // Don't destroy non-items, may be called after an item has been - // reduced to zero quantity however. - if (dest == NON_ITEM || !is_valid_item( mitm[dest] )) - return; - - if (mitm[dest].x == 0 && mitm[dest].y == 0) - { - // (0,0) is where the monster items are (and they're unlinked by igrd), - // although it also contains items that are not linked in yet. - // - // Check if a monster has it: - for (c = 0; c < MAX_MONSTERS; c++) - { - struct monsters *monster = &menv[c]; - - if (monster->type == -1) - continue; - - for (cy = 0; cy < NUM_MONSTER_SLOTS; cy++) - { - if (monster->inv[cy] == dest) - { - monster->inv[cy] = NON_ITEM; - - mitm[dest].x = 0; - mitm[dest].y = 0; - mitm[dest].link = NON_ITEM; - - // This causes problems when changing levels. -- bwr - // if (monster->type == MONS_DANCING_WEAPON) - // monster_die(monster, KILL_RESET, 0); - return; - } - } - } - - // Always return because this item might just be temporary. - return; - } - else - { - // Linked item on map: - // - // Use the items (x,y) to access the list (igrd[x][y]) where - // the item should be linked. - - // First check the top: - if (igrd[ mitm[dest].x ][ mitm[dest].y ] == dest) - { - // link igrd to the second item - igrd[ mitm[dest].x ][ mitm[dest].y ] = mitm[dest].link; - - mitm[dest].x = 0; - mitm[dest].y = 0; - mitm[dest].link = NON_ITEM; - return; - } - - // Okay, item is buried, find item that's on top of it: - for (c = igrd[ mitm[dest].x ][ mitm[dest].y ]; c != NON_ITEM; c = mitm[c].link) - { - // find item linking to dest item - if (is_valid_item( mitm[c] ) && mitm[c].link == dest) - { - // unlink dest - mitm[c].link = mitm[dest].link; - - mitm[dest].x = 0; - mitm[dest].y = 0; - mitm[dest].link = NON_ITEM; - return; - } - } - } - -#if DEBUG - // Okay, the sane ways are gone... let's warn the player: - mpr( "BUG WARNING: Problems unlinking item!!!", MSGCH_DANGER ); - - // Okay, first we scan all items to see if we have something - // linked to this item. We're not going to return if we find - // such a case... instead, since things are already out of - // alignment, let's assume there might be multiple links as well. - bool linked = false; - int old_link = mitm[dest].link; // used to try linking the first - - // clean the relevant parts of the object: - mitm[dest].base_type = OBJ_UNASSIGNED; - mitm[dest].quantity = 0; - mitm[dest].x = 0; - mitm[dest].y = 0; - mitm[dest].link = NON_ITEM; - - // Look through all items for links to this item. - for (c = 0; c < MAX_ITEMS; c++) - { - if (is_valid_item( mitm[c] ) && mitm[c].link == dest) - { - // unlink item - mitm[c].link = old_link; - - if (!linked) - { - old_link = NON_ITEM; - linked = true; - } - } - } - - // Now check the grids to see if it's linked as a list top. - for (c = 2; c < (GXM - 1); c++) - { - for (cy = 2; cy < (GYM - 1); cy++) - { - if (igrd[c][cy] == dest) - { - igrd[c][cy] = old_link; - - if (!linked) - { - old_link = NON_ITEM; // cleaned after the first - linked = true; - } - } - } - } - - - // Okay, finally warn player if we didn't do anything. - if (!linked) - mpr("BUG WARNING: Item didn't seem to be linked at all.", MSGCH_DANGER); -#endif -} // end unlink_item() - -static void item_cleanup(item_def &item) -{ - item.base_type = OBJ_UNASSIGNED; - item.quantity = 0; - item.orig_place = 0; - item.orig_monnum = 0; -} - -void destroy_item( int dest ) -{ - // Don't destroy non-items, but this function may be called upon - // to remove items reduced to zero quantity, so we allow "invalid" - // objects in. - if (dest == NON_ITEM || !is_valid_item( mitm[dest] )) - return; - - unlink_item( dest ); - - item_cleanup(mitm[dest]); -} - -void destroy_item_stack( int x, int y ) -{ - int o = igrd[x][y]; - - igrd[x][y] = NON_ITEM; - - while (o != NON_ITEM) - { - int next = mitm[o].link; - - if (is_valid_item( mitm[o] )) - { - if (mitm[o].base_type == OBJ_ORBS) - { - set_unique_item_status( OBJ_ORBS, mitm[o].sub_type, - UNIQ_LOST_IN_ABYSS ); - } - else if (is_fixed_artefact( mitm[o] )) - { - set_unique_item_status( OBJ_WEAPONS, mitm[o].special, - UNIQ_LOST_IN_ABYSS ); - } - - mitm[o].base_type = OBJ_UNASSIGNED; - mitm[o].quantity = 0; - } - - o = next; - } -} - - -/* - * Takes keyin as an argument because it will only display a long list of items - * if ; is pressed. - */ -void item_check(char keyin) -{ - char item_show[50][50]; - char temp_quant[10]; - - int counter = 0; - int counter_max = 0; - - const int grid = grd[you.x_pos][you.y_pos]; - - if (grid >= DNGN_ENTER_HELL && grid <= DNGN_PERMADRY_FOUNTAIN) - { - if (grid >= DNGN_STONE_STAIRS_DOWN_I && grid <= DNGN_ROCK_STAIRS_DOWN) - { - snprintf( info, INFO_SIZE, "There is a %s staircase leading down here.", - (grid == DNGN_ROCK_STAIRS_DOWN) ? "rock" : "stone" ); - - mpr(info); - } - else if (grid >= DNGN_STONE_STAIRS_UP_I && grid <= DNGN_ROCK_STAIRS_UP) - { - snprintf( info, INFO_SIZE, "There is a %s staircase leading upwards here.", - (grid == DNGN_ROCK_STAIRS_UP) ? "rock" : "stone" ); - - mpr(info); - } - else - { - switch (grid) - { - case DNGN_ENTER_HELL: - mpr("There is a gateway to Hell here."); - break; - case DNGN_ENTER_GEHENNA: - mpr("There is a gateway to Gehenna here."); - break; - case DNGN_ENTER_COCYTUS: - mpr("There is a gateway to the frozen wastes of Cocytus here."); - break; - case DNGN_ENTER_TARTARUS: - mpr("There is a gateway to Tartarus here."); - break; - case DNGN_ENTER_DIS: - mpr("There is a gateway to the Iron City of Dis here."); - break; - case DNGN_ENTER_SHOP: - snprintf( info, INFO_SIZE, "There is an entrance to %s here.", shop_name(you.x_pos, you.y_pos)); - mpr(info); - break; - case DNGN_ENTER_LABYRINTH: - mpr("There is an entrance to a labyrinth here."); - mpr("Beware, for starvation awaits!"); - break; - case DNGN_ENTER_ABYSS: - mpr("There is a one-way gate to the infinite horrors of the Abyss here."); - break; - case DNGN_STONE_ARCH: - mpr("There is an empty stone archway here."); - break; - case DNGN_EXIT_ABYSS: - mpr("There is a gateway leading out of the Abyss here."); - break; - case DNGN_ENTER_PANDEMONIUM: - mpr("There is a gate leading to the halls of Pandemonium here."); - break; - case DNGN_EXIT_PANDEMONIUM: - mpr("There is a gate leading out of Pandemonium here."); - break; - case DNGN_TRANSIT_PANDEMONIUM: - mpr("There is a gate leading to another region of Pandemonium here."); - break; - case DNGN_ENTER_ORCISH_MINES: - mpr("There is a staircase to the Orcish Mines here."); - break; - case DNGN_ENTER_HIVE: - mpr("There is a staircase to the Hive here."); - break; - case DNGN_ENTER_LAIR: - mpr("There is a staircase to the Lair here."); - break; - case DNGN_ENTER_SLIME_PITS: - mpr("There is a staircase to the Slime Pits here."); - break; - case DNGN_ENTER_VAULTS: - mpr("There is a staircase to the Vaults here."); - break; - case DNGN_ENTER_CRYPT: - mpr("There is a staircase to the Crypt here."); - break; - case DNGN_ENTER_HALL_OF_BLADES: - mpr("There is a staircase to the Hall of Blades here."); - break; - case DNGN_ENTER_ZOT: - mpr("There is a gate to the Realm of Zot here."); - break; - case DNGN_ENTER_TEMPLE: - mpr("There is a staircase to the Ecumenical Temple here."); - break; - case DNGN_ENTER_SNAKE_PIT: - mpr("There is a staircase to the Snake Pit here."); - break; - case DNGN_ENTER_ELVEN_HALLS: - mpr("There is a staircase to the Elven Halls here."); - break; - case DNGN_ENTER_TOMB: - mpr("There is a staircase to the Tomb here."); - break; - case DNGN_ENTER_SWAMP: - mpr("There is a staircase to the Swamp here."); - break; - case DNGN_RETURN_FROM_ORCISH_MINES: - case DNGN_RETURN_FROM_HIVE: - case DNGN_RETURN_FROM_LAIR: - case DNGN_RETURN_FROM_VAULTS: - case DNGN_RETURN_FROM_TEMPLE: - mpr("There is a staircase back to the Dungeon here."); - break; - case DNGN_RETURN_FROM_SLIME_PITS: - case DNGN_RETURN_FROM_SNAKE_PIT: - case DNGN_RETURN_FROM_SWAMP: - mpr("There is a staircase back to the Lair here."); - break; - case DNGN_RETURN_FROM_CRYPT: - case DNGN_RETURN_FROM_HALL_OF_BLADES: - mpr("There is a staircase back to the Vaults here."); - break; - case DNGN_RETURN_FROM_TOMB: - mpr("There is a staircase back to the Crypt here."); - break; - case DNGN_RETURN_FROM_ELVEN_HALLS: - mpr("There is a staircase back to the Mines here."); - break; - case DNGN_RETURN_FROM_ZOT: - mpr("There is a gate leading back out of this place here."); - break; - case DNGN_ALTAR_ZIN: - mpr("There is a glowing white marble altar of Zin here."); - break; - case DNGN_ALTAR_SHINING_ONE: - mpr("There is a glowing golden altar of the Shining One here."); - break; - case DNGN_ALTAR_KIKUBAAQUDGHA: - mpr("There is an ancient bone altar of Kikubaaqudgha here."); - break; - case DNGN_ALTAR_YREDELEMNUL: - mpr("There is a basalt altar of Yredelemnul here."); - break; - case DNGN_ALTAR_XOM: - mpr("There is a shimmering altar of Xom here."); - break; - case DNGN_ALTAR_VEHUMET: - mpr("There is a shining altar of Vehumet here."); - break; - case DNGN_ALTAR_OKAWARU: - mpr("There is an iron altar of Okawaru here."); - break; - case DNGN_ALTAR_MAKHLEB: - mpr("There is a burning altar of Makhleb here."); - break; - case DNGN_ALTAR_SIF_MUNA: - mpr("There is a deep blue altar of Sif Muna here."); - break; - case DNGN_ALTAR_TROG: - mpr("There is a bloodstained altar of Trog here."); - break; - case DNGN_ALTAR_NEMELEX_XOBEH: - mpr("There is a sparkling altar of Nemelex Xobeh here."); - break; - case DNGN_ALTAR_ELYVILON: - mpr("There is a silver altar of Elyvilon here."); - break; - case DNGN_BLUE_FOUNTAIN: - mpr("There is a fountain here (q to drink)."); - break; - case DNGN_SPARKLING_FOUNTAIN: - mpr("There is a sparkling fountain here (q to drink)."); - break; - case DNGN_DRY_FOUNTAIN_I: - case DNGN_DRY_FOUNTAIN_II: - case DNGN_DRY_FOUNTAIN_IV: - case DNGN_DRY_FOUNTAIN_VI: - case DNGN_DRY_FOUNTAIN_VIII: - case DNGN_PERMADRY_FOUNTAIN: - mpr("There is a dry fountain here."); - break; - } - } - } - - if (igrd[you.x_pos][you.y_pos] == NON_ITEM && keyin == ';') - { - mpr("There are no items here."); - return; - } - - autopickup(); - - origin_set(you.x_pos, you.y_pos); - - int objl = igrd[you.x_pos][you.y_pos]; - - while (objl != NON_ITEM) - { - counter++; - - if (counter > 45) - { - strcpy(item_show[counter], "Too many items."); - break; - } - - if (mitm[objl].base_type == OBJ_GOLD) - { - itoa(mitm[objl].quantity, temp_quant, 10); - strcpy(item_show[counter], temp_quant); - strcat(item_show[counter], " gold piece"); - if (mitm[objl].quantity > 1) - strcat(item_show[counter], "s"); - - } - else - { - char str_pass[ ITEMNAME_SIZE ]; - it_name(objl, DESC_NOCAP_A, str_pass); - strcpy(item_show[counter], str_pass); - } - - objl = mitm[objl].link; - } - - counter_max = counter; - counter = 0; - - if (counter_max == 1) - { - strcpy(info, "You see here "); // remember 'an'. - - strcat(info, item_show[counter_max]); - strcat(info, "."); - mpr(info); - - counter++; - counter_max = 0; // to skip next part. - - } - - if ((counter_max > 0 && counter_max < 6) - || (counter_max > 1 && keyin == ';')) - { - mpr("Things that are here:"); - - while (counter < counter_max) - { - // this is before the strcpy because item_show start at 1, not 0. - counter++; - mpr(item_show[counter]); - } - } - - if (counter_max > 5 && keyin != ';') - mpr("There are several objects here."); -} - - -void pickup_menu(int item_link) -{ - std::vector<item_def*> items; - - for (int i = item_link; i != NON_ITEM; i = mitm[i].link) - items.push_back( &mitm[i] ); - - std::vector<SelItem> selected = - select_items( items, "Select items to pick up" ); - redraw_screen(); - - for (int i = 0, count = selected.size(); i < count; ++i) { - for (int j = item_link; j != NON_ITEM; j = mitm[j].link) { - if (&mitm[j] == selected[i].item) { - if (j == item_link) - item_link = mitm[j].link; - - unsigned long oldflags = mitm[j].flags; - mitm[j].flags &= ~(ISFLAG_THROWN | ISFLAG_DROPPED); - int result = move_item_to_player( j, selected[i].quantity ); - - // If we cleared any flags on the items, but the pickup was - // partial, reset the flags for the items that remain on the - // floor. - if (is_valid_item(mitm[j])) - mitm[j].flags = oldflags; - - if (result == 0) - { - mpr("You can't carry that much weight."); - return; - } - else if (result == -1) - { - mpr("You can't carry that many items."); - return; - } - break; - } - } - } -} - -bool origin_known(const item_def &item) -{ - return (item.orig_place != 0); -} - -// We have no idea where the player found this item. -void origin_set_unknown(item_def &item) -{ - if (!origin_known(item)) - { - item.orig_place = 0xFFFF; - item.orig_monnum = 0; - } -} - -// This item is starting equipment. -void origin_set_startequip(item_def &item) -{ - if (!origin_known(item)) - { - item.orig_place = 0xFFFF; - item.orig_monnum = -1; - } -} - -void origin_set_monster(item_def &item, const monsters *monster) -{ - if (!origin_known(item)) - { - if (!item.orig_monnum) - item.orig_monnum = monster->type + 1; - item.orig_place = get_packed_place(); - } -} - -void origin_purchased(item_def &item) -{ - // We don't need to check origin_known if it's a shop purchase - item.orig_place = get_packed_place(); - // Hackiness - item.orig_monnum = -1; -} - -void origin_acquired(item_def &item, int agent) -{ - // We don't need to check origin_known if it's a divine gift - item.orig_place = get_packed_place(); - // Hackiness - item.orig_monnum = -2 - agent; -} - -void origin_set_inventory(void (*oset)(item_def &item)) -{ - for (int i = 0; i < ENDOFPACK; ++i) - { - if (is_valid_item(you.inv[i])) - oset(you.inv[i]); - } -} - -static int first_corpse_monnum(int x, int y) -{ - // We could look for a corpse on this square and assume that the - // items belonged to it, but that is unsatisfactory. - return (0); -} - -void origin_set(int x, int y) -{ - int monnum = first_corpse_monnum(x, y); - unsigned short pplace = get_packed_place(); - for (int link = igrd[x][y]; link != NON_ITEM; link = mitm[link].link) - { - item_def &item = mitm[link]; - if (origin_known(item)) - continue; - if (!item.orig_monnum) - item.orig_monnum = static_cast<short>( monnum ); - item.orig_place = pplace; - } -} - -void origin_set_monstercorpse(item_def &item, int x, int y) -{ - item.orig_monnum = first_corpse_monnum(x, y); -} - -void origin_freeze(item_def &item, int x, int y) -{ - if (!origin_known(item)) - { - if (!item.orig_monnum && x != -1 && y != -1) - origin_set_monstercorpse(item, x, y); - item.orig_place = get_packed_place(); - } -} - -static std::string origin_monster_name(const item_def &item) -{ - const int monnum = item.orig_monnum - 1; - if (monnum == MONS_PLAYER_GHOST) - return ("a player ghost"); - else if (monnum == MONS_PANDEMONIUM_DEMON) - return ("a demon"); - char monnamebuf[ITEMNAME_SIZE]; // Le sigh. - moname(monnum, true, DESC_NOCAP_A, monnamebuf); - return (monnamebuf); -} - -static std::string origin_monster_desc(const item_def &item) -{ - return (origin_monster_name(item)); -} - -static std::string origin_place_desc(const item_def &item) -{ - std::string place = branch_level_name(item.orig_place); - if (place.length() && place != "Pandemonium") - place[0] = tolower(place[0]); - return (place.find("level") == 0? - "on " + place - : "in " + place); -} - -bool is_rune(const item_def &item) -{ - return (item.base_type == OBJ_MISCELLANY && - item.sub_type == MISC_RUNE_OF_ZOT); -} - -bool origin_describable(const item_def &item) -{ - return (origin_known(item) - && (item.orig_place != 0xFFFFU || item.orig_monnum == -1) - && (!is_stackable_item(item) || is_rune(item)) - && item.quantity == 1 - && item.base_type != OBJ_CORPSES - && (item.base_type != OBJ_FOOD || item.sub_type != FOOD_CHUNK) - // Portable altars cannot be tracked meaningfully with Crawl's - // current handling for portable altars. - && (item.base_type != OBJ_MISCELLANY || - item.sub_type != MISC_PORTABLE_ALTAR_OF_NEMELEX)); -} - -std::string article_it(const item_def &item) -{ - /* - bool them = false; - if (item.quantity > 1) - them = true; - else if (item.base_type == OBJ_ARMOUR && - item.sub_type == ARM_BOOTS) - { - if (item.plus2 != TBOOT_NAGA_BARDING && - item.plus2 != TBOOT_CENTAUR_BARDING) - them = true; - } - else if (item.base_type == OBJ_ARMOUR && - item.sub_type == ARM_GLOVES) - { - them = true; - } - - return them? "them" : "it"; - */ - // "it" is always correct, since gloves and boots also come in pairs. - return "it"; -} - -bool origin_is_original_equip(const item_def &item) -{ - return (item.orig_place == 0xFFFFU && item.orig_monnum == -1); -} - -std::string origin_desc(const item_def &item) -{ - if (!origin_describable(item)) - return (""); - - if (origin_is_original_equip(item)) - return "Original Equipment"; - - std::string desc; - if (item.orig_monnum) - { - if (item.orig_monnum < 0) - { - int iorig = -item.orig_monnum - 2; - switch (iorig) - { - case -1: - desc += "You bought " + article_it(item) + " in a shop "; - break; - case AQ_SCROLL: - desc += "You acquired " + article_it(item) + " "; - break; - case AQ_CARD_ACQUISITION: - desc += "You drew \"Acquisition\" "; - break; - case AQ_CARD_VIOLENCE: - desc += "You drew \"Violence\" "; - break; - case AQ_CARD_PROTECTION: - desc += "You drew \"Protection\" "; - break; - case AQ_CARD_KNOWLEDGE: - desc += "You drew \"Knowledge\" "; - break; - case AQ_WIZMODE: - desc += "Your wizardly powers created " - + article_it(item) + " "; - break; - default: - if (iorig > GOD_NO_GOD && iorig < NUM_GODS) - desc += std::string(god_name(iorig)) - + " gifted " + article_it(item) + " to you "; - else - // Bug really. - desc += "You stumbled upon " + article_it(item) + " "; - break; - } - } - else if (item.orig_monnum - 1 == MONS_DANCING_WEAPON) - desc += "You subdued it "; - else - desc += "You took " + article_it(item) + " off " - + origin_monster_desc(item) + " "; - } - else - desc += "You found " + article_it(item) + " "; - desc += origin_place_desc(item); - return (desc); -} - -bool pickup_single_item(int link, int qty) -{ - if (you.attribute[ATTR_TRANSFORMATION] == TRAN_AIR - && you.duration[DUR_TRANSFORMATION] > 0) - { - mpr("You can't pick up anything in this form!"); - return (false); - } - - if (player_is_levitating() && !wearing_amulet(AMU_CONTROLLED_FLIGHT)) - { - mpr("You can't reach the floor from up here."); - return (false); - } - - if (qty < 1 || qty > mitm[link].quantity) - qty = mitm[link].quantity; - - unsigned long oldflags = mitm[link].flags; - mitm[link].flags &= ~(ISFLAG_THROWN | ISFLAG_DROPPED); - int num = move_item_to_player( link, qty ); - if (is_valid_item(mitm[link])) - mitm[link].flags = oldflags; - - if (num == -1) - { - mpr("You can't carry that many items."); - return (false); - } - else if (num == 0) - { - mpr("You can't carry that much weight."); - return (false); - } - - return (true); -} - -void pickup(void) -{ - int o = 0; - int m = 0; - unsigned char keyin = 0; - int next; - char str_pass[ ITEMNAME_SIZE ]; - - if (you.attribute[ATTR_TRANSFORMATION] == TRAN_AIR - && you.duration[DUR_TRANSFORMATION] > 0) - { - mpr("You can't pick up anything in this form!"); - return; - } - - if (player_is_levitating() && !wearing_amulet(AMU_CONTROLLED_FLIGHT)) - { - mpr("You can't reach the floor from up here."); - return; - } - - // Fortunately, the player is prevented from testing their - // portable altar in the Ecumenical Temple. -- bwr - if (grd[you.x_pos][you.y_pos] == DNGN_ALTAR_NEMELEX_XOBEH - && !player_in_branch( BRANCH_ECUMENICAL_TEMPLE )) - { - if (inv_count() >= ENDOFPACK) - { - mpr("There is a portable altar here, but you can't carry anything else."); - return; - } - - if (yesno("There is a portable altar here. Pick it up?")) - { - for (m = 0; m < ENDOFPACK; m++) - { - if (!is_valid_item( you.inv[m] )) - { - you.inv[m].base_type = OBJ_MISCELLANY; - you.inv[m].sub_type = MISC_PORTABLE_ALTAR_OF_NEMELEX; - you.inv[m].plus = 0; - you.inv[m].plus2 = 0; - you.inv[m].special = 0; - you.inv[m].colour = LIGHTMAGENTA; - you.inv[m].quantity = 1; - set_ident_flags( you.inv[m], ISFLAG_IDENT_MASK ); - - you.inv[m].x = -1; - you.inv[m].y = -1; - you.inv[m].link = m; - - burden_change(); - - in_name( m, DESC_INVENTORY_EQUIP, str_pass ); - strcpy( info, str_pass ); - mpr( info ); - break; - } - } - - grd[you.x_pos][you.y_pos] = DNGN_FLOOR; - } - } - - o = igrd[you.x_pos][you.y_pos]; - - if (o == NON_ITEM) - { - mpr("There are no items here."); - } - else if (mitm[o].link == NON_ITEM) // just one item? - { - pickup_single_item(o, mitm[o].quantity); - } // end of if items_here - else - { - mpr("There are several objects here."); - - while (o != NON_ITEM) - { - next = mitm[o].link; - - if (keyin != 'a') - { - strcpy(info, "Pick up "); - - if (mitm[o].base_type == OBJ_GOLD) - { - char st_prn[20]; - itoa(mitm[o].quantity, st_prn, 10); - strcat(info, st_prn); - strcat(info, " gold piece"); - - if (mitm[o].quantity > 1) - strcat(info, "s"); - } - else - { - it_name(o, DESC_NOCAP_A, str_pass); - strcat(info, str_pass); - } - - strcat(info, "\? (y,n,a,*,q)"); - mpr( info, MSGCH_PROMPT ); - - keyin = get_ch(); - } - - if (keyin == '*' || keyin == '?' || keyin == ',') - { - pickup_menu(o); - break; - } - - if (keyin == 'q') - break; - - if (keyin == 'y' || keyin == 'a') - { - mitm[o].flags &= ~(ISFLAG_THROWN | ISFLAG_DROPPED); - int result = move_item_to_player( o, mitm[o].quantity ); - - if (result == 0) - { - mpr("You can't carry that much weight."); - keyin = 'x'; // resets from 'a' - } - else if (result == -1) - { - mpr("You can't carry that many items."); - break; - } - } - - o = next; - } - } -} // end pickup() - -static bool is_stackable_item( const item_def &item ) -{ - if (!is_valid_item( item )) - return (false); - - if (item.base_type == OBJ_MISSILES - || (item.base_type == OBJ_FOOD && item.sub_type != FOOD_CHUNK) - || item.base_type == OBJ_SCROLLS - || item.base_type == OBJ_POTIONS - || item.base_type == OBJ_UNKNOWN_II - || (item.base_type == OBJ_MISCELLANY - && item.sub_type == MISC_RUNE_OF_ZOT)) - { - return (true); - } - - return (false); -} - -bool items_stack( const item_def &item1, const item_def &item2 ) -{ - // both items must be stackable - if (!is_stackable_item( item1 ) || !is_stackable_item( item2 )) - return (false); - - // base and sub-types must always be the same to stack - if (item1.base_type != item2.base_type || item1.sub_type != item2.sub_type) - return (false); - - // These classes also require pluses and special - if (item1.base_type == OBJ_MISSILES - || item1.base_type == OBJ_MISCELLANY) // only runes - { - if (item1.plus != item2.plus - || item1.plus2 != item2.plus2 - || item1.special != item2.special) - { - return (false); - } - } - - // Check the flags, food/scrolls/potions don't care about the item's - // ident status (scrolls and potions are known by identifying any - // one of them, the individual status might not be the same). - if (item1.base_type == OBJ_FOOD - || item1.base_type == OBJ_SCROLLS - || item1.base_type == OBJ_POTIONS) - { - if ((item1.flags & ~ISFLAG_IDENT_MASK) - != (item2.flags & ~ISFLAG_IDENT_MASK)) - { - return (false); - } - - // Thanks to mummy cursing, we can have potions of decay - // that don't look alike... so we don't stack potions - // if either isn't identified and they look different. -- bwr - if (item1.base_type == OBJ_POTIONS - && item1.special != item2.special - && (!item_ident( item1, ISFLAG_KNOW_TYPE ) - || !item_ident( item2, ISFLAG_KNOW_TYPE ))) - { - return (false); - } - } - else if (item1.flags != item2.flags) - { - return (false); - } - - return (true); -} - -static int userdef_find_free_slot(const item_def &i) -{ -#ifdef CLUA_BINDINGS - int slot = -1; - if (!clua.callfn("c_assign_invletter", "u>d", &i, &slot)) - return (-1); - return (slot); -#else - return -1; -#endif -} - -int find_free_slot(const item_def &i) -{ -#define slotisfree(s) \ - ((s) >= 0 && (s) < ENDOFPACK && !is_valid_item(you.inv[s])) - - bool searchforward = false; - // If we're doing Lua, see if there's a Lua function that can give - // us a free slot. - int slot = userdef_find_free_slot(i); - if (slot == -2 || Options.assign_item_slot == SS_FORWARD) - searchforward = true; - - if (slotisfree(slot)) - return slot; - - // See if the item remembers where it's been. Lua code can play with - // this field so be extra careful. - if ((i.slot >= 'a' && i.slot <= 'z') || - (i.slot >= 'A' && i.slot <= 'Z')) - slot = letter_to_index(i.slot); - - if (slotisfree(slot)) - return slot; - - if (searchforward) - { - // Return first free slot - for (slot = 0; slot < ENDOFPACK; ++slot) { - if (!is_valid_item(you.inv[slot])) - return slot; - } - } - else - { - // This is the new default free slot search. We look for the last - // available slot that does not leave a gap in the inventory. - bool accept_empty = false; - for (slot = ENDOFPACK - 1; slot >= 0; --slot) - { - if (is_valid_item(you.inv[slot])) - { - if (!accept_empty && slot + 1 < ENDOFPACK && - !is_valid_item(you.inv[slot + 1])) - return (slot + 1); - - accept_empty = true; - } - else if (accept_empty) - { - return slot; - } - } - } - return (-1); -#undef slotisfree -} - -// Returns quantity of items moved into player's inventory and -1 if -// the player's inventory is full. -int move_item_to_player( int obj, int quant_got, bool quiet ) -{ - int imass = 0; - int unit_mass = 0; - int retval = quant_got; - char brek = 0; - bool partialPickup = false; - - int m = 0; - - // Gold has no mass, so we handle it first. - if (mitm[obj].base_type == OBJ_GOLD) - { - you.gold += quant_got; - dec_mitm_item_quantity( obj, quant_got ); - you.redraw_gold = 1; - - if (!quiet) - { - snprintf( info, INFO_SIZE, "You pick up %d gold piece%s.", - quant_got, (quant_got > 1) ? "s" : "" ); - - mpr(info); - } - - you.turn_is_over = 1; - return (retval); - } - - unit_mass = item_mass( mitm[obj] ); - if (quant_got > mitm[obj].quantity || quant_got <= 0) - quant_got = mitm[obj].quantity; - - imass = unit_mass * quant_got; - - brek = 0; - - // multiply both constants * 10 - - if ((int) you.burden + imass > carrying_capacity()) - { - // calculate quantity we can actually pick up - int part = (carrying_capacity() - (int)you.burden) / unit_mass; - - if (part < 1) - return (0); - - // only pickup 'part' items - quant_got = part; - partialPickup = true; - - retval = part; - } - - if (is_stackable_item( mitm[obj] )) - { - for (m = 0; m < ENDOFPACK; m++) - { - if (items_stack( you.inv[m], mitm[obj] )) - { - if (!quiet && partialPickup) - mpr("You can only carry some of what is here."); - - inc_inv_item_quantity( m, quant_got ); - dec_mitm_item_quantity( obj, quant_got ); - burden_change(); - - if (!quiet) - { - in_name( m, DESC_INVENTORY, info ); - mpr(info); - } - - you.turn_is_over = 1; - - return (retval); - } - } // end of for m loop. - } - - // can't combine, check for slot space - if (inv_count() >= ENDOFPACK) - return (-1); - - if (!quiet && partialPickup) - mpr("You can only carry some of what is here."); - - int freeslot = find_free_slot(mitm[obj]); - if (freeslot < 0 || freeslot >= ENDOFPACK - || is_valid_item(you.inv[freeslot])) - { - // Something is terribly wrong - return (-1); - } - - item_def &item = you.inv[freeslot]; - // copy item - item = mitm[obj]; - item.x = -1; - item.y = -1; - item.link = freeslot; - - origin_freeze(item, you.x_pos, you.y_pos); - - item.quantity = quant_got; - dec_mitm_item_quantity( obj, quant_got ); - burden_change(); - - if (!quiet) - { - in_name( freeslot, DESC_INVENTORY, info ); - mpr(info); - } - - if (item.base_type == OBJ_ORBS - && you.char_direction == DIR_DESCENDING) - { - if (!quiet) - mpr("Now all you have to do is get back out of the dungeon!"); - you.char_direction = DIR_ASCENDING; - } - - you.turn_is_over = 1; - - return (retval); -} // end move_item_to_player() - - -// Moves mitm[obj] to (x,y)... will modify the value of obj to -// be the index of the final object (possibly different). -// -// Done this way in the hopes that it will be obvious from -// calling code that "obj" is possibly modified. -void move_item_to_grid( int *const obj, int x, int y ) -{ - // must be a valid reference to a valid object - if (*obj == NON_ITEM || !is_valid_item( mitm[*obj] )) - return; - - // If it's a stackable type... - if (is_stackable_item( mitm[*obj] )) - { - // Look for similar item to stack: - for (int i = igrd[x][y]; i != NON_ITEM; i = mitm[i].link) - { - // check if item already linked here -- don't want to unlink it - if (*obj == i) - return; - - if (items_stack( mitm[*obj], mitm[i] )) - { - // Add quantity to item already here, and dispose - // of obj, while returning the found item. -- bwr - inc_mitm_item_quantity( i, mitm[*obj].quantity ); - destroy_item( *obj ); - *obj = i; - return; - } - } - } - - ASSERT( *obj != NON_ITEM ); - - // Need to actually move object, so first unlink from old position. - unlink_item( *obj ); - - // move item to coord: - mitm[*obj].x = x; - mitm[*obj].y = y; - - // link item to top of list. - mitm[*obj].link = igrd[x][y]; - igrd[x][y] = *obj; - - return; -} - -void move_item_stack_to_grid( int x, int y, int targ_x, int targ_y ) -{ - // Tell all items in stack what the new coordinate is. - for (int o = igrd[x][y]; o != NON_ITEM; o = mitm[o].link) - { - mitm[o].x = targ_x; - mitm[o].y = targ_y; - } - - igrd[targ_x][targ_y] = igrd[x][y]; - igrd[x][y] = NON_ITEM; -} - - -// returns quantity dropped -bool copy_item_to_grid( const item_def &item, int x_plos, int y_plos, - int quant_drop, bool mark_dropped ) -{ - if (quant_drop == 0) - return (false); - - // default quant_drop == -1 => drop all - if (quant_drop < 0) - quant_drop = item.quantity; - - // loop through items at current location - if (is_stackable_item( item )) - { - for (int i = igrd[x_plos][y_plos]; i != NON_ITEM; i = mitm[i].link) - { - if (items_stack( item, mitm[i] )) - { - inc_mitm_item_quantity( i, quant_drop ); - - // If the items on the floor already have a nonzero slot, - // leave it as such, otherwise set the slot. - if (mark_dropped && !mitm[i].slot) - mitm[i].slot = index_to_letter(item.link); - return (true); - } - } - } - - // item not found in current stack, add new item to top. - int new_item = get_item_slot(10); - if (new_item == NON_ITEM) - return (false); - - // copy item - mitm[new_item] = item; - - // set quantity, and set the item as unlinked - mitm[new_item].quantity = quant_drop; - mitm[new_item].x = 0; - mitm[new_item].y = 0; - mitm[new_item].link = NON_ITEM; - - if (mark_dropped) - { - mitm[new_item].slot = index_to_letter(item.link); - mitm[new_item].flags |= ISFLAG_DROPPED; - mitm[new_item].flags &= ~ISFLAG_THROWN; - origin_set_unknown(mitm[new_item]); - } - - move_item_to_grid( &new_item, x_plos, y_plos ); - - return (true); -} // end copy_item_to_grid() - - -//--------------------------------------------------------------- -// -// move_top_item -- moves the top item of a stack to a new -// location. -// -//--------------------------------------------------------------- -bool move_top_item( int src_x, int src_y, int dest_x, int dest_y ) -{ - int item = igrd[ src_x ][ src_y ]; - if (item == NON_ITEM) - return (false); - - // Now move the item to its new possition... - move_item_to_grid( &item, dest_x, dest_y ); - - return (true); -} - - -//--------------------------------------------------------------- -// -// drop_gold -// -//--------------------------------------------------------------- -static void drop_gold(unsigned int amount) -{ - if (you.gold > 0) - { - if (amount > you.gold) - amount = you.gold; - - snprintf( info, INFO_SIZE, "You drop %d gold piece%s.", - amount, (amount > 1) ? "s" : "" ); - mpr(info); - - // loop through items at grid location, look for gold - int i = igrd[you.x_pos][you.y_pos]; - - while(i != NON_ITEM) - { - if (mitm[i].base_type == OBJ_GOLD) - { - inc_mitm_item_quantity( i, amount ); - you.gold -= amount; - you.redraw_gold = 1; - you.turn_is_over = 1; - return; - } - - // follow link - i = mitm[i].link; - } - - // place on top. - i = get_item_slot(10); - if (i == NON_ITEM) - { - mpr( "Too many items on this level, not dropping the gold." ); - return; - } - - mitm[i].base_type = OBJ_GOLD; - mitm[i].quantity = amount; - mitm[i].flags = 0; - - move_item_to_grid( &i, you.x_pos, you.y_pos ); - - you.gold -= amount; - you.redraw_gold = 1; - - you.turn_is_over = 1; - } - else - { - mpr("You don't have any money."); - } -} // end drop_gold() - -bool drop_item( int item_dropped, int quant_drop ) { - if (item_dropped == PROMPT_GOT_SPECIAL) // ie '$' for gold - { - // drop gold - if (quant_drop < 0 || quant_drop > static_cast< int >( you.gold )) - quant_drop = you.gold; - - drop_gold( quant_drop ); - return (true); - } - - if (quant_drop < 0 || quant_drop > you.inv[item_dropped].quantity) - quant_drop = you.inv[item_dropped].quantity; - - if (item_dropped == you.equip[EQ_LEFT_RING] - || item_dropped == you.equip[EQ_RIGHT_RING] - || item_dropped == you.equip[EQ_AMULET]) - { - mpr("You will have to take that off first."); - return (false); - } - - if (item_dropped == you.equip[EQ_WEAPON] - && you.inv[item_dropped].base_type == OBJ_WEAPONS - && item_cursed( you.inv[item_dropped] )) - { - mpr("That object is stuck to you!"); - return (false); - } - - for (int i = EQ_CLOAK; i <= EQ_BODY_ARMOUR; i++) - { - if (item_dropped == you.equip[i] && you.equip[i] != -1) - { - if (!Options.easy_armour) - { - mpr("You will have to take that off first."); - } - else - { - // If we take off the item, cue up the item being dropped - if (takeoff_armour( item_dropped )) - { - start_delay( DELAY_DROP_ITEM, 1, item_dropped, 1 ); - you.turn_is_over = 0; // turn happens later - } - } - - // Regardless, we want to return here because either we're - // aborting the drop, or the drop is delayed until after - // the armour is removed. -- bwr - return (false); - } - } - - // Unwield needs to be done before copy in order to clear things - // like temporary brands. -- bwr - if (item_dropped == you.equip[EQ_WEAPON]) - { - unwield_item(item_dropped); - you.equip[EQ_WEAPON] = -1; - canned_msg( MSG_EMPTY_HANDED ); - } - - if (!copy_item_to_grid( you.inv[item_dropped], - you.x_pos, you.y_pos, quant_drop, true )) - { - mpr( "Too many items on this level, not dropping the item." ); - return (false); - } - - char str_pass[ ITEMNAME_SIZE ]; - quant_name( you.inv[item_dropped], quant_drop, DESC_NOCAP_A, str_pass ); - snprintf( info, INFO_SIZE, "You drop %s.", str_pass ); - mpr(info); - - dec_inv_item_quantity( item_dropped, quant_drop ); - you.turn_is_over = 1; - - return (true); -} - - -static std::string drop_menu_title( int menuflags, const std::string &oldt ) { - std::string res = oldt; - res.erase(0, res.find_first_not_of(" \n\t")); - if (menuflags & MF_MULTISELECT) - res = "[Multidrop] " + res; - return " " + res; -} - -int get_equip_slot(const item_def *item) -{ - int worn = -1; - if (item && in_inventory(*item)) - { - for (int i = 0; i < NUM_EQUIP; ++i) - { - if (you.equip[i] == item->link) - { - worn = i; - break; - } - } - } - return worn; -} - -static std::string drop_selitem_text( const std::vector<MenuEntry*> *s ) -{ - char buf[130]; - bool extraturns = false; - - if (!s->size()) - return ""; - - for (int i = 0, size = s->size(); i < size; ++i) - { - const item_def *item = static_cast<item_def *>( (*s)[i]->data ); - int eq = get_equip_slot(item); - if (eq > EQ_WEAPON && eq < NUM_EQUIP) - { - extraturns = true; - break; - } - } - - snprintf( buf, sizeof buf, " (%lu%s turn%s)", (unsigned long) (s->size()), - extraturns? "+" : "", - s->size() > 1? "s" : "" ); - return buf; -} - -//--------------------------------------------------------------- -// -// drop -// -// Prompts the user for an item to drop -// -//--------------------------------------------------------------- -void drop(void) -{ - if (inv_count() < 1 && you.gold == 0) - { - canned_msg(MSG_NOTHING_CARRIED); - you.activity = ACT_NONE; - return; - } - - static std::vector<SelItem> selected; - - if (!you.activity || selected.empty()) - selected = prompt_invent_items( "Drop which item?", -1, - drop_menu_title, - true, true, '$', - &Options.drop_filter, - drop_selitem_text ); - - if (selected.empty()) - { - canned_msg( MSG_OK ); - you.activity = ACT_NONE; - return; - } - - // Throw away invalid items; items usually go invalid because - // of chunks rotting away. - while (!selected.empty() - // Don't look for gold in inventory - && selected[0].slot != PROMPT_GOT_SPECIAL - && !is_valid_item(you.inv[ selected[0].slot ])) - selected.erase( selected.begin() ); - - // Did the defunct item filter above deprive us of a drop target? - if (!selected.empty()) - { - drop_item( selected[0].slot, selected[0].quantity ); - // Forget the item we just dropped - selected.erase( selected.begin() ); - } - - // If we still have items that want to be dropped, start the multiturn - // activity - you.activity = selected.empty()? ACT_NONE : ACT_MULTIDROP; -} // end drop() - -//--------------------------------------------------------------- -// -// shift_monster -// -// Moves a monster to approximately (x,y) and returns true -// if monster was moved. -// -//--------------------------------------------------------------- -static bool shift_monster( struct monsters *mon, int x, int y ) -{ - bool found_move = false; - - int i, j; - int tx, ty; - int nx = 0, ny = 0; - - int count = 0; - - if (x == 0 && y == 0) - { - // try and find a random floor space some distance away - for (i = 0; i < 50; i++) - { - tx = 5 + random2( GXM - 10 ); - ty = 5 + random2( GYM - 10 ); - - int dist = grid_distance(x, y, tx, ty); - if (grd[tx][ty] == DNGN_FLOOR && dist > 10) - break; - } - - if (i == 50) - return (false); - } - - for (i = -1; i <= 1; i++) - { - for (j = -1; j <= 1; j++) - { - tx = x + i; - ty = y + j; - - if (tx < 5 || tx > GXM - 5 || ty < 5 || ty > GXM - 5) - continue; - - // won't drop on anything but vanilla floor right now - if (grd[tx][ty] != DNGN_FLOOR) - continue; - - if (mgrd[tx][ty] != NON_MONSTER) - continue; - - if (tx == you.x_pos && ty == you.y_pos) - continue; - - count++; - if (one_chance_in(count)) - { - nx = tx; - ny = ty; - found_move = true; - } - } - } - - if (found_move) - { - const int mon_index = mgrd[mon->x][mon->y]; - mgrd[mon->x][mon->y] = NON_MONSTER; - mgrd[nx][ny] = mon_index; - mon->x = nx; - mon->y = ny; - } - - return (found_move); -} - -//--------------------------------------------------------------- -// -// update_corpses -// -// Update all of the corpses and food chunks on the floor. (The -// elapsed time is a double because this is called when we re- -// enter a level and a *long* time may have elapsed). -// -//--------------------------------------------------------------- -void update_corpses(double elapsedTime) -{ - int cx, cy; - - if (elapsedTime <= 0.0) - return; - - const long rot_time = (long) (elapsedTime / 20.0); - - for (int c = 0; c < MAX_ITEMS; c++) - { - if (mitm[c].quantity < 1) - continue; - - if (mitm[c].base_type != OBJ_CORPSES && mitm[c].base_type != OBJ_FOOD) - { - continue; - } - - if (mitm[c].base_type == OBJ_CORPSES - && mitm[c].sub_type > CORPSE_SKELETON) - { - continue; - } - - if (mitm[c].base_type == OBJ_FOOD && mitm[c].sub_type != FOOD_CHUNK) - { - continue; - } - - if (rot_time >= mitm[c].special) - { - if (mitm[c].base_type == OBJ_FOOD) - { - destroy_item(c); - } - else - { - if (mitm[c].sub_type == CORPSE_SKELETON - || !mons_skeleton( mitm[c].plus )) - { - destroy_item(c); - } - else - { - mitm[c].sub_type = CORPSE_SKELETON; - mitm[c].special = 200; - mitm[c].colour = LIGHTGREY; - } - } - } - else - { - ASSERT(rot_time < 256); - mitm[c].special -= rot_time; - } - } - - - int fountain_checks = (int)(elapsedTime / 1000.0); - if (random2(1000) < (int)(elapsedTime) % 1000) - fountain_checks += 1; - - // dry fountains may start flowing again - if (fountain_checks > 0) - { - for (cx=0; cx<GXM; cx++) - { - for (cy=0; cy<GYM; cy++) - { - if (grd[cx][cy] > DNGN_SPARKLING_FOUNTAIN - && grd[cx][cy] < DNGN_PERMADRY_FOUNTAIN) - { - for (int i=0; i<fountain_checks; i++) - { - if (one_chance_in(100)) - { - if (grd[cx][cy] > DNGN_SPARKLING_FOUNTAIN) - grd[cx][cy]--; - } - } - } - } - } - } -} - -static bool remove_enchant_levels( struct monsters *mon, int slot, int min, - int levels ) -{ - const int new_level = mon->enchantment[slot] - levels; - - if (new_level < min) - { - mons_del_ench( mon, - mon->enchantment[slot], mon->enchantment[slot], true ); - return (true); - } - else - { - mon->enchantment[slot] = new_level; - } - - return (false); -} - -//--------------------------------------------------------------- -// -// update_enchantments -// -// Update a monster's enchantments when the player returns -// to the level. -// -// Management for enchantments... problems with this are the oddities -// (monster dying from poison several thousands of turns later), and -// game balance. -// -// Consider: Poison/Sticky Flame a monster at range and leave, monster -// dies but can't leave level to get to player (implied game balance of -// the delayed damage is that the monster could be a danger before -// it dies). This could be fixed by keeping some monsters active -// off level and allowing them to take stairs (a very serious change). -// -// Compare this to the current abuse where the player gets -// effectively extended duration of these effects (although only -// the actual effects only occur on level, the player can leave -// and heal up without having the effect disappear). -// -// This is a simple compromise between the two... the enchantments -// go away, but the effects don't happen off level. -- bwr -// -//--------------------------------------------------------------- -static void update_enchantments( struct monsters *mon, int levels ) -{ - int i; - - for (i = 0; i < NUM_MON_ENCHANTS; i++) - { - switch (mon->enchantment[i]) - { - case ENCH_YOUR_POISON_I: - case ENCH_YOUR_POISON_II: - case ENCH_YOUR_POISON_III: - case ENCH_YOUR_POISON_IV: - remove_enchant_levels( mon, i, ENCH_YOUR_POISON_I, levels ); - break; - - case ENCH_YOUR_SHUGGOTH_I: - case ENCH_YOUR_SHUGGOTH_II: - case ENCH_YOUR_SHUGGOTH_III: - case ENCH_YOUR_SHUGGOTH_IV: - remove_enchant_levels( mon, i, ENCH_YOUR_SHUGGOTH_I, levels ); - break; - - case ENCH_YOUR_ROT_I: - case ENCH_YOUR_ROT_II: - case ENCH_YOUR_ROT_III: - case ENCH_YOUR_ROT_IV: - remove_enchant_levels( mon, i, ENCH_YOUR_ROT_I, levels ); - break; - - case ENCH_BACKLIGHT_I: - case ENCH_BACKLIGHT_II: - case ENCH_BACKLIGHT_III: - case ENCH_BACKLIGHT_IV: - remove_enchant_levels( mon, i, ENCH_BACKLIGHT_I, levels ); - break; - - case ENCH_YOUR_STICKY_FLAME_I: - case ENCH_YOUR_STICKY_FLAME_II: - case ENCH_YOUR_STICKY_FLAME_III: - case ENCH_YOUR_STICKY_FLAME_IV: - remove_enchant_levels( mon, i, ENCH_YOUR_STICKY_FLAME_I, levels ); - break; - - case ENCH_POISON_I: - case ENCH_POISON_II: - case ENCH_POISON_III: - case ENCH_POISON_IV: - remove_enchant_levels( mon, i, ENCH_POISON_I, levels ); - break; - - case ENCH_STICKY_FLAME_I: - case ENCH_STICKY_FLAME_II: - case ENCH_STICKY_FLAME_III: - case ENCH_STICKY_FLAME_IV: - remove_enchant_levels( mon, i, ENCH_STICKY_FLAME_I, levels ); - break; - - case ENCH_FRIEND_ABJ_I: - case ENCH_FRIEND_ABJ_II: - case ENCH_FRIEND_ABJ_III: - case ENCH_FRIEND_ABJ_IV: - case ENCH_FRIEND_ABJ_V: - case ENCH_FRIEND_ABJ_VI: - if (remove_enchant_levels( mon, i, ENCH_FRIEND_ABJ_I, levels )) - { - monster_die( mon, KILL_RESET, 0 ); - } - break; - - case ENCH_ABJ_I: - case ENCH_ABJ_II: - case ENCH_ABJ_III: - case ENCH_ABJ_IV: - case ENCH_ABJ_V: - case ENCH_ABJ_VI: - if (remove_enchant_levels( mon, i, ENCH_ABJ_I, levels )) - { - monster_die( mon, KILL_RESET, 0 ); - } - break; - - - case ENCH_SHORT_LIVED: - monster_die( mon, KILL_RESET, 0 ); - break; - - case ENCH_TP_I: - case ENCH_TP_II: - case ENCH_TP_III: - case ENCH_TP_IV: - monster_teleport( mon, true ); - break; - - case ENCH_CONFUSION: - monster_blink( mon ); - break; - - case ENCH_GLOWING_SHAPESHIFTER: - case ENCH_SHAPESHIFTER: - case ENCH_CREATED_FRIENDLY: - case ENCH_SUBMERGED: - default: - // don't touch these - break; - - case ENCH_SLOW: - case ENCH_HASTE: - case ENCH_FEAR: - case ENCH_INVIS: - case ENCH_CHARM: - case ENCH_SLEEP_WARY: - // delete enchantment (using function to get this done cleanly) - mons_del_ench(mon, mon->enchantment[i], mon->enchantment[i], true); - break; - } - } -} - - -//--------------------------------------------------------------- -// -// update_level -// -// Update the level when the player returns to it. -// -//--------------------------------------------------------------- -void update_level( double elapsedTime ) -{ - int m, i; - int turns = (int) (elapsedTime / 10.0); - -#if DEBUG_DIAGNOSTICS - int mons_total = 0; - - snprintf( info, INFO_SIZE, "turns: %d", turns ); - mpr( info, MSGCH_DIAGNOSTICS ); -#endif - - update_corpses( elapsedTime ); - - for (m = 0; m < MAX_MONSTERS; m++) - { - struct monsters *mon = &menv[m]; - - if (mon->type == -1) - continue; - -#if DEBUG_DIAGNOSTICS - mons_total++; -#endif - - // following monsters don't get movement - if (mon->flags & MF_JUST_SUMMONED) - continue; - - // XXX: Allow some spellcasting (like Healing and Teleport)? -- bwr - // const bool healthy = (mon->hit_points * 2 > mon->max_hit_points); - - // This is the monster healing code, moved here from tag.cc: - if (monster_descriptor( mon->type, MDSC_REGENERATES ) - || mon->type == MONS_PLAYER_GHOST) - { - heal_monster( mon, turns, false ); - } - else - { - heal_monster( mon, (turns / 10), false ); - } - - if (turns >= 10) - update_enchantments( mon, turns / 10 ); - - // Don't move water or lava monsters around - if (monster_habitat( mon->type ) != DNGN_FLOOR) - continue; - - // Let sleeping monsters lie - if (mon->behaviour == BEH_SLEEP) - continue; - - - const int range = (turns * mon->speed) / 10; - const int moves = (range > 50) ? 50 : range; - - // const bool short_time = (range >= 5 + random2(10)); - const bool long_time = (range >= (500 + roll_dice( 2, 500 ))); - - const bool ranged_attack = (mons_has_ranged_spell( mon ) - || mons_has_ranged_attack( mon )); - -#if DEBUG_DIAGNOSTICS - // probably too annoying even for DEBUG_DIAGNOSTICS - snprintf( info, INFO_SIZE, - "mon #%d: range %d; long %d; pos (%d,%d); targ %d(%d,%d); flags %d", - m, range, long_time, mon->x, mon->y, - mon->foe, mon->target_x, mon->target_y, mon->flags ); - - mpr( info, MSGCH_DIAGNOSTICS ); -#endif - - if (range <= 0) - continue; - - if (long_time - && (mon->behaviour == BEH_FLEE - || mon->behaviour == BEH_CORNERED - || testbits( mon->flags, MF_BATTY ) - || ranged_attack - || coinflip())) - { - if (mon->behaviour != BEH_WANDER) - { - mon->behaviour = BEH_WANDER; - mon->foe = MHITNOT; - mon->target_x = 10 + random2( GXM - 10 ); - mon->target_y = 10 + random2( GYM - 10 ); - } - else - { - // monster will be sleeping after we move it - mon->behaviour = BEH_SLEEP; - } - } - else if (ranged_attack) - { - // if we're doing short time movement and the monster has a - // ranged attack (missile or spell), then the monster will - // flee to gain distance if its "too close", else it will - // just shift its position rather than charge the player. -- bwr - if (grid_distance(mon->x, mon->y, mon->target_x, mon->target_y) < 3) - { - mon->behaviour = BEH_FLEE; - - // if the monster is on the target square, fleeing won't work - if (mon->x == mon->target_x && mon->y == mon->target_y) - { - if (you.x_pos != mon->x || you.y_pos != mon->y) - { - // flee from player's position if different - mon->target_x = you.x_pos; - mon->target_y = you.y_pos; - } - else - { - // randomize the target so we have a direction to flee - mon->target_x += (random2(3) - 1); - mon->target_y += (random2(3) - 1); - } - } - -#if DEBUG_DIAGNOSTICS - mpr( "backing off...", MSGCH_DIAGNOSTICS ); -#endif - } - else - { - shift_monster( mon, mon->x, mon->y ); - -#if DEBUG_DIAGNOSTICS - snprintf(info, INFO_SIZE, "shifted to (%d,%d)", mon->x, mon->y); - mpr( info, MSGCH_DIAGNOSTICS ); -#endif - continue; - } - } - - int pos_x = mon->x, pos_y = mon->y; - - // dirt simple movement: - for (i = 0; i < moves; i++) - { - int mx = (pos_x > mon->target_x) ? -1 : - (pos_x < mon->target_x) ? 1 - : 0; - - int my = (pos_y > mon->target_y) ? -1 : - (pos_y < mon->target_y) ? 1 - : 0; - - if (mon->behaviour == BEH_FLEE) - { - mx *= -1; - my *= -1; - } - - if (pos_x + mx < 0 || pos_x + mx >= GXM) - mx = 0; - - if (pos_y + my < 0 || pos_y + my >= GXM) - my = 0; - - if (mx == 0 && my == 0) - break; - - if (grd[pos_x + mx][pos_y + my] < DNGN_FLOOR) - break; - - pos_x += mx; - pos_y += my; - } - - if (!shift_monster( mon, pos_x, pos_y )) - shift_monster( mon, mon->x, mon->y ); - -#if DEBUG_DIAGNOSTICS - snprintf( info, INFO_SIZE, "moved to (%d,%d)", mon->x, mon->y ); - mpr( info, MSGCH_DIAGNOSTICS ); -#endif - } - -#if DEBUG_DIAGNOSTICS - snprintf( info, INFO_SIZE, "total monsters on level = %d", mons_total ); - mpr( info, MSGCH_DIAGNOSTICS ); -#endif - - for (i = 0; i < MAX_CLOUDS; i++) - delete_cloud( i ); -} - - -//--------------------------------------------------------------- -// -// handle_time -// -// Do various time related actions... -// This function is called about every 20 turns. -// -//--------------------------------------------------------------- -void handle_time( long time_delta ) -{ - int temp_rand; // probability determination {dlb} - - // so as not to reduplicate f(x) calls {dlb} - unsigned int which_miscast = SPTYP_RANDOM; - - bool summon_instead; // for branching within a single switch {dlb} - int which_beastie = MONS_PROGRAM_BUG; // error trapping {dlb} - unsigned char i; // loop variable {dlb} - bool new_rotting_item = false; //mv: becomes true when some new item becomes rotting - - // BEGIN - Nasty things happen to people who spend too long in Hell: - if (player_in_hell() && coinflip()) - { - temp_rand = random2(17); - - mpr((temp_rand == 0) ? "\"You will not leave this place.\"" : - (temp_rand == 1) ? "\"Die, mortal!\"" : - (temp_rand == 2) ? "\"We do not forgive those who trespass against us!\"" : - (temp_rand == 3) ? "\"Trespassers are not welcome here!\"" : - (temp_rand == 4) ? "\"You do not belong in this place!\"" : - (temp_rand == 5) ? "\"Leave now, before it is too late!\"" : - (temp_rand == 6) ? "\"We have you now!\"" : - (temp_rand == 7) ? "You feel a terrible foreboding..." : - (temp_rand == 8) ? "You hear words spoken in a strange and terrible language..." : - - (temp_rand == 9) ? ((you.species != SP_MUMMY) - ? "You smell brimstone." : "Brimstone rains from above.") : - - (temp_rand == 10) ? "Something frightening happens." : - (temp_rand == 11) ? "You sense an ancient evil watching you..." : - (temp_rand == 12) ? "You feel lost and a long, long way from home..." : - (temp_rand == 13) ? "You suddenly feel all small and vulnerable." : - (temp_rand == 14) ? "A gut-wrenching scream fills the air!" : - (temp_rand == 15) ? "You shiver with fear." : - (temp_rand == 16) ? "You sense a hostile presence." - : "You hear diabolical laughter!", MSGCH_TALK); - - temp_rand = random2(27); - - if (temp_rand > 17) // 9 in 27 odds {dlb} - { - temp_rand = random2(8); - - if (temp_rand > 3) // 4 in 8 odds {dlb} - which_miscast = SPTYP_NECROMANCY; - else if (temp_rand > 1) // 2 in 8 odds {dlb} - which_miscast = SPTYP_SUMMONING; - else if (temp_rand > 0) // 1 in 8 odds {dlb} - which_miscast = SPTYP_CONJURATION; - else // 1 in 8 odds {dlb} - which_miscast = SPTYP_ENCHANTMENT; - - miscast_effect( which_miscast, 4 + random2(6), random2avg(97, 3), - 100, "the effects of Hell" ); - } - else if (temp_rand > 7) // 10 in 27 odds {dlb} - { - // 60:40 miscast:summon split {dlb} - summon_instead = (random2(5) > 2); - - switch (you.where_are_you) - { - case BRANCH_DIS: - if (summon_instead) - which_beastie = summon_any_demon(DEMON_GREATER); - else - which_miscast = SPTYP_EARTH; - break; - case BRANCH_GEHENNA: - if (summon_instead) - which_beastie = MONS_FIEND; - else - which_miscast = SPTYP_FIRE; - break; - case BRANCH_COCYTUS: - if (summon_instead) - which_beastie = MONS_ICE_FIEND; - else - which_miscast = SPTYP_ICE; - break; - case BRANCH_TARTARUS: - if (summon_instead) - which_beastie = MONS_SHADOW_FIEND; - else - which_miscast = SPTYP_NECROMANCY; - break; - default: // this is to silence gcc compiler warnings {dlb} - if (summon_instead) - which_beastie = MONS_FIEND; - else - which_miscast = SPTYP_NECROMANCY; - break; - } - - if (summon_instead) - { - create_monster( which_beastie, 0, BEH_HOSTILE, you.x_pos, - you.y_pos, MHITYOU, 250 ); - } - else - { - miscast_effect( which_miscast, 4 + random2(6), - random2avg(97, 3), 100, "the effects of Hell" ); - } - } - - // NB: no "else" - 8 in 27 odds that nothing happens through - // first chain {dlb} - // also note that the following is distinct from and in - // addition to the above chain: - - // try to summon at least one and up to five random monsters {dlb} - if (one_chance_in(3)) - { - create_monster( RANDOM_MONSTER, 0, BEH_HOSTILE, - you.x_pos, you.y_pos, MHITYOU, 250 ); - - for (i = 0; i < 4; i++) - { - if (one_chance_in(3)) - { - create_monster( RANDOM_MONSTER, 0, BEH_HOSTILE, - you.x_pos, you.y_pos, MHITYOU, 250 ); - } - } - } - } - // END - special Hellish things... - - // Adjust the player's stats if s/he's diseased (or recovering). - if (!you.disease) - { - if (you.strength < you.max_strength && one_chance_in(100)) - { - mpr("You feel your strength returning.", MSGCH_RECOVERY); - you.strength++; - you.redraw_strength = 1; - } - - if (you.dex < you.max_dex && one_chance_in(100)) - { - mpr("You feel your dexterity returning.", MSGCH_RECOVERY); - you.dex++; - you.redraw_dexterity = 1; - } - - if (you.intel < you.max_intel && one_chance_in(100)) - { - mpr("You feel your intelligence returning.", MSGCH_RECOVERY); - you.intel++; - you.redraw_intelligence = 1; - } - } - else - { - if (one_chance_in(30)) - { - mpr("Your disease is taking its toll.", MSGCH_WARN); - lose_stat(STAT_RANDOM, 1); - } - } - - // Adjust the player's stats if s/he has the deterioration mutation - if (you.mutation[MUT_DETERIORATION] - && random2(200) <= you.mutation[MUT_DETERIORATION] * 5 - 2) - { - lose_stat(STAT_RANDOM, 1); - } - - int added_contamination = 0; - - // Account for mutagenic radiation. Invis and haste will give the - // player about .1 points per turn, mutagenic randarts will give - // about 1.5 points on average, so they can corrupt the player - // quite quickly. Wielding one for a short battle is OK, which is - // as things should be. -- GDL - if (you.invis && random2(10) < 6) - added_contamination++; - - if (you.haste && !you.berserker && random2(10) < 6) - added_contamination++; - - // randarts are usually about 20x worse than running around invisible - // or hasted.. this seems OK. - added_contamination += random2(1 + scan_randarts(RAP_MUTAGENIC)); - - // we take off about .5 points per turn - if (!you.invis && !you.haste && coinflip()) - added_contamination -= 1; - - contaminate_player( added_contamination ); - - // only check for badness once every other turn - if (coinflip()) - { - if (you.magic_contamination >= 5 - /* && random2(150) <= you.magic_contamination */) - { - mpr("Your body shudders with the violent release of wild energies!", MSGCH_WARN); - - // for particularly violent releases, make a little boom - if (you.magic_contamination > 25 && one_chance_in(3)) - { - struct bolt boom; - boom.type = SYM_BURST; - boom.colour = BLACK; - boom.flavour = BEAM_RANDOM; - boom.target_x = you.x_pos; - boom.target_y = you.y_pos; - boom.damage = dice_def( 3, (you.magic_contamination / 2) ); - boom.thrower = KILL_MISC; - boom.aux_source = "a magical explosion"; - boom.beam_source = NON_MONSTER; - boom.is_beam = false; - boom.is_tracer = false; - boom.is_explosion = true; - strcpy(boom.beam_name, "magical storm"); - - boom.ench_power = (you.magic_contamination * 5); - boom.ex_size = (you.magic_contamination / 15); - if (boom.ex_size > 9) - boom.ex_size = 9; - - explosion(boom); - } - - // we want to warp the player, not do good stuff! - if (one_chance_in(5)) - mutate(100); - else - give_bad_mutation(coinflip()); - - // we're meaner now, what with explosions and whatnot, but - // we dial down the contamination a little faster if its actually - // mutating you. -- GDL - contaminate_player( -(random2(you.magic_contamination / 4) + 1) ); - } - } - - // Random chance to identify staff in hand based off of Spellcasting - // and an appropriate other spell skill... is 1/20 too fast? - if (you.equip[EQ_WEAPON] != -1 - && you.inv[you.equip[EQ_WEAPON]].base_type == OBJ_STAVES - && !item_ident( you.inv[you.equip[EQ_WEAPON]], ISFLAG_KNOW_TYPE ) - && one_chance_in(20)) - { - int total_skill = you.skills[SK_SPELLCASTING]; - - switch (you.inv[you.equip[EQ_WEAPON]].sub_type) - { - case STAFF_WIZARDRY: - case STAFF_ENERGY: - total_skill += you.skills[SK_SPELLCASTING]; - break; - case STAFF_FIRE: - if (you.skills[SK_FIRE_MAGIC] > you.skills[SK_ICE_MAGIC]) - total_skill += you.skills[SK_FIRE_MAGIC]; - else - total_skill += you.skills[SK_ICE_MAGIC]; - break; - case STAFF_COLD: - if (you.skills[SK_ICE_MAGIC] > you.skills[SK_FIRE_MAGIC]) - total_skill += you.skills[SK_ICE_MAGIC]; - else - total_skill += you.skills[SK_FIRE_MAGIC]; - break; - case STAFF_AIR: - if (you.skills[SK_AIR_MAGIC] > you.skills[SK_EARTH_MAGIC]) - total_skill += you.skills[SK_AIR_MAGIC]; - else - total_skill += you.skills[SK_EARTH_MAGIC]; - break; - case STAFF_EARTH: - if (you.skills[SK_EARTH_MAGIC] > you.skills[SK_AIR_MAGIC]) - total_skill += you.skills[SK_EARTH_MAGIC]; - else - total_skill += you.skills[SK_AIR_MAGIC]; - break; - case STAFF_POISON: - total_skill += you.skills[SK_POISON_MAGIC]; - break; - case STAFF_DEATH: - total_skill += you.skills[SK_NECROMANCY]; - break; - case STAFF_CONJURATION: - total_skill += you.skills[SK_CONJURATIONS]; - break; - case STAFF_ENCHANTMENT: - total_skill += you.skills[SK_ENCHANTMENTS]; - break; - case STAFF_SUMMONING: - total_skill += you.skills[SK_SUMMONINGS]; - break; - } - - if (random2(100) < total_skill) - { - set_ident_flags( you.inv[you.equip[EQ_WEAPON]], ISFLAG_IDENT_MASK ); - - char str_pass[ ITEMNAME_SIZE ]; - in_name(you.equip[EQ_WEAPON], DESC_NOCAP_A, str_pass); - snprintf( info, INFO_SIZE, "You are wielding %s.", str_pass ); - mpr(info); - more(); - - you.wield_change = true; - } - } - - // Check to see if an upset god wants to do something to the player - // jmf: moved huge thing to religion.cc - handle_god_time(); - - // If the player has the lost mutation forget portions of the map - if (you.mutation[MUT_LOST]) - { - if (random2(100) <= you.mutation[MUT_LOST] * 5) - forget_map(5 + random2(you.mutation[MUT_LOST] * 10)); - } - - // Update all of the corpses and food chunks on the floor - update_corpses(time_delta); - - // Update all of the corpses and food chunks in the player's - // inventory {should be moved elsewhere - dlb} - - - for (i = 0; i < ENDOFPACK; i++) - { - if (you.inv[i].quantity < 1) - continue; - - if (you.inv[i].base_type != OBJ_CORPSES && you.inv[i].base_type != OBJ_FOOD) - continue; - - if (you.inv[i].base_type == OBJ_CORPSES - && you.inv[i].sub_type > CORPSE_SKELETON) - { - continue; - } - - if (you.inv[i].base_type == OBJ_FOOD && you.inv[i].sub_type != FOOD_CHUNK) - continue; - - if ((time_delta / 20) >= you.inv[i].special) - { - if (you.inv[i].base_type == OBJ_FOOD) - { - if (you.equip[EQ_WEAPON] == i) - { - unwield_item(you.equip[EQ_WEAPON]); - you.equip[EQ_WEAPON] = -1; - you.wield_change = true; - } - - mpr( "Your equipment suddenly weighs less.", MSGCH_ROTTEN_MEAT ); - you.inv[i].quantity = 0; - burden_change(); - continue; - } - - if (you.inv[i].sub_type == CORPSE_SKELETON) - continue; // carried skeletons are not destroyed - - if (!mons_skeleton( you.inv[i].plus )) - { - if (you.equip[EQ_WEAPON] == i) - { - unwield_item(you.equip[EQ_WEAPON]); - you.equip[EQ_WEAPON] = -1; - } - - you.inv[i].quantity = 0; - burden_change(); - continue; - } - - you.inv[i].sub_type = 1; - you.inv[i].special = 0; - you.inv[i].colour = LIGHTGREY; - you.wield_change = true; - continue; - } - - you.inv[i].special -= (time_delta / 20); - - if (you.inv[i].special < 100 && (you.inv[i].special + (time_delta / 20)>=100)) - { - new_rotting_item = true; - } - } - - //mv: messages when chunks/corpses become rotten - if (new_rotting_item) - { - switch (you.species) - { - // XXX: should probably still notice? - case SP_MUMMY: // no smell - case SP_TROLL: // stupid, living in mess - doesn't care about it - break; - - case SP_GHOUL: //likes it - temp_rand = random2(8); - mpr( ((temp_rand < 5) ? "You smell something rotten." : - (temp_rand == 5) ? "Smell of rotting flesh makes you more hungry." : - (temp_rand == 6) ? "You smell decay. Yum-yum." - : "Wow! There is something tasty in your inventory."), - MSGCH_ROTTEN_MEAT ); - break; - - case SP_KOBOLD: //mv: IMO these race aren't so "touchy" - case SP_OGRE: - case SP_MINOTAUR: - case SP_HILL_ORC: - temp_rand = random2(8); - mpr( ((temp_rand < 5) ? "You smell something rotten." : - (temp_rand == 5) ? "You smell rotting flesh." : - (temp_rand == 6) ? "You smell decay." - : "There is something rotten in your inventory."), - MSGCH_ROTTEN_MEAT ); - break; - - default: - temp_rand = random2(8); - mpr( ((temp_rand < 5) ? "You smell something rotten." : - (temp_rand == 5) ? "Smell of rotting flesh makes you sick." : - (temp_rand == 6) ? "You smell decay. Yuk..." - : "Ugh! There is something really disgusting in your inventory."), - MSGCH_ROTTEN_MEAT ); - break; - } - } - - // exercise armour *xor* stealth skill: {dlb} - if (!player_light_armour()) - { - if (random2(1000) <= item_mass( you.inv[you.equip[EQ_BODY_ARMOUR]] )) - { - return; - } - - if (one_chance_in(6)) // lowered random roll from 7 to 6 -- bwross - exercise(SK_ARMOUR, 1); - } - else // exercise stealth skill: - { - if (you.burden_state != BS_UNENCUMBERED || you.berserker) - return; - - if (you.special_wield == SPWLD_SHADOW) - return; - - if (you.equip[EQ_BODY_ARMOUR] != -1 - && random2( item_mass( you.inv[you.equip[EQ_BODY_ARMOUR]] )) >= 100) - { - return; - } - - if (one_chance_in(18)) - exercise(SK_STEALTH, 1); - } - - return; -} // end handle_time() - -int autopickup_on = 1; - -static bool is_banned(const item_def &item) { - static char name[ITEMNAME_SIZE]; - item_name(item, DESC_INVENTORY, name, false); - - std::string iname = name; - for (unsigned i = 0; i < Options.banned_objects.size(); ++i) { - if (Options.banned_objects[i].matches(iname)) - return true; - } - return false; -} - -static void autopickup(void) -{ - //David Loewenstern 6/99 - int result, o, next; - bool did_pickup = false; - - if (autopickup_on == 0 || Options.autopickups == 0L) - return; - - if (you.attribute[ATTR_TRANSFORMATION] == TRAN_AIR - && you.duration[DUR_TRANSFORMATION] > 0) - { - return; - } - - if (player_is_levitating() && !wearing_amulet(AMU_CONTROLLED_FLIGHT)) - return; - - o = igrd[you.x_pos][you.y_pos]; - - while (o != NON_ITEM) - { - next = mitm[o].link; - - if ( ((mitm[o].flags & ISFLAG_THROWN) && Options.pickup_thrown) || - ( (Options.autopickups & (1L << mitm[o].base_type) -#ifdef CLUA_BINDINGS - || clua.callbooleanfn(false, "ch_autopickup", "u", &mitm[o]) -#endif - ) - && (Options.pickup_dropped || !(mitm[o].flags & ISFLAG_DROPPED)) - && !is_banned(mitm[o]))) - { - mitm[o].flags &= ~(ISFLAG_THROWN | ISFLAG_DROPPED); - - result = move_item_to_player( o, mitm[o].quantity); - - if (result == 0) - { - mpr("You can't carry any more."); - break; - } - else if (result == -1) - { - mpr("Your pack is full."); - break; - } - - did_pickup = true; - } - - o = next; - } - - // move_item_to_player should leave things linked. -- bwr - // relink_cell(you.x_pos, you.y_pos); - - if (did_pickup) - { - you.turn_is_over = 1; - start_delay( DELAY_AUTOPICKUP, 1 ); - } -} - -int inv_count(void) -{ - int count=0; - - for(int i=0; i< ENDOFPACK; i++) - { - if (is_valid_item( you.inv[i] )) - count += 1; - } - - return count; -} - -#ifdef ALLOW_DESTROY_ITEM_COMMAND -// Started with code from AX-crawl, although its modified to fix some -// serious problems. -- bwr -// -// Issues to watch for here: -// - no destroying things from the ground since that includes corpses -// which might be animated by monsters (butchering takes a few turns). -// This code provides a quicker way to get rid of a corpse, but -// the player has to be able to lift it first... something that was -// a valid preventative method before (although this allow the player -// to get rid of the mass on the next action). -// -// - artefacts can be destroyed -// -// - equipment cannot be destroyed... not only is this the more accurate -// than testing for curse status (to prevent easy removal of cursed items), -// but the original code would leave all the equiped items properties -// (including weight) which would cause a bit of a mess to state. -// -// - no item does anything for just carrying it... if that changes then -// this code will have to deal with that. -// -// - Do we want the player to be able to remove items from the game? -// This would make things considerably easier to keep weapons (esp -// those of distortion) from falling into the hands of monsters. -// Right now the player has to carry them to a safe area, or otherwise -// ingeniously dispose of them... do we care about this gameplay aspect? -// -// - Prompt for number to destroy? -// -void cmd_destroy_item( void ) -{ - int i; - char str_pass[ ITEMNAME_SIZE ]; - - // ask the item to destroy - int item = prompt_invent_item( "Destroy which item? ", -1, true, false ); - if (item == PROMPT_ABORT) - return; - - // Used to check for cursed... but that's not the real problem -- bwr - for (i = 0; i < NUM_EQUIP; i++) - { - if (you.equip[i] == item) - { - mesclr( true ); - mpr( "You cannot destroy equipped items!" ); - return; - } - } - - // ask confirmation - // quant_name(you.inv[item], you.inv[item].quantity, DESC_NOCAP_A, str_pass ); - item_name( you.inv[item], DESC_NOCAP_THE, str_pass ); - snprintf( info, INFO_SIZE, "Destroy %s? ", str_pass ); - - if (yesno( info, true )) - { - //destroy it!! - snprintf( info, INFO_SIZE, "You destroy %s.", str_pass ); - mpr( info ); - dec_inv_item_quantity( item, you.inv[item].quantity ); - burden_change(); - } -} -#endif |