/** * 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; }
/** * 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); }
/** * 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; } }
/** * 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); delist_object(cave, obj); object_delete(&obj); } } } }
/* * A rewrite of monster death that gets rid of some features * That we don't want to deal with. Namely, no notifying the * player and no generation of Morgoth artifacts * * It also replaces drop near with a new function that drops all * the items on the exact square that the monster was on. */ void monster_death_stats(int m_idx) { struct object *obj; struct monster *mon; bool uniq; assert(m_idx > 0); mon = cave_monster(cave, m_idx); /* Check if monster is UNIQUE */ uniq = rf_has(mon->race->flags,RF_UNIQUE); /* Mimicked objects will have already been counted as floor objects */ mon->mimicked_obj = NULL; /* Drop objects being carried */ obj = mon->held_obj; while (obj) { struct object *next = obj->next; /* Object no longer held */ obj->held_m_idx = 0; /* Get data */ get_obj_data(obj, mon->fy, mon->fx, true, uniq); /* Delete the object */ delist_object(cave, obj); object_delete(&obj); /* Next */ obj = next; } /* Forget objects */ mon->held_obj = NULL; }
/** * 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; } }
/** * 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); }
/** * Let an object fall to the ground at or near a location. * * The initial location is assumed to be "square_in_bounds_fully(cave, )". * * 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. * * Objects which fail to be carried by the floor are deleted. This function * attempts to add successfully dropped objects to, and to remove failures * from, the object list (as dropped items may or may not be already listed). */ void drop_near(struct chunk *c, struct object *dropped, 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; struct object *obj; char o_name[80]; bool flag = false; /* Only called in the current level */ assert(c == cave); /* Describe object */ object_desc(o_name, sizeof(o_name), dropped, ODESC_BASE); /* Handle normal "breakage" */ if (!dropped->artifact && (randint0(100) < chance)) { /* Message */ msg("The %s %s.", o_name, VERB_AGREEMENT(dropped->number, "breaks", "break")); /* Failure */ if (dropped->known) { delist_object(cave_k, dropped->known); object_delete(&dropped->known); } delist_object(c, dropped); object_delete(&dropped); return; } /* Score */ bs = -1; /* Picker */ bn = 0; /* Default */ by = y; bx = x; /* Scan local grids */ for (dy = -3; dy <= 3; dy++) { 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 (!square_in_bounds_fully(c, ty, tx)) continue; /* Require line of sight */ if (!los(c, y, x, ty, tx)) continue; /* Require floor space */ if (!square_isfloor(c, ty, tx)) continue; /* Require no trap or rune */ if (square_isplayertrap(c, ty, tx) || square_iswarded(c, ty, tx)) continue; /* No objects */ k = 0; n = 0; /* Scan objects in that grid */ for (obj = square_object(c, ty, tx); obj; obj = obj->next) { /* Check for possible combination */ if (object_similar(obj, dropped, OSTACK_FLOOR)) comb = true; /* Count objects */ if (!ignore_item_ok(obj)) k++; else n++; } /* Add new object */ if (!comb) k++; /* Option -- disallow stacking */ if (OPT(birth_no_stacking) && (k > 1)) continue; /* Paranoia? */ if ((k + n) > z_info->floor_size && !floor_get_oldest_ignored(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 && !dropped->artifact) { /* Message */ msg("The %s %s.", o_name, VERB_AGREEMENT(dropped->number, "disappears", "disappear")); /* Debug */ if (player->wizard) msg("Breakage (no floor space)."); /* Failure */ if (dropped->known) { delist_object(cave_k, dropped->known); object_delete(&dropped->known); } delist_object(c, dropped); object_delete(&dropped); return; } /* Find a grid */ for (i = 0; !flag; i++) { /* Bounce around */ if (i < 1000) { ty = rand_spread(by, 1); tx = rand_spread(bx, 1); } else { /* Random locations */ ty = randint0(c->height); tx = randint0(c->width); } /* Require floor space */ if (!square_canputitem(c, ty, tx)) continue; /* Bounce to that location */ by = ty; bx = tx; /* Okay */ flag = true; } /* Give it to the floor */ if (!floor_carry(c, by, bx, dropped, false)) { /* Message */ msg("The %s %s.", o_name, VERB_AGREEMENT(dropped->number, "disappears", "disappear")); /* Debug */ if (player->wizard) msg("Breakage (too many objects)."); if (dropped->artifact) dropped->artifact->created = false; /* Failure */ if (dropped->known) { delist_object(cave_k, dropped->known); object_delete(&dropped->known); } delist_object(c, dropped); object_delete(&dropped); return; } /* Sound */ sound(MSG_DROP); /* Message when an object falls under the player */ if (verbose && (c->squares[by][bx].mon < 0)) /* Check the item still exists and isn't ignored */ if (c->objects[dropped->oidx] && !ignore_item_ok(dropped)) msg("You feel something roll beneath your feet."); }
/** * 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; } }