xref: /openbsd/games/hack/hack.objnam.c (revision 25fdf802)
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