1 /**
2 * \file obj-desc.c
3 * \brief Create object name descriptions
4 *
5 * Copyright (c) 1997 - 2007 Angband contributors
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 "obj-chest.h"
21 #include "obj-desc.h"
22 #include "obj-gear.h"
23 #include "obj-ignore.h"
24 #include "obj-knowledge.h"
25 #include "obj-tval.h"
26 #include "obj-util.h"
27
28 /**
29 * Puts the object base kind's name into buf.
30 */
object_base_name(char * buf,size_t max,int tval,bool plural)31 void object_base_name(char *buf, size_t max, int tval, bool plural)
32 {
33 struct object_base *kb = &kb_info[tval];
34 size_t end = 0;
35
36 if (kb->name && kb->name[0])
37 (void) obj_desc_name_format(buf, max, end, kb->name, NULL, plural);
38 }
39
40
41 /**
42 * Puts a very stripped-down version of an object's name into buf.
43 * If easy_know is true, then the IDed names are used, otherwise
44 * flavours, scroll names, etc will be used.
45 *
46 * Just truncates if the buffer isn't big enough.
47 */
object_kind_name(char * buf,size_t max,const struct object_kind * kind,bool easy_know)48 void object_kind_name(char *buf, size_t max, const struct object_kind *kind,
49 bool easy_know)
50 {
51 /* If not aware, the plain flavour (e.g. Copper) will do. */
52 if (!easy_know && !kind->aware && kind->flavor)
53 my_strcpy(buf, kind->flavor->text, max);
54
55 /* Use proper name (Healing, or whatever) */
56 else
57 (void) obj_desc_name_format(buf, max, 0, kind->name, NULL, false);
58 }
59
60
61 /**
62 * A modifier string, put where '#' goes in the basename below. The weird
63 * games played with book names are to allow the non-essential part of the
64 * name to be abbreviated when there is not much room to display.
65 */
obj_desc_get_modstr(const struct object_kind * kind)66 static const char *obj_desc_get_modstr(const struct object_kind *kind)
67 {
68 if (tval_can_have_flavor_k(kind))
69 return kind->flavor ? kind->flavor->text : "";
70
71 if (tval_is_book_k(kind))
72 return kind->name;
73
74 return "";
75 }
76
77 /**
78 * An object's basic name - a generic name for flavored objects (with the
79 * actual name added later depending on awareness, the name from object.txt
80 * for almost everything else, and a bit extra for books.
81 */
obj_desc_get_basename(const struct object * obj,bool aware,bool terse,int mode)82 static const char *obj_desc_get_basename(const struct object *obj, bool aware,
83 bool terse, int mode)
84 {
85 bool show_flavor = !terse && obj->kind->flavor;
86
87 if (mode & ODESC_STORE)
88 show_flavor = false;
89 if (aware && !OPT(player, show_flavors)) show_flavor = false;
90
91 /* Artifacts are special */
92 if (obj->artifact && (aware || object_is_known_artifact(obj) || terse ||
93 !obj->kind->flavor))
94 return obj->kind->name;
95
96 /* Analyze the object */
97 switch (obj->tval)
98 {
99 case TV_FLASK:
100 case TV_CHEST:
101 case TV_SHOT:
102 case TV_BOLT:
103 case TV_ARROW:
104 case TV_BOW:
105 case TV_HAFTED:
106 case TV_POLEARM:
107 case TV_SWORD:
108 case TV_DIGGING:
109 case TV_BOOTS:
110 case TV_GLOVES:
111 case TV_CLOAK:
112 case TV_CROWN:
113 case TV_HELM:
114 case TV_SHIELD:
115 case TV_SOFT_ARMOR:
116 case TV_HARD_ARMOR:
117 case TV_DRAG_ARMOR:
118 case TV_LIGHT:
119 case TV_FOOD:
120 return obj->kind->name;
121
122 case TV_AMULET:
123 return (show_flavor ? "& # Amulet~" : "& Amulet~");
124
125 case TV_RING:
126 return (show_flavor ? "& # Ring~" : "& Ring~");
127
128 case TV_STAFF:
129 return (show_flavor ? "& # Sta|ff|ves|" : "& Sta|ff|ves|");
130
131 case TV_WAND:
132 return (show_flavor ? "& # Wand~" : "& Wand~");
133
134 case TV_ROD:
135 return (show_flavor ? "& # Rod~" : "& Rod~");
136
137 case TV_POTION:
138 return (show_flavor ? "& # Potion~" : "& Potion~");
139
140 case TV_SCROLL:
141 return (show_flavor ? "& Scroll~ titled #" : "& Scroll~");
142
143 case TV_MAGIC_BOOK:
144 if (terse)
145 return "& Book~ #";
146 else
147 return "& Book~ of Magic Spells #";
148
149 case TV_PRAYER_BOOK:
150 if (terse)
151 return "& Book~ #";
152 else
153 return "& Holy Book~ of Prayers #";
154
155 case TV_NATURE_BOOK:
156 if (terse)
157 return "& Book~ #";
158 else
159 return "& Book~ of Nature Magics #";
160
161 case TV_SHADOW_BOOK:
162 if (terse)
163 return "& Tome~ #";
164 else
165 return "& Necromantic Tome~ #";
166
167 case TV_OTHER_BOOK:
168 if (terse)
169 return "& Book~ #";
170 else
171 return "& Book of Mysteries~ #";
172
173 case TV_MUSHROOM:
174 return (show_flavor ? "& # Mushroom~" : "& Mushroom~");
175 }
176
177 return "(nothing)";
178 }
179
180
181 /**
182 * Start to description, indicating number/uniqueness (a, the, no more, 7, etc)
183 */
obj_desc_name_prefix(char * buf,size_t max,size_t end,const struct object * obj,const char * basename,const char * modstr,bool terse)184 static size_t obj_desc_name_prefix(char *buf, size_t max, size_t end,
185 const struct object *obj, const char *basename,
186 const char *modstr, bool terse)
187 {
188 if (obj->number == 0) {
189 strnfcat(buf, max, &end, "no more ");
190 } else if (obj->number > 1) {
191 strnfcat(buf, max, &end, "%d ", obj->number);
192 } else if (object_is_known_artifact(obj)) {
193 strnfcat(buf, max, &end, "the ");
194 } else if (*basename == '&') {
195 bool an = false;
196 const char *lookahead = basename + 1;
197
198 while (*lookahead == ' ') lookahead++;
199
200 if (*lookahead == '#') {
201 if (modstr && is_a_vowel(*modstr))
202 an = true;
203 } else if (is_a_vowel(*lookahead)) {
204 an = true;
205 }
206
207 if (!terse) {
208 if (an)
209 strnfcat(buf, max, &end, "an ");
210 else
211 strnfcat(buf, max, &end, "a ");
212 }
213 }
214
215 return end;
216 }
217
218
219
220 /**
221 * Formats 'fmt' into 'buf', with the following formatting characters:
222 *
223 * '~' at the end of a word (e.g. "fridge~") will pluralise
224 *
225 * '|x|y|' will be output as 'x' if singular or 'y' if plural
226 * (e.g. "kni|fe|ves|")
227 *
228 * '#' will be replaced with 'modstr' (which may contain the pluralising
229 * formats given above).
230 */
obj_desc_name_format(char * buf,size_t max,size_t end,const char * fmt,const char * modstr,bool pluralise)231 size_t obj_desc_name_format(char *buf, size_t max, size_t end,
232 const char *fmt, const char *modstr, bool pluralise)
233 {
234 /* Copy the string */
235 while (*fmt) {
236 /* Skip */
237 if (*fmt == '&') {
238 while (*fmt == ' ' || *fmt == '&')
239 fmt++;
240 continue;
241 } else if (*fmt == '~') {
242 /* Pluralizer (regular English plurals) */
243 char prev = *(fmt - 1);
244
245 if (!pluralise) {
246 fmt++;
247 continue;
248 }
249
250 /* e.g. cutlass-e-s, torch-e-s, box-e-s */
251 if (prev == 's' || prev == 'h' || prev == 'x')
252 strnfcat(buf, max, &end, "es");
253 else
254 strnfcat(buf, max, &end, "s");
255 } else if (*fmt == '|') {
256 /* Special plurals
257 * e.g. kni|fe|ves|
258 * ^ ^ ^ */
259 const char *singular = fmt + 1;
260 const char *plural = strchr(singular, '|');
261 const char *endmark = NULL;
262
263 if (plural) {
264 plural++;
265 endmark = strchr(plural, '|');
266 }
267
268 if (!singular || !plural || !endmark) return end;
269
270 if (!pluralise)
271 strnfcat(buf, max, &end, "%.*s", plural - singular - 1,
272 singular);
273 else
274 strnfcat(buf, max, &end, "%.*s", endmark - plural, plural);
275
276 fmt = endmark;
277 } else if (*fmt == '#') {
278 /* Add modstr, with pluralisation if relevant */
279 end = obj_desc_name_format(buf, max, end, modstr, NULL, pluralise);
280 }
281
282 else
283 buf[end++] = *fmt;
284
285 fmt++;
286 }
287
288 buf[end] = 0;
289
290 return end;
291 }
292
293
294 /**
295 * Format object obj's name into 'buf'.
296 */
obj_desc_name(char * buf,size_t max,size_t end,const struct object * obj,bool prefix,int mode,bool terse)297 static size_t obj_desc_name(char *buf, size_t max, size_t end,
298 const struct object *obj, bool prefix, int mode, bool terse)
299 {
300 bool store = mode & ODESC_STORE ? true : false;
301 bool spoil = mode & ODESC_SPOIL ? true : false;
302
303 /* Actual name for flavoured objects if aware, or in store, or spoiled */
304 bool aware = object_flavor_is_aware(obj) || store || spoil;
305 /* Pluralize if (not forced singular) and
306 * (not a known/visible artifact) and
307 * (not one in stack or forced plural) */
308 bool plural = !(mode & ODESC_SINGULAR) &&
309 !obj->artifact &&
310 (obj->number != 1 || (mode & ODESC_PLURAL));
311 const char *basename = obj_desc_get_basename(obj, aware, terse, mode);
312 const char *modstr = obj_desc_get_modstr(obj->kind);
313
314 /* Quantity prefix */
315 if (prefix)
316 end = obj_desc_name_prefix(buf, max, end, obj, basename, modstr, terse);
317
318 /* Base name */
319 end = obj_desc_name_format(buf, max, end, basename, modstr, plural);
320
321 /* Append extra names of various kinds */
322 if (object_is_known_artifact(obj))
323 strnfcat(buf, max, &end, " %s", obj->artifact->name);
324 else if ((obj->known->ego && !(mode & ODESC_NOEGO)) || (obj->ego && store))
325 strnfcat(buf, max, &end, " %s", obj->ego->name);
326 else if (aware && !obj->artifact &&
327 (obj->kind->flavor || obj->kind->tval == TV_SCROLL)) {
328 if (terse)
329 strnfcat(buf, max, &end, " '%s'", obj->kind->name);
330 else
331 strnfcat(buf, max, &end, " of %s", obj->kind->name);
332 }
333
334 return end;
335 }
336
337 /**
338 * Is obj armor?
339 */
obj_desc_show_armor(const struct object * obj)340 static bool obj_desc_show_armor(const struct object *obj)
341 {
342 if (player->obj_k->ac && (obj->ac || tval_is_armor(obj))) return true;
343
344 return false;
345 }
346
347 /**
348 * Special descriptions for types of chest traps
349 */
obj_desc_chest(const struct object * obj,char * buf,size_t max,size_t end)350 static size_t obj_desc_chest(const struct object *obj, char *buf, size_t max,
351 size_t end)
352 {
353 if (!tval_is_chest(obj)) return end;
354
355 /* The chest is unopened, but we know nothing about its trap/lock */
356 if (obj->pval && !obj->known->pval) return end;
357
358 /* Describe the traps */
359 strnfcat(buf, max, &end, format(" (%s)", chest_trap_name(obj)));
360
361 return end;
362 }
363
364 /**
365 * Describe combat properties of an item - damage dice, to-hit, to-dam, armor
366 * class, missile multipler
367 */
obj_desc_combat(const struct object * obj,char * buf,size_t max,size_t end,int mode)368 static size_t obj_desc_combat(const struct object *obj, char *buf, size_t max,
369 size_t end, int mode)
370 {
371 bool spoil = mode & ODESC_SPOIL ? true : false;
372
373 /* Display damage dice if they are known */
374 if (kf_has(obj->kind->kind_flags, KF_SHOW_DICE) &&
375 (player->obj_k->dd && player->obj_k->ds))
376 strnfcat(buf, max, &end, " (%dd%d)", obj->dd, obj->ds);
377
378 /* Display shooting power as part of the multiplier */
379 if (kf_has(obj->kind->kind_flags, KF_SHOW_MULT))
380 strnfcat(buf, max, &end, " (x%d)",
381 obj->pval + obj->modifiers[OBJ_MOD_MIGHT]);
382
383 /* No more if the object hasn't been assessed */
384 if (!((obj->notice & OBJ_NOTICE_ASSESSED) || spoil)) return end;
385
386 /* Show weapon bonuses if we know of any */
387 if (player->obj_k->to_h && player->obj_k->to_d &&
388 (tval_is_weapon(obj) || obj->to_d ||
389 (obj->to_h && !tval_is_body_armor(obj)) ||
390 (!object_has_standard_to_h(obj) && !obj->artifact && !obj->ego))) {
391 /* In general show full combat bonuses */
392 strnfcat(buf, max, &end, " (%+d,%+d)", obj->to_h, obj->to_d);
393 } else if (obj->to_h < 0 && object_has_standard_to_h(obj)) {
394 /* Special treatment for body armor with only a to-hit penalty */
395 strnfcat(buf, max, &end, " (%+d)", obj->to_h);
396 } else if (obj->to_d != 0 && player->obj_k->to_d) {
397 /* To-dam rune known only */
398 strnfcat(buf, max, &end, " (%+d)", obj->to_d);
399 } else if (obj->to_h != 0 && player->obj_k->to_h) {
400 /* To-hit rune known only */
401 strnfcat(buf, max, &end, " (%+d)", obj->to_h);
402 }
403
404 /* Show armor bonuses */
405 if (player->obj_k->to_a) {
406 if (obj_desc_show_armor(obj))
407 strnfcat(buf, max, &end, " [%d,%+d]", obj->ac, obj->to_a);
408 else if (obj->to_a)
409 strnfcat(buf, max, &end, " [%+d]", obj->to_a);
410 } else if (obj_desc_show_armor(obj)) {
411 strnfcat(buf, max, &end, " [%d]", obj->ac);
412 }
413
414 return end;
415 }
416
417 /**
418 * Describe remaining light for refuellable lights
419 */
obj_desc_light(const struct object * obj,char * buf,size_t max,size_t end)420 static size_t obj_desc_light(const struct object *obj, char *buf, size_t max,
421 size_t end)
422 {
423 /* Fuelled light sources get number of remaining turns appended */
424 if (tval_is_light(obj) && !of_has(obj->flags, OF_NO_FUEL))
425 strnfcat(buf, max, &end, " (%d turns)", obj->timeout);
426
427 return end;
428 }
429
430 /**
431 * Describe numerical modifiers to stats and other player qualities which
432 * allow numerical bonuses - speed, stealth, etc
433 */
obj_desc_mods(const struct object * obj,char * buf,size_t max,size_t end)434 static size_t obj_desc_mods(const struct object *obj, char *buf, size_t max,
435 size_t end)
436 {
437 int i, j, num_mods = 0;
438 int mods[OBJ_MOD_MAX] = { 0 };
439
440 /* Run through possible modifiers and store distinct ones */
441 for (i = 0; i < OBJ_MOD_MAX; i++) {
442 /* Check for known non-zero mods */
443 if (obj->modifiers[i] != 0) {
444 /* If no mods stored yet, store and move on */
445 if (!num_mods) {
446 mods[num_mods++] = obj->modifiers[i];
447 continue;
448 }
449
450 /* Run through the existing mods, quit on duplicates */
451 for (j = 0; j < num_mods; j++)
452 if (mods[j] == obj->modifiers[i]) break;
453
454 /* Add another mod if needed */
455 if (j == num_mods)
456 mods[num_mods++] = obj->modifiers[i];
457 }
458 }
459
460 if (!num_mods) return end;
461
462 /* Print the modifiers */
463 strnfcat(buf, max, &end, " <");
464 for (j = 0; j < num_mods; j++) {
465 if (j) strnfcat(buf, max, &end, ", ");
466 strnfcat(buf, max, &end, "%+d", mods[j]);
467 }
468 strnfcat(buf, max, &end, ">");
469
470 return end;
471 }
472
473 /**
474 * Describe charges or charging status for re-usable items with magic effects
475 */
obj_desc_charges(const struct object * obj,char * buf,size_t max,size_t end,int mode)476 static size_t obj_desc_charges(const struct object *obj, char *buf, size_t max,
477 size_t end, int mode)
478 {
479 bool aware = object_flavor_is_aware(obj) || (mode & ODESC_STORE);
480
481 /* Wands and staffs have charges, others may be charging */
482 if (aware && tval_can_have_charges(obj)) {
483 strnfcat(buf, max, &end, " (%d charge%s)", obj->pval,
484 PLURAL(obj->pval));
485 } else if (obj->timeout > 0) {
486 if (tval_is_rod(obj) && obj->number > 1)
487 strnfcat(buf, max, &end, " (%d charging)", number_charging(obj));
488 else if (tval_is_rod(obj) || obj->activation || obj->effect)
489 /* Artifacts, single rods */
490 strnfcat(buf, max, &end, " (charging)");
491 }
492
493 return end;
494 }
495
496 /**
497 * Add player-defined inscriptions or game-defined descriptions
498 */
obj_desc_inscrip(const struct object * obj,char * buf,size_t max,size_t end)499 static size_t obj_desc_inscrip(const struct object *obj, char *buf,
500 size_t max, size_t end)
501 {
502 const char *u[6] = { 0, 0, 0, 0, 0, 0 };
503 int n = 0;
504
505 /* Get inscription */
506 if (obj->note)
507 u[n++] = quark_str(obj->note);
508
509 /* Use special inscription, if any */
510 if (!object_flavor_is_aware(obj)) {
511 if (tval_can_have_charges(obj) && (obj->pval == 0))
512 u[n++] = "empty";
513 if (object_flavor_was_tried(obj))
514 u[n++] = "tried";
515 }
516
517 /* Note curses */
518 if (obj->known->curses)
519 u[n++] = "cursed";
520
521 /* Note ignore */
522 if (ignore_item_ok(obj))
523 u[n++] = "ignore";
524
525 /* Note unknown properties */
526 if (!object_runes_known(obj) && (obj->known->notice & OBJ_NOTICE_ASSESSED))
527 u[n++] = "??";
528
529 if (n) {
530 int i;
531 for (i = 0; i < n; i++) {
532 if (i == 0)
533 strnfcat(buf, max, &end, " {");
534 strnfcat(buf, max, &end, "%s", u[i]);
535 if (i < n - 1)
536 strnfcat(buf, max, &end, ", ");
537 }
538
539 strnfcat(buf, max, &end, "}");
540 }
541
542 return end;
543 }
544
545
546 /**
547 * Add "unseen" to the end of unaware items in stores,
548 * and "??" to not fully known unflavored items
549 */
obj_desc_aware(const struct object * obj,char * buf,size_t max,size_t end)550 static size_t obj_desc_aware(const struct object *obj, char *buf, size_t max,
551 size_t end)
552 {
553 if (!object_flavor_is_aware(obj)) {
554 strnfcat(buf, max, &end, " {unseen}");
555 } else if (!object_runes_known(obj)) {
556 strnfcat(buf, max, &end, " {??}");
557 } else if (obj->known->curses) {
558 strnfcat(buf, max, &end, " {cursed}");
559 }
560
561 return end;
562 }
563
564
565 /**
566 * Describes item `obj` into buffer `buf` of size `max`.
567 *
568 * ODESC_PREFIX prepends a 'the', 'a' or number
569 * ODESC_BASE results in a base description.
570 * ODESC_COMBAT will add to-hit, to-dam and AC info.
571 * ODESC_EXTRA will add pval/charge/inscription/ignore info.
572 * ODESC_PLURAL will pluralise regardless of the number in the stack.
573 * ODESC_STORE turns off ignore markers, for in-store display.
574 * ODESC_SPOIL treats the object as fully identified.
575 *
576 * Setting 'prefix' to true prepends a 'the', 'a' or the number in the stack,
577 * respectively.
578 *
579 * \returns The number of bytes used of the buffer.
580 */
object_desc(char * buf,size_t max,const struct object * obj,int mode)581 size_t object_desc(char *buf, size_t max, const struct object *obj, int mode)
582 {
583 bool prefix = mode & ODESC_PREFIX ? true : false;
584 bool spoil = mode & ODESC_SPOIL ? true : false;
585 bool terse = mode & ODESC_TERSE ? true : false;
586
587 size_t end = 0;
588
589 /* Simple description for null item */
590 if (!obj || !obj->known)
591 return strnfmt(buf, max, "(nothing)");
592
593 /* Unknown itema and cash get straightforward descriptions */
594 if (obj->known && obj->kind != obj->known->kind) {
595 if (prefix)
596 return strnfmt(buf, max, "an unknown item");
597 return strnfmt(buf, max, "unknown item");
598 }
599
600 if (tval_is_money(obj))
601 return strnfmt(buf, max, "%d gold pieces worth of %s%s",
602 obj->pval, obj->kind->name,
603 ignore_item_ok(obj) ? " {ignore}" : "");
604
605 /* Egos and kinds whose name we know are seen */
606 if (obj->known->ego && !spoil)
607 obj->ego->everseen = true;
608
609 if (object_flavor_is_aware(obj) && !spoil)
610 obj->kind->everseen = true;
611
612 /** Construct the name **/
613
614 /* Copy the base name to the buffer */
615 end = obj_desc_name(buf, max, end, obj, prefix, mode, terse);
616
617 /* Combat properties */
618 if (mode & ODESC_COMBAT) {
619 if (tval_is_chest(obj))
620 end = obj_desc_chest(obj, buf, max, end);
621 else if (tval_is_light(obj))
622 end = obj_desc_light(obj, buf, max, end);
623
624 end = obj_desc_combat(obj->known, buf, max, end, mode);
625 }
626
627 /* Modifiers, charges, flavour details, inscriptions */
628 if (mode & ODESC_EXTRA) {
629 end = obj_desc_mods(obj->known, buf, max, end);
630
631 end = obj_desc_charges(obj, buf, max, end, mode);
632
633 if (mode & ODESC_STORE)
634 end = obj_desc_aware(obj, buf, max, end);
635 else
636 end = obj_desc_inscrip(obj, buf, max, end);
637 }
638
639 return end;
640 }
641