/** * Get a list of the objects at the player's location. * * Return the number of objects acquired. */ int scan_floor(struct object **items, int max_size, object_floor_t mode, item_tester tester) { struct object *obj; int py = player->py; int px = player->px; int num = 0; /* Sanity */ if (!square_in_bounds(cave, py, px)) return 0; /* Scan all objects in the grid */ for (obj = square_object(cave, py, px); obj; obj = obj->next) { /* Enforce limit */ if (num >= max_size) break; /* Item tester */ if ((mode & OFLOOR_TEST) && !object_test(tester, obj)) continue; /* Sensed or known */ if ((mode & OFLOOR_SENSE) && (!obj->known)) continue; /* Visible */ if ((mode & OFLOOR_VISIBLE) && !is_unknown(obj) && ignore_item_ok(obj)) continue; /* Accept this item */ items[num++] = obj; /* Only one */ if (mode & OFLOOR_TOP) break; } return num; }
/** * Put an autoinscription on an object */ int apply_autoinscription(struct object *obj) { char o_name[80]; const char *note = obj ? get_autoinscription(obj->kind) : NULL; /* Don't inscribe unaware objects */ if (!note || !object_flavor_is_aware(obj)) return 0; /* Don't re-inscribe if it's already inscribed */ if (obj->note) return 0; /* Don't inscribe unless the player is carrying it */ if (!object_is_carried(player, obj)) return 0; /* Don't inscribe if ignored */ if (ignore_item_ok(obj)) return 0; /* Get an object description */ object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); if (note[0] != 0) obj->note = quark_add(note); else obj->note = 0; msg("You autoinscribe %s.", o_name); return 1; }
/** * 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 items only * 0x04 -- Only the top item * 0x08 -- Visible items only */ int scan_floor(struct object **items, int max_size, int y, int x, int mode, item_tester tester) { struct object *obj; int num = 0; /* Sanity */ if (!square_in_bounds(cave, y, x)) return 0; /* Scan all objects in the grid */ for (obj = square_object(cave, y, x); obj; obj = obj->next) { /* Enforce limit */ if (num >= max_size) break; /* Item tester */ if ((mode & 0x01) && !object_test(tester, obj)) continue; /* Marked */ if ((mode & 0x02) && (!obj->marked)) continue; /* Visible */ if ((mode & 0x08) && !is_unknown(obj) && ignore_item_ok(obj)) continue; /* Accept this item */ items[num++] = obj; /* Only one */ if (mode & 0x04) break; } return num; }
/** * 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. * * The calling function needs to deal with the consequences of the dropped * object being destroyed or absorbed into an existing pile. */ void drop_near(struct chunk *c, struct object **dropped, int chance, int y, int x, bool verbose) { char o_name[80]; int best_y = y; int best_x = x; /* 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)) { floor_carry_fail(*dropped, true); return; } /* Find the best grid and drop the item, destroying if there's no space */ drop_find_grid(*dropped, &best_y, &best_x); if (floor_carry(c, best_y, best_x, *dropped, false)) { sound(MSG_DROP); if (verbose && (c->squares[best_y][best_x].mon < 0) && c->objects[(*dropped)->oidx] && !ignore_item_ok(*dropped)) msg("You feel something roll beneath your feet."); } else { floor_carry_fail(*dropped, false); } }
/** * Determine if a grid contains a chest matching the query type, and * return a pointer to the first such chest */ struct object *chest_check(int y, int x, enum chest_query check_type) { struct object *obj; /* Scan all objects in the grid */ for (obj = square_object(cave, y, x); obj; obj = obj->next) { /* Ignore if requested */ if (ignore_item_ok(obj)) continue; /* Check for chests */ switch (check_type) { case CHEST_ANY: if (tval_is_chest(obj)) return obj; break; case CHEST_OPENABLE: if (tval_is_chest(obj) && (obj->pval != 0)) return obj; break; case CHEST_TRAPPED: if (is_trapped_chest(obj) && object_is_known(obj)) return obj; break; } } /* No chest */ return NULL; }
/** * 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; }
/** * Add player-defined inscriptions or game-defined descriptions */ static size_t obj_desc_inscrip(const struct object *obj, char *buf, size_t max, size_t end) { const char *u[4] = { 0, 0, 0, 0 }; int n = 0; int feel = object_pseudo(obj); bitflag flags_known[OF_SIZE], f2[OF_SIZE]; object_flags_known(obj, flags_known); /* Get inscription */ if (obj->note) u[n++] = quark_str(obj->note); /* Use special inscription, if any */ if (!object_is_known(obj)) { if (feel) { /* cannot tell excellent vs strange vs splendid until wield */ if (!object_was_worn(obj) && obj->ego) u[n++] = "ego"; else u[n++] = inscrip_text[feel]; } else if (tval_can_have_charges(obj) && (obj->pval == 0)) u[n++] = "empty"; else if (object_was_worn(obj)) u[n++] = (tval_is_weapon(obj)) ? "wielded" : "worn"; else if (!object_flavor_is_aware(obj) && object_flavor_was_tried(obj)) u[n++] = "tried"; } /* Note curses */ create_mask(f2, FALSE, OFT_CURSE, OFT_MAX); if (of_is_inter(flags_known, f2)) u[n++] = "cursed"; /* Note ignore */ if (ignore_item_ok(obj)) u[n++] = "ignore"; if (n) { int i; for (i = 0; i < n; i++) { if (i == 0) strnfcat(buf, max, &end, " {"); strnfcat(buf, max, &end, "%s", u[i]); if (i < n-1) strnfcat(buf, max, &end, ", "); } strnfcat(buf, max, &end, "}"); } return end; }
/** * Perform the basic "tunnel" command * * Assumes that no monster is blocking the destination. * Uses twall() (above) to do all "terrain feature changing". * Returns true if repeated commands may continue. */ static bool do_cmd_tunnel_aux(int y, int x) { bool more = false; int digging_chances[DIGGING_MAX]; bool okay = false; bool gold = square_hasgoldvein(cave, y, x); bool rubble = square_isrubble(cave, y, x); /* Verify legality */ if (!do_cmd_tunnel_test(y, x)) return (false); calc_digging_chances(&player->state, digging_chances); /* Do we succeed? */ okay = (digging_chances[square_digging(cave, y, x) - 1] > randint0(1600)); /* Success */ if (okay && twall(y, x)) { /* Rubble is a special case - could be handled more generally NRM */ if (rubble) { /* Message */ msg("You have removed the rubble."); /* Place an object (except in town) */ if ((randint0(100) < 10) && player->depth) { /* Create a simple object */ place_object(cave, y, x, player->depth, false, false, ORIGIN_RUBBLE, 0); /* Observe the new object */ if (!ignore_item_ok(square_object(cave, y, x)) && square_isseen(cave, y, x)) msg("You have found something!"); } } else if (gold) { /* Found treasure */ place_gold(cave, y, x, player->depth, ORIGIN_FLOOR); msg("You have found something!"); } else { msg("You have finished the tunnel."); } } else { /* Failure, continue digging */ if (rubble) msg("You dig in the rubble."); else msg("You tunnel into the %s.", square_apparent_name(cave, player, y, x)); more = true; if (square_issecretdoor(cave, y, x)) /* Occasional Search XXX XXX */ if (randint0(100) < 25) search(false); } /* Result */ return (more); }
/** * Find and return the oldest object on the given grid marked as "ignore". */ static struct object *floor_get_oldest_ignored(int y, int x) { struct object *obj, *ignore = NULL; for (obj = square_object(cave, y, x); obj; obj = obj->next) if (ignore_item_ok(obj)) ignore = obj; return ignore; }
/** * Determine if a chest is locked or trapped */ bool is_locked_chest(const struct object *obj) { if (!tval_is_chest(obj)) return FALSE; /* Ignore if requested */ if (ignore_item_ok(obj)) return FALSE; /* Disarmed or opened chests are not locked */ return (obj->pval > 0); }
/* Unlock chests */ static void project_object_handler_KILL_TRAP(project_object_handler_context_t *context) { /* Chests are noticed only if trapped or locked */ if (is_locked_chest(context->obj)) { /* Disarm or Unlock */ unlock_chest((struct object * const)context->obj); /* Notice */ if (context->obj->known && !ignore_item_ok(context->obj)) { context->obj->known->pval = context->obj->pval; msg("Click!"); context->obvious = true; } } }
/** * Drop all {ignore}able items. */ void ignore_drop(void) { struct object *obj; /* Scan through the slots backwards */ for (obj = gear_last_item(); obj; obj = obj->prev) { /* Skip non-objects and unignoreable objects */ assert(obj->kind); if (!ignore_item_ok(obj)) continue; /* Check for !d (no drop) inscription */ if (!check_for_inscrip(obj, "!d") && !check_for_inscrip(obj, "!*")) { /* Confirm the drop if the item is equipped. */ if (object_is_equipped(player->body, obj)) { if (!verify_object("Really take off and drop", obj)) { /* Hack - inscribe the item with !d to prevent repeated * confirmations. */ const char *inscription = quark_str(obj->note); if (inscription == NULL) { obj->note = quark_add("!d"); } else { char buffer[1024]; my_strcpy(buffer, inscription, sizeof(buffer)); my_strcat(buffer, "!d", sizeof(buffer)); obj->note = quark_add(buffer); } continue; } } /* We're allowed to drop it. */ if (!square_isshop(cave, player->py, player->px)) { player->upkeep->dropping = true; cmdq_push(CMD_DROP); cmd_set_arg_item(cmdq_peek(), "item", obj); cmd_set_arg_number(cmdq_peek(), "quantity", obj->number); } } } /* Update the gear */ player->upkeep->update |= (PU_INVEN); /* Combine/reorder the pack */ player->upkeep->notice |= (PN_COMBINE); }
/** * Determine if a chest is trapped */ bool is_trapped_chest(const struct object *obj) { if (!tval_is_chest(obj)) return FALSE; /* Ignore if requested */ if (ignore_item_ok(obj)) return FALSE; /* Disarmed or opened chests are not trapped */ if (obj->pval <= 0) return FALSE; /* Some chests simply don't have traps */ return (chest_traps[obj->pval] != 0); }
/* Unlock chests */ static void project_object_handler_KILL_TRAP(project_object_handler_context_t *context) { /* Chests are noticed only if trapped or locked */ if (is_locked_chest(context->obj)) { /* Disarm or Unlock */ unlock_chest((struct object * const)context->obj); /* Identify */ object_notice_everything((struct object * const)context->obj); /* Notice */ if (context->obj->marked > MARK_UNAWARE && !ignore_item_ok(context->obj)) { msg("Click!"); context->obvious = TRUE; } } }
/** * Put an autoinscription on an object */ int apply_autoinscription(struct object *obj) { char o_name[80]; bool aware = obj->kind->aware; const char *note = obj ? get_autoinscription(obj->kind, aware) : NULL; /* Remove unaware inscription if aware */ if (aware && quark_str(obj->note) && quark_str(obj->kind->note_unaware) && streq(quark_str(obj->note), quark_str(obj->kind->note_unaware))) obj->note = 0; /* Make rune autoinscription go first, for now */ runes_autoinscribe(obj); /* No note - don't inscribe */ if (!note) return 0; /* Don't re-inscribe if it's already inscribed */ if (obj->note) return 0; /* Don't inscribe unless the player is carrying it */ if (!object_is_carried(player, obj)) return 0; /* Don't inscribe if ignored */ if (ignore_item_ok(obj)) return 0; /* Get an object description */ object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); if (note[0] != 0) obj->note = quark_add(note); else obj->note = 0; msg("You autoinscribe %s.", o_name); return 1; }
/** * Pick up everything on the floor that requires no player action */ int do_autopickup(void) { int py = player->py; int px = player->px; struct object *obj, *next; /* Objects picked up. Used to determine time cost of command. */ byte objs_picked_up = 0; /* Nothing to pick up -- return */ if (!square_object(cave, py, px)) return 0; /* Always pickup gold, effortlessly */ player_pickup_gold(); /* Scan the remaining objects */ obj = square_object(cave, py, px); while (obj) { next = obj->next; /* Ignore all hidden objects and non-objects */ if (!ignore_item_ok(obj)) { /* Hack -- disturb */ disturb(player, 0); /* Automatically pick up items into the backpack */ if (auto_pickup_okay(obj)) { /* Pick up the object with message */ player_pickup_aux(obj, TRUE); objs_picked_up++; } } obj = next; } return objs_picked_up; }
/** * Determine if a given location is "interesting" */ bool target_accept(int y, int x) { object_type *obj; /* Player grids are always interesting */ if (cave->squares[y][x].mon < 0) return (TRUE); /* Handle hallucination */ if (player->timed[TMD_IMAGE]) return (FALSE); /* Visible monsters */ if (cave->squares[y][x].mon > 0) { monster_type *mon = square_monster(cave, y, x); /* Visible monsters */ if (mflag_has(mon->mflag, MFLAG_VISIBLE) && !mflag_has(mon->mflag, MFLAG_UNAWARE)) return (TRUE); } /* Traps */ if (square_isvisibletrap(cave, y, x)) return(TRUE); /* Scan all objects in the grid */ for (obj = square_object(cave, y, x); obj; obj = obj->next) /* Memorized object */ if (obj->marked && !ignore_item_ok(obj)) return (TRUE); /* Interesting memorized features */ if (square_ismark(cave, y, x) && square_isinteresting(cave, y, x)) return (TRUE); /* Nope */ return (FALSE); }
/** * 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 *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 current "run" path * * Return TRUE if the running should be stopped */ static bool run_test(void) { int py = player->py; int px = player->px; int prev_dir; int new_dir; int row, col; int i, max, inv; int option, option2; /* No options yet */ option = 0; option2 = 0; /* Where we came from */ prev_dir = run_old_dir; /* Range of newly adjacent grids */ max = (prev_dir & 0x01) + 1; /* Look at every newly adjacent square. */ for (i = -max; i <= max; i++) { struct object *obj; /* New direction */ new_dir = cycle[chome[prev_dir] + i]; /* New location */ row = py + ddy[new_dir]; col = px + ddx[new_dir]; /* Visible monsters abort running */ if (cave->squares[row][col].mon > 0) { monster_type *m_ptr = square_monster(cave, row, col); /* Visible monster */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) return (TRUE); } /* Visible objects abort running */ for (obj = square_object(cave, row, col); obj; obj = obj->next) /* Visible object */ if (obj->marked && !ignore_item_ok(obj)) return (TRUE); /* Assume unknown */ inv = TRUE; /* Check memorized grids */ if (square_ismark(cave, row, col)) { bool notice = square_noticeable(cave, row, col); /* Interesting feature */ if (notice) return (TRUE); /* The grid is "visible" */ inv = FALSE; } /* Analyze unknown grids and floors */ if (inv || square_ispassable(cave, row, col)) { /* Looking for open area */ if (run_open_area) { /* Nothing */ } else if (!option) { /* The first new direction. */ option = new_dir; } else if (option2) { /* Three new directions. Stop running. */ return (TRUE); } else if (option != cycle[chome[prev_dir] + i - 1]) { /* Two non-adjacent new directions. Stop running. */ return (TRUE); } else if (new_dir & 0x01) { /* Two new (adjacent) directions (case 1) */ option2 = new_dir; } else { /* Two new (adjacent) directions (case 2) */ option2 = option; option = new_dir; } } else { /* Obstacle, while looking for open area */ if (run_open_area) { if (i < 0) { /* Break to the right */ run_break_right = TRUE; } else if (i > 0) { /* Break to the left */ run_break_left = TRUE; } } } } /* Look at every soon to be newly adjacent square. */ for (i = -max; i <= max; i++) { /* New direction */ new_dir = cycle[chome[prev_dir] + i]; /* New location */ row = py + ddy[prev_dir] + ddy[new_dir]; col = px + ddx[prev_dir] + ddx[new_dir]; /* HACK: Ugh. Sometimes we come up with illegal bounds. This will * treat the symptom but not the disease. */ if (row >= cave->height || col >= cave->width) continue; if (row < 0 || col < 0) continue; /* Visible monsters abort running */ if (cave->squares[row][col].mon > 0) { monster_type *m_ptr = square_monster(cave, row, col); /* Visible monster */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE) && !is_mimicking(m_ptr)) return (TRUE); } } /* Looking for open area */ if (run_open_area) { /* Hack -- look again */ for (i = -max; i < 0; i++) { new_dir = cycle[chome[prev_dir] + i]; row = py + ddy[new_dir]; col = px + ddx[new_dir]; /* Unknown grid or non-wall */ /* Was: square_ispassable(cave, row, col) */ if (!square_ismark(cave, row, col) || (square_ispassable(cave, row, col))) { /* Looking to break right */ if (run_break_right) { return (TRUE); } } else { /* Obstacle */ /* Looking to break left */ if (run_break_left) { return (TRUE); } } } /* Hack -- look again */ for (i = max; i > 0; i--) { new_dir = cycle[chome[prev_dir] + i]; row = py + ddy[new_dir]; col = px + ddx[new_dir]; /* Unknown grid or non-wall */ /* Was: square_ispassable(cave, row, col) */ if (!square_ismark(cave, row, col) || (square_ispassable(cave, row, col))) { /* Looking to break left */ if (run_break_left) { return (TRUE); } } else { /* Obstacle */ /* Looking to break right */ if (run_break_right) { return (TRUE); } } } } else { /* Not looking for open area */ /* No options */ if (!option) { return (TRUE); } else if (!option2) { /* One option */ /* Primary option */ run_cur_dir = option; /* No other options */ run_old_dir = option; } else { /* Two options, examining corners */ /* Primary option */ run_cur_dir = option; /* Hack -- allow curving */ run_old_dir = option2; } } /* About to hit a known wall, stop */ if (see_wall(run_cur_dir, py, px)) return (TRUE); /* Failure */ return (FALSE); }
/** * Take one step along the current "run" path * * Called with a real direction to begin a new run, and with zero * to continue a run in progress. */ void run_step(int dir) { /* Start or continue run */ if (dir) { /* Initialize */ run_init(dir); /* Hack -- Set the run counter */ player->upkeep->running = 1000; /* Calculate torch radius */ player->upkeep->update |= (PU_TORCH); } else { /* Continue running */ if (!player->upkeep->running_withpathfind) { /* Update regular running */ if (run_test()) { /* Disturb */ disturb(player, 0); return; } } else if (pf_result_index < 0) { /* Pathfinding, and the path is finished */ disturb(player, 0); player->upkeep->running_withpathfind = false; return; } else { int y = player->py + ddy[pf_result[pf_result_index] - '0']; int x = player->px + ddx[pf_result[pf_result_index] - '0']; if (pf_result_index == 0) { /* Known wall */ if (square_isknown(cave, y, x) && !square_ispassable(cave, y, x)) { disturb(player, 0); player->upkeep->running_withpathfind = false; return; } } else if (pf_result_index > 0) { struct object *obj; /* If the player has computed a path that is going to end up * in a wall, we notice this and convert to a normal run. This * allows us to click on unknown areas to explore the map. * * We have to look ahead two, otherwise we don't know which is * the last direction moved and don't initialise the run * properly. */ y = player->py + ddy[pf_result[pf_result_index] - '0']; x = player->px + ddx[pf_result[pf_result_index] - '0']; /* Known wall */ if (square_isknown(cave, y, x) && !square_ispassable(cave, y, x)) { disturb(player, 0); player->upkeep->running_withpathfind = false; return; } /* Visible monsters abort running */ if (cave->squares[y][x].mon > 0) { struct monster *mon = square_monster(cave, y, x); /* Visible monster */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) { disturb(player, 0); player->upkeep->running_withpathfind = false; return; } } /* Visible objects abort running */ for (obj = square_object(cave, y, x); obj; obj = obj->next) /* Visible object */ if (obj->known && !ignore_item_ok(obj)) { disturb(player, 0); player->upkeep->running_withpathfind = false; return; } /* Get step after */ y = y + ddy[pf_result[pf_result_index - 1] - '0']; x = x + ddx[pf_result[pf_result_index - 1] - '0']; /* Known wall, so run the direction we were going */ if (square_isknown(cave, y, x) && !square_ispassable(cave, y, x)) { player->upkeep->running_withpathfind = false; run_init(pf_result[pf_result_index] - '0'); } } /* Now actually run the step if we're still going */ run_cur_dir = pf_result[pf_result_index--] - '0'; } } /* Decrease counter if it hasn't been cancelled */ if (player->upkeep->running) player->upkeep->running--; else if (!player->upkeep->running_withpathfind) return; /* Take time */ player->upkeep->energy_use = z_info->move_energy; /* Move the player; running straight into a trap == trying to disarm */ move_player(run_cur_dir, dir ? true : false); /* Prepare the next step */ if (player->upkeep->running) { cmdq_push(CMD_RUN); cmd_set_arg_direction(cmdq_peek(), "direction", 0); } }
/** * 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); } }
/** * Find a grid near the given one for an object to fall on * * 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. * * If no appropriate grid is found, the given grid is unchanged */ static void drop_find_grid(struct object *drop, int *y, int *x) { int best_score = -1; int best_y = *y; int best_x = *x; int i, dy, dx; struct object *obj; /* Scan local grids */ for (dy = -3; dy <= 3; dy++) { for (dx = -3; dx <= 3; dx++) { bool combine = false; int dist = (dy * dy) + (dx * dx); int ty = *y + dy; int tx = *x + dx; int num_shown = 0; int num_ignored = 0; int score; /* Lots of reasons to say no */ if ((dist > 10) || !square_in_bounds_fully(cave, ty, tx) || !los(cave, *y, *x, ty, tx) || !square_isfloor(cave, ty, tx) || square_isplayertrap(cave, ty, tx) || square_iswarded(cave, ty, tx)) continue; /* Analyse the grid for carrying the new object */ for (obj = square_object(cave, ty, tx); obj; obj = obj->next) { /* Check for possible combination */ if (object_similar(obj, drop, OSTACK_FLOOR)) combine = true; /* Count objects */ if (!ignore_item_ok(obj)) num_shown++; else num_ignored++; } if (!combine) num_shown++; /* Disallow if the stack size is too big */ if ((OPT(player, birth_stacking) && (num_shown > 1)) || ((num_shown + num_ignored) > z_info->floor_size && !floor_get_oldest_ignored(ty, tx))) continue; /* Score the location based on how close and how full the grid is */ score = 1000 - (dist + num_shown * 5); if ((score < best_score) || ((score == best_score) && one_in_(2))) continue; best_score = score; best_y = ty; best_x = tx; } } /* Return if we have a score, otherwise fail or try harder for artifacts */ if (best_score >= 0) { *y = best_y; *x = best_x; return; } else if (!drop->artifact) { return; } for (i = 0; i < 2000; i++) { /* Start bouncing from grid to grid, stopping if we find an empty one */ if (i < 1000) { best_y = rand_spread(best_y, 1); best_x = rand_spread(best_x, 1); } else { /* Now go to purely random locations */ best_y = randint0(cave->height); best_x = randint0(cave->width); } if (square_canputitem(cave, best_y, best_x)) { *y = best_y; *x = best_x; return; } } }
/** * 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."); }