1 /**
2 * \file mon-lore.c
3 * \brief Monster memory code.
4 *
5 * Copyright (c) 1997-2007 Ben Harrison, James E. Wilson, Robert A. Koeneke
6 *
7 * This work is free software; you can redistribute it and/or modify it
8 * under the terms of either:
9 *
10 * a) the GNU General Public License as published by the Free Software
11 * Foundation, version 2, or
12 *
13 * b) the "Angband licence":
14 * This software may be copied and distributed for educational, research,
15 * and not for profit purposes provided that this copyright and statement
16 * are included in all such copies. Other copyrights may also apply.
17 */
18
19 #include "angband.h"
20 #include "effects.h"
21 #include "game-world.h"
22 #include "init.h"
23 #include "mon-blows.h"
24 #include "mon-init.h"
25 #include "mon-lore.h"
26 #include "mon-make.h"
27 #include "mon-predicate.h"
28 #include "mon-spell.h"
29 #include "mon-util.h"
30 #include "obj-gear.h"
31 #include "obj-tval.h"
32 #include "obj-util.h"
33 #include "player-attack.h"
34 #include "player-calcs.h"
35 #include "player-timed.h"
36 #include "project.h"
37 #include "z-textblock.h"
38
39 /**
40 * Monster genders
41 */
42 enum monster_sex {
43 MON_SEX_NEUTER = 0,
44 MON_SEX_MALE,
45 MON_SEX_FEMALE,
46 MON_SEX_MAX,
47 };
48
49 typedef enum monster_sex monster_sex_t;
50
51 /**
52 * Determine the color to code a monster spell
53 *
54 * This function assigns a color to each monster spell, depending on how
55 * dangerous the attack is to the player given current state. Spells may be
56 * colored green (least dangerous), yellow, orange, or red (most dangerous).
57 */
spell_color(struct player * p,const struct monster_race * race,int spell_index)58 static int spell_color(struct player *p, const struct monster_race *race,
59 int spell_index)
60 {
61 const struct monster_spell *spell = monster_spell_by_index(spell_index);
62 struct monster_spell_level *level = spell->level;
63 struct effect *eff = spell ? spell->effect : NULL;
64
65 /* No spell */
66 if (!spell) return COLOUR_DARK;
67
68 /* Get the right level */
69 while (level->next && race->spell_power >= level->next->power) {
70 level = level->next;
71 }
72
73 /* Unresistable spells just use the default color */
74 if (!level->lore_attr_resist && !level->lore_attr_immune) {
75 return level->lore_attr;
76 }
77
78 /* Spells with a save */
79 if (level->save_message) {
80 /* Mixed results if the save may fail, perfect result if it can't */
81 if (p->known_state.skills[SKILL_SAVE] < 100) {
82 if (eff->index == EF_TELEPORT_LEVEL) {
83 /* Special case - teleport level */
84 if (p->known_state.el_info[ELEM_NEXUS].res_level > 0) {
85 return level->lore_attr_resist;
86 } else {
87 return level->lore_attr;
88 }
89 } else if (eff->index == EF_TIMED_INC) {
90 /* Simple timed effects */
91 if (player_inc_check(p, eff->subtype, true)) {
92 return level->lore_attr;
93 } else {
94 return level->lore_attr_resist;
95 }
96 } else if (level->lore_attr_immune) {
97 /* Multiple timed effects plus damage */
98 for (; eff; eff = eff->next) {
99 if (eff->index != EF_TIMED_INC) continue;
100 if (player_inc_check(p, eff->subtype, true)) {
101 return level->lore_attr;
102 }
103 }
104 return level->lore_attr_resist;
105 } else {
106 /* Straight damage */
107 return level->lore_attr;
108 }
109 } else if (level->lore_attr_immune) {
110 return level->lore_attr_immune;
111 } else {
112 return level->lore_attr_resist;
113 }
114 }
115
116 /* Bolts, balls and breaths */
117 if ((eff->index == EF_BOLT) || (eff->index == EF_BALL) ||
118 (eff->index == EF_BREATH)) {
119 /* Treat by element */
120 switch (eff->subtype) {
121 /* Special case - sound */
122 case ELEM_SOUND:
123 if (p->known_state.el_info[ELEM_SOUND].res_level > 0) {
124 return level->lore_attr_immune;
125 } else if (of_has(p->known_state.flags, OF_PROT_STUN)) {
126 return level->lore_attr_resist;
127 } else {
128 return level->lore_attr;
129 }
130 break;
131 /* Special case - nexus */
132 case ELEM_NEXUS:
133 if (p->known_state.el_info[ELEM_NEXUS].res_level > 0) {
134 return level->lore_attr_immune;
135 } else if (p->known_state.skills[SKILL_SAVE] >= 100) {
136 return level->lore_attr_resist;
137 } else {
138 return level->lore_attr;
139 }
140 break;
141 /* Elements that stun or confuse */
142 case ELEM_FORCE:
143 case ELEM_ICE:
144 case ELEM_PLASMA:
145 case ELEM_WATER:
146 if (!of_has(p->known_state.flags, OF_PROT_STUN)) {
147 return level->lore_attr;
148 } else if (!of_has(p->known_state.flags, OF_PROT_CONF) &&
149 (eff->subtype == ELEM_WATER)){
150 return level->lore_attr;
151 } else {
152 return level->lore_attr_resist;
153 }
154 break;
155 /* All other elements */
156 default:
157 if (p->known_state.el_info[eff->subtype].res_level == 3) {
158 return level->lore_attr_immune;
159 } else if (p->known_state.el_info[eff->subtype].res_level > 0) {
160 return level->lore_attr_resist;
161 } else {
162 return level->lore_attr;
163 }
164 }
165 }
166
167 return level->lore_attr;
168 }
169
170 /**
171 * Determine the color to code a monster melee blow effect
172 *
173 * This function assigns a color to each monster blow effect, depending on how
174 * dangerous the attack is to the player given current state. Blows may be
175 * colored green (least dangerous), yellow, orange, or red (most dangerous).
176 */
blow_color(struct player * p,int blow_idx)177 int blow_color(struct player *p, int blow_idx)
178 {
179 const struct blow_effect *blow = &blow_effects[blow_idx];
180
181 /* Some blows just use the default color */
182 if (!blow->lore_attr_resist && !blow->lore_attr_immune) {
183 return blow->lore_attr;
184 }
185
186 /* Effects with immunities are straightforward */
187 if (blow->lore_attr_immune) {
188 int i;
189
190 for (i = ELEM_ACID; i < ELEM_POIS; i++) {
191 if (proj_name_to_idx(blow->name) == i) {
192 break;
193 }
194 }
195
196 if (p->known_state.el_info[i].res_level == 3) {
197 return blow->lore_attr_immune;
198 } else if (p->known_state.el_info[i].res_level > 0) {
199 return blow->lore_attr_resist;
200 } else {
201 return blow->lore_attr;
202 }
203 }
204
205 /* Now look at what player attributes can protect from the effects */
206 if (streq(blow->effect_type, "theft")) {
207 if (p->lev + adj_dex_safe[p->known_state.stat_ind[STAT_DEX]] >= 100) {
208 return blow->lore_attr_resist;
209 } else {
210 return blow->lore_attr;
211 }
212 } else if (streq(blow->effect_type, "drain")) {
213 int i;
214 bool found = false;
215 for (i = 0; i < z_info->pack_size; i++) {
216 struct object *obj = player->upkeep->inven[i];
217 if (obj && tval_can_have_charges(obj) && obj->pval) {
218 found = true;
219 break;
220 }
221 }
222 if (found) {
223 return blow->lore_attr;
224 } else {
225 return blow->lore_attr_resist;
226 }
227 } else if (streq(blow->effect_type, "eat-food")) {
228 int i;
229 bool found = false;
230 for (i = 0; i < z_info->pack_size; i++) {
231 struct object *obj = player->upkeep->inven[i];
232 if (obj && tval_is_edible(obj)) {
233 found = true;
234 break;
235 }
236 }
237 if (found) {
238 return blow->lore_attr;
239 } else {
240 return blow->lore_attr_resist;
241 }
242 } else if (streq(blow->effect_type, "eat-light")) {
243 int light_slot = slot_by_name(player, "light");
244 struct object *obj = slot_object(player, light_slot);
245 if (obj && obj->timeout && !of_has(obj->flags, OF_NO_FUEL)) {
246 return blow->lore_attr;
247 } else {
248 return blow->lore_attr_resist;
249 }
250 } else if (streq(blow->effect_type, "element")) {
251 if (p->known_state.el_info[blow->resist].res_level > 0) {
252 return blow->lore_attr_resist;
253 } else {
254 return blow->lore_attr;
255 }
256 } else if (streq(blow->effect_type, "flag")) {
257 if (of_has(p->known_state.flags, blow->resist)) {
258 return blow->lore_attr_resist;
259 } else {
260 return blow->lore_attr;
261 }
262 } else if (streq(blow->effect_type, "all_sustains")) {
263 if (of_has(p->known_state.flags, OF_SUST_STR) &&
264 of_has(p->known_state.flags, OF_SUST_INT) &&
265 of_has(p->known_state.flags, OF_SUST_WIS) &&
266 of_has(p->known_state.flags, OF_SUST_DEX) &&
267 of_has(p->known_state.flags, OF_SUST_CON)) {
268 return blow->lore_attr_resist;
269 } else {
270 return blow->lore_attr;
271 }
272 }
273
274 return blow->lore_attr;
275 }
276
lore_learn_spell_if_has(struct monster_lore * lore,const struct monster_race * race,int flag)277 void lore_learn_spell_if_has(struct monster_lore *lore, const struct monster_race *race, int flag)
278 {
279 if (rsf_has(race->spell_flags, flag)) {
280 rsf_on(lore->spell_flags, flag);
281 }
282 }
283
lore_learn_spell_if_visible(struct monster_lore * lore,const struct monster * mon,int flag)284 void lore_learn_spell_if_visible(struct monster_lore *lore, const struct monster *mon, int flag)
285 {
286 if (monster_is_visible(mon)) {
287 rsf_on(lore->spell_flags, flag);
288 }
289 }
290
lore_learn_flag_if_visible(struct monster_lore * lore,const struct monster * mon,int flag)291 void lore_learn_flag_if_visible(struct monster_lore *lore, const struct monster *mon, int flag)
292 {
293 if (monster_is_visible(mon)) {
294 rf_on(lore->flags, flag);
295 }
296 }
297
298
299 /**
300 * Update which bits of lore are known
301 */
lore_update(const struct monster_race * race,struct monster_lore * lore)302 void lore_update(const struct monster_race *race, struct monster_lore *lore)
303 {
304 int i;
305 bitflag mask[RF_SIZE];
306
307 if (!race || !lore) return;
308
309 /* Assume some "obvious" flags */
310 create_mon_flag_mask(mask, RFT_OBV, RFT_MAX);
311 mflag_union(lore->flags, mask);
312
313 /* Blows */
314 for (i = 0; i < z_info->mon_blows_max; i++) {
315 if (!race->blow) break;
316 if (lore->blow_known[i] || lore->blows[i].times_seen ||
317 lore->all_known) {
318 lore->blow_known[i] = true;
319 lore->blows[i].method = race->blow[i].method;
320 lore->blows[i].effect = race->blow[i].effect;
321 lore->blows[i].dice = race->blow[i].dice;
322 }
323 }
324
325 /* Killing a monster reveals some properties */
326 if ((lore->tkills > 0) || lore->all_known) {
327 lore->armour_known = true;
328 lore->drop_known = true;
329 create_mon_flag_mask(mask, RFT_RACE_A, RFT_RACE_N, RFT_DROP, RFT_MAX);
330 mflag_union(lore->flags, mask);
331 rf_on(lore->flags, RF_FORCE_DEPTH);
332 }
333
334 /* Awareness */
335 if ((((int)lore->wake * (int)lore->wake) > race->sleep) ||
336 (lore->ignore == UCHAR_MAX) || lore->all_known ||
337 ((race->sleep == 0) && (lore->tkills >= 10)))
338 lore->sleep_known = true;
339
340 /* Spellcasting frequency */
341 if (lore->cast_innate > 50 || lore->all_known) {
342 lore->innate_freq_known = true;
343 }
344 if (lore->cast_spell > 50 || lore->all_known) {
345 lore->spell_freq_known = true;
346 }
347
348 /* Flags for probing and cheating */
349 if (lore->all_known) {
350 rf_setall(lore->flags);
351 rsf_copy(lore->spell_flags, race->spell_flags);
352 }
353 }
354
355 /**
356 * Learn everything about a monster.
357 *
358 * Sets the all_known variable, all flags and all relevant spell flags.
359 */
cheat_monster_lore(const struct monster_race * race,struct monster_lore * lore)360 void cheat_monster_lore(const struct monster_race *race, struct monster_lore *lore)
361 {
362 assert(race);
363 assert(lore);
364
365 /* Full knowledge */
366 lore->all_known = true;
367 lore_update(race, lore);
368 }
369
370 /**
371 * Forget everything about a monster.
372 */
wipe_monster_lore(const struct monster_race * race,struct monster_lore * lore)373 void wipe_monster_lore(const struct monster_race *race, struct monster_lore *lore)
374 {
375 struct monster_blow *blows;
376 bool *blow_known;
377 struct monster_drop *d;
378 struct monster_friends *f;
379 struct monster_friends_base *fb;
380 struct monster_mimic *mk;
381
382 assert(race);
383 assert(lore);
384
385 d = lore->drops;
386 while (d) {
387 struct monster_drop *dn = d->next;
388 mem_free(d);
389 d = dn;
390 }
391 f = lore->friends;
392 while (f) {
393 struct monster_friends *fn = f->next;
394 mem_free(f);
395 f = fn;
396 }
397 fb = lore->friends_base;
398 while (fb) {
399 struct monster_friends_base *fbn = fb->next;
400 mem_free(fb);
401 fb = fbn;
402 }
403 mk = lore->mimic_kinds;
404 while (mk) {
405 struct monster_mimic *mkn = mk->next;
406 mem_free(mk);
407 mk = mkn;
408 }
409 /*
410 * Keep the blows and blow_known pointers - other code assumes they
411 * are not NULL. Wipe the pointed to memory.
412 */
413 blows = lore->blows;
414 memset(blows, 0, z_info->mon_blows_max * sizeof(*blows));
415 blow_known = lore->blow_known;
416 memset(blow_known, 0, z_info->mon_blows_max * sizeof(*blow_known));
417 memset(lore, 0, sizeof(*lore));
418 lore->blows = blows;
419 lore->blow_known = blow_known;
420 }
421
422 /**
423 * Learn about a monster (by "probing" it)
424 */
lore_do_probe(struct monster * mon)425 void lore_do_probe(struct monster *mon)
426 {
427 struct monster_lore *lore = get_lore(mon->race);
428
429 lore->all_known = true;
430 lore_update(mon->race, lore);
431
432 /* Update monster recall window */
433 if (player->upkeep->monster_race == mon->race)
434 player->upkeep->redraw |= (PR_MONSTER);
435 }
436
437 /**
438 * Determine whether the monster is fully known
439 */
lore_is_fully_known(const struct monster_race * race)440 bool lore_is_fully_known(const struct monster_race *race)
441 {
442 unsigned i;
443 struct monster_lore *lore = get_lore(race);
444
445 /* Check if already known */
446 if (lore->all_known)
447 return true;
448
449 if (!lore->armour_known)
450 return false;
451 /* Only check spells if the monster can cast them */
452 if (!lore->spell_freq_known && race->freq_innate + race->freq_spell)
453 return false;
454 if (!lore->drop_known)
455 return false;
456 if (!lore->sleep_known)
457 return false;
458
459 /* Check if blows are known */
460 for (i = 0; i < z_info->mon_blows_max; i++){
461 /* Only check if the blow exists */
462 if (!race->blow[i].method)
463 break;
464 if (!lore->blow_known[i])
465 return false;
466
467 }
468
469 /* Check all the flags */
470 for (i = 0; i < RF_SIZE; i++)
471 if (!lore->flags[i])
472 return false;
473
474
475 /* Check spell flags */
476 for (i = 0; i < RSF_SIZE; i++)
477 if (lore->spell_flags[i] != race->spell_flags[i])
478 return false;
479
480 /* The player knows everything */
481 lore->all_known = true;
482 lore_update(race, lore);
483 return true;
484 }
485
486
487 /**
488 * Take note that the given monster just dropped some treasure
489 *
490 * Note that learning the "GOOD"/"GREAT" flags gives information
491 * about the treasure (even when the monster is killed for the first
492 * time, such as uniques, and the treasure has not been examined yet).
493 *
494 * This "indirect" method is used to prevent the player from learning
495 * exactly how much treasure a monster can drop from observing only
496 * a single example of a drop. This method actually observes how much
497 * gold and items are dropped, and remembers that information to be
498 * described later by the monster recall code.
499 */
lore_treasure(struct monster * mon,int num_item,int num_gold)500 void lore_treasure(struct monster *mon, int num_item, int num_gold)
501 {
502 struct monster_lore *lore = get_lore(mon->race);
503
504 assert(num_item >= 0);
505 assert(num_gold >= 0);
506
507 /* Note the number of things dropped */
508 if (num_item > lore->drop_item)
509 lore->drop_item = num_item;
510 if (num_gold > lore->drop_gold)
511 lore->drop_gold = num_gold;
512
513 /* Learn about drop quality */
514 rf_on(lore->flags, RF_DROP_GOOD);
515 rf_on(lore->flags, RF_DROP_GREAT);
516
517 /* Update monster recall window */
518 if (player->upkeep->monster_race == mon->race)
519 player->upkeep->redraw |= (PR_MONSTER);
520 }
521
522 /**
523 * Copies into `flags` the flags of the given monster race that are known
524 * to the given lore structure (usually the player's knowledge).
525 *
526 * Known flags will be 1 for present, or 0 for not present. Unknown flags
527 * will always be 0.
528 */
monster_flags_known(const struct monster_race * race,const struct monster_lore * lore,bitflag flags[RF_SIZE])529 void monster_flags_known(const struct monster_race *race,
530 const struct monster_lore *lore,
531 bitflag flags[RF_SIZE])
532 {
533 rf_copy(flags, race->flags);
534 rf_inter(flags, lore->flags);
535 }
536
537 /**
538 * Return a description for the given monster race awareness value.
539 *
540 * Descriptions are in a table within the function. Returns a sensible string
541 * for values not in the table.
542 *
543 * \param awareness is the inactivity counter of the race (monster_race.sleep).
544 */
lore_describe_awareness(s16b awareness)545 static const char *lore_describe_awareness(s16b awareness)
546 {
547 /* Value table ordered descending, for priority. Terminator is
548 * {SHRT_MAX, NULL}. */
549 static const struct lore_awareness {
550 s16b threshold;
551 const char *description;
552 } lore_awareness_description[] = {
553 {200, "prefers to ignore"},
554 {95, "pays very little attention to"},
555 {75, "pays little attention to"},
556 {45, "tends to overlook"},
557 {25, "takes quite a while to see"},
558 {10, "takes a while to see"},
559 {5, "is fairly observant of"},
560 {3, "is observant of"},
561 {1, "is very observant of"},
562 {0, "is vigilant for"},
563 {SHRT_MAX, NULL},
564 };
565 const struct lore_awareness *current = lore_awareness_description;
566
567 while (current->threshold != SHRT_MAX && current->description != NULL) {
568 if (awareness > current->threshold)
569 return current->description;
570
571 current++;
572 }
573
574 /* Values zero and less are the most vigilant */
575 return "is ever vigilant for";
576 }
577
578 /**
579 * Return a description for the given monster race speed value.
580 *
581 * Descriptions are in a table within the function. Returns a sensible string
582 * for values not in the table.
583 *
584 * \param speed is the speed rating of the race (monster_race.speed).
585 */
lore_describe_speed(byte speed)586 static const char *lore_describe_speed(byte speed)
587 {
588 /* Value table ordered descending, for priority. Terminator is
589 * {UCHAR_MAX, NULL}. */
590 static const struct lore_speed {
591 byte threshold;
592 const char *description;
593 } lore_speed_description[] = {
594 {130, "incredibly quickly"},
595 {120, "very quickly"},
596 {115, "quickly"},
597 {110, "fairly quickly"},
598 {109, "normal speed"}, /* 110 is normal speed */
599 {99, "slowly"},
600 {89, "very slowly"},
601 {0, "incredibly slowly"},
602 {UCHAR_MAX, NULL},
603 };
604 const struct lore_speed *current = lore_speed_description;
605
606 while (current->threshold != UCHAR_MAX && current->description != NULL) {
607 if (speed > current->threshold)
608 return current->description;
609
610 current++;
611 }
612
613 /* Return a weird description, since the value wasn't found in the table */
614 return "erroneously";
615 }
616
617 /**
618 * Append the monster speed, in words, to a textblock.
619 *
620 * \param tb is the textblock we are adding to.
621 * \param race is the monster race we are describing.
622 */
lore_adjective_speed(textblock * tb,const struct monster_race * race)623 void lore_adjective_speed(textblock *tb, const struct monster_race *race)
624 {
625 /* "at" is separate from the normal speed description in order to use the
626 * normal text colour */
627 if (race->speed == 110)
628 textblock_append(tb, "at ");
629
630 textblock_append_c(tb, COLOUR_GREEN, "%s", lore_describe_speed(race->speed));
631 }
632
633 /**
634 * Append the monster speed, in multipliers, to a textblock.
635 *
636 * \param tb is the textblock we are adding to.
637 * \param race is the monster race we are describing.
638 */
lore_multiplier_speed(textblock * tb,const struct monster_race * race)639 void lore_multiplier_speed(textblock *tb, const struct monster_race *race)
640 {
641 // moves at 2.3x normal speed (0.9x your current speed)
642 textblock_append(tb, "at ");
643
644 char buf[8] = "";
645 int multiplier = 10 * extract_energy[race->speed] / extract_energy[110];
646 byte int_mul = multiplier / 10;
647 byte dec_mul = multiplier % 10;
648 byte attr = COLOUR_ORANGE;
649
650 strnfmt(buf, sizeof(buf), "%d.%dx", int_mul, dec_mul);
651 textblock_append_c(tb, COLOUR_L_BLUE, "%s", buf);
652
653 textblock_append(tb, " normal speed, which is ");
654 multiplier = 100 * extract_energy[race->speed]
655 / extract_energy[player->state.speed];
656 int_mul = multiplier / 100;
657 dec_mul = multiplier % 100;
658 if (!dec_mul) {
659 strnfmt(buf, sizeof(buf), "%dx", int_mul);
660 } else if (!(dec_mul % 10)) {
661 strnfmt(buf, sizeof(buf), "%d.%dx", int_mul, dec_mul / 10);
662 } else {
663 strnfmt(buf, sizeof(buf), "%d.%02dx", int_mul, dec_mul);
664 }
665
666 if (player->state.speed > race->speed) {
667 attr = COLOUR_L_GREEN;
668 } else if (player->state.speed < race->speed) {
669 attr = COLOUR_RED;
670 }
671 if (player->state.speed == race->speed) {
672 textblock_append(tb, "the same as you");
673 } else {
674 textblock_append_c(tb, attr, "%s", buf);
675 textblock_append(tb, " your speed");
676 }
677 }
678
679 /**
680 * Return a value describing the sex of the provided monster race.
681 */
lore_monster_sex(const struct monster_race * race)682 static monster_sex_t lore_monster_sex(const struct monster_race *race)
683 {
684 if (rf_has(race->flags, RF_FEMALE))
685 return MON_SEX_FEMALE;
686 else if (rf_has(race->flags, RF_MALE))
687 return MON_SEX_MALE;
688
689 return MON_SEX_NEUTER;
690 }
691
692 /**
693 * Return a pronoun for a monster; used as the subject of a sentence.
694 *
695 * Descriptions are in a table within the function. Table must match
696 * monster_sex_t values.
697 *
698 * \param sex is the gender value (as provided by `lore_monster_sex()`.
699 * \param title_case indicates whether the initial letter should be
700 * capitalized; `true` is capitalized, `false` is not.
701 */
lore_pronoun_nominative(monster_sex_t sex,bool title_case)702 static const char *lore_pronoun_nominative(monster_sex_t sex, bool title_case)
703 {
704 static const char *lore_pronouns[MON_SEX_MAX][2] = {
705 {"it", "It"},
706 {"he", "He"},
707 {"she", "She"},
708 };
709
710 int pronoun_index = MON_SEX_NEUTER, case_index = 0;
711
712 if (sex < MON_SEX_MAX)
713 pronoun_index = sex;
714
715 if (title_case)
716 case_index = 1;
717
718 return lore_pronouns[pronoun_index][case_index];
719 }
720
721 /**
722 * Return a possessive pronoun for a monster.
723 *
724 * Descriptions are in a table within the function. Table must match
725 * monster_sex_t values.
726 *
727 * \param sex is the gender value (as provided by `lore_monster_sex()`.
728 * \param title_case indicates whether the initial letter should be
729 * capitalized; `true` is capitalized, `false` is not.
730 */
lore_pronoun_possessive(monster_sex_t sex,bool title_case)731 static const char *lore_pronoun_possessive(monster_sex_t sex, bool title_case)
732 {
733 static const char *lore_pronouns[MON_SEX_MAX][2] = {
734 {"its", "Its"},
735 {"his", "His"},
736 {"her", "Her"},
737 };
738
739 int pronoun_index = MON_SEX_NEUTER, case_index = 0;
740
741 if (sex < MON_SEX_MAX)
742 pronoun_index = sex;
743
744 if (title_case)
745 case_index = 1;
746
747 return lore_pronouns[pronoun_index][case_index];
748 }
749
750 /**
751 * Append a clause containing a list of descriptions of monster flags from
752 * list-mon-race-flags.h to a textblock.
753 *
754 * The text that joins the list is drawn using the default attributes. The list
755 * uses a serial comma ("a, b, c, and d").
756 *
757 * \param tb is the textblock we are adding to.
758 * \param f is the set of flags to be described.
759 * \param attr is the attribute each list item will be drawn with.
760 * \param start is a string to start the clause.
761 * \param conjunction is a string that is added before the last item.
762 * \param end is a string that is added after the last item.
763 */
lore_append_clause(textblock * tb,bitflag * f,byte attr,const char * start,const char * conjunction,const char * end)764 static void lore_append_clause(textblock *tb, bitflag *f, byte attr,
765 const char *start, const char *conjunction,
766 const char *end)
767 {
768 int count = rf_count(f);
769 bool comma = count > 2;
770
771 if (count) {
772 int flag;
773 textblock_append(tb, "%s", start);
774 for (flag = rf_next(f, FLAG_START); flag; flag = rf_next(f, flag + 1)) {
775 /* First entry starts immediately */
776 if (flag != rf_next(f, FLAG_START)) {
777 if (comma) {
778 textblock_append(tb, ",");
779 }
780 /* Last entry */
781 if (rf_next(f, flag + 1) == FLAG_END) {
782 textblock_append(tb, " ");
783 textblock_append(tb, "%s", conjunction);
784 }
785 textblock_append(tb, " ");
786 }
787 textblock_append_c(tb, attr, "%s", describe_race_flag(flag));
788 }
789 textblock_append(tb, "%s", end);
790 }
791 }
792
793
794 /**
795 * Append a list of spell descriptions.
796 *
797 * This is a modified version of `lore_append_clause()` to format spells.
798 *
799 * \param tb is the textblock we are adding to.
800 * \param f is the set of flags to be described.
801 * \param know_hp is whether the player knows the monster's AC.
802 * \param race is the monster race.
803 * \param conjunction is a string that is added before the last item.
804 * \param end is a string that is added after the last item.
805 */
lore_append_spell_clause(textblock * tb,bitflag * f,bool know_hp,const struct monster_race * race,const char * conjunction,const char * end)806 static void lore_append_spell_clause(textblock *tb, bitflag *f, bool know_hp,
807 const struct monster_race *race,
808 const char *conjunction,
809 const char *end)
810 {
811 int count = rsf_count(f);
812 bool comma = count > 2;
813
814 if (count) {
815 int spell;
816 for (spell = rsf_next(f, FLAG_START); spell;
817 spell = rsf_next(f, spell + 1)) {
818 int color = spell_color(player, race, spell);
819 int damage = mon_spell_lore_damage(spell, race, know_hp);
820
821 /* First entry starts immediately */
822 if (spell != rsf_next(f, FLAG_START)) {
823 if (comma) {
824 textblock_append(tb, ",");
825 }
826 /* Last entry */
827 if (rsf_next(f, spell + 1) == FLAG_END) {
828 textblock_append(tb, " ");
829 textblock_append(tb, "%s", conjunction);
830 }
831 textblock_append(tb, " ");
832 }
833 textblock_append_c(tb, color, "%s",
834 mon_spell_lore_description(spell, race));
835 if (damage > 0) {
836 textblock_append_c(tb, color, " (%d)", damage);
837 }
838 }
839 textblock_append(tb, "%s", end);
840 }
841 }
842
843 /**
844 * Append the kill history to a texblock for a given monster race.
845 *
846 * Known race flags are passed in for simplicity/efficiency.
847 *
848 * \param tb is the textblock we are adding to.
849 * \param race is the monster race we are describing.
850 * \param lore is the known information about the monster race.
851 * \param known_flags is the preprocessed bitfield of race flags known to the
852 * player.
853 */
lore_append_kills(textblock * tb,const struct monster_race * race,const struct monster_lore * lore,const bitflag known_flags[RF_SIZE])854 void lore_append_kills(textblock *tb, const struct monster_race *race,
855 const struct monster_lore *lore,
856 const bitflag known_flags[RF_SIZE])
857 {
858 monster_sex_t msex = MON_SEX_NEUTER;
859 bool out = true;
860
861 assert(tb && race && lore);
862
863 /* Extract a gender (if applicable) */
864 msex = lore_monster_sex(race);
865
866 /* Treat by whether unique, then by whether they have any player kills */
867 if (rf_has(known_flags, RF_UNIQUE)) {
868 /* Hack -- Determine if the unique is "dead" */
869 bool dead = (race->max_num == 0) ? true : false;
870
871 /* We've been killed... */
872 if (lore->deaths) {
873 /* Killed ancestors */
874 textblock_append(tb, "%s has slain %d of your ancestors",
875 lore_pronoun_nominative(msex, true), lore->deaths);
876
877 /* But we've also killed it */
878 if (dead)
879 textblock_append(tb, ", but you have taken revenge! ");
880
881 /* Unavenged (ever) */
882 else
883 textblock_append(tb, ", who %s unavenged. ",
884 VERB_AGREEMENT(lore->deaths, "remains",
885 "remain"));
886 } else if (dead) { /* Dead unique who never hurt us */
887 textblock_append(tb, "You have slain this foe. ");
888 } else {
889 /* Alive and never killed us */
890 out = false;
891 }
892 } else if (lore->deaths) {
893 /* Dead ancestors */
894 textblock_append(tb, "%d of your ancestors %s been killed by this creature, ", lore->deaths, VERB_AGREEMENT(lore->deaths, "has", "have"));
895
896 if (lore->pkills) {
897 /* Some kills this life */
898 textblock_append(tb, "and you have exterminated at least %d of the creatures. ", lore->pkills);
899 } else if (lore->tkills) {
900 /* Some kills past lives */
901 textblock_append(tb, "and your ancestors have exterminated at least %d of the creatures. ", lore->tkills);
902 } else {
903 /* No kills */
904 textblock_append_c(tb, COLOUR_RED, "and %s is not ever known to have been defeated. ", lore_pronoun_nominative(msex, false));
905 }
906 } else {
907 if (lore->pkills) {
908 /* Killed some this life */
909 textblock_append(tb, "You have killed at least %d of these creatures. ", lore->pkills);
910 } else if (lore->tkills) {
911 /* Killed some last life */
912 textblock_append(tb, "Your ancestors have killed at least %d of these creatures. ", lore->tkills);
913 } else {
914 /* Killed none */
915 textblock_append(tb, "No battles to the death are recalled. ");
916 }
917 }
918
919 /* Separate */
920 if (out)
921 textblock_append(tb, "\n");
922 }
923
924 /**
925 * Append the monster race description to a textblock.
926 *
927 * \param tb is the textblock we are adding to.
928 * \param race is the monster race we are describing.
929 */
lore_append_flavor(textblock * tb,const struct monster_race * race)930 void lore_append_flavor(textblock *tb, const struct monster_race *race)
931 {
932 assert(tb && race);
933
934 textblock_append(tb, "%s\n", race->text);
935 }
936
937 /**
938 * Append the monster type, location, and movement patterns to a textblock.
939 *
940 * Known race flags are passed in for simplicity/efficiency.
941 *
942 * \param tb is the textblock we are adding to.
943 * \param race is the monster race we are describing.
944 * \param lore is the known information about the monster race.
945 * \param known_flags is the preprocessed bitfield of race flags known to the
946 * player.
947 */
lore_append_movement(textblock * tb,const struct monster_race * race,const struct monster_lore * lore,bitflag known_flags[RF_SIZE])948 void lore_append_movement(textblock *tb, const struct monster_race *race,
949 const struct monster_lore *lore,
950 bitflag known_flags[RF_SIZE])
951 {
952 int f;
953 bitflag flags[RF_SIZE];
954
955 assert(tb && race && lore);
956
957 textblock_append(tb, "This");
958
959 /* Get adjectives */
960 create_mon_flag_mask(flags, RFT_RACE_A, RFT_MAX);
961 rf_inter(flags, race->flags);
962 for (f = rf_next(flags, FLAG_START); f; f = rf_next(flags, f + 1)) {
963 textblock_append_c(tb, COLOUR_L_BLUE, " %s", describe_race_flag(f));
964 }
965
966 /* Get noun */
967 create_mon_flag_mask(flags, RFT_RACE_N, RFT_MAX);
968 rf_inter(flags, race->flags);
969 f = rf_next(flags, FLAG_START);
970 if (f) {
971 textblock_append_c(tb, COLOUR_L_BLUE, " %s", describe_race_flag(f));
972 } else {
973 textblock_append_c(tb, COLOUR_L_BLUE, " creature");
974 }
975
976 /* Describe location */
977 if (race->level == 0) {
978 textblock_append(tb, " lives in the town");
979 } else {
980 byte colour = (race->level > player->max_depth) ? COLOUR_RED :
981 COLOUR_L_BLUE;
982
983 if (rf_has(known_flags, RF_FORCE_DEPTH))
984 textblock_append(tb, " is found ");
985 else
986 textblock_append(tb, " is normally found ");
987
988 textblock_append(tb, "at depths of ");
989 textblock_append_c(tb, colour, "%d", race->level * 50);
990 textblock_append(tb, " feet (level ");
991 textblock_append_c(tb, colour, "%d", race->level);
992 textblock_append(tb, ")");
993 }
994
995 textblock_append(tb, ", and moves");
996
997 /* Random-ness */
998 if (flags_test(known_flags, RF_SIZE, RF_RAND_50, RF_RAND_25, FLAG_END)) {
999 /* Adverb */
1000 if (rf_has(known_flags, RF_RAND_50) && rf_has(known_flags, RF_RAND_25))
1001 textblock_append(tb, " extremely");
1002 else if (rf_has(known_flags, RF_RAND_50))
1003 textblock_append(tb, " somewhat");
1004 else if (rf_has(known_flags, RF_RAND_25))
1005 textblock_append(tb, " a bit");
1006
1007 /* Adjective */
1008 textblock_append(tb, " erratically");
1009
1010 /* Hack -- Occasional conjunction */
1011 if (race->speed != 110) textblock_append(tb, ", and");
1012 }
1013
1014 /* Speed */
1015 textblock_append(tb, " ");
1016
1017 if (OPT(player, effective_speed))
1018 lore_multiplier_speed(tb, race);
1019 else
1020 lore_adjective_speed(tb, race);
1021
1022 /* The speed description also describes "attack speed" */
1023 if (rf_has(known_flags, RF_NEVER_MOVE)) {
1024 textblock_append(tb, ", but ");
1025 textblock_append_c(tb, COLOUR_L_GREEN,
1026 "does not deign to chase intruders");
1027 }
1028
1029 /* End this sentence */
1030 textblock_append(tb, ". ");
1031 }
1032
1033 /**
1034 * Append the monster AC, HP, and hit chance to a textblock.
1035 *
1036 * Known race flags are passed in for simplicity/efficiency.
1037 *
1038 * \param tb is the textblock we are adding to.
1039 * \param race is the monster race we are describing.
1040 * \param lore is the known information about the monster race.
1041 * \param known_flags is the preprocessed bitfield of race flags known to the
1042 * player.
1043 */
lore_append_toughness(textblock * tb,const struct monster_race * race,const struct monster_lore * lore,bitflag known_flags[RF_SIZE])1044 void lore_append_toughness(textblock *tb, const struct monster_race *race,
1045 const struct monster_lore *lore,
1046 bitflag known_flags[RF_SIZE])
1047 {
1048 monster_sex_t msex = MON_SEX_NEUTER;
1049 long chance = 0, chance2 = 0;
1050 struct object *weapon = equipped_item_by_slot_name(player, "weapon");
1051
1052 assert(tb && race && lore);
1053
1054 /* Extract a gender (if applicable) */
1055 msex = lore_monster_sex(race);
1056
1057 /* Describe monster "toughness" */
1058 if (lore->armour_known) {
1059 /* Armor */
1060 textblock_append(tb, "%s has an armor rating of ",
1061 lore_pronoun_nominative(msex, true));
1062 textblock_append_c(tb, COLOUR_L_BLUE, "%d", race->ac);
1063
1064 /* Hitpoints */
1065 textblock_append(tb, ", and a");
1066
1067 if (!rf_has(known_flags, RF_UNIQUE))
1068 textblock_append(tb, "n average");
1069
1070 textblock_append(tb, " life rating of ");
1071 textblock_append_c(tb, COLOUR_L_BLUE, "%d", race->avg_hp);
1072 textblock_append(tb, ". ");
1073
1074 /* Player's base chance to hit */
1075 chance = chance_of_melee_hit(player, weapon);
1076
1077 /* The following calculations are based on test_hit();
1078 * make sure to keep it in sync */
1079 if (chance < 9) {
1080 chance = 9;
1081 }
1082 chance2 = 12 + (100 - 12 - 5) * (chance - (race->ac * 2 / 3)) / chance;
1083 if (chance2 < 12) {
1084 chance2 = 12;
1085 }
1086
1087 textblock_append(tb, "You have a");
1088 if ((chance2 == 8) || ((chance2 / 10) == 8))
1089 textblock_append(tb, "n");
1090 textblock_append_c(tb, COLOUR_L_BLUE, " %d", chance2);
1091 textblock_append(tb, " percent chance to hit such a creature in melee (if you can see it). ");
1092 }
1093 }
1094
1095 /**
1096 * Append the experience value description to a textblock.
1097 *
1098 * Known race flags are passed in for simplicity/efficiency.
1099 *
1100 * \param tb is the textblock we are adding to.
1101 * \param race is the monster race we are describing.
1102 * \param lore is the known information about the monster race.
1103 * \param known_flags is the preprocessed bitfield of race flags known to the
1104 * player.
1105 */
lore_append_exp(textblock * tb,const struct monster_race * race,const struct monster_lore * lore,bitflag known_flags[RF_SIZE])1106 void lore_append_exp(textblock *tb, const struct monster_race *race,
1107 const struct monster_lore *lore,
1108 bitflag known_flags[RF_SIZE])
1109 {
1110 const char *ordinal, *article;
1111 char buf[20] = "";
1112 long exp_integer, exp_fraction;
1113 s16b level;
1114
1115 /* Check legality and that this is a placeable monster */
1116 assert(tb && race && lore);
1117 if (!race->rarity) return;
1118
1119 /* Introduction */
1120 if (rf_has(known_flags, RF_UNIQUE))
1121 textblock_append(tb, "Killing");
1122 else
1123 textblock_append(tb, "A kill of");
1124
1125 textblock_append(tb, " this creature");
1126
1127 /* calculate the integer exp part */
1128 exp_integer = (long)race->mexp * race->level / player->lev;
1129
1130 /* calculate the fractional exp part scaled by 100, must use long
1131 * arithmetic to avoid overflow */
1132 exp_fraction = ((((long)race->mexp * race->level % player->lev) *
1133 (long)1000 / player->lev + 5) / 10);
1134
1135 /* Calculate textual representation */
1136 strnfmt(buf, sizeof(buf), "%d", exp_integer);
1137 if (exp_fraction)
1138 my_strcat(buf, format(".%02d", exp_fraction), sizeof(buf));
1139
1140 /* Mention the experience */
1141 textblock_append(tb, " is worth ");
1142 textblock_append_c(tb, COLOUR_BLUE, format("%s point%s", buf, PLURAL((exp_integer == 1) && (exp_fraction == 0))));
1143
1144 /* Take account of annoying English */
1145 ordinal = "th";
1146 level = player->lev % 10;
1147 if ((player->lev / 10) == 1) /* nothing */;
1148 else if (level == 1) ordinal = "st";
1149 else if (level == 2) ordinal = "nd";
1150 else if (level == 3) ordinal = "rd";
1151
1152 /* Take account of "leading vowels" in numbers */
1153 article = "a";
1154 level = player->lev;
1155 if ((level == 8) || (level == 11) || (level == 18)) article = "an";
1156
1157 /* Mention the dependance on the player's level */
1158 textblock_append(tb, " for %s %u%s level character. ", article,
1159 level, ordinal);
1160 }
1161
1162 /**
1163 * Append the monster drop description to a textblock.
1164 *
1165 * Known race flags are passed in for simplicity/efficiency.
1166 *
1167 * \param tb is the textblock we are adding to.
1168 * \param race is the monster race we are describing.
1169 * \param lore is the known information about the monster race.
1170 * \param known_flags is the preprocessed bitfield of race flags known to the
1171 * player.
1172 */
lore_append_drop(textblock * tb,const struct monster_race * race,const struct monster_lore * lore,bitflag known_flags[RF_SIZE])1173 void lore_append_drop(textblock *tb, const struct monster_race *race,
1174 const struct monster_lore *lore,
1175 bitflag known_flags[RF_SIZE])
1176 {
1177 int n = 0;
1178 monster_sex_t msex = MON_SEX_NEUTER;
1179
1180 assert(tb && race && lore);
1181 if (!lore->drop_known) return;
1182
1183 /* Extract a gender (if applicable) */
1184 msex = lore_monster_sex(race);
1185
1186 /* Count maximum drop */
1187 n = mon_create_drop_count(race, true);
1188
1189 /* Drops gold and/or items */
1190 if (n > 0) {
1191 bool only_item = rf_has(known_flags, RF_ONLY_ITEM);
1192 bool only_gold = rf_has(known_flags, RF_ONLY_GOLD);
1193
1194 textblock_append(tb, "%s may carry",
1195 lore_pronoun_nominative(msex, true));
1196
1197 /* Count drops */
1198 if (n == 1)
1199 textblock_append_c(tb, COLOUR_BLUE, " a single ");
1200 else if (n == 2)
1201 textblock_append_c(tb, COLOUR_BLUE, " one or two ");
1202 else {
1203 textblock_append(tb, " up to ");
1204 textblock_append_c(tb, COLOUR_BLUE, format("%d ", n));
1205 }
1206
1207 /* Quality */
1208 if (rf_has(known_flags, RF_DROP_GREAT))
1209 textblock_append_c(tb, COLOUR_BLUE, "exceptional ");
1210 else if (rf_has(known_flags, RF_DROP_GOOD))
1211 textblock_append_c(tb, COLOUR_BLUE, "good ");
1212
1213 /* Objects or treasures */
1214 if (only_item && only_gold)
1215 textblock_append_c(tb, COLOUR_BLUE, "error%s", PLURAL(n));
1216 else if (only_item && !only_gold)
1217 textblock_append_c(tb, COLOUR_BLUE, "object%s", PLURAL(n));
1218 else if (!only_item && only_gold)
1219 textblock_append_c(tb, COLOUR_BLUE, "treasure%s", PLURAL(n));
1220 else if (!only_item && !only_gold)
1221 textblock_append_c(tb, COLOUR_BLUE, "object%s or treasure%s",
1222 PLURAL(n), PLURAL(n));
1223
1224 textblock_append(tb, ". ");
1225 }
1226 }
1227
1228 /**
1229 * Append the monster abilities (resists, weaknesses, other traits) to a
1230 * textblock.
1231 *
1232 * Known race flags are passed in for simplicity/efficiency. Note the macros
1233 * that are used to simplify the code.
1234 *
1235 * \param tb is the textblock we are adding to.
1236 * \param race is the monster race we are describing.
1237 * \param lore is the known information about the monster race.
1238 * \param known_flags is the preprocessed bitfield of race flags known to the
1239 * player.
1240 */
lore_append_abilities(textblock * tb,const struct monster_race * race,const struct monster_lore * lore,bitflag known_flags[RF_SIZE])1241 void lore_append_abilities(textblock *tb, const struct monster_race *race,
1242 const struct monster_lore *lore,
1243 bitflag known_flags[RF_SIZE])
1244 {
1245 int flag;
1246 char start[40];
1247 const char *initial_pronoun;
1248 bool prev = false;
1249 bitflag current_flags[RF_SIZE], test_flags[RF_SIZE];
1250 monster_sex_t msex = MON_SEX_NEUTER;
1251
1252 assert(tb && race && lore);
1253
1254 /* Extract a gender (if applicable) and get a pronoun for the start of
1255 * sentences */
1256 msex = lore_monster_sex(race);
1257 initial_pronoun = lore_pronoun_nominative(msex, true);
1258
1259 /* Describe environment-shaping abilities. */
1260 create_mon_flag_mask(current_flags, RFT_ALTER, RFT_MAX);
1261 rf_inter(current_flags, known_flags);
1262 my_strcpy(start, format("%s can ", initial_pronoun), sizeof(start));
1263 lore_append_clause(tb, current_flags, COLOUR_WHITE, start, "and", ". ");
1264
1265 /* Describe detection traits */
1266 create_mon_flag_mask(current_flags, RFT_DET, RFT_MAX);
1267 rf_inter(current_flags, known_flags);
1268 my_strcpy(start, format("%s is ", initial_pronoun), sizeof(start));
1269 lore_append_clause(tb, current_flags, COLOUR_WHITE, start, "and", ". ");
1270
1271 /* Describe special things */
1272 if (rf_has(known_flags, RF_UNAWARE))
1273 textblock_append(tb, "%s disguises itself as something else. ",
1274 initial_pronoun);
1275 if (rf_has(known_flags, RF_MULTIPLY))
1276 textblock_append_c(tb, COLOUR_ORANGE, "%s breeds explosively. ",
1277 initial_pronoun);
1278 if (rf_has(known_flags, RF_REGENERATE))
1279 textblock_append(tb, "%s regenerates quickly. ", initial_pronoun);
1280
1281 /* Describe light */
1282 if (race->light > 1) {
1283 textblock_append(tb, "%s illuminates %s surroundings. ",
1284 initial_pronoun, lore_pronoun_possessive(msex, false));
1285 } else if (race->light == 1) {
1286 textblock_append(tb, "%s is illuminated. ", initial_pronoun);
1287 } else if (race->light == -1) {
1288 textblock_append(tb, "%s is darkened. ", initial_pronoun);
1289 } else if (race->light < -1) {
1290 textblock_append(tb, "%s shrouds %s surroundings in darkness. ",
1291 initial_pronoun, lore_pronoun_possessive(msex, false));
1292 }
1293
1294 /* Collect susceptibilities */
1295 create_mon_flag_mask(current_flags, RFT_VULN, RFT_VULN_I, RFT_MAX);
1296 rf_inter(current_flags, known_flags);
1297 my_strcpy(start, format("%s is hurt by ", initial_pronoun), sizeof(start));
1298 lore_append_clause(tb, current_flags, COLOUR_VIOLET, start, "and", "");
1299 if (!rf_is_empty(current_flags)) {
1300 prev = true;
1301 }
1302
1303 /* Collect immunities and resistances */
1304 create_mon_flag_mask(current_flags, RFT_RES, RFT_MAX);
1305 rf_inter(current_flags, known_flags);
1306
1307 /* Note lack of vulnerability as a resistance */
1308 create_mon_flag_mask(test_flags, RFT_VULN, RFT_MAX);
1309 for (flag = rf_next(test_flags, FLAG_START); flag;
1310 flag = rf_next(test_flags, flag + 1)) {
1311 if (rf_has(lore->flags, flag) && !rf_has(known_flags, flag)) {
1312 rf_on(current_flags, flag);
1313 }
1314 }
1315 if (prev)
1316 my_strcpy(start, ", but resists ", sizeof(start));
1317 else
1318 my_strcpy(start, format("%s resists ", initial_pronoun), sizeof(start));
1319 lore_append_clause(tb, current_flags, COLOUR_L_UMBER, start, "and", "");
1320 if (!rf_is_empty(current_flags)) {
1321 prev = true;
1322 }
1323
1324 /* Collect known but average susceptibilities */
1325 rf_wipe(current_flags);
1326 create_mon_flag_mask(test_flags, RFT_RES, RFT_MAX);
1327 for (flag = rf_next(test_flags, FLAG_START); flag;
1328 flag = rf_next(test_flags, flag + 1)) {
1329 if (rf_has(lore->flags, flag) && !rf_has(known_flags, flag)) {
1330 rf_on(current_flags, flag);
1331 }
1332 }
1333
1334 /* Vulnerabilities need to be specifically removed */
1335 create_mon_flag_mask(test_flags, RFT_VULN_I, RFT_MAX);
1336 rf_inter(test_flags, known_flags);
1337 for (flag = rf_next(test_flags, FLAG_START); flag;
1338 flag = rf_next(test_flags, flag + 1)) {
1339 int susc_flag;
1340 for (susc_flag = rf_next(current_flags, FLAG_START); susc_flag;
1341 susc_flag = rf_next(current_flags, susc_flag + 1)) {
1342 if (streq(describe_race_flag(flag), describe_race_flag(susc_flag)))
1343 rf_off(current_flags, susc_flag);
1344 }
1345 }
1346 if (prev)
1347 my_strcpy(start, ", and does not resist ", sizeof(start));
1348 else
1349 my_strcpy(start, format("%s does not resist ", initial_pronoun),
1350 sizeof(start));
1351
1352 /* Special case for undead */
1353 if (rf_has(known_flags, RF_UNDEAD)) {
1354 rf_off(current_flags, RF_IM_NETHER);
1355 }
1356
1357 lore_append_clause(tb, current_flags, COLOUR_L_UMBER, start, "or", "");
1358 if (!rf_is_empty(current_flags)) {
1359 prev = true;
1360 }
1361
1362 /* Collect non-effects */
1363 create_mon_flag_mask(current_flags, RFT_PROT, RFT_MAX);
1364 rf_inter(current_flags, known_flags);
1365 if (prev)
1366 my_strcpy(start, ", and cannot be ", sizeof(start));
1367 else
1368 my_strcpy(start, format("%s cannot be ", initial_pronoun),
1369 sizeof(start));
1370 lore_append_clause(tb, current_flags, COLOUR_L_UMBER, start, "or", "");
1371 if (!rf_is_empty(current_flags)) {
1372 prev = true;
1373 }
1374
1375 if (prev)
1376 textblock_append(tb, ". ");
1377 }
1378
1379 /**
1380 * Append how the monster reacts to intruders and at what distance it does so.
1381 *
1382 * \param tb is the textblock we are adding to.
1383 * \param race is the monster race we are describing.
1384 * \param lore is the known information about the monster race.
1385 * \param known_flags is the preprocessed bitfield of race flags known to the
1386 * player.
1387 */
lore_append_awareness(textblock * tb,const struct monster_race * race,const struct monster_lore * lore,bitflag known_flags[RF_SIZE])1388 void lore_append_awareness(textblock *tb, const struct monster_race *race,
1389 const struct monster_lore *lore,
1390 bitflag known_flags[RF_SIZE])
1391 {
1392 monster_sex_t msex = MON_SEX_NEUTER;
1393
1394 assert(tb && race && lore);
1395
1396 /* Extract a gender (if applicable) */
1397 msex = lore_monster_sex(race);
1398
1399 /* Do we know how aware it is? */
1400 if (lore->sleep_known)
1401 {
1402 const char *aware = lore_describe_awareness(race->sleep);
1403 textblock_append(tb, "%s %s intruders, which %s may notice from ",
1404 lore_pronoun_nominative(msex, true), aware,
1405 lore_pronoun_nominative(msex, false));
1406 textblock_append_c(tb, COLOUR_L_BLUE, "%d", 10 * race->hearing);
1407 textblock_append(tb, " feet. ");
1408 }
1409 }
1410
1411 /**
1412 * Append information about what other races the monster appears with and if
1413 * they work together.
1414 *
1415 * \param tb is the textblock we are adding to.
1416 * \param race is the monster race we are describing.
1417 * \param lore is the known information about the monster race.
1418 * \param known_flags is the preprocessed bitfield of race flags known to the
1419 * player.
1420 */
lore_append_friends(textblock * tb,const struct monster_race * race,const struct monster_lore * lore,bitflag known_flags[RF_SIZE])1421 void lore_append_friends(textblock *tb, const struct monster_race *race,
1422 const struct monster_lore *lore,
1423 bitflag known_flags[RF_SIZE])
1424 {
1425 monster_sex_t msex = MON_SEX_NEUTER;
1426
1427 assert(tb && race && lore);
1428
1429 /* Extract a gender (if applicable) */
1430 msex = lore_monster_sex(race);
1431
1432 /* Describe friends */
1433 if (race->friends || race->friends_base) {
1434 textblock_append(tb, "%s may appear with other monsters",
1435 lore_pronoun_nominative(msex, true));
1436 if (rf_has(known_flags, RF_GROUP_AI))
1437 textblock_append(tb, " and hunts in packs");
1438 textblock_append(tb, ". ");
1439 }
1440 }
1441
1442 /**
1443 * Append the monster's attack spells to a textblock.
1444 *
1445 * Known race flags are passed in for simplicity/efficiency. Note the macros
1446 * that are used to simplify the code.
1447 *
1448 * \param tb is the textblock we are adding to.
1449 * \param race is the monster race we are describing.
1450 * \param lore is the known information about the monster race.
1451 * \param known_flags is the preprocessed bitfield of race flags known to the
1452 * player.
1453 */
lore_append_spells(textblock * tb,const struct monster_race * race,const struct monster_lore * lore,bitflag known_flags[RF_SIZE])1454 void lore_append_spells(textblock *tb, const struct monster_race *race,
1455 const struct monster_lore *lore,
1456 bitflag known_flags[RF_SIZE])
1457 {
1458 monster_sex_t msex = MON_SEX_NEUTER;
1459 bool innate = false;
1460 bool breath = false;
1461 const char *initial_pronoun;
1462 bool know_hp;
1463 bitflag current_flags[RSF_SIZE], test_flags[RSF_SIZE];
1464
1465 assert(tb && race && lore);
1466
1467 know_hp = lore->armour_known;
1468
1469 /* Extract a gender (if applicable) and get a pronoun for the start of
1470 * sentences */
1471 msex = lore_monster_sex(race);
1472 initial_pronoun = lore_pronoun_nominative(msex, true);
1473
1474 /* Collect innate (non-breath) attacks */
1475 create_mon_spell_mask(current_flags, RST_INNATE, RST_NONE);
1476 rsf_inter(current_flags, lore->spell_flags);
1477 create_mon_spell_mask(test_flags, RST_BREATH, RST_NONE);
1478 rsf_diff(current_flags, test_flags);
1479 if (!rsf_is_empty(current_flags)) {
1480 textblock_append(tb, "%s may ", initial_pronoun);
1481 lore_append_spell_clause(tb, current_flags, know_hp, race, "or", "");
1482 innate = true;
1483 }
1484
1485 /* Collect breaths */
1486 create_mon_spell_mask(current_flags, RST_BREATH, RST_NONE);
1487 rsf_inter(current_flags, lore->spell_flags);
1488 if (!rsf_is_empty(current_flags)) {
1489 if (innate) {
1490 textblock_append(tb, ", and may ");
1491 } else {
1492 textblock_append(tb, "%s may ", initial_pronoun);
1493 }
1494 textblock_append_c(tb, COLOUR_L_RED, "breathe ");
1495 lore_append_spell_clause(tb, current_flags, know_hp, race, "or", "");
1496 breath = true;
1497 }
1498
1499 /* End the sentence about innate spells and breaths */
1500 if ((innate || breath) && race->freq_innate) {
1501 if (lore->innate_freq_known) {
1502 /* Describe the spell frequency */
1503 textblock_append(tb, "; ");
1504 textblock_append_c(tb, COLOUR_L_GREEN, "1");
1505 textblock_append(tb, " time in ");
1506 textblock_append_c(tb, COLOUR_L_GREEN, "%d",
1507 100 / race->freq_innate);
1508 } else if (lore->cast_innate) {
1509 /* Guess at the frequency */
1510 int approx_frequency = MAX(((race->freq_innate + 9) / 10) * 10, 1);
1511 textblock_append(tb, "; about ");
1512 textblock_append_c(tb, COLOUR_L_GREEN, "1");
1513 textblock_append(tb, " time in ");
1514 textblock_append_c(tb, COLOUR_L_GREEN, "%d",
1515 100 / approx_frequency);
1516 }
1517
1518 textblock_append(tb, ". ");
1519 }
1520
1521 /* Collect spell information */
1522 rsf_copy(current_flags, lore->spell_flags);
1523 create_mon_spell_mask(test_flags, RST_BREATH, RST_INNATE, RST_NONE);
1524 rsf_diff(current_flags, test_flags);
1525 if (!rsf_is_empty(current_flags)) {
1526 /* Intro */
1527 textblock_append(tb, "%s may ", initial_pronoun);
1528
1529 /* Verb Phrase */
1530 textblock_append_c(tb, COLOUR_L_RED, "cast spells");
1531
1532 /* Adverb */
1533 if (rf_has(known_flags, RF_SMART))
1534 textblock_append(tb, " intelligently");
1535
1536 /* List */
1537 textblock_append(tb, " which ");
1538 lore_append_spell_clause(tb, current_flags, know_hp, race, "or", "");
1539
1540 /* End the sentence about innate/other spells */
1541 if (race->freq_spell) {
1542 if (lore->spell_freq_known) {
1543 /* Describe the spell frequency */
1544 textblock_append(tb, "; ");
1545 textblock_append_c(tb, COLOUR_L_GREEN, "1");
1546 textblock_append(tb, " time in ");
1547 textblock_append_c(tb, COLOUR_L_GREEN, "%d",
1548 100 / race->freq_spell);
1549 } else if (lore->cast_spell) {
1550 /* Guess at the frequency */
1551 int approx_frequency = MAX(((race->freq_spell + 9) / 10) * 10,
1552 1);
1553 textblock_append(tb, "; about ");
1554 textblock_append_c(tb, COLOUR_L_GREEN, "1");
1555 textblock_append(tb, " time in ");
1556 textblock_append_c(tb, COLOUR_L_GREEN, "%d",
1557 100 / approx_frequency);
1558 }
1559 }
1560
1561 textblock_append(tb, ". ");
1562 }
1563 }
1564
1565 /**
1566 * Append the monster's melee attacks to a textblock.
1567 *
1568 * Known race flags are passed in for simplicity/efficiency.
1569 *
1570 * \param tb is the textblock we are adding to.
1571 * \param race is the monster race we are describing.
1572 * \param lore is the known information about the monster race.
1573 * \param known_flags is the preprocessed bitfield of race flags known to the
1574 * player.
1575 * \param melee_colors is a list of colors that is associated with each
1576 * blow effect.
1577 */
lore_append_attack(textblock * tb,const struct monster_race * race,const struct monster_lore * lore,bitflag known_flags[RF_SIZE])1578 void lore_append_attack(textblock *tb, const struct monster_race *race,
1579 const struct monster_lore *lore,
1580 bitflag known_flags[RF_SIZE])
1581 {
1582 int i, known_attacks, total_attacks, described_count, total_centidamage;
1583 monster_sex_t msex = MON_SEX_NEUTER;
1584
1585 assert(tb && race && lore);
1586
1587 /* Extract a gender (if applicable) */
1588 msex = lore_monster_sex(race);
1589
1590 /* Notice lack of attacks */
1591 if (rf_has(known_flags, RF_NEVER_BLOW)) {
1592 textblock_append(tb, "%s has no physical attacks. ",
1593 lore_pronoun_nominative(msex, true));
1594 return;
1595 }
1596
1597 total_attacks = 0;
1598 known_attacks = 0;
1599
1600 /* Count the number of defined and known attacks */
1601 for (i = 0; i < z_info->mon_blows_max; i++) {
1602 /* Skip non-attacks */
1603 if (!race->blow[i].method) continue;
1604
1605 total_attacks++;
1606 if (lore->blow_known[i])
1607 known_attacks++;
1608 }
1609
1610 /* Describe the lack of knowledge */
1611 if (known_attacks == 0) {
1612 textblock_append_c(tb, COLOUR_ORANGE, "Nothing is known about %s attack. ",
1613 lore_pronoun_possessive(msex, false));
1614 return;
1615 }
1616
1617 described_count = 0;
1618 total_centidamage = 99; // round up the final result to the next higher point
1619
1620 /* Describe each melee attack */
1621 for (i = 0; i < z_info->mon_blows_max; i++) {
1622 random_value dice;
1623 const char *effect_str = NULL;
1624
1625 /* Skip unknown and undefined attacks */
1626 if (!race->blow[i].method || !lore->blow_known[i]) continue;
1627
1628 /* Extract the attack info */
1629 dice = race->blow[i].dice;
1630 effect_str = race->blow[i].effect->desc;
1631
1632 /* Introduce the attack description */
1633 if (described_count == 0)
1634 textblock_append(tb, "%s can ",
1635 lore_pronoun_nominative(msex, true));
1636 else if (described_count < known_attacks - 1)
1637 textblock_append(tb, ", ");
1638 else
1639 textblock_append(tb, ", and ");
1640
1641 /* Describe the method */
1642 textblock_append(tb, "%s", race->blow[i].method->desc);
1643
1644 /* Describe the effect (if any) */
1645 if (effect_str && strlen(effect_str) > 0) {
1646 int index = blow_index(race->blow[i].effect->name);
1647 /* Describe the attack type */
1648 textblock_append(tb, " to ");
1649 textblock_append_c(tb, blow_color(player, index), "%s", effect_str);
1650
1651 textblock_append(tb, " (");
1652 /* Describe damage (if known) */
1653 if (dice.base || (dice.dice && dice.sides) || dice.m_bonus) {
1654 if (dice.base)
1655 textblock_append_c(tb, COLOUR_L_GREEN, "%d", dice.base);
1656
1657 if (dice.dice && dice.sides)
1658 textblock_append_c(tb, COLOUR_L_GREEN, "%dd%d", dice.dice, dice.sides);
1659
1660 if (dice.m_bonus)
1661 textblock_append_c(tb, COLOUR_L_GREEN, "M%d", dice.m_bonus);
1662
1663 textblock_append(tb, ", ");
1664 }
1665
1666 /* Describe hit chances */
1667 long chance = 0, chance2 = 0;
1668 // These calculations are based on check_hit() and test_hit();
1669 // make sure to keep it in sync
1670 chance = (race->blow[i].effect->power + (race->level * 3));
1671 if (chance < 9) {
1672 chance = 9;
1673 }
1674 chance2 = 12 + (100 - 12 - 5) * (chance - ((player->state.ac + player->state.to_a) * 2 / 3)) / chance;
1675 if (chance2 < 12) {
1676 chance2 = 12;
1677 }
1678 textblock_append_c(tb, COLOUR_L_BLUE, "%d", chance2);
1679 textblock_append(tb, "%%)");
1680
1681 total_centidamage += (chance2 * randcalc(dice, 0, AVERAGE));
1682 }
1683
1684 described_count++;
1685 }
1686
1687 textblock_append(tb, ", averaging");
1688 if (known_attacks < total_attacks) {
1689 textblock_append_c(tb, COLOUR_ORANGE, " at least");
1690 }
1691 textblock_append_c(tb, COLOUR_L_GREEN, " %d", total_centidamage/100);
1692 textblock_append(tb, " damage on each of %s turns. ",
1693 lore_pronoun_possessive(msex, false));
1694 }
1695
1696 /**
1697 * Get the lore record for this monster race.
1698 */
get_lore(const struct monster_race * race)1699 struct monster_lore *get_lore(const struct monster_race *race)
1700 {
1701 assert(race);
1702 return &l_list[race->ridx];
1703 }
1704
1705
1706 /**
1707 * Write the monster lore
1708 */
write_lore_entries(ang_file * fff)1709 void write_lore_entries(ang_file *fff)
1710 {
1711 int i, n;
1712
1713 for (i = 0; i < z_info->r_max; i++) {
1714 /* Current entry */
1715 struct monster_race *race = &r_info[i];
1716 struct monster_lore *lore = &l_list[i];
1717
1718 /* Ignore non-existent or unseen monsters */
1719 if (!race->name) continue;
1720 if (!lore->sights && !lore->all_known) continue;
1721
1722 /* Output 'name' */
1723 file_putf(fff, "name:%s\n", race->name);
1724
1725 /* Output base if we're remembering everything */
1726 if (lore->all_known)
1727 file_putf(fff, "base:%s\n", race->base->name);
1728
1729 /* Output counts */
1730 file_putf(fff, "counts:%d:%d:%d:%d:%d:%d:%d\n", lore->sights,
1731 lore->deaths, lore->tkills, lore->wake, lore->ignore,
1732 lore->cast_innate, lore->cast_spell);
1733
1734 /* Output blow (up to max blows) */
1735 for (n = 0; n < z_info->mon_blows_max; n++) {
1736 /* End of blows */
1737 if (!lore->blow_known[n] && !lore->all_known) continue;
1738 if (!lore->blows[n].method) continue;
1739
1740 /* Output blow method */
1741 file_putf(fff, "blow:%s", lore->blows[n].method->name);
1742
1743 /* Output blow effect (may be none) */
1744 file_putf(fff, ":%s", lore->blows[n].effect->name);
1745
1746 /* Output blow damage (may be 0) */
1747 file_putf(fff, ":%d+%dd%dM%d", lore->blows[n].dice.base,
1748 lore->blows[n].dice.dice,
1749 lore->blows[n].dice.sides,
1750 lore->blows[n].dice.m_bonus);
1751
1752 /* Output number of times that blow has been seen */
1753 file_putf(fff, ":%d", lore->blows[n].times_seen);
1754
1755 /* Output blow index */
1756 file_putf(fff, ":%d", n);
1757
1758 /* End line */
1759 file_putf(fff, "\n");
1760 }
1761
1762 /* Output flags */
1763 write_flags(fff, "flags:", lore->flags, RF_SIZE, r_info_flags);
1764
1765 /* Output spell flags (multiple lines) */
1766 rsf_inter(lore->spell_flags, race->spell_flags);
1767 write_flags(fff, "spells:", lore->spell_flags, RSF_SIZE,
1768 r_info_spell_flags);
1769
1770 /* Output 'drop' */
1771 if (lore->drops) {
1772 struct monster_drop *drop = lore->drops;
1773 struct object_kind *kind = drop->kind;
1774 char name[120] = "";
1775
1776 while (drop) {
1777 if (kind) {
1778 object_short_name(name, sizeof name, kind->name);
1779 file_putf(fff, "drop:%s:%s:%d:%d:%d\n",
1780 tval_find_name(kind->tval), name,
1781 drop->percent_chance, drop->min, drop->max);
1782 drop = drop->next;
1783 } else {
1784 file_putf(fff, "drop-base:%s:%d:%d:%d\n",
1785 tval_find_name(drop->tval), drop->percent_chance,
1786 drop->min, drop->max);
1787 drop = drop->next;
1788 }
1789 }
1790 }
1791
1792 /* Output 'friends' */
1793 if (lore->friends) {
1794 struct monster_friends *f = lore->friends;
1795
1796 while (f) {
1797 if (f->role == MON_GROUP_MEMBER) {
1798 file_putf(fff, "friends:%d:%dd%d:%s\n", f->percent_chance,
1799 f->number_dice, f->number_side, f->race->name);
1800 } else {
1801 char *role_name = NULL;
1802 if (f->role == MON_GROUP_SERVANT) {
1803 role_name = string_make("servant");
1804 } else if (f->role == MON_GROUP_BODYGUARD) {
1805 role_name = string_make("bodyguard");
1806 }
1807 file_putf(fff, "friends:%d:%dd%d:%s:%s\n",
1808 f->percent_chance, f->number_dice,
1809 f->number_side, f->race->name, role_name);
1810 string_free(role_name);
1811 }
1812 f = f->next;
1813 }
1814 }
1815
1816 /* Output 'friends-base' */
1817 if (lore->friends_base) {
1818 struct monster_friends_base *b = lore->friends_base;
1819
1820 while (b) {
1821 if (b->role == MON_GROUP_MEMBER) {
1822 file_putf(fff, "friends-base:%d:%dd%d:%s\n",
1823 b->percent_chance, b->number_dice,
1824 b->number_side, b->base->name);
1825 } else {
1826 char *role_name = NULL;
1827 if (b->role == MON_GROUP_SERVANT) {
1828 role_name = string_make("servant");
1829 } else if (b->role == MON_GROUP_BODYGUARD) {
1830 role_name = string_make("bodyguard");
1831 }
1832 file_putf(fff, "friends-base:%d:%dd%d:%s:%s\n",
1833 b->percent_chance, b->number_dice,
1834 b->number_side, b->base->name, role_name);
1835 string_free(role_name);
1836 }
1837 b = b->next;
1838 }
1839 }
1840
1841 /* Output 'mimic' */
1842 if (lore->mimic_kinds) {
1843 struct monster_mimic *m = lore->mimic_kinds;
1844 struct object_kind *kind = m->kind;
1845 char name[120] = "";
1846
1847 while (m) {
1848 object_short_name(name, sizeof name, kind->name);
1849 file_putf(fff, "mimic:%s:%s\n",
1850 tval_find_name(kind->tval), name);
1851 m = m->next;
1852 }
1853 }
1854
1855 file_putf(fff, "\n");
1856 }
1857 }
1858
1859
1860 /**
1861 * Save the lore to a file in the user directory.
1862 *
1863 * \param name is the filename
1864 *
1865 * \returns true on success, false otherwise.
1866 */
lore_save(const char * name)1867 bool lore_save(const char *name)
1868 {
1869 char path[1024];
1870
1871 /* Write to the user directory */
1872 path_build(path, sizeof(path), ANGBAND_DIR_USER, name);
1873
1874 if (text_lines_to_file(path, write_lore_entries)) {
1875 msg("Failed to create file %s.new", path);
1876 return false;
1877 }
1878
1879 return true;
1880 }
1881