xref: /openbsd/games/robots/move.c (revision 891d7ab6)
1 /*	$OpenBSD: move.c,v 1.9 2009/10/27 23:59:26 deraadt Exp $	*/
2 /*	$NetBSD: move.c,v 1.4 1995/04/22 10:08:58 cgd Exp $	*/
3 
4 /*
5  * Copyright (c) 1980, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include "robots.h"
34 
35 #define	ESC	'\033'
36 
37 /*
38  * get_move:
39  *	Get and execute a move from the player
40  */
41 void
42 get_move(void)
43 {
44 	int	c;
45 	int retval;
46 	struct timeval t, tod;
47 	struct timezone tz;
48 #ifdef FANCY
49 	int lastmove;
50 #endif
51 
52 	if (Waiting)
53 		return;
54 
55 #ifdef	FANCY
56 	if (Pattern_roll) {
57 		if (Next_move >= Move_list)
58 			lastmove = *Next_move;
59 		else
60 			lastmove = -1;	/* flag for "first time in" */
61 	}
62 #endif
63 	if (Real_time) {
64 		t.tv_sec = tv.tv_sec;
65 		t.tv_usec = tv.tv_usec;
66 		(void)gettimeofday(&tod, &tz);
67 	}
68 	for (;;) {
69 		if (Teleport && must_telep())
70 			goto teleport;
71 		if (Running)
72 			c = Run_ch;
73 		else if (Count != 0)
74 			c = Cnt_move;
75 #ifdef	FANCY
76 		else if (Num_robots > 1 && Stand_still)
77 			c = '>';
78 		else if (Num_robots > 1 && Pattern_roll) {
79 			if (*++Next_move == '\0') {
80 				if (lastmove < 0)
81 					goto over;
82 				Next_move = Move_list;
83 			}
84 			c = *Next_move;
85 			mvaddch(0, 0, c);
86 			if (c == lastmove)
87 				goto over;
88 		}
89 #endif
90 		else {
91 over:
92 			if (Real_time) {
93 				FD_SET(STDIN_FILENO, &rset);
94 				retval = select(STDIN_FILENO + 1, &rset, NULL, NULL, &t);
95 				if (retval > 0)
96 					c = getchar();
97 				else	/* Don't move if timed out or error */
98 					c = ' ';
99 			} else {
100 				c = getchar();
101 				/* Can't use digits in real time mode, or digit/ESC
102 				 * is an effective way to stop the game.
103 				 */
104 				if (isdigit(c)) {
105 					Count = (c - '0');
106 					while (isdigit(c = getchar()))
107 						Count = Count * 10 + (c - '0');
108 					if (c == ESC)
109 						goto over;
110 					Cnt_move = c;
111 					if (Count)
112 						leaveok(stdscr, TRUE);
113 				}
114 			}
115 		}
116 
117 		switch (c) {
118 		  case ' ':
119 		  case '.':
120 			if (do_move(0, 0))
121 				goto ret;
122 			break;
123 		  case 'y':
124 			if (do_move(-1, -1))
125 				goto ret;
126 			break;
127 		  case 'k':
128 			if (do_move(-1, 0))
129 				goto ret;
130 			break;
131 		  case 'u':
132 			if (do_move(-1, 1))
133 				goto ret;
134 			break;
135 		  case 'h':
136 			if (do_move(0, -1))
137 				goto ret;
138 			break;
139 		  case 'l':
140 			if (do_move(0, 1))
141 				goto ret;
142 			break;
143 		  case 'b':
144 			if (do_move(1, -1))
145 				goto ret;
146 			break;
147 		  case 'j':
148 			if (do_move(1, 0))
149 				goto ret;
150 			break;
151 		  case 'n':
152 			if (do_move(1, 1))
153 				goto ret;
154 			break;
155 		  case 'Y': case 'U': case 'H': case 'J':
156 		  case 'K': case 'L': case 'B': case 'N':
157 		  case '>':
158 			Running = TRUE;
159 			if (c == '>')
160 				Run_ch = ' ';
161 			else
162 				Run_ch = tolower(c);
163 			leaveok(stdscr, TRUE);
164 			break;
165 		  case 'q':
166 		  case 'Q':
167 			if (query("Really quit?"))
168 				quit(0);
169 			refresh();
170 			break;
171 		  case 'w':
172 		  case 'W':
173 			Waiting = TRUE;
174 			leaveok(stdscr, TRUE);
175 #ifndef NCURSES_VERSION
176 			flushok(stdscr, FALSE);
177 #endif
178 			goto ret;
179 		  case 't':
180 		  case 'T':
181 teleport:
182 			Running = FALSE;
183 			mvaddch(My_pos.y, My_pos.x, ' ');
184 			My_pos = *rnd_pos();
185 			mvaddch(My_pos.y, My_pos.x, PLAYER);
186 			leaveok(stdscr, FALSE);
187 			refresh();
188 			flushinp();
189 			goto ret;
190 		  case CTRL('L'):
191 			wrefresh(curscr);
192 			break;
193 		  case EOF:
194 			quit(0);
195 			break;
196 		  default:
197 			beep();
198 			reset_count();
199 			break;
200 		}
201 		if (Real_time) {
202 			(void)gettimeofday(&t, &tz);
203 			t.tv_sec = tod.tv_sec + tv.tv_sec - t.tv_sec;
204 			t.tv_usec = tod.tv_usec + tv.tv_usec - t.tv_usec;
205 			if (t.tv_usec < 0) {
206 				t.tv_sec--;
207 				t.tv_usec += 1000000;	/* Now it must be > 0 */
208 			}
209 			if (t.tv_sec < 0)
210 				goto ret;
211 		}
212 	}
213 ret:
214 	if (Count > 0)
215 		if (--Count == 0)
216 			leaveok(stdscr, FALSE);
217 }
218 
219 /*
220  * must_telep:
221  *	Must I teleport; i.e., is there anywhere I can move without
222  * being eaten?
223  */
224 bool
225 must_telep(void)
226 {
227 	int		x, y;
228 	static COORD	newpos;
229 
230 #ifdef	FANCY
231 	if (Stand_still && Num_robots > 1 && eaten(&My_pos))
232 		return TRUE;
233 #endif
234 
235 	for (y = -1; y <= 1; y++) {
236 		newpos.y = My_pos.y + y;
237 		if (newpos.y <= 0 || newpos.y >= Y_FIELDSIZE)
238 			continue;
239 		for (x = -1; x <= 1; x++) {
240 			newpos.x = My_pos.x + x;
241 			if (newpos.x <= 0 || newpos.x >= X_FIELDSIZE)
242 				continue;
243 			if (Field[newpos.y][newpos.x] > 0)
244 				continue;
245 			if (!eaten(&newpos))
246 				return FALSE;
247 		}
248 	}
249 	return TRUE;
250 }
251 
252 /*
253  * do_move:
254  *	Execute a move
255  */
256 bool
257 do_move(int dy, int dx)
258 {
259 	static COORD	newpos;
260 
261 	newpos.y = My_pos.y + dy;
262 	newpos.x = My_pos.x + dx;
263 	if (newpos.y <= 0 || newpos.y >= Y_FIELDSIZE ||
264 	    newpos.x <= 0 || newpos.x >= X_FIELDSIZE ||
265 	    Field[newpos.y][newpos.x] > 0 || eaten(&newpos)) {
266 		if (Running) {
267 			Running = FALSE;
268 			leaveok(stdscr, FALSE);
269 			move(My_pos.y, My_pos.x);
270 			refresh();
271 		} else {
272 			beep();
273 			reset_count();
274 		}
275 		return FALSE;
276 	}
277 	else if (dy == 0 && dx == 0)
278 		return TRUE;
279 	mvaddch(My_pos.y, My_pos.x, ' ');
280 	My_pos = newpos;
281 	mvaddch(My_pos.y, My_pos.x, PLAYER);
282 	if (!jumping())
283 		refresh();
284 	return TRUE;
285 }
286 
287 /*
288  * eaten:
289  *	Player would get eaten at this place
290  */
291 bool
292 eaten(COORD *pos)
293 {
294 	int	x, y;
295 
296 	for (y = pos->y - 1; y <= pos->y + 1; y++) {
297 		if (y <= 0 || y >= Y_FIELDSIZE)
298 			continue;
299 		for (x = pos->x - 1; x <= pos->x + 1; x++) {
300 			if (x <= 0 || x >= X_FIELDSIZE)
301 				continue;
302 			if (Field[y][x] == 1)
303 				return TRUE;
304 		}
305 	}
306 	return FALSE;
307 }
308 
309 /*
310  * reset_count:
311  *	Reset the count variables
312  */
313 void
314 reset_count(void)
315 {
316 	Count = 0;
317 	Running = FALSE;
318 	leaveok(stdscr, FALSE);
319 	refresh();
320 }
321 
322 /*
323  * jumping:
324  *	See if we are jumping, i.e., we should not refresh.
325  */
326 bool
327 jumping(void)
328 {
329 	return (Jump && (Count || Running || Waiting));
330 }
331