/src/object/obj-util.c
C | 4266 lines | 2227 code | 911 blank | 1128 comment | 660 complexity | 86c9d7f46c718a0443cc78dab51aed02 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- /*
- * File: object2.c
- * Purpose: Object list maintenance and other object utilities
- *
- * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
- *
- * This work is free software; you can redistribute it and/or modify it
- * under the terms of either:
- *
- * a) the GNU General Public License as published by the Free Software
- * Foundation, version 2, or
- *
- * b) the "Angband licence":
- * This software may be copied and distributed for educational, research,
- * and not for profit purposes provided that this copyright and statement
- * are included in all such copies. Other copyrights may also apply.
- */
- #include "angband.h"
- #include "defines.h"
- #include "tvalsval.h"
- #include "effects.h"
- #include "game-cmd.h"
-
- /*
- * Hold the titles of scrolls, 6 to 14 characters each.
- */
- char scroll_adj[MAX_TITLES][16];
-
- static void flavor_assign_fixed(void)
- {
- int i, j;
-
- for (i = 0; i < z_info->flavor_max; i++)
- {
- flavor_type *flavor_ptr = &flavor_info[i];
-
- /* Skip random flavors */
- if (flavor_ptr->sval == SV_UNKNOWN) continue;
-
- for (j = 0; j < z_info->k_max; j++)
- {
- /* Skip other objects */
- if ((k_info[j].tval == flavor_ptr->tval) &&
- (k_info[j].sval == flavor_ptr->sval))
- {
- /* Store the flavor index */
- k_info[j].flavor = i;
- }
- }
- }
- }
-
-
- static void flavor_assign_random(byte tval)
- {
- int i, j;
- int flavor_count = 0;
- int choice;
-
- /* Count the random flavors for the given tval */
- for (i = 0; i < z_info->flavor_max; i++)
- {
- if ((flavor_info[i].tval == tval) &&
- (flavor_info[i].sval == SV_UNKNOWN))
- {
- flavor_count++;
- }
- }
-
- for (i = 0; i < z_info->k_max; i++)
- {
- /* Skip other object types */
- if (k_info[i].tval != tval) continue;
-
- /* Skip objects that already are flavored */
- if (k_info[i].flavor != 0) continue;
-
- /* HACK - Ordinary food is "boring" */
- if ((tval == TV_FOOD) && (k_info[i].sval < SV_FOOD_MIN_SHROOM))
- continue;
-
- if (!flavor_count) quit_fmt("Not enough flavors for tval %d.", tval);
-
- /* Select a flavor */
- choice = randint0(flavor_count);
-
- /* Find and store the flavor */
- for (j = 0; j < z_info->flavor_max; j++)
- {
- /* Skip other tvals */
- if (flavor_info[j].tval != tval) continue;
-
- /* Skip assigned svals */
- if (flavor_info[j].sval != SV_UNKNOWN) continue;
-
- if (choice == 0)
- {
- /* Store the flavor index */
- k_info[i].flavor = j;
-
- /* Mark the flavor as used */
- flavor_info[j].sval = k_info[i].sval;
-
- /* One less flavor to choose from */
- flavor_count--;
-
- break;
- }
-
- choice--;
- }
- }
- }
-
-
- /*
- * Prepare the "variable" part of the "k_info" array.
- *
- * The "color"/"metal"/"type" of an item is its "flavor".
- * For the most part, flavors are assigned randomly each game.
- *
- * Initialize descriptions for the "colored" objects, including:
- * Rings, Amulets, Staffs, Wands, Rods, Food, Potions, Scrolls.
- *
- * The first 4 entries for potions are fixed (Water, Apple Juice,
- * Slime Mold Juice, Unused Potion).
- *
- * Scroll titles are always between 6 and 14 letters long. This is
- * ensured because every title is composed of whole words, where every
- * word is from 2 to 8 letters long, and that no scroll is finished
- * until it attempts to grow beyond 15 letters. The first time this
- * can happen is when the current title has 6 letters and the new word
- * has 8 letters, which would result in a 6 letter scroll title.
- *
- * Hack -- make sure everything stays the same for each saved game
- * This is accomplished by the use of a saved "random seed", as in
- * "town_gen()". Since no other functions are called while the special
- * seed is in effect, so this function is pretty "safe".
- */
- void flavor_init(void)
- {
- int i, j;
-
- /* Hack -- Use the "simple" RNG */
- Rand_quick = TRUE;
-
- /* Hack -- Induce consistant flavors */
- Rand_value = seed_flavor;
-
- flavor_assign_fixed();
-
- flavor_assign_random(TV_RING);
- flavor_assign_random(TV_AMULET);
- flavor_assign_random(TV_STAFF);
- flavor_assign_random(TV_WAND);
- flavor_assign_random(TV_ROD);
- flavor_assign_random(TV_FOOD);
- flavor_assign_random(TV_POTION);
- flavor_assign_random(TV_SCROLL);
-
- /* Scrolls (random titles, always white) */
- for (i = 0; i < MAX_TITLES; i++)
- {
- char buf[24];
- char *end = buf;
- int titlelen = 0;
- int wordlen;
- bool okay = TRUE;
-
- wordlen = randname_make(RANDNAME_SCROLL, 2, 8, end, 24);
- while (titlelen + wordlen < (int)(sizeof(scroll_adj[0]) - 1))
- {
- end[wordlen] = ' ';
- titlelen += wordlen + 1;
- end += wordlen + 1;
- wordlen = randname_make(RANDNAME_SCROLL, 2, 8, end, 24 - titlelen);
- }
- buf[titlelen - 1] = '\0';
-
- /* Check the scroll name hasn't already been generated */
- for (j = 0; j < i; j++)
- {
- if (streq(buf, scroll_adj[j]))
- {
- okay = FALSE;
- break;
- }
- }
-
- if (okay)
- {
- my_strcpy(scroll_adj[i], buf, sizeof(scroll_adj[0]));
- }
- else
- {
- /* Have another go at making a name */
- i--;
- }
- }
-
- /* Hack -- Use the "complex" RNG */
- Rand_quick = FALSE;
-
- /* Analyze every object */
- for (i = 1; i < z_info->k_max; i++)
- {
- object_kind *k_ptr = &k_info[i];
-
- /* Skip "empty" objects */
- if (!k_ptr->name) continue;
-
- /* No flavor yields aware */
- if (!k_ptr->flavor) k_ptr->aware = TRUE;
- }
- }
-
-
-
- #ifdef ALLOW_BORG_GRAPHICS
- extern void init_translate_visuals(void);
- #endif /* ALLOW_BORG_GRAPHICS */
-
-
- /*
- * Reset the "visual" lists
- *
- * This involves resetting various things to their "default" state.
- *
- * If the "prefs" flag is TRUE, then we will also load the appropriate
- * "user pref file" based on the current setting of the "use_graphics"
- * flag. This is useful for switching "graphics" on/off.
- *
- * The features, objects, and monsters, should all be encoded in the
- * relevant "font.pref" and/or "graf.prf" files. XXX XXX XXX
- *
- * The "prefs" parameter is no longer meaningful. XXX XXX XXX
- */
- void reset_visuals(bool unused)
- {
- int i;
-
-
- /* Unused parameter */
- (void)unused;
-
- /* Extract default attr/char code for features */
- for (i = 0; i < z_info->f_max; i++)
- {
- feature_type *f_ptr = &f_info[i];
-
- /* Assume we will use the underlying values */
- f_ptr->x_attr = f_ptr->d_attr;
- f_ptr->x_char = f_ptr->d_char;
- }
-
- /* Extract default attr/char code for objects */
- for (i = 0; i < z_info->k_max; i++)
- {
- object_kind *k_ptr = &k_info[i];
-
- /* Default attr/char */
- k_ptr->x_attr = k_ptr->d_attr;
- k_ptr->x_char = k_ptr->d_char;
- }
-
- /* Extract default attr/char code for monsters */
- for (i = 0; i < z_info->r_max; i++)
- {
- monster_race *r_ptr = &r_info[i];
-
- /* Default attr/char */
- r_ptr->x_attr = r_ptr->d_attr;
- r_ptr->x_char = r_ptr->d_char;
- }
-
- /* Extract default attr/char code for flavors */
- for (i = 0; i < z_info->flavor_max; i++)
- {
- flavor_type *flavor_ptr = &flavor_info[i];
-
- /* Default attr/char */
- flavor_ptr->x_attr = flavor_ptr->d_attr;
- flavor_ptr->x_char = flavor_ptr->d_char;
- }
-
- /* Extract attr/chars for inventory objects (by tval) */
- for (i = 0; i < (int)N_ELEMENTS(tval_to_attr); i++)
- {
- /* Default to white */
- tval_to_attr[i] = TERM_WHITE;
- }
-
-
- /* Graphic symbols */
- if (use_graphics)
- {
- /* Process "graf.prf" */
- process_pref_file("graf.prf");
- }
-
- /* Normal symbols */
- else
- {
- /* Process "font.prf" */
- process_pref_file("font.prf");
- }
-
- #ifdef ALLOW_BORG_GRAPHICS
- /* Initialize the translation table for the borg */
- init_translate_visuals();
- #endif /* ALLOW_BORG_GRAPHICS */
- }
-
-
- /*
- * Obtain the "flags" for an item
- */
- void object_flags(const object_type *o_ptr, bitflag flags[OF_SIZE])
- {
- object_kind *k_ptr = &k_info[o_ptr->k_idx];
-
- of_wipe(flags);
-
- /* Obtain kind flags */
- of_union(flags, k_ptr->flags);
-
- /* Obtain artifact flags */
- if (o_ptr->name1)
- {
- artifact_type *a_ptr = &a_info[o_ptr->name1];
-
- of_union(flags, a_ptr->flags);
- }
-
- /* Obtain ego flags */
- if (o_ptr->name2)
- {
- ego_item_type *e_ptr = &e_info[o_ptr->name2];
-
- of_union(flags, e_ptr->flags);
- }
-
- /* Remove curse flags (use only the object's curse flags) */
- flags_clear(flags, OF_SIZE, OF_CURSE_MASK, FLAG_END);
-
- /* Obtain the object's flags */
- of_union(flags, o_ptr->flags);
- }
-
-
- /*
- * Obtain the "flags" for an item which are known to the player
- */
- void object_flags_known(const object_type *o_ptr, bitflag flags[OF_SIZE])
- {
- object_flags(o_ptr, flags);
-
- of_inter(flags, o_ptr->known_flags);
-
- if (object_flavor_is_aware(o_ptr))
- of_union(flags, k_info[o_ptr->k_idx].flags);
-
- if (o_ptr->name2 && easy_know(o_ptr))
- of_union(flags, e_info[o_ptr->name2].flags);
- }
-
-
- /*
- * Convert an inventory index into a one character label.
- *
- * Note that the label does NOT distinguish inven/equip.
- */
- char index_to_label(int i)
- {
- /* Indexes for "inven" are easy */
- if (i < INVEN_WIELD) return (I2A(i));
-
- /* Indexes for "equip" are offset */
- return (I2A(i - INVEN_WIELD));
- }
-
-
- /*
- * Convert a label into the index of an item in the "inven".
- *
- * Return "-1" if the label does not indicate a real item.
- */
- s16b label_to_inven(int c)
- {
- int i;
-
- /* Convert */
- i = (islower((unsigned char)c) ? A2I(c) : -1);
-
- /* Verify the index */
- if ((i < 0) || (i > INVEN_PACK)) return (-1);
-
- /* Empty slots can never be chosen */
- if (!inventory[i].k_idx) return (-1);
-
- /* Return the index */
- return (i);
- }
-
-
- /*
- * Convert a label into the index of a item in the "equip".
- *
- * Return "-1" if the label does not indicate a real item.
- */
- s16b label_to_equip(int c)
- {
- int i;
-
- /* Convert */
- i = (islower((unsigned char)c) ? A2I(c) : -1) + INVEN_WIELD;
-
- /* Verify the index */
- if ((i < INVEN_WIELD) || (i >= ALL_INVEN_TOTAL)) return (-1);
- if (i == INVEN_TOTAL) return (-1);
-
- /* Empty slots can never be chosen */
- if (!inventory[i].k_idx) return (-1);
-
- /* Return the index */
- return (i);
- }
-
-
- /*
- * Hack -- determine if an item is "wearable" (or a missile)
- */
- bool wearable_p(const object_type *o_ptr)
- {
- /* Valid "tval" codes */
- switch (o_ptr->tval)
- {
- case TV_SHOT:
- case TV_ARROW:
- case TV_BOLT:
- case TV_BOW:
- case TV_DIGGING:
- case TV_HAFTED:
- case TV_POLEARM:
- case TV_SWORD:
- case TV_BOOTS:
- case TV_GLOVES:
- case TV_HELM:
- case TV_CROWN:
- case TV_SHIELD:
- case TV_CLOAK:
- case TV_SOFT_ARMOR:
- case TV_HARD_ARMOR:
- case TV_DRAG_ARMOR:
- case TV_LIGHT:
- case TV_AMULET:
- case TV_RING: return (TRUE);
- }
-
- /* Nope */
- return (FALSE);
- }
-
- int get_inscribed_ammo_slot(const object_type *o_ptr)
- {
- char *s;
- if (!o_ptr->note) return 0;
- s = strchr(quark_str(o_ptr->note), 'f');
- if (!s || s[1] < '0' || s[1] > '9') return 0;
-
- return QUIVER_START + (s[1] - '0');
- }
-
- /**
- * Used by wield_slot() to find an appopriate slot for ammo. See wield_slot()
- * for information on what this returns.
- */
- s16b wield_slot_ammo(const object_type *o_ptr)
- {
- s16b i, open = 0;
-
- /* If the ammo is inscribed with a slot number, we'll try to put it in */
- /* that slot, if possible. */
- i = get_inscribed_ammo_slot(o_ptr);
- if (i && !inventory[i].k_idx) return i;
-
- for (i = QUIVER_START; i < QUIVER_END; i++)
- {
- if (!inventory[i].k_idx)
- {
- /* Save the open slot if we haven't found one already */
- if (!open) open = i;
- continue;
- }
-
- /* If ammo is cursed we can't stack it */
- if (cursed_p(&inventory[i])) continue;
-
- /* If they are stackable, we'll use this slot for sure */
- if (object_similar(&inventory[i], o_ptr)) return i;
- }
-
- /* If not absorbed, return an open slot (or QUIVER_START if no room) */
- return open ? open : QUIVER_START;
- }
-
- /**
- * Determine which equipment slot (if any) an item likes. The slot might (or
- * might not) be open, but it is a slot which the object could be equipped in.
- *
- * For items where multiple slots could work (e.g. ammo or rings), the function
- * will try to a return a stackable slot first (only for ammo), then an open
- * slot if possible, and finally a used (but valid) slot if necessary.
- */
- s16b wield_slot(const object_type *o_ptr)
- {
- /* Slot for equipment */
- switch (o_ptr->tval)
- {
- case TV_DIGGING:
- case TV_HAFTED:
- case TV_POLEARM:
- case TV_SWORD: return (INVEN_WIELD);
-
- case TV_BOW: return (INVEN_BOW);
-
- case TV_RING:
- return inventory[INVEN_RIGHT].k_idx ? INVEN_LEFT : INVEN_RIGHT;
-
- case TV_AMULET: return (INVEN_NECK);
-
- case TV_LIGHT: return (INVEN_LIGHT);
-
- case TV_DRAG_ARMOR:
- case TV_HARD_ARMOR:
- case TV_SOFT_ARMOR: return (INVEN_BODY);
-
- case TV_CLOAK: return (INVEN_OUTER);
-
- case TV_SHIELD: return (INVEN_ARM);
-
- case TV_CROWN:
- case TV_HELM: return (INVEN_HEAD);
-
- case TV_GLOVES: return (INVEN_HANDS);
-
- case TV_BOOTS: return (INVEN_FEET);
-
- case TV_BOLT:
- case TV_ARROW:
- case TV_SHOT: return wield_slot_ammo(o_ptr);
- }
-
- /* No slot available */
- return (-1);
- }
-
-
- /*
- * \returns whether item o_ptr will fit in slot 'slot'
- */
- bool slot_can_wield_item(int slot, const object_type *o_ptr)
- {
- if (o_ptr->tval == TV_RING)
- return (slot == INVEN_LEFT || slot == INVEN_RIGHT) ? TRUE : FALSE;
- else if (obj_is_ammo(o_ptr))
- return (slot >= QUIVER_START && slot < QUIVER_END) ? TRUE : FALSE;
- else
- return (wield_slot(o_ptr) == slot) ? TRUE : FALSE;
- }
-
-
- /*
- * Return a string mentioning how a given item is carried
- */
- const char *mention_use(int slot)
- {
- switch (slot)
- {
- case INVEN_WIELD:
- {
- if (adj_str_hold[p_ptr->state.stat_ind[A_STR]] < inventory[slot].weight / 10)
- return "Just lifting";
- else
- return "Wielding";
- }
-
- case INVEN_BOW:
- {
- if (adj_str_hold[p_ptr->state.stat_ind[A_STR]] < inventory[slot].weight / 10)
- return "Just holding";
- else
- return "Shooting";
- }
-
- case INVEN_LEFT: return "On left hand";
- case INVEN_RIGHT: return "On right hand";
- case INVEN_NECK: return "Around neck";
- case INVEN_LIGHT: return "Light source";
- case INVEN_BODY: return "On body";
- case INVEN_OUTER: return "About body";
- case INVEN_ARM: return "On arm";
- case INVEN_HEAD: return "On head";
- case INVEN_HANDS: return "On hands";
- case INVEN_FEET: return "On feet";
-
- case QUIVER_START + 0: return "In quiver [f0]";
- case QUIVER_START + 1: return "In quiver [f1]";
- case QUIVER_START + 2: return "In quiver [f2]";
- case QUIVER_START + 3: return "In quiver [f3]";
- case QUIVER_START + 4: return "In quiver [f4]";
- case QUIVER_START + 5: return "In quiver [f5]";
- case QUIVER_START + 6: return "In quiver [f6]";
- case QUIVER_START + 7: return "In quiver [f7]";
- case QUIVER_START + 8: return "In quiver [f8]";
- case QUIVER_START + 9: return "In quiver [f9]";
- }
-
- /*if (slot >= QUIVER_START && slot < QUIVER_END)
- return "In quiver";*/
-
- return "In pack";
- }
-
-
- /*
- * Return a string describing how a given item is being worn.
- * Currently, only used for items in the equipment, not inventory.
- */
- cptr describe_use(int i)
- {
- cptr p;
-
- switch (i)
- {
- case INVEN_WIELD: p = "attacking monsters with"; break;
- case INVEN_BOW: p = "shooting missiles with"; break;
- case INVEN_LEFT: p = "wearing on your left hand"; break;
- case INVEN_RIGHT: p = "wearing on your right hand"; break;
- case INVEN_NECK: p = "wearing around your neck"; break;
- case INVEN_LIGHT: p = "using to light the way"; break;
- case INVEN_BODY: p = "wearing on your body"; break;
- case INVEN_OUTER: p = "wearing on your back"; break;
- case INVEN_ARM: p = "wearing on your arm"; break;
- case INVEN_HEAD: p = "wearing on your head"; break;
- case INVEN_HANDS: p = "wearing on your hands"; break;
- case INVEN_FEET: p = "wearing on your feet"; break;
- default: p = "carrying in your pack"; break;
- }
-
- /* Hack -- Heavy weapon */
- if (i == INVEN_WIELD)
- {
- object_type *o_ptr;
- o_ptr = &inventory[i];
- if (adj_str_hold[p_ptr->state.stat_ind[A_STR]] < o_ptr->weight / 10)
- {
- p = "just lifting";
- }
- }
-
- /* Hack -- Heavy bow */
- if (i == INVEN_BOW)
- {
- object_type *o_ptr;
- o_ptr = &inventory[i];
- if (adj_str_hold[p_ptr->state.stat_ind[A_STR]] < o_ptr->weight / 10)
- {
- p = "just holding";
- }
- }
-
- /* Return the result */
- return p;
- }
-
-
-
-
-
- /*
- * Check an item against the item tester info
- */
- bool item_tester_okay(const object_type *o_ptr)
- {
- /* Hack -- allow listing empty slots */
- if (item_tester_full) return (TRUE);
-
- /* Require an item */
- if (!o_ptr->k_idx) return (FALSE);
-
- /* Hack -- ignore "gold" */
- if (o_ptr->tval == TV_GOLD) return (FALSE);
-
- /* Check the tval */
- if (item_tester_tval)
- {
- if (item_tester_tval != o_ptr->tval) return (FALSE);
- }
-
- /* Check the hook */
- if (item_tester_hook)
- {
- if (!(*item_tester_hook)(o_ptr)) return (FALSE);
- }
-
- /* Assume okay */
- return (TRUE);
- }
-
-
-
- /*
- * Get the indexes of objects at a given floor location. -TNB-
- *
- * Return the number of object indexes acquired.
- *
- * Valid flags are any combination of the bits:
- * 0x01 -- Verify item tester
- * 0x02 -- Marked/visible items only
- * 0x04 -- Only the top item
- */
- int scan_floor(int *items, int max_size, int y, int x, int mode)
- {
- int this_o_idx, next_o_idx;
-
- int num = 0;
-
- /* Sanity */
- if (!in_bounds(y, x)) return 0;
-
- /* Scan all objects in the grid */
- for (this_o_idx = cave_o_idx[y][x]; this_o_idx; this_o_idx = next_o_idx)
- {
- object_type *o_ptr;
-
- /* XXX Hack -- Enforce limit */
- if (num >= max_size) break;
-
-
- /* Get the object */
- o_ptr = &o_list[this_o_idx];
-
- /* Get the next object */
- next_o_idx = o_ptr->next_o_idx;
-
- /* Item tester */
- if ((mode & 0x01) && !item_tester_okay(o_ptr)) continue;
-
- /* Marked */
- if ((mode & 0x02) && (!o_ptr->marked || squelch_hide_item(o_ptr)))
- continue;
-
- /* Accept this item */
- items[num++] = this_o_idx;
-
- /* Only one */
- if (mode & 0x04) break;
- }
-
- return num;
- }
-
-
-
-
- /*
- * Excise a dungeon object from any stacks
- */
- void excise_object_idx(int o_idx)
- {
- object_type *j_ptr;
-
- s16b this_o_idx, next_o_idx = 0;
-
- s16b prev_o_idx = 0;
-
-
- /* Object */
- j_ptr = &o_list[o_idx];
-
- /* Monster */
- if (j_ptr->held_m_idx)
- {
- monster_type *m_ptr;
-
- /* Monster */
- m_ptr = &mon_list[j_ptr->held_m_idx];
-
- /* Scan all objects in the grid */
- for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
- {
- object_type *o_ptr;
-
- /* Get the object */
- o_ptr = &o_list[this_o_idx];
-
- /* Get the next object */
- next_o_idx = o_ptr->next_o_idx;
-
- /* Done */
- if (this_o_idx == o_idx)
- {
- /* No previous */
- if (prev_o_idx == 0)
- {
- /* Remove from list */
- m_ptr->hold_o_idx = next_o_idx;
- }
-
- /* Real previous */
- else
- {
- object_type *i_ptr;
-
- /* Previous object */
- i_ptr = &o_list[prev_o_idx];
-
- /* Remove from list */
- i_ptr->next_o_idx = next_o_idx;
- }
-
- /* Forget next pointer */
- o_ptr->next_o_idx = 0;
-
- /* Done */
- break;
- }
-
- /* Save prev_o_idx */
- prev_o_idx = this_o_idx;
- }
- }
-
- /* Dungeon */
- else
- {
- int y = j_ptr->iy;
- int x = j_ptr->ix;
-
- /* Scan all objects in the grid */
- for (this_o_idx = cave_o_idx[y][x]; this_o_idx; this_o_idx = next_o_idx)
- {
- object_type *o_ptr;
-
- /* Get the object */
- o_ptr = &o_list[this_o_idx];
-
- /* Get the next object */
- next_o_idx = o_ptr->next_o_idx;
-
- /* Done */
- if (this_o_idx == o_idx)
- {
- /* No previous */
- if (prev_o_idx == 0)
- {
- /* Remove from list */
- cave_o_idx[y][x] = next_o_idx;
- }
-
- /* Real previous */
- else
- {
- object_type *i_ptr;
-
- /* Previous object */
- i_ptr = &o_list[prev_o_idx];
-
- /* Remove from list */
- i_ptr->next_o_idx = next_o_idx;
- }
-
- /* Forget next pointer */
- o_ptr->next_o_idx = 0;
-
- /* Done */
- break;
- }
-
- /* Save prev_o_idx */
- prev_o_idx = this_o_idx;
- }
- }
- }
-
-
- /*
- * Delete a dungeon object
- *
- * Handle "stacks" of objects correctly.
- */
- void delete_object_idx(int o_idx)
- {
- object_type *j_ptr;
-
- /* Excise */
- excise_object_idx(o_idx);
-
- /* Object */
- j_ptr = &o_list[o_idx];
-
- /* Dungeon floor */
- if (!(j_ptr->held_m_idx))
- {
- int y, x;
-
- /* Location */
- y = j_ptr->iy;
- x = j_ptr->ix;
-
- /* Visual update */
- light_spot(y, x);
- }
-
- /* Wipe the object */
- object_wipe(j_ptr);
-
- /* Count objects */
- o_cnt--;
- }
-
-
- /*
- * Deletes all objects at given location
- */
- void delete_object(int y, int x)
- {
- s16b this_o_idx, next_o_idx = 0;
-
-
- /* Paranoia */
- if (!in_bounds(y, x)) return;
-
-
- /* Scan all objects in the grid */
- for (this_o_idx = cave_o_idx[y][x]; this_o_idx; this_o_idx = next_o_idx)
- {
- object_type *o_ptr;
-
- /* Get the object */
- o_ptr = &o_list[this_o_idx];
-
- /* Get the next object */
- next_o_idx = o_ptr->next_o_idx;
-
- /* Wipe the object */
- object_wipe(o_ptr);
-
- /* Count objects */
- o_cnt--;
- }
-
- /* Objects are gone */
- cave_o_idx[y][x] = 0;
-
- /* Visual update */
- light_spot(y, x);
- }
-
-
-
- /*
- * Move an object from index i1 to index i2 in the object list
- */
- static void compact_objects_aux(int i1, int i2)
- {
- int i;
-
- object_type *o_ptr;
-
-
- /* Do nothing */
- if (i1 == i2) return;
-
-
- /* Repair objects */
- for (i = 1; i < o_max; i++)
- {
- /* Get the object */
- o_ptr = &o_list[i];
-
- /* Skip "dead" objects */
- if (!o_ptr->k_idx) continue;
-
- /* Repair "next" pointers */
- if (o_ptr->next_o_idx == i1)
- {
- /* Repair */
- o_ptr->next_o_idx = i2;
- }
- }
-
-
- /* Get the object */
- o_ptr = &o_list[i1];
-
-
- /* Monster */
- if (o_ptr->held_m_idx)
- {
- monster_type *m_ptr;
-
- /* Get the monster */
- m_ptr = &mon_list[o_ptr->held_m_idx];
-
- /* Repair monster */
- if (m_ptr->hold_o_idx == i1)
- {
- /* Repair */
- m_ptr->hold_o_idx = i2;
- }
- }
-
- /* Dungeon */
- else
- {
- int y, x;
-
- /* Get location */
- y = o_ptr->iy;
- x = o_ptr->ix;
-
- /* Repair grid */
- if (cave_o_idx[y][x] == i1)
- {
- /* Repair */
- cave_o_idx[y][x] = i2;
- }
- }
-
-
- /* Hack -- move object */
- COPY(&o_list[i2], &o_list[i1], object_type);
-
- /* Hack -- wipe hole */
- object_wipe(o_ptr);
- }
-
-
- /*
- * Compact and reorder the object list
- *
- * This function can be very dangerous, use with caution!
- *
- * When compacting objects, we first destroy gold, on the basis that by the
- * time item compaction becomes an issue, the player really won't care.
- * We also nuke items marked as squelch.
- *
- * When compacting other objects, we base the saving throw on a combination of
- * object level, distance from player, and current "desperation".
- *
- * After compacting, we "reorder" the objects into a more compact order, and we
- * reset the allocation info, and the "live" array.
- */
- void compact_objects(int size)
- {
- int py = p_ptr->py;
- int px = p_ptr->px;
-
- int i, y, x, cnt;
-
- int cur_lev, cur_dis, chance;
-
-
- /* Reorder objects when not passed a size */
- if (!size)
- {
- /* Excise dead objects (backwards!) */
- for (i = o_max - 1; i >= 1; i--)
- {
- object_type *o_ptr = &o_list[i];
-
- /* Skip real objects */
- if (o_ptr->k_idx) continue;
-
- /* Move last object into open hole */
- compact_objects_aux(o_max - 1, i);
-
- /* Compress "o_max" */
- o_max--;
- }
-
- return;
- }
-
-
- /* Message */
- msg_print("Compacting objects...");
-
- /*** Try destroying objects ***/
-
- /* First do gold */
- for (i = 1; (i < o_max) && (size); i++)
- {
- object_type *o_ptr = &o_list[i];
-
- /* Nuke gold or squelched items */
- if (o_ptr->tval == TV_GOLD || squelch_item_ok(o_ptr))
- {
- delete_object_idx(i);
- size--;
- }
- }
-
-
- /* Compact at least 'size' objects */
- for (cnt = 1; size; cnt++)
- {
- /* Get more vicious each iteration */
- cur_lev = 5 * cnt;
-
- /* Get closer each iteration */
- cur_dis = 5 * (20 - cnt);
-
- /* Examine the objects */
- for (i = 1; (i < o_max) && (size); i++)
- {
- object_type *o_ptr = &o_list[i];
- object_kind *k_ptr = &k_info[o_ptr->k_idx];
-
- /* Skip dead objects */
- if (!o_ptr->k_idx) continue;
-
- /* Hack -- High level objects start out "immune" */
- if (k_ptr->level > cur_lev && !k_ptr->squelch)
- continue;
-
- /* Monster */
- if (o_ptr->held_m_idx)
- {
- monster_type *m_ptr;
-
- /* Get the monster */
- m_ptr = &mon_list[o_ptr->held_m_idx];
-
- /* Get the location */
- y = m_ptr->fy;
- x = m_ptr->fx;
-
- /* Monsters protect their objects */
- if ((randint0(100) < 90) && !k_ptr->squelch)
- continue;
- }
-
- /* Dungeon */
- else
- {
- /* Get the location */
- y = o_ptr->iy;
- x = o_ptr->ix;
- }
-
- /* Nearby objects start out "immune" */
- if ((cur_dis > 0) && (distance(py, px, y, x) < cur_dis) && !k_ptr->squelch)
- continue;
-
- /* Saving throw */
- chance = 90;
-
-
- /* Hack -- only compact artifacts in emergencies */
- if (artifact_p(o_ptr) && (cnt < 1000)) chance = 100;
-
- /* Apply the saving throw */
- if (randint0(100) < chance) continue;
-
- /* Delete the object */
- delete_object_idx(i);
- size--;
- }
- }
-
-
- /* Reorder objects */
- compact_objects(0);
- }
-
- /*
- * Mention artifact preservation for peeking wizards
- */
- static void mention_preserve(const object_type *o_ptr)
- {
- char o_name[80];
-
- /* Describe */
- object_desc(o_name, sizeof(o_name), o_ptr, ODESC_BASE | ODESC_SPOIL);
-
- msg_format("Preserving (%s)", o_name);
- }
-
- /*
- * Delete all the items when player leaves the level
- *
- * Note -- we do NOT visually reflect these (irrelevant) changes
- *
- * Hack -- we clear the "cave_o_idx[y][x]" field for every grid,
- * and the "m_ptr->next_o_idx" field for every monster, since
- * we know we are clearing every object. Technically, we only
- * clear those fields for grids/monsters containing objects,
- * and we clear it once for every such object.
- */
- void wipe_o_list(void)
- {
- int i;
-
- /* Delete the existing objects */
- for (i = 1; i < o_max; i++)
- {
- object_type *o_ptr = &o_list[i];
- artifact_type *a_ptr = artifact_of(o_ptr);
-
- /* Skip dead objects */
- if (!o_ptr->k_idx) continue;
-
- /* Preserve artifacts or mark them as lost in the history */
- if (a_ptr) {
- /* Preserve if dungeon creation failed, or preserve mode, and only artifacts not seen */
- if ((!character_dungeon || !OPT(adult_no_preserve)) && !object_was_sensed(o_ptr))
- {
- a_ptr->created = FALSE;
-
- /* Cheat -- Mention preserving */
- if (OPT(cheat_peek)) mention_preserve(o_ptr);
- }
- else
- {
- /* Mark artifact as lost in logs */
- history_lose_artifact(o_ptr->name1);
- }
- }
-
- /* Monster */
- if (o_ptr->held_m_idx)
- {
- monster_type *m_ptr;
-
- /* Monster */
- m_ptr = &mon_list[o_ptr->held_m_idx];
-
- /* Hack -- see above */
- m_ptr->hold_o_idx = 0;
- }
-
- /* Dungeon */
- else
- {
- /* Get the location */
- int y = o_ptr->iy;
- int x = o_ptr->ix;
-
- /* Hack -- see above */
- cave_o_idx[y][x] = 0;
- }
-
- /* Wipe the object */
- (void)WIPE(o_ptr, object_type);
- }
-
- /* Reset "o_max" */
- o_max = 1;
-
- /* Reset "o_cnt" */
- o_cnt = 0;
- }
-
-
- /*
- * Get and return the index of a "free" object.
- *
- * This routine should almost never fail, but in case it does,
- * we must be sure to handle "failure" of this routine.
- */
- s16b o_pop(void)
- {
- int i;
-
-
- /* Initial allocation */
- if (o_max < z_info->o_max)
- {
- /* Get next space */
- i = o_max;
-
- /* Expand object array */
- o_max++;
-
- /* Count objects */
- o_cnt++;
-
- /* Use this object */
- return (i);
- }
-
-
- /* Recycle dead objects */
- for (i = 1; i < o_max; i++)
- {
- object_type *o_ptr;
-
- /* Get the object */
- o_ptr = &o_list[i];
-
- /* Skip live objects */
- if (o_ptr->k_idx) continue;
-
- /* Count objects */
- o_cnt++;
-
- /* Use this object */
- return (i);
- }
-
-
- /* Warn the player (except during dungeon creation) */
- if (character_dungeon) msg_print("Too many objects!");
-
- /* Oops */
- return (0);
- }
-
-
- /*
- * Get the first object at a dungeon location
- * or NULL if there isn't one.
- */
- object_type *get_first_object(int y, int x)
- {
- s16b o_idx = cave_o_idx[y][x];
-
- if (o_idx) return (&o_list[o_idx]);
-
- /* No object */
- return (NULL);
- }
-
-
- /*
- * Get the next object in a stack or NULL if there isn't one.
- */
- object_type *get_next_object(const object_type *o_ptr)
- {
- if (o_ptr->next_o_idx) return (&o_list[o_ptr->next_o_idx]);
-
- /* No more objects */
- return (NULL);
- }
-
-
-
- /*
- * Determine if a weapon is 'blessed'
- */
- bool is_blessed(const object_type *o_ptr)
- {
- bitflag f[OF_SIZE];
-
- /* Get the flags */
- object_flags(o_ptr, f);
-
- /* Is the object blessed? */
- return (of_has(f, OF_BLESSED) ? TRUE : FALSE);
- }
-
-
-
- /*
- * Return the "value" of an "unknown" item
- * Make a guess at the value of non-aware items
- */
- static s32b object_value_base(const object_type *o_ptr)
- {
- object_kind *k_ptr = &k_info[o_ptr->k_idx];
-
- /* Use template cost for aware objects */
- if (object_flavor_is_aware(o_ptr) || o_ptr->ident & IDENT_STORE)
- return k_ptr->cost;
-
- /* Analyze the type */
- switch (o_ptr->tval)
- {
- case TV_FOOD:
- return 5;
- case TV_POTION:
- case TV_SCROLL:
- return 20;
- case TV_RING:
- case TV_AMULET:
- return 45;
- case TV_WAND:
- return 50;
- case TV_STAFF:
- return 70;
- case TV_ROD:
- return 90;
- }
-
- return 0;
- }
-
-
- /*
- * Return the "real" price of a "known" item, not including discounts.
- *
- * Wand and staffs get cost for each charge.
- *
- * Wearable items (weapons, launchers, jewelry, lights, armour) and ammo
- * are priced according to their power rating. All ammo, and normal (non-ego)
- * torches are scaled down by AMMO_RESCALER to reflect their impermanence.
- */
- static s32b object_value_real(const object_type *o_ptr, int qty, int verbose,
- bool known)
- {
- s32b value, total_value;
-
- object_kind *k_ptr = &k_info[o_ptr->k_idx];
-
- s32b power;
- int a = 2;
- int b = 1;
- static file_mode pricing_mode = MODE_WRITE;
-
- if (wearable_p(o_ptr))
- {
- char buf[1024];
- ang_file *log_file = NULL;
-
- if (verbose)
- {
- path_build(buf, sizeof(buf), ANGBAND_DIR_USER, "pricing.log");
- log_file = file_open(buf, pricing_mode, FTYPE_TEXT);
- if (!log_file)
- {
- msg_print("Error - can't open pricing.log for writing.");
- exit(1);
- }
- pricing_mode = MODE_APPEND;
- }
-
- LOG_PRINT1("object is %s", k_name + k_ptr->name);
- power = object_power(o_ptr, verbose, log_file, known);
- value = sign(power) * ((a * power * power) + (b * power));
-
- if ( (o_ptr->tval == TV_SHOT) || (o_ptr->tval == TV_ARROW) ||
- (o_ptr->tval == TV_BOLT) || ((o_ptr->tval == TV_LIGHT)
- && (o_ptr->sval == SV_LIGHT_TORCH) && !o_ptr->name2) )
- {
- value = value / AMMO_RESCALER;
- if (value < 1) value = 1;
- }
-
- LOG_PRINT2("a is %d and b is %d\n", a, b);
- LOG_PRINT1("value is %d\n", value);
- total_value = value * qty;
-
- if (verbose)
- {
- if (!file_close(log_file))
- {
- msg_print("Error - can't close pricing.log file.");
- exit(1);
- }
- }
- if (total_value < 0) total_value = 0;
-
- return (total_value);
- }
-
- /* Hack -- "worthless" items */
- if (!k_ptr->cost) return (0L);
-
- /* Base cost */
- value = k_ptr->cost;
-
- /* Analyze the item type and quantity*/
- switch (o_ptr->tval)
- {
- /* Wands/Staffs */
- case TV_WAND:
- case TV_STAFF:
- {
- int charges;
-
- total_value = value * qty;
-
- /* Calculate number of charges, rounded up */
- charges = o_ptr->pval * qty / o_ptr->number;
- if ((o_ptr->pval * qty) % o_ptr->number != 0) charges++;
-
- /* Pay extra for charges, depending on standard number of charges */
- total_value += value * charges / 20;
-
- /* Done */
- break;
- }
-
- default:
- {
- total_value = value * qty;
- break;
- }
- }
-
- /* No negative value */
- if (total_value < 0) total_value = 0;
-
- /* Return the value */
- return (total_value);
- }
-
-
- /*
- * Return the price of an item including plusses (and charges).
- *
- * This function returns the "value" of the given item (qty one).
- *
- * Never notice "unknown" bonuses or properties, including "curses",
- * since that would give the player information he did not have.
- *
- * Note that discounted items stay discounted forever.
- */
- s32b object_value(const object_type *o_ptr, int qty, int verbose)
- {
- s32b value;
-
-
- if (object_is_known(o_ptr))
- {
- if (cursed_p(o_ptr)) return (0L);
-
- value = object_value_real(o_ptr, qty, verbose, TRUE);
- }
- else if (wearable_p(o_ptr))
- {
- object_type object_type_body;
- object_type *j_ptr = &object_type_body;
-
- /* Hack -- Felt cursed items */
- if (object_was_sensed(o_ptr) && cursed_p(o_ptr)) return (0L);
-
- memcpy(j_ptr, o_ptr, sizeof(object_type));
-
- /* give j_ptr only the flags known to be in o_ptr */
- object_flags_known(o_ptr, j_ptr->flags);
-
- if (!object_attack_plusses_are_visible(o_ptr))
- j_ptr->to_h = j_ptr->to_d = 0;
- if (!object_defence_plusses_are_visible(o_ptr))
- j_ptr->to_a = 0;
-
- value = object_value_real(j_ptr, qty, verbose, FALSE);
- }
- else value = object_value_base(o_ptr) * qty;
-
-
- /* Return the final value */
- return (value);
- }
-
-
- /*
- * Determine if an item can "absorb" a second item
- *
- * See "object_absorb()" for the actual "absorption" code.
- *
- * If permitted, we allow weapons/armor to stack, if "known".
- *
- * Missiles will combine if both stacks have the same "known" status.
- * This is done to make unidentified stacks of missiles useful.
- *
- * Food, potions, scrolls, and "easy know" items always stack.
- *
- * Chests, and activatable items, except rods, never stack (for various
- * reasons).
- */
- bool object_similar(const object_type *o_ptr, const object_type *j_ptr)
- {
- int total = o_ptr->number + j_ptr->number;
-
-
- /* Require identical object types */
- if (o_ptr->k_idx != j_ptr->k_idx) return (0);
-
-
- /* Analyze the items */
- switch (o_ptr->tval)
- {
- /* Chests */
- case TV_CHEST:
- {
- /* Never okay */
- return (0);
- }
-
- /* Gold */
- case TV_GOLD:
- {
- /* Too much gold for one object_type */
- if (o_ptr->pval + j_ptr->pval > MAX_PVAL) return 0;
- }
-
- /* Food and Potions and Scrolls */
- case TV_FOOD:
- case TV_POTION:
- case TV_SCROLL:
- {
- /* Assume okay */
- break;
- }
-
- /* Staves and Wands */
- case TV_STAFF:
- case TV_WAND:
- {
- /* Too many charges for one object_type */
- if (o_ptr->pval + j_ptr->pval > MAX_PVAL) return 0;
- }
-
- /* Rods */
- case TV_ROD:
- {
- /* Assume okay */
- break;
- }
-
- /* Weaponsm, armour and jewelery */
- case TV_BOW:
- case TV_DIGGING:
- case TV_HAFTED:
- case TV_POLEARM:
- case TV_SWORD:
- case TV_BOOTS:
- case TV_GLOVES:
- case TV_HELM:
- case TV_CROWN:
- case TV_SHIELD:
- case TV_CLOAK:
- case TV_SOFT_ARMOR:
- case TV_HARD_ARMOR:
- case TV_DRAG_ARMOR:
- case TV_RING:
- case TV_AMULET:
- case TV_LIGHT:
- {
- /* Fall through */
- }
-
- /* Missiles */
- case TV_BOLT:
- case TV_ARROW:
- case TV_SHOT:
- {
- /* Require identical "bonuses" */
- if (o_ptr->to_h != j_ptr->to_h) return (FALSE);
- if (o_ptr->to_d != j_ptr->to_d) return (FALSE);
- if (o_ptr->to_a != j_ptr->to_a) return (FALSE);
-
- /* Require identical "pval" code */
- if (o_ptr->pval != j_ptr->pval) return (FALSE);
-
- /* Require identical "artifact" names */
- if (o_ptr->name1 != j_ptr->name1) return (FALSE);
-
- /* Require identical "ego-item" names */
- if (o_ptr->name2 != j_ptr->name2) return (FALSE);
-
- /* Hack - Never stack recharging items */
- if ((o_ptr->timeout || j_ptr->timeout) && o_ptr->tval != TV_LIGHT)
- return FALSE;
-
- /* Lights must have same amount of fuel */
- else if (o_ptr->timeout != j_ptr->timeout && o_ptr->tval == TV_LIGHT)
- return FALSE;
-
- /* Require identical "values" */
- if (o_ptr->ac != j_ptr->ac) return (FALSE);
- if (o_ptr->dd != j_ptr->dd) return (FALSE);
- if (o_ptr->ds != j_ptr->ds) return (FALSE);
-
- /* Probably okay */
- break;
- }
-
- /* Various */
- default:
- {
- /* Probably okay */
- break;
- }
- }
-
-
- /* Hack -- Require compatible inscriptions */
- if (o_ptr->note != j_ptr->note)
- {
- /* Never combine different inscriptions */
- if (o_ptr->note && j_ptr->note) return (0);
- }
-
-
- /* Different flags */
- if (!of_is_equal(o_ptr->flags, j_ptr->flags))
- return FALSE;
-
-
- /* Maximal "stacking" limit */
- if (total >= MAX_STACK_SIZE) return (0);
-
-
- /* They match, so they must be similar */
- return (TRUE);
- }
-
-
- /*
- * Allow one item to "absorb" another, assuming they are similar.
- *
- * The blending of the "note" field assumes that either (1) one has an
- * inscription and the other does not, or (2) neither has an inscription.
- * In both these cases, we can simply use the existing note, unless the
- * blending object has a note, in which case we use that note.
- *
- * The blending of the "discount" field assumes that either (1) one is a
- * special inscription and one is nothing, or (2) one is a discount and
- * one is a smaller discount, or (3) one is a discount and one is nothing,
- * or (4) both are nothing. In all of these cases, we can simply use the
- * "maximum" of the two "discount" fields.
- *
- * These assumptions are enforced by the "object_similar()" code.
- */
- void object_absorb(object_type *o_ptr, const object_type *j_ptr)
- {
- int total = o_ptr->number + j_ptr->number;
-
- /* Add together the item counts */
- o_ptr->number = ((total < MAX_STACK_SIZE) ? total : (MAX_STACK_SIZE - 1));
-
- /* Blend all knowledge */
- o_ptr->ident |= (j_ptr->ident & ~IDENT_EMPTY);
- of_union(o_ptr->known_flags, j_ptr->known_flags);
-
- /* Hack -- Blend "notes" */
- if (j_ptr->note != 0) o_ptr->note = j_ptr->note;
-
- /*
- * Hack -- if rods are stacking, re-calculate the
- * pvals (maximum timeouts) and current timeouts together
- */
- if (o_ptr->tval == TV_ROD)
- {
- o_ptr->pval = total * j_ptr->pval;
- o_ptr->timeout += j_ptr->timeout;
- }
-
- /* Hack -- if wands or staves are stacking, combine the charges */
- /* If gold is stacking combine the amount */
- if (o_ptr->tval == TV_WAND || o_ptr->tval == TV_STAFF ||
- o_ptr->tval == TV_GOLD)
- {
- int total = o_ptr->pval + j_ptr->pval;
- o_ptr->pval = total >= MAX_PVAL ? MAX_PVAL : total;
- }
-
- if ((o_ptr->origin != j_ptr->origin) ||
- (o_ptr->origin_depth != j_ptr->origin_depth) ||
- (o_ptr->origin_xtra != j_ptr->origin_xtra))
- {
- int act = 2;
-
- if ((o_ptr->origin == ORIGIN_DROP) && (o_ptr->origin == j_ptr->origin))
- {
- monster_race *r_ptr = &r_info[o_ptr->origin_xtra];
- monster_race *s_ptr = &r_info[j_ptr->origin_xtra];
-
- bool r_uniq = rf_has(r_ptr->flags, RF_UNIQUE) ? TRUE : FALSE;
- bool s_uniq = rf_has(s_ptr->flags, RF_UNIQUE) ? TRUE : FALSE;
-
- if (r_uniq && !s_uniq) act = 0;
- else if (s_uniq && !r_uniq) act = 1;
- else act = 2;
- }
-
- switch (act)
- {
- /* Overwrite with j_ptr */
- case 1:
- {
- o_ptr->origin = j_ptr->origin;
- o_ptr->origin_depth = j_ptr->origin_depth;
- o_ptr->origin_xtra = j_ptr->origin_xtra;
- }
-
- /* Set as "mixed" */
- case 2:
- {
- o_ptr->origin = ORIGIN_MIXED;
- }
- }
- }
- }
-
-
-
- /*
- * Wipe an object clean.
- */
- void object_wipe(object_type *o_ptr)
- {
- /* Wipe the structure */
- (void)WIPE(o_ptr, object_type);
- }
-
-
- /*
- * Prepare an object based on an existing object
- */
- void object_copy(object_type *o_ptr, const object_type *j_ptr)
- {
- /* Copy the structure */
- COPY(o_ptr, j_ptr, object_type);
- }
-
- /*
- * Prepare an object `dst` representing `amt` objects, based on an existing
- * object `src` representing at least `amt` objects.
- *
- * Takes care of the charge redistribution concerns of stacked items.
- */
- void object_copy_amt(object_type *dst, object_type *src, int amt)
- {
- const object_kind *k_ptr = &k_info[src->k_idx];
- int charge_time = randcalc(k_ptr->time, 0, AVERAGE), max_time;
-
- /* Get a copy of the object */
- object_copy(dst, src);
-
- /* Modify quantity */
- dst->number = amt;
-
- /*
- * If the item has charges/timeouts, set them to the correct level
- * too. We split off the same amount as distribute_charges.
- */
- if (src->tval == TV_WAND || src->tval == TV_STAFF)
- {
- dst->pval = src->pval * amt / src->number;
- }
-
- if (src->tval == TV_ROD)
- {
- max_time = charge_time * amt;
-
- if (src->timeout > max_time)
- dst->timeout = max_time;
- else
- dst->timeout = src->timeout;
- }
- }
-
-
- /**
- * Find and return the index to the oldest object on the given grid marked as
- * "squelch".
- */
- static s16b floor_get_idx_oldest_squelched(int y, int x)
- {
- s16b squelch_idx = 0;
- s16b this_o_idx;
-
- object_type *o_ptr = NULL;
-
- for (this_o_idx = cave_o_idx[y][x]; this_o_idx; this_o_idx = o_ptr->next_o_idx)
- {
- o_ptr = &o_list[this_o_idx];
-
- if (squelch_hide_item(o_ptr))
- squelch_idx = this_o_idx;
- }
-
- return squelch_idx;
- }
-
-
-
- /*
- * Let the floor carry an object, deleting old squelched items if necessary
- */
- s16b floor_carry(int y, int x, object_type *j_ptr)
- {
- int n = 0;
-
- s16b o_idx;
-
- s16b this_o_idx, next_o_idx = 0;
-
-
- /* Scan objects in that grid for combination */
- for (this_o_idx = cave_o_idx[y][x]; this_o_idx; this_o_idx = next_o_idx)
- {
- object_type *o_ptr = &o_list[this_o_idx];
-
- /* Get the next object */
- next_o_idx = o_ptr->next_o_idx;
-
- /* Check for combination */
- if (object_similar(o_ptr, j_ptr))
- {
- /* Combine the items */
- object_absorb(o_ptr, j_ptr);
-
- /* Result */
- return (this_o_idx);
- }
-
- /* Count objects */
- n++;
- }
-
- /* Option -- disallow stacking */
- if (OPT(adult_no_stacking) && n) return (0);
-
- /* The stack is already too large */
- if (n >= MAX_FLOOR_STACK)
- {
- /* Squelch the oldest squelched object */
- s16b squelch_idx = floor_get_idx_oldest_squelched(y, x);
-
- if (squelch_idx)
- delete_object_idx(squelch_idx);
- else
- return 0;
- }
-
-
- /* Make an object */
- o_idx = o_pop();
-
- /* Success */
- if (o_idx)
- {
- object_type *o_ptr;
-
- /* Get the object */
- o_ptr = &o_list[o_idx];
-
- /* Structure Copy */
- object_copy(o_ptr, j_ptr);
-
- /* Location */
- o_ptr->iy = y;
- o_ptr->ix = x;
-
- /* Forget monster */
- o_ptr->held_m_idx = 0;
-
- /* Link the object to the pile */
- o_ptr->next_o_idx = cave_o_idx[y][x];
-
- /* Link the floor to the object */
- cave_o_idx[y][x] = o_idx;
-
- /* Notice */
- note_spot(y, x);
-
- /* Redraw */
- light_spot(y, x);
- }
-
- /* Result */
- return (o_idx);
- }
-
-
- /*
- * Let an object fall to the ground at or near a location.
- *
- * The initial location is assumed to be "in_bounds_fully()".
- *
- * This function takes a parameter "chance". This is the percentage
- * chance that the item will "disappear" instead of drop. If the object
- * has been thrown, then this is the chance of disappearance on contact.
- *
- * This function will produce a description of a drop event under the player
- * when "verbose" is true.
- *
- * We check several locations to see if we can find a location at which
- * the object can combine, stack, or be placed. Artifacts will try very
- * hard to be placed, including "teleporting" to a useful grid if needed.
- */
- void drop_near(object_type *j_ptr, int chance, int y, int x, bool verbose)
- {
- int i, k, n, d, s;
-
- int bs, bn;
- int by, bx;
- int dy, dx;
- int ty, tx;
-
- object_type *o_ptr;
-
- char o_name[80];
-
- bool flag = FALSE;
-
- bool plural = FALSE;
-
-
- /* Extract plural */
- if (j_ptr->number != 1) plural = TRUE;
-
- /* Describe object */
- object_desc(o_name, sizeof(o_name), j_ptr, ODESC_BASE);
-
-
- /* Handle normal "breakage" */
- if (!artifact_p(j_ptr) && (randint0(100) < chance))
- {
- /* Message */
- msg_format("The %s break%s.", o_name, PLURAL(plural));
-
- /* Failure */
- return;
- }
-
-
- /* Score */
- bs = -1;
-
- /* Picker */
- bn = 0;
-
- /* Default */
- by = y;
- bx = x;
-
- /* Scan local grids */
- for (dy = -3; dy <= 3; dy++)
- {
- /* Scan local grids */
- for (dx = -3; dx <= 3; dx++)
- {
- bool comb = FALSE;
-
- /* Calculate actual distance */
- d = (dy * dy) + (dx * dx);
-
- /* Ignore distant grids */
- if (d > 10) continue;
-
- /* Location */
- ty = y + dy;
- tx = x + dx;
-
- /* Skip illegal grids */
- if (!in_bounds_fully(ty, tx)) continue;
-
- /* Require line of sight */
- if (!los(y, x, ty, tx)) continue;
-
- /* Require floor space */
- if (cave_feat[ty][tx] != FEAT_FLOOR) continue;
-
- /* No objects */
- k = 0;
- n = 0;
-
- /* Scan objects in that grid */
- for (o_ptr = get_first_object(ty, tx); o_ptr;
- o_ptr = get_next_object(o_ptr))
- {
- /* Check for possible combination */
- if (object_similar(o_ptr, j_ptr)) comb = TRUE;
-
- /* Count objects */
- if (!squelch_hide_item(o_ptr))
- k++;
- else
- n++;
- }
-
- /* Add new object */
- if (!comb) k++;
-
- /* Option -- disallow stacking */
- if (OPT(adult_no_stacking) && (k > 1)) continue;
-
- /* Paranoia? */
- if ((k + n) > MAX_FLOOR_STACK &&
- !floor_get_idx_oldest_squelched(ty, tx)) continue;
-
- /* Calculate score */
- s = 1000 - (d + k * 5);
-
- /* Skip bad values */
- if (s < bs) continue;
-
- /* New best value */
- if (s > bs) bn = 0;
-
- /* Apply the randomizer to equivalent values */
- if ((++bn >= 2) && (randint0(bn) != 0)) continue;
-
- /* Keep score */
- bs = s;
-
- /* Track it */
- by = ty;
- bx = tx;
-
- /* Okay */
- flag = TRUE;
- }
- }
-
-
- /* Handle lack of space */
- if (!flag && !artifact_p(j_ptr))
- {
- /* Message */
- msg_format("The %s disappear%s.", o_name, PLURAL(plural));
-
- /* Debug */
- if (p_ptr->wizard) msg_print("Breakage (no floor space).");
-
- /* Failure */
- return;
- }
-
-
- /* Find a grid */
- for (i = 0; !flag; i++)
- {
- /* Bounce around */
- if (i < 1000)
- {
- ty = rand_spread(by, 1);
- tx = rand_spread(bx, 1);
- }
-
- /* Random locations */
- else
- {
- ty = randint0(level_hgt);
- tx = randint0(level_wid);
- }
-
- /* Require floor space */
- if (cave_feat[ty][tx] != FEAT_FLOOR) continue;
-
- /* Bounce to that location */
- by = ty;
- bx = tx;
-
- /* Require floor space */
- if (!cave_clean_bold(by, bx)) continue;
-
- /* Okay */
- flag = TRUE;
- }
-
-
- /* Give it to the floor */
- if (!floor_carry(by, bx, j_ptr))
- {
- artifact_type *a_ptr = artifact_of(j_ptr);
-
- /* Message */
- msg_format("The %s disappear%s.", o_name, PLURAL(plural));
-
- /* Debug */
- if (p_ptr->wizard) msg_print("Breakage (too many objects).");
-
- if (a_ptr) a_ptr->created = FALSE;
-
- /* Failure */
- return;
- }
-
-
- /* Sound */
- sound(MSG_DROP);
-
- /* Message when an object falls under the player */
- if (verbose && (cave_m_idx[by][bx] < 0) && !squelch_item_ok(j_ptr))
- {
- msg_print("You feel something roll beneath your feet.");
- }
- }
-
-
- /*
- * Scatter some "great" objects near the player
- */
- void acquirement(int y1, int x1, int level, int num, bool great)
- {
- object_type *i_ptr;
- object_type object_type_body;
-
- /* Acquirement */
- while (num--)
- {
- /* Get local object */
- i_ptr = &object_type_body;
-
- /* Wipe the object */
- object_wipe(i_ptr);
-
- /* Make a good (or great) object (if possible) */
- if (!make_object(i_ptr, level, TRUE, great)) continue;
- i_ptr->origin = ORIGIN_ACQUIRE;
- i_ptr->origin_depth = p_ptr->depth;
-
- /* Drop the object */
- drop_near(i_ptr, 0, y1, x1, TRUE);
- }
- }
-
-
- /*
- * Describe the charges on an item in the inventory.
- */
- void inven_item_charges(int item)
- {
- object_type *o_ptr = &inventory[item];
-
- /* Require staff/wand */
- if ((o_ptr->tval != TV_STAFF) && (o_ptr->tval != TV_WAND)) return;
-
- /* Require known item */
- if (!object_is_known(o_ptr)) return;
-
- /* Print a message */
- msg_format("You have %d charge%s remaining.", o_ptr->pval,
- (o_ptr->pval != 1) ? "s" : "");
- }
-
-
- /*
- * Describe an item in the inventory.
- */
- void inven_item_describe(int item)
- {
- object_type *o_ptr = &inventory[item];
-
- char o_name[80];
-
- if (artifact_p(o_ptr) && object_is_known(o_ptr))
- {
- /* Get a description */
- object_desc(o_name, sizeof(o_name), o_ptr, ODESC_FULL);
-
- /* Print a message */
- msg_format("You no longer have the %s (%c).", o_name, index_to_label(item));
- }
- else
- {
- /* Get a description */
- object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL);
-
- /* Print a message */
- msg_format("You have %s (%c).", o_name, index_to_label(item));
- }
- }
-
-
- /*
- * Increase the "number" of an item in the inventory
- */
- void inven_item_increase(int item, int num)
- {
- object_type *o_ptr = &inventory[item];
-
- /* Apply */
- num += o_ptr->number;
-
- /* Bounds check */
- if (num > 255) num = 255;
- else if (num < 0) num = 0;
-
- /* Un-apply */
- num -= o_ptr->number;
-
- /* Change the number and weight */
- if (num)
- {
- /* Add the number */
- o_ptr->number += num;
-
- /* Add the weight */
- p_ptr->total_weight += (num * o_ptr->weight);
-
- /* Recalculate bonuses */
- p_ptr->update |= (PU_BONUS);
-
- /* Recalculate mana XXX */
- p_ptr->update |= (PU_MANA);
-
- /* Combine the pack */
- p_ptr->notice |= (PN_COMBINE);
-
- /* Redraw stuff */
- p_ptr->redraw |= (PR_INVEN | PR_EQUIP);
- }
- }
-
-
- /**
- * Save the size of the quiver.
- */
- void save_quiver_size(void)
- {
- int i, count …
Large files files are truncated, but you can click here to view the full file