/src/monster/monster2.c
C | 3300 lines | 1568 code | 747 blank | 985 comment | 443 complexity | 11ea1f6dbbecd5d4374da025f10ebf8b MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- /*
- * File: monster2.c
- * Purpose: Low-level monster manipulation
- *
- * Copyright (c) 1997 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 "object/tvalsval.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 = &mon_list[i];
-
- monster_race *r_ptr = &r_info[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;
-
-
- /* Delete objects */
- for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx)
- {
- object_type *o_ptr;
-
- /* Get the object */
- o_ptr = &o_list[this_o_idx];
-
- /* Get the 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 */
- mon_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 = &mon_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;
-
- /* Get the object */
- o_ptr = &o_list[this_o_idx];
-
- /* Get the 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 */
- COPY(&mon_list[i2], &mon_list[i1], monster_type);
-
- /* Hack -- wipe hole */
- (void)WIPE(&mon_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, num, cnt;
-
- int cur_lev, cur_dis, chance;
-
-
- /* Message (only if compacting) */
- if (size) msg_print("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);
-
- /* Check all the monsters */
- for (i = 1; i < mon_max; i++)
- {
- monster_type *m_ptr = &mon_list[i];
-
- monster_race *r_ptr = &r_info[m_ptr->r_idx];
-
- /* Paranoia -- skip "dead" monsters */
- if (!m_ptr->r_idx) 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 = mon_max - 1; i >= 1; i--)
- {
- /* Get the i'th monster */
- monster_type *m_ptr = &mon_list[i];
-
- /* Skip real monsters */
- if (m_ptr->r_idx) continue;
-
- /* Move last monster into open hole */
- compact_monsters_aux(mon_max - 1, i);
-
- /* Compress "mon_max" */
- mon_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_mon_list(void)
- {
- int i;
-
- /* Delete all the monsters */
- for (i = mon_max - 1; i >= 1; i--)
- {
- monster_type *m_ptr = &mon_list[i];
-
- monster_race *r_ptr = &r_info[m_ptr->r_idx];
-
- /* Skip dead monsters */
- if (!m_ptr->r_idx) continue;
-
- /* Mega-Hack -- preserve Unique's XXX XXX XXX */
-
- /* 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);
- }
-
- /* Reset "mon_max" */
- mon_max = 1;
-
- /* Reset "mon_cnt" */
- mon_cnt = 0;
-
- /* Hack -- reset "reproducer" count */
- num_repro = 0;
-
- /* Hack -- no more target */
- target_set_monster(0);
-
- /* Hack -- no more tracking */
- health_track(0);
- }
-
-
- /*
- * Get and return the index of a "free" monster.
- *
- * This routine should almost never fail, but it *can* happen.
- */
- s16b mon_pop(void)
- {
- int i;
-
-
- /* Normal allocation */
- if (mon_max < z_info->m_max)
- {
- /* Get the next hole */
- i = mon_max;
-
- /* Expand the array */
- mon_max++;
-
- /* Count monsters */
- mon_cnt++;
-
- /* Return the index */
- return (i);
- }
-
-
- /* Recycle dead monsters */
- for (i = 1; i < mon_max; i++)
- {
- monster_type *m_ptr;
-
- /* Get the monster */
- m_ptr = &mon_list[i];
-
- /* Skip live monsters */
- if (m_ptr->r_idx) continue;
-
- /* Count monsters */
- mon_cnt++;
-
- /* Use this monster */
- return (i);
- }
-
-
- /* Warn the player (except during dungeon creation) */
- if (character_dungeon) msg_print("Too many monsters!");
-
- /* Try not to crash */
- return (0);
- }
-
-
- /*
- * Apply a "monster restriction function" to the "monster allocation table"
- */
- void 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;
- }
-
-
-
- /*
- * Choose a monster race that seems "appropriate" to the given level
- *
- * 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/50) 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, j, p;
-
- int r_idx;
-
- long value, total;
-
- monster_race *r_ptr;
-
- alloc_entry *table = alloc_race_table;
-
- /* Occasionally produce a nastier monster in the dungeon */
- if (level > 0 && one_in_(NASTY_MON))
- level += MIN(level / 4 + 2, MON_OOD_MAX);
-
- /* Reset total */
- total = 0L;
-
- /* Process probabilities */
- for (i = 0; i < alloc_race_size; i++)
- {
- /* Monsters are sorted by depth */
- if (table[i].level > level) break;
-
- /* Default */
- table[i].prob3 = 0;
-
- /* Hack -- No town monsters in dungeon */
- if ((level > 0) && (table[i].level <= 0)) continue;
-
- /* Get the "r_idx" of the chosen monster */
- r_idx = table[i].index;
-
- /* Get the actual race */
- r_ptr = &r_info[r_idx];
-
- /* Hack -- "unique" monsters must be "unique" */
- if (rf_has(r_ptr->flags, RF_UNIQUE) &&
- r_ptr->cur_num >= r_ptr->max_num)
- {
- continue;
- }
-
- /* Depth Monsters never appear out of depth */
- if (rf_has(r_ptr->flags, RF_FORCE_DEPTH) && r_ptr->level > p_ptr->depth)
- {
- continue;
- }
-
- /* Accept */
- table[i].prob3 = table[i].prob2;
-
- /* Total */
- total += table[i].prob3;
- }
-
- /* No legal monsters */
- if (total <= 0) return (0);
-
-
- /* Pick a monster */
- value = randint0(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;
- }
-
-
- /* Power boost */
- p = randint0(100);
-
- /* Try for a "harder" monster once (50%) or twice (10%) */
- if (p < 60)
- {
- /* Save old */
- j = i;
-
- /* Pick a monster */
- value = randint0(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;
- }
-
- /* Keep the "best" one */
- if (table[i].level < table[j].level) i = j;
- }
-
- /* Try for a "harder" monster twice (10%) */
- if (p < 10)
- {
- /* Save old */
- j = i;
-
- /* Pick a monster */
- value = randint0(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;
- }
-
- /* Keep the "best" one */
- if (table[i].level < table[j].level) i = j;
- }
-
-
- /* Result */
- return (table[i].index);
- }
-
-
- /*
- * Display visible monsters in a window
- */
- void display_monlist(void)
- {
- 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;
- 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 (i = 1; i < (size_t)mon_max; i++)
- {
- monster_vis *v;
-
- m_ptr = &mon_list[i];
- r_ptr = &r_info[m_ptr->r_idx];
-
- /* Only consider visible 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_NONE))
- {
- /* Increment the total number of in-LOS monsters */
- los_count++;
-
- /* Increment the LOS count for this monster type */
- v->los++;
-
- /* Check if asleep and increment accordingly */
- if (m_ptr->csleep) v->los_asleep++;
- }
- /* Not in LOS so increment if asleep */
- 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 */
- /* Monsters of same depth are sorted by power */
- if ((r_ptr->level > r2_ptr->level) ||
- ((r_ptr->level == r2_ptr->level) &&
- (r_ptr->power > r2_ptr->power)))
- {
- /* 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]];
- m_name = r_name + r_ptr->name;
-
- /* Display uniques in a special colour */
- if (rf_has(r_ptr->flags, RF_UNIQUE))
- attr = TERM_VIOLET;
- else if (r_ptr->level > p_ptr->depth)
- attr = TERM_RED;
- else
- attr = TERM_WHITE;
-
- /* Build the monster name */
- if (list[order[i]].los == 1)
- strnfmt(buf, sizeof(buf), (list[order[i]].los_asleep ==
- 1 ? "%s (asleep) " : "%s "), m_name);
- else strnfmt(buf, sizeof(buf), (list[order[i]].los_asleep > 0 ?
- "%s (x%d, %d asleep) " : "%s (x%d)"), m_name,
- list[order[i]].los, list[order[i]].los_asleep);
-
- /* Display the pict */
- Term_putch(cur_x++, line, list[order[i]].attr, r_ptr->x_char);
- if (use_bigtile) Term_putch(cur_x++, line, 255, -1);
- Term_putch(cur_x++, line, TERM_WHITE, ' ');
-
- /* 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++)
- {
- /* 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 += (list[order[i]].count - list[order[i]].los);
-
- /* Get monster race and name */
- r_ptr = &r_info[order[i]];
- m_name = r_name + r_ptr->name;
-
- /* Display uniques in a special colour */
- if (rf_has(r_ptr->flags, RF_UNIQUE))
- attr = TERM_VIOLET;
- else if (r_ptr->level > p_ptr->depth)
- attr = TERM_RED;
- else
- attr = TERM_WHITE;
-
- /* Build the monster name */
- if ((list[order[i]].count - list[order[i]].los) == 1)
- strnfmt(buf, sizeof(buf), (list[order[i]].asleep ==
- 1 ? "%s (asleep) " : "%s "), m_name);
- else strnfmt(buf, sizeof(buf), (list[order[i]].asleep > 0 ?
- "%s (x%d, %d asleep) " : "%s (x%d) "), m_name,
- (list[order[i]].count - list[order[i]].los),
- list[order[i]].asleep);
-
- /* Display the pict */
- Term_putch(cur_x++, line, list[order[i]].attr, r_ptr->x_char);
- if (use_bigtile) Term_putch(cur_x++, line, 255, -1);
- Term_putch(cur_x++, line, TERM_WHITE, ' ');
-
- /* 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
- *
- * Useful Modes:
- * 0x00 --> Full nominative name ("the kobold") or "it"
- * 0x04 --> Full nominative name ("the kobold") or "something"
- * 0x80 --> Banishment 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, const monster_type *m_ptr, int mode)
- {
- cptr res;
-
- monster_race *r_ptr = &r_info[m_ptr->r_idx];
-
- cptr name = (r_name + r_ptr->name);
-
- 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
- {
- /* It could be a Unique */
- 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)
- {
- /* XXX Check plurality for "some" */
-
- /* Indefinite monsters need an indefinite article */
- my_strcpy(desc, is_a_vowel(name[0]) ? "an " : "a ", max);
- my_strcat(desc, name, max);
- }
-
- /* It could be a normal, definite, monster */
- else
- {
- /* Definite monsters need a definite article */
- my_strcpy(desc, "the ", max);
- 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);
- }
- }
- }
-
-
-
-
- /*
- * Learn about a monster (by "probing" it)
- */
- void lore_do_probe(int m_idx)
- {
- monster_type *m_ptr = &mon_list[m_idx];
- monster_race *r_ptr = &r_info[m_ptr->r_idx];
- monster_lore *l_ptr = &l_list[m_ptr->r_idx];
-
- unsigned i;
-
- /* Know various things */
- rsf_setall(l_ptr->flags);
- rsf_copy(l_ptr->spell_flags, r_ptr->spell_flags);
- for (i = 0; i < MONSTER_BLOW_MAX; i++)
- l_ptr->blows[i] = MAX_UCHAR;
-
- /* Update monster recall window */
- if (p_ptr->monster_race_idx == m_ptr->r_idx)
- 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 = &mon_list[m_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;
-
- /* Learn about drop quality */
- rf_on(l_ptr->flags, RF_DROP_GOOD);
- rf_on(l_ptr->flags, RF_DROP_GREAT);
-
- /* Update monster recall window */
- if (p_ptr->monster_race_idx == m_ptr->r_idx)
- {
- /* Window 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 targeted
- * or viewed directly, but old targets will remain set. XXX XXX
- *
- * The player can choose to be disturbed by several things, including
- * "OPT(disturb_move)" (monster which is viewable moves in some way), and
- * "OPT(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 = &mon_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 <= MAX_SIGHT)
- {
- /* Basic telepathy */
- if (p_ptr->state.telepathy)
- {
- /* Empty mind, no telepathy */
- if (rf_has(r_ptr->flags, RF_EMPTY_MIND))
- {
- /* Nothing! */
- }
-
- /* Weird mind, occasional telepathy */
- else if (rf_has(r_ptr->flags, RF_WEIRD_MIND))
- {
- /* One in ten individuals are detectable */
- if ((m_idx % 10) == 5)
- {
- /* Detectable */
- flag = TRUE;
-
- /* Check for LOS so that MFLAG_VIEW is set later */
- if (player_has_los_bold(fy, fx)) easy = TRUE;
- }
- }
-
- /* Normal mind, allow telepathy */
- else
- {
- /* Detectable */
- flag = TRUE;
-
- /* Check for LOS to that MFLAG_VIEW is set later */
- if (player_has_los_bold(fy, fx)) easy = TRUE;
- }
- }
-
- /* Normal line of sight, and not blind */
- if (player_has_los_bold(fy, fx) && !p_ptr->timed[TMD_BLIND])
- {
- /* Use "infravision" */
- if (d <= p_ptr->state.see_infra)
- {
- /* Learn about warm/cold blood */
- rf_on(l_ptr->flags, RF_COLD_BLOOD);
-
- /* Handle "warm blooded" monsters */
- if (!rf_has(r_ptr->flags, RF_COLD_BLOOD))
- {
- /* Easy to see */
- easy = flag = TRUE;
- }
- }
-
- /* See if the monster is emitting lite */
- /*if (rf_has(r_ptr->flags, RF_HAS_LITE)) easy = flag = TRUE;*/
-
- /* Use "illumination" */
- if (player_can_see_bold(fy, fx))
- {
- /* Learn it emits light */
- rf_on(l_ptr->flags, RF_HAS_LITE);
-
- /* Learn about invisibility */
- rf_on(l_ptr->flags, RF_INVISIBLE);
-
- /* Handle "invisible" monsters */
- if (rf_has(r_ptr->flags, RF_INVISIBLE))
- {
- /* See invisible */
- if (p_ptr->state.see_inv)
- {
- /* Easy to see */
- easy = flag = TRUE;
- }
- }
-
- /* Handle "normal" monsters */
- else
- {
- /* Easy to see */
- easy = flag = TRUE;
- }
- }
- }
- }
-
-
- /* The monster is now visible */
- if (flag)
- {
- /* Learn about the monster's mind */
- if (p_ptr->state.telepathy)
- {
- flags_set(l_ptr->flags, RF_SIZE, RF_EMPTY_MIND, RF_WEIRD_MIND, RF_SMART, RF_STUPID, FLAG_END);
- }
-
- /* 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);
-
- /* Hack -- Count "fresh" sightings */
- if (l_ptr->sights < MAX_SHORT) l_ptr->sights++;
-
- /* Disturb on appearance */
- if (OPT(disturb_move)) disturb(1, 0);
-
- /* Window 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);
-
- /* Disturb on disappearance */
- if (OPT(disturb_move)) disturb(1, 0);
-
- /* Window 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)) 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)) disturb(1, 0);
-
- /* Re-draw monster list 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 < mon_max; i++)
- {
- monster_type *m_ptr = &mon_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 = &mon_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;
-
- /* Get the object */
- o_ptr = &o_list[this_o_idx];
-
- /* Get the next object */
- next_o_idx = o_ptr->next_o_idx;
-
- /* Check for combination */
- if (object_similar(o_ptr, j_ptr))
- {
- /* 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;
-
- /* Link the object to the monster */
- o_ptr->held_m_idx = m_idx;
-
- /* Link the object to the pile */
- o_ptr->next_o_idx = m_ptr->hold_o_idx;
-
- /* Link the monster to the object */
- m_ptr->hold_o_idx = o_idx;
- }
-
- /* Result */
- return (o_idx);
- }
-
-
- /*
- * 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;
-
- monster_type *m_ptr;
-
- monster_race *r_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 = &mon_list[m1];
-
- /* Move monster */
- m_ptr->fy = y2;
- m_ptr->fx = x2;
-
- /* Update monster */
- update_mon(m1, TRUE);
-
- /* Radiate light? */
- r_ptr = &r_info[m_ptr->r_idx];
- if (rf_has(r_ptr->flags, RF_HAS_LITE)) p_ptr->redraw |= PU_UPDATE_VIEW;
-
- /* Redraw monster list */
- p_ptr->redraw |= (PR_MONLIST);
- }
-
- /* Player 1 */
- else if (m1 < 0)
- {
- /* Move player */
- p_ptr->py = y2;
- p_ptr->px = x2;
-
- /* Update the trap detection 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);
-
- /* Update the flow */
- p_ptr->update |= (PU_UPDATE_FLOW);
-
- /* Redraw monster list */
- p_ptr->redraw |= (PR_MONLIST);
- }
-
- /* Monster 2 */
- if (m2 > 0)
- {
- m_ptr = &mon_list[m2];
-
- /* Move monster */
- m_ptr->fy = y1;
- m_ptr->fx = x1;
-
- /* Update monster */
- update_mon(m2, TRUE);
-
- /* Radiate light? */
- r_ptr = &r_info[m_ptr->r_idx];
- if (rf_has(r_ptr->flags, RF_HAS_LITE)) p_ptr->update |= PU_UPDATE_VIEW;
-
- /* Redraw monster list */
- p_ptr->redraw |= (PR_MONLIST);
- }
-
- /* Player 2 */
- else if (m2 < 0)
- {
- /* Move player */
- p_ptr->py = y1;
- p_ptr->px = x1;
-
- /* Update the trap detection 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);
-
- /* Update the flow */
- p_ptr->update |= (PU_UPDATE_FLOW);
-
- /* Redraw monster list */
- p_ptr->redraw |= (PR_MONLIST);
- }
-
-
- /* Redraw */
- light_spot(y1, x1);
- light_spot(y2, x2);
- }
-
-
- /*
- * Place the player in the dungeon XXX XXX
- */
- s16b player_place(int y, int x)
- {
- /* Paranoia XXX XXX */
- if (cave_m_idx[y][x] != 0) return (0);
-
-
- /* Save player location */
- p_ptr->py = y;
- p_ptr->px = x;
-
- /* Mark cave grid */
- cave_m_idx[y][x] = -1;
-
- /* Success */
- return (-1);
- }
-
-
- /*
- * Place a copy of a monster in the dungeon XXX XXX
- */
- s16b monster_place(int y, int x, monster_type *n_ptr)
- {
- s16b m_idx;
-
- monster_type *m_ptr;
- monster_race *r_ptr;
-
-
- /* Paranoia XXX XXX */
- if (cave_m_idx[y][x] != 0) return (0);
-
-
- /* Get a new record */
- m_idx = mon_pop();
-
- /* Oops */
- if (m_idx)
- {
- /* Make a new monster */
- cave_m_idx[y][x] = m_idx;
-
- /* Get the new monster */
- m_ptr = &mon_list[m_idx];
-
- /* Copy the monster XXX */
- COPY(m_ptr, n_ptr, monster_type);
-
- /* Location */
- m_ptr->fy = y;
- m_ptr->fx = x;
-
- /* Update the monster */
- update_mon(m_idx, TRUE);
-
- /* Get the new race */
- r_ptr = &r_info[m_ptr->r_idx];
-
- /* Hack -- Notice new multi-hued monsters */
- if (rf_has(r_ptr->flags, RF_ATTR_MULTI)) shimmer_monsters = TRUE;
-
- /* Hack -- Count the number of "reproducers" */
- if (rf_has(r_ptr->flags, RF_MULTIPLY)) num_repro++;
-
- /* Count racial occurances */
- r_ptr->cur_num++;
- }
-
- /* Result */
- return (m_idx);
- }
-
-
-
- /*
- * Attempt to place a monster of the given race at the given location.
- *
- * To give the player a sporting chance, any monster that appears in
- * line-of-sight and is extremely dangerous can be marked as
- * "FORCE_SLEEP", which will cause them to be placed with low energy,
- * which often (but not always) lets the player move before they do.
- *
- * This routine refuses to place out-of-depth "FORCE_DEPTH" monsters.
- *
- * XXX XXX XXX Use special "here" and "dead" flags for unique monsters,
- * remove old "cur_num" and "max_num" fields.
- *
- * XXX XXX XXX Actually, do something similar for artifacts, to simplify
- * the "preserve" mode, and to make the "what artifacts" flag more useful.
- *
- * This is the only function which may place a monster in the dungeon,
- * except for the savefile loading code.
- */
- static bool place_monster_one(int y, int x, int r_idx, bool slp)
- {
- int i;
-
- monster_race *r_ptr;
-
- monster_type *n_ptr;
- monster_type monster_type_body;
-
- cptr name;
-
-
- /* Paranoia */
- if (!in_bounds(y, x)) return (FALSE);
-
- /* Require empty space */
- if (!cave_empty_bold(y, x)) return (FALSE);
-
- /* Hack -- no creation on glyph of warding */
- if (cave_feat[y][x] == FEAT_GLYPH) return (FALSE);
-
-
- /* Paranoia */
- if (!r_idx) return (FALSE);
-
- /* Race */
- r_ptr = &r_info[r_idx];
-
- /* Paranoia */
- if (!r_ptr->name) return (FALSE);
-
- /* Name */
- name = (r_name + r_ptr->name);
-
-
- /* Hack -- "unique" monsters must be "unique" */
- if (rf_has(r_ptr->flags, RF_UNIQUE) && r_ptr->cur_num >= r_ptr->max_num)
- {
- /* Cannot create */
- return (FALSE);
- }
-
-
- /* Depth monsters may NOT be created out of depth */
- if (rf_has(r_ptr->flags, RF_FORCE_DEPTH) && p_ptr->depth < r_ptr->level)
- {
- /* Cannot create */
- return (FALSE);
- }
-
-
- /* Powerful monster */
- if (r_ptr->level > p_ptr->depth)
- {
- /* Unique monsters */
- if (rf_has(r_ptr->flags, RF_UNIQUE))
- {
- /* Message for cheaters */
- if (OPT(cheat_hear)) msg_format("Deep Unique (%s).", name);
-
- /* Boost rating by twice delta-depth */
- rating += (r_ptr->level - p_ptr->depth) * 2;
- }
-
- /* Normal monsters */
- else
- {
- /* Message for cheaters */
- if (OPT(cheat_hear)) msg_format("Deep Monster (%s).", name);
-
- /* Boost rating by delta-depth */
- rating += (r_ptr->level - p_ptr->depth);
- }
- }
-
- /* Note the monster */
- else if (rf_has(r_ptr->flags, RF_UNIQUE))
- {
- /* Unique monsters induce message */
- if (OPT(cheat_hear)) msg_format("Unique (%s).", name);
- }
-
-
- /* Get local monster */
- n_ptr = &monster_type_body;
-
- /* Clean out the monster */
- (void)WIPE(n_ptr, monster_type);
-
-
- /* Save the race */
- n_ptr->r_idx = r_idx;
-
-
- /* Enforce sleeping if needed */
- if (slp && r_ptr->sleep)
- {
- int val = r_ptr->sleep;
- n_ptr->csleep = ((val * 2) + randint1(val * 10));
- }
-
-
- /* Uniques get a fixed amount of HP */
- if (rf_has(r_ptr->flags, RF_UNIQUE))
- {
- n_ptr->maxhp = r_ptr->avg_hp;
- }
- else
- {
- int std_dev = (((r_ptr->avg_hp * 10) / 8) + 5) / 10;
- if (r_ptr->avg_hp > 1) std_dev++;
-
- n_ptr->maxhp = Rand_normal(r_ptr->avg_hp, std_dev);
- n_ptr->maxhp = MAX(n_ptr->maxhp, 1);
- }
-
- /* And start out fully healthy */
- n_ptr->hp = n_ptr->maxhp;
-
-
- /* Extract the monster base speed */
- n_ptr->mspeed = r_ptr->speed;
-
- /* Hack -- small racial variety */
- if (!rf_has(r_ptr->flags, RF_UNIQUE))
- {
- /* Allow some small variation per monster */
- i = extract_energy[r_ptr->speed] / 10;
- if (i) n_ptr->mspeed += rand_spread(0, i);
- }
-
-
- /* Give a random starting energy */
- n_ptr->energy = (byte)randint0(50);
-
- /* Force monster to wait for player */
- if (rf_has(r_ptr->flags, RF_FORCE_SLEEP))
- {
- /* Monster is still being nice */
- n_ptr->mflag |= (MFLAG_NICE);
-
- /* Optimize -- Repair flags */
- repair_mflag_nice = TRUE;
- }
-
- /* Radiate light? */
- if (rf_has(r_ptr->flags, RF_HAS_LITE)) p_ptr->update |= PU_UPDATE_VIEW;
-
- /* Place the monster in the dungeon */
- if (!monster_place(y, x, n_ptr)) return (FALSE);
-
- /* Success */
- return (TRUE);
- }
-
-
- /*
- * Maximum size of a group of monsters
- */
- #define GROUP_MAX 32
-
-
- /*
- * Attempt to place a "group" of monsters around the given location
- */
- static bool place_monster_group(int y, int x, int r_idx, bool slp)
- {
- monster_race *r_ptr = &r_info[r_idx];
-
- int old, n, i;
- int total, extra = 0;
-
- int hack_n;
-
- byte hack_y[GROUP_MAX];
- byte hack_x[GROUP_MAX];
-
-
- /* Pick a group size */
- total = randint1(13);
-
- /* Hard monsters, small groups */
- if (r_ptr->level > p_ptr->depth)
- {
- extra = r_ptr->level - p_ptr->depth;
- extra = 0 - randint1(extra);
- }
-
- /* Easy monsters, large groups */
- else if (r_ptr->level < p_ptr->depth)
- {
- extra = p_ptr->depth - r_ptr->level;
- extra = randint1(extra);
- }
-
- /* Hack -- limit group reduction */
- if (extra > 12) extra = 12;
-
- /* Modify the group size */
- total += extra;
-
- /* Minimum size */
- if (total < 1) total = 1;
-
- /* Maximum size */
- if (total > GROUP_MAX) total = GROUP_MAX;
-
-
- /* Save the rating */
- old = rating;
-
- /* Start on the monster */
- hack_n = 1;
- hack_x[0] = x;
- hack_y[0] = y;
-
- /* Puddle monsters, breadth first, up to total */
- for (n = 0; (n < hack_n) && (hack_n < total); n++)
- {
- /* Grab the location */
- int hx = hack_x[n];
- int hy = hack_y[n];
-
- /* Check each direction, up to total */
- for (i = 0; (i < 8) && (hack_n < total); i++)
- {
- int mx = hx + ddx_ddd[i];
- int my = hy + ddy_ddd[i];
-
- /* Walls and Monsters block flow */
- if (!cave_empty_bold(my, mx)) continue;
-
- /* Attempt to place another monster */
- if (place_monster_one(my, mx, r_idx, slp))
- {
- /* Add it to the "hack" set */
- hack_y[hack_n] = my;
- hack_x[hack_n] = mx;
- hack_n++;
- }
- }
- }
-
- /* Hack -- restore the rating */
- rating = old;
-
-
- /* Success */
- return (TRUE);
- }
-
-
- /*
- * Hack -- help pick an escort type
- */
- static int place_monster_idx = 0;
-
- /*
- * Hack -- help pick an escort type
- */
- static bool place_monster_okay(int r_idx)
- {
- monster_race *r_ptr = &r_info[place_monster_idx];
-
- monster_race *z_ptr = &r_info[r_idx];
-
- /* Require similar "race" */
- if (z_ptr->d_char != r_ptr->d_char) return (FALSE);
-
- /* Skip more advanced monsters */
- if (z_ptr->level > r_ptr->level) return (FALSE);
-
- /* Skip unique monsters */
- if (rf_has(z_ptr->flags, RF_UNIQUE)) return (FALSE);
-
- /* Paranoia -- Skip identical monsters */
- if (place_monster_idx == r_idx) return (FALSE);
-
- /* Okay */
- return (TRUE);
- }
-
-
- /*
- * Attempt to place a monster of the given race at the given location
- *
- * Note that certain monsters are now marked as requiring "friends".
- * These monsters, if successfully placed, and if the "grp" parameter
- * is TRUE, will be surrounded by a "group" of identical monsters.
- *
- * Note that certain monsters are now marked as requiring an "escort",
- * which is a collection of monsters with similar "race" but lower level.
- *
- * Some monsters induce a fake "group" flag on their escorts.
- *
- * Note the "bizarre" use of non-recursion to prevent annoying output
- * when running a code profiler.
- *
- * Note the use of the new "monster allocation table" code to restrict
- * the "get_mon_num()" function to "legal" escort types.
- */
- bool place_monster_aux(int y, int x, int r_idx, bool slp, bool grp)
- {
- int i;
-
- monster_race *r_ptr = &r_info[r_idx];
-
-
- /* Place one monster, or fail */
- if (!place_monster_one(y, x, r_idx, slp)) return (FALSE);
-
-
- /* Require the "group" flag */
- if (!grp) return (TRUE);
-
-
- /* Friends for certain monsters */
- if (rf_has(r_ptr->flags, RF_FRIENDS))
- {
- /* Attempt to place a group */
- (void)place_monster_group(y, x, r_idx, slp);
- }
-
-
- /* Escorts for certain monsters */
- if (rf_has(r_ptr->flags, RF_ESCORT))
- {
- /* Try to place several "escorts" */
- for (i = 0; i < 50; i++)
- {
- int nx, ny, z, d = 3;
-
- /* Pick a location */
- scatter(&ny, &nx, y, x, d, 0);
-
- /* Require empty grids */
- if (!cave_empty_bold(ny, nx)) continue;
-
-
- /* Set the escort index */
- place_monster_idx = r_idx;
-
-
- /* Set the escort hook */
- get_mon_num_hook = place_monster_okay;
-
- /* Prepare allocation table */
- get_mon_num_prep();
-
-
- /* Pick a random race */
- z = get_mon_num(r_ptr->level);
-
-
- /* Remove restriction */
- get_mon_num_hook = NULL;
-
- /* Prepare allocation table */
- get_mon_num_prep();
-
-
- /* Handle failure */
- if (!z) break;
-
- /* Place a single escort */
- (void)place_monster_one(ny, nx, z, slp);
-
- /* Place a "group" of escorts if needed */
- if (rf_has(r_info[z].flags, RF_FRIENDS) ||
- rf_has(r_ptr->flags, RF_ESCORTS))
- {
- /* Place a group of monsters */
- (void)place_monster_group(ny, nx, z, slp);
- }
- }
- }
-
-
- /* Success */
- return (TRUE);
- }
-
-
- /*
- * Hack -- attempt to place a monster at the given location
- *
- * Attempt to find a monster appropriate to the given depth
- */
- bool place_monster(int y, int x, int depth, bool slp, bool grp)
- {
- int r_idx;
-
- /* Pick a monster */
- r_idx = get_mon_num(depth);
-
- /* Handle failure */
- if (!r_idx) return (FALSE);
-
- /* Attempt to place the monster */
- if (place_monster_aux(y, x, r_idx, slp, grp)) return (TRUE);
-
- /* Oops */
- return (FALSE);
- }
-
-
-
-
- /*
- * XXX XXX XXX Player Ghosts are such a hack, they have been completely
- * removed.
- *
- * An idea for reintroducing them is to create a small number of
- * "unique" monsters which will serve as the "player ghosts".
- * Each will have a place holder for the "name" of a deceased player,
- * which will be extracted from a "bone" file, or replaced with a
- * "default" name if a real name is not available. Each ghost will
- * appear exactly once and will not induce a special feeling.
- *
- * Possible methods:
- * (s) 1 Skeleton
- * (z) 1 Zombie
…
Large files files are truncated, but you can click here to view the full file