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