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