xref: /dragonfly/games/phantasia/io.c (revision a361ab31)
1 /*	$NetBSD: io.c,v 1.14 2009/08/31 08:27:16 dholland Exp $	*/
2 
3 /*
4  * io.c - input/output routines for Phantasia
5  */
6 
7 #include <string.h>
8 #include "include.h"
9 
10 static void catchalarm(int) __dead2;
11 
12 /*
13  * FUNCTION: read a string from operator
14  *
15  * ARGUMENTS:
16  *	char *cp - pointer to buffer area to fill
17  *	int mx - maximum number of characters to put in buffer
18  *
19  * GLOBAL INPUTS: Echo, _iob[], Wizard, *stdscr
20  *
21  * GLOBAL OUTPUTS: _iob[]
22  *
23  * DESCRIPTION:
24  *	Read a string from the keyboard.
25  *	This routine is specially designed to:
26  *
27  *	    - strip non-printing characters (unless Wizard)
28  *	    - echo, if desired
29  *	    - redraw the screen if CH_REDRAW is entered
30  *	    - read in only 'mx - 1' characters or less characters
31  *	    - nul-terminate string, and throw away newline
32  *
33  *	'mx' is assumed to be at least 2.
34  */
35 
36 void
37 getstring(char *cp, int mx)
38 {
39 	char *inptr;		/* pointer into string for next string */
40 	int x, y;		/* original x, y coordinates on screen */
41 	int ch;			/* input */
42 
43 	getyx(stdscr, y, x);	/* get coordinates on screen */
44 	inptr = cp;
45 	*inptr = '\0';		/* clear string to start */
46 	--mx;			/* reserve room in string for nul terminator */
47 
48 	do {
49 		/* get characters and process */
50 		if (Echo)
51 			mvaddstr(y, x, cp);	/* print string on screen */
52 		clrtoeol();	/* clear any data after string */
53 		refresh();	/* update screen */
54 
55 		ch = getchar();	/* get character */
56 
57 		switch (ch) {
58 		case CH_ERASE:	/* back up one character */
59 			if (inptr > cp)
60 				--inptr;
61 			break;
62 
63 		case CH_KILL:	/* back up to original location */
64 			inptr = cp;
65 			break;
66 
67 		case CH_NEWLINE:	/* terminate string */
68 			break;
69 
70 		case CH_REDRAW:	/* redraw screen */
71 			clearok(stdscr, TRUE);
72 			continue;
73 
74 		default:	/* put data in string */
75 			if (ch >= ' ' || Wizard)
76 				/* printing char; put in string */
77 				*inptr++ = ch;
78 		}
79 
80 		*inptr = '\0';	/* terminate string */
81 	} while (ch != CH_NEWLINE && inptr < cp + mx);
82 }
83 
84 /*
85  * FUNCTION: pause and prompt player
86  *
87  * ARGUMENTS:
88  *	int where - line on screen on which to pause
89  *
90  * GLOBAL INPUTS: *stdscr
91  *
92  * DESCRIPTION:
93  *	Print a message, and wait for a space character.
94  */
95 
96 void
97 more(int where)
98 {
99 	mvaddstr(where, 0, "-- more --");
100 	getanswer(" ", FALSE);
101 }
102 
103 /*
104  * FUNCTION: input a floating point number from operator
105  *
106  * RETURN VALUE: floating point number from operator
107  *
108  * GLOBAL INPUTS: Databuf[]
109  *
110  * DESCRIPTION:
111  *	Read a string from player, and scan for a floating point
112  *	number.
113  *	If no valid number is found, return 0.0.
114  */
115 
116 double
117 infloat(void)
118 {
119 	double result;		/* return value */
120 
121 	getstring(Databuf, SZ_DATABUF);
122 	if (sscanf(Databuf, "%lf", &result) < 1)
123 		/* no valid number entered */
124 		result = 0.0;
125 
126 	return (result);
127 }
128 
129 /*
130  * FUNCTION: input an option value from player
131  *
132  * GLOBAL INPUTS: Player
133  *
134  * GLOBAL OUTPUTS: Player
135  *
136  * DESCRIPTION:
137  *	Age increases with every move.
138  *	Refresh screen, and get a single character option from player.
139  *	Return a random value if player's ring has gone bad.
140  */
141 
142 int
143 inputoption(void)
144 {
145 	++Player.p_age;		/* increase age */
146 
147 	if (Player.p_ring.ring_type != R_SPOILED)
148 		/* ring ok */
149 		return (getanswer("T ", TRUE));
150 	else {
151 		/* bad ring */
152 		getanswer(" ", TRUE);
153 		return ((int)ROLL(0.0, 5.0) + '0');
154 	}
155 }
156 
157 /*
158  * FUNCTION: handle interrupt from operator
159  *
160  * GLOBAL INPUTS: Player, *stdscr
161  *
162  * DESCRIPTION:
163  *	Allow player to quit upon hitting the interrupt key.
164  *	If the player wants to quit while in battle, he/she automatically
165  *	dies.
166  */
167 
168 void
169 interrupt(void)
170 {
171 	char line[81];		/* a place to store data already on screen */
172 	int loop;		/* counter */
173 	int x, y;		/* coordinates on screen */
174 	int ch;			/* input */
175 	unsigned savealarm;	/* to save alarm value */
176 
177 #ifdef SYS3
178 	signal(SIGINT, SIG_IGN);
179 #endif
180 #ifdef SYS5
181 	signal(SIGINT, SIG_IGN);
182 #endif
183 
184 	savealarm = alarm(0);	/* turn off any alarms */
185 
186 	getyx(stdscr, y, x);	/* save cursor location */
187 
188 	for (loop = 0; loop < 80; ++loop) {	/* save line on screen */
189 		move(4, loop);
190 		line[loop] = inch();
191 	}
192 	line[80] = '\0';	/* nul terminate */
193 
194 	if (Player.p_status == S_INBATTLE || Player.p_status == S_MONSTER) {
195 		/* in midst of fighting */
196 		mvaddstr(4, 0, "Quitting now will automatically kill your character.  Still want to ? ");
197 		ch = getanswer("NY", FALSE);
198 		if (ch == 'Y')
199 			death("Bailing out");
200 		/* NOTREACHED */
201 	} else {
202 		mvaddstr(4, 0, "Do you really want to quit ? ");
203 		ch = getanswer("NY", FALSE);
204 		if (ch == 'Y')
205 			leavegame();
206 		/* NOTREACHED */
207 	}
208 
209 	mvaddstr(4, 0, line);	/* restore data on screen */
210 	move(y, x);		/* restore cursor */
211 	refresh();
212 
213 #ifdef SYS3
214 	signal(SIGINT, interrupt);
215 #endif
216 #ifdef SYS5
217 	signal(SIGINT, interrupt);
218 #endif
219 
220 	alarm(savealarm);	/* restore alarm */
221 }
222 
223 /*
224  * FUNCTION: get an answer from operator
225  *
226  * ARGUMENTS:
227  *	char *choices - string of (upper case) valid choices
228  *	bool def - set if default answer
229  *
230  * GLOBAL INPUTS: catchalarm(), Echo, _iob[], _ctype[], *stdscr, Timeout,
231  *	Timeoenv[]
232  *
233  * GLOBAL OUTPUTS: _iob[]
234  *
235  * DESCRIPTION:
236  *	Get a single character answer from operator.
237  *	Timeout waiting for response.  If we timeout, or the
238  *	answer in not in the list of valid choices, print choices,
239  *	and wait again, otherwise return the first character in ths
240  *	list of choices.
241  *	Give up after 3 tries.
242  */
243 
244 int
245 getanswer(const char *choices, bool def)
246 {
247 	int ch;				/* input */
248 	volatile int loop;		/* counter */
249 	volatile int oldx, oldy;	/* original coordinates on screen */
250 
251 	getyx(stdscr, oldy, oldx);
252 	alarm(0);			/* make sure alarm is off */
253 
254 	for (loop = 3; loop; --loop) {
255 		/* try for 3 times */
256 		if (setjmp(Timeoenv) != 0) {
257 			/* timed out waiting for response */
258 			if (def || loop <= 1)
259 				/* return default answer */
260 				break;
261 			else
262 				/* prompt, and try again */
263 				goto YELL;
264 		} else {
265 			/* wait for response */
266 			clrtoeol();
267 			refresh();
268 #ifdef BSD41
269 			sigset(SIGALRM, catchalarm);
270 #else
271 			signal(SIGALRM, catchalarm);
272 #endif
273 			/* set timeout */
274 			if (Timeout)
275 				alarm(7);	/* short */
276 			else
277 				alarm(600);	/* long */
278 
279 			ch = getchar();
280 
281 			alarm(0);	/* turn off timeout */
282 
283 			if (ch < 0) {
284 				/* caught some signal */
285 				++loop;
286 				continue;
287 			} else if (ch == CH_REDRAW) {
288 				/* redraw screen */
289 				clearok(stdscr, TRUE);	/* force clear screen */
290 				++loop;	/* don't count this input */
291 				continue;
292 			} else if (Echo) {
293 				addch(ch);	/* echo character */
294 				refresh();
295 			}
296 
297 			if (islower(ch))
298 				/* convert to upper case */
299 				ch = toupper(ch);
300 
301 			if (def || strchr(choices, ch) != NULL)
302 				/* valid choice */
303 				return (ch);
304 			else if (!def && loop > 1) {
305 				/* bad choice; prompt, and try again */
306 YELL:				mvprintw(oldy + 1, 0,
307 				    "Please choose one of : [%s]\n", choices);
308 				move(oldy, oldx);
309 				clrtoeol();
310 				continue;
311 			} else
312 				/* return default answer */
313 				break;
314 		}
315 	}
316 
317 	return (*choices);
318 }
319 
320 /*
321  * FUNCTION: catch timer when waiting for input
322  *
323  * GLOBAL INPUTS: Timeoenv[]
324  *
325  * DESCRIPTION:
326  *	Come here when the alarm expires while waiting for input.
327  *	Simply longjmp() into getanswer().
328  */
329 
330 static void
331 catchalarm(__unused int sig)
332 {
333 	longjmp(Timeoenv, 1);
334 }
335