/** * 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; } }
/** * 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; }
/** * This will push objects off a square. * * The methodology is to load all objects on the square into a queue. Replace * the previous square with a type that does not allow for objects. Drop the * objects. Last, put the square back to its original type. */ void push_object(int y, int x) { /* Save the original terrain feature */ struct feature *feat_old = square_feat(cave, y, x); struct object *obj = square_object(cave, y, x); struct queue *queue = q_new(z_info->floor_size); bool glyph = square_iswarded(cave, y, x); /* Push all objects on the square, stripped of pile info, into the queue */ while (obj) { struct object *next = obj->next; q_push_ptr(queue, obj); /* Orphan the object */ obj->next = NULL; obj->prev = NULL; obj->iy = 0; obj->ix = 0; /* Next object */ obj = next; } /* Disassociate the objects from the square */ cave->squares[y][x].obj = NULL; /* Set feature to an open door */ square_force_floor(cave, y, x); square_add_door(cave, y, x, false); /* Drop objects back onto the floor */ while (q_len(queue) > 0) { /* Take object from the queue */ obj = q_pop_ptr(queue); /* Drop the object */ drop_near(cave, &obj, 0, y, x, false); } /* Reset cave feature and rune if needed */ square_set_feat(cave, y, x, feat_old->fidx); if (glyph) square_add_ward(cave, y, x); q_free(queue); }
/** * Determine if a cave grid is allowed to have player traps in it. */ bool square_player_trap_allowed(struct chunk *c, int y, int x) { /* * We currently forbid multiple traps in a grid under normal conditions. * If this changes, various bits of code elsewhere will have to change too. */ if (square_istrap(c, y, x)) return FALSE; /* We currently forbid traps in a grid with objects. */ if (square_object(c, y, x)) return FALSE; /* Check the feature trap flag */ return (tf_has(f_info[c->squares[y][x].feat].flags, TF_TRAP)); }
/** * Determine if a cave grid is allowed to have player traps in it. */ bool square_player_trap_allowed(struct chunk *c, int y, int x) { /* * We currently forbid multiple traps in a grid under normal conditions. * If this changes, various bits of code elsewhere will have to change too. */ if (square_istrap(c, y, x)) return false; /* We currently forbid traps in a grid with objects. */ if (square_object(c, y, x)) return false; /* Check it's a trappable square */ return (square_istrappable(c, y, x)); }
/** * Pick up objects at the player's request */ void do_cmd_pickup(struct command *cmd) { int energy_cost; struct object *obj = square_object(cave, player->py, player->px); /* Autopickup first */ energy_cost = do_autopickup() * z_info->move_energy / 10; /* Pick up floor objects with a menu for multiple objects */ energy_cost += player_pickup_item(obj, FALSE) * z_info->move_energy / 10; /* Limit */ if (energy_cost > z_info->move_energy) energy_cost = z_info->move_energy; /* Charge this amount of energy. */ player->upkeep->energy_use = energy_cost; }
/** * Memorize interesting viewable object/features in the given grid * * This function should only be called on "legal" grids. * * This function will memorize the object and/or feature in the given grid, * if they are (1) see-able and (2) interesting. Note that all objects are * interesting, all terrain features except floors (and invisible traps) are * interesting, and floors (and invisible traps) are interesting sometimes * (depending on various options involving the illumination of floor grids). * * The automatic memorization of all objects and non-floor terrain features * as soon as they are displayed allows incredible amounts of optimization * in various places, especially "map_info()" and this function itself. * * Note that the memorization of objects is completely separate from the * memorization of terrain features, preventing annoying floor memorization * when a detected object is picked up from a dark floor, and object * memorization when an object is dropped into a floor grid which is * memorized but out-of-sight. * * This function should be called every time the "memorization" of a grid * (or the object in a grid) is called into question, such as when an object * is created in a grid, when a terrain feature "changes" from "floor" to * "non-floor", and when any grid becomes "see-able" for any reason. * * This function is called primarily from the "update_view()" function, for * each grid which becomes newly "see-able". */ void square_note_spot(struct chunk *c, int y, int x) { object_type *obj; /* Require "seen" flag */ if (!square_isseen(c, y, x)) return; for (obj = square_object(c, y, x); obj; obj = obj->next) obj->marked = MARK_SEEN; if (square_ismark(c, y, x)) return; /* Memorize this grid */ sqinfo_on(c->squares[y][x].info, SQUARE_MARK); }
/** * Light up the dungeon using "claravoyance" * * This function "illuminates" every grid in the dungeon, memorizes all * "objects" (or notes the existence of an object "if" full is TRUE), * and memorizes all grids as with magic mapping. */ void wiz_light(struct chunk *c, bool full) { int i, y, x; /* Scan all grids */ for (y = 1; y < c->height - 1; y++) { for (x = 1; x < c->width - 1; x++) { struct object *obj; /* Process all non-walls */ if (!square_seemslikewall(c, y, x)) { /* Scan all neighbors */ for (i = 0; i < 9; i++) { int yy = y + ddy_ddd[i]; int xx = x + ddx_ddd[i]; /* Perma-light the grid */ sqinfo_on(c->squares[yy][xx].info, SQUARE_GLOW); /* Memorize normal features */ if (!square_isfloor(c, yy, xx) || square_isvisibletrap(c, yy, xx)) { sqinfo_on(c->squares[yy][xx].info, SQUARE_MARK); cave_k->squares[yy][xx].feat = c->squares[yy][xx].feat; } } } /* Memorize objects */ for (obj = square_object(cave, y, x); obj; obj = obj->next) { /* Skip dead objects */ assert(obj->kind); /* Memorize it */ if (obj->marked < MARK_SEEN) obj->marked = full ? MARK_SEEN : MARK_AWARE; } } } /* Fully update the visuals */ player->upkeep->update |= (PU_FORGET_VIEW | PU_UPDATE_VIEW | PU_MONSTERS); /* Redraw whole map, monster list */ player->upkeep->redraw |= (PR_MAP | PR_MONLIST | PR_ITEMLIST); }
/** * Determine if a given location may be "destroyed" * * Used by destruction spells, and for placing stairs, etc. */ bool square_changeable(struct chunk *c, int y, int x) { object_type *obj; /* Forbid perma-grids */ if (square_isperm(c, y, x) || square_isshop(c, y, x) || square_isstairs(c, y, x)) return (FALSE); /* Check objects */ for (obj = square_object(c, y, x); obj; obj = obj->next) /* Forbid artifact grids */ if (obj->artifact) return (FALSE); /* Accept */ 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); } } } }
/** * Search for traps or secret doors */ static void search(void) { int py = player->py; int px = player->px; int y, x; struct object *obj; /* Various conditions mean no searching */ if (player->timed[TMD_BLIND] || no_light() || player->timed[TMD_CONFUSED] || player->timed[TMD_IMAGE]) return; /* Search the nearby grids, which are always in bounds */ for (y = (py - 1); y <= (py + 1); y++) { for (x = (px - 1); x <= (px + 1); x++) { /* Traps */ if (square_issecrettrap(cave, y, x)) { if (square_reveal_trap(cave, y, x, true)) disturb(player, 0); } /* Secret doors */ if (square_issecretdoor(cave, y, x)) { msg("You have found a secret door."); place_closed_door(cave, y, x); disturb(player, 0); } /* Traps on chests */ for (obj = square_object(cave, y, x); obj; obj = obj->next) { if (!obj->known || !is_trapped_chest(obj)) continue; if (obj->known->pval != obj->pval) { msg("You have discovered a trap on the chest!"); obj->known->pval = obj->pval; disturb(player, 0); } } } } }
/** * Put a rune autoinscription on all available objects */ void rune_autoinscribe(int i) { struct object *obj; int py = player->py; int px = player->px; /* Check the player knows the rune */ if (!player_knows_rune(player, i)) { return; } /* Autoinscribe each object on the ground */ if (cave) for (obj = square_object(cave, py, px); obj; obj = obj->next) if (object_has_rune(obj, i)) rune_add_autoinscription(obj, i); /* Autoinscribe each object in the inventory */ for (obj = player->gear; obj; obj = obj->next) if (object_has_rune(obj, i)) rune_add_autoinscription(obj, i); }
/** * Determine if a given location is "interesting" */ bool target_accept(int y, int x) { struct loc grid = loc(x, y); struct object *obj; /* Player grids are always interesting */ if (square(cave, grid).mon < 0) return true; /* Handle hallucination */ if (player->timed[TMD_IMAGE]) return false; /* Obvious monsters */ if (square(cave, grid).mon > 0) { struct monster *mon = square_monster(cave, grid); if (monster_is_obvious(mon)) { return true; } } /* Traps */ if (square_isvisibletrap(cave, grid)) return true; /* Scan all objects in the grid */ for (obj = square_object(player->cave, grid); obj; obj = obj->next) { /* Memorized object */ if ((obj->kind == unknown_item_kind) || !ignore_known_item_ok(obj)) { return true; } } /* Interesting memorized features */ if (square_isknown(cave, grid) && square_isinteresting(cave, grid)) { return true; } /* Nope */ return false; }
/** * 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 *m_ptr = square_monster(cave, y, x); /* Visible monsters */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE) && !mflag_has(m_ptr->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_isboring(cave, y, x)) return (TRUE); /* Nope */ return (FALSE); }
/** * Determine if a given location is "interesting" */ bool target_accept(int y, int x) { struct object *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) { struct monster *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_k, y, x); obj; obj = obj->next) /* Memorized object */ if (!ignore_known_item_ok(obj)) return (true); /* Interesting memorized features */ if (square_isknown(cave, y, x) && square_isinteresting(cave, y, x)) return (true); /* Nope */ return (false); }
/** * Return TRUE if the given object is on the floor at this grid */ bool square_holds_object(struct chunk *c, int y, int x, struct object *obj) { return pile_contains(square_object(c, y, x), obj); }
/** * True if the square is empty (an open square without any items). */ bool square_isempty(struct chunk *c, int y, int x) { if (square_isplayertrap(c, y, x)) return FALSE; return square_isopen(c, y, x) && !square_object(c, 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; } }
/** * True if the square is a floor square without items. */ bool square_canputitem(struct chunk *c, int y, int x) { return square_isfloor(c, y, x) && !square_object(c, y, x); }
/** * 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); }
/** * Handle "target" and "look". * * Note that this code can be called from "get_aim_dir()". * * Currently, when "flag" is true, that is, when * "interesting" grids are being used, and a directional key is used, we * only scroll by a single panel, in the direction requested, and check * for any interesting grids on that panel. The "correct" solution would * actually involve scanning a larger set of grids, including ones in * panels which are adjacent to the one currently scanned, but this is * overkill for this function. XXX XXX * * Hack -- targetting/observing an "outer border grid" may induce * problems, so this is not currently allowed. * * The player can use the direction keys to move among "interesting" * grids in a heuristic manner, or the "space", "+", and "-" keys to * move through the "interesting" grids in a sequential manner, or * can enter "location" mode, and use the direction keys to move one * grid at a time in any direction. The "t" (set target) command will * only target a monster (as opposed to a location) if the monster is * target_able and the "interesting" mode is being used. * * The current grid is described using the "look" method above, and * a new command may be entered at any time, but note that if the * "TARGET_LOOK" bit flag is set (or if we are in "location" mode, * where "space" has no obvious meaning) then "space" will scan * through the description of the current grid until done, instead * of immediately jumping to the next "interesting" grid. This * allows the "target" command to retain its old semantics. * * The "*", "+", and "-" keys may always be used to jump immediately * to the next (or previous) interesting grid, in the proper mode. * * The "return" key may always be used to scan through a complete * grid description (forever). * * This command will cancel any old target, even if used from * inside the "look" command. * * * 'mode' is one of TARGET_LOOK or TARGET_KILL. * 'x' and 'y' are the initial position of the target to be highlighted, * or -1 if no location is specified. * Returns TRUE if a target has been successfully set, FALSE otherwise. */ bool target_set_interactive(int mode, int x, int y) { int py = player->py; int px = player->px; int path_n; struct loc path_g[256]; int i, d, m, t, bd; int wid, hgt, help_prompt_loc; bool done = FALSE; bool flag = TRUE; bool help = FALSE; ui_event press; /* These are used for displaying the path to the target */ wchar_t *path_char = mem_zalloc(z_info->max_range * sizeof(wchar_t)); int *path_attr = mem_zalloc(z_info->max_range * sizeof(int)); struct point_set *targets; /* If we haven't been given an initial location, start on the player, otherwise honour it by going into "free targetting" mode. */ if (x == -1 || y == -1) { x = player->px; y = player->py; } else { flag = FALSE; } /* Cancel target */ target_set_monster(0); /* Calculate the window location for the help prompt */ Term_get_size(&wid, &hgt); help_prompt_loc = hgt - 1; /* Display the help prompt */ prt("Press '?' for help.", help_prompt_loc, 0); /* Prepare the target set */ targets = target_get_monsters(mode); /* Start near the player */ m = 0; /* Interact */ while (!done) { bool path_drawn = FALSE; /* Interesting grids if chosen and there are any, otherwise arbitrary */ if (flag && point_set_size(targets)) { y = targets->pts[m].y; x = targets->pts[m].x; /* Adjust panel if needed */ if (adjust_panel_help(y, x, help)) handle_stuff(player); /* Update help */ if (help) { bool good_target = target_able(square_monster(cave, y, x)); target_display_help(good_target, !(flag && point_set_size(targets))); } /* Find the path. */ path_n = project_path(path_g, z_info->max_range, py, px, y, x, PROJECT_THRU); /* Draw the path in "target" mode. If there is one */ if (mode & (TARGET_KILL)) path_drawn = draw_path(path_n, path_g, path_char, path_attr, py, px); /* Describe and Prompt */ press = target_set_interactive_aux(y, x, mode); /* Remove the path */ if (path_drawn) load_path(path_n, path_g, path_char, path_attr); /* Assume no "direction" */ d = 0; /* Analyze */ if (press.type == EVT_MOUSE) { if (press.mouse.button == 3) { /* give the target selection command */ press.mouse.button = 2; press.mouse.mods = KC_MOD_CONTROL; } if (press.mouse.button == 2) { y = KEY_GRID_Y(press); x = KEY_GRID_X(press); if (press.mouse.mods & KC_MOD_CONTROL) { /* same as keyboard target selection command below */ struct monster *m = square_monster(cave, y, x); if (target_able(m)) { /* Set up target information */ monster_race_track(player->upkeep, m->race); health_track(player->upkeep, m); target_set_monster(m); done = TRUE; } else { bell("Illegal target!"); } } else if (press.mouse.mods & KC_MOD_ALT) { /* go to spot - same as 'g' command below */ cmdq_push(CMD_PATHFIND); cmd_set_arg_point(cmdq_peek(), "point", y, x); done = TRUE; } else { /* cancel look mode */ done = TRUE; } } else { y = KEY_GRID_Y(press); x = KEY_GRID_X(press); if (square_monster(cave, y, x) || square_object(cave, y, x)) { /* reset the flag, to make sure we stay in this * mode if something is actually there */ flag = FALSE; /* scan the interesting list and see if there is * anything here */ for (i = 0; i < point_set_size(targets); i++) { if ((y == targets->pts[i].y) && (x == targets->pts[i].x)) { m = i; flag = TRUE; break; } } } else { flag = FALSE; } } } else switch (press.key.code) { case ESCAPE: case 'q': { done = TRUE; break; } case ' ': case '*': case '+': { if (++m == point_set_size(targets)) m = 0; break; } case '-': { if (m-- == 0) m = point_set_size(targets) - 1; break; } case 'p': { /* Recenter around player */ verify_panel(); /* Handle stuff */ handle_stuff(player); y = player->py; x = player->px; } case 'o': { flag = FALSE; break; } case 'm': { break; } case 't': case '5': case '0': case '.': { struct monster *m = square_monster(cave, y, x); if (target_able(m)) { health_track(player->upkeep, m); target_set_monster(m); done = TRUE; } else { bell("Illegal target!"); } break; } case 'g': { cmdq_push(CMD_PATHFIND); cmd_set_arg_point(cmdq_peek(), "point", y, x); done = TRUE; break; } case '?': { help = !help; /* Redraw main window */ player->upkeep->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIP); Term_clear(); handle_stuff(player); if (!help) prt("Press '?' for help.", help_prompt_loc, 0); break; } default: { /* Extract direction */ d = target_dir(press.key); /* Oops */ if (!d) bell("Illegal command for target mode!"); break; } } /* Hack -- move around */ if (d) { int old_y = targets->pts[m].y; int old_x = targets->pts[m].x; /* Find a new monster */ i = target_pick(old_y, old_x, ddy[d], ddx[d], targets); /* Scroll to find interesting grid */ if (i < 0) { int old_wy = Term->offset_y; int old_wx = Term->offset_x; /* Change if legal */ if (change_panel(d)) { /* Recalculate interesting grids */ point_set_dispose(targets); targets = target_get_monsters(mode); /* Find a new monster */ i = target_pick(old_y, old_x, ddy[d], ddx[d], targets); /* Restore panel if needed */ if ((i < 0) && modify_panel(Term, old_wy, old_wx)) { /* Recalculate interesting grids */ point_set_dispose(targets); targets = target_get_monsters(mode); } /* Handle stuff */ handle_stuff(player); } } /* Use interesting grid if found */ if (i >= 0) m = i; } } else { /* Update help */ if (help) { bool good_target = target_able(square_monster(cave, y, x)); target_display_help(good_target, !(flag && point_set_size(targets))); } /* Find the path. */ path_n = project_path(path_g, z_info->max_range, py, px, y, x, PROJECT_THRU); /* Draw the path in "target" mode. If there is one */ if (mode & (TARGET_KILL)) path_drawn = draw_path (path_n, path_g, path_char, path_attr, py, px); /* Describe and Prompt (enable "TARGET_LOOK") */ press = target_set_interactive_aux(y, x, mode | TARGET_LOOK); /* Remove the path */ if (path_drawn) load_path(path_n, path_g, path_char, path_attr); /* Assume no direction */ d = 0; /* Analyze the keypress */ if (press.type == EVT_MOUSE) { if (press.mouse.button == 3) { /* give the target selection command */ press.mouse.button = 2; press.mouse.mods = KC_MOD_CONTROL; } if (press.mouse.button == 2) { if (mode & (TARGET_KILL)) { if ((y == KEY_GRID_Y(press)) && (x == KEY_GRID_X(press))) { d = -1; } } y = KEY_GRID_Y(press); x = KEY_GRID_X(press); if (press.mouse.mods & KC_MOD_CONTROL) { /* same as keyboard target selection command below */ target_set_location(y, x); done = TRUE; } else if (press.mouse.mods & KC_MOD_ALT) { /* go to spot - same as 'g' command below */ cmdq_push(CMD_PATHFIND); cmd_set_arg_point(cmdq_peek(), "point", y, x); done = TRUE; } else { /* cancel look mode */ done = TRUE; if (d == -1) { target_set_location(y, x); d = 0; } } } else { int dungeon_hgt = cave->height; int dungeon_wid = cave->width; y = KEY_GRID_Y(press); x = KEY_GRID_X(press); if (press.mouse.y <= 1) { /* move the screen north */ y--; } else if (press.mouse.y >= (Term->hgt - 2)) { /* move the screen south */ y++; } else if (press.mouse.x <= COL_MAP) { /* move the screen in west */ x--; } else if (press.mouse.x >= (Term->wid - 2)) { /* move the screen east */ x++; } if (y < 0) y = 0; if (x < 0) x = 0; if (y >= dungeon_hgt-1) y = dungeon_hgt-1; if (x >= dungeon_wid-1) x = dungeon_wid-1; /* Adjust panel if needed */ if (adjust_panel_help(y, x, help)) { /* Handle stuff */ handle_stuff(player); /* Recalculate interesting grids */ point_set_dispose(targets); targets = target_get_monsters(mode); } if (square_monster(cave, y, x) || square_object(cave, y, x)) { /* scan the interesting list and see if there in * anything here */ for (i = 0; i < point_set_size(targets); i++) { if ((y == targets->pts[i].y) && (x == targets->pts[i].x)) { m = i; flag = TRUE; break; } } } else { flag = FALSE; } } } else switch (press.key.code) { case ESCAPE: case 'q': { done = TRUE; break; } case ' ': case '*': case '+': case '-': { break; } case 'p': { /* Recenter around player */ verify_panel(); /* Handle stuff */ handle_stuff(player); y = player->py; x = player->px; } case 'o': { break; } case 'm': { flag = TRUE; m = 0; bd = 999; /* Pick a nearby monster */ for (i = 0; i < point_set_size(targets); i++) { t = distance(y, x, targets->pts[i].y, targets->pts[i].x); /* Pick closest */ if (t < bd) { m = i; bd = t; } } /* Nothing interesting */ if (bd == 999) flag = FALSE; break; } case 't': case '5': case '0': case '.': { target_set_location(y, x); done = TRUE; break; } case 'g': { cmdq_push(CMD_PATHFIND); cmd_set_arg_point(cmdq_peek(), "point", y, x); done = TRUE; break; } case '?': { help = !help; /* Redraw main window */ player->upkeep->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIP); Term_clear(); handle_stuff(player); if (!help) prt("Press '?' for help.", help_prompt_loc, 0); break; } default: { /* Extract a direction */ d = target_dir(press.key); /* Oops */ if (!d) bell("Illegal command for target mode!"); break; } } /* Handle "direction" */ if (d) { int dungeon_hgt = cave->height; int dungeon_wid = cave->width; /* Move */ x += ddx[d]; y += ddy[d]; /* Slide into legality */ if (x >= dungeon_wid - 1) x--; else if (x <= 0) x++; /* Slide into legality */ if (y >= dungeon_hgt - 1) y--; else if (y <= 0) y++; /* Adjust panel if needed */ if (adjust_panel_help(y, x, help)) { /* Handle stuff */ handle_stuff(player); /* Recalculate interesting grids */ point_set_dispose(targets); targets = target_get_monsters(mode); } } } } /* Forget */ point_set_dispose(targets); /* Redraw as necessary */ if (help) { player->upkeep->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIP); Term_clear(); } else { prt("", 0, 0); prt("", help_prompt_loc, 0); player->upkeep->redraw |= (PR_DEPTH | PR_STATUS); } /* Recenter around player */ verify_panel(); /* Handle stuff */ handle_stuff(player); mem_free(path_attr); mem_free(path_char); /* Failure to set target */ if (!target_is_set()) return (FALSE); /* Success */ return (TRUE); }
/** * 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. */ }
/** * 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; } }
/** * 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; } } }
/** * Pick up objects and treasure on the floor. -LM- * * Scan the list of objects in that floor grid. Pick up gold automatically. * Pick up objects automatically until backpack space is full if * auto-pickup option is on, otherwise, store objects on * floor in an array, and tally both how many there are and can be picked up. * * If not picking up anything, indicate objects on the floor. Do the same * thing if we don't have room for anything. * * Pick up multiple objects using Tim Baker's menu system. Recursively * call this function (forcing menus for any number of objects) until * objects are gone, backpack is full, or player is satisfied. * * We keep track of number of objects picked up to calculate time spent. * This tally is incremented even for automatic pickup, so we are careful * (in "dungeon.c" and elsewhere) to handle pickup as either a separate * automated move or a no-cost part of the stay still or 'g'et command. * * Note the lack of chance for the character to be disturbed by unmarked * objects. They are truly "unknown". * * \param obj is the object to pick up. * \param menu is whether to present a menu to the player */ static byte player_pickup_item(struct object *obj, bool menu) { int py = player->py; int px = player->px; struct object *current = NULL; int floor_max = z_info->floor_size + 1; struct object **floor_list = mem_zalloc(floor_max * sizeof(*floor_list)); int floor_num = 0; int i; int can_pickup = 0; bool call_function_again = FALSE; bool domsg = TRUE; /* Objects picked up. Used to determine time cost of command. */ byte objs_picked_up = 0; /* Always pickup gold, effortlessly */ player_pickup_gold(); /* Nothing else to pick up -- return */ if (!square_object(cave, py, px)) { mem_free(floor_list); return objs_picked_up; } /* Tally objects that can be picked up.*/ floor_num = scan_floor(floor_list, floor_max, py, px, 0x08, NULL); for (i = 0; i < floor_num; i++) if (inven_carry_okay(floor_list[i])) can_pickup++; if (!can_pickup) { /* Can't pick up, but probably want to know what's there. */ event_signal(EVENT_SEEFLOOR); mem_free(floor_list); return objs_picked_up; } /* Use the item that we are given, if it is on the floor. */ if (square_holds_object(cave, py, px, obj)) current = obj; /* Use a menu interface for multiple objects, or pickup single objects */ if (!menu && !current) { if (floor_num > 1) menu = TRUE; else current = floor_list[0]; } /* Display a list if requested. */ if (menu && !current) { const char *q, *s; struct object *obj1; /* Get an object or exit. */ q = "Get which item?"; s = "You see nothing there."; if (!get_item(&obj1, q, s, CMD_PICKUP, inven_carry_okay, USE_FLOOR)) { mem_free(floor_list); return (objs_picked_up); } current = obj1; call_function_again = TRUE; /* With a list, we do not need explicit pickup messages */ domsg = FALSE; } /* Pick up object, if legal */ if (current) { /* Pick up the object */ player_pickup_aux(current, domsg); /* Indicate an object picked up. */ objs_picked_up = 1; } /* * If requested, call this function recursively. Count objects picked * up. Force the display of a menu in all cases. */ if (call_function_again) objs_picked_up += player_pickup_item(NULL, TRUE); mem_free(floor_list); /* Indicate how many objects have been picked up. */ return (objs_picked_up); }
/** * 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); } }
/** * Excise an entire floor pile. */ void square_excise_pile(struct chunk *c, int y, int x) { object_pile_free(square_object(c, y, x)); c->squares[y][x].obj = NULL; }
/** * True if the square is empty (an open square without any items). */ bool square_isempty(struct chunk *c, int y, int x) { return square_isopen(c, y, x) && !square_object(c, y, x); }
/** * 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); }
/** * Draw a visible path over the squares between (x1,y1) and (x2,y2). * * The path consists of "*", which are white except where there is a * monster, object or feature in the grid. * * This routine has (at least) three weaknesses: * - remembered objects/walls which are no longer present are not shown, * - squares which (e.g.) the player has walked through in the dark are * treated as unknown space. * - walls which appear strange due to hallucination aren't treated correctly. * * The first two result from information being lost from the dungeon arrays, * which requires changes elsewhere */ static int draw_path(u16b path_n, struct loc *path_g, wchar_t *c, int *a, int y1, int x1) { int i; bool on_screen; /* No path, so do nothing. */ if (path_n < 1) return 0; /* The starting square is never drawn, but notice if it is being * displayed. In theory, it could be the last such square. */ on_screen = panel_contains(y1, x1); /* Draw the path. */ for (i = 0; i < path_n; i++) { byte colour; /* Find the co-ordinates on the level. */ int y = path_g[i].y; int x = path_g[i].x; struct monster *mon = square_monster(cave, y, x); struct object *obj = square_object(cave, y, x); /* * As path[] is a straight line and the screen is oblong, * there is only section of path[] on-screen. * If the square being drawn is visible, this is part of it. * If none of it has been drawn, continue until some of it * is found or the last square is reached. * If some of it has been drawn, finish now as there are no * more visible squares to draw. */ if (panel_contains(y,x)) on_screen = TRUE; else if (on_screen) break; else continue; /* Find the position on-screen */ move_cursor_relative(y,x); /* This square is being overwritten, so save the original. */ Term_what(Term->scr->cx, Term->scr->cy, a+i, c+i); /* Choose a colour. */ if (mon && mflag_has(mon->mflag, MFLAG_VISIBLE)) { /* Mimics act as objects */ if (rf_has(mon->race->flags, RF_UNAWARE)) colour = COLOUR_YELLOW; else /* Visible monsters are red. */ colour = COLOUR_L_RED; } else if (obj && obj->marked) /* Known objects are yellow. */ colour = COLOUR_YELLOW; else if ((!square_isprojectable(cave, y,x) && square_ismark(cave, y, x)) || square_isseen(cave, y, x)) /* Known walls are blue. */ colour = COLOUR_BLUE; else if (!square_ismark(cave, y, x) && !square_isseen(cave, y, x)) /* Unknown squares are grey. */ colour = COLOUR_L_DARK; else /* Unoccupied squares are white. */ colour = COLOUR_WHITE; /* Draw the path segment */ (void)Term_addch(colour, L'*'); } return i; }