/src/monster2.c
C | 4842 lines | 2665 code | 937 blank | 1240 comment | 818 complexity | 3e481455d0ae8766e0139bb6846f0c6e MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- /** \file monster2.c
- \brief Monster generation, learning and removal
- * Monster processing, compacting, generation, goody drops, deletion,
- * place the player, monsters, and their escorts at a given location,
- * generation of monsters, summoning, monster reaction to pain
- * levels, monster learning.
- *
- * Copyright (c) 2009 Nick McConnell, Leon Marrick & Bahman Rabii,
- * Ben Harrison, James E. Wilson, Robert A. Koeneke
- *
- * This work is free software; you can redistribute it and/or modify it
- * under the terms of either:
- *
- * a) the GNU General Public License as published by the Free Software
- * Foundation, version 2, or
- *
- * b) the "Angband licence":
- * This software may be copied and distributed for educational, research,
- * and not for profit purposes provided that this copyright and statement
- * are included in all such copies. Other copyrights may also apply.
- */
- #include "angband.h"
- #include "cave.h"
- #include "generate.h"
- #include "history.h"
- #include "monster.h"
- #include "player.h"
- #include "target.h"
- #include "trap.h"
- /**
- * Delete a monster by index.
- *
- * When a monster is deleted, all of its objects are deleted.
- */
- void delete_monster_idx(int i)
- {
- int x, y;
- monster_type *m_ptr = &m_list[i];
- monster_race *r_ptr = &r_info[m_ptr->r_idx];
- monster_lore *l_ptr = &l_list[m_ptr->r_idx];
- s16b this_o_idx, next_o_idx = 0;
- /* Get location */
- y = m_ptr->fy;
- x = m_ptr->fx;
- /* Hack -- Reduce the racial counter */
- r_ptr->cur_num--;
- /* Hack -- count the number of "reproducers" */
- if (rf_has(r_ptr->flags, RF_MULTIPLY))
- num_repro--;
- /* Hack -- remove target monster */
- if (target_get_monster() == i)
- target_set_monster(0);
- /* Hack -- remove tracked monster */
- if (p_ptr->health_who == i)
- health_track(0);
- /* Monster is gone */
- cave_m_idx[y][x] = 0;
- /* Total Hack -- If the monster was a player ghost, remove it from the
- * monster memory, ensure that it never appears again, clear its bones
- * file selector and allow the next ghost to speak. */
- if (rf_has(r_ptr->flags, RF_PLAYER_GHOST)) {
- l_ptr->sights = 0;
- l_ptr->deaths = 0;
- l_ptr->pkills = 0;
- l_ptr->tkills = 0;
- bones_selector = 0;
- ghost_has_spoken = FALSE;
- r_ptr->rarity = 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;
- /* Acquire object */
- o_ptr = &o_list[this_o_idx];
- /* Acquire next object */
- next_o_idx = o_ptr->next_o_idx;
- /* Hack -- efficiency */
- o_ptr->held_m_idx = 0;
- /* Delete the object */
- delete_object_idx(this_o_idx);
- }
- /* Wipe the Monster */
- (void) WIPE(m_ptr, monster_type);
- /* Count monsters */
- m_cnt--;
- /* Visual update */
- light_spot(y, x);
- }
- /**
- * Delete the monster, if any, at a given location
- */
- void delete_monster(int y, int x)
- {
- /* Paranoia */
- if (!in_bounds(y, x))
- return;
- /* Delete the monster (if any) */
- if (cave_m_idx[y][x] > 0)
- delete_monster_idx(cave_m_idx[y][x]);
- }
- /**
- * Move an object from index i1 to index i2 in the object 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 = &m_list[i1];
- /* Location */
- y = m_ptr->fy;
- x = m_ptr->fx;
- /* Update the cave */
- cave_m_idx[y][x] = 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;
- /* Acquire object */
- o_ptr = &o_list[this_o_idx];
- /* Acquire next object */
- next_o_idx = o_ptr->next_o_idx;
- /* Reset monster pointer */
- o_ptr->held_m_idx = i2;
- }
- /* Hack -- Update the target */
- if (target_get_monster() == i1)
- target_set_monster(i2);
- /* Hack -- Update the health bar */
- if (p_ptr->health_who == i1)
- p_ptr->health_who = i2;
- /* Hack -- move monster */
- (void) COPY(&m_list[i2], &m_list[i1], monster_type);
- /* Hack -- wipe hole */
- (void) WIPE(&m_list[i1], monster_type);
- }
- /**
- * Compact and Reorder the monster list
- *
- * This function can be very dangerous, use with caution!
- *
- * When actually "compacting" monsters, we base the saving throw
- * on a combination of monster level, distance from player, and
- * current "desperation".
- *
- * After "compacting" (if needed), we "reorder" the monsters into a more
- * compact order, and we reset the allocation info, and the "live" array.
- */
- void compact_monsters(int size)
- {
- int i, j, num, cnt;
- int cur_lev, cur_dis, chance;
- /* Message (only if compacting) */
- if (size)
- msg("Compacting monsters...");
- /* Compact at least 'size' objects */
- for (num = 0, cnt = 1; num < size; cnt++) {
- /* Get more vicious each iteration */
- cur_lev = 5 * cnt;
- /* Get closer each iteration */
- cur_dis = 5 * (20 - cnt);
- j = 0;
- /* Check all the monsters */
- for (i = 1; i < m_max; i++) {
- monster_type *m_ptr = &m_list[i];
- monster_race *r_ptr = &r_info[m_ptr->r_idx];
- /* Paranoia -- skip "dead" monsters */
- if (!m_ptr->r_idx) {
- if (j++ >= size)
- return;
- else
- continue;
- }
- /* Hack -- High level monsters start out "immune" */
- if (r_ptr->level > cur_lev)
- continue;
- /* Ignore nearby monsters */
- if ((cur_dis > 0) && (m_ptr->cdis < cur_dis))
- continue;
- /* Saving throw chance */
- chance = 90;
- /* Only compact "Quest" Monsters in emergencies */
- if ((rf_has(r_ptr->flags, RF_QUESTOR)) && (cnt < 1000))
- chance = 100;
- /* Try not to compact Unique Monsters */
- if (rf_has(r_ptr->flags, RF_UNIQUE))
- chance = 99;
- /* All monsters get a saving throw */
- if (randint0(100) < chance)
- continue;
- /* Delete the monster */
- delete_monster_idx(i);
- /* Count the monster */
- num++;
- }
- }
- /* Excise dead monsters (backwards!) */
- for (i = m_max - 1; i >= 1; i--) {
- /* Get the i'th monster */
- monster_type *m_ptr = &m_list[i];
- /* Skip real monsters */
- if (m_ptr->r_idx)
- continue;
- /* Move last monster into open hole */
- compact_monsters_aux(m_max - 1, i);
- /* Compress "m_max" */
- m_max--;
- }
- }
- /**
- * Delete/Remove all the monsters when the player leaves the level
- *
- * This is an efficient method of simulating multiple calls to the
- * "delete_monster()" function, with no visual effects.
- */
- void wipe_m_list(void)
- {
- int i;
- /* Delete all the monsters */
- for (i = m_max - 1; i >= 1; i--) {
- monster_type *m_ptr = &m_list[i];
- monster_race *r_ptr = &r_info[m_ptr->r_idx];
- monster_lore *l_ptr = &l_list[m_ptr->r_idx];
- /* Skip dead monsters */
- if (!m_ptr->r_idx)
- continue;
- /* Total Hack -- Clear player ghost information. */
- if (rf_has(r_ptr->flags, RF_PLAYER_GHOST)) {
- l_ptr->sights = 0;
- l_ptr->deaths = 0;
- l_ptr->pkills = 0;
- l_ptr->tkills = 0;
- bones_selector = 0;
- ghost_has_spoken = FALSE;
- r_ptr->rarity = 0;
- }
- /* Hack -- Reduce the racial counter */
- r_ptr->cur_num--;
- /* Monster is gone */
- cave_m_idx[m_ptr->fy][m_ptr->fx] = 0;
- /* Wipe the Monster */
- (void) WIPE(m_ptr, monster_type);
- }
- /* Hack - wipe the player */
- cave_m_idx[p_ptr->py][p_ptr->px] = 0;
- /* Reset "m_max" */
- m_max = 1;
- /* Reset "m_cnt" */
- m_cnt = 0;
- /* Hack -- reset "reproducer" count */
- num_repro = 0;
- /* Hack -- no more target */
- target_set_monster(0);
- /* Hack -- no more tracking */
- health_track(0);
- /* Hack -- make sure there is no player ghost */
- bones_selector = 0;
- }
- /**
- * Acquires and returns the index of a "free" monster.
- *
- * This routine should almost never fail, but it *can* happen.
- */
- s16b m_pop(void)
- {
- int i;
- /* Normal allocation */
- if (m_max < z_info->m_max) {
- /* Access the next hole */
- i = m_max;
- /* Expand the array */
- m_max++;
- /* Count monsters */
- m_cnt++;
- /* Return the index */
- return (i);
- }
- /* Recycle dead monsters */
- for (i = 1; i < m_max; i++) {
- monster_type *m_ptr;
- /* Acquire monster */
- m_ptr = &m_list[i];
- /* Skip live monsters */
- if (m_ptr->r_idx)
- continue;
- /* Count monsters */
- m_cnt++;
- /* Use this monster */
- return (i);
- }
- /* Warn the player (except during dungeon creation) */
- if (character_dungeon)
- msg("Too many monsters!");
- /* Try not to crash */
- return (0);
- }
- /**
- * Apply a "monster restriction function" to the "monster allocation table"
- */
- errr get_mon_num_prep(void)
- {
- int i;
- /* Scan the allocation table */
- for (i = 0; i < alloc_race_size; i++) {
- /* Get the entry */
- alloc_entry *entry = &alloc_race_table[i];
- /* Accept monsters which pass the restriction, if any */
- if (!get_mon_num_hook || (*get_mon_num_hook) (entry->index)) {
- /* Accept this monster */
- entry->prob2 = entry->prob1;
- }
- /* Do not use this monster */
- else {
- /* Decline this monster */
- entry->prob2 = 0;
- }
- }
- /* Success */
- return (0);
- }
- /**
- * Choose a monster race that seems "appropriate" to the given level
- *
- * We use this function, not only to pick a monster but to build a
- * table of probabilities. This table can be used again and again, if
- * certain conditions (generation level being the most important) don't
- * change.
- *
- * This function uses the "prob2" field of the "monster allocation table",
- * and various local information, to calculate the "prob3" field of the
- * same table, which is then used to choose an "appropriate" monster, in
- * a relatively efficient manner.
- *
- * Note that "town" monsters will *only* be created in the town, and
- * "normal" monsters will *never* be created in the town, unless the
- * "level" is "modified", for example, by polymorph or summoning.
- *
- * There is a small chance (1/40) of "boosting" the given depth by
- * a small amount (up to four levels), except in the town.
- *
- * It is (slightly) more likely to acquire a monster of the given level
- * than one of a lower level. This is done by choosing several monsters
- * appropriate to the given level and keeping the "hardest" one.
- *
- * Note that if no monsters are "appropriate", then this function will
- * fail, and return zero, but this should *almost* never happen.
- */
- s16b get_mon_num(int level)
- {
- int i, d;
- int r_idx;
- long value;
- int failure = 0;
- int temp_level = level;
- monster_race *r_ptr;
- alloc_entry *table = alloc_race_table;
- /* Low-level monsters avoid the deep dungeon. */
- int depth_rare = 2 * level / 3;
- int depth_very_rare = level / 3;
- /* Sometimes, monsters in the dungeon can be out of depth */
- if (p_ptr->danger != 0) {
- /* Occasional boost to maximum level */
- if (randint0(NASTY_MON) == 0) {
- /* Pick a level bonus */
- d = level / 10 + 1;
- /* Boost the level */
- temp_level += ((d < 5) ? d : 5);
- /* Occasional second boost */
- if (randint0(NASTY_MON) == 0) {
- /* Pick a level bonus */
- d = level / 10 + 1;
- /* Boost the level */
- temp_level += ((d < 5) ? d : 5);
- }
- }
- }
- /* Try hard to find a suitable monster */
- while (TRUE) {
- /* Reset sum of final monster probabilities. */
- alloc_race_total = 0L;
- /* Process probabilities */
- for (i = 0; i < alloc_race_size; i++) {
- /* Assume no probability */
- table[i].prob3 = 0;
- /* Ignore illegal monsters */
- if (!table[i].prob2)
- continue;
- /* Monsters are sorted by depth */
- if (table[i].level > temp_level)
- continue;
- /* Hack -- No town monsters in dungeon */
- if ((p_ptr->danger != 0) && (table[i].level < 1))
- continue;
- /* Get the monster index */
- r_idx = table[i].index;
- /* Get the actual race */
- r_ptr = &r_info[r_idx];
- /* Hack -- some monsters are unique */
- if ((rf_has(r_ptr->flags, RF_UNIQUE))
- && (r_ptr->cur_num >= r_ptr->max_num))
- continue;
- /* Forced-depth monsters only appear at their level. */
- if ((rf_has(r_ptr->flags, RF_FORCE_DEPTH))
- && (r_ptr->level != p_ptr->danger))
- continue;
- /* Hack - dungeon-only monsters */
- if ((rf_has(r_ptr->flags, RF_DUNGEON))
- && (chunk_list[p_ptr->stage].z_pos == 0))
- continue;
- /* Accept */
- table[i].prob3 = table[i].prob2;
- /* Now modifications for locality etc follow -NRM-
- BELE all gone */
- /* Keep low-level monsters rare */
- if (table[i].level < depth_rare)
- table[i].prob3 /= 4;
- if (table[i].level < depth_very_rare)
- table[i].prob3 /= 4;
- /* Sum up probabilities */
- alloc_race_total += table[i].prob3;
- }
- /* No legal monsters */
- if (alloc_race_total == 0) {
- failure++;
- if (failure == 1) {
- /* Try relaxing the level restrictions */
- if (p_ptr->themed_level)
- temp_level += 20;
- else
- temp_level += 10;
- } else {
- /* Our monster restrictions are too stringent. */
- return (0);
- }
- }
- /* Success */
- else
- break;
- }
- /* Pick a monster */
- value = randint0(alloc_race_total);
- /* Find the monster */
- for (i = 0; i < alloc_race_size; i++) {
- /* Found the entry */
- if (value < table[i].prob3)
- break;
- /* Decrement */
- value = value - table[i].prob3;
- }
- /* Result */
- return (table[i].index);
- }
- /**
- * A replacement for "get_mon_num()", for use when that function has
- * built up a suitable table of monster probabilities, and all we want
- * to do is pull another monster from it.
- *
- * Usage of this function has a curious and quite intentional effect:
- * on rare occasion, 1 in NASTY_MON times, the effective generation level
- * is raised somewhat. Most monsters will actually end up being in depth,
- * but not all...
- */
- s16b get_mon_num_quick(int level)
- {
- int i;
- long value;
- alloc_entry *table = alloc_race_table;
- /*
- * No monsters available. XXX XXX - try using the standard
- * function again, although it probably failed the first time.
- */
- if (!alloc_race_total)
- return (get_mon_num(level));
- /* Pick a monster */
- value = randint0(alloc_race_total);
- /* Find the monster */
- for (i = 0; i < alloc_race_size; i++) {
- /* Found the entry */
- if (value < table[i].prob3)
- break;
- /* Decrement */
- value = value - table[i].prob3;
- }
- /* Result */
- return (table[i].index);
- }
- /**
- * Mega-hack - Fix plural names of monsters
- *
- * Taken from PernAngband via EY, modified to fit NPP monster list
- *
- * Note: It should handle all regular Angband monsters.
- *
- * TODO: Specify monster name plurals in monster.txt instead.
- */
- void plural_aux(char *name, size_t max)
- {
- int name_len = strlen(name);
- if (strstr(name, " of ")) {
- char *aider = strstr(name, " of ");
- char dummy[80];
- int i = 0;
- char *ctr = name;
- while (ctr < aider) {
- dummy[i] = *ctr;
- ctr++;
- i++;
- }
- if (dummy[i - 1] == 's') {
- strcpy(&(dummy[i]), "es");
- i++;
- } else {
- strcpy(&(dummy[i]), "s");
- }
- strcpy(&(dummy[i + 1]), aider);
- my_strcpy(name, dummy, max);
- } else if ((strstr(name, "coins")) || (strstr(name, "gems"))) {
- char dummy[80];
- strcpy(dummy, "Piles of c");
- my_strcat(dummy, &(name[1]), sizeof(dummy));
- my_strcpy(name, dummy, max);
- return;
- }
- else if (strstr(name, "Greater Servant of")) {
- char dummy[80];
- strcpy(dummy, "Greater Servants of ");
- my_strcat(dummy, &(name[1]), sizeof(dummy));
- my_strcpy(name, dummy, max);
- return;
- } else if (strstr(name, "Lesser Servant of")) {
- char dummy[80];
- strcpy(dummy, "Greater Servants of ");
- my_strcat(dummy, &(name[1]), sizeof(dummy));
- my_strcpy(name, dummy, max);
- return;
- } else if (strstr(name, "Servant of")) {
- char dummy[80];
- strcpy(dummy, "Servants of ");
- my_strcat(dummy, &(name[1]), sizeof(dummy));
- my_strcpy(name, dummy, max);
- return;
- } else if (strstr(name, "Great Wyrm")) {
- char dummy[80];
- strcpy(dummy, "Great Wyrms ");
- my_strcat(dummy, &(name[1]), sizeof(dummy));
- my_strcpy(name, dummy, max);
- return;
- } else if (strstr(name, "Spawn of")) {
- char dummy[80];
- strcpy(dummy, "Spawn of ");
- my_strcat(dummy, &(name[1]), sizeof(dummy));
- my_strcpy(name, dummy, max);
- return;
- } else if (strstr(name, "Descendant of")) {
- char dummy[80];
- strcpy(dummy, "Descendant of ");
- my_strcat(dummy, &(name[1]), sizeof(dummy));
- my_strcpy(name, dummy, max);
- return;
- } else if ((strstr(name, "Manes")) || (name[name_len - 1] == 'u')
- || (strstr(name, "Yeti"))
- || (streq(&(name[name_len - 2]), "ua"))
- || (streq(&(name[name_len - 3]), "nee"))
- || (streq(&(name[name_len - 4]), "idhe"))) {
- return;
- } else if (name[name_len - 1] == 'y') {
- strcpy(&(name[name_len - 1]), "ies");
- } else if (streq(&(name[name_len - 4]), "ouse")) {
- strcpy(&(name[name_len - 4]), "ice");
- } else if (streq(&(name[name_len - 4]), "lung")) {
- strcpy(&(name[name_len - 4]), "lungen");
- } else if (streq(&(name[name_len - 3]), "sus")) {
- strcpy(&(name[name_len - 3]), "si");
- } else if (streq(&(name[name_len - 4]), "star")) {
- strcpy(&(name[name_len - 4]), "stari");
- } else if (streq(&(name[name_len - 3]), "aia")) {
- strcpy(&(name[name_len - 3]), "aiar");
- } else if (streq(&(name[name_len - 3]), "inu")) {
- strcpy(&(name[name_len - 3]), "inur");
- } else if (streq(&(name[name_len - 5]), "culus")) {
- strcpy(&(name[name_len - 5]), "culi");
- } else if (streq(&(name[name_len - 4]), "sman")) {
- strcpy(&(name[name_len - 4]), "smen");
- } else if (streq(&(name[name_len - 4]), "lman")) {
- strcpy(&(name[name_len - 4]), "lmen");
- } else if (streq(&(name[name_len - 2]), "ex")) {
- strcpy(&(name[name_len - 2]), "ices");
- } else if ((name[name_len - 1] == 'f')
- && (!streq(&(name[name_len - 2]), "ff"))) {
- strcpy(&(name[name_len - 1]), "ves");
- } else
- if (((streq(&(name[name_len - 2]), "ch"))
- || (name[name_len - 1] == 's'))
- && (!streq(&(name[name_len - 5]), "iarch"))) {
- strcpy(&(name[name_len]), "es");
- } else {
- strcpy(&(name[name_len]), "s");
- }
- }
- /**
- * Helper function for display monlist. Prints the number of creatures,
- * followed by either a singular or plural version of the race name as
- * appropriate.
- */
- static void get_mon_name(char *output_name, size_t max,
- const monster_race * r_ptr, int num)
- {
- char race_name[80];
- assert(r_ptr);
- my_strcpy(race_name, r_ptr->name, sizeof(race_name));
- /* Unique names don't have a number */
- if (rf_has(r_ptr->flags, RF_UNIQUE))
- my_strcpy(output_name, "[U] ", max);
- /* Normal races */
- else {
- my_strcpy(output_name, format("%3d ", num), max);
- /* Make it plural, if needed. */
- if (num > 1)
- plural_aux(race_name, sizeof(race_name));
- }
- /* Mix the quantity and the header. */
- my_strcat(output_name, race_name, max);
- }
- /*
- * Monster data for the visible monster list
- */
- typedef struct {
- u16b count; /* total number of this type visible */
- u16b asleep; /* number asleep (not in LOS) */
- u16b neutral; /* number neutral (not in LOS) */
- u16b los; /* number in LOS */
- u16b los_asleep; /* number asleep and in LOS */
- u16b los_neutral; /* number neutral and in LOS */
- byte attr; /* attr to use for drawing */
- } monster_vis;
- /*
- * Display visible monsters in a window
- */
- void display_monlist(void)
- {
- int ii;
- size_t i, j, k;
- int max;
- int line = 1, x = 0;
- int cur_x;
- unsigned total_count = 0, disp_count = 0, type_count = 0, los_count =
- 0;
- byte attr;
- char m_name[80];
- char buf[80];
- monster_type *m_ptr;
- monster_race *r_ptr;
- monster_race *r2_ptr;
- monster_vis *list;
- u16b *order;
- bool in_term = (Term != angband_term[0]);
- /* Hallucination is weird */
- if (p_ptr->timed[TMD_IMAGE]) {
- if (in_term)
- clear_from(0);
- Term_gotoxy(0, 0);
- text_out_to_screen(TERM_ORANGE,
- "Your hallucinations are too wild to see things clearly.");
- return;
- }
- /* Clear the term if in a subwindow, set x otherwise */
- if (in_term) {
- clear_from(0);
- max = Term->hgt - 1;
- } else {
- x = 13;
- max = Term->hgt - 2;
- }
- /* Allocate the primary array */
- list = C_ZNEW(z_info->r_max, monster_vis);
- /* Scan the list of monsters on the level */
- for (ii = 1; ii < m_max; ii++) {
- monster_vis *v;
- m_ptr = &m_list[ii];
- r_ptr = &r_info[m_ptr->r_idx];
- /* Only consider visible, known monsters */
- if (!m_ptr->ml)
- continue;
- /* Take a pointer to this monster visibility entry */
- v = &list[m_ptr->r_idx];
- /* Note each monster type and save its display attr (color) */
- if (!v->count)
- type_count++;
- if (!v->attr)
- v->attr = m_ptr->attr ? m_ptr->attr : r_ptr->x_attr;
- /* Check for LOS
- * Hack - we should use (m_ptr->mflag & (MFLAG_VIEW)) here,
- * but this does not catch monsters detected by ESP which are
- * targetable, so we cheat and use projectable() instead
- */
- if (projectable(p_ptr->py, p_ptr->px, m_ptr->fy, m_ptr->fx,
- PROJECT_CHCK)) {
- /* Increment the total number of in-LOS monsters */
- los_count++;
- /* Increment the LOS count for this monster type */
- v->los++;
- /* Check if asleep or neutral and increment */
- if (m_ptr->hostile >= 0)
- v->los_neutral++;
- else if (m_ptr->csleep)
- v->los_asleep++;
- }
- /* Not in LOS so increment if asleep */
- else {
- if (m_ptr->hostile >= 0)
- v->neutral++;
- else if (m_ptr->csleep)
- v->asleep++;
- }
- /* Bump the count for this race, and the total count */
- v->count++;
- total_count++;
- }
- /* Note no visible monsters at all */
- if (!total_count) {
- /* Clear display and print note */
- c_prt(TERM_SLATE, "You see no monsters.", 0, 0);
- if (!in_term)
- Term_addstr(-1, TERM_WHITE, " (Press any key to continue.)");
- /* Free up memory */
- FREE(list);
- /* Done */
- return;
- }
- /* Allocate the secondary array */
- order = C_ZNEW(type_count, u16b);
- /* Sort, because we cannot rely on monster.txt being ordered */
- /* Populate the ordered array, starting at 1 to ignore @ */
- for (i = 1; i < z_info->r_max; i++) {
- /* No monsters of this race are visible */
- if (!list[i].count)
- continue;
- /* Get the monster info */
- r_ptr = &r_info[i];
- /* Fit this monster into the sorted array */
- for (j = 0; j < type_count; j++) {
- /* If we get to the end of the list, put this one in */
- if (!order[j]) {
- order[j] = i;
- break;
- }
- /* Get the monster info for comparison */
- r2_ptr = &r_info[order[j]];
- /* Monsters are sorted by depth */
- if (r_ptr->level > r2_ptr->level) {
- /* Move weaker monsters down the array */
- for (k = type_count - 1; k > j; k--) {
- order[k] = order[k - 1];
- }
- /* Put current monster in the right place */
- order[j] = i;
- break;
- }
- }
- }
- /* Message for monsters in LOS - even if there are none */
- if (!los_count)
- prt(format("You can see no monsters."), 0, 0);
- else
- prt(format("You can see %d monster%s", los_count, (los_count == 1
- ? ":" : "s:")),
- 0, 0);
- /* Print out in-LOS monsters in descending order */
- for (i = 0; (i < type_count) && (line < max); i++) {
- /* Skip if there are none of these in LOS */
- if (!list[order[i]].los)
- continue;
- /* Reset position */
- cur_x = x;
- /* Note that these have been displayed */
- disp_count += list[order[i]].los;
- /* Get monster race and name */
- r_ptr = &r_info[order[i]];
- get_mon_name(m_name, sizeof(m_name), r_ptr, list[order[i]].los);
- /* Display uniques in a special colour */
- if (rf_has(r_ptr->flags, RF_UNIQUE))
- attr = TERM_VIOLET;
- else if (r_ptr->level > p_ptr->danger)
- attr = TERM_RED;
- else
- attr = TERM_WHITE;
- /* Build the monster name */
- if (list[order[i]].los == 1) {
- if (list[order[i]].los_asleep == 1)
- strnfmt(buf, sizeof(buf), "%s (asleep) ", m_name);
- else if (list[order[i]].los_neutral == 1)
- strnfmt(buf, sizeof(buf), "%s (neutral) ", m_name);
- else
- strnfmt(buf, sizeof(buf), "%s ", m_name);
- } else {
- if (list[order[i]].los_asleep == 0) {
- if (list[order[i]].los_neutral == 0)
- strnfmt(buf, sizeof(buf), "%s", m_name);
- else
- strnfmt(buf, sizeof(buf), "%s (%d neutral) ",
- m_name, list[order[i]].los_neutral);
- } else {
- if (list[order[i]].los_neutral == 0)
- strnfmt(buf, sizeof(buf), "%s (%d asleep) ",
- m_name, list[order[i]].los_asleep);
- else
- strnfmt(buf, sizeof(buf),
- "%s (%d asleep, %d neutral) ", m_name,
- list[order[i]].los_asleep,
- list[order[i]].los_neutral);
- }
- }
- /* Display the pict */
- if ((tile_width == 1) && (tile_height == 1)) {
- Term_putch(cur_x++, line, list[order[i]].attr, r_ptr->x_char);
- Term_putch(cur_x++, line, TERM_WHITE, L' ');
- }
- /* Print and bump line counter */
- c_prt(attr, buf, line, cur_x);
- line++;
- /* Page wrap */
- if (!in_term && (line == max) && disp_count != total_count) {
- prt("-- more --", line, x);
- anykey();
- /* Clear the screen */
- for (line = 1; line <= max; line++)
- prt("", line, 0);
- /* Reprint Message */
- prt(format("You can see %d monster%s",
- los_count, (los_count > 0 ? (los_count == 1 ?
- ":" : "s:") : "s.")),
- 0, 0);
- /* Reset */
- line = 1;
- }
- }
- /* Message for monsters outside LOS, if there are any */
- if (total_count > los_count) {
- /* Leave a blank line */
- line++;
- prt(format("You are aware of %d %smonster%s",
- (total_count - los_count),
- (los_count > 0 ? "other " : ""),
- ((total_count - los_count) == 1 ? ":" : "s:")), line++,
- 0);
- }
- /* Print out non-LOS monsters in descending order */
- for (i = 0; (i < type_count) && (line < max); i++) {
- int out_of_los = list[order[i]].count - list[order[i]].los;
- /* Skip if there are none of these out of LOS */
- if (list[order[i]].count == list[order[i]].los)
- continue;
- /* Reset position */
- cur_x = x;
- /* Note that these have been displayed */
- disp_count += out_of_los;
- /* Get monster race and name */
- r_ptr = &r_info[order[i]];
- get_mon_name(m_name, sizeof(m_name), r_ptr, out_of_los);
- /* Display uniques in a special colour */
- if (rf_has(r_ptr->flags, RF_UNIQUE))
- attr = TERM_VIOLET;
- else if (r_ptr->level > p_ptr->danger)
- attr = TERM_RED;
- else
- attr = TERM_WHITE;
- /* Build the monster name */
- if (out_of_los == 1) {
- if (list[order[i]].asleep == 1)
- strnfmt(buf, sizeof(buf), "%s (asleep) ", m_name);
- else if (list[order[i]].neutral == 1)
- strnfmt(buf, sizeof(buf), "%s (neutral) ", m_name);
- else
- strnfmt(buf, sizeof(buf), "%s ", m_name);
- } else {
- if (list[order[i]].asleep == 0) {
- if (list[order[i]].neutral == 0)
- strnfmt(buf, sizeof(buf), "%s", m_name);
- else
- strnfmt(buf, sizeof(buf), "%s (%d neutral) ",
- m_name, list[order[i]].neutral);
- } else {
- if (list[order[i]].neutral == 0)
- strnfmt(buf, sizeof(buf), "%s (%d asleep) ",
- m_name, list[order[i]].asleep);
- else
- strnfmt(buf, sizeof(buf),
- "%s (%d asleep, %d neutral) ", m_name,
- list[order[i]].asleep, list[order[i]].neutral);
- }
- }
- /* Display the pict */
- if ((tile_width == 1) && (tile_height == 1)) {
- Term_putch(cur_x++, line, list[order[i]].attr, r_ptr->x_char);
- Term_putch(cur_x++, line, TERM_WHITE, L' ');
- }
- /* Print and bump line counter */
- c_prt(attr, buf, line, cur_x);
- line++;
- /* Page wrap */
- if (!in_term && (line == max) && disp_count != total_count) {
- prt("-- more --", line, x);
- anykey();
- /* Clear the screen */
- for (line = 1; line <= max; line++)
- prt("", line, 0);
- /* Reprint Message */
- prt(format("You are aware of %d %smonster%s",
- (total_count - los_count), (los_count > 0 ?
- "other " : ""),
- ((total_count - los_count) >
- 0 ? ((total_count - los_count) == 1 ? ":" : "s:")
- : "s.")), 0, 0);
- /* Reset */
- line = 1;
- }
- }
- /* Print "and others" message if we've run out of space */
- if (disp_count != total_count) {
- strnfmt(buf, sizeof buf, " ...and %d others.",
- total_count - disp_count);
- c_prt(TERM_WHITE, buf, line, x);
- }
- /* Otherwise clear a line at the end, for main-term display */
- else
- prt("", line, x);
- if (!in_term)
- Term_addstr(-1, TERM_WHITE, " (Press any key to continue.)");
- /* Free the arrays */
- FREE(list);
- FREE(order);
- }
- /**
- * Build a string describing a monster in some way.
- *
- * We can correctly describe monsters based on their visibility.
- * We can force all monsters to be treated as visible or invisible.
- * We can build nominatives, objectives, possessives, or reflexives.
- * We can selectively pronominalize hidden, visible, or all monsters.
- * We can use definite or indefinite descriptions for hidden monsters.
- * We can use definite or indefinite descriptions for visible monsters.
- *
- * Pronominalization involves the gender whenever possible and allowed,
- * so that by cleverly requesting pronominalization / visibility, you
- * can get messages like "You hit someone. She screams in agony!".
- *
- * Reflexives are acquired by requesting Objective plus Possessive.
- *
- * I am assuming that no monster name is more than 65 characters long,
- * so that "char desc[80];" is sufficiently large for any result, even
- * when the "offscreen" notation is added.
- *
- * Note that the "possessive" for certain unique monsters will look
- * really silly, as in "Morgoth, King of Darkness's". We should
- * perhaps add a flag to "remove" any "descriptives" in the name.
- *
- * Note that "offscreen" monsters will get a special "(offscreen)"
- * notation in their name if they are visible but offscreen. This
- * may look silly with possessives, as in "the rat's (offscreen)".
- * Perhaps the "offscreen" descriptor should be abbreviated.
- *
- * Mode Flags:
- * - 0x01 --> Objective (or Reflexive)
- * - 0x02 --> Possessive (or Reflexive)
- * - 0x04 --> Use indefinites for hidden monsters ("something")
- * - 0x08 --> Use indefinites for visible monsters ("a kobold")
- * - 0x10 --> Pronominalize hidden monsters
- * - 0x20 --> Pronominalize visible monsters
- * - 0x40 --> Assume the monster is hidden
- * - 0x80 --> Assume the monster is visible
- * - 0x100 --> Capitalise monster name
- *
- * Useful Modes:
- * - 0x00 --> Full nominative name ("the kobold") or "it"
- * - 0x04 --> Full nominative name ("the kobold") or "something"
- * - 0x80 --> Genocide resistance name ("the kobold")
- * - 0x88 --> Killing name ("a kobold")
- * - 0x22 --> Possessive, genderized if visable ("his") or "its"
- * - 0x23 --> Reflexive, genderized if visable ("himself") or "itself"
- */
- void monster_desc(char *desc, size_t max, monster_type * m_ptr, int mode)
- {
- const char *res;
- monster_race *r_ptr = &r_info[m_ptr->r_idx];
- const char *name = r_ptr->name;
- char undead_name[40] = "oops";
- bool seen, pron;
- /* Can we "see" it (forced, or not hidden + visible) */
- seen = ((mode & (0x80)) || (!(mode & (0x40)) && m_ptr->ml));
- /* Sexed Pronouns (seen and forced, or unseen and allowed) */
- pron = ((seen && (mode & (0x20))) || (!seen && (mode & (0x10))));
- /* First, try using pronouns, or describing hidden monsters */
- if (!seen || pron) {
- /* an encoding of the monster "sex" */
- int kind = 0x00;
- /* Extract the gender (if applicable) */
- if (rf_has(r_ptr->flags, RF_FEMALE))
- kind = 0x20;
- else if (rf_has(r_ptr->flags, RF_MALE))
- kind = 0x10;
- /* Ignore the gender (if desired) */
- if (!m_ptr || !pron)
- kind = 0x00;
- /* Assume simple result */
- res = "it";
- /* Brute force: split on the possibilities */
- switch (kind + (mode & 0x07)) {
- /* Neuter, or unknown */
- case 0x00:
- res = "it";
- break;
- case 0x01:
- res = "it";
- break;
- case 0x02:
- res = "its";
- break;
- case 0x03:
- res = "itself";
- break;
- case 0x04:
- res = "something";
- break;
- case 0x05:
- res = "something";
- break;
- case 0x06:
- res = "something's";
- break;
- case 0x07:
- res = "itself";
- break;
- /* Male (assume human if vague) */
- case 0x10:
- res = "he";
- break;
- case 0x11:
- res = "him";
- break;
- case 0x12:
- res = "his";
- break;
- case 0x13:
- res = "himself";
- break;
- case 0x14:
- res = "someone";
- break;
- case 0x15:
- res = "someone";
- break;
- case 0x16:
- res = "someone's";
- break;
- case 0x17:
- res = "himself";
- break;
- /* Female (assume human if vague) */
- case 0x20:
- res = "she";
- break;
- case 0x21:
- res = "her";
- break;
- case 0x22:
- res = "her";
- break;
- case 0x23:
- res = "herself";
- break;
- case 0x24:
- res = "someone";
- break;
- case 0x25:
- res = "someone";
- break;
- case 0x26:
- res = "someone's";
- break;
- case 0x27:
- res = "herself";
- break;
- }
- /* Copy the result */
- my_strcpy(desc, res, max);
- }
- /* Handle visible monsters, "reflexive" request */
- else if ((mode & 0x02) && (mode & 0x01)) {
- /* The monster is visible, so use its gender */
- if (rf_has(r_ptr->flags, RF_FEMALE))
- my_strcpy(desc, "herself", max);
- else if (rf_has(r_ptr->flags, RF_MALE))
- my_strcpy(desc, "himself", max);
- else
- my_strcpy(desc, "itself", max);
- }
- /* Handle all other visible monster requests */
- else {
- const char *race_name = NULL;
- /* Get a racial prefix if necessary */
- if (m_ptr->p_race != NON_RACIAL)
- race_name = p_info[m_ptr->p_race].name;
- /* It could be a player ghost. */
- if (rf_has(r_ptr->flags, RF_PLAYER_GHOST)) {
- /* Get the ghost name. */
- my_strcpy(desc, ghost_name, max);
- /* Get the undead name. */
- my_strcpy(undead_name, r_ptr->name, max);
- /* Build the ghost name. */
- my_strcat(desc, ", the ", max);
- my_strcat(desc, undead_name, max);
- }
- /* It could be a Unique */
- else if (rf_has(r_ptr->flags, RF_UNIQUE)) {
- /* Start with the name (thus nominative and objective) */
- my_strcpy(desc, name, max);
- }
- /* It could be an indefinite monster */
- else if (mode & 0x08) {
- bool vowel;
- char first[2];
- if (race_name)
- vowel = is_a_vowel(race_name[0]);
- else
- vowel = is_a_vowel(name[0]);
- /* XXX Check plurality for "some" */
- /* Indefinite monsters need an indefinite article */
- my_strcpy(desc, vowel ? "an " : "a ", max);
- /* Hack - no capital if there's a race name first */
- if (race_name) {
- my_strcat(desc, race_name, max);
- my_strcat(desc, " ", max);
- first[0] = tolower(name[0]);
- first[1] = '\0';
- my_strcat(desc, first, max);
- my_strcat(desc, name + 1, max);
- } else
- my_strcat(desc, name, max);
- }
- /* It could be a normal, definite, monster */
- else {
- char first[2];
- /* Definite monsters need a definite article */
- my_strcpy(desc, "the ", max);
- /* Hack - no capital if there's a race name first */
- if (race_name) {
- my_strcat(desc, race_name, max);
- my_strcat(desc, " ", max);
- first[0] = tolower(name[0]);
- first[1] = '\0';
- my_strcat(desc, first, max);
- my_strcat(desc, name + 1, max);
- } else
- my_strcat(desc, name, max);
- }
- /* Handle the Possessive as a special afterthought */
- if (mode & 0x02) {
- /* XXX Check for trailing "s" */
- /* Simply append "apostrophe" and "s" */
- my_strcat(desc, "'s", max);
- }
- /* Mention "offscreen" monsters XXX XXX */
- if (!panel_contains(m_ptr->fy, m_ptr->fx)) {
- /* Append special notation */
- my_strcat(desc, " (offscreen)", max);
- }
- }
- if (mode & 0x100)
- my_strcap(desc);
- }
- /**
- * Build a string describing a monster race, currently used for quests.
- *
- * Assumes a singular monster. This may need to be run through the
- * plural_aux function in the quest.c file. (Changes "wolf" to
- * wolves, etc.....)
- *
- * I am assuming that no monster name is more than 65 characters long,
- * so that "char desc[80];" is sufficiently large for any result, even
- * when the "offscreen" notation is added.
- *
- */
- void monster_desc_race(char *desc, size_t max, int r_idx)
- {
- monster_race *r_ptr = &r_info[r_idx];
- /* Write the name */
- my_strcpy(desc, r_ptr->name, max);
- }
- /**
- * Learn about a monster (by "probing" it)
- */
- void lore_do_probe(int m_idx)
- {
- monster_type *m_ptr = &m_list[m_idx];
- monster_race *r_ptr = &r_info[m_ptr->r_idx];
- monster_lore *l_ptr = &l_list[m_ptr->r_idx];
- /* Hack -- Memorize some flags */
- rf_copy(l_ptr->flags, r_ptr->flags);
- /* Update monster recall window */
- if (p_ptr->monster_race_idx == m_ptr->r_idx) {
- /* Redraw stuff */
- p_ptr->redraw |= (PR_MONSTER);
- }
- }
- /**
- * Take note that the given monster just dropped some treasure
- *
- * Note that learning the "GOOD"/"GREAT" flags gives information
- * about the treasure (even when the monster is killed for the first
- * time, such as uniques, and the treasure has not been examined yet).
- *
- * This "indirect" method is used to prevent the player from learning
- * exactly how much treasure a monster can drop from observing only
- * a single example of a drop. This method actually observes how much
- * gold and items are dropped, and remembers that information to be
- * described later by the monster recall code.
- */
- void lore_treasure(int m_idx, int num_item, int num_gold)
- {
- monster_type *m_ptr = &m_list[m_idx];
- monster_race *r_ptr = &r_info[m_ptr->r_idx];
- monster_lore *l_ptr = &l_list[m_ptr->r_idx];
- /* Note the number of things dropped */
- if (num_item > l_ptr->drop_item)
- l_ptr->drop_item = num_item;
- if (num_gold > l_ptr->drop_gold)
- l_ptr->drop_gold = num_gold;
- /* Hack -- memorize the good/great/chest flags */
- if (rf_has(r_ptr->flags, RF_DROP_GOOD))
- rf_on(l_ptr->flags, RF_DROP_GOOD);
- if (rf_has(r_ptr->flags, RF_DROP_GREAT))
- rf_on(l_ptr->flags, RF_DROP_GREAT);
- if (rf_has(r_ptr->flags, RF_DROP_CHEST))
- rf_on(l_ptr->flags, RF_DROP_CHEST);
- /* Update monster recall window */
- if (p_ptr->monster_race_idx == m_ptr->r_idx) {
- /* Redraw stuff */
- p_ptr->redraw |= (PR_MONSTER);
- }
- }
- /**
- * This function updates the monster record of the given monster
- *
- * This involves extracting the distance to the player (if requested),
- * and then checking for visibility (natural, infravision, see-invis,
- * telepathy), updating the monster visibility flag, redrawing (or
- * erasing) the monster when its visibility changes, and taking note
- * of any interesting monster flags (cold-blooded, invisible, etc).
- *
- * Note the new "mflag" field which encodes several monster state flags,
- * including "view" for when the monster is currently in line of sight,
- * and "mark" for when the monster is currently visible via detection.
- *
- * The only monster fields that are changed here are "cdis" (the
- * distance from the player), "ml" (visible to the player), and
- * "mflag" (to maintain the MFLAG_VIEW flag).
- *
- * Note the special update_monsters() function which can be used to
- * call this function once for every monster.
- *
- * Note the "full" flag which requests that the "cdis" field be updated,
- * this is only needed when the monster (or the player) has moved.
- *
- * Every time a monster moves, we must call this function for that
- * monster, and update the distance, and the visibility. Every time
- * the player moves, we must call this function for every monster, and
- * update the distance, and the visibility. Whenever the player "state"
- * changes in certain ways ("blindness", "infravision", "telepathy",
- * and "see invisible"), we must call this function for every monster,
- * and update the visibility.
- *
- * Routines that change the "illumination" of a grid must also call this
- * function for any monster in that grid, since the "visibility" of some
- * monsters may be based on the illumination of their grid.
- *
- * Note that this function is called once per monster every time the
- * player moves. When the player is running, this function is one
- * of the primary bottlenecks, along with update_view() and the
- * process_monsters() code, so efficiency is important.
- *
- * Note the optimized "inline" version of the distance() function.
- *
- * A monster is "visible" to the player if (1) it has been detected
- * by the player, (2) it is close to the player and the player has
- * telepathy, or (3) it is close to the player, and in line of sight
- * of the player, and it is "illuminated" by some combination of
- * infravision, torch light, or permanent light (invisible monsters
- * are only affected by "light" if the player can see invisible).
- *
- * Monsters which are not on the current panel may be "visible" to
- * the player, and their descriptions will include an "offscreen"
- * reference. Currently, offscreen monsters cannot be targetted
- * or viewed directly, but old targets will remain set. XXX XXX
- *
- * The player can choose to be disturbed by several things, including
- * disturb_move (monster which is viewable moves in some way), and
- * disturb_near (monster which is "easily" viewable moves in some
- * way). Note that "moves" includes "appears" and "disappears".
- */
- void update_mon(int m_idx, bool full)
- {
- monster_type *m_ptr = &m_list[m_idx];
- monster_race *r_ptr = &r_info[m_ptr->r_idx];
- monster_lore *l_ptr = &l_list[m_ptr->r_idx];
- int d;
- /* Current location */
- int fy = m_ptr->fy;
- int fx = m_ptr->fx;
- /* Seen at all */
- bool flag = FALSE;
- /* Seen by vision */
- bool easy = FALSE;
- /* Compute distance */
- if (full) {
- int py = p_ptr->py;
- int px = p_ptr->px;
- /* Distance components */
- int dy = (py > fy) ? (py - fy) : (fy - py);
- int dx = (px > fx) ? (px - fx) : (fx - px);
- /* Approximate distance */
- d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1));
- /* Restrict distance */
- if (d > 255)
- d = 255;
- /* Save the distance */
- m_ptr->cdis = d;
- }
- /* Extract distance */
- else {
- /* Extract the distance */
- d = m_ptr->cdis;
- }
- /* Detected */
- if (m_ptr->mflag & (MFLAG_MARK))
- flag = TRUE;
- /* Nearby */
- if (d <= (p_ptr->themed_level ? MAX_SIGHT / 2 : MAX_SIGHT)) {
- /* Basic telepathy */
- if (p_ptr->state.telepathy || p_ptr->timed[TMD_TELEPATHY]) {
- /* Empty mind, no telepathy */
- if (rf_has(r_ptr->flags, RF_EMPTY_MIND)) {
- /* Memorize flags */
- rf_on(l_ptr->flags, RF_EMPTY_MIND);
- }
- /* Weird mind, occasional telepathy */
- else if (rf_has(r_ptr->flags, RF_WEIRD_MIND)) {
- /* Monster is rarely detectable */
- if (((turn / 10) % 10) == (m_idx % 10)) {
- /* Detectable */
- notice_obj(OF_TELEPATHY, 0);
- flag = TRUE;
- /* Memorize flags */
- rf_on(l_ptr->flags, RF_WEIRD_MIND);
- /* Hack -- Memorize mental flags */
- if (rf_has(r_ptr->flags, RF_SMART))
- rf_on(l_ptr->flags, RF_SMART);
- if (rf_has(r_ptr->flags, RF_STUPID))
- rf_on(l_ptr->flags, RF_STUPID);
- }
- }
- /* Normal mind, allow telepathy */
- else {
- /* Detectable */
- notice_obj(OF_TELEPATHY, 0);
- flag = TRUE;
- /* Hack -- Memorize mental flags */
- if (rf_has(r_ptr->flags, RF_SMART))
- rf_on(l_ptr->flags, RF_SMART);
- if (rf_has(r_ptr->flags, RF_STUPID))
- rf_on(l_ptr->flags, RF_STUPID);
- }
- }
- /* Normal line of sight, and not blind */
- if (player_has_los_bold(fy, fx) && !p_ptr->timed[TMD_BLIND]) {
- bool do_invisible = FALSE;
- bool do_cold_blood = FALSE;
- /* Use "infravision" */
- if (d <= p_ptr->state.see_infra) {
- /* Handle "cold blooded" monsters */
- if (rf_has(r_ptr->flags, RF_COLD_BLOOD)) {
- /* Take note */
- do_cold_blood = TRUE;
- }
- /* Handle "warm blooded" monsters */
- else {
- /* Easy to see */
- easy = flag = TRUE;
- }
- }
- /* Use "illumination" */
- if (player_can_see_bold(fy, fx)) {
- /* Handle "invisible" monsters */
- if (rf_has(r_ptr->flags, RF_INVISIBLE)) {
- /* Take note */
- do_invisible = TRUE;
- /* See invisible */
- if (p_ptr->state.see_inv) {
- /* Easy to see */
- notice_obj(OF_SEE_INVIS, 0);
- easy = flag = TRUE;
- }
- }
- /* Handle "normal" monsters */
- else {
- /* Easy to see */
- easy = flag = TRUE;
- }
- }
- /* Visible */
- if (flag) {
- /* Memorize flags */
- if (do_invisible)
- rf_on(l_ptr->flags, RF_INVISIBLE);
- if (do_cold_blood)
- rf_on(l_ptr->flags, RF_COLD_BLOOD);
- }
- }
- }
- /* The monster is now visible */
- if (flag) {
- /* It was previously unseen */
- if (!m_ptr->ml) {
- /* Mark as visible */
- m_ptr->ml = TRUE;
- /* Draw the monster */
- light_spot(fy, fx);
- /* Update health bar as needed */
- if (p_ptr->health_who == m_idx)
- p_ptr->redraw |= (PR_HEALTH | PR_MON_MANA);
- /* Hack -- Count "fresh" sightings */
- if (l_ptr->sights < MAX_SHORT)
- l_ptr->sights++;
- /* Disturb on appearance */
- if (OPT(disturb_move) && (m_ptr->hostile == -1))
- disturb(1, 0);
- /* Redraw stuff */
- p_ptr->redraw |= PR_MONLIST;
- }
- }
- /* The monster is not visible */
- else {
- /* It was previously seen */
- if (m_ptr->ml) {
- /* Mark as not visible */
- m_ptr->ml = FALSE;
- /* Erase the monster */
- light_spot(fy, fx);
- /* Update health bar as needed */
- if (p_ptr->health_who == m_idx)
- p_ptr->redraw |= (PR_HEALTH | PR_MON_MANA);
- /* Disturb on disappearance */
- if (OPT(disturb_move) && (m_ptr->hostile == -1))
- disturb(1, 0);
- /* Redraw stuff */
- p_ptr->redraw |= PR_MONLIST;
- }
- }
- /* The monster is now easily visible */
- if (easy) {
- /* Change */
- if (!(m_ptr->mflag & (MFLAG_VIEW))) {
- /* Mark as easily visible */
- m_ptr->mflag |= (MFLAG_VIEW);
- /* Disturb on appearance */
- if (OPT(disturb_near) && (m_ptr->hostile == -1))
- disturb(1, 0);
- /* Re-draw monster window */
- p_ptr->redraw |= PR_MONLIST;
- }
- }
- /* The monster is not easily visible */
- else {
- /* Change */
- if (m_ptr->mflag & (MFLAG_VIEW)) {
- /* Mark as not easily visible */
- m_ptr->mflag &= ~(MFLAG_VIEW);
- /* Disturb on disappearance */
- if (OPT(disturb_near) && (m_ptr->hostile == -1))
- disturb(1, 0);
- /* Re-draw monster window */
- p_ptr->redraw |= PR_MONLIST;
- }
- }
- }
- /**
- * This function simply updates all the (non-dead) monsters (see above).
- */
- void update_monsters(bool full)
- {
- int i;
- /* Update each (live) monster */
- for (i = 1; i < m_max; i++) {
- monster_type *m_ptr = &m_list[i];
- /* Skip dead monsters */
- if (!m_ptr->r_idx)
- continue;
- /* Update the monster */
- update_mon(i, full);
- }
- }
- /**
- * Make a monster carry an object
- */
- s16b monster_carry(int m_idx, object_type * j_ptr)
- {
- s16b o_idx;
- s16b this_o_idx, next_o_idx = 0;
- monster_type *m_ptr = &m_list[m_idx];
- /* Scan objects already being held for combination */
- for (this_o_idx = m_ptr->hold_o_idx; this_o_idx;
- this_o_idx = next_o_idx) {
- object_type *o_ptr;
- /* Acquire object */
- o_ptr = &o_list[this_o_idx];
- /* Acquire next object */
- next_o_idx = o_ptr->next_o_idx;
- /* Check for combination */
- if (object_similar(o_ptr, j_ptr, OSTACK_MONSTER)) {
- /* Combine the items */
- object_absorb(o_ptr, j_ptr);
- /* Result */
- return (this_o_idx);
- }
- }
- /* Make an object */
- o_idx = o_pop();
- /* Success */
- if (o_idx) {
- object_type *o_ptr;
- /* Get new object */
- o_ptr = &o_list[o_idx];
- /* Copy object */
- object_copy(o_ptr, j_ptr);
- /* Forget mark */
- o_ptr->marked = FALSE;
- /* Forget location */
- o_ptr->iy = o_ptr->ix = 0;
- /* Memorize monster */
- o_ptr->held_m_idx = m_idx;
- /* Build stack */
- o_ptr->next_o_idx = m_ptr->hold_o_idx;
- /* Build stack */
- m_ptr->hold_o_idx = o_idx;
- }
- /* Result */
- return (o_idx);
- }
- /**
- * See whether all surrounding squares are trap detected
- */
- bool is_detected(int y, int x)
- {
- int d, xx, yy;
- feature_type *f_ptr;
- /* Check around (and under) the character */
- for (d = 0; d < 9; d++) {
- /* Extract adjacent (legal) location */
- yy = y + ddy_ddd[d];
- xx = x + ddx_ddd[d];
- /* Paranoia */
- if (!in_bounds_fully(yy, xx))
- continue;
- /* Only check trappable grids */
- f_ptr = &f_info[cave_feat[yy][xx]];
- if (!tf_has(f_ptr->flags, TF_TRAP))
- continue;
- /* Return false if undetected */
- if (!cave_has(cave_info[yy][xx], CAVE_DTRAP))
- return (FALSE);
- }
- /* Must be OK */
- return (TRUE);
- }
- /**
- * Swap the players/monsters (if any) at two locations XXX XXX XXX
- */
- void monster_swap(int y1, int x1, int y2, int x2)
- {
- int m1, m2;
- int y_offset, x_offset;
- int old_y_offset = p_ptr->py / CHUNK_HGT;
- int old_x_offset = p_ptr->px / CHUNK_WID;
- bool player_moved = FALSE;
- monster_type *m_ptr;
- /* Monsters */
- m1 = cave_m_idx[y1][x1];
- m2 = cave_m_idx[y2][x2];
- /* Update grids */
- cave_m_idx[y1][x1] = m2;
- cave_m_idx[y2][x2] = m1;
- /* Monster 1 */
- if (m1 > 0) {
- m_ptr = &m_list[m1];
- /* Make sure it's really there */
- if (m_ptr->r_idx != 0) {
- /* Move monster */
- m_ptr->fy = y2;
- m_ptr->fx = x2;
- /* Update monster */
- update_mon(m1, TRUE);
- /* Redraw monster list */
- p_ptr->redraw |= (PR_MONLIST);
- } else
- delete_monster_idx(m1);
- }
- /* Player 1 */
- else if (m1 < 0) {
- /* Move player */
- p_ptr->py = y2;
- p_ptr->px = x2;
- player_moved = TRUE;
- }
- /* Monster 2 */
- if (m2 > 0) {
- m_ptr = &m_list[m2];
- /* Make sure it's really there */
- if (m_ptr->r_idx != 0) {
- /* Move monster */
- m_ptr->fy = y1;
- m_ptr->fx = x1;
- /* Update monster */
- update_mon(m2, TRUE);
- /* Redraw monster list */
- p_ptr->redraw |= (PR_MONLIST);
- } else
- delete_monster_idx(m2);
- }
- /* Player 2 */
- else if (m2 < 0) {
- /* Move player */
- p_ptr->py = y1;
- p_ptr->px = x1;
- player_moved = TRUE;
- }
- /* Did the player move? */
- if (player_moved) {
- bool old_dtrap, new_dtrap;
- /* Calculate changes in dtrap status */
- old_dtrap = cave_has(cave_info[y1][x1], CAVE_DTRAP);
- new_dtrap = is_detected(y2, x2);
- /* Note the change in the detect status */
- p_ptr->redraw |= (PR_DTRAP);
- /* Update the panel */
- p_ptr->update |= (PU_PANEL);
- /* Update the visuals (and monster distances) */
- p_ptr->update |= (PU_UPDATE_VIEW | PU_DISTANCE);
- /* Redraw stuff */
- p_ptr->redraw |= PR_MAP;
- /* Redraw monster list */
- p_ptr->redraw |= (PR_MONLIST);
- /* Warn when leaving trap detected region */
- if (OPT(disturb_detect) && old_dtrap && !new_dtrap) {
- /* Disturb to break runs */
- disturb(0, 0);
- }
- }
- /* Redraw */
- light_spot(y1, x1);
- light_spot(y2, x2);
- /* Deal with change of chunk */
- y_offset = p_ptr->py / CHUNK_HGT;
- x_offset = p_ptr->px / CHUNK_WID;
- /* On the surface, re-align */
- if (p_ptr->danger == 0) {
- if ((y_offset != 1) || (x_offset != 1))
- chunk_change(0, y_offset, x_offset);
- }
- /* In the dungeon, change stage */
- else {
- int y0 = old_y_offset - y_offset;
- int x0 = old_x_offset - x_offset;
- int adj_index = chunk_offset_to_adjacent(0, 1 - y0, 1 - x0);
- if (adj_index != DIR_NONE) {
- p_ptr->last_stage = p_ptr->stage;
- p_ptr->stage = chunk_list[p_ptr->stage…
Large files files are truncated, but you can click here to view the full file