Angband 3.0.9b.
[angband.git] / src / monster1.c
blob891b612acf77223f7afa00052dc2afb69706bbfb
1 /* File: monster1.c */
3 /*
4 * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
6 * This software may be copied and distributed for educational, research,
7 * and not for profit purposes provided that this copyright and statement
8 * are included in all such copies. Other copyrights may also apply.
9 */
11 #include "angband.h"
15 * Pronoun arrays, by gender.
17 static cptr wd_he[3] =
18 { "it", "he", "she" };
19 static cptr wd_his[3] =
20 { "its", "his", "her" };
24 * Pluralizer. Args(count, singular, plural)
26 #define plural(c,s,p) \
27 (((c) == 1) ? (s) : (p))
35 * Determine if the "armor" is known
36 * The higher the level, the fewer kills needed.
38 static bool know_armour(int r_idx, const monster_lore *l_ptr)
40 const monster_race *r_ptr = &r_info[r_idx];
42 s32b level = r_ptr->level;
44 s32b kills = l_ptr->tkills;
46 /* Normal monsters */
47 if (kills > 304 / (4 + level)) return (TRUE);
49 /* Skip non-uniques */
50 if (!(r_ptr->flags1 & RF1_UNIQUE)) return (FALSE);
52 /* Unique monsters */
53 if (kills > 304 / (38 + (5 * level) / 4)) return (TRUE);
55 /* Assume false */
56 return (FALSE);
61 * Determine if the "damage" of the given attack is known
62 * the higher the level of the monster, the fewer the attacks you need,
63 * the more damage an attack does, the more attacks you need
65 static bool know_damage(int r_idx, const monster_lore *l_ptr, int i)
67 const monster_race *r_ptr = &r_info[r_idx];
69 s32b level = r_ptr->level;
71 s32b a = l_ptr->blows[i];
73 s32b d1 = r_ptr->blow[i].d_dice;
74 s32b d2 = r_ptr->blow[i].d_side;
76 s32b d = d1 * d2;
78 /* Normal monsters */
79 if ((4 + level) * a > 80 * d) return (TRUE);
81 /* Skip non-uniques */
82 if (!(r_ptr->flags1 & RF1_UNIQUE)) return (FALSE);
84 /* Unique monsters */
85 if ((4 + level) * (2 * a) > 80 * d) return (TRUE);
87 /* Assume false */
88 return (FALSE);
92 static void describe_monster_desc(int r_idx)
94 const monster_race *r_ptr = &r_info[r_idx];
95 char buf[2048];
97 /* Simple method */
98 my_strcpy(buf, r_text + r_ptr->text, sizeof(buf));
100 /* Dump it */
101 text_out(buf);
102 text_out("\n");
106 static void describe_monster_spells(int r_idx, const monster_lore *l_ptr)
108 const monster_race *r_ptr = &r_info[r_idx];
109 int m, n;
110 int msex = 0;
111 bool breath = FALSE;
112 bool magic = FALSE;
113 int vn;
114 cptr vp[64];
117 /* Extract a gender (if applicable) */
118 if (r_ptr->flags1 & RF1_FEMALE) msex = 2;
119 else if (r_ptr->flags1 & RF1_MALE) msex = 1;
121 /* Collect innate attacks */
122 vn = 0;
123 if (l_ptr->flags4 & RF4_SHRIEK) vp[vn++] = "shriek for help";
124 if (l_ptr->flags4 & RF4_XXX2) vp[vn++] = "do something";
125 if (l_ptr->flags4 & RF4_XXX3) vp[vn++] = "do something";
126 if (l_ptr->flags4 & RF4_XXX4) vp[vn++] = "do something";
127 if (l_ptr->flags4 & RF4_ARROW_1) vp[vn++] = "fire an arrow";
128 if (l_ptr->flags4 & RF4_ARROW_2) vp[vn++] = "fire arrows";
129 if (l_ptr->flags4 & RF4_ARROW_3) vp[vn++] = "fire a missile";
130 if (l_ptr->flags4 & RF4_ARROW_4) vp[vn++] = "fire missiles";
131 if (l_ptr->flags4 & RF4_BOULDER) vp[vn++] = "throw boulders";
133 /* Describe innate attacks */
134 if (vn)
136 /* Intro */
137 text_out(format("%^s", wd_he[msex]));
139 /* Scan */
140 for (n = 0; n < vn; n++)
142 /* Intro */
143 if (n == 0) text_out(" may ");
144 else if (n < vn-1) text_out(", ");
145 else text_out(" or ");
147 /* Dump */
148 text_out_c(TERM_L_RED, vp[n]);
151 /* End */
152 text_out(". ");
156 /* Collect breaths */
157 vn = 0;
158 if (l_ptr->flags4 & RF4_BR_ACID) vp[vn++] = "acid";
159 if (l_ptr->flags4 & RF4_BR_ELEC) vp[vn++] = "lightning";
160 if (l_ptr->flags4 & RF4_BR_FIRE) vp[vn++] = "fire";
161 if (l_ptr->flags4 & RF4_BR_COLD) vp[vn++] = "frost";
162 if (l_ptr->flags4 & RF4_BR_POIS) vp[vn++] = "poison";
163 if (l_ptr->flags4 & RF4_BR_NETH) vp[vn++] = "nether";
164 if (l_ptr->flags4 & RF4_BR_LITE) vp[vn++] = "light";
165 if (l_ptr->flags4 & RF4_BR_DARK) vp[vn++] = "darkness";
166 if (l_ptr->flags4 & RF4_BR_CONF) vp[vn++] = "confusion";
167 if (l_ptr->flags4 & RF4_BR_SOUN) vp[vn++] = "sound";
168 if (l_ptr->flags4 & RF4_BR_CHAO) vp[vn++] = "chaos";
169 if (l_ptr->flags4 & RF4_BR_DISE) vp[vn++] = "disenchantment";
170 if (l_ptr->flags4 & RF4_BR_NEXU) vp[vn++] = "nexus";
171 if (l_ptr->flags4 & RF4_BR_TIME) vp[vn++] = "time";
172 if (l_ptr->flags4 & RF4_BR_INER) vp[vn++] = "inertia";
173 if (l_ptr->flags4 & RF4_BR_GRAV) vp[vn++] = "gravity";
174 if (l_ptr->flags4 & RF4_BR_SHAR) vp[vn++] = "shards";
175 if (l_ptr->flags4 & RF4_BR_PLAS) vp[vn++] = "plasma";
176 if (l_ptr->flags4 & RF4_BR_WALL) vp[vn++] = "force";
177 if (l_ptr->flags4 & RF4_BR_MANA) vp[vn++] = "mana";
178 if (l_ptr->flags4 & RF4_XXX5) vp[vn++] = "something";
179 if (l_ptr->flags4 & RF4_XXX6) vp[vn++] = "something";
180 if (l_ptr->flags4 & RF4_XXX7) vp[vn++] = "something";
182 /* Describe breaths */
183 if (vn)
185 /* Note breath */
186 breath = TRUE;
188 /* Intro */
189 text_out(format("%^s", wd_he[msex]));
191 /* Scan */
192 for (n = 0; n < vn; n++)
194 /* Intro */
195 if (n == 0) text_out(" may breathe ");
196 else if (n < vn-1) text_out(", ");
197 else text_out(" or ");
199 /* Dump */
200 text_out_c(TERM_L_RED, vp[n]);
205 /* Collect spells */
206 vn = 0;
207 if (l_ptr->flags5 & RF5_BA_ACID) vp[vn++] = "produce acid balls";
208 if (l_ptr->flags5 & RF5_BA_ELEC) vp[vn++] = "produce lightning balls";
209 if (l_ptr->flags5 & RF5_BA_FIRE) vp[vn++] = "produce fire balls";
210 if (l_ptr->flags5 & RF5_BA_COLD) vp[vn++] = "produce frost balls";
211 if (l_ptr->flags5 & RF5_BA_POIS) vp[vn++] = "produce poison balls";
212 if (l_ptr->flags5 & RF5_BA_NETH) vp[vn++] = "produce nether balls";
213 if (l_ptr->flags5 & RF5_BA_WATE) vp[vn++] = "produce water balls";
214 if (l_ptr->flags5 & RF5_BA_MANA) vp[vn++] = "invoke mana storms";
215 if (l_ptr->flags5 & RF5_BA_DARK) vp[vn++] = "invoke darkness storms";
216 if (l_ptr->flags5 & RF5_DRAIN_MANA) vp[vn++] = "drain mana";
217 if (l_ptr->flags5 & RF5_MIND_BLAST) vp[vn++] = "cause mind blasting";
218 if (l_ptr->flags5 & RF5_BRAIN_SMASH) vp[vn++] = "cause brain smashing";
219 if (l_ptr->flags5 & RF5_CAUSE_1) vp[vn++] = "cause light wounds";
220 if (l_ptr->flags5 & RF5_CAUSE_2) vp[vn++] = "cause serious wounds";
221 if (l_ptr->flags5 & RF5_CAUSE_3) vp[vn++] = "cause critical wounds";
222 if (l_ptr->flags5 & RF5_CAUSE_4) vp[vn++] = "cause mortal wounds";
223 if (l_ptr->flags5 & RF5_BO_ACID) vp[vn++] = "produce acid bolts";
224 if (l_ptr->flags5 & RF5_BO_ELEC) vp[vn++] = "produce lightning bolts";
225 if (l_ptr->flags5 & RF5_BO_FIRE) vp[vn++] = "produce fire bolts";
226 if (l_ptr->flags5 & RF5_BO_COLD) vp[vn++] = "produce frost bolts";
227 if (l_ptr->flags5 & RF5_BO_POIS) vp[vn++] = "produce poison bolts";
228 if (l_ptr->flags5 & RF5_BO_NETH) vp[vn++] = "produce nether bolts";
229 if (l_ptr->flags5 & RF5_BO_WATE) vp[vn++] = "produce water bolts";
230 if (l_ptr->flags5 & RF5_BO_MANA) vp[vn++] = "produce mana bolts";
231 if (l_ptr->flags5 & RF5_BO_PLAS) vp[vn++] = "produce plasma bolts";
232 if (l_ptr->flags5 & RF5_BO_ICEE) vp[vn++] = "produce ice bolts";
233 if (l_ptr->flags5 & RF5_MISSILE) vp[vn++] = "produce magic missiles";
234 if (l_ptr->flags5 & RF5_SCARE) vp[vn++] = "terrify";
235 if (l_ptr->flags5 & RF5_BLIND) vp[vn++] = "blind";
236 if (l_ptr->flags5 & RF5_CONF) vp[vn++] = "confuse";
237 if (l_ptr->flags5 & RF5_SLOW) vp[vn++] = "slow";
238 if (l_ptr->flags5 & RF5_HOLD) vp[vn++] = "paralyze";
239 if (l_ptr->flags6 & RF6_HASTE) vp[vn++] = "haste-self";
240 if (l_ptr->flags6 & RF6_XXX1) vp[vn++] = "do something";
241 if (l_ptr->flags6 & RF6_HEAL) vp[vn++] = "heal-self";
242 if (l_ptr->flags6 & RF6_XXX2) vp[vn++] = "do something";
243 if (l_ptr->flags6 & RF6_BLINK) vp[vn++] = "blink-self";
244 if (l_ptr->flags6 & RF6_TPORT) vp[vn++] = "teleport-self";
245 if (l_ptr->flags6 & RF6_XXX3) vp[vn++] = "do something";
246 if (l_ptr->flags6 & RF6_XXX4) vp[vn++] = "do something";
247 if (l_ptr->flags6 & RF6_TELE_TO) vp[vn++] = "teleport to";
248 if (l_ptr->flags6 & RF6_TELE_AWAY) vp[vn++] = "teleport away";
249 if (l_ptr->flags6 & RF6_TELE_LEVEL) vp[vn++] = "teleport level";
250 if (l_ptr->flags6 & RF6_XXX5) vp[vn++] = "do something";
251 if (l_ptr->flags6 & RF6_DARKNESS) vp[vn++] = "create darkness";
252 if (l_ptr->flags6 & RF6_TRAPS) vp[vn++] = "create traps";
253 if (l_ptr->flags6 & RF6_FORGET) vp[vn++] = "cause amnesia";
254 if (l_ptr->flags6 & RF6_XXX6) vp[vn++] = "do something";
255 if (l_ptr->flags6 & RF6_S_KIN) vp[vn++] = "summon similar monsters";
256 if (l_ptr->flags6 & RF6_S_MONSTER) vp[vn++] = "summon a monster";
257 if (l_ptr->flags6 & RF6_S_MONSTERS) vp[vn++] = "summon monsters";
258 if (l_ptr->flags6 & RF6_S_ANIMAL) vp[vn++] = "summon animals";
259 if (l_ptr->flags6 & RF6_S_SPIDER) vp[vn++] = "summon spiders";
260 if (l_ptr->flags6 & RF6_S_HOUND) vp[vn++] = "summon hounds";
261 if (l_ptr->flags6 & RF6_S_HYDRA) vp[vn++] = "summon hydras";
262 if (l_ptr->flags6 & RF6_S_ANGEL) vp[vn++] = "summon an angel";
263 if (l_ptr->flags6 & RF6_S_DEMON) vp[vn++] = "summon a demon";
264 if (l_ptr->flags6 & RF6_S_UNDEAD) vp[vn++] = "summon an undead";
265 if (l_ptr->flags6 & RF6_S_DRAGON) vp[vn++] = "summon a dragon";
266 if (l_ptr->flags6 & RF6_S_HI_UNDEAD) vp[vn++] = "summon Greater Undead";
267 if (l_ptr->flags6 & RF6_S_HI_DRAGON) vp[vn++] = "summon Ancient Dragons";
268 if (l_ptr->flags6 & RF6_S_HI_DEMON) vp[vn++] = "summon Greater Demons";
269 if (l_ptr->flags6 & RF6_S_WRAITH) vp[vn++] = "summon Ring Wraiths";
270 if (l_ptr->flags6 & RF6_S_UNIQUE) vp[vn++] = "summon Unique Monsters";
272 /* Describe spells */
273 if (vn)
275 /* Note magic */
276 magic = TRUE;
278 /* Intro */
279 if (breath)
281 text_out(", and is also");
283 else
285 text_out(format("%^s is", wd_he[msex]));
288 /* Verb Phrase */
289 text_out(" magical, casting spells");
291 /* Adverb */
292 if (l_ptr->flags2 & RF2_SMART) text_out_c(TERM_ORANGE, " intelligently");
294 /* Scan */
295 for (n = 0; n < vn; n++)
297 /* Intro */
298 if (n == 0) text_out(" which ");
299 else if (n < vn-1) text_out(", ");
300 else text_out(" or ");
302 /* Dump */
303 text_out_c(TERM_L_RED, vp[n]);
308 /* End the sentence about innate/other spells */
309 if (breath || magic)
311 /* Total casting */
312 m = l_ptr->cast_innate + l_ptr->cast_spell;
314 /* Average frequency */
315 n = (r_ptr->freq_innate + r_ptr->freq_spell) / 2;
317 /* Describe the spell frequency */
318 if (m > 100)
320 text_out(format("; 1 time in %d", 100 / n));
323 /* Guess at the frequency */
324 else if (m)
326 n = ((n + 9) / 10) * 10;
327 text_out(format("; about 1 time in %d", 100 / n));
330 /* End this sentence */
331 text_out(". ");
336 static void describe_monster_drop(int r_idx, const monster_lore *l_ptr)
338 const monster_race *r_ptr = &r_info[r_idx];
340 bool sin = FALSE;
342 int n;
344 cptr p;
346 int msex = 0;
349 /* Extract a gender (if applicable) */
350 if (r_ptr->flags1 & RF1_FEMALE) msex = 2;
351 else if (r_ptr->flags1 & RF1_MALE) msex = 1;
353 /* Drops gold and/or items */
354 if (l_ptr->drop_gold || l_ptr->drop_item)
356 /* Intro */
357 text_out(format("%^s may carry", wd_he[msex]));
359 /* Count maximum drop */
360 n = MAX(l_ptr->drop_gold, l_ptr->drop_item);
362 /* One drop (may need an "n") */
363 if (n == 1)
365 text_out(" a");
366 sin = TRUE;
369 /* Two drops */
370 else if (n == 2)
372 text_out(" one or two");
375 /* Many drops */
376 else
378 text_out(format(" up to %d", n));
382 /* Great */
383 if (l_ptr->flags1 & RF1_DROP_GREAT)
385 p = " exceptional";
388 /* Good (no "n" needed) */
389 else if (l_ptr->flags1 & RF1_DROP_GOOD)
391 p = " good";
392 sin = FALSE;
395 /* Okay */
396 else
398 p = NULL;
402 /* Objects */
403 if (l_ptr->drop_item)
405 /* Handle singular "an" */
406 if (sin) text_out("n");
407 sin = FALSE;
409 /* Dump "object(s)" */
410 if (p) text_out(p);
411 text_out(" object");
412 if (n != 1) text_out("s");
414 /* Conjunction replaces variety, if needed for "gold" below */
415 p = " or";
418 /* Treasures */
419 if (l_ptr->drop_gold)
421 /* Cancel prefix */
422 if (!p) sin = FALSE;
424 /* Handle singular "an" */
425 if (sin) text_out("n");
427 /* Dump "treasure(s)" */
428 if (p) text_out(p);
429 text_out(" treasure");
430 if (n != 1) text_out("s");
433 /* End this sentence */
434 text_out(". ");
439 static void describe_monster_attack(int r_idx, const monster_lore *l_ptr)
441 const monster_race *r_ptr = &r_info[r_idx];
442 int m, n, r;
443 cptr p, q;
445 int msex = 0;
447 /* Extract a gender (if applicable) */
448 if (r_ptr->flags1 & RF1_FEMALE) msex = 2;
449 else if (r_ptr->flags1 & RF1_MALE) msex = 1;
452 /* Count the number of "known" attacks */
453 for (n = 0, m = 0; m < MONSTER_BLOW_MAX; m++)
455 /* Skip non-attacks */
456 if (!r_ptr->blow[m].method) continue;
458 /* Count known attacks */
459 if (l_ptr->blows[m]) n++;
462 /* Examine (and count) the actual attacks */
463 for (r = 0, m = 0; m < MONSTER_BLOW_MAX; m++)
465 int method, effect, d1, d2;
467 /* Skip non-attacks */
468 if (!r_ptr->blow[m].method) continue;
470 /* Skip unknown attacks */
471 if (!l_ptr->blows[m]) continue;
474 /* Extract the attack info */
475 method = r_ptr->blow[m].method;
476 effect = r_ptr->blow[m].effect;
477 d1 = r_ptr->blow[m].d_dice;
478 d2 = r_ptr->blow[m].d_side;
481 /* No method yet */
482 p = NULL;
484 /* Get the method */
485 switch (method)
487 case RBM_HIT: p = "hit"; break;
488 case RBM_TOUCH: p = "touch"; break;
489 case RBM_PUNCH: p = "punch"; break;
490 case RBM_KICK: p = "kick"; break;
491 case RBM_CLAW: p = "claw"; break;
492 case RBM_BITE: p = "bite"; break;
493 case RBM_STING: p = "sting"; break;
494 case RBM_XXX1: break;
495 case RBM_BUTT: p = "butt"; break;
496 case RBM_CRUSH: p = "crush"; break;
497 case RBM_ENGULF: p = "engulf"; break;
498 case RBM_XXX2: break;
499 case RBM_CRAWL: p = "crawl on you"; break;
500 case RBM_DROOL: p = "drool on you"; break;
501 case RBM_SPIT: p = "spit"; break;
502 case RBM_XXX3: break;
503 case RBM_GAZE: p = "gaze"; break;
504 case RBM_WAIL: p = "wail"; break;
505 case RBM_SPORE: p = "release spores"; break;
506 case RBM_XXX4: break;
507 case RBM_BEG: p = "beg"; break;
508 case RBM_INSULT: p = "insult"; break;
509 case RBM_MOAN: p = "moan"; break;
510 case RBM_XXX5: break;
514 /* Default effect */
515 q = NULL;
517 /* Get the effect */
518 switch (effect)
520 case RBE_HURT: q = "attack"; break;
521 case RBE_POISON: q = "poison"; break;
522 case RBE_UN_BONUS: q = "disenchant"; break;
523 case RBE_UN_POWER: q = "drain charges"; break;
524 case RBE_EAT_GOLD: q = "steal gold"; break;
525 case RBE_EAT_ITEM: q = "steal items"; break;
526 case RBE_EAT_FOOD: q = "eat your food"; break;
527 case RBE_EAT_LITE: q = "absorb light"; break;
528 case RBE_ACID: q = "shoot acid"; break;
529 case RBE_ELEC: q = "electrify"; break;
530 case RBE_FIRE: q = "burn"; break;
531 case RBE_COLD: q = "freeze"; break;
532 case RBE_BLIND: q = "blind"; break;
533 case RBE_CONFUSE: q = "confuse"; break;
534 case RBE_TERRIFY: q = "terrify"; break;
535 case RBE_PARALYZE: q = "paralyze"; break;
536 case RBE_LOSE_STR: q = "reduce strength"; break;
537 case RBE_LOSE_INT: q = "reduce intelligence"; break;
538 case RBE_LOSE_WIS: q = "reduce wisdom"; break;
539 case RBE_LOSE_DEX: q = "reduce dexterity"; break;
540 case RBE_LOSE_CON: q = "reduce constitution"; break;
541 case RBE_LOSE_CHR: q = "reduce charisma"; break;
542 case RBE_LOSE_ALL: q = "reduce all stats"; break;
543 case RBE_SHATTER: q = "shatter"; break;
544 case RBE_EXP_10: q = "lower experience"; break;
545 case RBE_EXP_20: q = "lower experience"; break;
546 case RBE_EXP_40: q = "lower experience"; break;
547 case RBE_EXP_80: q = "lower experience"; break;
548 case RBE_HALLU: q = "cause hallucinations"; break;
552 /* Introduce the attack description */
553 if (!r)
555 text_out(format("%^s can ", wd_he[msex]));
557 else if (r < n-1)
559 text_out(", ");
561 else
563 text_out(", and ");
567 /* Hack -- force a method */
568 if (!p) p = "do something weird";
570 /* Describe the method */
571 text_out(p);
574 /* Describe the effect (if any) */
575 if (q)
577 /* Describe the attack type */
578 text_out(" to ");
579 text_out_c(TERM_L_RED, q);
581 /* Describe damage (if known) */
582 if (d1 && d2 && know_damage(r_idx, l_ptr, m))
584 /* Display the damage */
585 text_out(" with damage");
586 text_out(format(" %dd%d", d1, d2));
591 /* Count the attacks as printed */
592 r++;
595 /* Finish sentence above */
596 if (r)
598 text_out(". ");
601 /* Notice lack of attacks */
602 else if (l_ptr->flags1 & RF1_NEVER_BLOW)
604 text_out(format("%^s has no physical attacks. ", wd_he[msex]));
607 /* Or describe the lack of knowledge */
608 else
610 text_out(format("Nothing is known about %s attack. ", wd_his[msex]));
615 static void describe_monster_abilities(int r_idx, const monster_lore *l_ptr)
617 const monster_race *r_ptr = &r_info[r_idx];
619 int n;
621 int vn;
622 cptr vp[64];
624 int msex = 0;
627 /* Extract a gender (if applicable) */
628 if (r_ptr->flags1 & RF1_FEMALE) msex = 2;
629 else if (r_ptr->flags1 & RF1_MALE) msex = 1;
631 /* Collect special abilities. */
632 vn = 0;
633 if (l_ptr->flags2 & RF2_OPEN_DOOR) vp[vn++] = "open doors";
634 if (l_ptr->flags2 & RF2_BASH_DOOR) vp[vn++] = "bash down doors";
635 if (l_ptr->flags2 & RF2_PASS_WALL) vp[vn++] = "pass through walls";
636 if (l_ptr->flags2 & RF2_KILL_WALL) vp[vn++] = "bore through walls";
637 if (l_ptr->flags2 & RF2_MOVE_BODY) vp[vn++] = "push past weaker monsters";
638 if (l_ptr->flags2 & RF2_KILL_BODY) vp[vn++] = "destroy weaker monsters";
639 if (l_ptr->flags2 & RF2_TAKE_ITEM) vp[vn++] = "pick up objects";
640 if (l_ptr->flags2 & RF2_KILL_ITEM) vp[vn++] = "destroy objects";
642 /* Describe special abilities. */
643 if (vn)
645 /* Intro */
646 text_out(format("%^s", wd_he[msex]));
648 /* Scan */
649 for (n = 0; n < vn; n++)
651 /* Intro */
652 if (n == 0) text_out(" can ");
653 else if (n < vn-1) text_out(", ");
654 else text_out(" and ");
656 /* Dump */
657 text_out(vp[n]);
660 /* End */
661 text_out(". ");
665 /* Describe special abilities. */
666 if (l_ptr->flags2 & RF2_INVISIBLE)
668 text_out(format("%^s is invisible. ", wd_he[msex]));
670 if (l_ptr->flags2 & RF2_COLD_BLOOD)
672 text_out(format("%^s is cold blooded. ", wd_he[msex]));
674 if (l_ptr->flags2 & RF2_EMPTY_MIND)
676 text_out(format("%^s is not detected by telepathy. ", wd_he[msex]));
678 if (l_ptr->flags2 & RF2_WEIRD_MIND)
680 text_out(format("%^s is rarely detected by telepathy. ", wd_he[msex]));
682 if (l_ptr->flags2 & RF2_MULTIPLY)
684 text_out(format("%^s breeds explosively. ", wd_he[msex]));
686 if (l_ptr->flags2 & RF2_REGENERATE)
688 text_out(format("%^s regenerates quickly. ", wd_he[msex]));
692 /* Collect susceptibilities */
693 vn = 0;
694 if (l_ptr->flags3 & RF3_HURT_ROCK) vp[vn++] = "rock remover";
695 if (l_ptr->flags3 & RF3_HURT_LITE) vp[vn++] = "bright light";
696 if (l_ptr->flags3 & RF3_HURT_FIRE) vp[vn++] = "fire";
697 if (l_ptr->flags3 & RF3_HURT_COLD) vp[vn++] = "cold";
699 /* Describe susceptibilities */
700 if (vn)
702 /* Intro */
703 text_out(format("%^s", wd_he[msex]));
705 /* Scan */
706 for (n = 0; n < vn; n++)
708 /* Intro */
709 if (n == 0) text_out(" is hurt by ");
710 else if (n < vn-1) text_out(", ");
711 else text_out(" and ");
713 /* Dump */
714 text_out_c(TERM_YELLOW, vp[n]);
717 /* End */
718 text_out(". ");
722 /* Collect immunities and resistances */
723 vn = 0;
724 if (l_ptr->flags3 & RF3_IM_ACID) vp[vn++] = "acid";
725 if (l_ptr->flags3 & RF3_IM_ELEC) vp[vn++] = "lightning";
726 if (l_ptr->flags3 & RF3_IM_FIRE) vp[vn++] = "fire";
727 if (l_ptr->flags3 & RF3_IM_COLD) vp[vn++] = "cold";
728 if (l_ptr->flags3 & RF3_IM_POIS) vp[vn++] = "poison";
729 if (l_ptr->flags3 & RF3_IM_WATER) vp[vn++] = "water";
730 if (l_ptr->flags3 & RF3_RES_NETH) vp[vn++] = "nether";
731 if (l_ptr->flags3 & RF3_RES_PLAS) vp[vn++] = "plasma";
732 if (l_ptr->flags3 & RF3_RES_NEXUS) vp[vn++] = "nexus";
733 if (l_ptr->flags3 & RF3_RES_DISE) vp[vn++] = "disenchantment";
735 /* Describe immunities and resistances */
736 if (vn)
738 /* Intro */
739 text_out(format("%^s", wd_he[msex]));
741 /* Scan */
742 for (n = 0; n < vn; n++)
744 /* Intro */
745 if (n == 0) text_out(" resists ");
746 else if (n < vn-1) text_out(", ");
747 else text_out(" and ");
749 /* Dump */
750 text_out_c(TERM_ORANGE, vp[n]);
753 /* End */
754 text_out(". ");
758 /* Collect non-effects */
759 vn = 0;
760 if (l_ptr->flags3 & RF3_NO_STUN) vp[vn++] = "stunned";
761 if (l_ptr->flags3 & RF3_NO_FEAR) vp[vn++] = "frightened";
762 if (l_ptr->flags3 & RF3_NO_CONF) vp[vn++] = "confused";
763 if (l_ptr->flags3 & RF3_NO_SLEEP) vp[vn++] = "slept";
765 /* Describe non-effects */
766 if (vn)
768 /* Intro */
769 text_out(format("%^s", wd_he[msex]));
771 /* Scan */
772 for (n = 0; n < vn; n++)
774 /* Intro */
775 if (n == 0) text_out(" cannot be ");
776 else if (n < vn-1) text_out(", ");
777 else text_out(" or ");
779 /* Dump */
780 text_out_c(TERM_YELLOW, vp[n]);
783 /* End */
784 text_out(". ");
788 /* Do we know how aware it is? */
789 if ((((int)l_ptr->wake * (int)l_ptr->wake) > r_ptr->sleep) ||
790 (l_ptr->ignore == MAX_UCHAR) ||
791 ((r_ptr->sleep == 0) && (l_ptr->tkills >= 10)))
793 cptr act;
795 if (r_ptr->sleep > 200)
797 act = "prefers to ignore";
799 else if (r_ptr->sleep > 95)
801 act = "pays very little attention to";
803 else if (r_ptr->sleep > 75)
805 act = "pays little attention to";
807 else if (r_ptr->sleep > 45)
809 act = "tends to overlook";
811 else if (r_ptr->sleep > 25)
813 act = "takes quite a while to see";
815 else if (r_ptr->sleep > 10)
817 act = "takes a while to see";
819 else if (r_ptr->sleep > 5)
821 act = "is fairly observant of";
823 else if (r_ptr->sleep > 3)
825 act = "is observant of";
827 else if (r_ptr->sleep > 1)
829 act = "is very observant of";
831 else if (r_ptr->sleep > 0)
833 act = "is vigilant for";
835 else
837 act = "is ever vigilant for";
840 text_out(format("%^s %s intruders, which %s may notice from %d feet. ",
841 wd_he[msex], act, wd_he[msex], 10 * r_ptr->aaf));
844 /* Describe escorts */
845 if ((l_ptr->flags1 & RF1_ESCORT) || (l_ptr->flags1 & RF1_ESCORTS))
847 text_out(format("%^s usually appears with escorts. ",
848 wd_he[msex]));
851 /* Describe friends */
852 else if ((l_ptr->flags1 & RF1_FRIEND) || (l_ptr->flags1 & RF1_FRIENDS))
854 text_out(format("%^s usually appears in groups. ",
855 wd_he[msex]));
860 static void describe_monster_kills(int r_idx, const monster_lore *l_ptr)
862 const monster_race *r_ptr = &r_info[r_idx];
864 int msex = 0;
866 bool out = TRUE;
869 /* Extract a gender (if applicable) */
870 if (r_ptr->flags1 & RF1_FEMALE) msex = 2;
871 else if (r_ptr->flags1 & RF1_MALE) msex = 1;
874 /* Treat uniques differently */
875 if (l_ptr->flags1 & RF1_UNIQUE)
877 /* Hack -- Determine if the unique is "dead" */
878 bool dead = (r_ptr->max_num == 0) ? TRUE : FALSE;
880 /* We've been killed... */
881 if (l_ptr->deaths)
883 /* Killed ancestors */
884 text_out(format("%^s has slain %d of your ancestors",
885 wd_he[msex], l_ptr->deaths));
887 /* But we've also killed it */
888 if (dead)
890 text_out(", but you have taken revenge! ");
893 /* Unavenged (ever) */
894 else
896 text_out(format(", who %s unavenged. ",
897 plural(l_ptr->deaths, "remains", "remain")));
901 /* Dead unique who never hurt us */
902 else if (dead)
904 text_out("You have slain this foe. ");
906 else
908 /* Alive and never killed us */
909 out = FALSE;
913 /* Not unique, but killed us */
914 else if (l_ptr->deaths)
916 /* Dead ancestors */
917 text_out(format("%d of your ancestors %s been killed by this creature, ",
918 l_ptr->deaths, plural(l_ptr->deaths, "has", "have")));
920 /* Some kills this life */
921 if (l_ptr->pkills)
923 text_out(format("and you have exterminated at least %d of the creatures. ",
924 l_ptr->pkills));
927 /* Some kills past lives */
928 else if (l_ptr->tkills)
930 text_out(format("and %s have exterminated at least %d of the creatures. ",
931 "your ancestors", l_ptr->tkills));
934 /* No kills */
935 else
937 text_out_c(TERM_RED, format("and %s is not ever known to have been defeated. ",
938 wd_he[msex]));
942 /* Normal monsters */
943 else
945 /* Killed some this life */
946 if (l_ptr->pkills)
948 text_out(format("You have killed at least %d of these creatures. ",
949 l_ptr->pkills));
952 /* Killed some last life */
953 else if (l_ptr->tkills)
955 text_out(format("Your ancestors have killed at least %d of these creatures. ",
956 l_ptr->tkills));
959 /* Killed none */
960 else
962 text_out("No battles to the death are recalled. ");
966 /* Separate */
967 if (out) text_out("\n");
971 static void describe_monster_toughness(int r_idx, const monster_lore *l_ptr)
973 const monster_race *r_ptr = &r_info[r_idx];
975 int msex = 0;
978 /* Extract a gender (if applicable) */
979 if (r_ptr->flags1 & RF1_FEMALE) msex = 2;
980 else if (r_ptr->flags1 & RF1_MALE) msex = 1;
982 /* Describe monster "toughness" */
983 if (know_armour(r_idx, l_ptr))
985 /* Armor */
986 text_out(format("%^s has an armor rating of %d",
987 wd_he[msex], r_ptr->ac));
989 /* Maximized hitpoints */
990 if (l_ptr->flags1 & RF1_FORCE_MAXHP)
992 text_out(format(" and a life rating of %d. ",
993 r_ptr->hdice * r_ptr->hside));
996 /* Variable hitpoints */
997 else
999 text_out(format(" and a life rating of %dd%d. ",
1000 r_ptr->hdice, r_ptr->hside));
1006 static void describe_monster_exp(int r_idx, const monster_lore *l_ptr)
1008 const monster_race *r_ptr = &r_info[r_idx];
1010 cptr p, q;
1012 long i, j;
1015 /* Describe experience if known */
1016 if (l_ptr->tkills)
1018 /* Introduction */
1019 if (l_ptr->flags1 & RF1_UNIQUE)
1020 text_out("Killing");
1021 else
1022 text_out("A kill of");
1024 text_out(" this creature");
1026 /* calculate the integer exp part */
1027 i = (long)r_ptr->mexp * r_ptr->level / p_ptr->lev;
1029 /* calculate the fractional exp part scaled by 100, */
1030 /* must use long arithmetic to avoid overflow */
1031 j = ((((long)r_ptr->mexp * r_ptr->level % p_ptr->lev) *
1032 (long)1000 / p_ptr->lev + 5) / 10);
1034 /* Mention the experience */
1035 text_out(format(" is worth %ld.%02ld point%s",
1036 (long)i, (long)j,
1037 (((i == 1) && (j == 0)) ? "" : "s")));
1039 /* Take account of annoying English */
1040 p = "th";
1041 i = p_ptr->lev % 10;
1042 if ((p_ptr->lev / 10) == 1) /* nothing */;
1043 else if (i == 1) p = "st";
1044 else if (i == 2) p = "nd";
1045 else if (i == 3) p = "rd";
1047 /* Take account of "leading vowels" in numbers */
1048 q = "";
1049 i = p_ptr->lev;
1050 if ((i == 8) || (i == 11) || (i == 18)) q = "n";
1052 /* Mention the dependance on the player's level */
1053 text_out(format(" for a%s %lu%s level character. ",
1054 q, (long)i, p));
1059 static void describe_monster_movement(int r_idx, const monster_lore *l_ptr)
1061 const monster_race *r_ptr = &r_info[r_idx];
1063 bool old = FALSE;
1066 text_out("This");
1068 if (l_ptr->flags3 & RF3_ANIMAL) text_out_c(TERM_L_BLUE, " natural");
1069 if (l_ptr->flags3 & RF3_EVIL) text_out_c(TERM_L_BLUE, " evil");
1070 if (l_ptr->flags3 & RF3_UNDEAD) text_out_c(TERM_L_BLUE, " undead");
1072 if (l_ptr->flags3 & RF3_DRAGON) text_out_c(TERM_L_BLUE, " dragon");
1073 else if (l_ptr->flags3 & RF3_DEMON) text_out_c(TERM_L_BLUE, " demon");
1074 else if (l_ptr->flags3 & RF3_GIANT) text_out_c(TERM_L_BLUE, " giant");
1075 else if (l_ptr->flags3 & RF3_TROLL) text_out_c(TERM_L_BLUE, " troll");
1076 else if (l_ptr->flags3 & RF3_ORC) text_out_c(TERM_L_BLUE, " orc");
1077 else text_out(" creature");
1079 /* Describe location */
1080 if (r_ptr->level == 0)
1082 text_out_c(TERM_SLATE, " lives in the town");
1083 old = TRUE;
1085 else if (l_ptr->tkills)
1087 if (l_ptr->flags1 & RF1_FORCE_DEPTH)
1088 text_out_c(TERM_SLATE, " is found ");
1089 else
1090 text_out_c(TERM_SLATE, " is normally found ");
1092 if (depth_in_feet)
1094 text_out_c(TERM_SLATE, format("at depths of %d feet",
1095 r_ptr->level * 50));
1097 else
1099 text_out_c(TERM_SLATE, format("on dungeon level %d",
1100 r_ptr->level));
1102 old = TRUE;
1105 if (old) text_out(", and");
1107 text_out(" moves");
1109 /* Random-ness */
1110 if ((l_ptr->flags1 & RF1_RAND_50) || (l_ptr->flags1 & RF1_RAND_25))
1112 /* Adverb */
1113 if ((l_ptr->flags1 & RF1_RAND_50) && (l_ptr->flags1 & RF1_RAND_25))
1115 text_out(" extremely");
1117 else if (l_ptr->flags1 & RF1_RAND_50)
1119 text_out(" somewhat");
1121 else if (l_ptr->flags1 & RF1_RAND_25)
1123 text_out(" a bit");
1126 /* Adjective */
1127 text_out(" erratically");
1129 /* Hack -- Occasional conjunction */
1130 if (r_ptr->speed != 110) text_out(", and");
1133 /* Speed */
1134 if (r_ptr->speed > 110)
1136 if (r_ptr->speed > 130) text_out_c(TERM_GREEN, " incredibly");
1137 else if (r_ptr->speed > 120) text_out_c(TERM_GREEN, " very");
1138 text_out_c(TERM_GREEN, " quickly");
1140 else if (r_ptr->speed < 110)
1142 if (r_ptr->speed < 90) text_out_c(TERM_GREEN, " incredibly");
1143 else if (r_ptr->speed < 100) text_out_c(TERM_GREEN, " very");
1144 text_out_c(TERM_GREEN, " slowly");
1146 else
1148 text_out_c(TERM_GREEN, " at normal speed");
1151 /* The code above includes "attack speed" */
1152 if (l_ptr->flags1 & RF1_NEVER_MOVE)
1154 text_out(", but does not deign to chase intruders");
1157 /* End this sentence */
1158 text_out(". ");
1164 * Learn everything about a monster (by cheating)
1166 static void cheat_monster_lore(int r_idx, monster_lore *l_ptr)
1168 const monster_race *r_ptr = &r_info[r_idx];
1170 int i;
1173 /* Hack -- Maximal kills */
1174 l_ptr->tkills = MAX_SHORT;
1176 /* Hack -- Maximal info */
1177 l_ptr->wake = l_ptr->ignore = MAX_UCHAR;
1179 /* Observe "maximal" attacks */
1180 for (i = 0; i < MONSTER_BLOW_MAX; i++)
1182 /* Examine "actual" blows */
1183 if (r_ptr->blow[i].effect || r_ptr->blow[i].method)
1185 /* Hack -- maximal observations */
1186 l_ptr->blows[i] = MAX_UCHAR;
1190 /* Hack -- maximal drops */
1191 l_ptr->drop_gold = l_ptr->drop_item =
1192 (((r_ptr->flags1 & RF1_DROP_4D2) ? 8 : 0) +
1193 ((r_ptr->flags1 & RF1_DROP_3D2) ? 6 : 0) +
1194 ((r_ptr->flags1 & RF1_DROP_2D2) ? 4 : 0) +
1195 ((r_ptr->flags1 & RF1_DROP_1D2) ? 2 : 0) +
1196 ((r_ptr->flags1 & RF1_DROP_90) ? 1 : 0) +
1197 ((r_ptr->flags1 & RF1_DROP_60) ? 1 : 0));
1199 /* Hack -- but only "valid" drops */
1200 if (r_ptr->flags1 & RF1_ONLY_GOLD) l_ptr->drop_item = 0;
1201 if (r_ptr->flags1 & RF1_ONLY_ITEM) l_ptr->drop_gold = 0;
1203 /* Hack -- observe many spells */
1204 l_ptr->cast_innate = MAX_UCHAR;
1205 l_ptr->cast_spell = MAX_UCHAR;
1207 /* Hack -- know all the flags */
1208 l_ptr->flags1 = r_ptr->flags1;
1209 l_ptr->flags2 = r_ptr->flags2;
1210 l_ptr->flags3 = r_ptr->flags3;
1211 l_ptr->flags4 = r_ptr->flags4;
1212 l_ptr->flags5 = r_ptr->flags5;
1213 l_ptr->flags6 = r_ptr->flags6;
1218 * Hack -- display monster information using "roff()"
1220 * Note that there is now a compiler option to only read the monster
1221 * descriptions from the raw file when they are actually needed, which
1222 * saves about 60K of memory at the cost of disk access during monster
1223 * recall, which is optional to the user.
1225 * This function should only be called with the cursor placed at the
1226 * left edge of the screen, on a cleared line, in which the recall is
1227 * to take place. One extra blank line is left after the recall.
1229 void describe_monster(int r_idx, bool spoilers)
1231 monster_lore lore;
1233 /* Get the race and lore */
1234 const monster_race *r_ptr = &r_info[r_idx];
1235 monster_lore *l_ptr = &l_list[r_idx];
1238 /* Hack -- create a copy of the monster-memory */
1239 COPY(&lore, l_ptr, monster_lore);
1241 /* Assume some "obvious" flags */
1242 lore.flags1 |= (r_ptr->flags1 & RF1_OBVIOUS_MASK);
1245 /* Killing a monster reveals some properties */
1246 if (lore.tkills)
1248 /* Know "race" flags */
1249 lore.flags3 |= (r_ptr->flags3 & RF3_RACE_MASK);
1251 /* Know "forced" flags */
1252 lore.flags1 |= (r_ptr->flags1 & (RF1_FORCE_DEPTH | RF1_FORCE_MAXHP));
1255 /* Cheat -- know everything */
1256 if (cheat_know || spoilers) cheat_monster_lore(r_idx, &lore);
1258 /* Show kills of monster vs. player(s) */
1259 if (!spoilers)
1260 describe_monster_kills(r_idx, &lore);
1262 /* Monster description */
1263 describe_monster_desc(r_idx);
1265 /* Describe the movement and level of the monster */
1266 describe_monster_movement(r_idx, &lore);
1268 /* Describe experience */
1269 if (!spoilers) describe_monster_exp(r_idx, &lore);
1271 /* Describe spells and innate attacks */
1272 describe_monster_spells(r_idx, &lore);
1274 /* Describe monster "toughness" */
1275 if (!spoilers) describe_monster_toughness(r_idx, &lore);
1277 /* Describe the abilities of the monster */
1278 describe_monster_abilities(r_idx, &lore);
1280 /* Describe the monster drop */
1281 describe_monster_drop(r_idx, &lore);
1283 /* Describe the known attacks */
1284 describe_monster_attack(r_idx, &lore);
1286 /* Notice "Quest" monsters */
1287 if (lore.flags1 & RF1_QUESTOR)
1289 text_out("You feel an intense desire to kill this monster... ");
1292 /* All done */
1293 text_out("\n");
1301 * Hack -- Display the "name" and "attr/chars" of a monster race
1303 void roff_top(int r_idx)
1305 monster_race *r_ptr = &r_info[r_idx];
1307 byte a1, a2;
1308 char c1, c2;
1311 /* Get the chars */
1312 c1 = r_ptr->d_char;
1313 c2 = r_ptr->x_char;
1315 /* Get the attrs */
1316 a1 = r_ptr->d_attr;
1317 a2 = r_ptr->x_attr;
1320 /* Clear the top line */
1321 Term_erase(0, 0, 255);
1323 /* Reset the cursor */
1324 Term_gotoxy(0, 0);
1326 /* A title (use "The" for non-uniques) */
1327 if (!(r_ptr->flags1 & RF1_UNIQUE))
1329 Term_addstr(-1, TERM_WHITE, "The ");
1332 /* Dump the name */
1333 Term_addstr(-1, TERM_WHITE, (r_name + r_ptr->name));
1335 /* Append the "standard" attr/char info */
1336 Term_addstr(-1, TERM_WHITE, " ('");
1337 Term_addch(a1, c1);
1338 Term_addstr(-1, TERM_WHITE, "')");
1340 /* Append the "optional" attr/char info */
1341 Term_addstr(-1, TERM_WHITE, "/('");
1342 Term_addch(a2, c2);
1343 if (use_bigtile && (a2 & 0x80)) Term_addch(255, -1);
1344 Term_addstr(-1, TERM_WHITE, "'):");
1350 * Hack -- describe the given monster race at the top of the screen
1352 void screen_roff(int r_idx)
1354 /* Flush messages */
1355 message_flush();
1357 /* Begin recall */
1358 Term_erase(0, 1, 255);
1360 /* Output to the screen */
1361 text_out_hook = text_out_to_screen;
1363 /* Recall monster */
1364 describe_monster(r_idx, FALSE);
1366 /* Describe monster */
1367 roff_top(r_idx);
1374 * Hack -- describe the given monster race in the current "term" window
1376 void display_roff(int r_idx)
1378 int y;
1380 /* Erase the window */
1381 for (y = 0; y < Term->hgt; y++)
1383 /* Erase the line */
1384 Term_erase(0, y, 255);
1387 /* Begin recall */
1388 Term_gotoxy(0, 1);
1390 /* Output to the screen */
1391 text_out_hook = text_out_to_screen;
1393 /* Recall monster */
1394 describe_monster(r_idx, FALSE);
1396 /* Describe monster */
1397 roff_top(r_idx);