1 /* $OpenBSD: hack.objnam.c,v 1.12 2023/09/06 11:53:56 jsg Exp $ */
2
3 /*
4 * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
5 * Amsterdam
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * - Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * - Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * - Neither the name of the Stichting Centrum voor Wiskunde en
20 * Informatica, nor the names of its contributors may be used to endorse or
21 * promote products derived from this software without specific prior
22 * written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
28 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37 /*
38 * Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
39 * All rights reserved.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 * notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 * notice, this list of conditions and the following disclaimer in the
48 * documentation and/or other materials provided with the distribution.
49 * 3. The name of the author may not be used to endorse or promote products
50 * derived from this software without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
53 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
54 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
55 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
56 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
57 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
58 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
59 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
60 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
61 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62 */
63
64 #include <ctype.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67
68 #include "hack.h"
69
70 #define PREFIX 15
71 extern int bases[];
72
73 static char bufr[BUFSZ];
74
75 static char *sitoa(int);
76
77 char *
strprepend(char * s,char * pref)78 strprepend(char *s, char *pref)
79 {
80 int i = strlen(pref);
81
82 if(i > PREFIX) {
83 pline("WARNING: prefix too short.");
84 return(s);
85 }
86 s -= i;
87 (void) strncpy(s, pref, i); /* do not copy trailing 0 */
88 return(s);
89 }
90
91 static char *
sitoa(int a)92 sitoa(int a)
93 {
94 static char buf[13];
95
96 snprintf(buf, sizeof buf, (a < 0) ? "%d" : "+%d", a);
97 return(buf);
98 }
99
100 char *
typename(int otyp)101 typename(int otyp)
102 {
103 static char buf[BUFSZ];
104 struct objclass *ocl = &objects[otyp];
105 char *an = ocl->oc_name;
106 char *dn = ocl->oc_descr;
107 char *un = ocl->oc_uname;
108 char *bp;
109 int nn = ocl->oc_name_known;
110
111 switch(ocl->oc_olet) {
112 case POTION_SYM:
113 strlcpy(buf, "potion", sizeof buf);
114 break;
115 case SCROLL_SYM:
116 strlcpy(buf, "scroll", sizeof buf);
117 break;
118 case WAND_SYM:
119 strlcpy(buf, "wand", sizeof buf);
120 break;
121 case RING_SYM:
122 strlcpy(buf, "ring", sizeof buf);
123 break;
124 default:
125 if(nn) {
126 strlcpy(buf, an, sizeof buf);
127 if(otyp >= TURQUOISE && otyp <= JADE)
128 strlcat(buf, " stone", sizeof buf);
129 if(un) {
130 bp = eos(buf);
131 snprintf(bp, buf + sizeof buf - bp,
132 " called %s", un);
133 }
134 if(dn) {
135 bp = eos(buf);
136 snprintf(bp, buf + sizeof buf - bp,
137 " (%s)", dn);
138 }
139 } else {
140 strlcpy(buf, dn ? dn : an, sizeof buf);
141 if(ocl->oc_olet == GEM_SYM)
142 strlcat(buf, " gem", sizeof buf);
143 if(un) {
144 bp = eos(buf);
145 snprintf(bp, buf + sizeof buf - bp,
146 " called %s", un);
147 }
148 }
149 return(buf);
150 }
151 /* here for ring/scroll/potion/wand */
152 if(nn) {
153 bp = eos(buf);
154 snprintf(bp, buf + sizeof buf - bp, " of %s", an);
155 }
156 if(un) {
157 bp = eos(buf);
158 snprintf(bp, buf + sizeof buf - bp, " called %s", un);
159 }
160 if(dn) {
161 bp = eos(buf);
162 snprintf(bp, buf + sizeof buf - bp, " (%s)", dn);
163 }
164 return(buf);
165 }
166
167 char *
xname(struct obj * obj)168 xname(struct obj *obj)
169 {
170 char *buf = &(bufr[PREFIX]); /* leave room for "17 -3 " */
171 int nn = objects[obj->otyp].oc_name_known;
172 char *an = objects[obj->otyp].oc_name;
173 char *dn = objects[obj->otyp].oc_descr;
174 char *un = objects[obj->otyp].oc_uname;
175 int pl = (obj->quan != 1);
176 size_t len = bufr + sizeof bufr - buf;
177
178 if(!obj->dknown && !Blind) obj->dknown = 1; /* %% doesn't belong here */
179 switch(obj->olet) {
180 case AMULET_SYM:
181 strlcpy(buf, (obj->spe < 0 && obj->known)
182 ? "cheap plastic imitation of the " : "", len);
183 strlcat(buf,"Amulet of Yendor", len);
184 break;
185 case TOOL_SYM:
186 if(!nn) {
187 strlcpy(buf, dn, len);
188 break;
189 }
190 strlcpy(buf,an,len);
191 break;
192 case FOOD_SYM:
193 if(obj->otyp == DEAD_HOMUNCULUS && pl) {
194 pl = 0;
195 strlcpy(buf, "dead homunculi", len);
196 break;
197 }
198 /* fungis ? */
199 /* fall into next case */
200 case WEAPON_SYM:
201 if(obj->otyp == WORM_TOOTH && pl) {
202 pl = 0;
203 strlcpy(buf, "worm teeth", len);
204 break;
205 }
206 if(obj->otyp == CRYSKNIFE && pl) {
207 pl = 0;
208 strlcpy(buf, "crysknives", len);
209 break;
210 }
211 /* fall into next case */
212 case ARMOR_SYM:
213 case CHAIN_SYM:
214 case ROCK_SYM:
215 strlcpy(buf,an,len);
216 break;
217 case BALL_SYM:
218 snprintf(buf, len, "%sheavy iron ball",
219 (obj->owt > objects[obj->otyp].oc_weight) ? "very " : "");
220 break;
221 case POTION_SYM:
222 if(nn || un || !obj->dknown) {
223 strlcpy(buf, "potion", len);
224 if(pl) {
225 pl = 0;
226 strlcat(buf, "s", len);
227 }
228 if(!obj->dknown) break;
229 if(un) {
230 strlcat(buf, " called ", len);
231 strlcat(buf, un, len);
232 } else {
233 strlcat(buf, " of ", len);
234 strlcat(buf, an, len);
235 }
236 } else {
237 strlcpy(buf, dn, len);
238 strlcat(buf, " potion", len);
239 }
240 break;
241 case SCROLL_SYM:
242 strlcpy(buf, "scroll", len);
243 if(pl) {
244 pl = 0;
245 strlcat(buf, "s", len);
246 }
247 if(!obj->dknown) break;
248 if(nn) {
249 strlcat(buf, " of ", len);
250 strlcat(buf, an, len);
251 } else if(un) {
252 strlcat(buf, " called ", len);
253 strlcat(buf, un, len);
254 } else {
255 strlcat(buf, " labeled ", len);
256 strlcat(buf, dn, len);
257 }
258 break;
259 case WAND_SYM:
260 if(!obj->dknown)
261 snprintf(buf, len, "wand");
262 else if(nn)
263 snprintf(buf, len, "wand of %s", an);
264 else if(un)
265 snprintf(buf, len, "wand called %s", un);
266 else
267 snprintf(buf, len, "%s wand", dn);
268 break;
269 case RING_SYM:
270 if(!obj->dknown)
271 snprintf(buf, len, "ring");
272 else if(nn)
273 snprintf(buf, len, "ring of %s", an);
274 else if(un)
275 snprintf(buf, len, "ring called %s", un);
276 else
277 snprintf(buf, len, "%s ring", dn);
278 break;
279 case GEM_SYM:
280 if(!obj->dknown) {
281 strlcpy(buf, "gem", len);
282 break;
283 }
284 if(!nn) {
285 snprintf(buf, len, "%s gem", dn);
286 break;
287 }
288 strlcpy(buf, an, len);
289 if(obj->otyp >= TURQUOISE && obj->otyp <= JADE)
290 strlcat(buf, " stone", len);
291 break;
292 default:
293 snprintf(buf,len,"glorkum %c (0%o) %u %d",
294 obj->olet,obj->olet,obj->otyp,obj->spe);
295 }
296 if(pl) {
297 char *p;
298
299 for(p = buf; *p; p++) {
300 if(!strncmp(" of ", p, 4)) {
301 /* pieces of, cloves of, lumps of */
302 int c1, c2 = 's';
303
304 do {
305 c1 = c2; c2 = *p; *p++ = c1;
306 } while(c1);
307 goto nopl;
308 }
309 }
310 p = eos(buf)-1;
311 if(*p == 's' || *p == 'z' || *p == 'x' ||
312 (*p == 'h' && p[-1] == 's'))
313 strlcat(buf, "es", len); /* boxes */
314 else if(*p == 'y' && !strchr(vowels, p[-1]))
315 /* rubies, zruties */
316 strlcpy(p, "ies", bufr + sizeof bufr - p);
317 else
318 strlcat(buf, "s", len);
319 }
320 nopl:
321 if(obj->onamelth) {
322 strlcat(buf, " named ", len);
323 strlcat(buf, ONAME(obj), len);
324 }
325 return(buf);
326 }
327
328 char *
doname(struct obj * obj)329 doname(struct obj *obj)
330 {
331 char prefix[PREFIX];
332 char *bp = xname(obj);
333 char *p;
334
335 if(obj->quan != 1)
336 snprintf(prefix, sizeof prefix, "%u ", obj->quan);
337 else
338 strlcpy(prefix, "a ", sizeof prefix);
339 switch(obj->olet) {
340 case AMULET_SYM:
341 if(strncmp(bp, "cheap ", 6))
342 strlcpy(prefix, "the ", sizeof prefix);
343 break;
344 case ARMOR_SYM:
345 if(obj->owornmask & W_ARMOR)
346 strlcat(bp, " (being worn)", bufr + sizeof bufr - bp);
347 /* fall into next case */
348 case WEAPON_SYM:
349 if(obj->known) {
350 strlcat(prefix, sitoa(obj->spe), sizeof prefix);
351 strlcat(prefix, " ", sizeof prefix);
352 }
353 break;
354 case WAND_SYM:
355 if(obj->known) {
356 p = eos(bp);
357 snprintf(p, bufr + sizeof bufr - p, " (%d)", obj->spe);
358 }
359 break;
360 case RING_SYM:
361 if(obj->owornmask & W_RINGR)
362 strlcat(bp, " (on right hand)", bufr + sizeof bufr - bp);
363 if(obj->owornmask & W_RINGL)
364 strlcat(bp, " (on left hand)", bufr + sizeof bufr - bp);
365 if(obj->known && (objects[obj->otyp].bits & SPEC)) {
366 strlcat(prefix, sitoa(obj->spe), sizeof prefix);
367 strlcat(prefix, " ", sizeof prefix);
368 }
369 break;
370 }
371 if(obj->owornmask & W_WEP)
372 strlcat(bp, " (weapon in hand)", bufr + sizeof bufr - bp);
373 if(obj->unpaid)
374 strlcat(bp, " (unpaid)", bufr + sizeof bufr - bp);
375 if(!strcmp(prefix, "a ") && strchr(vowels, *bp))
376 strlcpy(prefix, "an ", sizeof prefix);
377 bp = strprepend(bp, prefix);
378 return(bp);
379 }
380
381 /* used only in hack.fight.c (thitu) */
382 void
setan(char * str,char * buf,size_t len)383 setan(char *str, char *buf, size_t len)
384 {
385 if(strchr(vowels,*str))
386 snprintf(buf, len, "an %s", str);
387 else
388 snprintf(buf, len, "a %s", str);
389 }
390
391 char *
aobjnam(struct obj * otmp,char * verb)392 aobjnam(struct obj *otmp, char *verb)
393 {
394 char *bp = xname(otmp);
395 char prefix[PREFIX];
396
397 if(otmp->quan != 1) {
398 snprintf(prefix, sizeof prefix, "%u ", otmp->quan);
399 bp = strprepend(bp, prefix);
400 }
401
402 if(verb) {
403 /* verb is given in plural (i.e., without trailing s) */
404 strlcat(bp, " ", bufr + sizeof bufr - bp);
405 if(otmp->quan != 1)
406 strlcat(bp, verb, bufr + sizeof bufr - bp);
407 else if(!strcmp(verb, "are"))
408 strlcat(bp, "is", bufr + sizeof bufr - bp);
409 else {
410 strlcat(bp, verb, bufr + sizeof bufr - bp);
411 strlcat(bp, "s", bufr + sizeof bufr - bp);
412 }
413 }
414 return(bp);
415 }
416
417 char *
Doname(struct obj * obj)418 Doname(struct obj *obj)
419 {
420 char *s = doname(obj);
421
422 if('a' <= *s && *s <= 'z') *s -= ('a' - 'A');
423 return(s);
424 }
425
426 char *wrp[] = { "wand", "ring", "potion", "scroll", "gem" };
427 char wrpsym[] = { WAND_SYM, RING_SYM, POTION_SYM, SCROLL_SYM, GEM_SYM };
428
429 struct obj *
readobjnam(char * bp,size_t len)430 readobjnam(char *bp, size_t len)
431 {
432 char *p, *cp = bp;
433 int i;
434 int cnt, spe, spesgn, typ, heavy;
435 char let;
436 char *un, *dn, *an;
437
438 /* int the = 0; char *oname = 0; */
439 cnt = spe = spesgn = typ = heavy = 0;
440 let = 0;
441 an = dn = un = 0;
442 for(p = cp; *p; p++)
443 if('A' <= *p && *p <= 'Z') *p += 'a'-'A';
444 if(!strncmp(cp, "the ", 4)){
445 /* the = 1; */
446 cp += 4;
447 } else if(!strncmp(cp, "an ", 3)){
448 cnt = 1;
449 cp += 3;
450 } else if(!strncmp(cp, "a ", 2)){
451 cnt = 1;
452 cp += 2;
453 }
454 if(!cnt && isdigit((unsigned char)*cp)){
455 cnt = atoi(cp);
456 while(isdigit((unsigned char)*cp)) cp++;
457 while(*cp == ' ') cp++;
458 }
459 if(!cnt) cnt = 1; /* %% what with "gems" etc. ? */
460
461 if(*cp == '+' || *cp == '-'){
462 spesgn = (*cp++ == '+') ? 1 : -1;
463 spe = atoi(cp);
464 while(isdigit((unsigned char)*cp)) cp++;
465 while(*cp == ' ') cp++;
466 } else {
467 p = strrchr(cp, '(');
468 if(p) {
469 if(p > cp && p[-1] == ' ') p[-1] = 0;
470 else *p = 0;
471 p++;
472 spe = atoi(p);
473 while(isdigit((unsigned char)*p)) p++;
474 if(strcmp(p, ")")) spe = 0;
475 else spesgn = 1;
476 }
477 }
478 /* now we have the actual name, as delivered by xname, say
479 green potions called whisky
480 scrolls labeled "QWERTY"
481 egg
482 dead zruties
483 fortune cookies
484 very heavy iron ball named hoei
485 wand of wishing
486 elven cloak
487 */
488 for(p = cp; *p; p++) if(!strncmp(p, " named ", 7)) {
489 *p = 0;
490 /* oname = p+7; */
491 }
492 for(p = cp; *p; p++) if(!strncmp(p, " called ", 8)) {
493 *p = 0;
494 un = p+8;
495 }
496 for(p = cp; *p; p++) if(!strncmp(p, " labeled ", 9)) {
497 *p = 0;
498 dn = p+9;
499 }
500
501 /* first change to singular if necessary */
502 if(cnt != 1) {
503 /* find "cloves of garlic", "worthless pieces of blue glass" */
504 for(p = cp; *p; p++) if(!strncmp(p, "s of ", 5)){
505 while ((*p = p[1]))
506 p++;
507 goto sing;
508 }
509 /* remove -s or -es (boxes) or -ies (rubies, zruties) */
510 p = eos(cp);
511 if(p[-1] == 's') {
512 if(p[-2] == 'e') {
513 if(p[-3] == 'i') {
514 if(!strcmp(p-7, "cookies"))
515 goto mins;
516 strlcpy(p-3, "y", bp + len - (p-3));
517 goto sing;
518 }
519
520 /* note: cloves / knives from clove / knife */
521 if(!strcmp(p-6, "knives")) {
522 strlcpy(p-3, "fe", bp + len - (p-3));
523 goto sing;
524 }
525
526 /* note: nurses, axes but boxes */
527 if(!strcmp(p-5, "boxes")) {
528 p[-2] = 0;
529 goto sing;
530 }
531 }
532 mins:
533 p[-1] = 0;
534 } else {
535 if(!strcmp(p-9, "homunculi")) {
536 strlcpy(p-1, "us", bp + len - (p-1));
537 goto sing;
538 }
539 if(!strcmp(p-5, "teeth")) {
540 strlcpy(p-5, "tooth", bp + len - (p-5));
541 goto sing;
542 }
543 /* here we cannot find the plural suffix */
544 }
545 }
546 sing:
547 if(!strcmp(cp, "amulet of yendor")) {
548 typ = AMULET_OF_YENDOR;
549 goto typfnd;
550 }
551 p = eos(cp);
552 if(!strcmp(p-5, " mail")){ /* Note: ring mail is not a ring ! */
553 let = ARMOR_SYM;
554 an = cp;
555 goto srch;
556 }
557 for(i = 0; i < sizeof(wrpsym); i++) {
558 int j = strlen(wrp[i]);
559 if(!strncmp(cp, wrp[i], j)){
560 let = wrpsym[i];
561 cp += j;
562 if(!strncmp(cp, " of ", 4)) an = cp+4;
563 /* else if(*cp) ?? */
564 goto srch;
565 }
566 if(!strcmp(p-j, wrp[i])){
567 let = wrpsym[i];
568 p -= j;
569 *p = 0;
570 if(p[-1] == ' ') p[-1] = 0;
571 dn = cp;
572 goto srch;
573 }
574 }
575 if(!strcmp(p-6, " stone")){
576 p[-6] = 0;
577 let = GEM_SYM;
578 an = cp;
579 goto srch;
580 }
581 if(!strcmp(cp, "very heavy iron ball")){
582 heavy = 1;
583 typ = HEAVY_IRON_BALL;
584 goto typfnd;
585 }
586 an = cp;
587 srch:
588 if(!an && !dn && !un)
589 goto any;
590 i = 1;
591 if(let) i = bases[letindex(let)];
592 while(i <= NROFOBJECTS && (!let || objects[i].oc_olet == let)){
593 char *zn = objects[i].oc_name;
594
595 if(!zn) goto nxti;
596 if(an && strcmp(an, zn))
597 goto nxti;
598 if(dn && (!(zn = objects[i].oc_descr) || strcmp(dn, zn)))
599 goto nxti;
600 if(un && (!(zn = objects[i].oc_uname) || strcmp(un, zn)))
601 goto nxti;
602 typ = i;
603 goto typfnd;
604 nxti:
605 i++;
606 }
607 any:
608 if(!let) let = wrpsym[rn2(sizeof(wrpsym))];
609 typ = probtype(let);
610 typfnd:
611 { struct obj *otmp;
612 let = objects[typ].oc_olet;
613 otmp = mksobj(typ);
614 if(heavy)
615 otmp->owt += 15;
616 if(cnt > 0 && strchr("%?!*)", let) &&
617 (cnt < 4 || (let == WEAPON_SYM && typ <= ROCK && cnt < 20)))
618 otmp->quan = cnt;
619
620 if(spe > 3 && spe > otmp->spe)
621 spe = 0;
622 else if(let == WAND_SYM)
623 spe = otmp->spe;
624 if(spe == 3 && u.uluck < 0)
625 spesgn = -1;
626 if(let != WAND_SYM && spesgn == -1)
627 spe = -spe;
628 if(let == BALL_SYM)
629 spe = 0;
630 else if(let == AMULET_SYM)
631 spe = -1;
632 else if(typ == WAN_WISHING && rn2(10))
633 spe = (rn2(10) ? -1 : 0);
634 otmp->spe = spe;
635
636 if(spesgn == -1)
637 otmp->cursed = 1;
638
639 return(otmp);
640 }
641 }
642