1 /*
2  * All sorts of miscellaneous routines
3  *
4  * misc.c	1.4		(A.I. Design)	12/14/84
5  */
6 
7 #include "rogue.h"
8 #include "curses.h"
9 
10 /*
11  * tr_name:
12  *	Print the name of a trap
13  */
14 char *
15 tr_name(type)
16 byte type;
17 {
18     switch (type)
19     {
20 	case T_DOOR:
21 	    return "a trapdoor";
22 	case T_BEAR:
23 	    return "a beartrap";
24 	case T_SLEEP:
25 	    return "a sleeping gas trap";
26 	case T_ARROW:
27 	    return "an arrow trap";
28 	case T_TELEP:
29 	    return "a teleport trap";
30 	case T_DART:
31 	    return "a poison dart trap";
32     }
33     msg("wierd trap: %d", type);
34     return NULL;
35 }
36 
37 /*
38  * look:
39  *	A quick glance all around the player
40  */
41 look(wakeup)
42 bool wakeup;
43 {
44     register int x, y;
45     register byte ch, pch;
46     register int index;
47     register THING *tp;
48     register struct room *rp;
49     register int ey, ex;
50     register int passcount = 0;
51     register byte pfl, *fp;
52     register int sy, sx, sumhero, diffhero;
53 
54     rp = proom;
55     index = INDEX(hero.y, hero.x);
56     pfl = _flags[index];
57     pch = _level[index];
58     /*
59      * if the hero has moved
60      */
61     if (!ce(oldpos, hero)) {
62 		if (!on(player,ISBLIND)) {
63 		    for (x = oldpos.x - 1; x <= (oldpos.x + 1); x++)
64 				for (y = oldpos.y - 1; y <= (oldpos.y + 1); y++) {
65 				    if ((y == hero.y && x == hero.x) || offmap(y,x))
66 						continue;
67 				    move(y,x);
68 				    ch = inch();
69 				    if (ch == FLOOR) {
70 				        if ((oldrp->r_flags & (ISGONE|ISDARK)) == ISDARK)
71 						    addch(' ');
72 				    } else {
73 						fp = &_flags[INDEX(y,x)];
74 						/*
75 						 * if the maze or passage (that the hero is in!!)
76 						 * needs to be redrawn (passages once draw always
77 						 * stay on) do it now.
78 						 */
79 						if (((*fp&F_MAZE) || (*fp&F_PASS)) && (ch!=PASSAGE)
80 						    && (ch != STAIRS) && 
81 					    	((*fp & F_PNUM) == (pfl & F_PNUM)) )
82 							    addch(PASSAGE);
83 				    }
84 				}
85 		}
86 		oldpos = hero;
87 		oldrp = rp;
88     }
89     ey = hero.y + 1;
90     ex = hero.x + 1;
91     sx = hero.x - 1;
92     sy = hero.y - 1;
93     if (door_stop && !firstmove && running) {
94 		sumhero = hero.y + hero.x;
95 		diffhero = hero.y - hero.x;
96     }
97     for (y = sy; y <= ey; y++)
98 		if (y > 0 && y < maxrow) for (x = sx; x <= ex; x++) {
99 		    if (x <= 0 || x >= COLS)
100 				continue;
101 		    if (!on(player, ISBLIND)) {
102 				if (y == hero.y && x == hero.x)
103 				    continue;
104 		    } else if (y != hero.y || x != hero.x)
105 				continue;
106 
107 		    index = INDEX(y, x);
108 		    /*
109 		     * THIS REPLICATES THE moat() MACRO.  IF MOAT IS CHANGED,
110 		     * THIS MUST BE CHANGED ALSO ?? What does this really mean ??
111 		     */
112 		    fp = &_flags[index];
113 		    ch = _level[index];
114 		    /*
115 		     * No Doors
116 		     */
117 		    if (pch != DOOR && ch != DOOR)
118 		    	/*
119 		    	 * Either hero or other in a passage
120 		    	 */
121 				if ((pfl & F_PASS) != (*fp & F_PASS)) {
122 				    /*
123 				     * Neither is in a maze
124 				     */
125 				    if ( ! (pfl & F_MAZE) && ! (*fp & F_MAZE))
126 				        continue;
127 				}
128 				/*
129 				 * Not in same passage
130 				 */
131 				else if ((*fp & F_PASS) && (*fp & F_PNUM) != (pfl & F_PNUM))
132 				    continue;
133 
134 		    if ((tp = moat(y,x)) != NULL) 
135 				if (on(player, SEEMONST) && on(*tp, ISINVIS)) {
136 				    if (door_stop && !firstmove)
137 						running = FALSE;
138 				    continue;
139 				} else {
140 				    if (wakeup)
141 						wake_monster(y, x);
142 				    if (tp->t_oldch != ' ' ||
143 						(!(rp->r_flags & ISDARK) && !on(player, ISBLIND)))
144 						    tp->t_oldch = _level[index];
145 				    if (see_monst(tp))
146 						ch = tp->t_disguise;
147 				}
148 
149 		    if ((ch!=PASSAGE) && (*fp & (F_PASS | F_MAZE)))
150 		    	/*
151 		    	 * The current character used for IBM ARMOR doesn't
152 		    	 * look right in Inverse
153 		    	 */
154 		    	if (ch != ARMOR)
155 				    standout();
156 
157 		    move(y, x);
158 		    addch(ch);
159 		    standend();
160 
161 		    if (door_stop && !firstmove && running) {
162 				switch (runch) {
163 			    when 'h':
164 					if (x == ex)
165 					    continue;
166 			    when 'j':
167 					if (y == sy)
168 					    continue;
169 			    when 'k':
170 					if (y == ey)
171 					    continue;
172 			    when 'l':
173 					if (x == sx)
174 					    continue;
175 			    when 'y':
176 					if ((y + x) - sumhero >= 1)
177 					    continue;
178 			    when 'u':
179 					if ((y - x) - diffhero >= 1)
180 					    continue;
181 			    when 'n':
182 					if ((y + x) - sumhero <= -1)
183 					    continue;
184 			    when 'b':
185 					if ((y - x) - diffhero <= -1)
186 					    continue;
187 				}
188 				switch (ch) {
189 			    case DOOR:
190 					if (x == hero.x || y == hero.y)
191 					    running = FALSE;
192 					break;
193 			    case PASSAGE:
194 					if (x == hero.x || y == hero.y)
195 					    passcount++;
196 					break;
197 			    case FLOOR:
198 			    case VWALL:
199 			    case HWALL:
200 			    case ULWALL:
201 			    case URWALL:
202 			    case LLWALL:
203 			    case LRWALL:
204 			    case ' ':
205 					break;
206 			    default:
207 					running = FALSE;
208 					break;
209 				}
210 		    }
211 		}
212     if (door_stop && !firstmove && passcount > 1)
213 		running = FALSE;
214     move(hero.y, hero.x);
215     if ((flat(hero.y,hero.x) & F_PASS) || (was_trapped > TRUE) 
216     				|| (flat(hero.y,hero.x) & F_MAZE))
217     	standout();
218     addch(PLAYER);
219     standend();
220     if (was_trapped) {
221     	beep();
222     	was_trapped = FALSE;
223     }
224 }
225 
226 /*
227  * find_obj:
228  *	Find the unclaimed object at y, x
229  */
230 THING *
231 find_obj(y, x)
232 register int y, x;
233 {
234     register THING *op;
235 
236     for (op = lvl_obj; op != NULL; op = next(op))
237 		if (op->o_pos.y == y && op->o_pos.x == x)
238 			return op;
239 #ifdef DEBUG
240     debug(sprintf(prbuf, "Non-object %c %d,%d", chat(y, x), y, x));
241     return NULL;
242 #else
243     /* NOTREACHED */
244 #endif
245 }
246 
247 /*
248  * eat:
249  *	She wants to eat something, so let her try
250  */
251 eat()
252 {
253     register THING *obj;
254 
255     if ((obj = get_item("eat", FOOD)) == NULL)
256 	return;
257     if (obj->o_type != FOOD)
258     {
259 	msg("ugh, you would get ill if you ate that");
260 	return;
261     }
262     inpack--;
263     if (--obj->o_count < 1)
264     {
265 	detach(pack, obj);
266 	discard(obj);
267     }
268     if (food_left < 0)
269 	food_left = 0;
270 	if (food_left > (STOMACHSIZE - 20)) 
271 		no_command += 2 + rnd(5);
272     if ((food_left += HUNGERTIME - 200 + rnd(400)) > STOMACHSIZE)
273 	food_left = STOMACHSIZE;
274     hungry_state = 0;
275     if (obj == cur_weapon)
276 	cur_weapon = NULL;
277     if (obj->o_which == 1)
278 	msg("my, that was a yummy %s", fruit);
279     else
280 	if (rnd(100) > 70)
281 	{
282 	    pstats.s_exp++;
283 	    msg("yuk, this food tastes awful");
284 	    check_level();
285 	}
286 	else
287 	    msg("yum, that tasted good");
288 	if (no_command)
289 		msg("You feel bloated and fall asleep");
290 }
291 
292 /*
293  * chg_str:
294  *	Used to modify the player's strength.  It keeps track of the
295  *	highest it has been, just in case
296  */
297 chg_str(amt)
298 register int amt;
299 {
300     str_t comp;
301 
302     if (amt == 0)
303 	return;
304     add_str(&pstats.s_str, amt);
305     comp = pstats.s_str;
306     if (ISRING(LEFT, R_ADDSTR))
307 		add_str(&comp, -cur_ring[LEFT]->o_ac);
308     if (ISRING(RIGHT, R_ADDSTR))
309 		add_str(&comp, -cur_ring[RIGHT]->o_ac);
310     if (comp > max_stats.s_str)
311 		max_stats.s_str = comp;
312 }
313 
314 /*
315  * add_str:
316  *	Perform the actual add, checking upper and lower bound
317  */
318 add_str(sp, amt)
319 register str_t *sp;
320 int amt;
321 {
322     if ((*sp += amt) < 3)
323 	*sp = 3;
324     else if (*sp > 31)
325 	*sp = 31;
326 }
327 
328 /*
329  * add_haste:
330  *	Add a haste to the player
331  */
332 add_haste(potion)
333 bool potion;
334 {
335     if (on(player, ISHASTE))
336     {
337 	no_command += rnd(8);
338 	player.t_flags &= ~ISRUN;
339 	extinguish(nohaste);
340 	player.t_flags &= ~ISHASTE;
341 	msg("you faint from exhaustion");
342 	return FALSE;
343     }
344     else
345     {
346 	player.t_flags |= ISHASTE;
347 	if (potion)
348 	    fuse(nohaste, 0, rnd(4)+10);
349 	return TRUE;
350     }
351 }
352 
353 /*
354  * aggravate:
355  *	Aggravate all the monsters on this level
356  */
357 aggravate()
358 {
359     register THING *mi;
360 
361     for (mi = mlist; mi != NULL; mi = next(mi))
362 	start_run(&mi->t_pos);
363 }
364 
365 /*
366  * vowelstr:
367  *      For printfs: if string starts with a vowel, return "n" for an
368  *	"an".
369  */
370 char *
371 vowelstr(str)
372 register char *str;
373 {
374     switch (*str)
375     {
376 	case 'a': case 'A':
377 	case 'e': case 'E':
378 	case 'i': case 'I':
379 	case 'o': case 'O':
380 	case 'u': case 'U':
381 	    return "n";
382 	default:
383 	    return "";
384     }
385 }
386 
387 /* 
388  * is_current:
389  *	See if the object is one of the currently used items
390  */
391 is_current(obj)
392 register THING *obj;
393 {
394     if (obj == NULL)
395 		return FALSE;
396     if (obj == cur_armor || obj == cur_weapon || obj == cur_ring[LEFT]
397 		|| obj == cur_ring[RIGHT]) {
398 		msg("That's already in use");
399 		return TRUE;
400     }
401     return FALSE;
402 }
403 
404 /*
405  * get_dir:
406  *      Set up the direction co_ordinate for use in varios "prefix"
407  *	commands
408  */
409 get_dir()
410 {
411     register char *prompt;
412     bool gotit;
413     register int ch;
414 
415 	if (again)
416 		return TRUE;
417     msg("which direction? ");
418 	do
419 		if ((ch = readchar()) == ESCAPE) {
420 			msg("");
421 			return FALSE;
422 		}
423 	while (find_dir(ch, &delta) == 0);
424     msg("");
425     if (on(player, ISHUH) && rnd(5) == 0)
426 	do {
427 	    delta.y = rnd(3) - 1;
428 	    delta.x = rnd(3) - 1;
429 	} while (delta.y == 0 && delta.x == 0);
430     return TRUE;
431 }
432 
433 find_dir(ch, cp)
434 byte ch;
435 coord *cp;
436 {
437 	bool gotit;
438 
439 	gotit = TRUE;
440 	switch (ch) {
441 	    when 'h': case'H': cp->y =  0; cp->x = -1;
442 	    when 'j': case'J': cp->y =  1; cp->x =  0;
443 	    when 'k': case'K': cp->y = -1; cp->x =  0;
444 	    when 'l': case'L': cp->y =  0; cp->x =  1;
445 	    when 'y': case'Y': cp->y = -1; cp->x = -1;
446 	    when 'u': case'U': cp->y = -1; cp->x =  1;
447 	    when 'b': case'B': cp->y =  1; cp->x = -1;
448 	    when 'n': case'N': cp->y =  1; cp->x =  1;
449 	    otherwise: gotit = FALSE;
450 	}
451 	return gotit;
452 }
453 
454 /*
455  * sign:
456  *	Return the sign of the number
457  */
458 sign(nm)
459 register int nm;
460 {
461     if (nm < 0)
462 		return -1;
463     else
464 		return (nm > 0);
465 }
466 
467 /*
468  * spread:
469  *	Give a spread around a given number (+/- 10%)
470  */
471 spread(nm)
472 register int nm;
473 {
474 	register int r = nm - nm / 10 + rnd(nm / 5);
475 	return r;
476 }
477 
478 /*
479  * call_it:
480  *	Call an object something after use.
481  */
482 call_it(know, guess)
483 register bool know;
484 register char **guess;
485 {
486     if (know && **guess)
487 		**guess = NULL;
488     else if (!know && **guess == NULL) {
489         msg("%scall it? ",noterse("what do you want to "));
490 		getinfo(prbuf,MAXNAME);
491 		if (*prbuf != ESCAPE)
492 		    strcpy(*guess, prbuf);
493 		msg("");
494     }
495 }
496 
497 /*
498  * step_ok:
499  *	Returns true if it is ok to step on ch
500  */
501 step_ok(ch)
502 {
503     switch (ch)
504     {
505 	case ' ':
506 	case VWALL:
507 	case HWALL:
508 	case ULWALL:
509 	case URWALL:
510 	case LLWALL:
511 	case LRWALL:
512 	    return FALSE;
513 	default:
514 	    return ((ch < 'A') || (ch > 'Z'));
515     }
516 }
517 
518 /*
519  * goodch:
520  *	Decide how good an object is and return the correct character for
521  * printing.
522  */
523 
524 goodch(obj)
525 register THING *obj;
526 {
527     register int ch = MAGIC;
528 
529     if (obj->o_flags & ISCURSED)
530 	ch = BMAGIC;
531     switch (obj->o_type) {
532 	when ARMOR:
533 	    if (obj->o_ac > a_class[obj->o_which])
534 		ch = BMAGIC;
535 	when WEAPON:
536 	    if (obj->o_hplus < 0 || obj->o_dplus < 0)
537 		ch = BMAGIC;
538 	when SCROLL:
539 	    switch (obj->o_which) {
540 		when S_SLEEP:
541 		case S_CREATE:
542 		case S_AGGR:
543 		    ch = BMAGIC;
544 	    }
545 	when POTION:
546 	    switch (obj->o_which) {
547 		when P_CONFUSE:
548 		case P_PARALYZE:
549 		case P_POISON:
550 		case P_BLIND:
551 		    ch = BMAGIC;
552 	    }
553 	when STICK:
554 	    switch (obj->o_which) {
555 		when WS_HASTE_M:
556 		case WS_TELTO:
557 		    ch = BMAGIC;
558 	    }
559 	when RING:
560 	    switch (obj->o_which) {
561 		when R_PROTECT:
562 		case R_ADDSTR:
563 		case R_ADDDAM:
564 		case R_ADDHIT:
565 		    if (obj->o_ac < 0)
566 			ch = BMAGIC;
567 		when R_AGGR:
568 		case R_TELEPORT:
569 		    ch = BMAGIC;
570 	    }
571     }
572     return ch;
573 }
574 
575 /*
576  * help: prints out help screens
577  */
578 help(helpscr)
579     char **helpscr;
580 {
581 #ifdef HELP
582 	register int hcount = 0;
583 	register int hrow, hcol;
584 	int isfull;
585 	byte answer;
586 
587     wdump();
588 	while (*helpscr && answer != ESCAPE)
589 	{
590 	    isfull = FALSE;
591 	    if ((hcount % (terse?23:46)) == 0)
592 	    	clear();
593 		/*
594 		 * determine row and column
595 		 */
596         hcol = 0;
597 	    if (terse)
598 	    {
599 	        hrow = hcount % 23;
600 	        if (hrow == 22)
601 	            isfull = TRUE;
602 	    }
603 	    else 
604 {
605 	        hrow = (hcount % 46) / 2;
606 	        if (hcount % 2)
607 	        	hcol = 40;
608 	        if (hrow == 22 && hcol == 40)
609 	             isfull = TRUE;
610 	    }
611 
612 	    move (hrow,hcol);
613 
614         addstr(*helpscr++);
615 
616         /*
617          * decide if we need print a continue type message
618          */
619         if ( (*helpscr == 0) || isfull)
620         {
621             if (*helpscr == 0)
622                 mvaddstr (24,0,"--press space to continue--");
623             else if (terse)
624                 mvaddstr (24,0,"--Space for more, Esc to continue--");
625             else
626                 mvaddstr (24,0,"--Press space for more, Esc to continue--");
627             do
628                 answer = readchar();
629             while (answer != ' ' && answer != ESCAPE) ;
630         }
631         hcount++;
632    }
633    wrestor();
634 #endif HELP
635 }
636 
637 #ifndef UNIX
638 
639 DISTANCE(y1, x1, y2, x2)
640 int y1, x1, y2, x2;
641 {
642 	register int dx, dy;
643 
644 	dx = (x1 - x2);
645 	dy = (y1 - y2);
646 	return dx * dx + dy * dy;
647 }
648 
649 _ce(a,b)
650     coord *a, *b;
651 {
652     return(a->x == b->x && a->y == b->y);
653 }
654 
655 INDEX(y,x)
656 {
657 #ifdef DEBUG
658     if (offmap(y,x) && me())
659 		fatal("BAD INDEX");
660 #endif DEBUG
661     return((x * (maxrow-1)) + y - 1);
662 }
663 
664 offmap(y,x)
665 {
666 	return (y < 1 || y >= maxrow || x < 0 || x >= COLS) ;
667 }
668 
669 winat(y,x)
670     int y, x;
671 {
672     return(moat(y,x) != NULL ? moat(y,x)->t_disguise : chat(y,x));
673 }
674 #endif
675 
676 /*
677  * search:
678  *	Player gropes about him to find hidden things.
679  */
680 search()
681 {
682     register int y, x;
683     register byte *fp;
684     register int ey, ex;
685 
686     if (on(player, ISBLIND))
687 	return;
688     ey = hero.y + 1;
689     ex = hero.x + 1;
690     for (y = hero.y - 1; y <= ey; y++) 
691 	for (x = hero.x - 1; x <= ex; x++)
692 	{
693 	    if ((y == hero.y && x == hero.x) || offmap(y, x))
694 		continue;
695 	    fp = &flat(y, x);
696 	    if (!(*fp & F_REAL))
697 		switch (chat(y, x))
698 		{
699 		    case VWALL:
700 		    case HWALL:
701 		    case ULWALL:
702 		    case URWALL:
703 		    case LLWALL:
704 		    case LRWALL:
705 			if (rnd(5) != 0)
706 			    break;
707 			chat(y, x) = DOOR;
708 			*fp |= F_REAL;
709 			count = running = FALSE;
710 			break;
711 		    case FLOOR:
712 			if (rnd(2) != 0)
713 			    break;
714 			chat(y, x) = TRAP;
715 			*fp |= F_REAL;
716 			count = running = FALSE;
717 			msg("you found %s", tr_name(*fp & F_TMASK));
718 			break;
719 		}
720 	}
721 }
722 
723 
724 /*
725  * d_level:
726  *	He wants to go down a level
727  */
728 d_level()
729 {
730     if (chat(hero.y, hero.x) != STAIRS)
731 		msg("I see no way down");
732     else {
733 		level++;
734 		new_level();
735     }
736 }
737 
738 /*
739  * u_level:
740  *	He wants to go up a level
741  */
742 u_level()
743 {
744     if (chat(hero.y, hero.x) == STAIRS)
745 		if (amulet) {
746 		    level--;
747 		    if (level == 0)
748 				total_winner();
749 		    new_level();
750 		    msg("you feel a wrenching sensation in your gut");
751 		} else
752 		    msg("your way is magically blocked");
753     else
754 		msg("I see no way up");
755 }
756 
757 /*
758  * call:
759  *	Allow a user to call a potion, scroll, or ring something
760  */
761 call()
762 {
763     register THING *obj;
764     register char **guess, *elsewise;
765     register bool *know;
766 
767     obj = get_item("call", CALLABLE);
768     /*
769      * Make certain that it is somethings that we want to wear
770      */
771     if (obj == NULL)
772 	return;
773     switch (obj->o_type)
774     {
775 	when RING:
776 	    guess = (char **)r_guess;
777 	    know = r_know;
778 	    elsewise = (*guess[obj->o_which] != NULL ?
779 			guess[obj->o_which] : r_stones[obj->o_which]);
780 	when POTION:
781 	    guess = (char **)p_guess;
782 	    know = p_know;
783 	    elsewise = (*guess[obj->o_which] != NULL ?
784 			guess[obj->o_which] : p_colors[obj->o_which]);
785 	when SCROLL:
786 	    guess = (char **)s_guess;
787 	    know = s_know;
788 	    elsewise = (*guess[obj->o_which] != NULL ?
789 			guess[obj->o_which] : (char *)(&s_names[obj->o_which]));
790 	when STICK:
791 	    guess = (char **)ws_guess;
792 	    know = ws_know;
793 	    elsewise = (*guess[obj->o_which] != NULL ?
794 			guess[obj->o_which] : ws_made[obj->o_which]);
795 	otherwise:
796 	    msg("you can't call that anything");
797 	    return;
798     }
799     if (know[obj->o_which])
800     {
801 	msg("that has already been identified");
802 	return;
803     }
804     msg("Was called \"%s\"", elsewise);
805     msg("what do you want to call it? ");
806     getinfo(prbuf,MAXNAME);
807     if (*prbuf && *prbuf != ESCAPE)
808         strcpy(guess[obj->o_which], prbuf);
809     msg("");
810 }
811 
812 /*
813  * prompt player for definition of macro
814  */
815 do_macro(buf,sz)
816     char *buf;
817     int sz;
818 {
819 	register char *cp = prbuf;
820 
821     msg("F9 was %s, enter new macro: ",buf);
822     if (getinfo(prbuf,sz-1) != ESCAPE)
823     	do {
824     		if (*cp != CTRL(F))
825     			*buf++ = *cp;
826     	} while (*cp++) ;
827     msg("");
828     flush_type();
829 }
830 
831 #ifdef ME
832 me()
833 {
834 	return is_me;
835 }
836 #endif ME
837 
838 
839 #ifdef TEST
840 istest()
841 {
842 	return (!strcmp("debug",fruit));
843 }
844 #endif TEST
845 