/** * Remove an amount of an object from the floor, returning a detached object * which can be used - it is assumed that the object is on the player grid. * * Optionally describe what remains. */ struct object *floor_object_for_use(struct object *obj, int num, bool message, bool *none_left) { struct object *usable; char name[80]; /* Bounds check */ num = MIN(num, obj->number); /* Split off a usable object if necessary */ if (obj->number > num) { usable = object_split(obj, num); } else { usable = obj; square_excise_object(player->cave, usable->iy, usable->ix, usable->known); delist_object(player->cave, usable->known); square_excise_object(cave, usable->iy, usable->ix, usable); delist_object(cave, usable); *none_left = true; /* Stop tracking item */ if (tracked_object_is(player->upkeep, obj)) track_object(player->upkeep, NULL); /* Inventory has changed, so disable repeat command */ cmd_disable_repeat(); } /* Object no longer has a location */ usable->known->iy = 0; usable->known->ix = 0; usable->iy = 0; usable->ix = 0; /* Housekeeping */ player->upkeep->update |= (PU_BONUS | PU_INVEN); player->upkeep->notice |= (PN_COMBINE); player->upkeep->redraw |= (PR_INVEN | PR_EQUIP); /* Print a message if requested and there is anything left */ if (message) { if (usable == obj) obj->number = 0; /* Get a description */ object_desc(name, sizeof(name), obj, ODESC_PREFIX | ODESC_FULL); if (usable == obj) obj->number = num; /* Print a message */ msg("You see %s.", name); } return usable; }
/** * Let the floor carry an object, deleting old ignored items if necessary. * The calling function must deal with the dropped object on failure. * * Optionally put the object at the top or bottom of the pile */ bool floor_carry(struct chunk *c, int y, int x, struct object *drop, bool last) { int n = 0; struct object *obj, *ignore = floor_get_oldest_ignored(y, x); /* Fail if the square can't hold objects */ if (!square_isobjectholding(c, y, x)) return false; /* Scan objects in that grid for combination */ for (obj = square_object(c, y, x); obj; obj = obj->next) { /* Check for combination */ if (object_similar(obj, drop, OSTACK_FLOOR)) { /* Combine the items */ object_absorb(obj, drop); /* Result */ return true; } /* Count objects */ n++; } /* The stack is already too large */ if (n >= z_info->floor_size || (!OPT(player, birth_stacking) && n)) { /* Delete the oldest ignored object */ if (ignore) { square_excise_object(c, y, x, ignore); delist_object(c, ignore); object_delete(&ignore); } else return false; } /* Location */ drop->iy = y; drop->ix = x; /* Forget monster */ drop->held_m_idx = 0; /* Link to the first or last object in the pile */ if (last) pile_insert_end(&c->squares[y][x].obj, drop); else pile_insert(&c->squares[y][x].obj, drop); /* Record in the level list */ list_object(c, drop); /* Redraw */ square_note_spot(c, y, x); square_light_spot(c, y, x); /* Result */ return true; }
/** * Scans the dungeon for objects */ static void scan_for_objects(void) { int y, x; for (y = 1; y < cave->height - 1; y++) { for (x = 1; x < cave->width - 1; x++) { struct object *obj; while ((obj = square_object(cave, y, x))) { /* Get data on the object */ get_obj_data(obj, y, x, FALSE, FALSE); /* Delete the object */ square_excise_object(cave, y, x, obj); object_delete(&obj); } } } }
/** * Delete an object when the floor fails to carry it, and attempt to remove * it from the object list */ static void floor_carry_fail(struct object *drop, bool broke) { struct object *known = drop->known; /* Delete completely */ if (known) { char o_name[80]; char *verb = broke ? VERB_AGREEMENT(drop->number, "breaks", "break") : VERB_AGREEMENT(drop->number, "disappears", "disappear"); object_desc(o_name, sizeof(o_name), drop, ODESC_BASE); msg("The %s %s.", o_name, verb); if (known->iy && known->ix) square_excise_object(player->cave, known->iy, known->ix, known); delist_object(player->cave, known); object_delete(&known); } delist_object(cave, drop); object_delete(&drop); }
/** * Move an object from a floor pile to the player's gear */ static void player_pickup_aux(struct object *obj, bool domsg) { /* Confirm the object can be picked up*/ if (!inven_carry_okay(obj)) quit_fmt("Failed pickup of %s", obj->kind->name); /* Set ignore status */ player->upkeep->notice |= PN_IGNORE; /* Automatically sense artifacts */ object_sense_artifact(obj); /* Log artifacts if found */ if (obj->artifact) history_add_artifact(obj->artifact, object_is_known(obj), TRUE); /* Carry the object */ square_excise_object(cave, player->py, player->px, obj); inven_carry(player, obj, domsg); }
/** * Update the player's knowledge of the objects on a grid in the current level */ void square_know_pile(struct chunk *c, int y, int x) { struct object *obj; if (c != cave) return; object_lists_check_integrity(c, player->cave); /* Know every item on this grid, greater knowledge for the player grid */ for (obj = square_object(c, y, x); obj; obj = obj->next) { object_see(player, obj); if ((y == player->py) && (x == player->px)) { object_touch(player, obj); } } /* Remove known location of anything not on this grid */ obj = square_object(player->cave, y, x); while (obj) { struct object *next = obj->next; assert(c->objects[obj->oidx]); if (!square_holds_object(c, y, x, c->objects[obj->oidx])) { struct object *original = c->objects[obj->oidx]; square_excise_object(player->cave, y, x, obj); obj->iy = 0; obj->ix = 0; /* Delete objects which no longer exist anywhere */ if (obj->notice & OBJ_NOTICE_IMAGINED) { delist_object(player->cave, obj); object_delete(&obj); original->known = NULL; delist_object(c, original); object_delete(&original); } } obj = next; } }
/** * Called from project() to affect objects * * Called for projections with the PROJECT_ITEM flag set, which includes * beam, ball and breath effects. * * \param who is the monster list index of the caster * \param r is the distance from the centre of the effect * \param y the coordinates of the grid being handled * \param x the coordinates of the grid being handled * \param dam is the "damage" from the effect at distance r from the centre * \param typ is the projection (GF_) type * \param protected_obj is an object that should not be affected by the * projection, typically the object that created it * \return whether the effects were obvious * * Note that this function determines if the player can see anything that * happens by taking into account: blindness, line-of-sight, and illumination. * * Hack -- effects on objects which are memorized but not in view are also seen. */ bool project_o(int who, int r, int y, int x, int dam, int typ, const struct object *protected_obj) { struct object *obj = square_object(cave, y, x); bool obvious = false; /* Scan all objects in the grid */ while (obj) { bool ignore = false; bool do_kill = false; const char *note_kill = NULL; struct object *next = obj->next; project_object_handler_context_t context = { who, r, y, x, dam, typ, obj, obvious, do_kill, ignore, note_kill, }; project_object_handler_f object_handler = object_handlers[typ]; if (object_handler != NULL) object_handler(&context); obvious = context.obvious; do_kill = context.do_kill && (obj != protected_obj); ignore = context.ignore; note_kill = context.note_kill; /* Attempt to destroy the object */ if (do_kill) { char o_name[80]; /* Effect observed */ if (obj->known && !ignore_item_ok(obj) && square_isseen(cave, y, x)) { obvious = true; object_desc(o_name, sizeof(o_name), obj, ODESC_BASE); } /* Artifacts, and other objects, get to resist */ if (obj->artifact || ignore) { /* Observe the resist */ if (obvious && obj->known && !ignore_item_ok(obj)) msg("The %s %s unaffected!", o_name, VERB_AGREEMENT(obj->number, "is", "are")); } else if (obj->mimicking_m_idx) { /* Reveal mimics */ if (obvious) become_aware(cave_monster(cave, obj->mimicking_m_idx)); } else { /* Describe if needed */ if (obvious && obj->known && note_kill && !ignore_item_ok(obj)) msgt(MSG_DESTROY, "The %s %s!", o_name, note_kill); /* Delete the object */ square_excise_object(cave, y, x, obj); delist_object(cave, obj); object_delete(&obj); /* Redraw */ square_note_spot(cave, y, x); square_light_spot(cave, y, x); } } /* Next object */ obj = next; } /* Return "Anything seen?" */ return (obvious); }
/** * 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; } }
/** * 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); } }
/** * Deletes a monster by index. * * When a monster is deleted, all of its objects are deleted. */ void delete_monster_idx(int m_idx) { assert(m_idx > 0); struct monster *mon = cave_monster(cave, m_idx); int y = mon->fy; int x = mon->fx; assert(square_in_bounds(cave, y, x)); /* Hack -- Reduce the racial counter */ mon->race->cur_num--; /* Hack -- count the number of "reproducers" */ if (rf_has(mon->race->flags, RF_MULTIPLY)) num_repro--; /* Hack -- remove target monster */ if (target_get_monster() == mon) target_set_monster(NULL); /* Hack -- remove tracked monster */ if (player->upkeep->health_who == mon) health_track(player->upkeep, NULL); /* Monster is gone */ cave->squares[y][x].mon = 0; /* Delete objects */ struct object *obj = mon->held_obj; while (obj) { struct object *next = obj->next; /* Preserve unseen artifacts (we assume they were created as this * monster's drop) - this will cause unintended behaviour in preserve * off mode if monsters can pick up artifacts */ if (obj->artifact && !(obj->known && obj->known->artifact)) obj->artifact->created = false; /* Delete the object */ delist_object(cave, obj); object_delete(&obj); obj = next; } /* Delete mimicked objects */ if (mon->mimicked_obj) { square_excise_object(cave, y, x, mon->mimicked_obj); delist_object(cave, mon->mimicked_obj); object_delete(&mon->mimicked_obj); } /* Wipe the Monster */ memset(mon, 0, sizeof(struct monster)); /* Count monsters */ cave->mon_cnt--; /* Visual update */ square_light_spot(cave, y, x); }
/** * 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; } }
/** * Update the player's knowledge of the objects on a grid in the current level */ void floor_pile_know(struct chunk *c, int y, int x) { struct object *obj; if (c != cave) return; object_lists_check_integrity(); /* Know 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 known objects, fully know sensed ones, relocate old ones */ if (known_obj == NULL) { /* Make and/or list the new object */ struct object *new_obj; /* Check whether we need to make a new one or list the old one */ if (obj->known) { new_obj = obj->known; } else { new_obj = object_new(); obj->known = new_obj; object_set_base_known(obj); } cave_k->objects[obj->oidx] = new_obj; new_obj->oidx = obj->oidx; /* Attach it to the current floor pile */ new_obj->iy = y; new_obj->ix = x; new_obj->number = obj->number; if (!square_holds_object(cave_k, y, x, new_obj)) pile_insert_end(&cave_k->squares[y][x].obj, new_obj); } else if (known_obj->kind != obj->kind) { int iy = known_obj->iy; int ix = known_obj->ix; /* Make sure knowledge is correct */ assert(known_obj == obj->known); /* Detach from any old pile (possibly the correct one) */ if (iy && ix && square_holds_object(cave_k, iy, ix, known_obj)) square_excise_object(cave_k, iy, ix, known_obj); /* Copy over actual details */ object_set_base_known(obj); /* Attach it to the current floor pile */ known_obj->iy = y; known_obj->ix = x; known_obj->held_m_idx = 0; if (!square_holds_object(cave_k, y, x, known_obj)) pile_insert_end(&cave_k->squares[y][x].obj, known_obj); } else if (!square_holds_object(cave_k, y, x, known_obj)) { int iy = known_obj->iy; int ix = known_obj->ix; /* Make sure knowledge is correct */ assert(known_obj == obj->known); known_obj->number = obj->number; /* Detach from any old pile */ if (iy && ix && square_holds_object(cave_k, iy, ix, known_obj)) square_excise_object(cave_k, iy, ix, known_obj); /* Attach it to the current floor pile */ known_obj->iy = y; known_obj->ix = x; known_obj->held_m_idx = 0; pile_insert_end(&cave_k->squares[y][x].obj, known_obj); } } /* Remove known location of anything not on this grid */ obj = square_object(cave_k, y, x); while (obj) { struct object *next = obj->next; assert(c->objects[obj->oidx]); if (!square_holds_object(c, y, x, c->objects[obj->oidx])) { struct object *original = c->objects[obj->oidx]; square_excise_object(cave_k, y, x, obj); obj->iy = 0; obj->ix = 0; /* Delete objects which no longer exist anywhere */ if (obj->notice & OBJ_NOTICE_IMAGINED) { delist_object(cave_k, obj); object_delete(&obj); original->known = NULL; delist_object(c, original); object_delete(&original); } } obj = next; } }
/** * Wield or wear a single item from the pack or floor */ void inven_wield(struct object *obj, int slot) { struct object *wielded, *old = player->body.slots[slot].obj; const char *fmt; char o_name[80]; /* Increase equipment counter if empty slot */ if (old == NULL) player->upkeep->equip_cnt++; /* Take a turn */ player->upkeep->energy_use = z_info->move_energy; /* Split off a new object if necessary */ if (obj->number > 1) { /* Split off a new single object */ wielded = object_split(obj, 1); /* If it's a gear object, give the split item a list entry */ if (pile_contains(player->gear, obj)) { wielded->next = obj->next; obj->next = wielded; wielded->prev = obj; if (wielded->next) (wielded->next)->prev = wielded; } } else wielded = obj; /* Carry floor items, don't allow combining */ if (square_holds_object(cave, player->py, player->px, wielded)) { square_excise_object(cave, player->py, player->px, wielded); inven_carry(player, wielded, FALSE, FALSE); } /* Wear the new stuff */ player->body.slots[slot].obj = wielded; /* Do any ID-on-wield */ object_notice_on_wield(wielded); /* Where is the item now */ if (tval_is_melee_weapon(wielded)) fmt = "You are wielding %s (%c)."; else if (wielded->tval == TV_BOW) fmt = "You are shooting with %s (%c)."; else if (tval_is_light(wielded)) fmt = "Your light source is %s (%c)."; else fmt = "You are wearing %s (%c)."; /* Describe the result */ object_desc(o_name, sizeof(o_name), wielded, ODESC_PREFIX | ODESC_FULL); /* Message */ msgt(MSG_WIELD, fmt, o_name, I2A(slot)); /* Cursed! */ if (cursed_p(wielded->flags)) { /* Warn the player */ msgt(MSG_CURSED, "Oops! It feels deathly cold!"); /* Sense the object */ object_notice_curses(wielded); } /* See if we have to overflow the pack */ combine_pack(); pack_overflow(old); /* Recalculate bonuses, torch, mana, gear */ player->upkeep->notice |= (PN_IGNORE); player->upkeep->update |= (PU_BONUS | PU_INVEN); player->upkeep->redraw |= (PR_INVEN | PR_EQUIP | PR_ARMOR); player->upkeep->redraw |= (PR_STATS | PR_HP | PR_MANA | PR_SPEED); /* Disable repeats */ cmd_disable_repeat(); }