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