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