/** * Check if a monster should stagger or not. Always stagger when confused, * but also deal with random movement for RAND_25 and _50 monsters. */ static bool monster_turn_should_stagger(struct monster *mon) { struct monster_lore *lore = get_lore(mon->race); int chance = 0; /* Increase chance of being erratic for every level of confusion */ int conf_level = monster_effect_level(mon, MON_TMD_CONF); while (conf_level) { int accuracy = 100 - chance; accuracy *= (100 - CONF_ERRATIC_CHANCE); accuracy /= 100; chance = 100 - accuracy; conf_level--; } /* RAND_25 and RAND_50 are cumulative */ if (rf_has(mon->race->flags, RF_RAND_25)) { chance += 25; if (monster_is_visible(mon)) rf_on(lore->flags, RF_RAND_25); } if (rf_has(mon->race->flags, RF_RAND_50)) { chance += 50; if (monster_is_visible(mon)) rf_on(lore->flags, RF_RAND_50); } return randint0(100) < chance; }
/** * Check if a monster should stagger or not. Always stagger when confused, * but also deal with random movement for RAND_25 and _50 monsters. */ static bool process_monster_should_stagger(struct monster *mon) { struct monster_lore *lore = get_lore(mon->race); int chance = 0; /* Confused */ if (mon->m_timed[MON_TMD_CONF]) return true; /* RAND_25 and RAND_50 are cumulative */ if (rf_has(mon->race->flags, RF_RAND_25)) { chance += 25; if (mflag_has(mon->mflag, MFLAG_VISIBLE)) rf_on(lore->flags, RF_RAND_25); } if (rf_has(mon->race->flags, RF_RAND_50)) { chance += 50; if (mflag_has(mon->mflag, MFLAG_VISIBLE)) rf_on(lore->flags, RF_RAND_50); } return randint0(100) < chance; }
/** * Wake a monster or reduce its depth of sleep * * Chance of waking up is dependent only on the player's stealth, but the * amount of sleep reduction takes into account the monster's distance from * the player. Currently straight line distance is used; possibly this * should take into account dungeon structure. */ static void monster_reduce_sleep(struct chunk *c, struct monster *mon) { bool woke_up = false; int stealth = player->state.skills[SKILL_STEALTH]; int player_noise = 1 << (30 - stealth); int notice = randint0(1024); struct monster_lore *lore = get_lore(mon->race); /* Aggravation */ if (player_of_has(player, OF_AGGRAVATE)) { char m_name[80]; /* Wake the monster */ mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false); /* Get the monster name */ monster_desc(m_name, sizeof(m_name), mon, MDESC_CAPITAL | MDESC_IND_HID); /* Notify the player if aware */ if (monster_is_obvious(mon)) msg("%s wakes up.", m_name); woke_up = true; } else if ((notice * notice * notice) <= player_noise) { int sleep_reduction = 1; int local_noise = c->noise.grids[mon->fy][mon->fx]; /* Test - wake up faster in hearing distance of the player * Note no dependence on stealth for now */ if ((local_noise > 0) && (local_noise < 50)) { sleep_reduction = (100 / local_noise); } /* Note a complete wakeup */ if (mon->m_timed[MON_TMD_SLEEP] <= sleep_reduction) { woke_up = true; } /* Monster wakes up a bit */ mon_dec_timed(mon, MON_TMD_SLEEP, sleep_reduction, MON_TMD_FLG_NOTIFY, false); /* Update knowledge */ if (monster_is_obvious(mon)) { if (!woke_up && lore->ignore < UCHAR_MAX) lore->ignore++; else if (woke_up && lore->wake < UCHAR_MAX) lore->wake++; lore_update(mon->race, lore); } } }
/** * Extract the multiplier from a given object hitting a given monster. * * \param o_ptr is the object being used to attack * \param m_ptr is the monster being attacked * \param best_s_ptr is the best applicable slay_table entry, or NULL if no * slay already known * \param real is whether this is a real attack (where we update lore) or a * simulation (where we don't) * \param known_only is whether we are using all the object flags, or only * the ones we *already* know about */ void improve_attack_modifier(object_type *o_ptr, const monster_type *m_ptr, const struct slay **best_s_ptr, bool real, bool known_only) { monster_lore *l_ptr = get_lore(m_ptr->race); bitflag f[OF_SIZE], known_f[OF_SIZE], note_f[OF_SIZE]; int i; object_flags(o_ptr, f); object_flags_known(o_ptr, known_f); for (i = 0; i < SL_MAX; i++) { const struct slay *s_ptr = &slay_table[i]; if ((known_only && !of_has(known_f, s_ptr->object_flag)) || (!known_only && !of_has(f, s_ptr->object_flag))) continue; /* In a real attack, learn about monster resistance or slay match if: * EITHER the slay flag on the object is known, * OR the monster is vulnerable to the slay/brand */ if (real && (of_has(known_f, s_ptr->object_flag) || (s_ptr->monster_flag && rf_has(m_ptr->race->flags, s_ptr->monster_flag)) || (s_ptr->resist_flag && !rf_has(m_ptr->race->flags, s_ptr->resist_flag)))) { /* notice any brand or slay that would affect monster */ of_wipe(note_f); of_on(note_f, s_ptr->object_flag); object_notice_slays(o_ptr, note_f); if (m_ptr->ml && s_ptr->monster_flag) rf_on(l_ptr->flags, s_ptr->monster_flag); if (m_ptr->ml && s_ptr->resist_flag) rf_on(l_ptr->flags, s_ptr->resist_flag); } /* If the monster doesn't resist or the slay flag matches */ if ((s_ptr->brand && !rf_has(m_ptr->race->flags, s_ptr->resist_flag)) || (s_ptr->monster_flag && rf_has(m_ptr->race->flags, s_ptr->monster_flag))) { /* compare multipliers to determine best attack */ if ((*best_s_ptr == NULL) || ((*best_s_ptr)->mult < s_ptr->mult)) *best_s_ptr = s_ptr; } } }
/** * Try to push past / kill another monster. Returns true on success. */ static bool process_monster_try_push(struct chunk *c, struct monster *mon, const char *m_name, int nx, int ny) { struct monster *mon1 = square_monster(c, ny, nx); struct monster_lore *lore = get_lore(mon->race); /* Kill weaker monsters */ int kill_ok = rf_has(mon->race->flags, RF_KILL_BODY); /* Move weaker monsters if they can swap places */ /* (not in a wall) */ int move_ok = (rf_has(mon->race->flags, RF_MOVE_BODY) && square_ispassable(c, mon->fy, mon->fx)); if (compare_monsters(mon, mon1) > 0) { /* Learn about pushing and shoving */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) { rf_on(lore->flags, RF_KILL_BODY); rf_on(lore->flags, RF_MOVE_BODY); } if (kill_ok || move_ok) { /* Get the names of the monsters involved */ char n_name[80]; monster_desc(n_name, sizeof(n_name), mon1, MDESC_IND_HID); /* Reveal mimics */ if (is_mimicking(mon1)) become_aware(mon1); /* Note if visible */ if (mflag_has(mon->mflag, MFLAG_VISIBLE) && mflag_has(mon->mflag, MFLAG_VIEW)) msg("%s %s %s.", m_name, kill_ok ? "tramples over" : "pushes past", n_name); /* Monster ate another monster */ if (kill_ok) delete_monster(ny, nx); monster_swap(mon->fy, mon->fx, ny, nx); return true; } } return false; }
/** * Attempt to reproduce, if possible. All monsters are checked here for * lore purposes, the unfit fail. */ static bool process_monster_multiply(struct chunk *c, struct monster *m_ptr) { int oy = m_ptr->fy; int ox = m_ptr->fx; int k = 0, y, x; monster_lore *l_ptr = get_lore(m_ptr->race); /* Too many breeders on the level already */ if (num_repro >= z_info->repro_monster_max) return FALSE; /* Count the adjacent monsters */ for (y = oy - 1; y <= m_ptr->fy + 1; y++) for (x = ox - 1; x <= m_ptr->fx + 1; x++) if (c->squares[y][x].mon > 0) k++; /* Multiply slower in crowded areas */ if ((k < 4) && (k == 0 || one_in_(k * z_info->repro_monster_rate))) { /* Successful breeding attempt, learn about that now */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) rf_on(l_ptr->flags, RF_MULTIPLY); /* Leave now if not a breeder */ if (!rf_has(m_ptr->race->flags, RF_MULTIPLY)) return FALSE; /* Try to multiply */ if (multiply_monster(m_ptr)) { /* Make a sound */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) sound(MSG_MULTIPLY); /* Multiplying takes energy */ return TRUE; } } return FALSE; }
/** * Attempt to reproduce, if possible. All monsters are checked here for * lore purposes, the unfit fail. */ static bool monster_turn_multiply(struct chunk *c, struct monster *mon) { int k = 0, y, x; struct monster_lore *lore = get_lore(mon->race); /* Too many breeders on the level already */ if (num_repro >= z_info->repro_monster_max) return false; /* Count the adjacent monsters */ for (y = mon->fy - 1; y <= mon->fy + 1; y++) for (x = mon->fx - 1; x <= mon->fx + 1; x++) if (c->squares[y][x].mon > 0) k++; /* Multiply slower in crowded areas */ if ((k < 4) && (k == 0 || one_in_(k * z_info->repro_monster_rate))) { /* Successful breeding attempt, learn about that now */ if (monster_is_visible(mon)) rf_on(lore->flags, RF_MULTIPLY); /* Leave now if not a breeder */ if (!rf_has(mon->race->flags, RF_MULTIPLY)) return false; /* Try to multiply */ if (multiply_monster(c, mon)) { /* Make a sound */ if (monster_is_visible(mon)) sound(MSG_MULTIPLY); /* Multiplying takes energy */ return true; } } return false; }
/** * Work out if a monster can move through the grid, if necessary bashing * down doors in the way. * * Returns TRUE if the monster is able to move through the grid. */ static bool process_monster_can_move(struct chunk *c, struct monster *m_ptr, const char *m_name, int nx, int ny, bool *did_something) { monster_lore *l_ptr = get_lore(m_ptr->race); /* Floor is open? */ if (square_ispassable(c, ny, nx)) return TRUE; /* Permanent wall in the way */ if (square_iswall(c, ny, nx) && square_isperm(c, ny, nx)) return FALSE; /* Normal wall, door, or secret door in the way */ /* There's some kind of feature in the way, so learn about * kill-wall and pass-wall now */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) { rf_on(l_ptr->flags, RF_PASS_WALL); rf_on(l_ptr->flags, RF_KILL_WALL); } /* Monster moves through walls (and doors) */ if (rf_has(m_ptr->race->flags, RF_PASS_WALL)) return TRUE; /* Monster destroys walls (and doors) */ else if (rf_has(m_ptr->race->flags, RF_KILL_WALL)) { /* Forget the wall */ sqinfo_off(c->squares[ny][nx].info, SQUARE_MARK); /* Notice */ square_destroy_wall(c, ny, nx); /* Note changes to viewable region */ if (player_has_los_bold(ny, nx)) player->upkeep->update |= PU_UPDATE_VIEW; /* Update the flow, since walls affect flow */ player->upkeep->update |= PU_UPDATE_FLOW; return TRUE; } /* Handle doors and secret doors */ else if (square_iscloseddoor(c, ny, nx) || square_issecretdoor(c, ny, nx)) { bool may_bash = rf_has(m_ptr->race->flags, RF_BASH_DOOR) && one_in_(2); /* Take a turn */ *did_something = TRUE; /* Learn about door abilities */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) { rf_on(l_ptr->flags, RF_OPEN_DOOR); rf_on(l_ptr->flags, RF_BASH_DOOR); } /* Creature can open or bash doors */ if (!rf_has(m_ptr->race->flags, RF_OPEN_DOOR) && !rf_has(m_ptr->race->flags, RF_BASH_DOOR)) return FALSE; /* Stuck door -- try to unlock it */ if (square_islockeddoor(c, ny, nx)) { int k = square_door_power(c, ny, nx); if (randint0(m_ptr->hp / 10) > k) { if (may_bash) msg("%s slams against the door.", m_name); else msg("%s fiddles with the lock.", m_name); /* Reduce the power of the door by one */ square_set_door_lock(c, ny, nx, k - 1); } } else { /* Handle viewable doors */ if (player_has_los_bold(ny, nx)) player->upkeep->update |= PU_UPDATE_VIEW; /* Closed or secret door -- open or bash if allowed */ if (may_bash) { square_smash_door(c, ny, nx); msg("You hear a door burst open!"); disturb(player, 0); /* Fall into doorway */ return TRUE; } else if (rf_has(m_ptr->race->flags, RF_OPEN_DOOR)) { square_open_door(c, ny, nx); } } } return FALSE; }
/** * Work out if a monster can move through the grid, if necessary bashing * down doors in the way. * * Returns true if the monster is able to move through the grid. */ static bool process_monster_can_move(struct chunk *c, struct monster *mon, const char *m_name, int nx, int ny, bool *did_something) { struct monster_lore *lore = get_lore(mon->race); /* Only fiery creatures can handle lava */ if (square_isfiery(c, ny, nx) && !rf_has(mon->race->flags, RF_IM_FIRE)) return false; /* Floor is open? */ if (square_ispassable(c, ny, nx)) return true; /* Permanent wall in the way */ if (square_iswall(c, ny, nx) && square_isperm(c, ny, nx)) return false; /* Normal wall, door, or secret door in the way */ /* There's some kind of feature in the way, so learn about * kill-wall and pass-wall now */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) { rf_on(lore->flags, RF_PASS_WALL); rf_on(lore->flags, RF_KILL_WALL); } /* Monster may be able to deal with walls and doors */ if (rf_has(mon->race->flags, RF_PASS_WALL)) { return true; } else if (rf_has(mon->race->flags, RF_KILL_WALL)) { /* Remove the wall */ square_destroy_wall(c, ny, nx); /* Note changes to viewable region */ if (square_isview(c, ny, nx)) player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); /* Fully update the flow since terrain changed */ player->upkeep->update |= (PU_FORGET_FLOW | PU_UPDATE_FLOW); return true; } else if (square_iscloseddoor(c, ny, nx) || square_issecretdoor(c, ny, nx)) { bool may_bash = rf_has(mon->race->flags, RF_BASH_DOOR) && one_in_(2); /* Take a turn */ *did_something = true; /* Learn about door abilities */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) { rf_on(lore->flags, RF_OPEN_DOOR); rf_on(lore->flags, RF_BASH_DOOR); } /* Creature can open or bash doors */ if (!rf_has(mon->race->flags, RF_OPEN_DOOR) && !rf_has(mon->race->flags, RF_BASH_DOOR)) return false; /* Stuck door -- try to unlock it */ if (square_islockeddoor(c, ny, nx)) { int k = square_door_power(c, ny, nx); if (randint0(mon->hp / 10) > k) { if (may_bash) msg("%s slams against the door.", m_name); else msg("%s fiddles with the lock.", m_name); /* Reduce the power of the door by one */ square_set_door_lock(c, ny, nx, k - 1); } } else { /* Handle viewable doors */ if (square_isview(c, ny, nx)) player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); /* Closed or secret door -- open or bash if allowed */ if (may_bash) { square_smash_door(c, ny, nx); msg("You hear a door burst open!"); disturb(player, 0); /* Fall into doorway */ return true; } else if (rf_has(mon->race->flags, RF_OPEN_DOOR)) { square_open_door(c, ny, nx); } } } return false; }
/** * Determines whether the given monster successfully resists the given effect. * * If MON_TMD_FLG_NOFAIL is set in `flag`, this returns false. * Then we determine if the monster resists the effect for some racial * reason. For example, the monster might have the NO_SLEEP flag, in which * case it always resists sleep. Or if it breathes chaos, it always resists * confusion. If the given monster doesn't resist for any of these reasons, * then it makes a saving throw. If MON_TMD_MON_SOURCE is set in `flag`, * indicating that another monster caused this effect, then the chance of * success on the saving throw just depends on the monster's native depth. * Otherwise, the chance of success decreases as `timer` increases. * * Also marks the lore for any appropriate resists. */ static bool mon_resist_effect(const struct monster *mon, int ef_idx, int timer, u16b flag) { struct mon_timed_effect *effect; int resist_chance; struct monster_lore *lore; assert(ef_idx >= 0 && ef_idx < MON_TMD_MAX); assert(mon); effect = &effects[ef_idx]; lore = get_lore(mon->race); /* Hasting never fails */ if (ef_idx == MON_TMD_FAST) return (false); /* Some effects are marked to never fail */ if (flag & MON_TMD_FLG_NOFAIL) return (false); /* A sleeping monster resists further sleeping */ if (ef_idx == MON_TMD_SLEEP && mon->m_timed[ef_idx]) return (true); /* If the monster resists innately, learn about it */ if (rf_has(mon->race->flags, effect->flag_resist)) { if (mflag_has(mon->mflag, MFLAG_VISIBLE)) rf_on(lore->flags, effect->flag_resist); return (true); } /* Monsters with specific breaths resist stunning */ if (ef_idx == MON_TMD_STUN && (rsf_has(mon->race->spell_flags, RSF_BR_SOUN) || rsf_has(mon->race->spell_flags, RSF_BR_WALL))) { /* Add the lore */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) { if (rsf_has(mon->race->spell_flags, RSF_BR_SOUN)) rsf_on(lore->spell_flags, RSF_BR_SOUN); if (rsf_has(mon->race->spell_flags, RSF_BR_WALL)) rsf_on(lore->spell_flags, RSF_BR_WALL); } return (true); } /* Monsters with specific breaths resist confusion */ if ((ef_idx == MON_TMD_CONF) && rsf_has(mon->race->spell_flags, RSF_BR_CHAO)) { /* Add the lore */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) if (rsf_has(mon->race->spell_flags, RSF_BR_CHAO)) rsf_on(lore->spell_flags, RSF_BR_CHAO); return (true); } /* Inertia breathers resist slowing */ if (ef_idx == MON_TMD_SLOW && rsf_has(mon->race->spell_flags, RSF_BR_INER)){ rsf_on(lore->spell_flags, RSF_BR_INER); return (true); } /* Calculate the chance of the monster making its saving throw. */ if (ef_idx == MON_TMD_SLEEP) timer /= 25; /* Hack - sleep uses much bigger numbers */ if (flag & MON_TMD_MON_SOURCE) resist_chance = mon->race->level; else resist_chance = mon->race->level + 40 - (timer / 2); if (randint0(100) < resist_chance) return (true); /* Uniques are doubly hard to affect */ if (rf_has(mon->race->flags, RF_UNIQUE)) if (randint0(100) < resist_chance) return (true); return (false); }
int context_menu_cave(struct cave *c, int y, int x, int adjacent, int mx, int my) { menu_type *m; region r; int selected; char *labels; bool allowed = TRUE; int mode = OPT(rogue_like_commands) ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG; unsigned char cmdkey; m = menu_dynamic_new(); if (!m) { return 0; } labels = string_make(lower_case); m->selections = labels; /* Looking has different keys, but we don't have a way to look them up (see cmd-process.c). */ cmdkey = (mode == KEYMAP_MODE_ORIG) ? 'l' : 'x'; menu_dynamic_add_label(m, "Look At", cmdkey, MENU_VALUE_LOOK, labels); if (c->m_idx[y][x]) { /* '/' is used for recall in both keymaps. */ menu_dynamic_add_label(m, "Recall Info", '/', MENU_VALUE_RECALL, labels); } ADD_LABEL("Use Item On", CMD_USE_ANY, MN_ROW_VALID); if (player_can_cast(p_ptr, FALSE)) { ADD_LABEL("Cast On", CMD_CAST, MN_ROW_VALID); } if (adjacent) { ADD_LABEL((c->m_idx[y][x]) ? "Attack" : "Alter", CMD_ALTER, MN_ROW_VALID); if (c->o_idx[y][x]) { s16b o_idx = chest_check(y,x, CHEST_ANY); if (o_idx) { object_type *o_ptr = object_byid(o_idx); if (!squelch_item_ok(o_ptr)) { if (object_is_known(o_ptr)) { if (is_locked_chest(o_ptr)) { ADD_LABEL("Disarm Chest", CMD_DISARM, MN_ROW_VALID); ADD_LABEL("Open Chest", CMD_OPEN, MN_ROW_VALID); } else { ADD_LABEL("Open Disarmed Chest", CMD_OPEN, MN_ROW_VALID); } } else { ADD_LABEL("Open Chest", CMD_OPEN, MN_ROW_VALID); } } } } if (cave_istrap(c, y, x)) { ADD_LABEL("Disarm", CMD_DISARM, MN_ROW_VALID); ADD_LABEL("Jump Onto", CMD_JUMP, MN_ROW_VALID); } if (cave_isopendoor(c, y, x)) { ADD_LABEL("Close", CMD_CLOSE, MN_ROW_VALID); } else if (cave_iscloseddoor(c, y, x)) { ADD_LABEL("Open", CMD_OPEN, MN_ROW_VALID); ADD_LABEL("Lock", CMD_DISARM, MN_ROW_VALID); } else if (cave_isdiggable(c, y, x)) { ADD_LABEL("Tunnel", CMD_TUNNEL, MN_ROW_VALID); } ADD_LABEL("Search", CMD_SEARCH, MN_ROW_VALID); ADD_LABEL("Walk Towards", CMD_WALK, MN_ROW_VALID); } else { /* ',' is used for squelch in rogue keymap, so we'll just swap letters. */ cmdkey = (mode == KEYMAP_MODE_ORIG) ? ',' : '.'; menu_dynamic_add_label(m, "Pathfind To", cmdkey, CMD_PATHFIND, labels); ADD_LABEL("Walk Towards", CMD_WALK, MN_ROW_VALID); ADD_LABEL("Run Towards", CMD_RUN, MN_ROW_VALID); } if (player_can_fire(p_ptr, FALSE)) { ADD_LABEL("Fire On", CMD_FIRE, MN_ROW_VALID); } ADD_LABEL("Throw To", CMD_THROW, MN_ROW_VALID); /* work out display region */ r.width = (int)menu_dynamic_longest_entry(m) + 3 + 2; /* +3 for tag, 2 for pad */ if (mx > Term->wid - r.width - 1) { r.col = Term->wid - r.width - 1; } else { r.col = mx + 1; } r.page_rows = m->count; if (my > Term->hgt - r.page_rows - 1) { if (my - r.page_rows - 1 <= 0) { /* menu has too many items, so put in upper right corner */ r.row = 1; r.col = Term->wid - r.width - 1; } else { r.row = Term->hgt - r.page_rows - 1; } } else { r.row = my + 1; } /* Hack -- no flush needed */ msg_flag = FALSE; screen_save(); menu_layout(m, &r); region_erase_bordered(&r); if (p_ptr->timed[TMD_IMAGE]) { prt("(Enter to select command, ESC to cancel) You see something strange:", 0, 0); } else if (c->m_idx[y][x]) { char m_name[80]; monster_type *m_ptr = cave_monster_at(c, y, x); /* Get the monster name ("a kobold") */ monster_desc(m_name, sizeof(m_name), m_ptr, MDESC_IND_VIS); prt(format("(Enter to select command, ESC to cancel) You see %s:", m_name), 0, 0); } else if (c->o_idx[y][x] && !squelch_item_ok(object_byid(c->o_idx[y][x]))) { char o_name[80]; /* Get the single object in the list */ object_type *o_ptr = object_byid(c->o_idx[y][x]); /* Obtain an object description */ object_desc(o_name, sizeof (o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); prt(format("(Enter to select command, ESC to cancel) You see %s:", o_name), 0, 0); } else { /* Feature (apply mimic) */ const char *name = cave_apparent_name(c, p_ptr, y, x); /* Hack -- special introduction for store doors */ if (cave_isshop(cave, y, x)) { prt(format("(Enter to select command, ESC to cancel) You see the entrance to the %s:", name), 0, 0); } else { prt(format("(Enter to select command, ESC to cancel) You see %s %s:", (is_a_vowel(name[0])) ? "an" : "a", name), 0, 0); } } selected = menu_dynamic_select(m); menu_dynamic_free(m); string_free(labels); screen_load(); cmdkey = cmd_lookup_key(selected, mode); /* Check the command to see if it is allowed. */ switch (selected) { case -1: /* User cancelled the menu. */ return 3; case MENU_VALUE_LOOK: case MENU_VALUE_RECALL: case CMD_PATHFIND: allowed = TRUE; break; case CMD_SEARCH: case CMD_ALTER: case CMD_DISARM: case CMD_JUMP: case CMD_CLOSE: case CMD_OPEN: case CMD_TUNNEL: case CMD_WALK: case CMD_RUN: case CMD_CAST: case CMD_FIRE: case CMD_THROW: case CMD_USE_ANY: /* Only check for ^ inscriptions, since we don't have an object selected (if we need one). */ allowed = key_confirm_command(cmdkey); break; default: /* Invalid command; prevent anything from happening. */ bell("Invalid context menu command."); allowed = FALSE; break; } if (!allowed) return 1; /* Perform the command. */ switch (selected) { case MENU_VALUE_LOOK: /* look at the spot */ if (target_set_interactive(TARGET_LOOK, x, y)) { msg("Target Selected."); } break; case MENU_VALUE_RECALL: { /* recall monster Info */ monster_type *m_ptr = cave_monster_at(c, y, x); if (m_ptr) { monster_lore *lore = get_lore(m_ptr->race); lore_show_interactive(m_ptr->race, lore); } } break; case CMD_SEARCH: cmd_insert(selected); break; case CMD_PATHFIND: cmd_insert(selected); cmd_set_arg_point(cmd_get_top(), 0, x, y); break; case CMD_ALTER: case CMD_DISARM: case CMD_JUMP: case CMD_CLOSE: case CMD_OPEN: case CMD_TUNNEL: case CMD_WALK: case CMD_RUN: cmd_insert(selected); cmd_set_arg_direction(cmd_get_top(), 0, coords_to_dir(y,x)); break; case CMD_CAST: if (textui_obj_cast_ret() >= 0) { cmd_set_arg_target(cmd_get_top(), 1, DIR_TARGET); } break; case CMD_FIRE: case CMD_THROW: case CMD_USE_ANY: cmd_insert(selected); cmd_set_arg_target(cmd_get_top(), 1, DIR_TARGET); break; default: break; } return 1; }
/** * 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 ui_event target_set_interactive_aux(int y, int x, int mode) { struct object *obj = NULL; const char *s1, *s2, *s3; bool boring; int floor_max = z_info->floor_size; struct object **floor_list = mem_zalloc(floor_max * sizeof(*floor_list)); int floor_num; ui_event press; char out_val[TARGET_OUT_VAL_SIZE]; char coords[20]; const char *name; /* Describe the square location */ coords_desc(coords, sizeof(coords), y, x); /* Repeat forever */ while (1) { /* Paranoia */ press.type = EVT_KBRD; press.key.code = ' '; press.key.mods = 0; /* Assume boring */ boring = TRUE; /* Default */ s1 = "You see "; s2 = ""; s3 = ""; /* The player */ if (cave->squares[y][x].mon < 0) { /* Description */ s1 = "You are "; /* Preposition */ s2 = "on "; } /* Hallucination messes things up */ if (player->timed[TMD_IMAGE]) { const char *name = "something strange"; /* Display a message */ if (player->wizard) strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d, cost=%d, when=%d).", s1, s2, s3, name, coords, y, x, (int)cave->squares[y][x].cost, (int)cave->squares[y][x].when); 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); press.key = inkey(); /* Stop on everything but "return" */ if (press.key.code == KC_ENTER) continue; mem_free(floor_list); return press; } /* Actual monsters */ if (cave->squares[y][x].mon > 0) { monster_type *m_ptr = square_monster(cave, y, x); const monster_lore *l_ptr = get_lore(m_ptr->race); /* Visible */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE) && !mflag_has(m_ptr->mflag, MFLAG_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_IND_VIS); /* Hack -- track this monster race */ monster_race_track(player->upkeep, m_ptr->race); /* Hack -- health bar for this monster */ health_track(player->upkeep, m_ptr); /* Hack -- handle stuff */ handle_stuff(player); /* Interact */ while (1) { /* Recall or target */ if (recall) { lore_show_interactive(m_ptr->race, l_ptr); press = inkey_m(); } else { char buf[80]; /* Describe the monster */ look_mon_desc(buf, sizeof(buf), cave->squares[y][x].mon); /* Describe, and prompt for recall */ if (player->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s (%s), %s (%d:%d, cost=%d, when=%d).", s1, s2, s3, m_name, buf, coords, y, x, (int)cave->squares[y][x].cost, (int)cave->squares[y][x].when); } 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 */ press = inkey_m(); } /* Normal commands */ if ((press.type == EVT_MOUSE) && (press.mouse.button == 1) && (KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y)) recall = !recall; else if ((press.type == EVT_KBRD) && (press.key.code == 'r')) recall = !recall; else break; } if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button == 2) break; /* Sometimes stop at "space" key */ if (press.mouse.button && !(mode & (TARGET_LOOK))) break; } else { /* Stop on everything but "return"/"space" */ if (press.key.code != KC_ENTER && press.key.code != ' ') break; /* Sometimes stop at "space" key */ if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break; } /* Take account of gender */ if (rf_has(m_ptr->race->flags, RF_FEMALE)) s1 = "She is "; else if (rf_has(m_ptr->race->flags, RF_MALE)) s1 = "He is "; else s1 = "It is "; /* Use a verb */ s2 = "carrying "; /* Scan all objects being carried */ for (obj = m_ptr->held_obj; obj; obj = obj->next) { char o_name[80]; /* Obtain an object description */ object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); /* Describe the object */ if (player->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d, cost=%d, when=%d).", s1, s2, s3, o_name, coords, y, x, (int)cave->squares[y][x].cost, (int)cave->squares[y][x].when); } prt(out_val, 0, 0); move_cursor_relative(y, x); press = inkey_m(); if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button == 2) break; /* Sometimes stop at "space" key */ if (press.mouse.button && !(mode & (TARGET_LOOK))) break; } else { /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; /* Sometimes stop at "space" key */ if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break; } /* Change the intro */ s2 = "also carrying "; } /* Double break */ if (obj) break; /* Use a preposition */ s2 = "on "; } } /* A trap */ if (square_isvisibletrap(cave, y, x)) { struct trap *trap = cave->squares[y][x].trap; /* Not boring */ boring = FALSE; /* Interact */ while (1) { /* Change the intro */ if (cave->squares[y][x].mon < 0) { s1 = "You are "; s2 = "on "; } else { s1 = "You see "; s2 = ""; } /* Pick proper indefinite article */ s3 = (is_a_vowel(trap->kind->desc[0])) ? "an " : "a "; /* Describe, and prompt for recall */ if (player->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d, cost=%d, when=%d).", s1, s2, s3, trap->kind->name, coords, y, x, (int)cave->squares[y][x].cost, (int)cave->squares[y][x].when); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, trap->kind->desc, coords); } prt(out_val, 0, 0); /* Place cursor */ move_cursor_relative(y, x); /* Command */ press = inkey_m(); /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; /* Sometimes stop at "space" key */ if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break; } } /* Double break */ if (square_isvisibletrap(cave, y, x)) break; /* Assume not floored */ floor_num = scan_floor(floor_list, floor_max, y, x, 0x0A, NULL); /* Scan all marked objects in the grid */ if ((floor_num > 0) && (!(player->timed[TMD_BLIND]) || (y == player->py && x == player->px))) { /* Not boring */ boring = FALSE; track_object(player->upkeep, floor_list[0]); handle_stuff(player); /* If there is more than one item... */ if (floor_num > 1) while (1) { /* Describe the pile */ if (player->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%sa pile of %d objects, %s (%d:%d, cost=%d, when=%d).", s1, s2, s3, floor_num, coords, y, x, (int)cave->squares[y][x].cost, (int)cave->squares[y][x].when); } 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); press = inkey_m(); /* Display objects */ if (((press.type == EVT_MOUSE) && (press.mouse.button == 1) && (KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y)) || ((press.type == EVT_KBRD) && (press.key.code == 'r'))) { int rdone = 0; int pos; while (!rdone) { /* Save screen */ screen_save(); /* Display */ show_floor(floor_list, floor_num, (OLIST_WEIGHT | OLIST_GOLD), NULL); /* Describe the pile */ prt(out_val, 0, 0); press = inkey_m(); /* Load screen */ screen_load(); if (press.type == EVT_MOUSE) { pos = press.mouse.y-1; } else { pos = press.key.code - 'a'; } if (0 <= pos && pos < floor_num) { track_object(player->upkeep, floor_list[pos]); handle_stuff(player); continue; } rdone = 1; } /* Now that the user's done with the display loop, * let's do the outer loop over again */ continue; } /* Done */ break; } /* Only one object to display */ else { /* Get the single object in the list */ struct object *obj = floor_list[0]; /* Allow user to recall an object */ press = target_recall_loop_object(obj, y, x, out_val, s1, s2, s3, coords); /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; /* Sometimes stop at "space" key */ if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break; /* Plurals */ s1 = VERB_AGREEMENT(obj->number, "It is ", "They are "); /* Preposition */ s2 = "on "; } } /* Double break */ if (obj) break; name = square_apparent_name(cave, player, y, x); /* Terrain feature if needed */ if (boring || square_isinteresting(cave, y, x)) { /* Hack -- handle unknown grids */ /* Pick a prefix */ if (*s2 && square_isdoor(cave, y, x)) s2 = "in "; /* Pick proper indefinite article */ s3 = (is_a_vowel(name[0])) ? "an " : "a "; /* Hack -- special introduction for store doors */ if (square_isshop(cave, y, x)) s3 = "the entrance to the "; /* Display a message */ if (player->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d, cost=%d, when=%d).", s1, s2, s3, name, coords, y, x, (int)cave->squares[y][x].cost, (int)cave->squares[y][x].when); } 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); press = inkey_m(); if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button == 2) break; } else { /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; } } /* Stop on everything but "return" */ if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button != 2) break; } else { if (press.key.code != KC_ENTER) break; } } mem_free(floor_list); /* Keep going */ return (press); }
/** * Work out if a monster can move through the grid, if necessary bashing * down doors in the way. * * Returns true if the monster is able to move through the grid. */ static bool monster_turn_can_move(struct chunk *c, struct monster *mon, const char *m_name, int nx, int ny, bool *did_something) { struct monster_lore *lore = get_lore(mon->race); /* Dangerous terrain in the way */ if (monster_hates_grid(c, mon, ny, nx)) { return false; } /* Floor is open? */ if (square_ispassable(c, ny, nx)) { return true; } /* Permanent wall in the way */ if (square_iswall(c, ny, nx) && square_isperm(c, ny, nx)) { return false; } /* Normal wall, door, or secret door in the way */ /* There's some kind of feature in the way, so learn about * kill-wall and pass-wall now */ if (monster_is_visible(mon)) { rf_on(lore->flags, RF_PASS_WALL); rf_on(lore->flags, RF_KILL_WALL); } /* Monster may be able to deal with walls and doors */ if (rf_has(mon->race->flags, RF_PASS_WALL)) { return true; } else if (rf_has(mon->race->flags, RF_KILL_WALL)) { /* Remove the wall */ square_destroy_wall(c, ny, nx); /* Note changes to viewable region */ if (square_isview(c, ny, nx)) player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); return true; } else if (square_iscloseddoor(c, ny, nx) || square_issecretdoor(c, ny, nx)) { bool can_open = rf_has(mon->race->flags, RF_OPEN_DOOR); bool can_bash = rf_has(mon->race->flags, RF_BASH_DOOR); bool will_bash = false; /* Take a turn */ *did_something = true; /* Learn about door abilities */ if (monster_is_visible(mon)) { rf_on(lore->flags, RF_OPEN_DOOR); rf_on(lore->flags, RF_BASH_DOOR); } /* If creature can open or bash doors, make a choice */ if (can_open) { /* Sometimes bash anyway (impatient) */ if (can_bash) { will_bash = one_in_(2) ? true : false; } } else if (can_bash) { /* Only choice */ will_bash = true; } else { /* Door is an insurmountable obstacle */ return false; } /* Now outcome depends on type of door */ if (square_islockeddoor(c, ny, nx)) { /* Locked door -- test monster strength against door strength */ int k = square_door_power(c, ny, nx); if (randint0(mon->hp / 10) > k) { if (will_bash) { msg("%s slams against the door.", m_name); } else { msg("%s fiddles with the lock.", m_name); } /* Reduce the power of the door by one */ square_set_door_lock(c, ny, nx, k - 1); } } else { /* Closed or secret door -- always open or bash */ if (square_isview(c, ny, nx)) player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); if (will_bash) { square_smash_door(c, ny, nx); msg("You hear a door burst open!"); disturb(player, 0); /* Fall into doorway */ return true; } else { square_open_door(c, ny, nx); } } } return false; }
/** * Process a monster's timed effects, e.g. decrease them. * * Returns TRUE if the monster is skipping its turn. */ static bool process_monster_timed(struct chunk *c, struct monster *m_ptr) { monster_lore *l_ptr = get_lore(m_ptr->race); /* Handle "sleep" */ if (m_ptr->m_timed[MON_TMD_SLEEP]) { bool woke_up = FALSE; /* Anti-stealth */ int notice = randint0(1024); /* Aggravation */ if (player_of_has(player, OF_AGGRAVATE)) { /* Wake the monster and notify player */ mon_clear_timed(m_ptr, MON_TMD_SLEEP, MON_TMD_FLG_NOTIFY, FALSE); woke_up = TRUE; /* Hack See if monster "notices" player */ } else if ((notice * notice * notice) <= player->state.noise) { int d = 1; /* Wake up faster near the player */ if (m_ptr->cdis < 50) d = (100 / m_ptr->cdis); /* Note a complete wakeup */ if (m_ptr->m_timed[MON_TMD_SLEEP] <= d) woke_up = TRUE; /* Monster wakes up a bit */ mon_dec_timed(m_ptr, MON_TMD_SLEEP, d, MON_TMD_FLG_NOTIFY, FALSE); /* Update knowledge */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE) && !mflag_has(m_ptr->mflag, MFLAG_UNAWARE)) { if (!woke_up && l_ptr->ignore < MAX_UCHAR) l_ptr->ignore++; else if (woke_up && l_ptr->wake < MAX_UCHAR) l_ptr->wake++; lore_update(m_ptr->race, l_ptr); } } /* Update the health bar */ if (woke_up && mflag_has(m_ptr->mflag, MFLAG_VISIBLE) && !mflag_has(m_ptr->mflag, MFLAG_UNAWARE) && player->upkeep->health_who == m_ptr) player->upkeep->redraw |= (PR_HEALTH); /* Sleeping monsters don't recover in any other ways */ /* If the monster just woke up, then it doesn't act */ return TRUE; } if (m_ptr->m_timed[MON_TMD_FAST]) mon_dec_timed(m_ptr, MON_TMD_FAST, 1, 0, FALSE); if (m_ptr->m_timed[MON_TMD_SLOW]) mon_dec_timed(m_ptr, MON_TMD_SLOW, 1, 0, FALSE); if (m_ptr->m_timed[MON_TMD_STUN]) { int d = 1; /* Make a "saving throw" against stun */ if (randint0(5000) <= m_ptr->race->level * m_ptr->race->level) /* Recover fully */ d = m_ptr->m_timed[MON_TMD_STUN]; /* Hack -- Recover from stun */ mon_dec_timed(m_ptr, MON_TMD_STUN, d, MON_TMD_FLG_NOTIFY, FALSE); } if (m_ptr->m_timed[MON_TMD_CONF]) { int d = randint1(m_ptr->race->level / 10 + 1); mon_dec_timed(m_ptr, MON_TMD_CONF, d, MON_TMD_FLG_NOTIFY, FALSE); } if (m_ptr->m_timed[MON_TMD_FEAR]) { int d = randint1(m_ptr->race->level / 10 + 1); mon_dec_timed(m_ptr, MON_TMD_FEAR, d, MON_TMD_FLG_NOTIFY, FALSE); } /* Don't do anything if stunned */ return m_ptr->m_timed[MON_TMD_STUN] ? TRUE : FALSE; }
/** * Process a monster's timed effects, e.g. decrease them. * * Returns true if the monster is skipping its turn. */ static bool process_monster_timed(struct chunk *c, struct monster *mon) { struct monster_lore *lore = get_lore(mon->race); /* Handle "sleep" */ if (mon->m_timed[MON_TMD_SLEEP]) { bool woke_up = false; /* Anti-stealth */ int notice = randint0(1024); /* Aggravation */ if (player_of_has(player, OF_AGGRAVATE)) { char m_name[80]; /* Wake the monster */ mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false); /* Get the monster name */ monster_desc(m_name, sizeof(m_name), mon, MDESC_CAPITAL | MDESC_IND_HID); /* Notify the player if aware */ if (mflag_has(mon->mflag, MFLAG_VISIBLE) && !mflag_has(mon->mflag, MFLAG_UNAWARE)) msg("%s wakes up.", m_name); woke_up = true; } else if ((notice * notice * notice) <= player->state.noise) { /* See if monster "notices" player */ int d = 1; /* Wake up faster near the player */ if (mon->cdis < 50) d = (100 / mon->cdis); /* Note a complete wakeup */ if (mon->m_timed[MON_TMD_SLEEP] <= d) woke_up = true; /* Monster wakes up a bit */ mon_dec_timed(mon, MON_TMD_SLEEP, d, MON_TMD_FLG_NOTIFY, false); /* Update knowledge */ if (mflag_has(mon->mflag, MFLAG_VISIBLE) && !mflag_has(mon->mflag, MFLAG_UNAWARE)) { if (!woke_up && lore->ignore < UCHAR_MAX) lore->ignore++; else if (woke_up && lore->wake < UCHAR_MAX) lore->wake++; lore_update(mon->race, lore); } } /* Sleeping monsters don't recover in any other ways */ /* If the monster just woke up, then it doesn't act */ return true; } if (mon->m_timed[MON_TMD_FAST]) mon_dec_timed(mon, MON_TMD_FAST, 1, 0, false); if (mon->m_timed[MON_TMD_SLOW]) mon_dec_timed(mon, MON_TMD_SLOW, 1, 0, false); if (mon->m_timed[MON_TMD_STUN]) { int d = 1; /* Make a "saving throw" against stun */ if (randint0(5000) <= mon->race->level * mon->race->level) /* Recover fully */ d = mon->m_timed[MON_TMD_STUN]; /* Hack -- Recover from stun */ mon_dec_timed(mon, MON_TMD_STUN, d, MON_TMD_FLG_NOTIFY, false); } if (mon->m_timed[MON_TMD_CONF]) { int d = randint1(mon->race->level / 10 + 1); mon_dec_timed(mon, MON_TMD_CONF, d, MON_TMD_FLG_NOTIFY, false); } if (mon->m_timed[MON_TMD_FEAR]) { int d = randint1(mon->race->level / 10 + 1); mon_dec_timed(mon, MON_TMD_FEAR, d, MON_TMD_FLG_NOTIFY, false); } /* Don't do anything if stunned */ return mon->m_timed[MON_TMD_STUN] ? true : false; }
/** * 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; } }
/** * 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(struct monster *m_ptr, int dam, bool *fear, const char *note) { s32b div, new_exp, new_exp_frac; monster_lore *l_ptr = get_lore(m_ptr->race); /* Redraw (later) if needed */ if (p_ptr->health_who == m_ptr) p_ptr->redraw |= (PR_HEALTH); /* Wake it up */ mon_clear_timed(m_ptr, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, FALSE); /* Become aware of its presence */ if (m_ptr->unaware) become_aware(m_ptr); /* 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(m_ptr->race->flags, RF_UNIQUE)) { if (m_ptr->race->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 { char *str = format("%s%s", m_name, note); my_strcap(str); msgt(soundfx, "%s", str); } } /* 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(m_ptr->race)) 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)m_ptr->race->mexp * m_ptr->race->level) / div; /* Handle fractional experience */ new_exp_frac = ((((long)m_ptr->race->mexp * m_ptr->race->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(m_ptr->race->flags, RF_UNIQUE)) { char unique_name[80]; m_ptr->race->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_ptr, FALSE); /* Recall even invisible uniques or winners */ if (m_ptr->ml || rf_has(m_ptr->race->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->race); } /* Delete the monster */ delete_monster_idx(m_ptr->midx); /* 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_ptr, MON_TMD_FEAR, tmp, MON_TMD_FLG_NOMESSAGE, FALSE); } /* Cure all the fear */ else { /* Cure fear */ mon_clear_timed(m_ptr, 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(m_ptr->race->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_ptr, MON_TMD_FEAR, timer, MON_TMD_FLG_NOMESSAGE | MON_TMD_FLG_NOFAIL, FALSE); } } /* Not dead yet */ return (FALSE); }
/** * Attack the player via physical attacks. */ bool make_attack_normal(struct monster *mon, struct player *p) { struct monster_lore *lore = get_lore(mon->race); int ap_cnt; int k, tmp, ac, rlev; char m_name[80]; char ddesc[80]; bool blinked; /* Not allowed to attack */ if (rf_has(mon->race->flags, RF_NEVER_BLOW)) return (false); /* Total armor */ ac = p->state.ac + p->state.to_a; /* Extract the effective monster level */ rlev = ((mon->race->level >= 1) ? mon->race->level : 1); /* Get the monster name (or "it") */ monster_desc(m_name, sizeof(m_name), mon, MDESC_STANDARD); /* Get the "died from" information (i.e. "a kobold") */ monster_desc(ddesc, sizeof(ddesc), mon, MDESC_SHOW | MDESC_IND_VIS); /* Assume no blink */ blinked = false; /* Scan through all blows */ for (ap_cnt = 0; ap_cnt < z_info->mon_blows_max; ap_cnt++) { bool visible = false; bool obvious = false; bool do_break = false; int power = 0; int damage = 0; int do_cut = 0; int do_stun = 0; int sound_msg = MSG_GENERIC; const char *act = NULL; /* Extract the attack infomation */ int effect = mon->race->blow[ap_cnt].effect; int method = mon->race->blow[ap_cnt].method; random_value dice = mon->race->blow[ap_cnt].dice; /* Hack -- no more attacks */ if (!method) break; /* Handle "leaving" */ if (p->is_dead || p->upkeep->generate_level) break; /* Extract visibility (before blink) */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) visible = true; /* Extract visibility from carrying light */ if (rf_has(mon->race->flags, RF_HAS_LIGHT)) visible = true; /* Extract the attack "power" */ power = monster_blow_effect_power(effect); /* Monster hits player */ if (!effect || check_hit(p, power, rlev)) { melee_effect_handler_f effect_handler; /* Always disturbing */ disturb(p, 1); /* Hack -- Apply "protection from evil" */ if (p->timed[TMD_PROTEVIL] > 0) { /* Learn about the evil flag */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) rf_on(lore->flags, RF_EVIL); if (rf_has(mon->race->flags, RF_EVIL) && p->lev >= rlev && randint0(100) + p->lev > 50) { /* Message */ msg("%s is repelled.", m_name); /* Hack -- Next attack */ continue; } } /* Describe the attack method */ act = monster_blow_method_action(method); do_cut = monster_blow_method_cut(method); do_stun = monster_blow_method_stun(method); sound_msg = monster_blow_method_message(method); /* Message */ if (act) msgt(sound_msg, "%s %s", m_name, act); /* Hack -- assume all attacks are obvious */ obvious = true; /* Roll dice */ damage = randcalc(dice, rlev, RANDOMISE); /* Perform the actual effect. */ effect_handler = melee_handler_for_blow_effect(effect); if (effect_handler != NULL) { melee_effect_handler_context_t context = { p, mon, rlev, method, ac, ddesc, obvious, blinked, do_break, damage, }; effect_handler(&context); /* Save any changes made in the handler for later use. */ obvious = context.obvious; blinked = context.blinked; damage = context.damage; do_break = context.do_break; } else { msg("ERROR: Effect handler not found for %d.", effect); } /* Don't cut or stun if player is dead */ if (p->is_dead) { do_cut = false; do_stun = false; } /* Hack -- only one of cut or stun */ if (do_cut && do_stun) { /* Cancel cut */ if (randint0(100) < 50) do_cut = 0; /* Cancel stun */ else do_stun = 0; } /* Handle cut */ if (do_cut) { /* Critical hit (zero if non-critical) */ tmp = monster_critical(dice, rlev, damage); /* Roll for damage */ switch (tmp) { case 0: k = 0; break; case 1: k = randint1(5); break; case 2: k = randint1(5) + 5; break; case 3: k = randint1(20) + 20; break; case 4: k = randint1(50) + 50; break; case 5: k = randint1(100) + 100; break; case 6: k = 300; break; default: k = 500; break; } /* Apply the cut */ if (k) (void)player_inc_timed(p, TMD_CUT, k, true, true); } /* Handle stun */ if (do_stun) { /* Critical hit (zero if non-critical) */ tmp = monster_critical(dice, rlev, damage); /* Roll for damage */ switch (tmp) { case 0: k = 0; break; case 1: k = randint1(5); break; case 2: k = randint1(10) + 10; break; case 3: k = randint1(20) + 20; break; case 4: k = randint1(30) + 30; break; case 5: k = randint1(40) + 40; break; case 6: k = 100; break; default: k = 200; break; } /* Apply the stun */ if (k) (void)player_inc_timed(p, TMD_STUN, k, true, true); } } else { /* Visible monster missed player, so notify if appropriate. */ if (mflag_has(mon->mflag, MFLAG_VISIBLE) && monster_blow_method_miss(method)) { /* Disturbing */ disturb(p, 1); msg("%s misses you.", m_name); } } /* Analyze "visible" monsters only */ if (visible) { /* Count "obvious" attacks (and ones that cause damage) */ if (obvious || damage || (lore->blows[ap_cnt].times_seen > 10)) { /* Count attacks of this type */ if (lore->blows[ap_cnt].times_seen < UCHAR_MAX) lore->blows[ap_cnt].times_seen++; } } /* Skip the other blows if necessary */ if (do_break) break; } /* Blink away */ if (blinked) { char dice[5]; msg("There is a puff of smoke!"); strnfmt(dice, sizeof(dice), "%d", z_info->max_sight * 2 + 5); effect_simple(EF_TELEPORT, dice, 0, 0, 0, NULL); } /* Always notice cause of death */ if (p->is_dead && (lore->deaths < SHRT_MAX)) lore->deaths++; /* Learn lore */ lore_update(mon->race, lore); /* Assume we attacked */ return (true); }
/** * Creatures can cast spells, shoot missiles, and breathe. * * Returns "true" if a spell (or whatever) was (successfully) cast. * * XXX XXX XXX This function could use some work, but remember to * keep it as optimized as possible, while retaining generic code. * * Verify the various "blind-ness" checks in the code. * * XXX XXX XXX Note that several effects should really not be "seen" * if the player is blind. * * Perhaps monsters should breathe at locations *near* the player, * since this would allow them to inflict "partial" damage. * * Perhaps smart monsters should decline to use "bolt" spells if * there is a monster in the way, unless they wish to kill it. * * It will not be possible to "correctly" handle the case in which a * monster attempts to attack a location which is thought to contain * the player, but which in fact is nowhere near the player, since this * might induce all sorts of messages about the attack itself, and about * the effects of the attack, which the player might or might not be in * a position to observe. Thus, for simplicity, it is probably best to * only allow "faulty" attacks by a monster if one of the important grids * (probably the initial or final grid) is in fact in view of the player. * It may be necessary to actually prevent spell attacks except when the * monster actually has line of sight to the player. Note that a monster * could be left in a bizarre situation after the player ducked behind a * pillar and then teleported away, for example. * * Note that this function attempts to optimize the use of spells for the * cases in which the monster has no spells, or has spells but cannot use * them, or has spells but they will have no "useful" effect. Note that * this function has been an efficiency bottleneck in the past. * * Note the special "MFLAG_NICE" flag, which prevents a monster from using * any spell attacks until the player has had a single chance to move. */ bool make_attack_spell(struct monster *mon) { int chance, thrown_spell, rlev, failrate; bitflag f[RSF_SIZE]; struct monster_lore *lore = get_lore(mon->race); char m_name[80], m_poss[80], ddesc[80]; /* Player position */ int px = player->px; int py = player->py; /* Extract the blind-ness */ bool blind = (player->timed[TMD_BLIND] ? true : false); /* Extract the "see-able-ness" */ bool seen = (!blind && mflag_has(mon->mflag, MFLAG_VISIBLE)); /* Assume "normal" target */ bool normal = true; /* Cannot cast spells when confused */ if (mon->m_timed[MON_TMD_CONF]) return (false); /* Cannot cast spells when nice */ if (mflag_has(mon->mflag, MFLAG_NICE)) return false; /* Hack -- Extract the spell probability */ chance = (mon->race->freq_innate + mon->race->freq_spell) / 2; /* Not allowed to cast spells */ if (!chance) return false; /* Only do spells occasionally */ if (randint0(100) >= chance) return false; /* Hack -- require projectable player */ if (normal) { /* Check range */ if (mon->cdis > z_info->max_range) return false; /* Check path */ if (!projectable(cave, mon->fy, mon->fx, py, px, PROJECT_NONE)) return false; } /* Extract the monster level */ rlev = ((mon->race->level >= 1) ? mon->race->level : 1); /* Extract the racial spell flags */ rsf_copy(f, mon->race->spell_flags); /* Allow "desperate" spells */ if (rf_has(mon->race->flags, RF_SMART) && mon->hp < mon->maxhp / 10 && randint0(100) < 50) /* Require intelligent spells */ ignore_spells(f, RST_BOLT | RST_BALL | RST_BREATH | RST_ATTACK | RST_INNATE); /* Remove the "ineffective" spells */ remove_bad_spells(mon, f); /* Check whether summons and bolts are worth it. */ if (!rf_has(mon->race->flags, RF_STUPID)) { /* Check for a clean bolt shot */ if (test_spells(f, RST_BOLT) && !projectable(cave, mon->fy, mon->fx, py, px, PROJECT_STOP)) /* Remove spells that will only hurt friends */ ignore_spells(f, RST_BOLT); /* Check for a possible summon */ if (!(summon_possible(mon->fy, mon->fx))) /* Remove summoning spells */ ignore_spells(f, RST_SUMMON); } /* No spells left */ if (rsf_is_empty(f)) return false; /* Get the monster name (or "it") */ monster_desc(m_name, sizeof(m_name), mon, MDESC_STANDARD); /* Get the monster possessive ("his"/"her"/"its") */ monster_desc(m_poss, sizeof(m_poss), mon, MDESC_PRO_VIS | MDESC_POSS); /* Get the "died from" name */ monster_desc(ddesc, sizeof(ddesc), mon, MDESC_DIED_FROM); /* Choose a spell to cast */ thrown_spell = choose_attack_spell(f); /* Abort if no spell was chosen */ if (!thrown_spell) return false; /* If we see an unaware monster try to cast a spell, become aware of it */ if (mflag_has(mon->mflag, MFLAG_UNAWARE)) become_aware(mon); /* Calculate spell failure rate */ failrate = 25 - (rlev + 3) / 4; if (mon->m_timed[MON_TMD_FEAR]) failrate += 20; /* Stupid monsters will never fail (for jellies and such) */ if (rf_has(mon->race->flags, RF_STUPID)) failrate = 0; /* Check for spell failure (innate attacks never fail) */ if (!mon_spell_is_innate(thrown_spell) && (randint0(100) < failrate)) { /* Message */ msg("%s tries to cast a spell, but fails.", m_name); return true; } /* Cast the spell. */ disturb(player, 1); do_mon_spell(thrown_spell, mon, seen); /* Remember what the monster did to us */ if (seen) { rsf_on(lore->spell_flags, thrown_spell); /* Innate spell */ if (mon_spell_is_innate(thrown_spell)) { if (lore->cast_innate < UCHAR_MAX) lore->cast_innate++; } else { /* Bolt or Ball, or Special spell */ if (lore->cast_spell < UCHAR_MAX) lore->cast_spell++; } } /* Always take note of monsters that kill you */ if (player->is_dead && (lore->deaths < SHRT_MAX)) { lore->deaths++; } /* Record any new info */ lore_update(mon->race, lore); /* A spell was cast */ return true; }
/** * Process a monster * * In several cases, we directly update the monster lore * * Note that a monster is only allowed to "reproduce" if there * are a limited number of "reproducing" monsters on the current * level. This should prevent the level from being "swamped" by * reproducing monsters. It also allows a large mass of mice to * prevent a louse from multiplying, but this is a small price to * pay for a simple multiplication method. * * XXX Monster fear is slightly odd, in particular, monsters will * fixate on opening a door even if they cannot open it. Actually, * the same thing happens to normal monsters when they hit a door * * In addition, monsters which *cannot* open or bash down a door * will still stand there trying to open it... XXX XXX XXX * * Technically, need to check for monster in the way combined * with that monster being in a wall (or door?) XXX */ static void process_monster(struct chunk *c, struct monster *mon) { struct monster_lore *lore = get_lore(mon->race); bool did_something = false; int i; int dir = 0; bool stagger = false; char m_name[80]; /* Get the monster name */ monster_desc(m_name, sizeof(m_name), mon, MDESC_CAPITAL | MDESC_IND_HID); /* Try to multiply - this can use up a turn */ if (process_monster_multiply(c, mon)) return; /* Attempt to cast a spell */ if (make_attack_spell(mon)) return; /* Work out what kind of movement to use - AI or staggered movement */ if (!process_monster_should_stagger(mon)) { if (!get_moves(c, mon, &dir)) return; } else { stagger = true; } /* Process moves */ for (i = 0; i < 5 && !did_something; i++) { int oy = mon->fy; int ox = mon->fx; /* Get the direction (or stagger) */ int d = (stagger ? ddd[randint0(8)] : side_dirs[dir][i]); /* Get the destination */ int ny = oy + ddy[d]; int nx = ox + ddx[d]; /* Check if we can move */ if (!process_monster_can_move(c, mon, m_name, nx, ny, &did_something)) continue; /* Try to break the glyph if there is one. This can happen multiple * times per turn because failure does not break the loop */ if (square_iswarded(c, ny, nx) && !process_monster_glyph(c, mon, nx, ny)) continue; /* The player is in the way. */ if (square_isplayer(c, ny, nx)) { /* Learn about if the monster attacks */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) rf_on(lore->flags, RF_NEVER_BLOW); /* Some monsters never attack */ if (rf_has(mon->race->flags, RF_NEVER_BLOW)) continue; /* Otherwise, attack the player */ make_attack_normal(mon, player); did_something = true; break; } else { /* Some monsters never move */ if (rf_has(mon->race->flags, RF_NEVER_MOVE)) { /* Learn about lack of movement */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) rf_on(lore->flags, RF_NEVER_MOVE); return; } } /* A monster is in the way, try to push past/kill */ if (square_monster(c, ny, nx)) { did_something = process_monster_try_push(c, mon, m_name, nx, ny); } else { /* Otherwise we can just move */ monster_swap(oy, ox, ny, nx); did_something = true; } /* Scan all objects in the grid, if we reached it */ if (mon == square_monster(c, ny, nx)) { monster_desc(m_name, sizeof(m_name), mon, MDESC_CAPITAL | MDESC_IND_HID); process_monster_grab_objects(c, mon, m_name, nx, ny); } } if (did_something) { /* Learn about no lack of movement */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) rf_on(lore->flags, RF_NEVER_MOVE); /* Possible disturb */ if (mflag_has(mon->mflag, MFLAG_VISIBLE) && mflag_has(mon->mflag, MFLAG_VIEW) && OPT(player, disturb_near)) disturb(player, 0); } /* Hack -- get "bold" if out of options */ if (!did_something && mon->m_timed[MON_TMD_FEAR]) mon_clear_timed(mon, MON_TMD_FEAR, MON_TMD_FLG_NOTIFY, false); /* If we see an unaware monster do something, become aware of it */ if (did_something && mflag_has(mon->mflag, MFLAG_UNAWARE)) become_aware(mon); }
/** * Grab all objects from the grid. */ void process_monster_grab_objects(struct chunk *c, struct monster *m_ptr, const char *m_name, int nx, int ny) { monster_lore *l_ptr = get_lore(m_ptr->race); struct object *obj = square_object(c, ny, nx); bool is_item = obj ? TRUE : FALSE;; if (is_item && mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) { rf_on(l_ptr->flags, RF_TAKE_ITEM); rf_on(l_ptr->flags, RF_KILL_ITEM); } /* Abort if can't pickup/kill */ if (!rf_has(m_ptr->race->flags, RF_TAKE_ITEM) && !rf_has(m_ptr->race->flags, RF_KILL_ITEM)) { return; } /* Take or kill objects on the floor */ while (obj) { char o_name[80]; bool safe = obj->artifact ? TRUE : FALSE; struct object *next = obj->next; /* Skip gold */ if (tval_is_money(obj)) { obj = next; continue; } /* Get the object name */ object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); /* React to objects that hurt the monster */ if (react_to_slay(obj, m_ptr)) safe = TRUE; /* The object cannot be picked up by the monster */ if (safe) { /* Only give a message for "take_item" */ if (rf_has(m_ptr->race->flags, RF_TAKE_ITEM) && mflag_has(m_ptr->mflag, MFLAG_VISIBLE) && player_has_los_bold(ny, nx) && !ignore_item_ok(obj)) { /* Dump a message */ msg("%s tries to pick up %s, but fails.", m_name, o_name); } /* Pick up the item */ } else if (rf_has(m_ptr->race->flags, RF_TAKE_ITEM)) { /* Describe observable situations */ if (player_has_los_bold(ny, nx) && !ignore_item_ok(obj)) msg("%s picks up %s.", m_name, o_name); /* Carry the object */ square_excise_object(c, ny, nx, obj); monster_carry(c, m_ptr, obj); /* Destroy the item */ } else { /* Describe observable situations */ if (player_has_los_bold(ny, nx) && !ignore_item_ok(obj)) msgt(MSG_DESTROY, "%s crushes %s.", m_name, o_name); /* Delete the object */ square_excise_object(c, ny, nx, obj); object_delete(obj); } /* Next object */ obj = next; } }
//static struct keypress target_set_interactive_aux(int y, int x, int mode) static ui_event 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 floor_list[MAX_FLOOR_STACK]; int floor_num; //struct keypress query; ui_event press; char out_val[256]; char coords[20]; const char *name; /* Describe the square location */ coords_desc(coords, sizeof(coords), y, x); /* Repeat forever */ while (1) { /* Paranoia */ press.type = EVT_KBRD; press.key.code = ' '; press.key.mods = 0; /* 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 "; } /* Hallucination messes things up */ 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); //input = inkey_m(); //if ( press.key = inkey(); /* Stop on everything but "return" */ if (press.key.code == KC_ENTER) continue; return press; } /* Actual monsters */ if (cave->m_idx[y][x] > 0) { monster_type *m_ptr = cave_monster_at(cave, y, x); const monster_lore *l_ptr = get_lore(m_ptr->race); /* 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->race); /* Hack -- health bar for this monster */ health_track(p_ptr, m_ptr); /* Hack -- handle stuff */ handle_stuff(p_ptr); /* Interact */ while (1) { /* Recall */ if (recall) { /* Save screen */ screen_save(); /* Recall on screen */ screen_roff(m_ptr->race, l_ptr); /* Command */ press = inkey_m(); /* 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 */ press = inkey_m(); } /* Normal commands */ if ((press.type == EVT_MOUSE) && (press.mouse.button == 1) && (KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y)) recall = !recall; else if ((press.type == EVT_KBRD) && (press.key.code == 'r')) recall = !recall; else break; } if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button == 2) break; /* Sometimes stop at "space" key */ if (press.mouse.button && !(mode & (TARGET_LOOK))) break; } else { /* Stop on everything but "return"/"space" */ if (press.key.code != KC_ENTER && press.key.code != ' ') break; /* Sometimes stop at "space" key */ if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break; } /* Take account of gender */ if (rf_has(m_ptr->race->flags, RF_FEMALE)) s1 = "She is "; else if (rf_has(m_ptr->race->flags, RF_MALE)) s1 = "He is "; else s1 = "It is "; /* Use a verb */ 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); } /* Disabled since monsters now carry their drops 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); press = inkey_m(); if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button == 2) break; /* Sometimes stop at "space" key */ if (press.mouse.button && !(mode & (TARGET_LOOK))) break; } else { /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; /* Sometimes stop at "space" key */ if ((press.key.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, 0x0A); /* 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(p_ptr); /* 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); press = inkey_m(); /* Display objects */ if (((press.type == EVT_MOUSE) && (press.mouse.button == 1) && (KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y)) || ((press.type == EVT_KBRD) && (press.key.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); press = inkey_m(); /* Load screen */ screen_load(); if (press.type == EVT_MOUSE) { pos = press.mouse.y-1; } else { pos = press.key.code - 'a'; } if (0 <= pos && pos < floor_num) { track_object(-floor_list[pos]); handle_stuff(p_ptr); 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); press = inkey_m(); /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; /* Sometimes stop at "space" key */ if ((press.key.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; name = cave_apparent_name(cave, p_ptr, y, x); /* Terrain feature if needed */ if (boring || cave_isinteresting(cave, y, x)) { /* Hack -- handle unknown grids */ /* Pick a prefix */ if (*s2 && cave_isdoor(cave, y, x)) s2 = "in "; /* Pick proper indefinite article */ s3 = (is_a_vowel(name[0])) ? "an " : "a "; /* Hack -- special introduction for store doors */ if (cave_isshop(cave, y, x)) { 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); press = inkey_m(); if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button == 2) break; } else { /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; } } /* Stop on everything but "return" */ if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button != 2) break; } else { if (press.key.code != KC_ENTER) break; } } /* Keep going */ return (press); }
/* * Ask for and parse a "debug command" */ void do_cmd_debug(void) { int py = p_ptr->py; int px = p_ptr->px; struct keypress cmd; /* Get a "debug command" */ if (!get_com("Debug Command: ", &cmd)) return; /* Analyze the command */ switch (cmd.code) { /* Ignore */ case ESCAPE: case ' ': case KC_ENTER: { break; } #ifdef ALLOW_SPOILERS /* Hack -- Generate Spoilers */ case '"': { do_cmd_spoilers(); break; } #endif /* Hack -- Help */ case '?': { do_cmd_wiz_help(); break; } /* Cure all maladies */ case 'a': { do_cmd_wiz_cure_all(); break; } /* Make the player powerful */ case 'A': { do_cmd_wiz_advance(); break; } /* Teleport to target */ case 'b': { do_cmd_wiz_bamf(); break; } /* Create any object */ case 'c': { wiz_create_item(); break; } /* Create an artifact */ case 'C': { char name[80] = ""; int a_idx = -1; /* Avoid the prompt getting in the way */ screen_save(); /* Prompt */ prt("Create which artifact? ", 0, 0); /* Get the name */ if (askfor_aux(name, sizeof(name), NULL)) { /* See if an a_idx was entered */ a_idx = get_idx_from_name(name); /* If not, find the artifact with that name */ if (a_idx < 1) a_idx = lookup_artifact_name(name); /* Did we find a valid artifact? */ if (a_idx != -1 && a_idx < z_info->a_max) wiz_create_artifact(a_idx); else msg("No artifact found."); } /* Reload the screen */ screen_load(); break; } /* Detect everything */ case 'd': { detect_all(TRUE); break; } /* Test for disconnected dungeon */ case 'D': { disconnect_stats(); break; } /* Edit character */ case 'e': { do_cmd_wiz_change(); break; } case 'f': { stats_collect(); break; } /* Good Objects */ case 'g': { int n; screen_save(); n= get_quantity("How many good objects? ", 40); screen_load(); if (n < 1) n = 1; acquirement(py, px, p_ptr->depth, n, FALSE); break; } /* GF demo */ case 'G': { wiz_gf_demo(); break; } /* Hitpoint rerating */ case 'h': { do_cmd_rerate(); break; } /* Hit all monsters in LOS */ case 'H': { dispel_monsters(10000); break; } /* Identify */ case 'i': { (void)ident_spell(); break; } /* Go up or down in the dungeon */ case 'j': { do_cmd_wiz_jump(); break; } /* Learn about objects */ case 'l': { do_cmd_wiz_learn(100); break; } case 'L': do_cmd_keylog(); break; /* Magic Mapping */ case 'm': { map_area(); break; } /* Summon Named Monster */ case 'n': { monster_race *r = NULL; char name[80] = ""; /* Avoid the prompt getting in the way */ screen_save(); /* Prompt */ prt("Summon which monster? ", 0, 0); /* Get the name */ if (askfor_aux(name, sizeof(name), NULL)) { /* See if a r_idx was entered */ int r_idx = get_idx_from_name(name); if (r_idx) r = &r_info[r_idx]; else /* If not, find the monster with that name */ r = lookup_monster(name); p_ptr->redraw |= (PR_MAP | PR_MONLIST); } /* Reload the screen */ screen_load(); if (r) do_cmd_wiz_named(r, TRUE); else msg("No monster found."); break; } /* Object playing routines */ case 'o': { do_cmd_wiz_play(); break; } /* Phase Door */ case 'p': { teleport_player(10); break; } /* Monster pit stats */ case 'P': { pit_stats(); break; } /* Query the dungeon */ case 'q': { do_cmd_wiz_query(); break; } /* Get full recall for a monster */ case 'r': { const monster_race *r_ptr = NULL; struct keypress sym; char *prompt = "Full recall for [a]ll monsters or [s]pecific monster? "; if (!get_com(prompt, &sym)) return; if (sym.code == 'a' || sym.code == 'A') { int i; for (i = 0; i < z_info->r_max; i++) cheat_monster_lore(&r_info[i], &l_list[i]); msg("Done."); } else if (sym.code == 's' || sym.code == 'S') { char name[80] = ""; /* Avoid the prompt getting in the way */ screen_save(); /* Prompt */ prt("Which monster? ", 0, 0); /* Get the name */ if (askfor_aux(name, sizeof(name), NULL)) { /* See if a r_idx was entered */ int r_idx = get_idx_from_name(name); if (r_idx) r_ptr = &r_info[r_idx]; else /* If not, find the monster with that name */ r_ptr = lookup_monster(name); } /* Reload the screen */ screen_load(); /* Did we find a valid monster? */ if (r_ptr) cheat_monster_lore(r_ptr, get_lore(r_ptr)); else msg("No monster found."); } break; } /* Summon Random Monster(s) */ case 's': { int n; screen_save(); n = get_quantity("How many monsters? ", 40); screen_load(); if (n < 1) n = 1; do_cmd_wiz_summon(n); break; } /* Collect stats (S) */ case 'S': { stats_collect(); break; } /* Teleport */ case 't': { teleport_player(100); break; } /* Create a trap */ case 'T': { if (!cave_isfloor(cave, p_ptr->py, p_ptr->px)) msg("You can't place a trap there!"); else if (p_ptr->depth == 0) msg("You can't place a trap in the town!"); else cave_add_trap(cave, p_ptr->py, p_ptr->px); break; } /* Un-hide all monsters */ case 'u': { detect_monsters_entire_level(); break; } /* Very Good Objects */ case 'v': { int n; screen_save(); n = get_quantity("How many great objects? ", 40); screen_load(); if (n < 1) n = 1; acquirement(py, px, p_ptr->depth, n, TRUE); break; } case 'V': { int n; screen_save(); n = get_quantity("Create all items of what tval? ", 255); screen_load(); if (n) wiz_test_kind(n); break; } /* Wizard Light the Level */ case 'w': { wiz_light(TRUE); break; } /* Wipe recall for a monster */ case 'W': { const monster_race *r_ptr = NULL; s16b r_idx = 0; struct keypress sym; char *prompt = "Wipe recall for [a]ll monsters or [s]pecific monster? "; if (!get_com(prompt, &sym)) return; if (sym.code == 'a' || sym.code == 'A') { int i; for (i = 0; i < z_info->r_max; i++) wipe_monster_lore(&r_info[i], &l_list[i]); msg("Done."); } else if (sym.code == 's' || sym.code == 'S') { char name[80] = ""; /* Avoid the prompt getting in the way */ screen_save(); /* Prompt */ prt("Which monster? ", 0, 0); /* Get the name */ if (askfor_aux(name, sizeof(name), NULL)) { /* See if a r_idx was entered */ r_idx = get_idx_from_name(name); if (r_idx) r_ptr = &r_info[r_idx]; else /* If not, find the monster with that name */ r_ptr = lookup_monster(name); } /* Reload the screen */ screen_load(); /* Did we find a valid monster? */ if (r_ptr) wipe_monster_lore(r_ptr, get_lore(r_ptr)); else msg("No monster found."); } break; } /* Increase Experience */ case 'x': { int n; screen_save(); n = get_quantity("Gain how much experience? ", 9999); screen_load(); if (n < 1) n = 1; player_exp_gain(p_ptr, n); break; } /* Zap Monsters (Banishment) */ case 'z': { int n; screen_save(); n = get_quantity("Zap within what distance? ", MAX_SIGHT); screen_load(); do_cmd_wiz_zap(n); break; } /* Hack */ case '_': { do_cmd_wiz_hack_ben(); break; } /* Oops */ default: { msg("That is not a valid debug command."); break; } } }