/** * Called from project() to affect the player * * Called for projections with the PROJECT_PLAY flag set, which includes * bolt, 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 * \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 * \return whether the effects were obvious * * If "r" is non-zero, then the blast was centered elsewhere; the damage * is reduced in project() before being passed in here. This can happen if a * monster breathes at the player and hits a wall instead. * * We assume the player is aware of some effect, and always return "TRUE". */ bool project_p(int who, int r, int y, int x, int dam, int typ) { bool blind, seen; bool obvious = TRUE; /* Source monster */ monster_type *m_ptr; /* Monster name (for damage) */ char killer[80]; project_player_handler_f player_handler = player_handlers[typ]; project_player_handler_context_t context = { who, r, y, x, dam, typ, obvious, }; /* No player here */ if (!(cave->squares[y][x].mon < 0)) return (FALSE); /* Never affect projector */ if (cave->squares[y][x].mon == who) return (FALSE); /* Source monster */ m_ptr = cave_monster(cave, who); /* Player blind-ness */ blind = (player->timed[TMD_BLIND] ? TRUE : FALSE); /* Extract the "see-able-ness" */ seen = (!blind && mflag_has(m_ptr->mflag, MFLAG_VISIBLE)); /* Get the monster's real name */ monster_desc(killer, sizeof(killer), m_ptr, MDESC_DIED_FROM); /* Let player know what is going on */ if (!seen) msg("You are hit by %s!", gf_blind_desc(typ)); /* Adjust damage for resistance, immunity or vulnerability, and apply it */ dam = adjust_dam(player, typ, dam, RANDOMISE, player->state.el_info[typ].res_level); if (dam) take_hit(player, dam, killer); /* Handle side effects */ if (player_handler != NULL) player_handler(&context); obvious = context.obvious; /* Disturb */ disturb(player, 1); /* Return "Anything seen?" */ return (obvious); }
/** * Deletes a monster by index. * * When a monster is deleted, all of its objects are deleted. */ void delete_monster_idx(int m_idx) { int x, y; struct object *obj; struct monster *mon; assert(m_idx > 0); mon = cave_monster(cave, m_idx); /* Monster location */ y = mon->fy; x = mon->fx; assert(square_in_bounds(cave, y, x)); /* Hack -- Reduce the racial counter */ mon->race->cur_num--; /* Hack -- count the number of "reproducers" */ if (rf_has(mon->race->flags, RF_MULTIPLY)) num_repro--; /* Hack -- remove target monster */ if (target_get_monster() == mon) target_set_monster(NULL); /* Hack -- remove tracked monster */ if (player->upkeep->health_who == mon) health_track(player->upkeep, NULL); /* Monster is gone */ cave->squares[y][x].mon = 0; /* Delete objects */ obj = mon->held_obj; while (obj) { struct object *next = obj->next; /* Preserve unseen artifacts (we assume they were created as this * monster's drop) - this will cause unintended behaviour in preserve * off mode if monsters can pick up artifacts */ if (obj->artifact && !(obj->known && obj->known->artifact)) obj->artifact->created = false; /* Delete the object */ delist_object(cave, obj); object_delete(&obj); obj = next; } /* Delete mimicked objects */ if (mon->mimicked_obj) { square_excise_object(cave, y, x, mon->mimicked_obj); delist_object(cave, mon->mimicked_obj); object_delete(&mon->mimicked_obj); } /* Wipe the Monster */ memset(mon, 0, sizeof(struct monster)); /* Count monsters */ cave->mon_cnt--; /* Visual update */ square_light_spot(cave, y, x); }
/** * Move player in the given direction. * * This routine should only be called when energy has been expended. * * Note that this routine handles monsters in the destination grid, * and also handles attempting to move into walls/doors/rubble/etc. */ void move_player(int dir, bool disarm) { int py = player->py; int px = player->px; int y = py + ddy[dir]; int x = px + ddx[dir]; int m_idx = cave->squares[y][x].mon; struct monster *mon = cave_monster(cave, m_idx); bool alterable = (square_isknowntrap(cave, y, x) || square_iscloseddoor(cave, y, x)); /* Attack monsters, alter traps/doors on movement, hit obstacles or move */ if (m_idx > 0) { /* Mimics surprise the player */ if (is_mimicking(mon)) { become_aware(mon); /* Mimic wakes up */ mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false); } else { py_attack(y, x); } } else if (disarm && square_isknown(cave, y, x) && alterable) { /* Auto-repeat if not already repeating */ if (cmd_get_nrepeats() == 0) cmd_set_repeat(99); do_cmd_alter_aux(dir); } else if (player->upkeep->running && square_isknowntrap(cave, y, x)) { /* Stop running before known traps */ disturb(player, 0); } else if (!square_ispassable(cave, y, x)) { /* Disturb the player */ disturb(player, 0); /* Notice unknown obstacles, mention known obstacles */ if (!square_isknown(cave, y, x)) { if (square_isrubble(cave, y, x)) { msgt(MSG_HITWALL, "You feel a pile of rubble blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } else if (square_iscloseddoor(cave, y, x)) { msgt(MSG_HITWALL, "You feel a door blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } else { msgt(MSG_HITWALL, "You feel a wall blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } } else { if (square_isrubble(cave, y, x)) msgt(MSG_HITWALL, "There is a pile of rubble blocking your way."); else if (square_iscloseddoor(cave, y, x)) msgt(MSG_HITWALL, "There is a door blocking your way."); else msgt(MSG_HITWALL, "There is a wall blocking your way."); } } else { /* See if trap detection status will change */ bool old_dtrap = square_isdtrap(cave, py, px); bool new_dtrap = square_isdtrap(cave, y, x); /* Note the change in the detect status */ if (old_dtrap != new_dtrap) player->upkeep->redraw |= (PR_DTRAP); /* Disturb player if the player is about to leave the area */ if (player->upkeep->running && !player->upkeep->running_firststep && old_dtrap && !new_dtrap) { disturb(player, 0); return; } /* Move player */ monster_swap(py, px, y, x); /* New location */ y = py = player->py; x = px = player->px; /* Searching */ if (player->searching || (player->state.skills[SKILL_SEARCH_FREQUENCY] >= 50) || one_in_(50 - player->state.skills[SKILL_SEARCH_FREQUENCY])) search(false); /* Handle store doors, or notice objects */ if (square_isshop(cave, player->py, player->px)) { /* Disturb */ disturb(player, 0); event_signal(EVENT_ENTER_STORE); event_remove_handler_type(EVENT_ENTER_STORE); event_signal(EVENT_USE_STORE); event_remove_handler_type(EVENT_USE_STORE); event_signal(EVENT_LEAVE_STORE); event_remove_handler_type(EVENT_LEAVE_STORE); } else { /* Know objects, queue autopickup */ floor_pile_know(cave, player->py, player->px); cmdq_push(CMD_AUTOPICKUP); } /* Discover invisible traps, set off visible ones */ if (square_issecrettrap(cave, y, x)) { /* Disturb */ disturb(player, 0); /* Hit the trap. */ hit_trap(y, x); } else if (square_isknowntrap(cave, y, x)) { /* Disturb */ disturb(player, 0); /* Hit the trap */ hit_trap(y, x); } } player->upkeep->running_firststep = false; }
/** * 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. */ }
/** * Process a monster spell * * \param spell is the monster spell flag (RSF_FOO) * \param m_idx is the attacking monster * \param seen is whether the player can see the monster at this moment */ void do_mon_spell(int spell, int m_idx, bool seen) { const struct mon_spell *rs_ptr = &mon_spell_table[spell]; monster_type *m_ptr = cave_monster(cave, m_idx); monster_race *r_ptr = &r_info[m_ptr->r_idx]; char m_name[80], ddesc[80]; bool hits = FALSE; int dam = 0, flag = 0, rad = 0; /* Extract the monster level */ int rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1); /* Get the monster name (or "it") */ monster_desc(m_name, sizeof(m_name), m_ptr, 0x00); /* See if it hits */ if (rs_ptr->hit == 100) hits = TRUE; else if (rs_ptr->hit == 0) hits = FALSE; else hits = check_hit(p_ptr, rs_ptr->hit, rlev); /* Tell the player what's going on */ disturb(p_ptr, 1,0); if (!seen) msg("Something %s.", rs_ptr->blind_verb); else if (!hits) { msg("%^s %s %s, but misses.", m_name, rs_ptr->verb, rs_ptr->desc); return; } else if (rs_ptr->msgt) msgt(rs_ptr->msgt, "%^s %s %s.", m_name, rs_ptr->verb, rs_ptr->desc); else msg("%^s %s %s.", m_name, rs_ptr->verb, rs_ptr->desc); /* Try a saving throw if available */ if (rs_ptr->save && randint0(100) < p_ptr->state.skills[SKILL_SAVE]) { msg("You avoid the effects!"); return; } /* Calculate the damage */ dam = mon_spell_dam(spell, m_ptr->hp, rlev, RANDOMISE); /* Get the "died from" name in case this attack kills @ */ monster_desc(ddesc, sizeof(ddesc), m_ptr, MDESC_SHOW | MDESC_IND2); /* Display the attack, adjust for resists and apply effects */ if (rs_ptr->type & RST_BOLT) flag = PROJECT_STOP | PROJECT_KILL; else if (rs_ptr->type & (RST_BALL | RST_BREATH)) { flag = PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL; rad = rf_has(r_ptr->flags, RF_POWERFUL) ? 3 : 2; } if (rs_ptr->gf) { (void)project(m_idx, rad, p_ptr->py, p_ptr->px, dam, rs_ptr->gf, flag); monster_learn_resists(m_ptr, p_ptr, rs_ptr->gf); } else /* Note that non-projectable attacks are unresistable */ take_hit(p_ptr, dam, ddesc); do_side_effects(spell, dam, m_idx, seen); return; }
/** * Compacts and reorders the monster list. * * This function can be very dangerous, use with caution! * * When `num_to_compact` is 0, we just reorder the monsters into a more compact * order, eliminating any "holes" left by dead monsters. If `num_to_compact` is * positive, then we delete at least that many monsters and then reorder. * We try not to delete monsters that are high level or close to the player. * Each time we make a full pass through the monster list, if we haven't * deleted enough monsters, we relax our bounds a little to accept * monsters of a slightly higher level, and monsters slightly closer to * the player. */ void compact_monsters(int num_to_compact) { int m_idx, num_compacted, iter; int max_lev, min_dis, chance; /* Message (only if compacting) */ if (num_to_compact) msg("Compacting monsters..."); /* Compact at least 'num_to_compact' objects */ for (num_compacted = 0, iter = 1; num_compacted < num_to_compact; iter++) { /* Get more vicious each iteration */ max_lev = 5 * iter; /* Get closer each iteration */ min_dis = 5 * (20 - iter); /* Check all the monsters */ for (m_idx = 1; m_idx < cave_monster_max(cave); m_idx++) { monster_type *m_ptr = cave_monster(cave, m_idx); /* Skip "dead" monsters */ if (!m_ptr->race) continue; /* High level monsters start out "immune" */ if (m_ptr->race->level > max_lev) continue; /* Ignore nearby monsters */ if ((min_dis > 0) && (m_ptr->cdis < min_dis)) continue; /* Saving throw chance */ chance = 90; /* Only compact "Quest" Monsters in emergencies */ if (rf_has(m_ptr->race->flags, RF_QUESTOR) && (iter < 1000)) chance = 100; /* Try not to compact Unique Monsters */ if (rf_has(m_ptr->race->flags, RF_UNIQUE)) chance = 99; /* All monsters get a saving throw */ if (randint0(100) < chance) continue; /* Delete the monster */ delete_monster(m_ptr->fy, m_ptr->fx); /* Count the monster */ num_compacted++; } } /* Excise dead monsters (backwards!) */ for (m_idx = cave_monster_max(cave) - 1; m_idx >= 1; m_idx--) { monster_type *m_ptr = cave_monster(cave, m_idx); /* Skip real monsters */ if (m_ptr->race) continue; /* Move last monster into open hole */ compact_monsters_aux(cave_monster_max(cave) - 1, m_idx); /* Compress "cave->mon_max" */ cave->mon_max--; } }
/** * Attack the monster at the given location with a single blow. */ bool py_attack_real(int y, int x, bool *fear) { /* Information about the target of the attack */ monster_type *m_ptr = cave_monster(cave, cave->m_idx[y][x]); monster_race *r_ptr = &r_info[m_ptr->r_idx]; char m_name[80]; bool stop = FALSE; /* The weapon used */ object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD]; /* Information about the attack */ int bonus = p_ptr->state.to_h + o_ptr->to_h; int chance = p_ptr->state.skills[SKILL_TO_HIT_MELEE] + bonus * BTH_PLUS_ADJ; bool do_quake = FALSE; bool success = FALSE; /* Default to punching for one damage */ const char *hit_verb = "punch"; int dmg = 1; u32b msg_type = MSG_HIT; /* Extract monster name (or "it") */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* Auto-Recall if possible and visible */ if (m_ptr->ml) monster_race_track(m_ptr->r_idx); /* Track a new monster */ if (m_ptr->ml) health_track(p_ptr, cave->m_idx[y][x]); /* Handle player fear (only for invisible monsters) */ if (p_ptr->state.flags[OF_AFRAID]) { msgt(MSG_AFRAID, "You are too afraid to attack %s!", m_name); return FALSE; } /* Disturb the monster */ mon_clear_timed(cave->m_idx[y][x], MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE); /* See if the player hit */ success = test_hit(chance, r_ptr->ac, m_ptr->ml); /* If a miss, skip this hit */ if (!success) { msgt(MSG_MISS, "You miss %s.", m_name); return FALSE; } /* Handle normal weapon */ if (o_ptr->kind) { int i; const struct slay *best_s_ptr = NULL; hit_verb = "hit"; /* Get the best attack from all slays or * brands on all non-launcher equipment */ for (i = INVEN_LEFT; i < INVEN_TOTAL; i++) { struct object *obj = &p_ptr->inventory[i]; if (obj->kind) improve_attack_modifier(obj, m_ptr, &best_s_ptr, TRUE, FALSE); } improve_attack_modifier(o_ptr, m_ptr, &best_s_ptr, TRUE, FALSE); if (best_s_ptr != NULL) hit_verb = best_s_ptr->melee_verb; dmg = damroll(o_ptr->dd, o_ptr->ds); dmg *= (best_s_ptr == NULL) ? 1 : best_s_ptr->mult; dmg += o_ptr->to_d; dmg = critical_norm(o_ptr->weight, o_ptr->to_h, dmg, &msg_type); /* Learn by use for the weapon */ object_notice_attack_plusses(o_ptr); if (p_ptr->state.flags[OF_IMPACT] && dmg > 50) { do_quake = TRUE; wieldeds_notice_flag(OF_IMPACT); } } /* Learn by use for other equipped items */ wieldeds_notice_offweapon_attack_plusses(); /* Apply the player damage bonuses */ dmg += p_ptr->state.to_d; /* No negative damage */ if (dmg <= 0) dmg = 0; /* Tell the player what happened */ if (dmg <= 0) msgt(MSG_MISS, "You fail to harm %s.", m_name); else if (msg_type == MSG_HIT) msgt(MSG_HIT, "You %s %s.", hit_verb, m_name); else if (msg_type == MSG_HIT_GOOD) msgt(MSG_HIT_GOOD, "You %s %s. %s", hit_verb, m_name, "It was a good hit!"); else if (msg_type == MSG_HIT_GREAT) msgt(MSG_HIT_GREAT, "You %s %s. %s", hit_verb, m_name, "It was a great hit!"); else if (msg_type == MSG_HIT_SUPERB) msgt(MSG_HIT_SUPERB, "You %s %s. %s", hit_verb, m_name, "It was a superb hit!"); else if (msg_type == MSG_HIT_HI_GREAT) msgt(MSG_HIT_HI_GREAT, "You %s %s. %s", hit_verb, m_name, "It was a *GREAT* hit!"); else if (msg_type == MSG_HIT_HI_SUPERB) msgt(MSG_HIT_HI_SUPERB, "You %s %s. %s", hit_verb, m_name, "It was a *SUPERB* hit!"); /* Complex message */ if (p_ptr->wizard) msg("You do %d (out of %d) damage.", dmg, m_ptr->hp); /* Confusion attack */ if (p_ptr->confusing) { p_ptr->confusing = FALSE; msg("Your hands stop glowing."); mon_inc_timed(cave->m_idx[y][x], MON_TMD_CONF, (10 + randint0(p_ptr->lev) / 10), MON_TMD_FLG_NOTIFY); } /* Damage, check for fear and death */ stop = mon_take_hit(cave->m_idx[y][x], dmg, fear, NULL); if (stop) (*fear) = FALSE; /* Apply earthquake brand */ if (do_quake) { earthquake(p_ptr->py, p_ptr->px, 10); if (cave->m_idx[y][x] == 0) stop = TRUE; } return stop; }
/* * Open a closed/locked/jammed door or a closed/locked chest. * * Unlocking a locked door/chest is worth one experience point. */ void do_cmd_open(cmd_code code, cmd_arg args[]) { int y, x, dir; s16b o_idx; bool more = FALSE; dir = args[0].direction; /* Get location */ y = p_ptr->py + ddy[dir]; x = p_ptr->px + ddx[dir]; /* Check for chests */ o_idx = chest_check(y, x, CHEST_OPENABLE); /* Verify legality */ if (!o_idx && !do_cmd_open_test(y, x)) { /* Cancel repeat */ disturb(p_ptr, 0, 0); return; } /* Take a turn */ p_ptr->energy_use = 100; /* Apply confusion */ if (player_confuse_dir(p_ptr, &dir, FALSE)) { /* Get location */ y = p_ptr->py + ddy[dir]; x = p_ptr->px + ddx[dir]; /* Check for chest */ o_idx = chest_check(y, x, CHEST_OPENABLE); } /* Monster */ if (cave->m_idx[y][x] > 0) { int m_idx = cave->m_idx[y][x]; struct monster *m_ptr = cave_monster(cave, m_idx); /* Mimics surprise the player */ if (is_mimicking(m_ptr)) { become_aware(m_ptr); /* Mimic wakes up */ mon_clear_timed(m_ptr, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, FALSE); } else { /* Message */ msg("There is a monster in the way!"); /* Attack */ py_attack(y, x); } } /* Chest */ else if (o_idx) { /* Open the chest */ more = do_cmd_open_chest(y, x, o_idx); } /* Door */ else { /* Open the door */ more = do_cmd_open_aux(y, x); } /* Cancel repeat unless we may continue */ if (!more) disturb(p_ptr, 0, 0); }
/** * 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)) { /* 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. */ }
/** * Creates a specific monster's drop, including any drops specified * in the monster.txt file. * * Returns TRUE if anything is created, FALSE if nothing is. */ static bool mon_create_drop(int m_idx, byte origin) { struct monster_drop *drop; monster_type *m_ptr; monster_race *r_ptr; bool great, good, gold_ok, item_ok; bool any = FALSE; int number = 0, level, j; object_type *i_ptr; object_type object_type_body; assert(m_idx > 0); m_ptr = cave_monster(cave, m_idx); r_ptr = &r_info[m_ptr->r_idx]; great = (rf_has(r_ptr->flags, RF_DROP_GREAT)); good = great || (rf_has(r_ptr->flags, RF_DROP_GOOD)); gold_ok = (!rf_has(r_ptr->flags, RF_ONLY_ITEM)); item_ok = (!rf_has(r_ptr->flags, RF_ONLY_GOLD)); /* Determine how much we can drop */ if (rf_has(r_ptr->flags, RF_DROP_20) && randint0(100) < 20) number++; if (rf_has(r_ptr->flags, RF_DROP_40) && randint0(100) < 40) number++; if (rf_has(r_ptr->flags, RF_DROP_60) && randint0(100) < 60) number++; if (rf_has(r_ptr->flags, RF_DROP_4)) number += rand_range(2, 6); if (rf_has(r_ptr->flags, RF_DROP_3)) number += rand_range(2, 4); if (rf_has(r_ptr->flags, RF_DROP_2)) number += rand_range(1, 3); if (rf_has(r_ptr->flags, RF_DROP_1)) number++; /* Take the best of (average of monster level and current depth) and (monster level) - to reward fighting OOD monsters */ level = MAX((r_ptr->level + p_ptr->depth) / 2, r_ptr->level); /* Specified drops */ for (drop = r_ptr->drops; drop; drop = drop->next) { if ((unsigned int)randint0(100) >= drop->percent_chance) continue; i_ptr = &object_type_body; if (drop->artifact) { object_prep(i_ptr, objkind_get(drop->artifact->tval, drop->artifact->sval), level, RANDOMISE); i_ptr->artifact = drop->artifact; copy_artifact_data(i_ptr, i_ptr->artifact); i_ptr->artifact->created = 1; } else { object_prep(i_ptr, drop->kind, level, RANDOMISE); apply_magic(i_ptr, level, TRUE, good, great); } i_ptr->origin = origin; i_ptr->origin_depth = p_ptr->depth; i_ptr->origin_xtra = m_ptr->r_idx; i_ptr->number = randint0(drop->max - drop->min) + drop->min; if (monster_carry(m_ptr, i_ptr)) any = TRUE; } /* Make some objects */ for (j = 0; j < number; j++) { i_ptr = &object_type_body; object_wipe(i_ptr); if (gold_ok && (!item_ok || (randint0(100) < 50))) { make_gold(i_ptr, level, SV_GOLD_ANY); } else { if (!make_object(cave, i_ptr, level, good, great, NULL)) continue; } i_ptr->origin = origin; i_ptr->origin_depth = p_ptr->depth; i_ptr->origin_xtra = m_ptr->r_idx; if (monster_carry(m_ptr, i_ptr)) any = TRUE; } return any; }
/** * Process all the "live" monsters, once per game turn. * * During each game turn, we scan through the list of all the "live" monsters, * (backwards, so we can excise any "freshly dead" monsters), energizing each * monster, and allowing fully energized monsters to move, attack, pass, etc. * * This function and its children are responsible for a considerable fraction * of the processor time in normal situations, greater if the character is * resting. */ void process_monsters(struct chunk *c, int minimum_energy) { int i; int mspeed; /* Only process some things every so often */ bool regen = false; /* Regenerate hitpoints and mana every 100 game turns */ if (turn % 100 == 0) regen = true; /* Process the monsters (backwards) */ for (i = cave_monster_max(c) - 1; i >= 1; i--) { struct monster *mon; bool moving; /* Handle "leaving" */ if (player->is_dead || player->upkeep->generate_level) break; /* Get a 'live' monster */ mon = cave_monster(c, i); if (!mon->race) continue; /* Ignore monsters that have already been handled */ if (mflag_has(mon->mflag, MFLAG_HANDLED)) continue; /* Not enough energy to move yet */ if (mon->energy < minimum_energy) continue; /* Does this monster have enough energy to move? */ moving = mon->energy >= z_info->move_energy ? true : false; /* Prevent reprocessing */ mflag_on(mon->mflag, MFLAG_HANDLED); /* Handle monster regeneration if requested */ if (regen) regen_monster(mon, 1); /* Calculate the net speed */ mspeed = mon->mspeed; if (mon->m_timed[MON_TMD_FAST]) mspeed += 10; if (mon->m_timed[MON_TMD_SLOW]) { int slow_level = monster_effect_level(mon, MON_TMD_SLOW); mspeed -= (2 * slow_level); } /* Give this monster some energy */ mon->energy += turn_energy(mspeed); /* End the turn of monsters without enough energy to move */ if (!moving) continue; /* Use up "some" energy */ mon->energy -= z_info->move_energy; /* Mimics lie in wait */ if (monster_is_mimicking(mon)) continue; /* Check if the monster is active */ if (monster_check_active(c, mon)) { /* Process timed effects - skip turn if necessary */ if (process_monster_timed(c, mon)) continue; /* Set this monster to be the current actor */ c->mon_current = i; /* The monster takes its turn */ monster_turn(c, mon); /* Monster is no longer current */ c->mon_current = -1; } } /* Update monster visibility after this */ /* XXX This may not be necessary */ player->upkeep->update |= PU_MONSTERS; }
/** * Decreases a monster's hit points by `dam` and handle monster death. * * Hack -- we "delay" fear messages by passing around a "fear" flag. * * We announce monster death (using an optional "death message" (`note`) * if given, and a otherwise a generic killed/destroyed message). * * Returns TRUE if the monster has been killed (and deleted). * * TODO: Consider decreasing monster experience over time, say, by using * "(m_exp * m_lev * (m_lev)) / (p_lev * (m_lev + n_killed))" instead * of simply "(m_exp * m_lev) / (p_lev)", to make the first monster * worth more than subsequent monsters. This would also need to * induce changes in the monster recall code. XXX XXX XXX **/ bool mon_take_hit(int m_idx, int dam, bool *fear, const char *note) { monster_type *m_ptr; monster_race *r_ptr; monster_lore *l_ptr; s32b div, new_exp, new_exp_frac; assert(m_idx > 0); m_ptr = cave_monster(cave, m_idx); r_ptr = &r_info[m_ptr->r_idx]; l_ptr = &l_list[m_ptr->r_idx]; /* Redraw (later) if needed */ if (p_ptr->health_who == m_idx) p_ptr->redraw |= (PR_HEALTH); /* Wake it up */ mon_clear_timed(m_idx, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, FALSE); /* Become aware of its presence */ if (m_ptr->unaware) become_aware(m_idx); /* Hurt it */ m_ptr->hp -= dam; /* It is dead now */ if (m_ptr->hp < 0) { char m_name[80]; char buf[80]; /* Assume normal death sound */ int soundfx = MSG_KILL; /* Play a special sound if the monster was unique */ if (rf_has(r_ptr->flags, RF_UNIQUE)) { if (r_ptr->base == lookup_monster_base("Morgoth")) soundfx = MSG_KILL_KING; else soundfx = MSG_KILL_UNIQUE; } /* Extract monster name */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); /* Death by Missile/Spell attack */ if (note) { /* Hack -- allow message suppression */ if (strlen(note) <= 1) { /* Be silent */ } else msgt(soundfx, "%^s%s", m_name, note); } /* Death by physical attack -- invisible monster */ else if (!m_ptr->ml) msgt(soundfx, "You have killed %s.", m_name); /* Death by Physical attack -- non-living monster */ else if (monster_is_unusual(r_ptr)) msgt(soundfx, "You have destroyed %s.", m_name); /* Death by Physical attack -- living monster */ else msgt(soundfx, "You have slain %s.", m_name); /* Player level */ div = p_ptr->lev; /* Give some experience for the kill */ new_exp = ((long)r_ptr->mexp * r_ptr->level) / div; /* Handle fractional experience */ new_exp_frac = ((((long)r_ptr->mexp * r_ptr->level) % div) * 0x10000L / div) + p_ptr->exp_frac; /* Keep track of experience */ if (new_exp_frac >= 0x10000L) { new_exp++; p_ptr->exp_frac = (u16b)(new_exp_frac - 0x10000L); } else p_ptr->exp_frac = (u16b)new_exp_frac; /* When the player kills a Unique, it stays dead */ if (rf_has(r_ptr->flags, RF_UNIQUE)) { char unique_name[80]; r_ptr->max_num = 0; /* * This gets the correct name if we slay an invisible * unique and don't have See Invisible. */ monster_desc(unique_name, sizeof(unique_name), m_ptr, MDESC_SHOW | MDESC_IND2); /* Log the slaying of a unique */ strnfmt(buf, sizeof(buf), "Killed %s", unique_name); history_add(buf, HISTORY_SLAY_UNIQUE, 0); } /* Gain experience */ player_exp_gain(p_ptr, new_exp); /* Generate treasure */ monster_death(m_idx, FALSE); /* Recall even invisible uniques or winners */ if (m_ptr->ml || rf_has(r_ptr->flags, RF_UNIQUE)) { /* Count kills this life */ if (l_ptr->pkills < MAX_SHORT) l_ptr->pkills++; /* Count kills in all lives */ if (l_ptr->tkills < MAX_SHORT) l_ptr->tkills++; /* Hack -- Auto-recall */ monster_race_track(m_ptr->r_idx); } /* Delete the monster */ delete_monster_idx(m_idx); /* Not afraid */ (*fear) = FALSE; /* Monster is dead */ return (TRUE); } /* Mega-Hack -- Pain cancels fear */ if (!(*fear) && m_ptr->m_timed[MON_TMD_FEAR] && (dam > 0)) { int tmp = randint1(dam); /* Cure a little fear */ if (tmp < m_ptr->m_timed[MON_TMD_FEAR]) { /* Reduce fear */ mon_dec_timed(m_idx, MON_TMD_FEAR, tmp, MON_TMD_FLG_NOMESSAGE, FALSE); } /* Cure all the fear */ else { /* Cure fear */ mon_clear_timed(m_idx, MON_TMD_FEAR, MON_TMD_FLG_NOMESSAGE, FALSE); /* No more fear */ (*fear) = FALSE; } } /* Sometimes a monster gets scared by damage */ if (!m_ptr->m_timed[MON_TMD_FEAR] && !rf_has(r_ptr->flags, RF_NO_FEAR) && dam > 0) { int percentage; /* Percentage of fully healthy */ percentage = (100L * m_ptr->hp) / m_ptr->maxhp; /* * Run (sometimes) if at 10% or less of max hit points, * or (usually) when hit for half its current hit points */ if ((randint1(10) >= percentage) || ((dam >= m_ptr->hp) && (randint0(100) < 80))) { int timer = randint1(10) + (((dam >= m_ptr->hp) && (percentage > 7)) ? 20 : ((11 - percentage) * 5)); /* Hack -- note fear */ (*fear) = TRUE; mon_inc_timed(m_idx, MON_TMD_FEAR, timer, MON_TMD_FLG_NOMESSAGE | MON_TMD_FLG_NOFAIL, FALSE); } } /* Not dead yet */ return (FALSE); }
/** * Handles the "death" of a monster. * * Disperses treasures carried by the monster centered at the monster location. * Note that objects dropped may disappear in crowded rooms. * * Checks for "Quest" completion when a quest monster is killed. * * Note that only the player can induce "monster_death()" on Uniques. * Thus (for now) all Quest monsters should be Uniques. * * If `stats` is true, then we skip updating the monster memory. This is * used by stats-generation code, for efficiency. */ void monster_death(int m_idx, bool stats) { int i, y, x; int dump_item = 0; int dump_gold = 0; int total = 0; s16b this_o_idx, next_o_idx = 0; monster_type *m_ptr; monster_race *r_ptr; bool visible; object_type *i_ptr; object_type object_type_body; assert(m_idx > 0); m_ptr = cave_monster(cave, m_idx); r_ptr = &r_info[m_ptr->r_idx]; visible = (m_ptr->ml || rf_has(r_ptr->flags, RF_UNIQUE)); /* Get the location */ y = m_ptr->fy; x = m_ptr->fx; /* Delete any mimicked objects */ if (m_ptr->mimicked_o_idx > 0) delete_object_idx(m_ptr->mimicked_o_idx); /* Drop objects being carried */ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) { object_type *o_ptr; /* Get the object */ o_ptr = object_byid(this_o_idx); /* Line up the next object */ next_o_idx = o_ptr->next_o_idx; /* Paranoia */ o_ptr->held_m_idx = 0; /* Get local object, copy it and delete the original */ i_ptr = &object_type_body; object_copy(i_ptr, o_ptr); delete_object_idx(this_o_idx); /* Count it and drop it - refactor once origin is a bitflag */ if (!stats) { if ((i_ptr->tval == TV_GOLD) && (i_ptr->origin != ORIGIN_STOLEN)) dump_gold++; else if ((i_ptr->tval != TV_GOLD) && ((i_ptr->origin == ORIGIN_DROP) || (i_ptr->origin == ORIGIN_DROP_PIT) || (i_ptr->origin == ORIGIN_DROP_VAULT) || (i_ptr->origin == ORIGIN_DROP_SUMMON) || (i_ptr->origin == ORIGIN_DROP_SPECIAL) || (i_ptr->origin == ORIGIN_DROP_BREED) || (i_ptr->origin == ORIGIN_DROP_POLY) || (i_ptr->origin == ORIGIN_DROP_WIZARD))) dump_item++; } /* Change origin if monster is invisible, unless we're in stats mode */ if (!visible && !stats) i_ptr->origin = ORIGIN_DROP_UNKNOWN; drop_near(cave, i_ptr, 0, y, x, TRUE); } /* Forget objects */ m_ptr->hold_o_idx = 0; /* Take note of any dropped treasure */ if (visible && (dump_item || dump_gold)) lore_treasure(m_idx, dump_item, dump_gold); /* Update monster list window */ p_ptr->redraw |= PR_MONLIST; /* Nothing else to do for non-"Quest Monsters" */ if (!rf_has(r_ptr->flags, RF_QUESTOR)) return; /* Mark quests as complete */ for (i = 0; i < MAX_Q_IDX; i++) { /* Note completed quests */ if (q_list[i].level == r_ptr->level) q_list[i].level = 0; /* Count incomplete quests */ if (q_list[i].level) total++; } /* Build magical stairs */ build_quest_stairs(y, x); /* Nothing left, game over... */ if (total == 0) { p_ptr->total_winner = TRUE; p_ptr->redraw |= (PR_TITLE); msg("*** CONGRATULATIONS ***"); msg("You have won the game!"); msg("You may retire (commit suicide) when you are ready."); } }
/** * Attempts to place a copy of the given monster at the given position in * the dungeon. * * All of the monster placement routines eventually call this function. This * is what actually puts the monster in the dungeon (i.e., it notifies the cave * and sets the monsters position). The dungeon loading code also calls this * function directly. * * `origin` is the item origin to use for any monster drops (e.g. ORIGIN_DROP, * ORIGIN_DROP_PIT, etc.) The dungeon loading code calls this with origin = 0, * which prevents the monster's drops from being generated again. * * Returns the m_idx of the newly copied monster, or 0 if the placement fails. */ s16b place_monster(struct chunk *c, int y, int x, struct monster *mon, byte origin) { s16b m_idx; struct monster *new_mon; assert(square_in_bounds(c, y, x)); assert(!square_monster(c, y, x)); /* Get a new record */ m_idx = mon_pop(c); if (!m_idx) return 0; /* Copy the monster */ new_mon = cave_monster(c, m_idx); memcpy(new_mon, mon, sizeof(struct monster)); /* Set the ID */ new_mon->midx = m_idx; /* Set the location */ c->squares[y][x].mon = new_mon->midx; new_mon->fy = y; new_mon->fx = x; assert(square_monster(c, y, x) == new_mon); update_mon(new_mon, c, true); /* Hack -- Count the number of "reproducers" */ if (rf_has(new_mon->race->flags, RF_MULTIPLY)) num_repro++; /* Count racial occurrences */ new_mon->race->cur_num++; /* Create the monster's drop, if any */ if (origin) (void)mon_create_drop(c, new_mon, origin); /* Make mimics start mimicking */ if (origin && new_mon->race->mimic_kinds) { struct object *obj; struct object_kind *kind = new_mon->race->mimic_kinds->kind; struct monster_mimic *mimic_kind; int i = 1; /* Pick a random object kind to mimic */ for (mimic_kind = new_mon->race->mimic_kinds; mimic_kind; mimic_kind = mimic_kind->next, i++) { if (one_in_(i)) kind = mimic_kind->kind; } if (tval_is_money_k(kind)) { obj = make_gold(player->depth, kind->name); } else { obj = object_new(); object_prep(obj, kind, new_mon->race->level, RANDOMISE); apply_magic(obj, new_mon->race->level, true, false, false, false); obj->number = 1; obj->origin = ORIGIN_DROP_MIMIC; obj->origin_depth = player->depth; } obj->mimicking_m_idx = m_idx; new_mon->mimicked_obj = obj; /* Put the object on the floor if it goes, otherwise no mimicry */ if (floor_carry(c, y, x, obj, false)) { list_object(c, obj); } else { /* Clear the mimicry */ obj->mimicking_m_idx = 0; new_mon->mimicked_obj = NULL; /* Give the object to the monster if appropriate */ if (rf_has(new_mon->race->flags, RF_MIMIC_INV)) { monster_carry(c, new_mon, obj); } else { /* Otherwise delete the mimicked object */ object_delete(&obj); } } } /* Result */ return m_idx; }
/** * Deletes a monster by index. * * When a monster is deleted, all of its objects are deleted. */ void delete_monster_idx(int m_idx) { int x, y; s16b this_o_idx, next_o_idx = 0; monster_type *m_ptr; assert(m_idx > 0); m_ptr = cave_monster(cave, m_idx); /* Monster location */ y = m_ptr->fy; x = m_ptr->fx; /* Hack -- Reduce the racial counter */ m_ptr->race->cur_num--; /* Hack -- count the number of "reproducers" */ if (rf_has(m_ptr->race->flags, RF_MULTIPLY)) num_repro--; /* Hack -- remove target monster */ if (target_get_monster() == m_ptr) target_set_monster(NULL); /* Hack -- remove tracked monster */ if (p_ptr->health_who == m_ptr) health_track(p_ptr, NULL); /* Monster is gone */ cave->m_idx[y][x] = 0; /* Delete objects */ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) { object_type *o_ptr; /* Get the object */ o_ptr = object_byid(this_o_idx); /* Get the next object */ next_o_idx = o_ptr->next_o_idx; /* Preserve unseen artifacts (we assume they were created as this * monster's drop) - this will cause unintended behaviour in preserve * off mode if monsters can pick up artifacts */ if (o_ptr->artifact && !object_was_sensed(o_ptr)) o_ptr->artifact->created = FALSE; /* Clear held_m_idx now to avoid wasting time in delete_object_idx */ o_ptr->held_m_idx = 0; /* Delete the object */ delete_object_idx(this_o_idx); } /* Delete mimicked objects */ if (m_ptr->mimicked_o_idx > 0) delete_object_idx(m_ptr->mimicked_o_idx); /* Wipe the Monster */ (void)WIPE(m_ptr, monster_type); /* Count monsters */ cave->mon_cnt--; /* Visual update */ cave_light_spot(cave, y, x); }
/** * 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, u16b *path_g, char *c, byte *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 = GRID_Y(path_g[i]); int x = GRID_X(path_g[i]); /* * 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 (cave->m_idx[y][x] && cave_monster(cave, cave->m_idx[y][x])->ml) { /* Visible monsters are red. */ monster_type *m_ptr = cave_monster(cave, cave->m_idx[y][x]); monster_race *r_ptr = &r_info[m_ptr->r_idx]; /*mimics act as objects*/ if (rf_has(r_ptr->flags, RF_UNAWARE)) colour = TERM_YELLOW; else colour = TERM_L_RED; } else if (cave->o_idx[y][x] && object_byid(cave->o_idx[y][x])->marked) /* Known objects are yellow. */ colour = TERM_YELLOW; else if (!cave_floor_bold(y,x) && ((cave->info[y][x] & (CAVE_MARK)) || player_can_see_bold(y,x))) /* Known walls are blue. */ colour = TERM_BLUE; else if (!(cave->info[y][x] & (CAVE_MARK)) && !player_can_see_bold(y,x)) /* Unknown squares are grey. */ colour = TERM_L_DARK; else /* Unoccupied squares are white. */ colour = TERM_WHITE; /* Draw the path segment */ (void)Term_addch(colour, '*'); } return i; }
/** * Move a monster from index i1 to index i2 in the monster list. */ static void compact_monsters_aux(int i1, int i2) { int y, x; monster_type *m_ptr; s16b this_o_idx, next_o_idx = 0; /* Do nothing */ if (i1 == i2) return; /* Old monster */ m_ptr = cave_monster(cave, i1); y = m_ptr->fy; x = m_ptr->fx; /* Update the cave */ cave->m_idx[y][x] = i2; /* Update midx */ m_ptr->midx = i2; /* Repair objects being carried by monster */ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) { object_type *o_ptr; /* Get the object */ o_ptr = object_byid(this_o_idx); /* Get the next object */ next_o_idx = o_ptr->next_o_idx; /* Reset monster pointer */ o_ptr->held_m_idx = i2; } /* Move mimicked objects */ if (m_ptr->mimicked_o_idx > 0) { object_type *o_ptr; /* Get the object */ o_ptr = object_byid(m_ptr->mimicked_o_idx); /* Reset monster pointer */ o_ptr->mimicking_m_idx = i2; } /* Hack -- Update the target */ if (target_get_monster() == m_ptr) target_set_monster(cave_monster(cave, i2)); /* Hack -- Update the health bar */ if (p_ptr->health_who == m_ptr) p_ptr->health_who = cave_monster(cave, i2); /* Hack -- move monster */ COPY(cave_monster(cave, i2), cave_monster(cave, i1), struct monster); /* Hack -- wipe hole */ (void)WIPE(cave_monster(cave, i1), monster_type); }
/* * Hack -- determine if a given location is "interesting" */ static bool target_set_interactive_accept(int y, int x) { object_type *o_ptr; /* Player grids are always interesting */ if (cave->m_idx[y][x] < 0) return (TRUE); /* Handle hallucination */ if (p_ptr->timed[TMD_IMAGE]) return (FALSE); /* Visible monsters */ if (cave->m_idx[y][x] > 0) { monster_type *m_ptr = cave_monster(cave, cave->m_idx[y][x]); /* Visible monsters */ if (m_ptr->ml && !m_ptr->unaware) return (TRUE); } /* Scan all objects in the grid */ for (o_ptr = get_first_object(y, x); o_ptr; o_ptr = get_next_object(o_ptr)) { /* Memorized object */ if (o_ptr->marked && !squelch_item_ok(o_ptr)) return (TRUE); } /* Interesting memorized features */ if (cave->info[y][x] & (CAVE_MARK)) { /* Notice glyphs */ if (cave->feat[y][x] == FEAT_GLYPH) return (TRUE); /* Notice doors */ if (cave->feat[y][x] == FEAT_OPEN) return (TRUE); if (cave->feat[y][x] == FEAT_BROKEN) return (TRUE); /* Notice stairs */ if (cave->feat[y][x] == FEAT_LESS) return (TRUE); if (cave->feat[y][x] == FEAT_MORE) return (TRUE); /* Notice shops */ if ((cave->feat[y][x] >= FEAT_SHOP_HEAD) && (cave->feat[y][x] <= FEAT_SHOP_TAIL)) return (TRUE); /* Notice traps */ if (cave_isknowntrap(cave, y, x)) return TRUE; /* Notice doors */ if (cave_iscloseddoor(cave, y, x)) return TRUE; /* Notice rubble */ if (cave->feat[y][x] == FEAT_RUBBLE) return (TRUE); /* Notice veins with treasure */ if (cave->feat[y][x] == FEAT_MAGMA_K) return (TRUE); if (cave->feat[y][x] == FEAT_QUARTZ_K) return (TRUE); } /* Nope */ return (FALSE); }
/** * Attempts to place a copy of the given monster at the given position in * the dungeon. * * All of the monster placement routines eventually call this function. This * is what actually puts the monster in the dungeon (i.e., it notifies the cave * and sets the monsters position). The dungeon loading code also calls this * function directly. * * `origin` is the item origin to use for any monster drops (e.g. ORIGIN_DROP, * ORIGIN_DROP_PIT, etc.) The dungeon loading code calls this with origin = 0, * which prevents the monster's drops from being generated again. * * Returns the m_idx of the newly copied monster, or 0 if the placement fails. */ s16b place_monster(int y, int x, monster_type *mon, byte origin) { s16b m_idx; monster_type *m_ptr; assert(cave_in_bounds(cave, y, x)); assert(!cave_monster_at(cave, y, x)); /* Get a new record */ m_idx = mon_pop(); if (!m_idx) return 0; /* Copy the monster */ m_ptr = cave_monster(cave, m_idx); COPY(m_ptr, mon, monster_type); /* Set the ID */ m_ptr->midx = m_idx; /* Set the location */ cave->m_idx[y][x] = m_ptr->midx; m_ptr->fy = y; m_ptr->fx = x; assert(cave_monster_at(cave, y, x) == m_ptr); update_mon(m_ptr, TRUE); /* Hack -- Count the number of "reproducers" */ if (rf_has(m_ptr->race->flags, RF_MULTIPLY)) num_repro++; /* Count racial occurrences */ m_ptr->race->cur_num++; /* Create the monster's drop, if any */ if (origin) (void)mon_create_drop(m_ptr, origin); /* Make mimics start mimicking */ if (origin && m_ptr->race->mimic_kinds) { object_type *i_ptr; object_type object_type_body; object_kind *kind = m_ptr->race->mimic_kinds->kind; struct monster_mimic *mimic_kind; int i = 1; /* Pick a random object kind to mimic */ for (mimic_kind = m_ptr->race->mimic_kinds; mimic_kind; mimic_kind = mimic_kind->next, i++) { if (one_in_(i)) kind = mimic_kind->kind; } i_ptr = &object_type_body; if (kind->tval == TV_GOLD) { make_gold(i_ptr, p_ptr->depth, kind->sval); } else { object_prep(i_ptr, kind, m_ptr->race->level, RANDOMISE); apply_magic(i_ptr, m_ptr->race->level, TRUE, FALSE, FALSE, FALSE); i_ptr->number = 1; } i_ptr->origin = origin; i_ptr->mimicking_m_idx = m_idx; m_ptr->mimicked_o_idx = floor_carry(cave, y, x, i_ptr); } /* Result */ return m_idx; }
/* * Examine a grid, return a keypress. * * The "mode" argument contains the "TARGET_LOOK" bit flag, which * indicates that the "space" key should scan through the contents * of the grid, instead of simply returning immediately. This lets * the "look" command get complete information, without making the * "target" command annoying. * * The "info" argument contains the "commands" which should be shown * inside the "[xxx]" text. This string must never be empty, or grids * containing monsters will be displayed with an extra comma. * * Note that if a monster is in the grid, we update both the monster * recall info and the health bar info to track that monster. * * This function correctly handles multiple objects per grid, and objects * and terrain features in the same grid, though the latter never happens. * * This function must handle blindness/hallucination. */ static struct keypress target_set_interactive_aux(int y, int x, int mode) { s16b this_o_idx = 0, next_o_idx = 0; const char *s1, *s2, *s3; bool boring; int feat; int floor_list[MAX_FLOOR_STACK]; int floor_num; struct keypress query; char out_val[256]; char coords[20]; /* Describe the square location */ coords_desc(coords, sizeof(coords), y, x); /* Repeat forever */ while (1) { /* Paranoia */ query.code = ' '; /* Assume boring */ boring = TRUE; /* Default */ s1 = "You see "; s2 = ""; s3 = ""; /* The player */ if (cave->m_idx[y][x] < 0) { /* Description */ s1 = "You are "; /* Preposition */ s2 = "on "; } /* Hack -- hallucination */ if (p_ptr->timed[TMD_IMAGE]) { const char *name = "something strange"; /* Display a message */ if (p_ptr->wizard) strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, name, coords, y, x); else strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, name, coords); prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey(); /* Stop on everything but "return" */ if (query.code == '\n' || query.code == '\r') continue; return query; } /* Actual monsters */ if (cave->m_idx[y][x] > 0) { monster_type *m_ptr = cave_monster(cave, cave->m_idx[y][x]); monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Visible */ if (m_ptr->ml && !m_ptr->unaware) { bool recall = FALSE; char m_name[80]; /* Not boring */ boring = FALSE; /* Get the monster name ("a kobold") */ monster_desc(m_name, sizeof(m_name), m_ptr, MDESC_IND2); /* Hack -- track this monster race */ monster_race_track(m_ptr->r_idx); /* Hack -- health bar for this monster */ health_track(p_ptr, cave->m_idx[y][x]); /* Hack -- handle stuff */ handle_stuff(); /* Interact */ while (1) { /* Recall */ if (recall) { /* Save screen */ screen_save(); /* Recall on screen */ screen_roff(m_ptr->r_idx); /* Command */ query = inkey(); /* Load screen */ screen_load(); } /* Normal */ else { char buf[80]; /* Describe the monster */ look_mon_desc(buf, sizeof(buf), cave->m_idx[y][x]); /* Describe, and prompt for recall */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s (%s), %s (%d:%d).", s1, s2, s3, m_name, buf, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s (%s), %s.", s1, s2, s3, m_name, buf, coords); } prt(out_val, 0, 0); /* Place cursor */ move_cursor_relative(y, x); /* Command */ query = inkey(); } /* Normal commands */ if (query.code == 'r') recall = !recall; else break; } /* Stop on everything but "return"/"space" */ if (query.code != '\n' && query.code != '\r' && query.code != ' ') break; /* Sometimes stop at "space" key */ if ((query.code == ' ') && !(mode & (TARGET_LOOK))) break; /* Take account of gender */ if (rf_has(r_ptr->flags, RF_FEMALE)) s1 = "She is "; else if (rf_has(r_ptr->flags, RF_MALE)) s1 = "He is "; else s1 = "It is "; /* Use a preposition */ s2 = "carrying "; /* Scan all objects being carried */ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) { char o_name[80]; object_type *o_ptr; /* Get the object */ o_ptr = object_byid(this_o_idx); /* Get the next object */ next_o_idx = o_ptr->next_o_idx; /* Obtain an object description */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Describe the object */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, o_name, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, o_name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey(); /* Stop on everything but "return"/"space" */ if ((query.code != '\n') && (query.code != '\r') && (query.code != ' ')) break; /* Sometimes stop at "space" key */ if ((query.code == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s2 = "also carrying "; } /* Double break */ if (this_o_idx) break; /* Use a preposition */ s2 = "on "; } } /* Assume not floored */ floor_num = scan_floor(floor_list, N_ELEMENTS(floor_list), y, x, 0x02); /* Scan all marked objects in the grid */ if ((floor_num > 0) && (!(p_ptr->timed[TMD_BLIND]) || (y == p_ptr->py && x == p_ptr->px))) { /* Not boring */ boring = FALSE; track_object(-floor_list[0]); handle_stuff(); /* If there is more than one item... */ if (floor_num > 1) while (1) { /* Describe the pile */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%sa pile of %d objects, %s (%d:%d).", s1, s2, s3, floor_num, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%sa pile of %d objects, %s.", s1, s2, s3, floor_num, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey(); /* Display objects */ if (query.code == 'r') { int rdone = 0; int pos; while (!rdone) { /* Save screen */ screen_save(); /* Display */ show_floor(floor_list, floor_num, (OLIST_WEIGHT | OLIST_GOLD)); /* Describe the pile */ prt(out_val, 0, 0); query = inkey(); /* Load screen */ screen_load(); pos = query.code - 'a'; if (0 <= pos && pos < floor_num) { track_object(-floor_list[pos]); handle_stuff(); continue; } rdone = 1; } /* Now that the user's done with the display loop, let's */ /* the outer loop over again */ continue; } /* Done */ break; } /* Only one object to display */ else { char o_name[80]; /* Get the single object in the list */ object_type *o_ptr = object_byid(floor_list[0]); /* Not boring */ boring = FALSE; /* Obtain an object description */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Describe the object */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, o_name, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, o_name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey(); /* Stop on everything but "return"/"space" */ if ((query.code != '\n') && (query.code != '\r') && (query.code != ' ')) break; /* Sometimes stop at "space" key */ if ((query.code == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s1 = "It is "; /* Plurals */ if (o_ptr->number != 1) s1 = "They are "; /* Preposition */ s2 = "on "; } } /* Double break */ if (this_o_idx) break; /* Feature (apply "mimic") */ feat = f_info[cave->feat[y][x]].mimic; /* Require knowledge about grid, or ability to see grid */ if (!(cave->info[y][x] & (CAVE_MARK)) && !player_can_see_bold(y,x)) { /* Forget feature */ feat = FEAT_NONE; } /* Terrain feature if needed */ if (boring || (feat > FEAT_INVIS)) { const char *name = f_info[feat].name; /* Hack -- handle unknown grids */ if (feat == FEAT_NONE) name = "unknown grid"; /* Pick a prefix */ if (*s2 && (feat >= FEAT_DOOR_HEAD)) s2 = "in "; /* Pick proper indefinite article */ s3 = (is_a_vowel(name[0])) ? "an " : "a "; /* Hack -- special introduction for store doors */ if ((feat >= FEAT_SHOP_HEAD) && (feat <= FEAT_SHOP_TAIL)) { s3 = "the entrance to the "; } /* Display a message */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, name, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey(); /* Stop on everything but "return"/"space" */ if ((query.code != '\n') && (query.code != '\r') && (query.code != ' ')) break; } /* Stop on everything but "return" */ if ((query.code != '\n') && (query.code != '\r')) break; } /* Keep going */ return (query); }
/** * 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. */ void ranged_helper(int item, int dir, int range, int shots, ranged_attack attack) { /* Get the ammo */ object_type *o_ptr = object_from_item_idx(item); int i, j; byte missile_attr = object_attr(o_ptr); char missile_char = object_char(o_ptr); object_type object_type_body; object_type *i_ptr = &object_type_body; char o_name[80]; int path_n; u16b path_g[256]; int msec = op_ptr->delay_factor; /* Start at the player */ int x = p_ptr->px; int y = p_ptr->py; /* Predict the "target" location */ s16b ty = y + 99 * ddy[dir]; s16b tx = x + 99 * ddx[dir]; bool hit_target = FALSE; /* Check for target validity */ if ((dir == 5) && target_okay()) { target_get(&tx, &ty); if (distance(y, x, ty, tx) > range) { if (!get_check("Target out of range. Fire anyway?")) return; } } /* Sound */ sound(MSG_SHOOT); object_notice_on_firing(o_ptr); /* Describe the object */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_FULL | ODESC_SINGULAR); /* Actually "fire" the object -- Take a partial turn */ p_ptr->energy_use = (100 / shots); /* Calculate the path */ path_n = project_path(path_g, range, y, x, ty, tx, 0); /* Hack -- Handle stuff */ handle_stuff(); /* Start at the player */ x = p_ptr->px; y = p_ptr->py; /* Project along the path */ for (i = 0; i < path_n; ++i) { int ny = GRID_Y(path_g[i]); int nx = GRID_X(path_g[i]); /* Hack -- Stop before hitting walls */ if (!cave_floor_bold(ny, nx)) break; /* Advance */ x = nx; y = ny; /* Only do visuals if the player can "see" the missile */ if (player_can_see_bold(y, x)) { print_rel(missile_char, missile_attr, y, x); move_cursor_relative(y, x); Term_fresh(); if (p_ptr->redraw) redraw_stuff(); Term_xtra(TERM_XTRA_DELAY, msec); cave_light_spot(cave, y, x); Term_fresh(); if (p_ptr->redraw) redraw_stuff(); } else { /* Delay anyway for consistency */ Term_xtra(TERM_XTRA_DELAY, msec); } /* Handle monster */ if (cave->m_idx[y][x] > 0) break; } /* Try the attack on the monster at (x, y) if any */ if (cave->m_idx[y][x] > 0) { monster_type *m_ptr = cave_monster(cave, cave->m_idx[y][x]); monster_race *r_ptr = &r_info[m_ptr->r_idx]; int visible = m_ptr->ml; bool fear = FALSE; char m_name[80]; const char *note_dies = monster_is_unusual(r_ptr) ? " is destroyed." : " dies."; struct attack_result result = attack(o_ptr, y, x); int dmg = result.dmg; u32b msg_type = result.msg_type; const char *hit_verb = result.hit_verb; if (result.success) { hit_target = TRUE; /* Get "the monster" or "it" */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); object_notice_attack_plusses(o_ptr); /* No negative damage; change verb if no damage done */ if (dmg <= 0) { dmg = 0; hit_verb = "fail to harm"; } if (!visible) { /* Invisible monster */ msgt(MSG_SHOOT_HIT, "The %s finds a mark.", o_name); } else { /* Visible monster */ if (msg_type == MSG_SHOOT_HIT) msgt(MSG_SHOOT_HIT, "The %s %s %s.", o_name, hit_verb, m_name); else if (msg_type == MSG_HIT_GOOD) { msgt(MSG_HIT_GOOD, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a good hit!"); } else if (msg_type == MSG_HIT_GREAT) { msgt(MSG_HIT_GREAT, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a great hit!"); } else if (msg_type == MSG_HIT_SUPERB) { msgt(MSG_HIT_SUPERB, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a superb hit!"); } /* Track this monster */ if (m_ptr->ml) monster_race_track(m_ptr->r_idx); if (m_ptr->ml) health_track(p_ptr, cave->m_idx[y][x]); } /* Complex message */ if (p_ptr->wizard) msg("You do %d (out of %d) damage.", dmg, m_ptr->hp); /* Hit the monster, check for death */ if (!mon_take_hit(cave->m_idx[y][x], dmg, &fear, note_dies)) { message_pain(cave->m_idx[y][x], dmg); if (fear && m_ptr->ml) add_monster_message(m_name, cave->m_idx[y][x], MON_MSG_FLEE_IN_TERROR, TRUE); } } } /* Obtain a local object */ object_copy(i_ptr, o_ptr); object_split(i_ptr, o_ptr, 1); /* See if the ammunition broke or not */ j = breakage_chance(i_ptr, hit_target); /* Drop (or break) near that location */ drop_near(cave, i_ptr, j, y, x, TRUE); if (item >= 0) { /* The ammo is from the inventory */ inven_item_increase(item, -1); inven_item_describe(item); inven_item_optimize(item); } else { /* The ammo is from the floor */ floor_item_increase(0 - item, -1); floor_item_optimize(0 - item); } }
/** * Stack a codified message for the given monster race. You must supply * the description of some monster of this race. You can also supply * different monster descriptions for the same race. * Return TRUE on success. */ bool add_monster_message(const char *mon_name, int m_idx, int msg_code, bool delay) { int i; byte mon_flags = 0; monster_type *m_ptr; int r_idx; assert(m_idx > 0); m_ptr = cave_monster(cave, m_idx); r_idx = m_ptr->r_idx; assert(msg_code >= 0 && msg_code < MAX_MON_MSG); if (redundant_monster_message(m_idx, msg_code)) return (FALSE); /* Paranoia */ if (!mon_name || !mon_name[0]) mon_name = "it"; /* Save the "hidden" mark, if present */ if (strstr(mon_name, "(hidden)")) mon_flags |= 0x01; /* Save the "offscreen" mark, if present */ if (strstr(mon_name, "(offscreen)")) mon_flags |= 0x02; /* Monster is invisible or out of LOS */ if (streq(mon_name, "it") || streq(mon_name, "something")) mon_flags |= 0x04; /* Query if the message is already stored */ for (i = 0; i < size_mon_msg; i++) { /* We found the race and the message code */ if ((mon_msg[i].mon_race == r_idx) && (mon_msg[i].mon_flags == mon_flags) && (mon_msg[i].msg_code == msg_code)) { /* Can we increment the counter? */ if (mon_msg[i].mon_count < MAX_UCHAR) { /* Stack the message */ ++(mon_msg[i].mon_count); } /* Success */ return (TRUE); } } /* The message isn't stored. Check free space */ if (size_mon_msg >= MAX_STORED_MON_MSG) return (FALSE); /* Assign the message data to the free slot */ mon_msg[i].mon_race = r_idx; mon_msg[i].mon_flags = mon_flags; mon_msg[i].msg_code = msg_code; mon_msg[i].delay = delay; /* Just this monster so far */ mon_msg[i].mon_count = 1; /* One more entry */ ++size_mon_msg; p_ptr->notice |= PN_MON_MESSAGE; /* record which monster had this message stored */ if (size_mon_hist >= MAX_STORED_MON_CODES) return (TRUE); mon_message_hist[size_mon_hist].monster_idx = m_idx; mon_message_hist[size_mon_hist].message_code = msg_code; size_mon_hist++; /* Success */ return (TRUE); }
/** * Apply side effects from a spell attack to the player * * \param spell is the attack type * \param dam is the amount of damage caused by the attack * \param m_idx is the attacking monster * \param rlev is its level * \param seen is whether @ can see it */ static void do_side_effects(int spell, int dam, int m_idx, bool seen) { monster_type *m_ptr = cave_monster(cave, m_idx); monster_race *r_ptr = &r_info[m_ptr->r_idx]; const struct spell_effect *re_ptr; const struct mon_spell *rs_ptr = &mon_spell_table[spell]; int i, choice[99], dur = 0, j = 0, count = 0; s32b d = 0; bool sustain = FALSE, perma = FALSE, chosen[RSE_MAX] = { 0 }; /* Extract the monster level */ int rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1); /* First we note all the effects we'll be doing. */ for (re_ptr = spell_effect_table; re_ptr->index < RSE_MAX; re_ptr++) { if ((re_ptr->method && (re_ptr->method == rs_ptr->index)) || (re_ptr->gf && (re_ptr->gf == rs_ptr->gf))) { /* If we have a choice of effects, we create a cum freq table */ if (re_ptr->chance) { for (i = j; i < (j + re_ptr->chance); i++) choice[i] = re_ptr->index; j = i; } else chosen[re_ptr->index] = TRUE; } } /* If we have built a cum freq table, choose an effect from it */ if (j) chosen[choice[randint0(j)]] = TRUE; /* Now we cycle through again to activate the chosen effects */ for (re_ptr = spell_effect_table; re_ptr->index < RSE_MAX; re_ptr++) { if (chosen[re_ptr->index]) { /* * Check for resistance - there are three possibilities: * 1. Immunity to the attack type if side_immune is TRUE * 2. Resistance to the attack type if it affords no immunity * 3. Resistance to the specific side-effect * * TODO - add interesting messages to the RSE_ and GF_ tables * to replace the generic ones below. (See #1376) */ if (re_ptr->res_flag) update_smart_learn(m_ptr, p_ptr, re_ptr->res_flag); if ((rs_ptr->gf && check_side_immune(rs_ptr->gf)) || check_state(p_ptr, re_ptr->res_flag, p_ptr->state.flags)) { msg("You resist the effect!"); continue; } /* Allow saving throw if available */ if (re_ptr->save && randint0(100) < p_ptr->state.skills[SKILL_SAVE]) { msg("You avoid the effect!"); continue; } /* Implement the effect */ if (re_ptr->timed) { /* Calculate base duration (m_bonus is not used) */ dur = randcalc(re_ptr->base, 0, RANDOMISE); /* Calculate the damage-dependent duration (m_bonus is * used as a cap) */ dur += damcalc(re_ptr->dam.dice, re_ptr->dam.sides * dam / 100, RANDOMISE); if (re_ptr->dam.m_bonus && (dur > re_ptr->dam.m_bonus)) dur = re_ptr->dam.m_bonus; /* Apply the effect - we have already checked for resistance */ (void)player_inc_timed(p_ptr, re_ptr->flag, dur, TRUE, FALSE); } else { switch (re_ptr->flag) { case S_INV_DAM: if (dam > 0) inven_damage(p_ptr, re_ptr->gf, MIN(dam * randcalc(re_ptr->dam, 0, RANDOMISE), 300)); break; case S_TELEPORT: /* m_bonus is used as a clev filter */ if (!re_ptr->dam.m_bonus || randint1(re_ptr->dam.m_bonus) > p_ptr->lev) teleport_player(randcalc(re_ptr->base, 0, RANDOMISE)); break; case S_TELE_TO: teleport_player_to(m_ptr->fy, m_ptr->fx); break; case S_TELE_LEV: teleport_player_level(); break; case S_TELE_SELF: teleport_away(m_ptr, randcalc(re_ptr->base, 0, RANDOMISE)); break; case S_DRAIN_LIFE: d = re_ptr->base.base + (p_ptr->exp * re_ptr->base.sides / 100) * MON_DRAIN_LIFE; msg("You feel your life force draining away!"); player_exp_lose(p_ptr, d, FALSE); break; case S_DRAIN_STAT: /* m_bonus is used as a flag */ if (re_ptr->dam.m_bonus > 0) sustain = TRUE; if (abs(re_ptr->dam.m_bonus) > 1) perma = TRUE; drain_stats(randcalc(re_ptr->base, 0, RANDOMISE), sustain, perma); break; case S_SWAP_STAT: swap_stats(); break; case S_DRAIN_ALL: msg("You're not as powerful as you used to be..."); for (i = 0; i < A_MAX; i++) player_stat_dec(p_ptr, i, FALSE); break; case S_DISEN: (void)apply_disenchant(0); break; case S_DRAIN_MANA: drain_mana(m_idx, rlev, seen); break; case S_HEAL: heal_self(m_idx, rlev, seen); break; case S_DARKEN: (void)unlight_area(0, 3); break; case S_TRAPS: (void)trap_creation(); break; case S_AGGRAVATE: aggravate_monsters(m_idx); break; case S_KIN: summon_kin_type = r_ptr->d_char; case S_MONSTER: case S_MONSTERS: case S_SPIDER: case S_HOUND: case S_HYDRA: case S_AINU: case S_ANIMAL: case S_DEMON: case S_HI_DEMON: case S_UNDEAD: case S_HI_UNDEAD: case S_WRAITH: case S_DRAGON: case S_HI_DRAGON: case S_UNIQUE: count = summon_monster_aux(re_ptr->flag, m_idx, rlev, re_ptr->base.base); /* In the special case that uniques or wraiths were summoned but all were dead S_HI_UNDEAD is used instead */ if ((!count) && ((re_ptr->flag == S_WRAITH) || (re_ptr->flag == S_UNIQUE))) count = summon_monster_aux(S_HI_UNDEAD, m_idx, rlev, re_ptr->base.base); if (count && p_ptr->timed[TMD_BLIND]) msgt(rs_ptr->msgt, "You hear %s appear nearby.", (count > 1 ? "many things" : "something")); default: break; } } } } return; }
/** * This function takes a pointer to a grid info struct describing the * contents of a grid location (as obtained through the function map_info) * and fills in the character and attr pairs for display. * * ap and cp are filled with the attr/char pair for the monster, object or * floor tile that is at the "top" of the grid (monsters covering objects, * which cover floor, assuming all are present). * * tap and tcp are filled with the attr/char pair for the floor, regardless * of what is on it. This can be used by graphical displays with * transparency to place an object onto a floor tile, is desired. * * Any lighting effects are also applied to these pairs, clear monsters allow * the underlying colour or feature to show through (ATTR_CLEAR and * CHAR_CLEAR), multi-hued colour-changing (ATTR_MULTI) is applied, and so on. * Technically, the flag "CHAR_MULTI" is supposed to indicate that a monster * looks strange when examined, but this flag is currently ignored. * * NOTES: * This is called pretty frequently, whenever a grid on the map display * needs updating, so don't overcomplicate it. * * The "zero" entry in the feature/object/monster arrays are * used to provide "special" attr/char codes, with "monster zero" being * used for the player attr/char, "object zero" being used for the "pile" * attr/char, and "feature zero" being used for the "darkness" attr/char. * * TODO: * The transformations for tile colors, or brightness for the 16x16 * tiles should be handled differently. One possibility would be to * extend feature_type with attr/char definitions for the different states. * This will probably be done outside of the current text->graphics mappings * though. */ void grid_data_as_text(grid_data *g, int *ap, wchar_t *cp, int *tap, wchar_t *tcp) { feature_type *f_ptr = &f_info[g->f_idx]; int a = feat_x_attr[g->lighting][f_ptr->fidx]; wchar_t c = feat_x_char[g->lighting][f_ptr->fidx]; /* Check for trap detection boundaries */ if (use_graphics == GRAPHICS_NONE) grid_get_attr(g, &a); else if (g->trapborder && tf_has(f_ptr->flags, TF_FLOOR) && (g->m_idx || g->first_kind)) { /* if there is an object or monster here, and this is a plain floor * display the border here rather than an overlay below */ a = feat_x_attr[g->lighting][FEAT_DTRAP_FLOOR]; c = feat_x_char[g->lighting][FEAT_DTRAP_FLOOR]; } /* Save the terrain info for the transparency effects */ (*tap) = a; (*tcp) = c; /* There is a trap in this grid, and we are not hallucinating */ if (g->trap && (!g->hallucinate)) /* Change graphics to indicate a trap (if visible) */ get_trap_graphics(cave, g, &a, &c); /* If there's an object, deal with that. */ if (g->unseen_money) { /* $$$ gets an orange star*/ a = object_kind_attr(&k_info[7]); c = object_kind_char(&k_info[7]); } else if (g->unseen_object) { /* Everything else gets a red star */ a = object_kind_attr(&k_info[6]); c = object_kind_char(&k_info[6]); } else if (g->first_kind) { if (g->hallucinate) { /* Just pick a random object to display. */ hallucinatory_object(&a, &c); } else if (g->multiple_objects) { /* Get the "pile" feature instead */ a = object_kind_attr(&k_info[0]); c = object_kind_char(&k_info[0]); } else { /* Normal attr and char */ a = object_kind_attr(g->first_kind); c = object_kind_char(g->first_kind); } } /* Handle monsters, the player and trap borders */ if (g->m_idx > 0) { if (g->hallucinate) { /* Just pick a random monster to display. */ hallucinatory_monster(&a, &c); } else if (!is_mimicking(cave_monster(cave, g->m_idx))) { monster_type *m_ptr = cave_monster(cave, g->m_idx); byte da; wchar_t dc; /* Desired attr & char */ da = monster_x_attr[m_ptr->race->ridx]; dc = monster_x_char[m_ptr->race->ridx]; /* Special handling of attrs and/or chars */ if (da & 0x80) { /* Special attr/char codes */ a = da; c = dc; } else if (OPT(purple_uniques) && rf_has(m_ptr->race->flags, RF_UNIQUE)) { /* Turn uniques purple if desired (violet, actually) */ a = COLOUR_VIOLET; c = dc; } else if (rf_has(m_ptr->race->flags, RF_ATTR_MULTI) || rf_has(m_ptr->race->flags, RF_ATTR_FLICKER) || rf_has(m_ptr->race->flags, RF_ATTR_RAND)) { /* Multi-hued monster */ a = m_ptr->attr ? m_ptr->attr : da; c = dc; } else if (!flags_test(m_ptr->race->flags, RF_SIZE, RF_ATTR_CLEAR, RF_CHAR_CLEAR, FLAG_END)) { /* Normal monster (not "clear" in any way) */ a = da; /* Desired attr & char. da is not used, should a be set to it?*/ /*da = monster_x_attr[m_ptr->race->ridx];*/ dc = monster_x_char[m_ptr->race->ridx]; c = dc; } else if (a & 0x80) { /* Hack -- Bizarre grid under monster */ a = da; c = dc; } else if (!rf_has(m_ptr->race->flags, RF_CHAR_CLEAR)) { /* Normal char, Clear attr, monster */ c = dc; } else if (!rf_has(m_ptr->race->flags, RF_ATTR_CLEAR)) { /* Normal attr, Clear char, monster */ a = da; } /* Store the drawing attr so we can use it elsewhere */ m_ptr->attr = a; } } else if (g->is_player) { monster_race *r_ptr = &r_info[0]; /* Get the "player" attr */ a = monster_x_attr[r_ptr->ridx]; if ((OPT(hp_changes_color)) && !(a & 0x80)) { switch(player->chp * 10 / player->mhp) { case 10: case 9: { a = COLOUR_WHITE; break; } case 8: case 7: { a = COLOUR_YELLOW; break; } case 6: case 5: { a = COLOUR_ORANGE; break; } case 4: case 3: { a = COLOUR_L_RED; break; } case 2: case 1: case 0: { a = COLOUR_RED; break; } default: { a = COLOUR_WHITE; break; } } } /* Get the "player" char */ c = monster_x_char[r_ptr->ridx]; } else if (g->trapborder && (g->f_idx) && !(g->first_kind) && (use_graphics != GRAPHICS_NONE)) { /* No overlay is used, so we can use the trap border overlay */ a = feat_x_attr[g->lighting][FEAT_DTRAP_WALL]; c = feat_x_char[g->lighting][FEAT_DTRAP_WALL]; } /* Result */ (*ap) = a; (*cp) = c; }
/** * 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 * \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 * \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) { 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; ignore = context.ignore; note_kill = context.note_kill; /* Attempt to destroy the object */ if (do_kill) { char o_name[80]; /* Effect "observed" */ if (obj->marked && !ignore_item_ok(obj)) { 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 (obj->marked && !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 */ become_aware(cave_monster(cave, obj->mimicking_m_idx)); } else { /* Describe if needed */ if (obj->marked && 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); object_delete(obj); /* Redraw */ square_light_spot(cave, y, x); } } /* Next object */ obj = next; } /* Return "Anything seen?" */ return (obvious); }
/* * 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 = p_ptr->py; int px = p_ptr->px; int path_n; u16b path_g[256]; int i, d, m, t, bd; int wid, hgt, help_prompt_loc; bool done = FALSE; bool flag = TRUE; bool help = FALSE; //struct keypress query; ui_event press; /* These are used for displaying the path to the target */ wchar_t path_char[MAX_RANGE]; byte path_attr[MAX_RANGE]; struct point_set *targets; /* If we haven't been given an initial location, start on the player. */ if (x == -1 || y == -1) { x = p_ptr->px; y = p_ptr->py; } /* If we /have/ been given an initial location, make sure we honour it by going into "free targetting" mode. */ else { flag = FALSE; } /* Cancel target */ target_set_monster(0); /* Cancel tracking */ /* health_track(NULL); */ /* 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_set_interactive_prepare(mode); /* Start near the player */ m = 0; /* Interact */ while (!done) { bool path_drawn = FALSE; /* Interesting grids */ 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(p_ptr); /* Update help */ if (help) { bool good_target = (cave->m_idx[y][x] > 0) && target_able(cave->m_idx[y][x]); target_display_help(good_target, !(flag && point_set_size(targets))); } /* Find the path. */ path_n = project_path(path_g, 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); /* Cancel tracking */ /* health_track(NULL); */ /* 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);//.mouse.y; x = KEY_GRID_X(press);//.mouse.x; if (press.mouse.mods & KC_MOD_CONTROL) { /* same as keyboard target selection command below */ int m_idx = cave->m_idx[y][x]; if ((m_idx > 0) && target_able(m_idx)) { monster_type *m_ptr = cave_monster(cave, m_idx); /* Set up target information */ monster_race_track(m_ptr->r_idx); health_track(p_ptr, m_ptr); /*health_track(p_ptr, m_idx);*/ target_set_monster(m_idx); done = TRUE; } else { bell("Illegal target!"); } } else if (press.mouse.mods & KC_MOD_ALT) { /* go to spot - same as 'g' command below */ cmd_insert(CMD_PATHFIND); cmd_set_arg_point(cmd_get_top(), 0, y, x); done = TRUE; } else /* cancel look mode */ done = TRUE; } else /* if (press.mouse.button == 3) { } else */ { y = KEY_GRID_Y(press);//.mouse.y; x = KEY_GRID_X(press);//.mouse.x; if (cave->m_idx[y][x] || cave->o_idx[y][x]){// || cave->feat[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 '+': { 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(p_ptr); y = p_ptr->py; x = p_ptr->px; } case 'o': { flag = FALSE; break; } case 'm': { break; } case 't': case '5': case '0': case '.': { int m_idx = cave->m_idx[y][x]; if ((m_idx > 0) && target_able(m_idx)) { health_track(p_ptr, cave_monster(cave, m_idx)); target_set_monster(m_idx); done = TRUE; } else { bell("Illegal target!"); } break; } case 'g': { cmd_insert(CMD_PATHFIND); cmd_set_arg_point(cmd_get_top(), 0, y, x); done = TRUE; break; } case '?': { help = !help; /* Redraw main window */ p_ptr->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIP); Term_clear(); handle_stuff(p_ptr); 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_set_interactive_prepare(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_set_interactive_prepare(mode); } /* Handle stuff */ handle_stuff(p_ptr); } } /* Use interesting grid if found */ if (i >= 0) m = i; } } /* Arbitrary grids */ else { /* Update help */ if (help) { bool good_target = ((cave->m_idx[y][x] > 0) && target_able(cave->m_idx[y][x])); target_display_help(good_target, !(flag && point_set_size(targets))); } /* Find the path. */ path_n = project_path(path_g, 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); /* Cancel tracking */ /* health_track(0); */ /* 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);//.mouse.y; x = KEY_GRID_X(press);//.mouse.x; 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 */ cmd_insert(CMD_PATHFIND); cmd_set_arg_point(cmd_get_top(), 0, y, x); done = TRUE; } else { /* cancel look mode */ done = TRUE; if (d == -1) { target_set_location(y, x); d = 0; } } } else /*if (press.mouse.button == 3) { } else*/ { int dungeon_hgt = (p_ptr->depth == 0) ? TOWN_HGT : DUNGEON_HGT; int dungeon_wid = (p_ptr->depth == 0) ? TOWN_WID : DUNGEON_WID; y = KEY_GRID_Y(press);//.mouse.y; x = KEY_GRID_X(press);//.mouse.x; if (Term) { 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(p_ptr); /* Recalculate interesting grids */ point_set_dispose(targets); targets = target_set_interactive_prepare(mode); } if (cave->m_idx[y][x] || cave->o_idx[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(p_ptr); y = p_ptr->py; x = p_ptr->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': { cmd_insert(CMD_PATHFIND); cmd_set_arg_point(cmd_get_top(), 0, y, x); done = TRUE; break; } case '?': { help = !help; /* Redraw main window */ p_ptr->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIP); Term_clear(); handle_stuff(p_ptr); 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 = (p_ptr->depth == 0) ? TOWN_HGT : DUNGEON_HGT; int dungeon_wid = (p_ptr->depth == 0) ? TOWN_WID : DUNGEON_WID; /* 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(p_ptr); /* Recalculate interesting grids */ point_set_dispose(targets); targets = target_set_interactive_prepare(mode); } } } } /* Forget */ point_set_dispose(targets); /* Redraw as necessary */ if (help) { p_ptr->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIP); Term_clear(); } else { prt("", 0, 0); prt("", help_prompt_loc, 0); p_ptr->redraw |= (PR_DEPTH | PR_STATUS); } /* Recenter around player */ verify_panel(); /* Handle stuff */ handle_stuff(p_ptr); /* Failure to set target */ if (!target_set) return (FALSE); /* Success */ return (TRUE); }