1 /*
2  * Various input/output functions
3  *
4  * io.c		1.4		(A.I. Design) 12/10/84
5  */
6 
7 #include	"rogue.h"
8 #include	"curses.h"
9 #include	"extern.h"
10 
11 extern char *stccpy(), *stpchr();
12 extern int scr_type;
13 extern unsigned tick;
14 
15 #define AC(a) (-((a)-11))
16 char *stpbrk();
17 #define PT(i,j) ((COLS==40)?i:j)
18 /*
19  * msg:
20  *	Display a message at the top of the screen.
21  */
22 extern char *msgbuf;
23 static int newpos = 0;
24 
25 /* VARARGS1 */
26 
27 ifterse(tfmt,fmt,a1,a2,a3,a4,a5)
28 char *tfmt,*fmt;
29 int a1,a2,a3,a4,a5;
30 {
31 	if (expert)
32 	    msg(tfmt,a1,a2,a3,a4,a5);
33 	else
34 	    msg(fmt,a1,a2,a3,a4,a5);
35 }
36 
37 msg(fmt, a1, a2, a3, a4, a5)
38 char *fmt;
39 int a1,a2,a3,a4,a5;
40 {
41     /*
42      * if the string is "", just clear the line
43      */
44     if (*fmt == '\0')
45     {
46 	move(0, 0);
47 	clrtoeol();
48 	mpos = 0;
49 	return;
50     }
51     /*
52      * otherwise add to the message and flush it out
53      */
54     doadd(fmt, a1,a2,a3,a4,a5);
55     endmsg();
56 }
57 
58 /*
59  * addmsg:
60  *	Add things to the current message
61  */
62 /* VARARGS1 */
63 addmsg(fmt, a1,a2,a3,a4,a5)
64 char *fmt;
65 int a1,a2,a3,a4,a5;
66 {
67     doadd(fmt, a1,a2,a3,a4,a5);
68 }
69 
70 /*
71  * endmsg:
72  *	Display a new msg (giving him a chance to see the previous one
73  *	if it is up there with the -More-)
74  */
75 endmsg()
76 {
77     if (save_msg)
78 		strcpy(huh, msgbuf);
79     if (mpos) {
80 		look(FALSE);
81         move(0,mpos);
82 		more(" More ");
83     }
84     /*
85      * All messages should start with uppercase, except ones that
86      * start with a pack addressing character
87      */
88     if (islower(msgbuf[0]) && msgbuf[1] != ')')
89 		msgbuf[0] = toupper(msgbuf[0]);
90     putmsg(0,msgbuf);
91     mpos = newpos;
92     newpos = 0;
93 }
94 
95 
96 /*
97  *  More:  tag the end of a line and wait for a space
98  */
99 more(msg)
100     char *msg;
101 {
102     int x, y;
103 	register int i, msz;
104     char mbuf[80];
105     int morethere = TRUE;
106     int covered = FALSE;
107 
108     msz = strlen(msg);
109     getxy(&x,&y);
110 	/*
111 	 * it is reasonable to assume that if the you are no longer
112 	 * on line 0, you must have wrapped.
113 	 */
114 	if (x != 0) {
115 		x=0;
116 		y=COLS;
117 	}
118     if ((y+msz)>COLS) {
119         move(x,y=COLS-msz);
120         covered = TRUE;
121     }
122     
123     for(i=0;i<msz;i++) {
124         mbuf[i] = inch();
125         if ((i+y) < (COLS-2))
126             move(x,y+i+1);
127         mbuf[i+1] = 0;
128      }
129 
130      move(x,y);
131      standout();
132      addstr(msg);
133      standend();
134 
135      while (readchar() != ' ') {
136      	if (covered && morethere) {
137      	    move(x,y);
138      	    addstr(mbuf);
139      	    morethere = FALSE;
140      	}
141      	else if (covered)
142         {
143             move(x,y);
144             standout();
145             addstr(msg);
146             standend();
147             morethere = TRUE;
148         }     		
149      }
150      move(x,y);
151      addstr(mbuf);
152 }
153 
154 
155 /*
156  * doadd:
157  *	Perform an add onto the message buffer
158  */
159 doadd(fmt, a1, a2, a3, a4, a5)
160 char *fmt;
161 int a1, a2, a3, a4, a5;
162 {
163     sprintf(&msgbuf[newpos],fmt, a1, a2, a3, a4, a5);
164     newpos = strlen(msgbuf);
165 }
166 
167 /*
168  * putmsg:
169  *  put a msg on the line, make sure that it will fit, if it won't
170  *  scroll msg sideways until he has read it all
171  */
172 putmsg(msgline,msg)
173     int msgline;
174     char *msg;
175 {
176     register char *curmsg, *lastmsg=0, *tmpmsg;
177     int curlen;
178 
179     curmsg = msg;
180     do {
181         scrl(msgline,lastmsg,curmsg);
182         newpos = curlen = strlen(curmsg);
183         if (curlen > COLS) {
184             more(" Cont ");
185             lastmsg = curmsg;
186             do {
187                 tmpmsg = stpbrk(curmsg," ");
188                 /*
189                  * If there are no blanks in line
190                  */
191                 if ((tmpmsg==0 || tmpmsg>=&lastmsg[COLS]) && lastmsg==curmsg) {
192                     curmsg = &lastmsg[COLS];
193                     break;
194                 }
195                 if ((tmpmsg >= (lastmsg+COLS)) || (strlen(curmsg) < COLS))
196                     break;
197                 curmsg = tmpmsg + 1;
198             } while (1) ;
199         }            
200     } while (curlen > COLS) ;
201 }
202 
203 /*
204  * scrl:  scroll a message accross the line
205  */
206 scrl(msgline,str1,str2)
207     int msgline;
208     char *str1, *str2;
209 {
210 	char *fmt;
211 	register int x,y;
212 
213 	if (COLS > 40)
214 		fmt = "%.80s";
215 	else
216 		fmt = "%.40s";
217 
218 	if (str1 == 0) {
219         move(msgline,0);
220 		if (strlen(str2) < COLS)
221 		    clrtoeol();
222 		printw(fmt,str2);
223 	} else
224 		while (str1 <= str2) {
225 			move(msgline,0);
226 			printw(fmt,str1++);
227 			if (strlen(str1) < (COLS-1))
228 				clrtoeol();
229 		}
230 }
231 
232 /*
233  * unctrl:
234  *	Print a readable version of a certain character
235  */
236 char *
237 unctrl(ch)
238 unsigned char ch;
239 {
240     static chstr[9];		/* Defined in curses library */
241 
242 	if (isspace(ch) )
243 		strcpy(chstr," ");
244 	else if (!isprint(ch))
245 		if (ch < ' ')
246 			sprintf(chstr, "^%c", ch + '@');
247 		else
248 			sprintf(chstr, "\\x%x",ch);
249 	else {
250 		chstr[0] = ch;
251 		chstr[1] = 0;
252 	}
253 
254     return chstr;
255 }
256 
257 /*
258  * status:
259  *	Display the important stats line.  Keep the cursor where it was.
260  */
261 status()
262 {
263     int oy, ox;
264     static int s_hungry;
265     static int s_lvl, s_pur = -1, s_hp, s_ac = 0;
266     static str_t s_str;
267     static int s_elvl = 0;
268     static char *state_name[] =
269     {
270 	"      ", "Hungry", "Weak", "Faint","?"
271     };
272 
273     SIG2();
274 
275     getyx(stdscr, oy, ox);
276     if (is_color)
277         yellow();
278 
279     /*
280      * Level:
281      */
282     if (s_lvl != level)
283     {
284         s_lvl = level;
285 	move(PT(22,23),0);
286 	printw("Level:%-4.4d", level);
287     }
288 
289     /*
290      * Hits:
291      */
292     if (s_hp != pstats.s_hpt)
293     {
294         s_hp = pstats.s_hpt;
295         move(PT(22,23),12);
296         printw("Hits:%.3d(%.3d) ", pstats.s_hpt, max_hp);
297 		/* just in case they get wraithed with 3 digit max hits */
298 		if (pstats.s_hpt < 100)
299         	addch(' ');
300     }
301 
302     /*
303      * Str:
304      */
305     if (pstats.s_str != s_str)
306     {
307         s_str = pstats.s_str;
308         move(PT(22,23),26);
309 	printw("Str:%.3d(%.3d) ", pstats.s_str, max_stats.s_str);
310     }
311 
312     /*
313      * Gold
314      */
315     if(s_pur != purse)
316     {
317         s_pur = purse;
318 	move(23, PT(0,40));
319         printw("Gold:%-5.5u",purse);
320     }
321 
322     /*
323      * Armor:
324      */
325     if(s_ac != (cur_armor != NULL ? cur_armor->o_ac : pstats.s_arm))
326     {
327         s_ac = (cur_armor != NULL ? cur_armor->o_ac : pstats.s_arm);
328 	if (ISRING(LEFT,R_PROTECT))
329             s_ac -= cur_ring[LEFT]->o_ac;
330 	if (ISRING(RIGHT,R_PROTECT))
331             s_ac -= cur_ring[RIGHT]->o_ac;
332         move(23,PT(12,52));
333         printw("Armor:%-2.2d",
334             AC(cur_armor != NULL ? cur_armor->o_ac : pstats.s_arm));
335     }
336 
337     /*
338      * Exp:
339      */
340     if (s_elvl != pstats.s_lvl)
341     {
342         s_elvl = pstats.s_lvl;
343         move(23, PT(22, 62)); 
344         printw("%-12s", he_man[s_elvl-1]); 
345     }
346 
347     /*
348      * Hungry state
349      */
350     if (s_hungry != hungry_state)
351     {
352         s_hungry = hungry_state;
353         move(24, PT(28,58));
354 	addstr(state_name[0]);
355         move(24, PT(28,58));
356         if (hungry_state)
357         {
358             bold();
359             addstr(state_name[hungry_state]);
360             standend();
361         }
362     }
363 
364     if (is_color)
365        standend();
366 
367     move(oy, ox);
368 
369 }
370 
371 /*
372  * wait_for
373  *	Sit around until the guy types the right key
374  */
375 wait_for(ch)
376 char ch;
377 {
378     register char c;
379 
380     if (ch == '\n')
381         while ((c = readchar()) != '\n' && c != '\r')
382 	    continue;
383     else
384        while (readchar() != ch)
385 	    continue;
386 }
387 
388 /*
389  * show_win:
390  *	Function used to display a window and wait before returning
391  */
392 show_win(scr, message)
393 int *scr;
394 char *message;
395 {
396     mvaddstr(0,0,message);
397     move(hero.y, hero.x);
398     wait_for(' ');
399 }
400 
401 
402 /*
403  * This routine reads information from the keyboard
404  * It should do all the strange processing that is
405  * needed to retrieve sensible data from the user
406  */
407 getinfo(str,size)
408     char *str;
409     int size;
410 {
411     register char *retstr, ch;
412     int readcnt = 0;
413     int wason, ret = 1;
414     char buf[160];
415 
416 	dmain(buf, 80, scr_ds, 0);
417 	retstr = str;
418 	*str = 0;
419 	wason = cursor(TRUE);
420 	while(ret == 1)
421         switch(ch = getch()) {
422         	case ESCAPE:
423         		while(str != retstr) {
424         		    backspace();
425                 	readcnt--;
426                 	str--;
427                 }
428                 ret = *str = ESCAPE;
429                 cursor(wason);
430                 break;
431             case '\b':
432                 if (str != retstr) {
433                     backspace();
434                 	readcnt--;
435                 	str--;
436                 }
437                 break;
438             default:
439                 if ( readcnt >= size) {
440                    beep();
441                    break;
442                 }
443                 readcnt++;
444             	addch(ch);
445                 *str++ = ch;
446                	if ((ch & 0x80) == 0)
447 					break;
448             case '\n':
449             case '\r':
450                 *str = 0;
451                 cursor(wason);
452                 ret = ch;
453                 break;
454         }
455 	dmaout(buf, 80, scr_ds, 0);
456     return ret;
457 }
458 
459 backspace()
460 {
461 	int x, y;
462 	getxy(&x,&y);
463 	if (--y<0)
464 		y = 0;
465 	move(x,y);
466     putchr(' ');
467 }
468 
469 /*
470  * str_attr:  format a string with attributes.
471  *
472  *    formats:
473  *        %i - the following character is turned inverse vidio
474  *        %I - All characters upto %$ or null are turned inverse vidio
475  *        %u - the following character is underlined
476  *        %U - All characters upto %$ or null are underlined
477  *        %$ - Turn off all attributes
478  *
479  *     Attributes do not nest, therefore turning on an attribute while
480  *     a different one is in effect simply changes the attribute.
481  *
482  *     "No attribute" is the default and is set on leaving this routine
483  *
484  *     Eventually this routine will contain colors and character intensity
485  *     attributes.  And I'm not sure how I'm going to interface this with
486  *     printf certainly '%' isn't a good choice of characters.  jll.
487  */
488 str_attr(str)
489     char *str;
490 {
491 #ifdef LUXURY
492     register int is_attr_on = FALSE, was_touched = FALSE;
493 
494     while(*str)
495     {
496     	if (was_touched == TRUE)
497     	{
498     	    standend();
499     	    is_attr_on = FALSE;
500     	    was_touched = FALSE;
501     	}
502 	if (*str == '%')
503 	{
504 	    str++;
505 	    switch(*str)
506 	    {	
507 		case 'u':
508                     was_touched = TRUE;
509                 case 'U':
510 		    uline();
511                     is_attr_on = TRUE;
512                     str++;
513                     break;
514                 case 'i':
515                     was_touched = TRUE;
516                 case 'I':
517                     standout();
518                     is_attr_on = TRUE;
519                     str++;
520                     break;
521                 case '$':
522                     if (is_attr_on)
523                         was_touched = TRUE;
524                     str++;
525                     continue;
526              }
527         }
528         if ((*str == '\n') || (*str == '\r'))
529         {
530         	str++;
531         	printw("\n");
532         }
533         else if (*str != 0)
534             addch(*str++);
535     }
536     if (is_attr_on)
537     	standend();
538 #else
539     while (*str)
540     {
541 	if (*str == '%') {
542 	    str++;
543 	    standout();
544 	}
545 	addch(*str++);
546 	standend();
547     }
548 	
549 #endif LUXURY
550 }
551 
552 /*
553  * key_state:
554  */
555 SIG2()
556 {
557 	static unsigned icnt = 0, ntick = 0;
558 	static int key_init = TRUE;
559 	static int numl, capsl;
560 	static int nspot, cspot, tspot;
561 	register int new_numl, new_capsl, new_fmode;
562 	static int bighand, littlehand;
563 	int showtime = FALSE, spare;
564 	int x, y;
565 	char wbuf[10];
566 #ifdef DEMO
567 	static tot_time = 0;
568 #endif DEMO
569 
570 	if (tick < ntick)
571 		return;
572 	ntick = tick + 6;
573 	if (is_saved || scr_type < 0)
574         	return;
575 	regs->ax = 0x200;
576 	swint(SW_KEY, regs);
577 	new_numl = regs->ax;
578 	new_capsl = new_numl & 0x40;
579 	new_fmode = new_numl & 0x10;
580 	new_numl &= 0x20;
581 	/*
582 	 * set up the clock the first time here
583 	 */
584 	if (key_init) {
585 		regs->ax = 0x2c << 8;
586 		swint(SW_DOS, regs);
587 		bighand = (regs->cx >> 8) % 12;
588 		littlehand = regs->cx & 0xFF;
589 		showtime++;
590 	}
591 	if (tick > 1092) {
592 		/*
593 		 * time os call kills jr and others we keep track of it 
594 		 * ourselves
595 		 */
596 		littlehand = (littlehand + 1) % 60;
597 		if (littlehand == 0)
598 			bighand = (bighand + 1) % 12;
599 		tick = tick - 1092;
600 		ntick = tick + 6;
601 		showtime++;
602 	}
603 
604 	/*
605 	 * this is built for speed so set up once first time this
606 	 * is executed
607 	 */
608 	if (key_init || reinit)
609 	{
610 		reinit = key_init = FALSE;
611 	    if (COLS == 40)
612 	    {
613 	    	nspot = 10;
614 	    	cspot = 19;
615 	    	tspot = 35;
616 	    }
617 	    else
618 	    {
619 	        nspot = 20;
620 	        cspot = 39;
621 	        tspot = 75;
622 	    }
623 	    /*
624 	     * this will force all fields to be updated first time through
625 	     */
626 	    numl = !new_numl;
627 	    capsl = !new_capsl;
628 	    showtime++;
629 	    faststate = !new_fmode;
630     }
631     
632     getxy(&x, &y);
633 
634     if (faststate != new_fmode)
635     {
636         
637         faststate = new_fmode;
638         count = 0;
639 		show_count();
640         running = FALSE;
641         move(LINES-1,0);
642         if (faststate)
643         {
644             bold();
645             addstr("Fast Play");
646             standend();
647          }
648          else
649          {
650             addstr("         ");
651          }
652     }
653 
654     if (numl != new_numl)
655     {
656     	numl = new_numl;
657     	count = 0;
658 		show_count();
659     	running = FALSE;
660     	move(24,nspot);
661     	if (numl)
662     	{
663     		bold();
664     		addstr("NUM LOCK");
665     		standend();
666     	}
667     	else
668     		addstr("        ");
669     }
670     if (capsl != new_capsl)
671     {
672     	capsl = new_capsl;
673     	move(24,cspot);
674     	if (capsl)
675     	{
676     		bold();
677     		addstr("CAP LOCK");
678     		standend();
679     	}
680     	else
681     		addstr("        ");
682     }
683     if (showtime)
684     {
685 		showtime = FALSE;
686 #ifdef DEMO
687 	    /*
688 	     * Don't let them get by level 10 because they might do something
689 	     * nasty like disable the clock
690 	     */
691 		if (((tot_time++ - max_level) > DEMOTIME) || max_level > 10)
692 			demo(DEMOTIME);
693 #endif DEMO    	
694 		/* work around the compiler buggie boos */
695 		spare = littlehand % 10;
696         move(24,tspot);
697         bold();
698 		printw("%2d:%1d%1d",bighand?bighand:12,littlehand/10,spare);
699 		standend();
700     }
701     move(x, y);
702 }
703 
704 /*
705  * Replacement printf
706  * Michael Toy, AI Design, January 1984
707  */
708 
709 static int	pf_str(), pf_chr(), pf_uint(), pf_int(), pf_per();
710 static char *formats = "scud%", *bp, left_justify;
711 static int min_width, max_width;
712 
713 static int (*(pfuncs[]))() = {
714 	pf_str, pf_chr, pf_uint, pf_int, pf_per
715 };
716 
717 char *my_stccpy(a, b, c)
718 {
719 	stccpy(a, b, c);
720 	return a + strlen(a);
721 }
722 
723 char *
724 sprintf(buf, fmt, arg)
725 char *buf, *fmt;
726 int arg;
727 {
728 	register char *cp, *init;
729 	int *ap = &arg, pad;
730 	char tbuf[128];
731 
732 	init = buf;
733 	while (*fmt) {
734 		if (*fmt != '%')
735 			*buf++ = *fmt++;
736 		else {
737 			left_justify = max_width = 0;
738 			if (*++fmt == '-')
739 				left_justify++, fmt++;
740 			min_width = scan_num(fmt);
741 			if (*bp == '.')
742 				max_width = scan_num(++bp);
743 			fmt = bp;
744 			bp = tbuf;
745 			if (cp = stpchr(formats, *fmt))
746 				ap += (*(pfuncs[cp - formats]))(ap);
747 			*bp = 0;
748 			if (max_width && strlen(tbuf) > max_width)
749 				tbuf[max_width] = 0;
750 			pad = min_width - strlen(tbuf);
751 			bp = buf;
752 			if (!left_justify)
753 				blanks(pad);
754 			bp = my_stccpy(bp, tbuf, 200);
755 			if (left_justify)
756 				blanks(pad);
757 			buf = bp;
758 			if (*fmt)
759 				fmt++;
760 		}
761 	}
762 	*buf = 0;
763 	return init;
764 }
765 
766 scan_num(cp)
767 char *cp;
768 {
769 	register int i = 0;
770 
771 	bp = cp;
772 	while (isdigit(*bp))
773 		i = i * 10 + *bp++ - '0';
774 	return i;
775 }
776 
777 pf_str(cp)
778 char **cp;
779 {
780 	bp = my_stccpy(bp, *cp, 200);
781 	return 1;
782 }
783 
784 blanks(cnt)
785 {
786 	while (cnt-- > 0)
787 		*bp++ = ' ';
788 	*bp = 0;
789 }
790 
791 pf_chr(c)
792 char *c;
793 {
794 	*bp++ = *c;
795 	return 1;
796 }
797 
798 static char ibuf[6];
799 
800 pf_int(ip)
801 int *ip;
802 {
803 	if (*ip < 0) {
804 		*bp++ = '-';
805 		*ip = (-*ip);
806 	}
807 	return pf_uint(ip);
808 }
809 
810 pf_uint(ip)
811 unsigned int *ip;
812 {
813 	char *cp = ibuf, once;
814 	unsigned int i = *ip, d = 10000, r;
815 
816 	if (*ip == 0) {
817 		*ibuf = '0';
818 		ibuf[1] = 0;
819 	} else {
820 		once = 0;
821 		while (d) {
822 			if ((r = i/d) || once) {
823 				*cp++ = r + '0';
824 				once =1;
825 				i -= r*d;
826 			}
827 			d /= 10;
828 		}
829 		*cp = 0;
830 	}
831 	bp = my_stccpy(bp, ibuf, 6);
832 	return 1;
833 }
834 
835 pf_per(ip)
836 {
837 	*bp++ = '%';
838 	return 0;
839 }
840 
841 noterse(str)
842 	char *str;
843 {
844 	return( terse || expert ? nullstr : str);
845 }
846 
847 