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