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