/* Destroy Traps (and Locks) */ static void project_feature_handler_KILL_TRAP(project_feature_handler_context_t *context) { const int x = context->x; const int y = context->y; /* Reveal secret doors */ if (square_issecretdoor(cave, y, x)) { place_closed_door(cave, y, x); /* Check line of sight */ if (square_isseen(cave, y, x)) context->obvious = true; } /* Destroy traps, unlock doors */ if (square_istrap(cave, y, x)) { /* Check line of sight */ if (square_isview(cave, y, x)) { msg("There is a bright flash of light!"); context->obvious = true; } /* Destroy the trap */ square_destroy_trap(cave, y, x); } else if (square_islockeddoor(cave, y, x)) { /* Unlock the door */ square_unlock_door(cave, y, x); /* Check line of sound */ if (square_isview(cave, y, x)) { msg("Click!"); context->obvious = true; } } }
/** * Remove traps. * * If called with t_idx < 0, will remove all traps in the location given. * Otherwise, will remove all traps with the given kind. * * Return TRUE if no traps now exist in this grid. */ bool square_remove_trap(struct chunk *c, int y, int x, bool domsg, int t_idx) { bool trap_exists; struct trap **trap_slot = &c->squares[y][x].trap; struct trap *next_trap; /* Look at the traps in this grid */ while (*trap_slot) { /* Get the next trap (may be NULL) */ next_trap = (*trap_slot)->next; /* If called with a specific index, skip others */ if ((t_idx >= 0) && (t_idx != (*trap_slot)->t_idx)) { if (!next_trap) break; trap_slot = &next_trap; continue; } /* Remove it */ remove_trap_aux(c, *trap_slot, y, x, domsg); /* Replace with the next trap */ *trap_slot = next_trap; } /* Refresh grids that the character can see */ if (square_isseen(c, y, x)) square_light_spot(c, y, x); /* Verify traps (remove marker if appropriate) */ trap_exists = square_verify_trap(c, y, x, 0); /* Report whether any traps exist in this grid */ return (!trap_exists); }
const char *square_apparent_name(struct chunk *c, struct player *p, int y, int x) { int f = f_info[c->squares[y][x].feat].mimic; if (!square_ismark(c, y, x) && !square_isseen(c, y, x)) return "unknown grid"; return f_info[f].name; }
/** * True if the player's current target is in LOS. */ bool target_sighted(void) { return target_okay() && panel_contains(target_y, target_x) && /* either the target is a grid and is visible, or it is a monster * that is visible */ ((!target_who && square_isseen(cave, target_y, target_x)) || (target_who && mflag_has(target_who->mflag, MFLAG_VISIBLE))); }
/** * 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); }
/** * True if the player's current target is in LOS. */ bool target_sighted(void) { return target_okay() && panel_contains(target.grid.y, target.grid.x) && /* either the target is a grid and is visible, or it is a monster * that is visible */ ((!target.midx && square_isseen(cave, target.grid)) || (target.midx && monster_is_visible(cave_monster(cave, target.midx)))); }
/** * 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); }
/** * Remove traps. * * If called with t_idx < 0, will remove all traps in the location given. * Otherwise, will remove all traps with the given kind. * * Return true if no traps now exist in this grid. */ bool square_set_trap_timeout(struct chunk *c, int y, int x, bool domsg, int t_idx, int time) { bool trap_exists; struct trap *current_trap = NULL; /* Bounds check */ assert(square_in_bounds(c, y, x)); /* Look at the traps in this grid */ current_trap = c->squares[y][x].trap; while (current_trap) { /* Get the next trap (may be NULL) */ struct trap *next_trap = current_trap->next; /* If called with a specific index, skip others */ if ((t_idx >= 0) && (t_idx != current_trap->t_idx)) { if (!next_trap) break; current_trap = next_trap; continue; } /* Set the timer */ current_trap->timeout = time; /* Message if requested */ msg("You have disabled the %s.", current_trap->kind->name); /* Replace with the next trap */ current_trap = next_trap; } /* Refresh grids that the character can see */ if (square_isseen(c, y, x)) square_light_spot(c, y, x); /* Verify traps (remove marker if appropriate) */ trap_exists = square_verify_trap(c, y, x, 0); /* Report whether any traps exist in this grid */ return (!trap_exists); }
/** * 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) { /* Require "seen" flag and the current level */ if (c != cave) return; if (!square_isseen(c, y, x)) return; /* Make the player know precisely what is on this grid */ square_know_pile(c, y, x); /* Notice traps */ if (square_issecrettrap(c, y, x)) { square_reveal_trap(c, y, x, false, true); } if (square_isknown(c, y, x)) return; /* Memorize this grid */ square_memorize(c, y, x); }
/** * Remove traps. * * If called with t_idx < 0, will remove all traps in the location given. * Otherwise, will remove all traps with the given kind. * * Return true if traps were removed. */ bool square_remove_trap(struct chunk *c, int y, int x, int t_idx_remove) { assert(square_in_bounds(c, y, x)); bool removed = false; /* Look at the traps in this grid */ struct trap *prev_trap = NULL; struct trap *trap = c->squares[y][x].trap; while (trap) { struct trap *next_trap = trap->next; if (t_idx_remove == trap->t_idx) { mem_free(trap); removed = true; if (prev_trap) { prev_trap->next = next_trap; } else { c->squares[y][x].trap = next_trap; } break; } prev_trap = trap; trap = next_trap; } /* Refresh grids that the character can see */ if (square_isseen(c, y, x)) square_light_spot(c, y, x); (void)square_verify_trap(c, y, x, 0); return removed; }
/** * Remove all traps from a grid. * * Return true if traps were removed. */ bool square_remove_all_traps(struct chunk *c, int y, int x) { assert(square_in_bounds(c, y, x)); struct trap *trap = c->squares[y][x].trap; bool were_there_traps = trap == NULL ? false : true; while (trap) { struct trap *next_trap = trap->next; mem_free(trap); trap = next_trap; } c->squares[y][x].trap = NULL; /* Refresh grids that the character can see */ if (square_isseen(c, y, x)) { square_light_spot(c, y, x); } (void)square_verify_trap(c, y, x, 0); return were_there_traps; }
/** * Try to break a glyph. */ static bool process_monster_glyph(struct chunk *c, struct monster *mon, int nx, int ny) { assert(square_iswarded(c, ny, nx)); /* Break the ward */ if (randint1(z_info->glyph_hardness) < mon->race->level) { /* Describe observable breakage */ if (square_isseen(c, ny, nx)) { msg("The rune of protection is broken!"); /* Forget the rune */ square_forget(c, ny, nx); } /* Break the rune */ square_remove_ward(c, ny, nx); return true; } /* Unbroken ward - can't move */ 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); }
/** * This is a helper function used by do_cmd_throw and do_cmd_fire. * * It abstracts out the projectile path, display code, identify and clean up * logic, while using the 'attack' parameter to do work particular to each * kind of attack. */ static void ranged_helper(struct object *obj, int dir, int range, int shots, ranged_attack attack) { int i, j; char o_name[80]; int path_n; struct loc path_g[256]; /* Start at the player */ int x = player->px; int y = player->py; /* Predict the "target" location */ int ty = y + 99 * ddy[dir]; int tx = x + 99 * ddx[dir]; bool hit_target = FALSE; bool none_left = FALSE; struct object *missile; /* Check for target validity */ if ((dir == 5) && target_okay()) { int taim; target_get(&tx, &ty); taim = distance(y, x, ty, tx); if (taim > range) { char msg[80]; strnfmt(msg, sizeof(msg), "Target out of range by %d squares. Fire anyway? ", taim - range); if (!get_check(msg)) return; } } /* Sound */ sound(MSG_SHOOT); /* Describe the object */ object_desc(o_name, sizeof(o_name), obj, ODESC_FULL | ODESC_SINGULAR); /* Actually "fire" the object -- Take a partial turn */ player->upkeep->energy_use = (z_info->move_energy / shots); /* Calculate the path */ path_n = project_path(path_g, range, y, x, ty, tx, 0); /* Hack -- Handle stuff */ handle_stuff(player); /* Start at the player */ x = player->px; y = player->py; /* Project along the path */ for (i = 0; i < path_n; ++i) { struct monster *mon = NULL; int ny = path_g[i].y; int nx = path_g[i].x; bool see = square_isseen(cave, ny, nx); /* Stop before hitting walls */ if (!(square_ispassable(cave, ny, nx)) && !(square_isprojectable(cave, ny, nx))) break; /* Advance */ x = nx; y = ny; /* Tell the UI to display the missile */ event_signal_missile(EVENT_MISSILE, obj, see, y, x); /* Try the attack on the monster at (x, y) if any */ mon = square_monster(cave, y, x); if (mon) { int visible = mflag_has(mon->mflag, MFLAG_VISIBLE); bool fear = FALSE; const char *note_dies = monster_is_unusual(mon->race) ? " is destroyed." : " dies."; struct attack_result result = attack(obj, y, x); int dmg = result.dmg; u32b msg_type = result.msg_type; char hit_verb[20]; my_strcpy(hit_verb, result.hit_verb, sizeof(hit_verb)); mem_free(result.hit_verb); if (result.success) { hit_target = TRUE; object_notice_attack_plusses(obj); /* Learn by use for other equipped items */ equip_notice_to_hit_on_attack(player); /* No negative damage; change verb if no damage done */ if (dmg <= 0) { dmg = 0; msg_type = MSG_MISS; my_strcpy(hit_verb, "fails to harm", sizeof(hit_verb)); } if (!visible) { /* Invisible monster */ msgt(MSG_SHOOT_HIT, "The %s finds a mark.", o_name); } else { for (j = 0; j < (int)N_ELEMENTS(ranged_hit_types); j++) { char m_name[80]; const char *dmg_text = ""; if (msg_type != ranged_hit_types[j].msg) continue; if (OPT(show_damage)) dmg_text = format(" (%d)", dmg); monster_desc(m_name, sizeof(m_name), mon, MDESC_OBJE); if (ranged_hit_types[j].text) msgt(msg_type, "Your %s %s %s%s. %s", o_name, hit_verb, m_name, dmg_text, ranged_hit_types[j].text); else msgt(msg_type, "Your %s %s %s%s.", o_name, hit_verb, m_name, dmg_text); } /* Track this monster */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) { monster_race_track(player->upkeep, mon->race); health_track(player->upkeep, mon); } } /* Hit the monster, check for death */ if (!mon_take_hit(mon, dmg, &fear, note_dies)) { message_pain(mon, dmg); if (fear && mflag_has(mon->mflag, MFLAG_VISIBLE)) { char m_name[80]; monster_desc(m_name, sizeof(m_name), mon, MDESC_DEFAULT); add_monster_message(m_name, mon, MON_MSG_FLEE_IN_TERROR, TRUE); } } } /* Stop the missile */ break; } /* Stop if non-projectable but passable */ if (!(square_isprojectable(cave, ny, nx))) break; } /* Get the missile */ if (object_is_carried(player, obj)) missile = gear_object_for_use(obj, 1, TRUE, &none_left); else missile = floor_object_for_use(obj, 1, TRUE, &none_left); /* Drop (or break) near that location */ drop_near(cave, missile, breakage_chance(missile, hit_target), y, x, 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(player, 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, struct grid_data *g) { struct object *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 = lookup_feat(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; if (g->in_view) { g->lighting = LIGHTING_LOS; if (!square_isglow(cave, y, x) && OPT(player, view_yellow_light)) g->lighting = LIGHTING_TORCH; /* Remember seen feature */ square_memorize(cave, y, x); } else if (!square_isknown(cave, y, x)) { g->f_idx = FEAT_NONE; } else if (square_isglow(cave, y, x)) { g->lighting = LIGHTING_LIT; } /* Use known feature */ g->f_idx = player->cave->squares[y][x].feat; if (f_info[g->f_idx].mimic) g->f_idx = lookup_feat(f_info[g->f_idx].mimic); /* There is a trap in this square */ if (square_istrap(cave, y, x) && square_isknown(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 - only if not disabled, maybe we need * a special graphic for this */ if (!trap->timeout) { g->trap = trap; break; } } trap = trap->next; } } /* Objects */ for (obj = square_object(player->cave, y, x); obj; obj = obj->next) { if (obj->kind == unknown_gold_kind) { g->unseen_money = true; } else if (obj->kind == unknown_item_kind) { g->unseen_object = true; } else if (ignore_known_item_ok(obj)) { /* Item stays hidden */ } else 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.*/ struct monster *mon = cave_monster(cave, g->m_idx); if (!monster_is_visible(mon)) 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; } }
/** * 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. */ }
/* Destroy walls (and doors) */ static void project_feature_handler_KILL_WALL(project_feature_handler_context_t *context) { const int x = context->x; const int y = context->y; /* Non-walls (etc) */ if (square_ispassable(cave, y, x)) return; /* Permanent walls */ if (square_isperm(cave, y, x)) return; /* Different treatment for different walls */ if (square_iswall(cave, y, x) && !square_hasgoldvein(cave, y, x)) { /* Message */ if (square_isseen(cave, y, x)) { msg("The wall turns into mud!"); context->obvious = true; /* Forget the wall */ square_forget(cave, y, x); } /* Destroy the wall */ square_destroy_wall(cave, y, x); } else if (square_iswall(cave, y, x) && square_hasgoldvein(cave, y, x)) { /* Message */ if (square_isseen(cave, y, x)) { msg("The vein turns into mud!"); msg("You have found something!"); context->obvious = true; /* Forget the wall */ square_forget(cave, y, x); } /* Destroy the wall */ square_destroy_wall(cave, y, x); /* Place some gold */ place_gold(cave, y, x, player->depth, ORIGIN_FLOOR); } else if (square_ismagma(cave, y, x) || square_isquartz(cave, y, x)) { /* Message */ if (square_isseen(cave, y, x)) { msg("The vein turns into mud!"); context->obvious = true; /* Forget the wall */ square_forget(cave, y, x); } /* Destroy the wall */ square_destroy_wall(cave, y, x); } else if (square_isrubble(cave, y, x)) { /* Message */ if (square_isseen(cave, y, x)) { msg("The rubble turns into mud!"); context->obvious = true; /* Forget the wall */ square_forget(cave, y, x); } /* Destroy the rubble */ square_destroy_rubble(cave, y, x); /* Hack -- place an object */ if (randint0(100) < 10){ if (square_isseen(cave, y, x)) { msg("There was something buried in the rubble!"); context->obvious = true; } place_object(cave, y, x, player->depth, false, false, ORIGIN_RUBBLE, 0); } } else if (square_isdoor(cave, y, x)) { /* Hack -- special message */ if (square_isseen(cave, y, x)) { msg("The door turns into mud!"); context->obvious = true; /* Forget the wall */ square_forget(cave, y, x); } /* Destroy the feature */ square_destroy_door(cave, y, x); } /* Update the visuals */ player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); /* Fully update the flow */ player->upkeep->update |= (PU_FORGET_FLOW | PU_UPDATE_FLOW); }
/** * 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; }