/** * Handles the "death" of a monster. * * Disperses treasures carried by the monster centered at the monster location. * Note that objects dropped may disappear in crowded rooms. * * Checks for "Quest" completion when a quest monster is killed. * * Note that only the player can induce "monster_death()" on Uniques. * Thus (for now) all Quest monsters should be Uniques. * * If `stats` is true, then we skip updating the monster memory. This is * used by stats-generation code, for efficiency. */ void monster_death(struct monster *mon, bool stats) { int dump_item = 0; int dump_gold = 0; struct object *obj = mon->held_obj; bool visible = (mflag_has(mon->mflag, MFLAG_VISIBLE) || rf_has(mon->race->flags, RF_UNIQUE)); /* Delete any mimicked objects */ if (mon->mimicked_obj) object_delete(&mon->mimicked_obj); /* Drop objects being carried */ while (obj) { struct object *next = obj->next; /* Object no longer held */ obj->held_m_idx = 0; pile_excise(&mon->held_obj, obj); /* Count it and drop it - refactor once origin is a bitflag */ if (!stats) { if (tval_is_money(obj) && (obj->origin != ORIGIN_STOLEN)) dump_gold++; else if (!tval_is_money(obj) && ((obj->origin == ORIGIN_DROP) || (obj->origin == ORIGIN_DROP_PIT) || (obj->origin == ORIGIN_DROP_VAULT) || (obj->origin == ORIGIN_DROP_SUMMON) || (obj->origin == ORIGIN_DROP_SPECIAL) || (obj->origin == ORIGIN_DROP_BREED) || (obj->origin == ORIGIN_DROP_POLY) || (obj->origin == ORIGIN_DROP_WIZARD))) dump_item++; } /* Change origin if monster is invisible, unless we're in stats mode */ if (!visible && !stats) obj->origin = ORIGIN_DROP_UNKNOWN; drop_near(cave, obj, 0, mon->fy, mon->fx, true); obj = next; } /* Forget objects */ mon->held_obj = NULL; /* Take note of any dropped treasure */ if (visible && (dump_item || dump_gold)) lore_treasure(mon, dump_item, dump_gold); /* Update monster list window */ player->upkeep->redraw |= PR_MONLIST; /* Check if we finished a quest */ quest_check(mon); }
/** * Sense the existence of objects on a grid in the current level */ void floor_pile_sense(struct chunk *c, int y, int x) { struct object *obj; if (c != cave) return; /* Sense every item on this grid */ for (obj = square_object(c, y, x); obj; obj = obj->next) { struct object *known_obj = cave_k->objects[obj->oidx]; /* Make new sensed objects where necessary */ if (known_obj == NULL) { /* Make and list the new object */ struct object *new_obj = object_new(); cave_k->objects[obj->oidx] = new_obj; new_obj->oidx = obj->oidx; obj->known = new_obj; new_obj->number = 1; /* Give it a fake kind */ if (tval_is_money(obj)) new_obj->kind = unknown_gold_kind; else new_obj->kind = unknown_item_kind; /* Attach it to the current floor pile */ new_obj->iy = y; new_obj->ix = x; pile_insert_end(&cave_k->squares[y][x].obj, new_obj); } } }
/** * Describes item `obj` into buffer `buf` of size `max`. * * ODESC_PREFIX prepends a 'the', 'a' or number * ODESC_BASE results in a base description. * ODESC_COMBAT will add to-hit, to-dam and AC info. * ODESC_EXTRA will add pval/charge/inscription/ignore info. * ODESC_PLURAL will pluralise regardless of the number in the stack. * ODESC_STORE turns off ignore markers, for in-store display. * ODESC_SPOIL treats the object as fully identified. * * Setting 'prefix' to TRUE prepends a 'the', 'a' or the number in the stack, * respectively. * * \returns The number of bytes used of the buffer. */ size_t object_desc(char *buf, size_t max, const struct object *obj, int mode) { bool prefix = mode & ODESC_PREFIX ? TRUE : FALSE; bool spoil = mode & ODESC_SPOIL ? TRUE : FALSE; bool terse = mode & ODESC_TERSE ? TRUE : FALSE; size_t end = 0; /* Simple description for null item */ if (!obj) return strnfmt(buf, max, "(nothing)"); /* Egos whose name we know are seen */ if (object_name_is_visible(obj) && obj->ego && !spoil) obj->ego->everseen = TRUE; /*** Some things get really simple descriptions ***/ if (obj->marked == MARK_AWARE) { if (prefix) return strnfmt(buf, max, "an unknown item"); return strnfmt(buf, max, "unknown item"); } if (tval_is_money(obj)) return strnfmt(buf, max, "%d gold pieces worth of %s%s", obj->pval, obj->kind->name, ignore_item_ok(obj) ? " {ignore}" : ""); /** Construct the name **/ /* Copy the base name to the buffer */ end = obj_desc_name(buf, max, end, obj, prefix, mode, spoil, terse); if (mode & ODESC_COMBAT) { if (tval_is_chest(obj)) end = obj_desc_chest(obj, buf, max, end); else if (tval_is_light(obj)) end = obj_desc_light(obj, buf, max, end); end = obj_desc_combat(obj, buf, max, end, spoil); } if (mode & ODESC_EXTRA) { end = obj_desc_mods(obj, buf, max, end, spoil); end = obj_desc_charges(obj, buf, max, end, mode); if (mode & ODESC_STORE) end = obj_desc_aware(obj, buf, max, end); else end = obj_desc_inscrip(obj, buf, max, end); } return end; }
/** * 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. * * These assumptions are enforced by the "object_similar()" code. */ static void object_absorb_merge(struct object *obj1, const struct object *obj2) { int total; /* Blend all knowledge */ of_union(obj1->known_flags, obj2->known_flags); of_union(obj1->id_flags, obj2->id_flags); /* Merge inscriptions */ if (obj2->note) obj1->note = obj2->note; /* Combine timeouts for rod stacking */ if (tval_can_have_timeout(obj1)) obj1->timeout += obj2->timeout; /* Combine pvals for wands and staves */ if (tval_can_have_charges(obj1) || tval_is_money(obj1)) { total = obj1->pval + obj2->pval; obj1->pval = total >= MAX_PVAL ? MAX_PVAL : total; } /* Combine origin data as best we can */ if (obj1->origin != obj2->origin || obj1->origin_depth != obj2->origin_depth || obj1->origin_xtra != obj2->origin_xtra) { int act = 2; if (obj1->origin_xtra && obj2->origin_xtra) { monster_race *r_ptr = &r_info[obj1->origin_xtra]; monster_race *s_ptr = &r_info[obj2->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 obj2 */ case 1: { obj1->origin = obj2->origin; obj1->origin_depth = obj2->origin_depth; obj1->origin_xtra = obj2->origin_xtra; } /* Set as "mixed" */ case 2: { obj1->origin = ORIGIN_MIXED; } } } }
/** * Build the object list. */ static void build_obj_list(int last, struct object **list, item_tester tester, olist_detail_t mode) { int i; bool gold_ok = (mode & OLIST_GOLD) ? true : false; bool in_term = (mode & OLIST_WINDOW) ? true : false; bool dead = (mode & OLIST_DEATH) ? true : false; bool show_empty = (mode & OLIST_SEMPTY) ? true : false; bool equip = list ? false : true; bool quiver = list == player->upkeep->quiver ? true : false; /* Build the object list */ for (i = 0; i <= last; i++) { char buf[80]; struct object *obj = equip ? slot_object(player, i) : list[i]; /* Acceptable items get a label */ if (object_test(tester, obj) || (obj && tval_is_money(obj) && gold_ok)) strnfmt(items[num_obj].label, sizeof(items[num_obj].label), "%c) ", quiver ? I2D(i) : I2A(i)); /* Unacceptable items are still sometimes shown */ else if ((!obj && show_empty) || in_term) my_strcpy(items[num_obj].label, " ", sizeof(items[num_obj].label)); /* Unacceptable items are skipped in the main window */ else continue; /* Show full slot labels for equipment (or quiver in subwindow) */ if (equip) { strnfmt(buf, sizeof(buf), "%-14s: ", equip_mention(player, i)); my_strcpy(items[num_obj].equip_label, buf, sizeof(items[num_obj].equip_label)); } else if ((in_term || dead) && quiver) { strnfmt(buf, sizeof(buf), "Slot %-9d: ", i); my_strcpy(items[num_obj].equip_label, buf, sizeof(items[num_obj].equip_label)); } else { strnfmt(items[num_obj].equip_label, sizeof(items[num_obj].equip_label), ""); } /* Save the object */ items[num_obj].object = obj; items[num_obj].key = (items[num_obj].label)[0]; num_obj++; } }
/** * Return true if the object should be omitted from the object list. */ static bool object_list_should_ignore_object(const struct object *obj) { struct object *base_obj = cave->objects[obj->oidx]; assert(obj->kind); assert(base_obj); if (!is_unknown(base_obj) && ignore_known_item_ok(obj)) return true; if (tval_is_money(base_obj)) return true; return false; }
/** * Combine items in the pack, confirming no blank objects or gold */ void combine_pack(void) { struct object *obj1, *obj2, *prev; bool display_message = false; /* Combine the pack (backwards) */ obj1 = gear_last_item(); while (obj1) { assert(obj1->kind); assert(!tval_is_money(obj1)); prev = obj1->prev; /* Scan the items above that item */ for (obj2 = player->gear; obj2 && obj2 != obj1; obj2 = obj2->next) { assert(obj2->kind); /* Can we drop "obj1" onto "obj2"? */ if (object_similar(obj2, obj1, OSTACK_PACK)) { display_message = true; object_absorb(obj2->known, obj1->known); obj1->known = NULL; object_absorb(obj2, obj1); break; } else if (inven_can_stack_partial(obj2, obj1, OSTACK_PACK)) { /* Setting this to true spams the combine message. */ display_message = false; object_absorb_partial(obj2->known, obj1->known); object_absorb_partial(obj2, obj1); break; } } obj1 = prev; } calc_inventory(player->upkeep, player->gear, player->body); /* Redraw gear */ event_signal(EVENT_INVENTORY); event_signal(EVENT_EQUIPMENT); /* Message */ if (display_message) { msg("You combine some items in your pack."); /* Stop "repeat last command" from working. */ cmd_disable_repeat(); } }
/** * Combine items in the pack, confirming no blank objects or gold */ void combine_pack(void) { struct object *obj1, *obj2; bool display_message = FALSE; bool redraw = FALSE; /* Combine the pack (backwards) */ for (obj1 = gear_last_item(); obj1; obj1 = obj1->prev) { assert(obj1->kind); assert(!tval_is_money(obj1)); /* Scan the items above that item */ for (obj2 = player->gear; obj2 && obj2 != obj1; obj2 = obj2->next) { assert(obj2->kind); /* Can we drop "obj1" onto "obj2"? */ if (object_similar(obj2, obj1, OSTACK_PACK)) { display_message = TRUE; redraw = TRUE; object_absorb(obj2, obj1); } else if (inven_can_stack_partial(obj2, obj1, OSTACK_PACK)) { /* Setting this to TRUE spams the combine message. */ display_message = FALSE; redraw = TRUE; object_absorb_partial(obj2, obj1); break; } } } calc_inventory(player->upkeep, player->gear, player->body); /* Redraw stuff */ if (redraw) { player->upkeep->redraw |= (PR_INVEN | PR_EQUIP); player->upkeep->update |= (PU_INVEN); } /* Message */ if (display_message) { msg("You combine some items in your pack."); /* Stop "repeat last command" from working. */ cmd_disable_repeat(); } }
/** * Grab all objects from the grid. */ void process_monster_grab_objects(struct chunk *c, struct monster *mon, const char *m_name, int nx, int ny) { struct monster_lore *lore = get_lore(mon->race); struct object *obj; bool visible = mflag_has(mon->mflag, MFLAG_VISIBLE); /* Learn about item pickup behavior */ for (obj = square_object(c, ny, nx); obj; obj = obj->next) { if (!tval_is_money(obj) && visible) { rf_on(lore->flags, RF_TAKE_ITEM); rf_on(lore->flags, RF_KILL_ITEM); break; } } /* Abort if can't pickup/kill */ if (!rf_has(mon->race->flags, RF_TAKE_ITEM) && !rf_has(mon->race->flags, RF_KILL_ITEM)) { return; } /* Take or kill objects on the floor */ obj = square_object(c, ny, nx); while (obj) { char o_name[80]; bool safe = obj->artifact ? true : false; struct object *next = obj->next; /* Skip gold */ if (tval_is_money(obj)) { obj = next; continue; } /* Skip mimicked objects */ if (obj->mimicking_m_idx) { obj = next; continue; } /* Get the object name */ object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); /* React to objects that hurt the monster */ if (react_to_slay(obj, mon)) safe = true; /* Try to pick up, or crush */ if (safe) { /* Only give a message for "take_item" */ if (rf_has(mon->race->flags, RF_TAKE_ITEM) && visible && square_isview(c, ny, nx) && !ignore_item_ok(obj)) { /* Dump a message */ msg("%s tries to pick up %s, but fails.", m_name, o_name); } } else if (rf_has(mon->race->flags, RF_TAKE_ITEM)) { /* Describe observable situations */ if (square_isseen(c, ny, nx) && !ignore_item_ok(obj)) msg("%s picks up %s.", m_name, o_name); /* Carry the object */ square_excise_object(c, ny, nx, obj); monster_carry(c, mon, obj); square_note_spot(c, ny, nx); square_light_spot(c, ny, nx); } else { /* Describe observable situations */ if (square_isseen(c, ny, nx) && !ignore_item_ok(obj)) msgt(MSG_DESTROY, "%s crushes %s.", m_name, o_name); /* Delete the object */ square_excise_object(c, ny, nx, obj); delist_object(c, obj); object_delete(&obj); square_note_spot(c, ny, nx); square_light_spot(c, ny, nx); } /* Next object */ obj = next; } }
/** * This function takes a grid location (x, y) and extracts information the * player is allowed to know about it, filling in the grid_data structure * passed in 'g'. * * The information filled in is as follows: * - g->f_idx is filled in with the terrain's feature type, or FEAT_NONE * if the player doesn't know anything about the grid. The function * makes use of the "mimic" field in terrain in order to allow one * feature to look like another (hiding secret doors, invisible traps, * etc). This will return the terrain type the player "Knows" about, * not necessarily the real terrain. * - g->m_idx is set to the monster index, or 0 if there is none (or the * player doesn't know it). * - g->first_kind is set to the object_kind of the first object in a grid * that the player knows about, or NULL for no objects. * - g->muliple_objects is TRUE if there is more than one object in the * grid that the player knows and cares about (to facilitate any special * floor stack symbol that might be used). * - g->in_view is TRUE if the player can currently see the grid - this can * be used to indicate field-of-view, such as through the OPT(view_bright_light) * option. * - g->lighting is set to indicate the lighting level for the grid: * LIGHTING_DARK for unlit grids, LIGHTING_LIT for inherently light * grids (lit rooms, etc), LIGHTING_TORCH for grids lit by the player's * light source, and LIGHTING_LOS for grids in the player's line of sight. * Note that lighting is always LIGHTING_LIT for known "interesting" grids * like walls. * - g->is_player is TRUE if the player is on the given grid. * - g->hallucinate is TRUE if the player is hallucinating something "strange" * for this grid - this should pick a random monster to show if the m_idx * is non-zero, and a random object if first_kind is non-zero. * * NOTES: * This is called pretty frequently, whenever a grid on the map display * needs updating, so don't overcomplicate it. * * Terrain is remembered separately from objects and monsters, so can be * shown even when the player can't "see" it. This leads to things like * doors out of the player's view still change from closed to open and so on. * * TODO: * Hallucination is currently disabled (it was a display-level hack before, * and we need it to be a knowledge-level hack). The idea is that objects * may turn into different objects, monsters into different monsters, and * terrain may be objects, monsters, or stay the same. */ void map_info(unsigned y, unsigned x, grid_data *g) { object_type *obj; assert(x < (unsigned) cave->width); assert(y < (unsigned) cave->height); /* Default "clear" values, others will be set later where appropriate. */ g->first_kind = NULL; g->trap = NULL; g->multiple_objects = FALSE; g->lighting = LIGHTING_DARK; g->unseen_object = FALSE; g->unseen_money = FALSE; /* Use real feature (remove later) */ g->f_idx = cave->squares[y][x].feat; if (f_info[g->f_idx].mimic) g->f_idx = f_info[g->f_idx].mimic; g->in_view = (square_isseen(cave, y, x)) ? TRUE : FALSE; g->is_player = (cave->squares[y][x].mon < 0) ? TRUE : FALSE; g->m_idx = (g->is_player) ? 0 : cave->squares[y][x].mon; g->hallucinate = player->timed[TMD_IMAGE] ? TRUE : FALSE; g->trapborder = (square_isdedge(cave, y, x)) ? TRUE : FALSE; if (g->in_view) { g->lighting = LIGHTING_LOS; if (!square_isglow(cave, y, x) && OPT(view_yellow_light)) g->lighting = LIGHTING_TORCH; /* Remember seen feature */ cave_k->squares[y][x].feat = cave->squares[y][x].feat; } else if (!square_ismark(cave, y, x)) { g->f_idx = FEAT_NONE; //cave_k->squares[y][x].feat = FEAT_NONE; } else if (square_isglow(cave, y, x)) { g->lighting = LIGHTING_LIT; } /* Use known feature */ /* g->f_idx = cave_k->squares[y][x].feat; if (f_info[g->f_idx].mimic) g->f_idx = f_info[g->f_idx].mimic;*/ /* There is a trap in this square */ if (square_istrap(cave, y, x) && square_ismark(cave, y, x)) { struct trap *trap = cave->squares[y][x].trap; /* Scan the square trap list */ while (trap) { if (trf_has(trap->flags, TRF_TRAP) || trf_has(trap->flags, TRF_RUNE)) { /* Accept the trap */ g->trap = trap; break; } trap = trap->next; } } /* Objects */ for (obj = square_object(cave, y, x); obj; obj = obj->next) { if (obj->marked == MARK_AWARE) { /* Distinguish between unseen money and objects */ if (tval_is_money(obj)) { g->unseen_money = TRUE; } else { g->unseen_object = TRUE; } } else if (obj->marked == MARK_SEEN && !ignore_item_ok(obj)) { if (!g->first_kind) { g->first_kind = obj->kind; } else { g->multiple_objects = TRUE; break; } } } /* Monsters */ if (g->m_idx > 0) { /* If the monster isn't "visible", make sure we don't list it.*/ monster_type *m_ptr = cave_monster(cave, g->m_idx); if (!mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) g->m_idx = 0; } /* Rare random hallucination on non-outer walls */ if (g->hallucinate && g->m_idx == 0 && g->first_kind == 0) { if (one_in_(128) && (int) g->f_idx != FEAT_PERM) g->m_idx = 1; else if (one_in_(128) && (int) g->f_idx != FEAT_PERM) /* if hallucinating, we just need first_kind to not be NULL */ g->first_kind = k_info; else g->hallucinate = FALSE; } assert((int) g->f_idx <= FEAT_PASS_RUBBLE); if (!g->hallucinate) assert((int)g->m_idx < cave->mon_max); /* All other g fields are 'flags', mostly booleans. */ }
/** * Pick up all gold at the player's current location. */ static void player_pickup_gold(void) { s32b total_gold = 0L; char name[30] = ""; struct object *obj = square_object(cave, player->py, player->px), *next; int sound_msg; bool verbal = FALSE; bool at_most_one = TRUE; /* Pick up all the ordinary gold objects */ while (obj) { struct object_kind *kind = NULL; /* Get next object */ next = obj->next; /* Ignore if not legal treasure */ kind = lookup_kind(obj->tval, obj->sval); if (!tval_is_money(obj) || !kind) { obj = next; continue; } /* Multiple types if we have a second name, otherwise record the name */ if (total_gold && !streq(kind->name, name)) at_most_one = FALSE; else my_strcpy(name, kind->name, sizeof(name)); /* Remember whether feedback message is in order */ if (!ignore_item_ok(obj)) verbal = TRUE; /* Increment total value */ total_gold += (s32b)obj->pval; /* Delete the gold */ square_excise_object(cave, player->py, player->px, obj); object_delete(obj); obj = next; } /* Pick up the gold, if present */ if (total_gold) { char buf[100]; /* Build a message */ (void)strnfmt(buf, sizeof(buf), "You have found %ld gold pieces worth of ", (long)total_gold); /* One treasure type.. */ if (at_most_one) my_strcat(buf, name, sizeof(buf)); /* ... or more */ else my_strcat(buf, "treasures", sizeof(buf)); my_strcat(buf, ".", sizeof(buf)); /* Determine which sound to play */ if (total_gold < 200) sound_msg = MSG_MONEY1; else if (total_gold < 600) sound_msg = MSG_MONEY2; else sound_msg = MSG_MONEY3; /* Display the message */ if (verbal) msgt(sound_msg, "%s", buf); /* Add gold to purse */ player->au += total_gold; /* Redraw gold */ player->upkeep->redraw |= (PR_GOLD); } }
/** * 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. * * These assumptions are enforced by the "object_similar()" code. */ static void object_absorb_merge(struct object *obj1, const struct object *obj2) { int total; /* First object gains any extra knowledge from second */ if (obj1->known && obj2->known) { if (obj2->known->effect) obj1->known->effect = obj1->effect; player_know_object(player, obj1); } /* Merge inscriptions */ if (obj2->note) obj1->note = obj2->note; /* Combine timeouts for rod stacking */ if (tval_can_have_timeout(obj1)) obj1->timeout += obj2->timeout; /* Combine pvals for wands and staves */ if (tval_can_have_charges(obj1) || tval_is_money(obj1)) { total = obj1->pval + obj2->pval; obj1->pval = total >= MAX_PVAL ? MAX_PVAL : total; } /* Combine origin data as best we can */ if (obj1->origin != obj2->origin || obj1->origin_depth != obj2->origin_depth || obj1->origin_xtra != obj2->origin_xtra) { int act = 2; if (obj1->origin_xtra && obj2->origin_xtra) { struct monster_race *race1 = &r_info[obj1->origin_xtra]; struct monster_race *race2 = &r_info[obj2->origin_xtra]; bool r1_uniq = rf_has(race1->flags, RF_UNIQUE) ? true : false; bool r2_uniq = rf_has(race2->flags, RF_UNIQUE) ? true : false; if (r1_uniq && !r2_uniq) act = 0; else if (r2_uniq && !r1_uniq) act = 1; else act = 2; } switch (act) { /* Overwrite with obj2 */ case 1: { obj1->origin = obj2->origin; obj1->origin_depth = obj2->origin_depth; obj1->origin_xtra = obj2->origin_xtra; } /* Set as "mixed" */ case 2: { obj1->origin = ORIGIN_MIXED; } } } }
/** * 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_stackable(const struct object *obj1, const struct object *obj2, object_stack_t mode) { int i; /* Equipment items don't stack */ if (object_is_equipped(player->body, obj1)) return false; if (object_is_equipped(player->body, obj2)) return false; /* If either item is unknown, do not stack */ if (mode & OSTACK_LIST && obj1->kind != obj1->known->kind) return false; if (mode & OSTACK_LIST && obj2->kind != obj2->known->kind) return false; /* Hack -- identical items cannot be stacked */ if (obj1 == obj2) return false; /* Require identical object kinds */ if (obj1->kind != obj2->kind) return false; /* Different flags don't stack */ if (!of_is_equal(obj1->flags, obj2->flags)) return false; /* Different elements don't stack */ for (i = 0; i < ELEM_MAX; i++) { if (obj1->el_info[i].res_level != obj2->el_info[i].res_level) return false; if ((obj1->el_info[i].flags & (EL_INFO_HATES | EL_INFO_IGNORE)) != (obj2->el_info[i].flags & (EL_INFO_HATES | EL_INFO_IGNORE))) return false; } /* Artifacts never stack */ if (obj1->artifact || obj2->artifact) return false; /* Analyze the items */ if (tval_is_chest(obj1)) { /* Chests never stack */ return false; } else if (tval_is_edible(obj1) || tval_is_potion(obj1) || tval_is_scroll(obj1) || tval_is_rod(obj1)) { /* Food, potions, scrolls and rods all stack nicely, since the kinds are identical, either both will be aware or both will be unaware */ } else if (tval_can_have_charges(obj1) || tval_is_money(obj1)) { /* Gold, staves and wands stack most of the time */ /* Too much gold or too many charges */ if (obj1->pval + obj2->pval > MAX_PVAL) return false; /* ... otherwise ok */ } else if (tval_is_weapon(obj1) || tval_is_armor(obj1) || tval_is_jewelry(obj1) || tval_is_light(obj1)) { bool obj1_is_known = object_fully_known((struct object *)obj1); bool obj2_is_known = object_fully_known((struct object *)obj2); /* Require identical values */ if (obj1->ac != obj2->ac) return false; if (obj1->dd != obj2->dd) return false; if (obj1->ds != obj2->ds) return false; /* Require identical bonuses */ if (obj1->to_h != obj2->to_h) return false; if (obj1->to_d != obj2->to_d) return false; if (obj1->to_a != obj2->to_a) return false; /* Require all identical modifiers */ for (i = 0; i < OBJ_MOD_MAX; i++) if (obj1->modifiers[i] != obj2->modifiers[i]) return (false); /* Require identical ego-item types */ if (obj1->ego != obj2->ego) return false; /* Require identical curses */ if (!curses_are_equal(obj1->curses, obj2->curses)) return false; /* Hack - Never stack recharging wearables ... */ if ((obj1->timeout || obj2->timeout) && !tval_is_light(obj1)) return false; /* ... and lights must have same amount of fuel */ else if ((obj1->timeout != obj2->timeout) && tval_is_light(obj1)) return false; /* Prevent unIDd items stacking with IDd items in the object list */ if (mode & OSTACK_LIST && (obj1_is_known != obj2_is_known)) return false; } else { /* Anything else probably okay */ } /* Require compatible inscriptions */ if (obj1->note && obj2->note && (obj1->note != obj2->note)) return false; /* They must be similar enough */ return true; }
/** * Grab all objects from the grid. */ void process_monster_grab_objects(struct chunk *c, struct monster *m_ptr, const char *m_name, int nx, int ny) { monster_lore *l_ptr = get_lore(m_ptr->race); struct object *obj = square_object(c, ny, nx); bool is_item = obj ? TRUE : FALSE;; if (is_item && mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) { rf_on(l_ptr->flags, RF_TAKE_ITEM); rf_on(l_ptr->flags, RF_KILL_ITEM); } /* Abort if can't pickup/kill */ if (!rf_has(m_ptr->race->flags, RF_TAKE_ITEM) && !rf_has(m_ptr->race->flags, RF_KILL_ITEM)) { return; } /* Take or kill objects on the floor */ while (obj) { char o_name[80]; bool safe = obj->artifact ? TRUE : FALSE; struct object *next = obj->next; /* Skip gold */ if (tval_is_money(obj)) { obj = next; continue; } /* Get the object name */ object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); /* React to objects that hurt the monster */ if (react_to_slay(obj, m_ptr)) safe = TRUE; /* The object cannot be picked up by the monster */ if (safe) { /* Only give a message for "take_item" */ if (rf_has(m_ptr->race->flags, RF_TAKE_ITEM) && mflag_has(m_ptr->mflag, MFLAG_VISIBLE) && player_has_los_bold(ny, nx) && !ignore_item_ok(obj)) { /* Dump a message */ msg("%s tries to pick up %s, but fails.", m_name, o_name); } /* Pick up the item */ } else if (rf_has(m_ptr->race->flags, RF_TAKE_ITEM)) { /* Describe observable situations */ if (player_has_los_bold(ny, nx) && !ignore_item_ok(obj)) msg("%s picks up %s.", m_name, o_name); /* Carry the object */ square_excise_object(c, ny, nx, obj); monster_carry(c, m_ptr, obj); /* Destroy the item */ } else { /* Describe observable situations */ if (player_has_los_bold(ny, nx) && !ignore_item_ok(obj)) msgt(MSG_DESTROY, "%s crushes %s.", m_name, o_name); /* Delete the object */ square_excise_object(c, ny, nx, obj); object_delete(obj); } /* Next object */ obj = next; } }